From: John Crispin Date: Mon, 16 Apr 2012 12:31:48 +0000 (+0000) Subject: adds 3.3 patches and files X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=83948d6385c9ce23545a159ffd64f5b2854332ed;p=openwrt%2Fstaging%2Fjow.git adds 3.3 patches and files SVN-Revision: 31307 --- diff --git a/target/linux/lantiq/files-3.3/arch/mips/include/asm/mach-lantiq/dev-gpio-buttons.h b/target/linux/lantiq/files-3.3/arch/mips/include/asm/mach-lantiq/dev-gpio-buttons.h new file mode 100644 index 0000000000..adb531c39b --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/include/asm/mach-lantiq/dev-gpio-buttons.h @@ -0,0 +1,26 @@ +/* + * Lantiq GPIO button support + * + * Copyright (C) 2008-2009 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef _LANTIQ_DEV_GPIO_BUTTONS_H +#define _LANTIQ_DEV_GPIO_BUTTONS_H + +#include +#include + +#define LTQ_KEYS_POLL_INTERVAL 20 /* msecs */ +#define LTQ_KEYS_DEBOUNCE_INTERVAL (3 * LTQ_KEYS_POLL_INTERVAL) + +void ltq_register_gpio_keys_polled(int id, + unsigned poll_interval, + unsigned nbuttons, + struct gpio_keys_button *buttons); + +#endif /* _LANTIQ_DEV_GPIO_BUTTONS_H */ diff --git a/target/linux/lantiq/files-3.3/arch/mips/include/asm/mach-lantiq/dev-gpio-leds.h b/target/linux/lantiq/files-3.3/arch/mips/include/asm/mach-lantiq/dev-gpio-leds.h new file mode 100644 index 0000000000..d51e496d14 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/include/asm/mach-lantiq/dev-gpio-leds.h @@ -0,0 +1,21 @@ +/* + * Lantiq GPIO LED device support + * + * Copyright (C) 2008-2009 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef _LANTIQ_DEV_LEDS_GPIO_H +#define _LANTIQ_DEV_LEDS_GPIO_H + +#include + +void ltq_add_device_gpio_leds(int id, + unsigned num_leds, + struct gpio_led *leds) __init; + +#endif /* _LANTIQ_DEV_LEDS_GPIO_H */ diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/dev-gpio-buttons.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/dev-gpio-buttons.c new file mode 100644 index 0000000000..ad25cac797 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/dev-gpio-buttons.c @@ -0,0 +1,58 @@ +/* + * Lantiq GPIO button support + * + * Copyright (C) 2008-2009 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +void __init ltq_register_gpio_keys_polled(int id, + unsigned poll_interval, + unsigned nbuttons, + struct gpio_keys_button *buttons) +{ + struct platform_device *pdev; + struct gpio_keys_platform_data pdata; + struct gpio_keys_button *p; + int err; + + p = kmalloc(nbuttons * sizeof(*p), GFP_KERNEL); + if (!p) + return; + + memcpy(p, buttons, nbuttons * sizeof(*p)); + + pdev = platform_device_alloc("gpio-keys-polled", id); + if (!pdev) + goto err_free_buttons; + + memset(&pdata, 0, sizeof(pdata)); + pdata.poll_interval = poll_interval; + pdata.nbuttons = nbuttons; + pdata.buttons = p; + + err = platform_device_add_data(pdev, &pdata, sizeof(pdata)); + if (err) + goto err_put_pdev; + + err = platform_device_add(pdev); + if (err) + goto err_put_pdev; + + return; + +err_put_pdev: + platform_device_put(pdev); + +err_free_buttons: + kfree(p); +} diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/dev-gpio-leds.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/dev-gpio-leds.c new file mode 100644 index 0000000000..89dc79de61 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/dev-gpio-leds.c @@ -0,0 +1,57 @@ +/* + * Lantiq GPIO LED device support + * + * Copyright (C) 2008-2009 Gabor Juhos + * Copyright (C) 2008 Imre Kaloz + * + * Parts of this file are based on Atheros' 2.6.15 BSP + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include + +#include + +void __init ltq_add_device_gpio_leds(int id, unsigned num_leds, + struct gpio_led *leds) +{ + struct platform_device *pdev; + struct gpio_led_platform_data pdata; + struct gpio_led *p; + int err; + + p = kmalloc(num_leds * sizeof(*p), GFP_KERNEL); + if (!p) + return; + + memcpy(p, leds, num_leds * sizeof(*p)); + + pdev = platform_device_alloc("leds-gpio", id); + if (!pdev) + goto err_free_leds; + + memset(&pdata, 0, sizeof(pdata)); + pdata.num_leds = num_leds; + pdata.leds = p; + + err = platform_device_add_data(pdev, &pdata, sizeof(pdata)); + if (err) + goto err_put_pdev; + + err = platform_device_add(pdev); + if (err) + goto err_put_pdev; + + return; + +err_put_pdev: + platform_device_put(pdev); + +err_free_leds: + kfree(p); +} diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/addon-easy98000.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/addon-easy98000.c new file mode 100644 index 0000000000..317ee4001b --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/addon-easy98000.c @@ -0,0 +1,213 @@ +/* + * EASY98000 CPLD Addon driver + * + * Copyright (C) 2011 Thomas Langer + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct easy98000_reg_cpld { + u16 cmdreg1; /* 0x1 */ + u16 cmdreg0; /* 0x0 */ + u16 idreg0; /* 0x3 */ + u16 resreg; /* 0x2 */ + u16 intreg; /* 0x5 */ + u16 idreg1; /* 0x4 */ + u16 ledreg; /* 0x7 */ + u16 pcmconconfig; /* 0x6 */ + u16 res0; /* 0x9 */ + u16 ethledreg; /* 0x8 */ + u16 res1[4]; /* 0xa-0xd */ + u16 cpld1v; /* 0xf */ + u16 cpld2v; /* 0xe */ +}; +static struct easy98000_reg_cpld * const cpld = + (struct easy98000_reg_cpld *)(KSEG1 | 0x17c00000); +#define cpld_r8(reg) (__raw_readw(&cpld->reg) & 0xFF) +#define cpld_w8(val, reg) __raw_writew((val) & 0xFF, &cpld->reg) + +int easy98000_addon_has_dm9000(void) +{ + if ((cpld_r8(idreg0) & 0xF) == 1) + return 1; + return 0; +} + +#if defined(CONFIG_PROC_FS) +typedef void (*cpld_dump) (struct seq_file *s); +struct proc_entry { + char *name; + void *callback; +}; + +static int cpld_proc_show ( struct seq_file *s, void *p ) +{ + cpld_dump dump = s->private; + + if ( dump != NULL ) + dump(s); + + return 0; +} + +static int cpld_proc_open ( struct inode *inode, struct file *file ) +{ + return single_open ( file, cpld_proc_show, PDE(inode)->data ); +} + +static void cpld_versions_get ( struct seq_file *s ) +{ + seq_printf(s, "CPLD1: V%d\n", cpld_r8(cpld1v)); + seq_printf(s, "CPLD2: V%d\n", cpld_r8(cpld2v)); +} + +static void cpld_ebu_module_get ( struct seq_file *s ) +{ + u8 addon_id; + + addon_id = cpld_r8(idreg0) & 0xF; + switch (addon_id) { + case 0xF: /* nothing connected */ + break; + case 1: + seq_printf(s, "Ethernet Controller module (dm9000)\n"); + break; + default: + seq_printf(s, "Unknown EBU module (EBU_ID=0x%02X)\n", addon_id); + break; + } +} + +static void cpld_xmii_module_get ( struct seq_file *s ) +{ + u8 addon_id; + char *mod = NULL; + + addon_id = cpld_r8(idreg1) & 0xF; + switch (addon_id) { + case 0xF: + mod = "no module"; + break; + case 0x1: + mod = "RGMII module"; + break; + case 0x4: + mod = "GMII MAC Mode (XWAY TANTOS-3G)"; + break; + case 0x6: + mod = "TMII MAC Mode (XWAY TANTOS-3G)"; + break; + case 0x8: + mod = "GMII PHY module"; + break; + case 0x9: + mod = "MII PHY module"; + break; + case 0xA: + mod = "RMII PHY module"; + break; + default: + break; + } + if (mod) + seq_printf(s, "%s\n", mod); + else + seq_printf(s, "unknown xMII module (xMII_ID=0x%02X)\n", addon_id); +} + +static struct proc_entry proc_entries[] = { + {"versions", cpld_versions_get}, + {"ebu", cpld_ebu_module_get}, + {"xmii", cpld_xmii_module_get}, +}; + +static struct file_operations ops = { + .owner = THIS_MODULE, + .open = cpld_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void cpld_proc_entry_create(struct proc_dir_entry *parent_node, + struct proc_entry *proc_entry) +{ + proc_create_data ( proc_entry->name, (S_IFREG | S_IRUGO), parent_node, + &ops, proc_entry->callback); +} + +static int cpld_proc_install(void) +{ + struct proc_dir_entry *driver_proc_node; + + driver_proc_node = proc_mkdir("cpld", NULL); + if (driver_proc_node != NULL) { + int i; + for (i = 0; i < ARRAY_SIZE(proc_entries); i++) + cpld_proc_entry_create(driver_proc_node, + &proc_entries[i]); + } else { + printk("cannot create proc entry"); + return -1; + } + return 0; +} +#else +static inline int cpld_proc_install(void) {} +#endif + +static int easy98000_addon_probe(struct platform_device *pdev) +{ + return cpld_proc_install(); +} + +static int easy98000_addon_remove(struct platform_device *pdev) +{ +#if defined(CONFIG_PROC_FS) + char buf[64]; + int i; + + for (i = 0; i < sizeof(proc_entries) / sizeof(proc_entries[0]); i++) { + sprintf(buf, "cpld/%s", proc_entries[i].name); + remove_proc_entry(buf, 0); + } + remove_proc_entry("cpld", 0); +#endif + return 0; +} + +static struct platform_driver easy98000_addon_driver = { + .probe = easy98000_addon_probe, + .remove = __devexit_p(easy98000_addon_remove), + .driver = { + .name = "easy98000_addon", + .owner = THIS_MODULE, + }, +}; + +int __init easy98000_addon_init(void) +{ + return platform_driver_register(&easy98000_addon_driver); +} + +void __exit easy98000_addon_exit(void) +{ + platform_driver_unregister(&easy98000_addon_driver); +} + +module_init(easy98000_addon_init); +module_exit(easy98000_addon_exit); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/dev-leds-easy98000-cpld.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/dev-leds-easy98000-cpld.c new file mode 100644 index 0000000000..94622cfda8 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/dev-leds-easy98000-cpld.c @@ -0,0 +1,161 @@ +/* + * EASY98000 CPLD LED driver + * + * Copyright (C) 2010 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dev-leds-easy98000-cpld.h" + +const char *led_name[8] = { + "ge0_act", + "ge0_link", + "ge1_act", + "ge1_link", + "fe2_act", + "fe2_link", + "fe3_act", + "fe3_link" +}; + +#define cpld_base7 ((u16 *)(KSEG1 | 0x17c0000c)) +#define cpld_base8 ((u16 *)(KSEG1 | 0x17c00012)) + +#define ltq_r16(reg) __raw_readw(reg) +#define ltq_w16(val, reg) __raw_writew(val, reg) + +struct cpld_led_dev { + struct led_classdev cdev; + u8 mask; + u16 *base; +}; + +struct cpld_led_drvdata { + struct cpld_led_dev *led_devs; + int num_leds; +}; + +void led_set(u8 mask, u16 *base) +{ + ltq_w16(ltq_r16(base) | mask, base); +} + +void led_clear(u8 mask, u16 *base) +{ + ltq_w16(ltq_r16(base) & (~mask), base); +} + +void led_blink_clear(u8 mask, u16 *base) +{ + led_clear(mask, base); +} + +static void led_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct cpld_led_dev *led_dev = + container_of(led_cdev, struct cpld_led_dev, cdev); + + if (value) + led_set(led_dev->mask, led_dev->base); + else + led_clear(led_dev->mask, led_dev->base); +} + +static int led_probe(struct platform_device *pdev) +{ + int i; + char name[32]; + struct cpld_led_drvdata *drvdata; + int ret = 0; + + drvdata = kzalloc(sizeof(struct cpld_led_drvdata) + + sizeof(struct cpld_led_dev) * MAX_LED, + GFP_KERNEL); + if (!drvdata) + return -ENOMEM; + + drvdata->led_devs = (struct cpld_led_dev *) &drvdata[1]; + + for (i = 0; i < MAX_LED; i++) { + struct cpld_led_dev *led_dev = &drvdata->led_devs[i]; + led_dev->cdev.brightness_set = led_brightness; + led_dev->cdev.default_trigger = NULL; + led_dev->mask = 1 << (i % 8); + if(i < 8) { + sprintf(name, "easy98000-cpld:%s", led_name[i]); + led_dev->base = cpld_base8; + } else { + sprintf(name, "easy98000-cpld:red:%d", i-8); + led_dev->base = cpld_base7; + } + led_dev->cdev.name = name; + ret = led_classdev_register(&pdev->dev, &led_dev->cdev); + if (ret) + goto err; + } + platform_set_drvdata(pdev, drvdata); + return 0; + +err: + printk("led_probe: 3\n"); + for (i = i - 1; i >= 0; i--) + led_classdev_unregister(&drvdata->led_devs[i].cdev); + + kfree(drvdata); + return ret; +} + +static int led_remove(struct platform_device *pdev) +{ + int i; + struct cpld_led_drvdata *drvdata = platform_get_drvdata(pdev); + for (i = 0; i < MAX_LED; i++) + led_classdev_unregister(&drvdata->led_devs[i].cdev); + kfree(drvdata); + return 0; +} + +static struct platform_driver led_driver = { + .probe = led_probe, + .remove = __devexit_p(led_remove), + .driver = { + .name = LED_NAME, + .owner = THIS_MODULE, + }, +}; + +int __init easy98000_cpld_led_init(void) +{ + pr_info(LED_DESC ", Version " LED_VERSION + " (c) Copyright 2011, Lantiq Deutschland GmbH\n"); + return platform_driver_register(&led_driver); +} + +void __exit easy98000_cpld_led_exit(void) +{ + platform_driver_unregister(&led_driver); +} + +module_init(easy98000_cpld_led_init); +module_exit(easy98000_cpld_led_exit); + +MODULE_DESCRIPTION(LED_NAME); +MODULE_DESCRIPTION(LED_DESC); +MODULE_AUTHOR("Ralph Hempel "); +MODULE_LICENSE("GPL v2"); + diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/dev-leds-easy98000-cpld.h b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/dev-leds-easy98000-cpld.h new file mode 100644 index 0000000000..3160189fe9 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/dev-leds-easy98000-cpld.h @@ -0,0 +1,20 @@ +/* + * EASY98000 CPLD LED driver + * + * Copyright (C) 2010 Ralph Hempel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + */ +#ifndef _INCLUDE_EASY98000_CPLD_LED_H_ +#define _INCLUDE_EASY98000_CPLD_LED_H_ + +#define LED_NAME "easy98000_cpld_led" +#define LED_DESC "EASY98000 LED driver" +#define LED_VERSION "1.0.0" + +#define MAX_LED 16 + +#endif /* _INCLUDE_EASY98000_CPLD_LED_H_ */ diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/mach-95C3AM1.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/mach-95C3AM1.c new file mode 100644 index 0000000000..42a3344ae9 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/mach-95C3AM1.c @@ -0,0 +1,94 @@ +#include +#include +#include + +#include + +#include "../machtypes.h" +#include "devices.h" + +#define BOARD_95C3AM1_GPIO_LED_0 10 +#define BOARD_95C3AM1_GPIO_LED_1 11 +#define BOARD_95C3AM1_GPIO_LED_2 12 +#define BOARD_95C3AM1_GPIO_LED_3 13 + +static struct mtd_partition board_95C3AM1_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x40000, + }, + { + .name = "uboot_env", + .offset = 0x40000, + .size = 0x40000, /* 2 sectors for redundant env. */ + }, + { + .name = "linux", + .offset = 0x80000, + .size = 0xF80000, /* map only 16 MiB */ + }, +}; + +static struct flash_platform_data board_95C3AM1_flash_platform_data = { + .name = "sflash", + .parts = board_95C3AM1_partitions, + .nr_parts = ARRAY_SIZE(board_95C3AM1_partitions) +}; + +static struct spi_board_info board_95C3AM1_flash_data __initdata = { + .modalias = "m25p80", + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 10 * 1000 * 1000, + .mode = SPI_MODE_3, + .platform_data = &board_95C3AM1_flash_platform_data +}; + +static struct gpio_led board_95C3AM1_gpio_leds[] __initdata = { + { + .name = "power", + .gpio = BOARD_95C3AM1_GPIO_LED_0, + .active_low = 0, + }, { + .name = "optical", + .gpio = BOARD_95C3AM1_GPIO_LED_1, + .active_low = 0, + }, { + .name = "lan", + .gpio = BOARD_95C3AM1_GPIO_LED_2, + .active_low = 0, + }, { + .name = "update", + .gpio = BOARD_95C3AM1_GPIO_LED_3, + .active_low = 0, + } +}; + +static struct i2c_gpio_platform_data board_95C3AM1_i2c_gpio_data = { + .sda_pin = 107, + .scl_pin = 108, +}; + +static struct platform_device board_95C3AM1_i2c_gpio_device = { + .name = "i2c-gpio", + .id = 0, + .dev = { + .platform_data = &board_95C3AM1_i2c_gpio_data, + } +}; + +static void __init board_95C3AM1_init(void) +{ + falcon_register_i2c(); + falcon_register_spi_flash(&board_95C3AM1_flash_data); + platform_device_register(&board_95C3AM1_i2c_gpio_device); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(board_95C3AM1_gpio_leds), + board_95C3AM1_gpio_leds); +} + +MIPS_MACHINE(LANTIQ_MACH_95C3AM1, + "95C3AM1", + "95C3AM1 Board", + board_95C3AM1_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/mach-easy98020.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/mach-easy98020.c new file mode 100644 index 0000000000..4cdfc199db --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/falcon/mach-easy98020.c @@ -0,0 +1,118 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "../machtypes.h" +#include "devices.h" + +#define EASY98020_GPIO_LED_0 9 +#define EASY98020_GPIO_LED_1 10 +#define EASY98020_GPIO_LED_2 11 +#define EASY98020_GPIO_LED_3 12 +#define EASY98020_GPIO_LED_GE0_ACT 110 +#define EASY98020_GPIO_LED_GE0_LINK 109 +#define EASY98020_GPIO_LED_GE1_ACT 106 +#define EASY98020_GPIO_LED_GE1_LINK 105 + +static struct mtd_partition easy98020_spi_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x40000, + }, + { + .name = "uboot_env", + .offset = 0x40000, + .size = 0x40000, /* 2 sectors for redundant env. */ + }, + { + .name = "linux", + .offset = 0x80000, + .size = 0xF80000, /* map only 16 MiB */ + }, +}; + +static struct flash_platform_data easy98020_spi_flash_platform_data = { + .name = "sflash", + .parts = easy98020_spi_partitions, + .nr_parts = ARRAY_SIZE(easy98020_spi_partitions) +}; + +static struct spi_board_info easy98020_spi_flash_data __initdata = { + .modalias = "m25p80", + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 10 * 1000 * 1000, + .mode = SPI_MODE_3, + .platform_data = &easy98020_spi_flash_platform_data +}; + +static struct gpio_led easy98020_gpio_leds[] __initdata = { + { + .name = "easy98020:green:0", + .gpio = EASY98020_GPIO_LED_0, + .active_low = 0, + }, { + .name = "easy98020:green:1", + .gpio = EASY98020_GPIO_LED_1, + .active_low = 0, + }, { + .name = "easy98020:green:2", + .gpio = EASY98020_GPIO_LED_2, + .active_low = 0, + }, { + .name = "easy98020:green:3", + .gpio = EASY98020_GPIO_LED_3, + .active_low = 0, + }, { + .name = "easy98020:ge0_act", + .gpio = EASY98020_GPIO_LED_GE0_ACT, + .active_low = 0, + }, { + .name = "easy98020:ge0_link", + .gpio = EASY98020_GPIO_LED_GE0_LINK, + .active_low = 0, + }, { + .name = "easy98020:ge1_act", + .gpio = EASY98020_GPIO_LED_GE1_ACT, + .active_low = 0, + }, { + .name = "easy98020:ge1_link", + .gpio = EASY98020_GPIO_LED_GE1_LINK, + .active_low = 0, + } +}; + +static void __init easy98020_init(void) +{ + falcon_register_i2c(); + falcon_register_spi_flash(&easy98020_spi_flash_data); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(easy98020_gpio_leds), + easy98020_gpio_leds); +} + +MIPS_MACHINE(LANTIQ_MACH_EASY98020, + "EASY98020", + "EASY98020 Eval Board", + easy98020_init); + +MIPS_MACHINE(LANTIQ_MACH_EASY98020_1LAN, + "EASY98020_1LAN", + "EASY98020 Eval Board (1 LAN port)", + easy98020_init); + +MIPS_MACHINE(LANTIQ_MACH_EASY98020_2LAN, + "EASY98020_2LAN", + "EASY98020 Eval Board (2 LAN ports)", + easy98020_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-dwc_otg.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-dwc_otg.c new file mode 100644 index 0000000000..67f580527f --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-dwc_otg.c @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define LTQ_USB_IOMEM_BASE 0x1e101000 +#define LTQ_USB_IOMEM_SIZE 0x00001000 + +static struct resource resources[] = +{ + [0] = { + .name = "dwc_otg_membase", + .start = LTQ_USB_IOMEM_BASE, + .end = LTQ_USB_IOMEM_BASE + LTQ_USB_IOMEM_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .name = "dwc_otg_irq", + .flags = IORESOURCE_IRQ, + }, +}; + +static u64 dwc_dmamask = (u32)0x1fffffff; + +static struct platform_device platform_dev = { + .name = "dwc_otg", + .dev = { + .dma_mask = &dwc_dmamask, + }, + .resource = resources, + .num_resources = ARRAY_SIZE(resources), +}; + +int __init +xway_register_dwc(int pin) +{ + struct irq_data d; + d.irq = resources[1].start; + ltq_enable_irq(&d); + resources[1].start = ltq_is_ase() ? LTQ_USB_ASE_INT : LTQ_USB_INT; + platform_dev.dev.platform_data = (void*) pin; + return platform_device_register(&platform_dev); +} diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-dwc_otg.h b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-dwc_otg.h new file mode 100644 index 0000000000..521fad05ef --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-dwc_otg.h @@ -0,0 +1,17 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Copyright (C) 2010 John Crispin + */ + +#ifndef _LTQ_DEV_DWC_H__ +#define _LTQ_DEV_DWC_H__ + +#include + +extern void __init xway_register_dwc(int pin); + +#endif diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-athxk.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-athxk.c new file mode 100644 index 0000000000..a75abe3e00 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-athxk.c @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2011 John Crispin + * Copyright (C) 2011 Andrej VlaÅ¡ić + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include + +#include "dev-wifi-athxk.h" + +extern int (*ltqpci_plat_dev_init)(struct pci_dev *dev); +struct ath5k_platform_data ath5k_pdata; +struct ath9k_platform_data ath9k_pdata = { + .led_pin = -1, + .endian_check = true, +}; + +static int +ath5k_pci_plat_dev_init(struct pci_dev *dev) +{ + dev->dev.platform_data = &ath5k_pdata; + return 0; +} + +static int +ath9k_pci_plat_dev_init(struct pci_dev *dev) +{ + dev->dev.platform_data = &ath9k_pdata; + return 0; +} + +void __init +ltq_register_ath5k(u16 *eeprom_data, u8 *macaddr) +{ + ath5k_pdata.eeprom_data = eeprom_data; + ath5k_pdata.macaddr = macaddr; + ltqpci_plat_dev_init = ath5k_pci_plat_dev_init; +} + +void __init +ltq_register_ath9k(u16 *eeprom_data, u8 *macaddr) +{ + memcpy(ath9k_pdata.eeprom_data, eeprom_data, sizeof(ath9k_pdata.eeprom_data)); + ath9k_pdata.macaddr = macaddr; + ltqpci_plat_dev_init = ath9k_pci_plat_dev_init; +} diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-athxk.h b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-athxk.h new file mode 100644 index 0000000000..5fdb46b612 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-athxk.h @@ -0,0 +1,16 @@ +/* + * Copyright (C) 2011 John Crispin + * Copyright (C) 2011 Andrej VlaÅ¡ić + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef _DEV_WIFI_ATHXK_H__ +#define _DEV_WIFI_ATHXK_H__ + +extern void ltq_register_ath5k(u16 *eeprom_data, u8 *macaddr); +extern void ltq_register_ath9k(u16 *eeprom_data, u8 *macaddr); + +#endif diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-rt2x00.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-rt2x00.c new file mode 100644 index 0000000000..94932df785 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-rt2x00.c @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2011 John Crispin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include "dev-wifi-rt2x00.h" + +extern int (*ltqpci_plat_dev_init)(struct pci_dev *dev); +struct rt2x00_platform_data rt2x00_pdata; + +static int +rt2x00_pci_plat_dev_init(struct pci_dev *dev) +{ + dev->dev.platform_data = &rt2x00_pdata; + return 0; +} + +void __init +ltq_register_rt2x00(const char *firmware) +{ + rt2x00_pdata.eeprom_file_name = kstrdup(firmware, GFP_KERNEL); + ltqpci_plat_dev_init = rt2x00_pci_plat_dev_init; +} diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-rt2x00.h b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-rt2x00.h new file mode 100644 index 0000000000..060ca50270 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/dev-wifi-rt2x00.h @@ -0,0 +1,14 @@ +/* + * Copyright (C) 2011 John Crispin + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#ifndef _DEV_WIFI_RT2X00_H__ +#define _DEV_WIFI_RT2X00_H__ + +extern void ltq_register_rt2x00(const char *firmware); + +#endif diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-arv.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-arv.c new file mode 100644 index 0000000000..99926c5c81 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-arv.c @@ -0,0 +1,785 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../machtypes.h" +#include "dev-wifi-rt2x00.h" +#include "dev-wifi-athxk.h" +#include "devices.h" +#include "dev-dwc_otg.h" +#include "pci-ath-fixup.h" + +static struct mtd_partition arv45xx_brnboot_partitions[] = +{ + { + .name = "brn-boot", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "config", + .offset = 0x20000, + .size = 0x30000, + }, + { + .name = "linux", + .offset = 0x50000, + .size = 0x390000, + }, + { + .name = "reserved", /* 12-byte signature at 0x3efff4 :/ */ + .offset = 0x3e0000, + .size = 0x010000, + }, + { + .name = "eeprom", + .offset = 0x3f0000, + .size = 0x10000, + }, +}; + +static struct mtd_partition arv75xx_brnboot_partitions[] = +{ + { + .name = "brn-boot", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "config", + .offset = 0x20000, + .size = 0x40000, + }, + { + .name = "linux", + .offset = 0x440000, + .size = 0x3a0000, + }, + { + .name = "reserved", /* 12-byte signature at 0x7efff4 :/ */ + .offset = 0x7e0000, + .size = 0x010000, + }, + { + .name = "board_config", + .offset = 0x7f0000, + .size = 0x10000, + }, +}; + +/* + * this is generic configuration for all arv based boards, note that it can be + * rewriten in arv_load_nor() + */ +static struct mtd_partition arv_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "uboot_env", + .offset = 0x20000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x30000, + .size = 0x3c0000, + }, + { + .name = "board_config", + .offset = 0x3f0000, + .size = 0x10000, + }, +}; + +static struct physmap_flash_data arv45xx_brnboot_flash_data = { + .nr_parts = ARRAY_SIZE(arv45xx_brnboot_partitions), + .parts = arv45xx_brnboot_partitions, +}; + +static struct physmap_flash_data arv75xx_brnboot_flash_data = { + .nr_parts = ARRAY_SIZE(arv75xx_brnboot_partitions), + .parts = arv75xx_brnboot_partitions, +}; + +static struct physmap_flash_data arv_flash_data = { + .nr_parts = ARRAY_SIZE(arv_partitions), + .parts = arv_partitions, +}; + +static void arv_load_nor(unsigned int max) +{ +#define UBOOT_MAGIC 0x27051956 + + int i; + int sector = -1; + + if (ltq_brn_boot) { + if (max == 0x800000) + ltq_register_nor(&arv75xx_brnboot_flash_data); + else + ltq_register_nor(&arv45xx_brnboot_flash_data); + return; + } + + for (i = 1; i < 4 && sector < 0; i++) { + unsigned int uboot_magic; + memcpy_fromio(&uboot_magic, (void *)KSEG1ADDR(LTQ_FLASH_START) + (i * 0x10000), 4); + if (uboot_magic == UBOOT_MAGIC) + sector = i; + } + + if (sector < 0) + return; + + arv_partitions[0].size = arv_partitions[1].offset = (sector - 1) * 0x10000; + arv_partitions[2].offset = arv_partitions[0].size + 0x10000; + arv_partitions[2].size = max - arv_partitions[2].offset - 0x10000; + arv_partitions[3].offset = max - 0x10000; + ltq_register_nor(&arv_flash_data); +} + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_EXT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_RMII, +}; + +static struct gpio_led +arv4510pw_gpio_leds[] __initdata = { + { .name = "soc:green:foo", .gpio = 4, .active_low = 1, }, +}; + +static struct gpio_led +arv4518pw_gpio_leds[] __initdata = { + { .name = "soc:green:power", .gpio = 3, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:adsl", .gpio = 4, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:internet", .gpio = 5, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wifi", .gpio = 6, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:yellow:wps", .gpio = 7, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:fail", .gpio = 8, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:usb", .gpio = 19, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:voip", .gpio = 100, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:fxs1", .gpio = 101, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:fxs2", .gpio = 102, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:fxo", .gpio = 103, .active_low = 1, .default_trigger = "default-on" }, +}; + +static struct gpio_keys_button +arv4518pw_gpio_keys[] __initdata = { + { + .desc = "wifi", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 28, + .active_low = 1, + }, + { + .desc = "reset", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 30, + .active_low = 1, + }, + { + .desc = "wps", + .type = EV_KEY, + .code = BTN_2, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 29, + .active_low = 1, + }, +}; + +static struct gpio_led +arv4519pw_gpio_leds[] __initdata = { + { .name = "soc:red:power", .gpio = 7, .active_low = 1, }, + { .name = "soc:green:power", .gpio = 2, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wifi", .gpio = 6, .active_low = 1, }, + { .name = "soc:green:adsl", .gpio = 4, .active_low = 1, }, + { .name = "soc:green:internet", .gpio = 5, .active_low = 1, }, + { .name = "soc:red:internet", .gpio = 8, .active_low = 1, }, + { .name = "soc:green:voip", .gpio = 100, .active_low = 1, }, + { .name = "soc:green:phone1", .gpio = 101, .active_low = 1, }, + { .name = "soc:green:phone2", .gpio = 102, .active_low = 1, }, + { .name = "soc:green:fxo", .gpio = 103, .active_low = 1, }, + { .name = "soc:green:usb", .gpio = 19, .active_low = 1, }, + { .name = "soc:orange:wps", .gpio = 104, .active_low = 1, }, + { .name = "soc:green:wps", .gpio = 105, .active_low = 1, }, + { .name = "soc:red:wps", .gpio = 106, .active_low = 1, }, + +}; + +static struct gpio_keys_button +arv4519pw_gpio_keys[] __initdata = { + { + .desc = "reset", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 30, + .active_low = 1, + }, + { + .desc = "wlan", + .type = EV_KEY, + .code = BTN_2, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 28, + .active_low = 1, + }, +}; + +static struct gpio_led +arv4520pw_gpio_leds[] __initdata = { + { .name = "soc:blue:power", .gpio = 3, .active_low = 1, }, + { .name = "soc:blue:adsl", .gpio = 4, .active_low = 1, }, + { .name = "soc:blue:internet", .gpio = 5, .active_low = 1, }, + { .name = "soc:red:power", .gpio = 6, .active_low = 1, }, + { .name = "soc:yellow:wps", .gpio = 7, .active_low = 1, }, + { .name = "soc:red:wps", .gpio = 9, .active_low = 1, }, + { .name = "soc:blue:voip", .gpio = 100, .active_low = 1, }, + { .name = "soc:blue:fxs1", .gpio = 101, .active_low = 1, }, + { .name = "soc:blue:fxs2", .gpio = 102, .active_low = 1, }, + { .name = "soc:blue:fxo", .gpio = 103, .active_low = 1, }, + { .name = "soc:blue:voice", .gpio = 104, .active_low = 1, }, + { .name = "soc:blue:usb", .gpio = 105, .active_low = 1, }, + { .name = "soc:blue:wifi", .gpio = 106, .active_low = 1, }, +}; + +static struct gpio_led +arv452cpw_gpio_leds[] __initdata = { + { .name = "soc:blue:power", .gpio = 3, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:adsl", .gpio = 4, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:isdn", .gpio = 5, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:power", .gpio = 6, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:yellow:wps", .gpio = 7, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:wps", .gpio = 9, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:fxs1", .gpio = 100, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:fxs2", .gpio = 101, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:wps", .gpio = 102, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:fxo", .gpio = 103, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:voice", .gpio = 104, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:usb", .gpio = 105, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:wifi", .gpio = 106, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:internet", .gpio = 108, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:internet", .gpio = 109, .active_low = 1, .default_trigger = "default-on" }, +}; + +static struct gpio_led +arv4525pw_gpio_leds[] __initdata = { + { .name = "soc:green:dsl", .gpio = 6, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wifi", .gpio = 8, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:online", .gpio = 9, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:fxs-internet", .gpio = 5, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:fxs-festnetz", .gpio = 4, .active_low = 1, .default_trigger = "default-on" }, +}; + +#define ARV4525PW_PHYRESET 13 +#define ARV4525PW_RELAY 31 + +static struct gpio arv4525pw_gpios[] __initdata = { + { ARV4525PW_PHYRESET, GPIOF_OUT_INIT_HIGH, "phyreset" }, + { ARV4525PW_RELAY, GPIOF_OUT_INIT_HIGH, "relay" }, +}; + + +static struct gpio_led +arv752dpw22_gpio_leds[] __initdata = { + { .name = "soc:blue:power", .gpio = 3, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:internet", .gpio = 5, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:power", .gpio = 6, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:wps", .gpio = 8, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:fxo", .gpio = 103, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:voice", .gpio = 104, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:usb", .gpio = 105, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wifi", .gpio = 106, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wifi1", .gpio = 107, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:wifi", .gpio = 108, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:blue:wifi1", .gpio = 109, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:eth1", .gpio = 111, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:eth2", .gpio = 112, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:eth3", .gpio = 113, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:eth4", .gpio = 114, .active_low = 1, .default_trigger = "default-on", }, +}; + +static struct gpio_keys_button +arv752dpw22_gpio_keys[] __initdata = { + { + .desc = "btn0", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 12, + .active_low = 1, + }, + { + .desc = "btn1", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 13, + .active_low = 1, + }, + { + .desc = "btn2", + .type = EV_KEY, + .code = BTN_2, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 28, + .active_low = 1, + }, +}; + +static struct gpio_led +arv7518pw_gpio_leds[] __initdata = { + { .name = "soc:red:power", .gpio = 7, .active_low = 1, }, + { .name = "soc:green:power", .gpio = 2, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wifi", .gpio = 6, .active_low = 1, }, + { .name = "soc:green:adsl", .gpio = 4, .active_low = 1, }, + { .name = "soc:green:internet", .gpio = 5, .active_low = 1, }, + { .name = "soc:red:internet", .gpio = 8, .active_low = 1, }, + { .name = "soc:green:voip", .gpio = 100, .active_low = 1, }, + { .name = "soc:green:phone1", .gpio = 101, .active_low = 1, }, + { .name = "soc:green:phone2", .gpio = 102, .active_low = 1, }, + { .name = "soc:orange:fail", .gpio = 103, .active_low = 1, }, + { .name = "soc:green:usb", .gpio = 19, .active_low = 1, }, + { .name = "soc:orange:wps", .gpio = 104, .active_low = 1, }, + { .name = "soc:green:wps", .gpio = 105, .active_low = 1, }, + { .name = "soc:red:wps", .gpio = 106, .active_low = 1, }, + +}; + +static struct gpio_keys_button +arv7518pw_gpio_keys[] __initdata = { + /*{ + .desc = "reset", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 23, + .active_low = 1, + },*/ + { + .desc = "wifi", + .type = EV_KEY, + .code = BTN_2, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 25, + .active_low = 1, + }, +}; + +static struct gpio_keys_button +arv7525pw_gpio_keys[] __initdata = { + { + .desc = "restart", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 29, + .active_low = 1, + }, +}; + +static void +arv_register_ethernet(unsigned int mac_addr) +{ + memcpy_fromio(<q_eth_data.mac.sa_data, + (void *)KSEG1ADDR(LTQ_FLASH_START + mac_addr), 6); + ltq_register_etop(<q_eth_data); +} + +static u16 arv_ath5k_eeprom_data[ATH5K_PLAT_EEP_MAX_WORDS]; +static u16 arv_ath9k_eeprom_data[ATH9K_PLAT_EEP_MAX_WORDS]; +static u8 arv_athxk_eeprom_mac[6]; + +void __init +arv_register_ath5k(unsigned int ath_addr, unsigned int mac_addr) +{ + int i; + + memcpy_fromio(arv_athxk_eeprom_mac, + (void *)KSEG1ADDR(LTQ_FLASH_START + mac_addr), 6); + arv_athxk_eeprom_mac[5]++; + memcpy_fromio(arv_ath5k_eeprom_data, + (void *)KSEG1ADDR(LTQ_FLASH_START + ath_addr), ATH5K_PLAT_EEP_MAX_WORDS); + // swap eeprom bytes + for (i = 0; i < ATH5K_PLAT_EEP_MAX_WORDS>>1; i++) { + arv_ath5k_eeprom_data[i] = swab16(arv_ath5k_eeprom_data[i]); + if (i == 0x17e>>1) { + /* + * regdomain is invalid. it's unknown how did original + * fw convered value to 0x82d4 so for now force to 0x67 + */ + arv_ath5k_eeprom_data[i] &= 0x0000; + arv_ath5k_eeprom_data[i] |= 0x67; + } + } +} + +void __init +arv_register_ath9k(unsigned int ath_addr, unsigned int mac_addr) +{ + int i; + u16 *eepdata, sum, el; + + memcpy_fromio(arv_athxk_eeprom_mac, + (void *)KSEG1ADDR(LTQ_FLASH_START + mac_addr), 6); + arv_athxk_eeprom_mac[5]++; + memcpy_fromio(arv_ath9k_eeprom_data, + (void *)KSEG1ADDR(LTQ_FLASH_START + ath_addr), ATH9K_PLAT_EEP_MAX_WORDS); + + // force regdomain to 0x67 + arv_ath9k_eeprom_data[0x208>>1] = 0x67; + + // calculate new checksum + sum = arv_ath9k_eeprom_data[0x200>>1]; + el = sum / sizeof(u16) - 2; /* skip length and (old) checksum */ + eepdata = (u16 *) (&arv_ath9k_eeprom_data[0x204>>1]); /* after checksum */ + for (i = 0; i < el; i++) + sum ^= *eepdata++; + sum ^= 0xffff; + arv_ath9k_eeprom_data[0x202>>1] = sum; +} + +static void __init +arv3527p_init(void) +{ +#define ARV3527P_MAC_ADDR 0x3f0016 + + ltq_register_gpio_stp(); + //ltq_add_device_gpio_leds(arv3527p_gpio_leds, ARRAY_SIZE(arv3527p_gpio_leds)); + arv_load_nor(0x400000); + arv_register_ethernet(ARV3527P_MAC_ADDR); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV3527P, + "ARV3527P", + "ARV3527P - Arcor Easybox 401", + arv3527p_init); + +static void __init +arv4510pw_init(void) +{ +#define ARV4510PW_MAC_ADDR 0x3f0014 + + ltq_register_gpio_stp(); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv4510pw_gpio_leds), arv4510pw_gpio_leds); + arv_load_nor(0x400000); + ltq_pci_data.irq[12] = (INT_NUM_IM2_IRL0 + 31); + ltq_pci_data.irq[15] = (INT_NUM_IM0_IRL0 + 26); + ltq_pci_data.gpio |= PCI_EXIN2 | PCI_REQ2; + ltq_register_pci(<q_pci_data); + arv_register_ethernet(ARV4510PW_MAC_ADDR); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV4510PW, + "ARV4510PW", + "ARV4510PW - Wippies Homebox", + arv4510pw_init); + +static void __init +arv4518pw_init(void) +{ +#define ARV4518PW_EBU 0 +#define ARV4518PW_USB 14 +#define ARV4518PW_SWITCH_RESET 13 +#define ARV4518PW_ATH_ADDR 0x3f0400 +#define ARV4518PW_MADWIFI_ADDR 0xb03f0400 +#define ARV4518PW_MAC_ADDR 0x3f0016 + + ltq_register_gpio_ebu(ARV4518PW_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv4518pw_gpio_leds), arv4518pw_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(arv4518pw_gpio_keys), arv4518pw_gpio_keys); + arv_load_nor(0x400000); + ltq_pci_data.gpio = PCI_GNT2 | PCI_REQ2; + ltq_register_pci(<q_pci_data); + xway_register_dwc(ARV4518PW_USB); + arv_register_ethernet(ARV4518PW_MAC_ADDR); + arv_register_ath5k(ARV4518PW_ATH_ADDR, ARV4518PW_MAC_ADDR); + ltq_register_ath5k(arv_ath5k_eeprom_data, arv_athxk_eeprom_mac); + + gpio_request(ARV4518PW_SWITCH_RESET, "switch"); + gpio_direction_output(ARV4518PW_SWITCH_RESET, 1); + gpio_export(ARV4518PW_SWITCH_RESET, 0); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV4518PW, + "ARV4518PW", + "ARV4518PW - SMC7908A-ISP, Airties WAV-221", + arv4518pw_init); + +static void __init +arv4519pw_init(void) +{ +#define ARV4519PW_EBU 0 +#define ARV4519PW_USB 14 +#define ARV4519PW_RELAY 31 +#define ARV4519PW_SWITCH_RESET 13 +#define ARV4519PW_ATH_ADDR 0x3f0400 +#define ARV4519PW_MAC_ADDR 0x3f0016 + + arv_load_nor(0x400000); + ltq_register_gpio_ebu(ARV4519PW_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv4519pw_gpio_leds), arv4519pw_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(arv4519pw_gpio_keys), arv4519pw_gpio_keys); + ltq_pci_data.gpio = PCI_GNT2 | PCI_REQ1; + ltq_register_pci(<q_pci_data); + xway_register_dwc(ARV4519PW_USB); + arv_register_ethernet(ARV4519PW_MAC_ADDR); + arv_register_ath5k(ARV4519PW_ATH_ADDR, ARV4519PW_MAC_ADDR); + ltq_register_ath5k(arv_ath5k_eeprom_data, arv_athxk_eeprom_mac); + + gpio_request(ARV4519PW_RELAY, "relay"); + gpio_direction_output(ARV4519PW_RELAY, 1); + gpio_export(ARV4519PW_RELAY, 0); + + gpio_request(ARV4519PW_SWITCH_RESET, "switch"); + gpio_set_value(ARV4519PW_SWITCH_RESET, 1); + gpio_export(ARV4519PW_SWITCH_RESET, 0); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV4519PW, + "ARV4519PW", + "ARV4519PW - Vodafone, Pirelli", + arv4519pw_init); + +static void __init +arv4520pw_init(void) +{ +#define ARV4520PW_EBU 0x400 +#define ARV4520PW_USB 28 +#define ARV4520PW_SWITCH_RESET 110 +#define ARV4520PW_MAC_ADDR 0x3f0016 + + ltq_register_gpio_ebu(ARV4520PW_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv4520pw_gpio_leds), arv4520pw_gpio_leds); + arv_load_nor(0x400000); + ltq_register_pci(<q_pci_data); + ltq_register_tapi(); + arv_register_ethernet(ARV4520PW_MAC_ADDR); + xway_register_dwc(ARV4520PW_USB); + + gpio_request(ARV4520PW_SWITCH_RESET, "switch"); + gpio_set_value(ARV4520PW_SWITCH_RESET, 1); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV4520PW, + "ARV4520PW", + "ARV4520PW - Airties WAV-281, Arcor A800", + arv4520pw_init); + +static void __init +arv452Cpw_init(void) +{ +#define ARV452CPW_EBU 0x77f +#define ARV452CPW_USB 28 +#define ARV452CPW_RELAY1 31 +#define ARV452CPW_RELAY2 107 +#define ARV452CPW_SWITCH_RESET 110 +#define ARV452CPW_ATH_ADDR 0x3f0400 +#define ARV452CPW_MADWIFI_ADDR 0xb03f0400 +#define ARV452CPW_MAC_ADDR 0x3f0016 + + ltq_register_gpio_ebu(ARV452CPW_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv452cpw_gpio_leds), arv452cpw_gpio_leds); + arv_load_nor(0x400000); + ltq_register_pci(<q_pci_data); + xway_register_dwc(ARV452CPW_USB); + arv_register_ethernet(ARV452CPW_MAC_ADDR); + arv_register_ath5k(ARV452CPW_ATH_ADDR, ARV452CPW_MAC_ADDR); + ltq_register_ath5k(arv_ath5k_eeprom_data, arv_athxk_eeprom_mac); + + gpio_request(ARV452CPW_SWITCH_RESET, "switch"); + gpio_set_value(ARV452CPW_SWITCH_RESET, 1); + gpio_export(ARV452CPW_SWITCH_RESET, 0); + + gpio_request(ARV452CPW_RELAY1, "relay1"); + gpio_direction_output(ARV452CPW_RELAY1, 1); + gpio_export(ARV452CPW_RELAY1, 0); + + gpio_request(ARV452CPW_RELAY2, "relay2"); + gpio_set_value(ARV452CPW_RELAY2, 1); + gpio_export(ARV452CPW_RELAY2, 0); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV452CPW, + "ARV452CPW", + "ARV452CPW - Arcor A801", + arv452Cpw_init); + +static void __init +arv4525pw_init(void) +{ +#define ARV4525PW_ATH_ADDR 0x3f0400 +#define ARV4525PW_MADWIFI_ADDR 0xb03f0400 +#define ARV4525PW_MAC_ADDR 0x3f0016 + + arv_load_nor(0x400000); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv4525pw_gpio_leds), arv4525pw_gpio_leds); + gpio_request_array(arv4525pw_gpios, ARRAY_SIZE(arv4525pw_gpios)); + gpio_export(ARV4525PW_RELAY, false); + gpio_export(ARV4525PW_PHYRESET, false); + ltq_pci_data.clock = PCI_CLOCK_INT; + ltq_register_pci(<q_pci_data); + arv_register_ath5k(ARV4525PW_ATH_ADDR, ARV4525PW_MADWIFI_ADDR); + ltq_register_ath5k(arv_ath5k_eeprom_data, arv_athxk_eeprom_mac); + ltq_eth_data.mii_mode = PHY_INTERFACE_MODE_MII; + arv_register_ethernet(ARV4525PW_MAC_ADDR); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV4525PW, + "ARV4525PW", + "ARV4525PW - Speedport W502V", + arv4525pw_init); + +static void __init +arv7525pw_init(void) +{ +#define ARV7525P_MAC_ADDR 0x3f0016 + + arv_load_nor(0x400000); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv4525pw_gpio_leds), arv4525pw_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(arv7525pw_gpio_keys), arv7525pw_gpio_keys); + ltq_pci_data.clock = PCI_CLOCK_INT; + ltq_pci_data.gpio = PCI_GNT1 | PCI_EXIN1; + ltq_pci_data.irq[14] = (INT_NUM_IM3_IRL0 + 31); + ltq_register_pci(<q_pci_data); + ltq_eth_data.mii_mode = PHY_INTERFACE_MODE_MII; + ltq_register_rt2x00("RT2860.eeprom"); + ltq_register_tapi(); + arv_register_ethernet(ARV7525P_MAC_ADDR); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV7525PW, + "ARV7525PW", + "ARV7525PW - Speedport W303V", + arv7525pw_init); + +static void __init +arv7518pw_init(void) +{ +#define ARV7518PW_EBU 0x2 +#define ARV7518PW_USB 14 +#define ARV7518PW_SWITCH_RESET 13 +#define ARV7518PW_ATH_ADDR 0x7f0400 +#define ARV7518PW_MAC_ADDR 0x7f0016 + + arv_load_nor(0x800000); + ltq_register_gpio_ebu(ARV7518PW_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv7518pw_gpio_leds), arv7518pw_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(arv7518pw_gpio_keys), arv7518pw_gpio_keys); + ltq_register_pci(<q_pci_data); + ltq_register_tapi(); + xway_register_dwc(ARV7518PW_USB); + arv_register_ethernet(ARV7518PW_MAC_ADDR); + arv_register_ath9k(ARV7518PW_ATH_ADDR, ARV7518PW_MAC_ADDR); + ltq_register_ath9k(arv_ath9k_eeprom_data, arv_athxk_eeprom_mac); + ltq_pci_ath_fixup(14, arv_ath9k_eeprom_data); + + gpio_request(ARV7518PW_SWITCH_RESET, "switch"); + gpio_direction_output(ARV7518PW_SWITCH_RESET, 1); + gpio_export(ARV7518PW_SWITCH_RESET, 0); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV7518PW, + "ARV7518PW", + "ARV7518PW - ASTORIA", + arv7518pw_init); + +static void __init +arv752dpw22_init(void) +{ +#define ARV752DPW22_EBU 0x2 +#define ARV752DPW22_USB 100 +#define ARV752DPW22_RELAY 101 +#define ARV752DPW22_MAC_ADDR 0x7f0016 + + arv_load_nor(0x800000); + ltq_register_gpio_ebu(ARV752DPW22_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv752dpw22_gpio_leds), arv752dpw22_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(arv752dpw22_gpio_keys), arv752dpw22_gpio_keys); + ltq_pci_data.irq[15] = (INT_NUM_IM3_IRL0 + 31); + ltq_pci_data.gpio |= PCI_EXIN1 | PCI_REQ2; + ltq_register_pci(<q_pci_data); + xway_register_dwc(ARV752DPW22_USB); + arv_register_ethernet(ARV752DPW22_MAC_ADDR); + + gpio_request(ARV752DPW22_RELAY, "relay"); + gpio_set_value(ARV752DPW22_RELAY, 1); + gpio_export(ARV752DPW22_RELAY, 0); +} + +MIPS_MACHINE(LANTIQ_MACH_ARV752DPW22, + "ARV752DPW22", + "ARV752DPW22 - Arcor A803", + arv752dpw22_init); + +static void __init +arv752dpw_init(void) +{ +#define ARV752DPW22_EBU 0x2 +#define ARV752DPW22_USB 100 +#define ARV752DPW22_RELAY 101 +#define ARV752DPW22_MAC_ADDR 0x7f0016 + + arv_load_nor(0x800000); + ltq_register_gpio_ebu(ARV752DPW22_EBU); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(arv752dpw22_gpio_leds), arv752dpw22_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, ARRAY_SIZE(arv752dpw22_gpio_keys), arv752dpw22_gpio_keys); + ltq_pci_data.irq[14] = (INT_NUM_IM3_IRL0 + 31); + ltq_pci_data.gpio |= PCI_EXIN1 | PCI_REQ2; + ltq_register_pci(<q_pci_data); + xway_register_dwc(ARV752DPW22_USB); + ltq_register_rt2x00("RT2860.eeprom"); + arv_register_ethernet(ARV752DPW22_MAC_ADDR); + gpio_request(ARV752DPW22_RELAY, "relay"); + gpio_set_value(ARV752DPW22_RELAY, 1); + gpio_export(ARV752DPW22_RELAY, 0); + +} + +MIPS_MACHINE(LANTIQ_MACH_ARV752DPW, + "ARV752DPW", + "ARV752DPW - Arcor A802", + arv752dpw_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-bthomehubv2b.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-bthomehubv2b.c new file mode 100644 index 0000000000..44fe2f4efe --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-bthomehubv2b.c @@ -0,0 +1,542 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2011 Andrej VlaÅ¡ić + * Copyright (C) 2011 Luka Perkov + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../machtypes.h" +//#include "dev-wifi-ath9k.h" +#include "devices.h" +#include "dev-dwc_otg.h" + +#undef USE_BTHH_GPIO_INIT + +// this reads certain data from u-boot if it's there +#define USE_UBOOT_ENV_DATA + +#define UBOOT_ENV_OFFSET 0x040000 +#define UBOOT_ENV_SIZE 0x010000 + +#ifdef NAND_ORGLAYOUT +// this is only here for reference +// definition of NAND flash area +static struct mtd_partition bthomehubv2b_nand_partitions[] = +{ + { + .name = "ART", + .offset = 0x0000000, + .size = 0x0004000, + }, + { + .name = "image1", + .offset = 0x0004000, + .size = 0x0E00000, + }, + { + .name = "unknown1", + .offset = 0x0E04000, + .size = 0x00FC000, + }, + { + .name = "image2", + .offset = 0x0F00000, + .size = 0x0E00000, + }, + { + .name = "unknown2", + .offset = 0x1D00000, + .size = 0x0300000, + }, + +}; +#endif + +#ifdef NAND_KEEPOPENRG +// this allows both firmwares to co-exist + +static struct mtd_partition bthomehubv2b_nand_partitions[] = +{ + { + .name = "art", + .offset = 0x0000000, + .size = 0x0004000, + }, + { + .name = "Image1", + .offset = 0x0004000, + .size = 0x0E00000, + }, + { + .name = "linux", + .offset = 0x0E04000, + .size = 0x11fC000, + }, + { + .name = "wholeflash", + .offset = 0x0000000, + .size = 0x2000000, + }, + +}; +#endif + +// this gives more jffs2 by overwriting openrg + +static struct mtd_partition bthomehubv2b_nand_partitions[] = +{ + { + .name = "art", + .offset = 0x0000000, + .size = 0x0004000, + }, + { + .name = "linux", + .offset = 0x0004000, + .size = 0x1ffC000, + }, + { + .name = "wholeflash", + .offset = 0x0000000, + .size = 0x2000000, + }, + +}; + +extern void __init xway_register_nand(struct mtd_partition *parts, int count); + +// end BTHH_USE_NAND + +static struct gpio_led +bthomehubv2b_gpio_leds[] __initdata = { + + { .name = "soc:orange:upgrading", .gpio = 213, }, + { .name = "soc:orange:phone", .gpio = 214, }, + { .name = "soc:blue:phone", .gpio = 215, }, + { .name = "soc:orange:wireless", .gpio = 216, }, + { .name = "soc:blue:wireless", .gpio = 217, }, + { .name = "soc:red:broadband", .gpio = 218, }, + { .name = "soc:orange:broadband", .gpio = 219, }, + { .name = "soc:blue:broadband", .gpio = 220, }, + { .name = "soc:red:power", .gpio = 221, }, + { .name = "soc:orange:power", .gpio = 222, }, + { .name = "soc:blue:power", .gpio = 223, }, +}; + +static struct gpio_keys_button +bthomehubv2b_gpio_keys[] __initdata = { + { + .desc = "restart", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 2, + .active_low = 1, + }, + { + .desc = "findhandset", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 15, + .active_low = 1, + }, + { + .desc = "wps", + .type = EV_KEY, + .code = BTN_2, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 22, + .active_low = 1, + }, +}; + +// definition of NOR flash area - as per original. +static struct mtd_partition bthomehubv2b_partitions[] = +{ + { + .name = "uboot", + .offset = 0x000000, + .size = 0x040000, + }, + { + .name = "uboot_env", + .offset = UBOOT_ENV_OFFSET, + .size = UBOOT_ENV_SIZE, + }, + { + .name = "rg_conf_1", + .offset = 0x050000, + .size = 0x010000, + }, + { + .name = "rg_conf_2", + .offset = 0x060000, + .size = 0x010000, + }, + { + .name = "rg_conf_factory", + .offset = 0x070000, + .size = 0x010000, + }, +}; + + +/* nor flash */ +/* bt homehubv2b has a very small nor flash */ +/* so make it look for a small one, else we get a lot of alias chips identified - */ +/* not a bug problem, but fills the logs. */ +static struct resource bthhv2b_nor_resource = + MEM_RES("nor", LTQ_FLASH_START, 0x80000); + +static struct platform_device ltq_nor = { + .name = "ltq_nor", + .resource = &bthhv2b_nor_resource, + .num_resources = 1, +}; + +static void __init bthhv2b_register_nor(struct physmap_flash_data *data) +{ + ltq_nor.dev.platform_data = data; + platform_device_register(<q_nor); +} + +static struct physmap_flash_data bthomehubv2b_flash_data = { + .nr_parts = ARRAY_SIZE(bthomehubv2b_partitions), + .parts = bthomehubv2b_partitions, +}; + + + + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { [14] = INT_NUM_IM0_IRL0 + 22, }, +}; + + + + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_MII, +}; + + + + +static char __init *get_uboot_env_var(char *haystack, int haystack_len, char *needle, int needle_len) { + int i; + for (i = 0; i <= haystack_len - needle_len; i++) { + if (memcmp(haystack + i, needle, needle_len) == 0) { + return haystack + i + needle_len; + } + } + return NULL; +} + +/* + * bthomehubv2b_parse_hex_* are not uniq. in arm/orion there are also duplicates: + * dns323_parse_hex_* + * TODO: one day write a patch for this :) + */ +static int __init bthomehubv2b_parse_hex_nibble(char n) { + if (n >= '0' && n <= '9') + return n - '0'; + + if (n >= 'A' && n <= 'F') + return n - 'A' + 10; + + if (n >= 'a' && n <= 'f') + return n - 'a' + 10; + + return -1; +} + +static int __init bthomehubv2b_parse_hex_byte(const char *b) { + int hi; + int lo; + + hi = bthomehubv2b_parse_hex_nibble(b[0]); + lo = bthomehubv2b_parse_hex_nibble(b[1]); + + if (hi < 0 || lo < 0) + return -1; + + return (hi << 4) | lo; +} + +static int __init bthomehubv2b_register_ethernet(void) { + u_int8_t addr[6]; + int i; + char *mac = "01:02:03:04:05:06"; + int gotmac = 0; + char *boardid = "BTHHV2B"; + int gotboardid = 0; + + char *uboot_env_page; + uboot_env_page = ioremap(LTQ_FLASH_START + UBOOT_ENV_OFFSET, UBOOT_ENV_SIZE); + if (uboot_env_page) + { + char *Data = NULL; + Data = get_uboot_env_var(uboot_env_page, UBOOT_ENV_SIZE, "\0ethaddr=", 9); + if (Data) + { + mac = Data; + } + + Data = get_uboot_env_var(uboot_env_page, UBOOT_ENV_SIZE, "\0boardid=", 9); + + if (Data) + boardid = Data; + } + else + { + printk("bthomehubv2b: Failed to get uboot_env_page"); + } + + if (!mac) { + goto error_fail; + } + + if (!boardid) { + goto error_fail; + } + + /* Sanity check the string we're looking at */ + for (i = 0; i < 5; i++) { + if (*(mac + (i * 3) + 2) != ':') { + goto error_fail; + } + } + + for (i = 0; i < 6; i++) { + int byte; + byte = bthomehubv2b_parse_hex_byte(mac + (i * 3)); + if (byte < 0) { + goto error_fail; + } + addr[i] = byte; + } + + if (gotmac) + printk("bthomehubv2b: Found ethernet MAC address: "); + else + printk("bthomehubv2b: using default MAC (pls set ethaddr in u-boot): "); + + for (i = 0; i < 6; i++) + printk("%.2x%s", addr[i], (i < 5) ? ":" : ".\n"); + + memcpy(<q_eth_data.mac.sa_data, addr, 6); + ltq_register_etop(<q_eth_data); + + //memcpy(&bthomehubv2b_ath5k_eeprom_mac, addr, 6); + //bthomehubv2b_ath5k_eeprom_mac[5]++; + + if (gotboardid) + printk("bthomehubv2b: Board id is %s.", boardid); + else + printk("bthomehubv2b: Default Board id is %s.", boardid); + + if (strncmp(boardid, "BTHHV2B", 7) == 0) { + // read in dev-wifi-ath9k + //memcpy(&bthomehubv2b_ath5k_eeprom_data, sx763_eeprom_data, ATH5K_PLAT_EEP_MAX_WORDS); + } + else { + printk("bthomehubv2b: Board id is unknown, fix uboot_env data."); + } + + + // should not unmap while we are using the ram? + if (uboot_env_page) + iounmap(uboot_env_page); + + return 0; + +error_fail: + if (uboot_env_page) + iounmap(uboot_env_page); + return -EINVAL; +} + + +#define PORTA2_HW_PASS1 0 +#define PORTA2_HW_PASS1_SC14480 1 +#define PORTA2_HW_PASS2 2 + +int porta2_hw_revision = -1; +EXPORT_SYMBOL(porta2_hw_revision); + +#define LTQ_GPIO_OUT 0x00 +#define LTQ_GPIO_IN 0x04 +#define LTQ_GPIO_DIR 0x08 +#define LTQ_GPIO_ALTSEL0 0x0C +#define LTQ_GPIO_ALTSEL1 0x10 +#define LTQ_GPIO_OD 0x14 +#define LTQ_GPIO_PUDSEL 0x1C +#define LTQ_GPIO_PUDEN 0x20 + +#ifdef USE_BTHH_GPIO_INIT +static void bthomehubv2b_board_prom_init(void) +{ + int revision = 0; + unsigned int gpio = 0; + void __iomem *mem = ioremap(LTQ_GPIO0_BASE_ADDR, LTQ_GPIO_SIZE*2); + +#define DANUBE_GPIO_P0_OUT (unsigned short *)(mem + LTQ_GPIO_OUT) +#define DANUBE_GPIO_P0_IN (unsigned short *)(mem + LTQ_GPIO_IN) +#define DANUBE_GPIO_P0_DIR (unsigned short *)(mem + LTQ_GPIO_DIR) +#define DANUBE_GPIO_P0_ALTSEL0 (unsigned short *)(mem + LTQ_GPIO_ALTSEL0) +#define DANUBE_GPIO_P0_ALTSEL1 (unsigned short *)(mem + LTQ_GPIO_ALTSEL1) + +#define DANUBE_GPIO_P1_OUT (unsigned short *)(mem + LTQ_GPIO_SIZE + LTQ_GPIO_OUT) +#define DANUBE_GPIO_P1_IN (unsigned short *)(mem + LTQ_GPIO_SIZE + LTQ_GPIO_IN) +#define DANUBE_GPIO_P1_DIR (unsigned short *)(mem + LTQ_GPIO_SIZE + LTQ_GPIO_DIR) +#define DANUBE_GPIO_P1_ALTSEL0 (unsigned short *)(mem + LTQ_GPIO_SIZE + LTQ_GPIO_ALTSEL0) +#define DANUBE_GPIO_P1_ALTSEL1 (unsigned short *)(mem + LTQ_GPIO_SIZE + LTQ_GPIO_ALTSEL1) +#define DANUBE_GPIO_P1_OD (unsigned short *)(mem + LTQ_GPIO_SIZE + LTQ_GPIO_OD) + + printk("About to sense board using GPIOs at %8.8X\n", (unsigned int)mem); + + + /* First detect HW revision of the board. For that we need to set the GPIO + * lines according to table 7.2.1/7.2.2 in HSI */ + *DANUBE_GPIO_P0_OUT = 0x0200; + *DANUBE_GPIO_P0_DIR = 0x2610; + *DANUBE_GPIO_P0_ALTSEL0 = 0x7812; + *DANUBE_GPIO_P0_ALTSEL1 = 0x0000; + + *DANUBE_GPIO_P1_OUT = 0x7008; + *DANUBE_GPIO_P1_DIR = 0xF3AE; + *DANUBE_GPIO_P1_ALTSEL0 = 0x83A7; + *DANUBE_GPIO_P1_ALTSEL1 = 0x0400; + + gpio = (*DANUBE_GPIO_P0_IN & 0xFFFF) | + ((*DANUBE_GPIO_P1_IN & 0xFFFF) << 16); + + revision |= (gpio & (1 << 27)) ? (1 << 0) : 0; + revision |= (gpio & (1 << 20)) ? (1 << 1) : 0; + revision |= (gpio & (1 << 8)) ? (1 << 2) : 0; + revision |= (gpio & (1 << 6)) ? (1 << 3) : 0; + revision |= (gpio & (1 << 5)) ? (1 << 4) : 0; + revision |= (gpio & (1 << 0)) ? (1 << 5) : 0; + + porta2_hw_revision = revision; + printk("PORTA2: detected HW revision %d\n", revision); + + /* Set GPIO lines according to HW revision. */ + /* !!! Note that we are setting SPI_CS5 (GPIO 9) to be GPIO out with value + * of HIGH since the FXO does not use the SPI CS mechanism, it does it + * manually by controlling the GPIO line. We need the CS line to be disabled + * (HIGH) until needed since it will intefere with other devices on the SPI + * bus. */ + *DANUBE_GPIO_P0_OUT = 0x0200; + /* + * During the manufacturing process a different machine takes over uart0 + * so set it as input (so it wouldn't drive the line) + */ +#define cCONFIG_SHC_BT_MFG_TEST 0 + *DANUBE_GPIO_P0_DIR = 0x2671 | (cCONFIG_SHC_BT_MFG_TEST ? 0 : (1 << 12)); + + if (revision == PORTA2_HW_PASS1_SC14480 || revision == PORTA2_HW_PASS2) + *DANUBE_GPIO_P0_ALTSEL0 = 0x7873; + else + *DANUBE_GPIO_P0_ALTSEL0 = 0x3873; + + *DANUBE_GPIO_P0_ALTSEL1 = 0x0001; + + + //################################################################################### + // Register values before patch + // P1_ALTSEL0 = 0x83A7 + // P1_ALTSEL1 = 0x0400 + // P1_OU T = 0x7008 + // P1_DIR = 0xF3AE + // P1_OD = 0xE3Fc + printk("\nApplying Patch for CPU1 IRQ Issue\n"); + *DANUBE_GPIO_P1_ALTSEL0 &= ~(1<<12); // switch P1.12 (GPIO28) to GPIO functionality + *DANUBE_GPIO_P1_ALTSEL1 &= ~(1<<12); // switch P1.12 (GPIO28) to GPIO functionality + *DANUBE_GPIO_P1_OUT &= ~(1<<12); // set P1.12 (GPIO28) to 0 + *DANUBE_GPIO_P1_DIR |= (1<<12); // configure P1.12 (GPIO28) as output + *DANUBE_GPIO_P1_OD |= (1<<12); // activate Push/Pull mode + udelay(100); // wait a little bit (100us) + *DANUBE_GPIO_P1_OD &= ~(1<<12); // switch back from Push/Pull to Open Drain + // important: before! setting output to 1 (3,3V) the mode must be switched + // back to Open Drain because the reset pin of the SC14488 is internally + // pulled to 1,8V + *DANUBE_GPIO_P1_OUT |= (1<<12); // set output P1.12 (GPIO28) to 1 + // Register values after patch, should be the same as before + // P1_ALTSEL0 = 0x83A7 + // P1_ALTSEL1 = 0x0400 + // P1_OUT = 0x7008 + // P1_DIR = 0xF3AE + // P1_OD = 0xE3Fc + //################################################################################### + + + *DANUBE_GPIO_P1_OUT = 0x7008; + *DANUBE_GPIO_P1_DIR = 0xEBAE | (revision == PORTA2_HW_PASS2 ? 0x1000 : 0); + *DANUBE_GPIO_P1_ALTSEL0 = 0x8BA7; + *DANUBE_GPIO_P1_ALTSEL1 = 0x0400; + + iounmap(mem); +} +#endif +static void __init bthomehubv2b_init(void) { +#define bthomehubv2b_USB 13 + + // read the board version +#ifdef USE_BTHH_GPIO_INIT + bthomehubv2b_board_prom_init(); +#endif + + // register extra GPPOs used by LEDs as GPO 0x200+ + ltq_register_gpio_stp(); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(bthomehubv2b_gpio_leds), bthomehubv2b_gpio_leds); + bthhv2b_register_nor(&bthomehubv2b_flash_data); + xway_register_nand(bthomehubv2b_nand_partitions, ARRAY_SIZE(bthomehubv2b_nand_partitions)); + ltq_register_pci(<q_pci_data); + ltq_register_tapi(); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, ARRAY_SIZE(bthomehubv2b_gpio_keys), bthomehubv2b_gpio_keys); +// ltq_register_ath9k(); + xway_register_dwc(bthomehubv2b_USB); + bthomehubv2b_register_ethernet(); + +} + +MIPS_MACHINE(LANTIQ_MACH_BTHOMEHUBV2BOPENRG, + "BTHOMEHUBV2BOPENRG", + "BTHOMEHUBV2B - BT Homehub V2.0 Type B with OpenRG image retained", + bthomehubv2b_init); + +MIPS_MACHINE(LANTIQ_MACH_BTHOMEHUBV2B, + "BTHOMEHUBV2B", + "BTHOMEHUBV2B - BT Homehub V2.0 Type B", + bthomehubv2b_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-easy50601.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-easy50601.c new file mode 100644 index 0000000000..a5a01057a3 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-easy50601.c @@ -0,0 +1,57 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "../machtypes.h" +#include "devices.h" + +static struct mtd_partition easy50601_partitions[] = { + { + .name = "uboot", + .offset = 0x0, + .size = 0x10000, + }, + { + .name = "uboot_env", + .offset = 0x10000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x20000, + .size = 0x3d0000, + }, +}; + +static struct physmap_flash_data easy50601_flash_data = { + .nr_parts = ARRAY_SIZE(easy50601_partitions), + .parts = easy50601_partitions, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = -1, /* use EPHY */ +}; + +static void __init easy50601_init(void) +{ + ltq_register_nor(&easy50601_flash_data); + ltq_register_etop(<q_eth_data); +} + +MIPS_MACHINE(LTQ_MACH_EASY50601, + "EASY50601", + "EASY50601 Eval Board", + easy50601_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-easy50712.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-easy50712.c new file mode 100644 index 0000000000..2fddfca6db --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-easy50712.c @@ -0,0 +1,70 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../machtypes.h" +#include "devices.h" + +static struct mtd_partition easy50712_partitions[] = { + { + .name = "uboot", + .offset = 0x0, + .size = 0x10000, + }, + { + .name = "uboot_env", + .offset = 0x10000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x20000, + .size = 0x3d0000, + }, +}; + +static struct physmap_flash_data easy50712_flash_data = { + .nr_parts = ARRAY_SIZE(easy50712_partitions), + .parts = easy50712_partitions, +}; + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_MII, +}; + +static void __init easy50712_init(void) +{ + ltq_register_gpio_stp(); + ltq_register_nor(&easy50712_flash_data); + ltq_register_pci(<q_pci_data); + ltq_register_etop(<q_eth_data); + ltq_register_tapi(); +} + +MIPS_MACHINE(LTQ_MACH_EASY50712, + "EASY50712", + "EASY50712 Eval Board", + easy50712_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-fritz_ar9.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-fritz_ar9.c new file mode 100644 index 0000000000..503e4bebe3 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-fritz_ar9.c @@ -0,0 +1,114 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../machtypes.h" +#include "devices.h" +#include "dev-ifxhcd.h" +#include "dev-gpio-leds.h" +#include "dev-gpio-buttons.h" + +static struct mtd_partition fritz7320_partitions[] = { + { + .name = "urlader", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "linux", + .offset = 0x20000, + .size = 0xf60000, + }, + { + .name = "tffs (1)", + .offset = 0xf80000, + .size = 0x40000, + }, + { + .name = "tffs (2)", + .offset = 0xfc0000, + .size = 0x40000, + }, +}; + +static struct physmap_flash_data fritz7320_flash_data = { + .nr_parts = ARRAY_SIZE(fritz7320_partitions), + .parts = fritz7320_partitions, +}; + +static struct gpio_led +fritz7320_gpio_leds[] __initdata = { + { .name = "soc:green:power", .gpio = 44, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:internet", .gpio = 47, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:dect", .gpio = 38, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:wlan", .gpio = 37, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:dual1", .gpio = 35, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:dual2", .gpio = 45, .active_low = 1, .default_trigger = "default-on" }, +}; + +static struct gpio_keys_button +fritz7320_gpio_keys[] __initdata = { + { + .desc = "wifi", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 1, + .active_low = 1, + }, + { + .desc = "dect", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 2, + .active_low = 1, + }, +}; + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_RMII, +}; + +static int usb_pins[2] = { 50, 51 }; + +static void __init fritz7320_init(void) +{ + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(fritz7320_gpio_keys), fritz7320_gpio_keys); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(fritz7320_gpio_leds), fritz7320_gpio_leds); + ltq_register_pci(<q_pci_data); + ltq_register_etop(<q_eth_data); + ltq_register_nor(&fritz7320_flash_data); + xway_register_hcd(usb_pins); +} + +MIPS_MACHINE(LANTIQ_MACH_FRITZ7320, + "FRITZ7320", + "FRITZ!BOX 7320", + fritz7320_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-fritz_vr9.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-fritz_vr9.c new file mode 100644 index 0000000000..4a38988380 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-fritz_vr9.c @@ -0,0 +1,163 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../machtypes.h" +#include "devices.h" +#include "dev-ifxhcd.h" +#include "dev-gpio-leds.h" +#include "dev-gpio-buttons.h" + +static struct mtd_partition fritz3370_partitions[] = { + { + .name = "linux", + .offset = 0x0, + .size = 0x400000, + }, + { + .name = "filesystem", + .offset = 0x400000, + .size = 0x3000000, + }, + { + .name = "reserved-kernel", + .offset = 0x3400000, + .size = 0x400000, + }, + { + .name = "reserved", + .offset = 0x3800000, + .size = 0x3000000, + }, + { + .name = "config", + .offset = 0x6800000, + .size = 0x200000, + }, + { + .name = "nand-filesystem", + .offset = 0x6a00000, + .size = 0x1600000, + }, +}; + +static struct mtd_partition spi_flash_partitions[] = { + { + .name = "urlader", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "tffs", + .offset = 0x20000, + .size = 0x10000, + }, + { + .name = "tffs", + .offset = 0x30000, + .size = 0x10000, + }, +}; + +static struct gpio_led +fritz3370_gpio_leds[] __initdata = { + { .name = "soc:green:1", .gpio = 32, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:2", .gpio = 33, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:3", .gpio = 34, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:4", .gpio = 35, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:5", .gpio = 36, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:green:6", .gpio = 47, .active_low = 1, .default_trigger = "default-on" }, +}; + +static struct gpio_keys_button +fritz3370_gpio_keys[] __initdata = { + { + .desc = "wifi", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 29, + .active_low = 1, + }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_RMII, +}; + +static int usb_pins[2] = { 5, 14 }; + +#define SPI_GPIO_MRST 16 +#define SPI_GPIO_MTSR 17 +#define SPI_GPIO_CLK 18 +#define SPI_GPIO_CS0 10 + +static struct spi_gpio_platform_data spi_gpio_data = { + .sck = SPI_GPIO_CLK, + .mosi = SPI_GPIO_MTSR, + .miso = SPI_GPIO_MRST, + .num_chipselect = 2, +}; + +static struct platform_device spi_gpio_device = { + .name = "spi_gpio", + .dev.platform_data = &spi_gpio_data, +}; + +static struct flash_platform_data spi_flash_data = { + .name = "SPL", + .parts = spi_flash_partitions, + .nr_parts = ARRAY_SIZE(spi_flash_partitions), +}; + +static struct spi_board_info spi_flash __initdata = { + .modalias = "m25p80", + .bus_num = 0, + .chip_select = 0, + .max_speed_hz = 10 * 1000 * 1000, + .mode = SPI_MODE_3, + .chip_select = 0, + .controller_data = (void *) SPI_GPIO_CS0, + .platform_data = &spi_flash_data +}; + +static void __init +spi_gpio_init(void) +{ + spi_register_board_info(&spi_flash, 1); + platform_device_register(&spi_gpio_device); +} + +static void __init fritz3370_init(void) +{ + spi_gpio_init(); + platform_device_register_simple("pcie-xway", 0, NULL, 0); + xway_register_nand(fritz3370_partitions, ARRAY_SIZE(fritz3370_partitions)); + xway_register_hcd(usb_pins); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(fritz3370_gpio_leds), fritz3370_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, + ARRAY_SIZE(fritz3370_gpio_keys), fritz3370_gpio_keys); + ltq_register_vrx200(<q_eth_data); +} + +MIPS_MACHINE(LANTIQ_MACH_FRITZ3370, + "FRITZ3370", + "FRITZ!BOX 3370", + fritz3370_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-gigasx76x.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-gigasx76x.c new file mode 100644 index 0000000000..c7a2de5017 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-gigasx76x.c @@ -0,0 +1,166 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2011 Andrej VlaÅ¡ić + * Copyright (C) 2011 Luka Perkov + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "../machtypes.h" +#include "dev-wifi-athxk.h" +#include "devices.h" +#include "dev-dwc_otg.h" + +#include "mach-gigasx76x.h" + +static u8 ltq_ethaddr[6] = { 0 }; + +static int __init setup_ethaddr(char *str) +{ + if (!mac_pton(str, ltq_ethaddr)) + memset(ltq_ethaddr, 0, 6); + return 0; +} +__setup("ethaddr=", setup_ethaddr); + + +enum { + UNKNOWN = 0, + SX761, + SX762, + SX763, +}; +static u8 board = SX763; + +static int __init setup_board(char *str) +{ + if (!strcmp(str, "sx761")) + board = SX761; + else if (!strcmp(str, "sx762")) + board = SX762; + else if (!strcmp(str, "sx763")) + board = SX763; + else + board = UNKNOWN; + return 0; +} +__setup("board=", setup_board); + +static struct mtd_partition gigasx76x_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x10000, + }, + { + .name = "uboot_env", + .offset = 0x10000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x20000, + .size = 0x7e0000, + }, +}; + +static struct gpio_led +gigasx76x_gpio_leds[] __initdata = { + { .name = "soc:green:voip", .gpio = 216, }, + { .name = "soc:green:adsl", .gpio = 217, }, + { .name = "soc:green:usb", .gpio = 218, }, + { .name = "soc:green:wifi", .gpio = 219, }, + { .name = "soc:green:phone2", .gpio = 220, }, + { .name = "soc:green:phone1", .gpio = 221, }, + { .name = "soc:green:line", .gpio = 222, }, + { .name = "soc:green:online", .gpio = 223, }, +}; + +static struct gpio_keys_button +gigasx76x_gpio_keys[] __initdata = { + { + .desc = "wps", + .type = EV_KEY, + .code = KEY_WPS_BUTTON, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 22, + .active_low = 1, + }, + { + .desc = "reset", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 14, + .active_low = 0, + }, +}; + +static struct physmap_flash_data gigasx76x_flash_data = { + .nr_parts = ARRAY_SIZE(gigasx76x_partitions), + .parts = gigasx76x_partitions, +}; + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { [14] = INT_NUM_IM0_IRL0 + 22, }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_MII, +}; + +static void __init gigasx76x_init(void) +{ +#define GIGASX76X_USB 29 + + ltq_register_gpio_stp(); + ltq_register_nor(&gigasx76x_flash_data); + ltq_register_pci(<q_pci_data); + ltq_register_tapi(); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(gigasx76x_gpio_leds), gigasx76x_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, ARRAY_SIZE(gigasx76x_gpio_keys), gigasx76x_gpio_keys); + xway_register_dwc(GIGASX76X_USB); + + if (!is_valid_ether_addr(ltq_ethaddr)) + random_ether_addr(ltq_ethaddr); + + memcpy(<q_eth_data.mac.sa_data, ltq_ethaddr, 6); + ltq_register_etop(<q_eth_data); + if (board == SX762) + ltq_register_ath5k(sx762_eeprom_data, ltq_ethaddr); + else + ltq_register_ath5k(sx763_eeprom_data, ltq_ethaddr); +} + +MIPS_MACHINE(LANTIQ_MACH_GIGASX76X, + "GIGASX76X", + "GIGASX76X - Gigaset SX761,SX762,SX763", + gigasx76x_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-gigasx76x.h b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-gigasx76x.h new file mode 100644 index 0000000000..c25a679978 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-gigasx76x.h @@ -0,0 +1,208 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2011 Andrej VlaÅ¡ić + * Copyright (C) 2011 Luka Perkov + * + */ + +#ifndef _MACH_GIGASX76X_H__ +#define _MACH_GIGASX76X_H__ + +static u16 sx763_eeprom_data[ATH5K_PLAT_EEP_MAX_WORDS]= +{ +0x0013,0x168c,0x0200,0x0001,0x0000,0x5001,0x0000,0x2051,0x2051,0x1c0a,0x0100, +0x0000,0x01c2,0x0002,0xc606,0x0001,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xf165,0x7fbe,0x0003,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5aa5,0x0000,0x0000,0x0313,0x4943, +0x2053,0x7104,0x1202,0x0400,0x0306,0x0001,0x0000,0x0500,0x410e,0x39b1,0x1eb5, +0x4e2d,0x3056,0xffff,0xe902,0x0700,0x0106,0x0000,0x0100,0x1500,0x0752,0x4101, +0x6874,0x7265,0x736f,0x4320,0x6d6f,0x756d,0x696e,0x6163,0x6974,0x6e6f,0x2c73, +0x4920,0x636e,0x002e,0x5241,0x3035,0x3130,0x302d,0x3030,0x2d30,0x3030,0x3030, +0x5700,0x7269,0x6c65,0x7365,0x2073,0x414c,0x204e,0x6552,0x6566,0x6572,0x636e, +0x2065,0x6143,0x6472,0x3000,0x0030,0x00ff,0x2100,0x0602,0x2201,0x0205,0x8d80, +0x005b,0x0522,0x4002,0x8954,0x2200,0x0205,0x1b00,0x00b7,0x0522,0x8002,0x12a8, +0x2201,0x0205,0x3600,0x016e,0x0522,0x0002,0x2551,0x2202,0x0205,0x6c00,0x02dc, +0x0522,0x8002,0x37f9,0x2203,0x0205,0xa200,0x044a,0x0222,0x0803,0x0822,0x0604, +0x0300,0xbe7f,0x65f1,0x0222,0x0105,0x00ff,0x0000,0x0000,0x0000,0x0000,0x0000, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0x0037,0x971f,0x5003,0x9a66,0x0001,0x81c4,0x016a, +0x02ff,0x84ff,0x15a3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x2d2c,0x0000,0x0000,0x0000,0x0000,0xe028,0xa492,0x1c00, +0x000e,0xb8ca,0x0013,0x0000,0x0000,0x6b4b,0xc059,0x1571,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x2370,0x00a5,0x9618,0x419a,0x68a2,0xda35,0x001c,0x0007,0xb0ff,0x01b5,0x0000, +0x0000,0xff70,0x19ff,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x3170,0x00a5,0x9618,0x419a,0x68a2,0xda35, +0x001c,0x000e,0xb0ff,0x21b5,0x0000,0x2fd8,0xff70,0x1226,0x19ff,0x07be,0x6201, +0x032e,0x0587,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1112, +0x1441,0x4231,0x3234,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x8000,0x0000,0x0000,0x0000,0x0000,0x8000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4d31,0x7f54,0x3c93,0x1205,0x1931, +0x492d,0x7f50,0x3c93,0x0e01,0x192d,0x0070,0x0000,0x8140,0x724b,0x2ba9,0x3a09, +0x99d9,0x1949,0x0070,0x0000,0x80e0,0x624a,0x2af8,0x35c7,0x9d47,0x1938,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x7082,0x0820,0xb882,0x0820,0x7092,0x28a0,0x8992, +0x28a0,0xa292,0x28a0,0x70a2,0xa7ac,0x0000,0x0000,0x2464,0x6424,0x0000,0x0000, +0x70a2,0xa7ac,0x0000,0x0000,0x2464,0x6424,0x0000,0x0000,0x8989,0x0000,0x0000, +0x0000,0x2424,0x0000,0x0000,0x0000,0x7075,0xa2ac,0xb800,0x0000,0x2464,0x2424, +0x2400,0x0000,0x7075,0xa2ac,0x0000,0x0000,0x2464,0x2424,0x0000,0x0000,0x7075, +0xa7ac,0x0000,0x0000,0x2464,0x6424,0x0000,0x0000,0x7075,0xa7ac,0x0000,0x0000, +0x2464,0x6424,0x0000,0x0000,0x8989,0x0000,0x0000,0x0000,0x2424,0x0000,0x0000, +0x0000,0x0000,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff}; + +static u16 sx762_eeprom_data[ATH5K_PLAT_EEP_MAX_WORDS]= +{ +0x001a,0x168c,0x0200,0x0001,0x0000,0x5001,0x0000,0x2051,0x2051,0x1c0a,0x0100, +0x0000,0x01c2,0x0002,0xc606,0x0001,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0xf165,0x7fbe,0x0003,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x5aa5,0x0000,0x0000,0x0313,0x4943, +0x2053,0x7104,0x1202,0x0400,0x0306,0x0001,0x0000,0x0500,0x410e,0x39b1,0x1eb5, +0x4e2d,0x3056,0xffff,0xe902,0x0700,0x0106,0x0000,0x0100,0x1500,0x0752,0x4101, +0x6874,0x7265,0x736f,0x4320,0x6d6f,0x756d,0x696e,0x6163,0x6974,0x6e6f,0x2c73, +0x4920,0x636e,0x002e,0x5241,0x3035,0x3130,0x302d,0x3030,0x2d30,0x3030,0x3030, +0x5700,0x7269,0x6c65,0x7365,0x2073,0x414c,0x204e,0x6552,0x6566,0x6572,0x636e, +0x2065,0x6143,0x6472,0x3000,0x0030,0x00ff,0x2100,0x0602,0x2201,0x0205,0x8d80, +0x005b,0x0522,0x4002,0x8954,0x2200,0x0205,0x1b00,0x00b7,0x0522,0x8002,0x12a8, +0x2201,0x0205,0x3600,0x016e,0x0522,0x0002,0x2551,0x2202,0x0205,0x6c00,0x02dc, +0x0522,0x8002,0x37f9,0x2203,0x0205,0xa200,0x044a,0x0222,0x0803,0x0822,0x0604, +0x0300,0xbe7f,0x65f1,0x0222,0x0105,0x00ff,0x0000,0x0000,0x0000,0x0000,0x0000, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0x0037,0x6aaa,0x5003,0x9a66,0x0001,0x81c4,0x016a, +0x02ff,0x84ff,0x15a3,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x2d2c,0x0000,0x0000,0x0000,0x0000,0xe028,0xa492,0x1c00, +0x000e,0xb8ca,0x0013,0x0000,0x0000,0x6b4b,0xc059,0x1571,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x2370,0x00a5,0x9618,0x419a,0x68a2,0xda35,0x001c,0x0007,0xb0ff,0x01b5,0x0000, +0x0000,0xff70,0x19ff,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x3170,0x00a5,0x9618,0x419a,0x68a2,0xda35, +0x001c,0x000e,0xb0ff,0x21b5,0x0000,0x2fd8,0xff70,0x1226,0x19ff,0x07fa,0x6201, +0x032e,0x0587,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x1112, +0x1441,0x4231,0x3234,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x8000,0x0000,0x0000,0x0000,0x0000,0x8000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x4d31,0x7f54,0x3c93,0x1205,0x1931, +0x492d,0x7f50,0x3c93,0x0e01,0x192d,0x0070,0x0000,0x8180,0x724d,0xab59,0x3a08, +0xdd79,0x2559,0x0070,0x0000,0x81a0,0x6e4d,0x2b99,0x3a09,0x9989,0x2157,0x0000, +0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000,0x0000, +0x0000,0x0000,0x0000,0x0000,0x7092,0x4924,0xb892,0x4924,0x7092,0x289e,0x8992, +0x289e,0xa292,0x289e,0x70a2,0xa7ac,0x0000,0x0000,0x2462,0x5e13,0x0000,0x0000, +0x70a2,0xa7ac,0x0000,0x0000,0x1e5c,0x5713,0x0000,0x0000,0x8989,0x0000,0x0000, +0x0000,0x2424,0x0000,0x0000,0x0000,0x7075,0xa2ac,0xb800,0x0000,0x2868,0x2828, +0x2800,0x0000,0x7075,0xa2ac,0x0000,0x0000,0x2868,0x2828,0x0000,0x0000,0x7075, +0xac00,0x0000,0x0000,0x2161,0x2100,0x0000,0x0000,0x7075,0xac00,0x0000,0x0000, +0x1b5b,0x1b00,0x0000,0x0000,0x8989,0x0000,0x0000,0x0000,0x2121,0x0000,0x0000, +0x0000,0x0000,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff,0xffff, +0xffff,0xffff}; + +#endif diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-netgear.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-netgear.c new file mode 100644 index 0000000000..f30478c0fd --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-netgear.c @@ -0,0 +1,88 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../machtypes.h" +#include "devices.h" +#include "dev-dwc_otg.h" + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_MII, +}; + +static struct mtd_partition easy98000_nor_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x40000, + }, + { + .name = "uboot_env", + .offset = 0x40000, + .size = 0x40000, /* 2 sectors for redundant env. */ + }, + { + .name = "linux", + .offset = 0x80000, + .size = 0xF80000, /* map only 16 MiB */ + }, +}; + +static struct flash_platform_data easy98000_spi_flash_platform_data = { + .name = "sflash", + .parts = easy98000_nor_partitions, + .nr_parts = ARRAY_SIZE(easy98000_nor_partitions) +}; + +static struct spi_board_info spi_info __initdata = { + .modalias = "m25p80", + .bus_num = 0, + .chip_select = 3, + .max_speed_hz = 10 * 1000 * 1000, + .mode = SPI_MODE_3, + .platform_data = &easy98000_spi_flash_platform_data +}; + +struct ltq_spi_platform_data ltq_spi_data = { + .num_chipselect = 4, +}; + +static void __init dgn3500_init(void) +{ + ltq_register_pci(<q_pci_data); + ltq_register_etop(<q_eth_data); + ltq_register_spi(<q_spi_data, &spi_info, 1); + /* The usb power is always enabled, protected by a fuse */ + xway_register_dwc(-1); +} + +MIPS_MACHINE(LANTIQ_MACH_DGN3500B, + "DGN3500B", + "Netgear DGN3500B", + dgn3500_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-p2601hnf1.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-p2601hnf1.c new file mode 100644 index 0000000000..98c118135e --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-p2601hnf1.c @@ -0,0 +1,106 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "../machtypes.h" +#include "devices.h" +#include "../dev-gpio-leds.h" +#include "dev-dwc_otg.h" + + +static struct mtd_partition p2601hnf1_partitions[] __initdata = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x20000, + }, +/* { + .name = "uboot_env", + .offset = 0x20000, + .size = 0x20000, + }, +*/ { + .name = "linux", + .offset = 0x020000, + .size = 0xfc0000, + }, + { + .name = "board_config", + .offset = 0xfe0000, + .size = 0x20000, + }, +}; + +static struct physmap_flash_data p2601hnf1_flash_data __initdata = { + .nr_parts = ARRAY_SIZE(p2601hnf1_partitions), + .parts = p2601hnf1_partitions, +}; + +static struct gpio_led p2601hnf1_leds_gpio[] __initdata = { + { .name = "soc:red:power", .gpio = 29, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:yellow:phone", .gpio = 64, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:green:phone", .gpio = 65, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:yellow:wlan", .gpio = 66, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:green:power", .gpio = 67, .active_low = 1, .default_trigger = "default-on" }, + { .name = "soc:red:internet", .gpio = 68, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:green:internet", .gpio = 69, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:green:dsl", .gpio = 70, .active_low = 1, .default_trigger = "default-off" }, + { .name = "soc:green:wlan", .gpio = 71, .active_low = 1, .default_trigger = "default-off" }, +}; + +static struct gpio_button +p2601hnf1_gpio_buttons[] /*__initdata*/ = { + { .desc = "reset", .type = EV_KEY, .code = BTN_0, .threshold = 3, .gpio = 53, .active_low = 1, }, + { .desc = "wlan", .type = EV_KEY, .code = BTN_1, .threshold = 1, .gpio = 54, .active_low = 1, }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_RMII, +}; + +static void __init +p2601hnf1_init(void) +{ + +#define P2601HNF1_USB 9 + + ltq_register_gpio_stp(); + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(p2601hnf1_leds_gpio), p2601hnf1_leds_gpio); + ltq_register_gpio_buttons(p2601hnf1_gpio_buttons, ARRAY_SIZE(p2601hnf1_gpio_buttons)); + ltq_register_nor(&p2601hnf1_flash_data); + ltq_register_etop(<q_eth_data); + xway_register_dwc(P2601HNF1_USB); + + // enable the ethernet ports on the SoC +// ltq_w32((ltq_r32(LTQ_GPORT_P0_CTL) & ~(1 << 17)) | (1 << 18), LTQ_GPORT_P0_CTL); +// ltq_w32((ltq_r32(LTQ_GPORT_P1_CTL) & ~(1 << 17)) | (1 << 18), LTQ_GPORT_P1_CTL); +// ltq_w32((ltq_r32(LTQ_GPORT_P2_CTL) & ~(1 << 17)) | (1 << 18), LTQ_GPORT_P2_CTL); +} + +MIPS_MACHINE(LANTIQ_MACH_P2601HNF1, + "P2601HNF1", + "ZyXEL P-2601HN-F1", + p2601hnf1_init); + diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-wbmr.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-wbmr.c new file mode 100644 index 0000000000..b11c2631bb --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/mach-wbmr.c @@ -0,0 +1,120 @@ +/* + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + * + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../machtypes.h" +#include "devices.h" +#include "dev-dwc_otg.h" + +static struct mtd_partition wbmr_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x40000, + }, + { + .name = "uboot-env", + .offset = 0x40000, + .size = 0x20000, + }, + { + .name = "linux", + .offset = 0x60000, + .size = 0x1f20000, + }, + { + .name = "calibration", + .offset = 0x1fe0000, + .size = 0x20000, + }, +}; + +static struct physmap_flash_data wbmr_flash_data = { + .nr_parts = ARRAY_SIZE(wbmr_partitions), + .parts = wbmr_partitions, +}; + +static struct gpio_led +wbmr_gpio_leds[] __initdata = { + { .name = "soc:blue:movie", .gpio = 20, .active_low = 1, }, + { .name = "soc:red:internet", .gpio = 18, .active_low = 1, }, + { .name = "soc:green:internet", .gpio = 17, .active_low = 1, }, + { .name = "soc:green:adsl", .gpio = 16, .active_low = 1, }, + { .name = "soc:green:wlan", .gpio = 15, .active_low = 1, }, + { .name = "soc:red:security", .gpio = 14, .active_low = 1, }, + { .name = "soc:green:power", .gpio = 1, .active_low = 1, }, + { .name = "soc:red:power", .gpio = 5, .active_low = 1, }, + { .name = "soc:green:usb", .gpio = 28, .active_low = 1, }, +}; + +static struct gpio_keys_button +wbmr_gpio_keys[] __initdata = { + { + .desc = "aoss", + .type = EV_KEY, + .code = BTN_0, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 0, + .active_low = 1, + }, + { + .desc = "reset", + .type = EV_KEY, + .code = BTN_1, + .debounce_interval = LTQ_KEYS_DEBOUNCE_INTERVAL, + .gpio = 37, + .active_low = 1, + }, +}; + +static struct ltq_pci_data ltq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct ltq_eth_data ltq_eth_data = { + .mii_mode = PHY_INTERFACE_MODE_RGMII, +}; + +static void __init +wbmr_init(void) +{ +#define WMBR_BRN_MAC 0x1fd0024 + + ltq_add_device_gpio_leds(-1, ARRAY_SIZE(wbmr_gpio_leds), wbmr_gpio_leds); + ltq_register_gpio_keys_polled(-1, LTQ_KEYS_POLL_INTERVAL, ARRAY_SIZE(wbmr_gpio_keys), wbmr_gpio_keys); + ltq_register_nor(&wbmr_flash_data); + ltq_register_pci(<q_pci_data); + memcpy_fromio(<q_eth_data.mac.sa_data, + (void *)KSEG1ADDR(LTQ_FLASH_START + WMBR_BRN_MAC), 6); + ltq_register_etop(<q_eth_data); + xway_register_dwc(36); +} + +MIPS_MACHINE(LANTIQ_MACH_WBMR, + "WBMR", + "WBMR", + wbmr_init); diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/pci-ath-fixup.c b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/pci-ath-fixup.c new file mode 100644 index 0000000000..c87ffb2091 --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/pci-ath-fixup.c @@ -0,0 +1,109 @@ +/* + * Atheros AP94 reference board PCI initialization + * + * Copyright (C) 2009-2010 Gabor Juhos + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published + * by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#define LTQ_PCI_MEM_BASE 0x18000000 + +struct ath_fixup { + u16 *cal_data; + unsigned slot; +}; + +static int ath_num_fixups; +static struct ath_fixup ath_fixups[2]; + +static void ath_pci_fixup(struct pci_dev *dev) +{ + void __iomem *mem; + u16 *cal_data = NULL; + u16 cmd; + u32 bar0; + u32 val; + unsigned i; + + for (i = 0; i < ath_num_fixups; i++) { + if (ath_fixups[i].cal_data == NULL) + continue; + + if (ath_fixups[i].slot != PCI_SLOT(dev->devfn)) + continue; + + cal_data = ath_fixups[i].cal_data; + break; + } + + if (cal_data == NULL) + return; + + if (*cal_data != 0xa55a) { + pr_err("pci %s: invalid calibration data\n", pci_name(dev)); + return; + } + + pr_info("pci %s: fixup device configuration\n", pci_name(dev)); + + mem = ioremap(LTQ_PCI_MEM_BASE, 0x10000); + if (!mem) { + pr_err("pci %s: ioremap error\n", pci_name(dev)); + return; + } + + pci_read_config_dword(dev, PCI_BASE_ADDRESS_0, &bar0); + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, LTQ_PCI_MEM_BASE); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd |= PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, cmd); + + /* set pointer to first reg address */ + cal_data += 3; + while (*cal_data != 0xffff) { + u32 reg; + reg = *cal_data++; + val = *cal_data++; + val |= (*cal_data++) << 16; + + ltq_w32(swab32(val), mem + reg); + udelay(100); + } + + pci_read_config_dword(dev, PCI_VENDOR_ID, &val); + dev->vendor = val & 0xffff; + dev->device = (val >> 16) & 0xffff; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &val); + dev->revision = val & 0xff; + dev->class = val >> 8; /* upper 3 bytes */ + + pr_info("pci %s: fixup info: [%04x:%04x] revision %02x class %#08x\n", + pci_name(dev), dev->vendor, dev->device, dev->revision, dev->class); + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + cmd &= ~(PCI_COMMAND_MASTER | PCI_COMMAND_MEMORY); + pci_write_config_word(dev, PCI_COMMAND, cmd); + + pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, bar0); + + iounmap(mem); +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_ATHEROS, PCI_ANY_ID, ath_pci_fixup); + +void __init ltq_pci_ath_fixup(unsigned slot, u16 *cal_data) +{ + if (ath_num_fixups >= ARRAY_SIZE(ath_fixups)) + return; + + ath_fixups[ath_num_fixups].slot = slot; + ath_fixups[ath_num_fixups].cal_data = cal_data; + ath_num_fixups++; +} diff --git a/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/pci-ath-fixup.h b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/pci-ath-fixup.h new file mode 100644 index 0000000000..095d2619ce --- /dev/null +++ b/target/linux/lantiq/files-3.3/arch/mips/lantiq/xway/pci-ath-fixup.h @@ -0,0 +1,6 @@ +#ifndef _PCI_ATH_FIXUP +#define _PCI_ATH_FIXUP + +void ltq_pci_ath_fixup(unsigned slot, u16 *cal_data) __init; + +#endif /* _PCI_ATH_FIXUP */ diff --git a/target/linux/lantiq/patches-3.3/0001-MTD-add-m25p80-id-for-mx25l2005a.patch b/target/linux/lantiq/patches-3.3/0001-MTD-add-m25p80-id-for-mx25l2005a.patch new file mode 100644 index 0000000000..fdbaf74d08 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0001-MTD-add-m25p80-id-for-mx25l2005a.patch @@ -0,0 +1,24 @@ +From 1e54b748744aa6a6dbb86cdbdb9ea775609da7e1 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 13 Mar 2012 18:03:33 +0100 +Subject: [PATCH 01/70] MTD: add m25p80 id for mx25l2005a + +--- + drivers/mtd/devices/m25p80.c | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c +index 7c60ddd..64b3b99 100644 +--- a/drivers/mtd/devices/m25p80.c ++++ b/drivers/mtd/devices/m25p80.c +@@ -676,6 +676,7 @@ static const struct spi_device_id m25p_ids[] = { + { "640s33b", INFO(0x898913, 0, 64 * 1024, 128, 0) }, + + /* Macronix */ ++ { "mx25l2005a", INFO(0xc22012, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l4005a", INFO(0xc22013, 0, 64 * 1024, 8, SECT_4K) }, + { "mx25l8005", INFO(0xc22014, 0, 64 * 1024, 16, 0) }, + { "mx25l1606e", INFO(0xc22015, 0, 64 * 1024, 32, SECT_4K) }, +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0002-MIPS-lantiq-reorganize-xway-code.patch b/target/linux/lantiq/patches-3.3/0002-MIPS-lantiq-reorganize-xway-code.patch new file mode 100644 index 0000000000..ded2ca4e85 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0002-MIPS-lantiq-reorganize-xway-code.patch @@ -0,0 +1,881 @@ +From ebbdb83f27aad0eec162c24f32d8dd852e460584 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 10 Aug 2011 14:57:04 +0200 +Subject: [PATCH 02/70] MIPS: lantiq: reorganize xway code + +Inside the folder arch/mips/lantiq/xway, there were alot of small files with +lots of duplicated code. This patch adds a wrapper function for inserting and +requesting resources and unifies the small files into one bigger file. + +This patch makes the xway code consistent with the falcon support added later +in this series. + +Signed-off-by: John Crispin +--- + arch/mips/include/asm/mach-lantiq/lantiq.h | 14 +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 14 ++++ + arch/mips/lantiq/clk.c | 25 +------ + arch/mips/lantiq/devices.c | 30 ++------ + arch/mips/lantiq/devices.h | 4 + + arch/mips/lantiq/prom.c | 51 +++++++++++-- + arch/mips/lantiq/prom.h | 4 + + arch/mips/lantiq/xway/Makefile | 6 +- + arch/mips/lantiq/xway/devices.c | 42 ++--------- + arch/mips/lantiq/xway/dma.c | 21 +---- + arch/mips/lantiq/xway/ebu.c | 52 ------------- + arch/mips/lantiq/xway/pmu.c | 69 ----------------- + arch/mips/lantiq/xway/prom-ase.c | 9 ++ + arch/mips/lantiq/xway/prom-xway.c | 10 +++ + arch/mips/lantiq/xway/reset.c | 21 +---- + arch/mips/lantiq/xway/setup-ase.c | 19 ----- + arch/mips/lantiq/xway/setup-xway.c | 20 ----- + arch/mips/lantiq/xway/sysctrl.c | 78 ++++++++++++++++++++ + drivers/watchdog/lantiq_wdt.c | 2 +- + 19 files changed, 199 insertions(+), 292 deletions(-) + delete mode 100644 arch/mips/lantiq/xway/ebu.c + delete mode 100644 arch/mips/lantiq/xway/pmu.c + delete mode 100644 arch/mips/lantiq/xway/setup-ase.c + delete mode 100644 arch/mips/lantiq/xway/setup-xway.c + create mode 100644 arch/mips/lantiq/xway/sysctrl.c + +diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h +index ce2f029..66d7300 100644 +--- a/arch/mips/include/asm/mach-lantiq/lantiq.h ++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h +@@ -9,6 +9,7 @@ + #define _LANTIQ_H__ + + #include ++#include + + /* generic reg access functions */ + #define ltq_r32(reg) __raw_readl(reg) +@@ -18,15 +19,6 @@ + #define ltq_r8(reg) __raw_readb(reg) + #define ltq_w8(val, reg) __raw_writeb(val, reg) + +-/* register access macros for EBU and CGU */ +-#define ltq_ebu_w32(x, y) ltq_w32((x), ltq_ebu_membase + (y)) +-#define ltq_ebu_r32(x) ltq_r32(ltq_ebu_membase + (x)) +-#define ltq_cgu_w32(x, y) ltq_w32((x), ltq_cgu_membase + (y)) +-#define ltq_cgu_r32(x) ltq_r32(ltq_cgu_membase + (x)) +- +-extern __iomem void *ltq_ebu_membase; +-extern __iomem void *ltq_cgu_membase; +- + extern unsigned int ltq_get_cpu_ver(void); + extern unsigned int ltq_get_soc_type(void); + +@@ -51,7 +43,9 @@ extern void ltq_enable_irq(struct irq_data *data); + + /* find out what caused the last cpu reset */ + extern int ltq_reset_cause(void); +-#define LTQ_RST_CAUSE_WDTRST 0x20 ++ ++/* helper for requesting and remapping resources */ ++extern void __iomem *ltq_remap_resource(struct resource *res); + + #define IOPORT_RESOURCE_START 0x10000000 + #define IOPORT_RESOURCE_END 0xffffffff +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 8a3c6be..9b7ee366 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -61,6 +61,8 @@ + #define LTQ_CGU_BASE_ADDR 0x1F103000 + #define LTQ_CGU_SIZE 0x1000 + ++#define CGU_EPHY 0x10 ++ + /* ICU - interrupt control unit */ + #define LTQ_ICU_BASE_ADDR 0x1F880200 + #define LTQ_ICU_SIZE 0x100 +@@ -97,6 +99,8 @@ + #define LTQ_WDT_BASE_ADDR 0x1F8803F0 + #define LTQ_WDT_SIZE 0x10 + ++#define LTQ_RST_CAUSE_WDTRST 0x20 ++ + /* STP - serial to parallel conversion unit */ + #define LTQ_STP_BASE_ADDR 0x1E100BB0 + #define LTQ_STP_SIZE 0x40 +@@ -121,11 +125,21 @@ + #define LTQ_MPS_BASE_ADDR (KSEG1 + 0x1F107000) + #define LTQ_MPS_CHIPID ((u32 *)(LTQ_MPS_BASE_ADDR + 0x0344)) + ++/* register access macros for EBU and CGU */ ++#define ltq_ebu_w32(x, y) ltq_w32((x), ltq_ebu_membase + (y)) ++#define ltq_ebu_r32(x) ltq_r32(ltq_ebu_membase + (x)) ++#define ltq_cgu_w32(x, y) ltq_w32((x), ltq_cgu_membase + (y)) ++#define ltq_cgu_r32(x) ltq_r32(ltq_cgu_membase + (x)) ++ ++extern __iomem void *ltq_ebu_membase; ++extern __iomem void *ltq_cgu_membase; ++ + /* request a non-gpio and set the PIO config */ + extern int ltq_gpio_request(unsigned int pin, unsigned int alt0, + unsigned int alt1, unsigned int dir, const char *name); + extern void ltq_pmu_enable(unsigned int module); + extern void ltq_pmu_disable(unsigned int module); ++extern void ltq_cgu_enable(unsigned int clk); + + static inline int ltq_is_ar9(void) + { +diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c +index 412814f..39eef7f 100644 +--- a/arch/mips/lantiq/clk.c ++++ b/arch/mips/lantiq/clk.c +@@ -22,6 +22,7 @@ + #include + + #include "clk.h" ++#include "prom.h" + + struct clk { + const char *name; +@@ -46,16 +47,6 @@ static struct clk cpu_clk_generic[] = { + }, + }; + +-static struct resource ltq_cgu_resource = { +- .name = "cgu", +- .start = LTQ_CGU_BASE_ADDR, +- .end = LTQ_CGU_BASE_ADDR + LTQ_CGU_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; +- +-/* remapped clock register range */ +-void __iomem *ltq_cgu_membase; +- + void clk_init(void) + { + cpu_clk = cpu_clk_generic; +@@ -133,21 +124,11 @@ void __init plat_time_init(void) + { + struct clk *clk; + +- if (insert_resource(&iomem_resource, <q_cgu_resource) < 0) +- panic("Failed to insert cgu memory"); +- +- if (request_mem_region(ltq_cgu_resource.start, +- resource_size(<q_cgu_resource), "cgu") < 0) +- panic("Failed to request cgu memory"); ++ ltq_soc_init(); + +- ltq_cgu_membase = ioremap_nocache(ltq_cgu_resource.start, +- resource_size(<q_cgu_resource)); +- if (!ltq_cgu_membase) { +- pr_err("Failed to remap cgu memory\n"); +- unreachable(); +- } + clk = clk_get(0, "cpu"); + mips_hpt_frequency = clk_get_rate(clk) / ltq_get_counter_resolution(); + write_c0_compare(read_c0_count()); ++ pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000); + clk_put(clk); + } +diff --git a/arch/mips/lantiq/devices.c b/arch/mips/lantiq/devices.c +index de1cb2b..7193d78 100644 +--- a/arch/mips/lantiq/devices.c ++++ b/arch/mips/lantiq/devices.c +@@ -27,12 +27,8 @@ + #include "devices.h" + + /* nor flash */ +-static struct resource ltq_nor_resource = { +- .name = "nor", +- .start = LTQ_FLASH_START, +- .end = LTQ_FLASH_START + LTQ_FLASH_MAX - 1, +- .flags = IORESOURCE_MEM, +-}; ++static struct resource ltq_nor_resource = ++ MEM_RES("nor", LTQ_FLASH_START, LTQ_FLASH_MAX); + + static struct platform_device ltq_nor = { + .name = "ltq_nor", +@@ -47,12 +43,8 @@ void __init ltq_register_nor(struct physmap_flash_data *data) + } + + /* watchdog */ +-static struct resource ltq_wdt_resource = { +- .name = "watchdog", +- .start = LTQ_WDT_BASE_ADDR, +- .end = LTQ_WDT_BASE_ADDR + LTQ_WDT_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; ++static struct resource ltq_wdt_resource = ++ MEM_RES("watchdog", LTQ_WDT_BASE_ADDR, LTQ_WDT_SIZE); + + void __init ltq_register_wdt(void) + { +@@ -61,24 +53,14 @@ void __init ltq_register_wdt(void) + + /* asc ports */ + static struct resource ltq_asc0_resources[] = { +- { +- .name = "asc0", +- .start = LTQ_ASC0_BASE_ADDR, +- .end = LTQ_ASC0_BASE_ADDR + LTQ_ASC_SIZE - 1, +- .flags = IORESOURCE_MEM, +- }, ++ MEM_RES("asc0", LTQ_ASC0_BASE_ADDR, LTQ_ASC_SIZE), + IRQ_RES(tx, LTQ_ASC_TIR(0)), + IRQ_RES(rx, LTQ_ASC_RIR(0)), + IRQ_RES(err, LTQ_ASC_EIR(0)), + }; + + static struct resource ltq_asc1_resources[] = { +- { +- .name = "asc1", +- .start = LTQ_ASC1_BASE_ADDR, +- .end = LTQ_ASC1_BASE_ADDR + LTQ_ASC_SIZE - 1, +- .flags = IORESOURCE_MEM, +- }, ++ MEM_RES("asc1", LTQ_ASC1_BASE_ADDR, LTQ_ASC_SIZE), + IRQ_RES(tx, LTQ_ASC_TIR(1)), + IRQ_RES(rx, LTQ_ASC_RIR(1)), + IRQ_RES(err, LTQ_ASC_EIR(1)), +diff --git a/arch/mips/lantiq/devices.h b/arch/mips/lantiq/devices.h +index 2947bb1..a03c23f 100644 +--- a/arch/mips/lantiq/devices.h ++++ b/arch/mips/lantiq/devices.h +@@ -14,6 +14,10 @@ + + #define IRQ_RES(resname, irq) \ + {.name = #resname, .start = (irq), .flags = IORESOURCE_IRQ} ++#define MEM_RES(resname, adr_start, adr_size) \ ++ { .name = resname, .flags = IORESOURCE_MEM, \ ++ .start = ((adr_start) & ~KSEG1), \ ++ .end = ((adr_start + adr_size - 1) & ~KSEG1) } + + extern void ltq_register_nor(struct physmap_flash_data *data); + extern void ltq_register_wdt(void); +diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c +index e34fcfd..e3b1e25 100644 +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -16,6 +16,10 @@ + #include "prom.h" + #include "clk.h" + ++/* access to the ebu needs to be locked between different drivers */ ++DEFINE_SPINLOCK(ebu_lock); ++EXPORT_SYMBOL_GPL(ebu_lock); ++ + static struct ltq_soc_info soc_info; + + unsigned int ltq_get_cpu_ver(void) +@@ -55,16 +59,51 @@ static void __init prom_init_cmdline(void) + } + } + +-void __init prom_init(void) ++void __iomem *ltq_remap_resource(struct resource *res) + { +- struct clk *clk; ++ __iomem void *ret = NULL; ++ struct resource *lookup = lookup_resource(&iomem_resource, res->start); ++ ++ if (lookup && strcmp(lookup->name, res->name)) { ++ pr_err("conflicting memory range %s\n", res->name); ++ return NULL; ++ } ++ if (!lookup) { ++ if (insert_resource(&iomem_resource, res) < 0) { ++ pr_err("Failed to insert %s memory\n", res->name); ++ return NULL; ++ } ++ } ++ if (request_mem_region(res->start, ++ resource_size(res), res->name) < 0) { ++ pr_err("Failed to request %s memory\n", res->name); ++ goto err_res; ++ } + ++ ret = ioremap_nocache(res->start, resource_size(res)); ++ if (!ret) ++ goto err_mem; ++ ++ pr_debug("remap: 0x%08X-0x%08X : \"%s\"\n", ++ res->start, res->end, res->name); ++ return ret; ++ ++err_mem: ++ panic("Failed to remap %s memory\n", res->name); ++ release_mem_region(res->start, resource_size(res)); ++ ++err_res: ++ release_resource(res); ++ return NULL; ++} ++EXPORT_SYMBOL(ltq_remap_resource); ++ ++void __init prom_init(void) ++{ + ltq_soc_detect(&soc_info); + clk_init(); +- clk = clk_get(0, "cpu"); +- snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN - 1, "%s rev1.%d", +- soc_info.name, soc_info.rev); +- clk_put(clk); ++ snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN - 1, "%s rev %s", ++ soc_info.name, soc_info.rev_type); + soc_info.sys_type[LTQ_SYS_TYPE_LEN - 1] = '\0'; + pr_info("SoC: %s\n", soc_info.sys_type); + prom_init_cmdline(); +diff --git a/arch/mips/lantiq/prom.h b/arch/mips/lantiq/prom.h +index b4229d9..51dba1b 100644 +--- a/arch/mips/lantiq/prom.h ++++ b/arch/mips/lantiq/prom.h +@@ -9,17 +9,21 @@ + #ifndef _LTQ_PROM_H__ + #define _LTQ_PROM_H__ + ++#define LTQ_SYS_REV_LEN 0x10 + #define LTQ_SYS_TYPE_LEN 0x100 + + struct ltq_soc_info { + unsigned char *name; + unsigned int rev; ++ unsigned char rev_type[LTQ_SYS_REV_LEN]; ++ unsigned int srev; + unsigned int partnum; + unsigned int type; + unsigned char sys_type[LTQ_SYS_TYPE_LEN]; + }; + + extern void ltq_soc_detect(struct ltq_soc_info *i); ++extern void ltq_soc_init(void); + extern void ltq_soc_setup(void); + + #endif +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index c517f2e..6678402 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,7 +1,7 @@ +-obj-y := pmu.o ebu.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o ++obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o + +-obj-$(CONFIG_SOC_XWAY) += clk-xway.o prom-xway.o setup-xway.o +-obj-$(CONFIG_SOC_AMAZON_SE) += clk-ase.o prom-ase.o setup-ase.o ++obj-$(CONFIG_SOC_XWAY) += clk-xway.o prom-xway.o ++obj-$(CONFIG_SOC_AMAZON_SE) += clk-ase.o prom-ase.o + + obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o + obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c +index d614aa7..f97e565 100644 +--- a/arch/mips/lantiq/xway/devices.c ++++ b/arch/mips/lantiq/xway/devices.c +@@ -31,22 +31,9 @@ + + /* gpio */ + static struct resource ltq_gpio_resource[] = { +- { +- .name = "gpio0", +- .start = LTQ_GPIO0_BASE_ADDR, +- .end = LTQ_GPIO0_BASE_ADDR + LTQ_GPIO_SIZE - 1, +- .flags = IORESOURCE_MEM, +- }, { +- .name = "gpio1", +- .start = LTQ_GPIO1_BASE_ADDR, +- .end = LTQ_GPIO1_BASE_ADDR + LTQ_GPIO_SIZE - 1, +- .flags = IORESOURCE_MEM, +- }, { +- .name = "gpio2", +- .start = LTQ_GPIO2_BASE_ADDR, +- .end = LTQ_GPIO2_BASE_ADDR + LTQ_GPIO_SIZE - 1, +- .flags = IORESOURCE_MEM, +- } ++ MEM_RES("gpio0", LTQ_GPIO0_BASE_ADDR, LTQ_GPIO_SIZE), ++ MEM_RES("gpio1", LTQ_GPIO1_BASE_ADDR, LTQ_GPIO_SIZE), ++ MEM_RES("gpio2", LTQ_GPIO2_BASE_ADDR, LTQ_GPIO_SIZE), + }; + + void __init ltq_register_gpio(void) +@@ -64,12 +51,8 @@ void __init ltq_register_gpio(void) + } + + /* serial to parallel conversion */ +-static struct resource ltq_stp_resource = { +- .name = "stp", +- .start = LTQ_STP_BASE_ADDR, +- .end = LTQ_STP_BASE_ADDR + LTQ_STP_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; ++static struct resource ltq_stp_resource = ++ MEM_RES("stp", LTQ_STP_BASE_ADDR, LTQ_STP_SIZE); + + void __init ltq_register_gpio_stp(void) + { +@@ -78,12 +61,7 @@ void __init ltq_register_gpio_stp(void) + + /* asc ports - amazon se has its own serial mapping */ + static struct resource ltq_ase_asc_resources[] = { +- { +- .name = "asc0", +- .start = LTQ_ASC1_BASE_ADDR, +- .end = LTQ_ASC1_BASE_ADDR + LTQ_ASC_SIZE - 1, +- .flags = IORESOURCE_MEM, +- }, ++ MEM_RES("asc0", LTQ_ASC1_BASE_ADDR, LTQ_ASC_SIZE), + IRQ_RES(tx, LTQ_ASC_ASE_TIR), + IRQ_RES(rx, LTQ_ASC_ASE_RIR), + IRQ_RES(err, LTQ_ASC_ASE_EIR), +@@ -96,12 +74,8 @@ void __init ltq_register_ase_asc(void) + } + + /* ethernet */ +-static struct resource ltq_etop_resources = { +- .name = "etop", +- .start = LTQ_ETOP_BASE_ADDR, +- .end = LTQ_ETOP_BASE_ADDR + LTQ_ETOP_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; ++static struct resource ltq_etop_resources = ++ MEM_RES("etop", LTQ_ETOP_BASE_ADDR, LTQ_ETOP_SIZE); + + static struct platform_device ltq_etop = { + .name = "ltq_etop", +diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c +index b210e93..6cf883b 100644 +--- a/arch/mips/lantiq/xway/dma.c ++++ b/arch/mips/lantiq/xway/dma.c +@@ -24,6 +24,8 @@ + #include + #include + ++#include "../devices.h" ++ + #define LTQ_DMA_CTRL 0x10 + #define LTQ_DMA_CPOLL 0x14 + #define LTQ_DMA_CS 0x18 +@@ -55,12 +57,8 @@ + #define ltq_dma_w32_mask(x, y, z) ltq_w32_mask(x, y, \ + ltq_dma_membase + (z)) + +-static struct resource ltq_dma_resource = { +- .name = "dma", +- .start = LTQ_DMA_BASE_ADDR, +- .end = LTQ_DMA_BASE_ADDR + LTQ_DMA_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; ++static struct resource ltq_dma_resource = ++ MEM_RES("dma", LTQ_DMA_BASE_ADDR, LTQ_DMA_SIZE); + + static void __iomem *ltq_dma_membase; + +@@ -220,17 +218,8 @@ ltq_dma_init(void) + { + int i; + +- /* insert and request the memory region */ +- if (insert_resource(&iomem_resource, <q_dma_resource) < 0) +- panic("Failed to insert dma memory"); +- +- if (request_mem_region(ltq_dma_resource.start, +- resource_size(<q_dma_resource), "dma") < 0) +- panic("Failed to request dma memory"); +- + /* remap dma register range */ +- ltq_dma_membase = ioremap_nocache(ltq_dma_resource.start, +- resource_size(<q_dma_resource)); ++ ltq_dma_membase = ltq_remap_resource(<q_dma_resource); + if (!ltq_dma_membase) + panic("Failed to remap dma memory"); + +diff --git a/arch/mips/lantiq/xway/ebu.c b/arch/mips/lantiq/xway/ebu.c +deleted file mode 100644 +index 862e3e8..0000000 +--- a/arch/mips/lantiq/xway/ebu.c ++++ /dev/null +@@ -1,52 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * EBU - the external bus unit attaches PCI, NOR and NAND +- * +- * Copyright (C) 2010 John Crispin +- */ +- +-#include +-#include +-#include +- +-#include +- +-/* all access to the ebu must be locked */ +-DEFINE_SPINLOCK(ebu_lock); +-EXPORT_SYMBOL_GPL(ebu_lock); +- +-static struct resource ltq_ebu_resource = { +- .name = "ebu", +- .start = LTQ_EBU_BASE_ADDR, +- .end = LTQ_EBU_BASE_ADDR + LTQ_EBU_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; +- +-/* remapped base addr of the clock unit and external bus unit */ +-void __iomem *ltq_ebu_membase; +- +-static int __init lantiq_ebu_init(void) +-{ +- /* insert and request the memory region */ +- if (insert_resource(&iomem_resource, <q_ebu_resource) < 0) +- panic("Failed to insert ebu memory"); +- +- if (request_mem_region(ltq_ebu_resource.start, +- resource_size(<q_ebu_resource), "ebu") < 0) +- panic("Failed to request ebu memory"); +- +- /* remap ebu register range */ +- ltq_ebu_membase = ioremap_nocache(ltq_ebu_resource.start, +- resource_size(<q_ebu_resource)); +- if (!ltq_ebu_membase) +- panic("Failed to remap ebu memory"); +- +- /* make sure to unprotect the memory region where flash is located */ +- ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0); +- return 0; +-} +- +-postcore_initcall(lantiq_ebu_init); +diff --git a/arch/mips/lantiq/xway/pmu.c b/arch/mips/lantiq/xway/pmu.c +deleted file mode 100644 +index fe85361..0000000 +--- a/arch/mips/lantiq/xway/pmu.c ++++ /dev/null +@@ -1,69 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2010 John Crispin +- */ +- +-#include +-#include +-#include +- +-#include +- +-/* PMU - the power management unit allows us to turn part of the core +- * on and off +- */ +- +-/* the enable / disable registers */ +-#define LTQ_PMU_PWDCR 0x1C +-#define LTQ_PMU_PWDSR 0x20 +- +-#define ltq_pmu_w32(x, y) ltq_w32((x), ltq_pmu_membase + (y)) +-#define ltq_pmu_r32(x) ltq_r32(ltq_pmu_membase + (x)) +- +-static struct resource ltq_pmu_resource = { +- .name = "pmu", +- .start = LTQ_PMU_BASE_ADDR, +- .end = LTQ_PMU_BASE_ADDR + LTQ_PMU_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; +- +-static void __iomem *ltq_pmu_membase; +- +-void ltq_pmu_enable(unsigned int module) +-{ +- int err = 1000000; +- +- ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) & ~module, LTQ_PMU_PWDCR); +- do {} while (--err && (ltq_pmu_r32(LTQ_PMU_PWDSR) & module)); +- +- if (!err) +- panic("activating PMU module failed!"); +-} +-EXPORT_SYMBOL(ltq_pmu_enable); +- +-void ltq_pmu_disable(unsigned int module) +-{ +- ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) | module, LTQ_PMU_PWDCR); +-} +-EXPORT_SYMBOL(ltq_pmu_disable); +- +-int __init ltq_pmu_init(void) +-{ +- if (insert_resource(&iomem_resource, <q_pmu_resource) < 0) +- panic("Failed to insert pmu memory"); +- +- if (request_mem_region(ltq_pmu_resource.start, +- resource_size(<q_pmu_resource), "pmu") < 0) +- panic("Failed to request pmu memory"); +- +- ltq_pmu_membase = ioremap_nocache(ltq_pmu_resource.start, +- resource_size(<q_pmu_resource)); +- if (!ltq_pmu_membase) +- panic("Failed to remap pmu memory"); +- return 0; +-} +- +-core_initcall(ltq_pmu_init); +diff --git a/arch/mips/lantiq/xway/prom-ase.c b/arch/mips/lantiq/xway/prom-ase.c +index ae4959a..3f86a3b 100644 +--- a/arch/mips/lantiq/xway/prom-ase.c ++++ b/arch/mips/lantiq/xway/prom-ase.c +@@ -13,6 +13,7 @@ + + #include + ++#include "devices.h" + #include "../prom.h" + + #define SOC_AMAZON_SE "Amazon_SE" +@@ -26,6 +27,7 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + { + i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT; + i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT; ++ sprintf(i->rev_type, "1.%d", i->rev); + switch (i->partnum) { + case SOC_ID_AMAZON_SE: + i->name = SOC_AMAZON_SE; +@@ -37,3 +39,10 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + break; + } + } ++ ++void __init ltq_soc_setup(void) ++{ ++ ltq_register_ase_asc(); ++ ltq_register_gpio(); ++ ltq_register_wdt(); ++} +diff --git a/arch/mips/lantiq/xway/prom-xway.c b/arch/mips/lantiq/xway/prom-xway.c +index 2228133..d823a92 100644 +--- a/arch/mips/lantiq/xway/prom-xway.c ++++ b/arch/mips/lantiq/xway/prom-xway.c +@@ -13,6 +13,7 @@ + + #include + ++#include "devices.h" + #include "../prom.h" + + #define SOC_DANUBE "Danube" +@@ -28,6 +29,7 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + { + i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT; + i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT; ++ sprintf(i->rev_type, "1.%d", i->rev); + switch (i->partnum) { + case SOC_ID_DANUBE1: + case SOC_ID_DANUBE2: +@@ -52,3 +54,11 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + break; + } + } ++ ++void __init ltq_soc_setup(void) ++{ ++ ltq_register_asc(0); ++ ltq_register_asc(1); ++ ltq_register_gpio(); ++ ltq_register_wdt(); ++} +diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c +index 8b66bd8..c705bbf 100644 +--- a/arch/mips/lantiq/xway/reset.c ++++ b/arch/mips/lantiq/xway/reset.c +@@ -15,6 +15,8 @@ + + #include + ++#include "../devices.h" ++ + #define ltq_rcu_w32(x, y) ltq_w32((x), ltq_rcu_membase + (y)) + #define ltq_rcu_r32(x) ltq_r32(ltq_rcu_membase + (x)) + +@@ -25,12 +27,8 @@ + #define LTQ_RCU_RST_STAT 0x0014 + #define LTQ_RCU_STAT_SHIFT 26 + +-static struct resource ltq_rcu_resource = { +- .name = "rcu", +- .start = LTQ_RCU_BASE_ADDR, +- .end = LTQ_RCU_BASE_ADDR + LTQ_RCU_SIZE - 1, +- .flags = IORESOURCE_MEM, +-}; ++static struct resource ltq_rcu_resource = ++ MEM_RES("rcu", LTQ_RCU_BASE_ADDR, LTQ_RCU_SIZE); + + /* remapped base addr of the reset control unit */ + static void __iomem *ltq_rcu_membase; +@@ -67,17 +65,8 @@ static void ltq_machine_power_off(void) + + static int __init mips_reboot_setup(void) + { +- /* insert and request the memory region */ +- if (insert_resource(&iomem_resource, <q_rcu_resource) < 0) +- panic("Failed to insert rcu memory"); +- +- if (request_mem_region(ltq_rcu_resource.start, +- resource_size(<q_rcu_resource), "rcu") < 0) +- panic("Failed to request rcu memory"); +- + /* remap rcu register range */ +- ltq_rcu_membase = ioremap_nocache(ltq_rcu_resource.start, +- resource_size(<q_rcu_resource)); ++ ltq_rcu_membase = ltq_remap_resource(<q_rcu_resource); + if (!ltq_rcu_membase) + panic("Failed to remap rcu memory"); + +diff --git a/arch/mips/lantiq/xway/setup-ase.c b/arch/mips/lantiq/xway/setup-ase.c +deleted file mode 100644 +index f6f3267..0000000 +--- a/arch/mips/lantiq/xway/setup-ase.c ++++ /dev/null +@@ -1,19 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2011 John Crispin +- */ +- +-#include +- +-#include "../prom.h" +-#include "devices.h" +- +-void __init ltq_soc_setup(void) +-{ +- ltq_register_ase_asc(); +- ltq_register_gpio(); +- ltq_register_wdt(); +-} +diff --git a/arch/mips/lantiq/xway/setup-xway.c b/arch/mips/lantiq/xway/setup-xway.c +deleted file mode 100644 +index c292f64..0000000 +--- a/arch/mips/lantiq/xway/setup-xway.c ++++ /dev/null +@@ -1,20 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2011 John Crispin +- */ +- +-#include +- +-#include "../prom.h" +-#include "devices.h" +- +-void __init ltq_soc_setup(void) +-{ +- ltq_register_asc(0); +- ltq_register_asc(1); +- ltq_register_gpio(); +- ltq_register_wdt(); +-} +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +new file mode 100644 +index 0000000..8fd13a1 +--- /dev/null ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -0,0 +1,78 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "../devices.h" ++ ++/* clock control register */ ++#define LTQ_CGU_IFCCR 0x0018 ++ ++/* the enable / disable registers */ ++#define LTQ_PMU_PWDCR 0x1C ++#define LTQ_PMU_PWDSR 0x20 ++ ++#define ltq_pmu_w32(x, y) ltq_w32((x), ltq_pmu_membase + (y)) ++#define ltq_pmu_r32(x) ltq_r32(ltq_pmu_membase + (x)) ++ ++static struct resource ltq_cgu_resource = ++ MEM_RES("cgu", LTQ_CGU_BASE_ADDR, LTQ_CGU_SIZE); ++ ++static struct resource ltq_pmu_resource = ++ MEM_RES("pmu", LTQ_PMU_BASE_ADDR, LTQ_PMU_SIZE); ++ ++static struct resource ltq_ebu_resource = ++ MEM_RES("ebu", LTQ_EBU_BASE_ADDR, LTQ_EBU_SIZE); ++ ++void __iomem *ltq_cgu_membase; ++void __iomem *ltq_ebu_membase; ++static void __iomem *ltq_pmu_membase; ++ ++void ltq_cgu_enable(unsigned int clk) ++{ ++ ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | clk, LTQ_CGU_IFCCR); ++} ++ ++void ltq_pmu_enable(unsigned int module) ++{ ++ int err = 1000000; ++ ++ ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) & ~module, LTQ_PMU_PWDCR); ++ do {} while (--err && (ltq_pmu_r32(LTQ_PMU_PWDSR) & module)); ++ ++ if (!err) ++ panic("activating PMU module failed!\n"); ++} ++EXPORT_SYMBOL(ltq_pmu_enable); ++ ++void ltq_pmu_disable(unsigned int module) ++{ ++ ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) | module, LTQ_PMU_PWDCR); ++} ++EXPORT_SYMBOL(ltq_pmu_disable); ++ ++void __init ltq_soc_init(void) ++{ ++ ltq_pmu_membase = ltq_remap_resource(<q_pmu_resource); ++ if (!ltq_pmu_membase) ++ panic("Failed to remap pmu memory\n"); ++ ++ ltq_cgu_membase = ltq_remap_resource(<q_cgu_resource); ++ if (!ltq_cgu_membase) ++ panic("Failed to remap cgu memory\n"); ++ ++ ltq_ebu_membase = ltq_remap_resource(<q_ebu_resource); ++ if (!ltq_ebu_membase) ++ panic("Failed to remap ebu memory\n"); ++ ++ /* make sure to unprotect the memory region where flash is located */ ++ ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0); ++} +diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c +index d3a63be..9c8b10c 100644 +--- a/drivers/watchdog/lantiq_wdt.c ++++ b/drivers/watchdog/lantiq_wdt.c +@@ -16,7 +16,7 @@ + #include + #include + +-#include ++#include + + /* Section 3.4 of the datasheet + * The password sequence protects the WDT control register from unintended +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0003-MIPS-lantiq-change-ltq_request_gpio-call-signature.patch b/target/linux/lantiq/patches-3.3/0003-MIPS-lantiq-change-ltq_request_gpio-call-signature.patch new file mode 100644 index 0000000000..1a49218bb8 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0003-MIPS-lantiq-change-ltq_request_gpio-call-signature.patch @@ -0,0 +1,149 @@ +From 834353ea867a84649d452bac03b97ce32dc6e318 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 11 Nov 2011 12:45:24 +0100 +Subject: [PATCH 03/70] MIPS: lantiq: change ltq_request_gpio() call signature + +ltq_request_gpio() was using alt0/1 to multiplex the function of GPIO pins. +This was XWAY specific. In order to also accomodate SoCs that require more bits +we use a 32bit mask instead. This way the call signature is consistent between +XWAY and FALC-ON. + +Signed-off-by: John Crispin +Signed-off-by: Thomas Langer +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 4 +- + arch/mips/lantiq/xway/gpio.c | 8 ++-- + arch/mips/lantiq/xway/gpio_stp.c | 6 ++-- + arch/mips/pci/pci-lantiq.c | 36 +++++++++---------- + 4 files changed, 26 insertions(+), 28 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 9b7ee366..87f6d24 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -135,8 +135,8 @@ extern __iomem void *ltq_ebu_membase; + extern __iomem void *ltq_cgu_membase; + + /* request a non-gpio and set the PIO config */ +-extern int ltq_gpio_request(unsigned int pin, unsigned int alt0, +- unsigned int alt1, unsigned int dir, const char *name); ++extern int ltq_gpio_request(unsigned int pin, unsigned int mux, ++ unsigned int dir, const char *name); + extern void ltq_pmu_enable(unsigned int module); + extern void ltq_pmu_disable(unsigned int module); + extern void ltq_cgu_enable(unsigned int clk); +diff --git a/arch/mips/lantiq/xway/gpio.c b/arch/mips/lantiq/xway/gpio.c +index d2fa98f..f204f6c 100644 +--- a/arch/mips/lantiq/xway/gpio.c ++++ b/arch/mips/lantiq/xway/gpio.c +@@ -48,8 +48,8 @@ int irq_to_gpio(unsigned int gpio) + } + EXPORT_SYMBOL(irq_to_gpio); + +-int ltq_gpio_request(unsigned int pin, unsigned int alt0, +- unsigned int alt1, unsigned int dir, const char *name) ++int ltq_gpio_request(unsigned int pin, unsigned int mux, ++ unsigned int dir, const char *name) + { + int id = 0; + +@@ -67,13 +67,13 @@ int ltq_gpio_request(unsigned int pin, unsigned int alt0, + pin -= PINS_PER_PORT; + id++; + } +- if (alt0) ++ if (mux & 0x2) + ltq_gpio_setbit(ltq_gpio_port[id].membase, + LTQ_GPIO_ALTSEL0, pin); + else + ltq_gpio_clearbit(ltq_gpio_port[id].membase, + LTQ_GPIO_ALTSEL0, pin); +- if (alt1) ++ if (mux & 0x1) + ltq_gpio_setbit(ltq_gpio_port[id].membase, + LTQ_GPIO_ALTSEL1, pin); + else +diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c +index ff9991c..2c78660 100644 +--- a/arch/mips/lantiq/xway/gpio_stp.c ++++ b/arch/mips/lantiq/xway/gpio_stp.c +@@ -79,9 +79,9 @@ static struct gpio_chip ltq_stp_chip = { + static int ltq_stp_hw_init(void) + { + /* the 3 pins used to control the external stp */ +- ltq_gpio_request(4, 1, 0, 1, "stp-st"); +- ltq_gpio_request(5, 1, 0, 1, "stp-d"); +- ltq_gpio_request(6, 1, 0, 1, "stp-sh"); ++ ltq_gpio_request(4, 2, 1, "stp-st"); ++ ltq_gpio_request(5, 2, 1, "stp-d"); ++ ltq_gpio_request(6, 2, 1, "stp-sh"); + + /* sane defaults */ + ltq_stp_w32(0, LTQ_STP_AR); +diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c +index be1e1af..c001c5a 100644 +--- a/arch/mips/pci/pci-lantiq.c ++++ b/arch/mips/pci/pci-lantiq.c +@@ -70,28 +70,27 @@ + + struct ltq_pci_gpio_map { + int pin; +- int alt0; +- int alt1; ++ int mux; + int dir; + char *name; + }; + + /* the pci core can make use of the following gpios */ + static struct ltq_pci_gpio_map ltq_pci_gpio_map[] = { +- { 0, 1, 0, 0, "pci-exin0" }, +- { 1, 1, 0, 0, "pci-exin1" }, +- { 2, 1, 0, 0, "pci-exin2" }, +- { 39, 1, 0, 0, "pci-exin3" }, +- { 10, 1, 0, 0, "pci-exin4" }, +- { 9, 1, 0, 0, "pci-exin5" }, +- { 30, 1, 0, 1, "pci-gnt1" }, +- { 23, 1, 0, 1, "pci-gnt2" }, +- { 19, 1, 0, 1, "pci-gnt3" }, +- { 38, 1, 0, 1, "pci-gnt4" }, +- { 29, 1, 0, 0, "pci-req1" }, +- { 31, 1, 0, 0, "pci-req2" }, +- { 3, 1, 0, 0, "pci-req3" }, +- { 37, 1, 0, 0, "pci-req4" }, ++ { 0, 2, 0, "pci-exin0" }, ++ { 1, 2, 0, "pci-exin1" }, ++ { 2, 2, 0, "pci-exin2" }, ++ { 39, 2, 0, "pci-exin3" }, ++ { 10, 2, 0, "pci-exin4" }, ++ { 9, 2, 0, "pci-exin5" }, ++ { 30, 2, 1, "pci-gnt1" }, ++ { 23, 2, 1, "pci-gnt2" }, ++ { 19, 2, 1, "pci-gnt3" }, ++ { 38, 2, 1, "pci-gnt4" }, ++ { 29, 2, 0, "pci-req1" }, ++ { 31, 2, 0, "pci-req2" }, ++ { 3, 2, 0, "pci-req3" }, ++ { 37, 2, 0, "pci-req4" }, + }; + + __iomem void *ltq_pci_mapped_cfg; +@@ -157,13 +156,12 @@ static void ltq_pci_setup_gpio(int gpio) + for (i = 0; i < ARRAY_SIZE(ltq_pci_gpio_map); i++) { + if (gpio & (1 << i)) { + ltq_gpio_request(ltq_pci_gpio_map[i].pin, +- ltq_pci_gpio_map[i].alt0, +- ltq_pci_gpio_map[i].alt1, ++ ltq_pci_gpio_map[i].mux, + ltq_pci_gpio_map[i].dir, + ltq_pci_gpio_map[i].name); + } + } +- ltq_gpio_request(21, 0, 0, 1, "pci-reset"); ++ ltq_gpio_request(21, 0, 1, "pci-reset"); + ltq_pci_req_mask = (gpio >> PCI_REQ_SHIFT) & PCI_REQ_MASK; + } + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0004-MIPS-lantiq-make-irq.c-support-the-FALC-ON.patch b/target/linux/lantiq/patches-3.3/0004-MIPS-lantiq-make-irq.c-support-the-FALC-ON.patch new file mode 100644 index 0000000000..f313ff30dc --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0004-MIPS-lantiq-make-irq.c-support-the-FALC-ON.patch @@ -0,0 +1,73 @@ +From 9eabaa2969af9aa157d50b7cfbb447f65db95f06 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Aug 2011 12:25:55 +0200 +Subject: [PATCH 04/70] MIPS: lantiq: make irq.c support the FALC-ON + +There are minor differences in how irqs work on xway and falcon socs. +Xway needs 2 quirks that we need to disable for falcon to also work with +this code. + +* EBU irq does not need to send a special ack to the EBU +* The EIU does not exist + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +--- + arch/mips/lantiq/irq.c | 24 +++++++++++++----------- + 1 files changed, 13 insertions(+), 11 deletions(-) + +diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c +index d673731..f56bcce 100644 +--- a/arch/mips/lantiq/irq.c ++++ b/arch/mips/lantiq/irq.c +@@ -195,7 +195,7 @@ static void ltq_hw_irqdispatch(int module) + do_IRQ((int)irq + INT_NUM_IM0_IRL0 + (INT_NUM_IM_OFFSET * module)); + + /* if this is a EBU irq, we need to ack it or get a deadlock */ +- if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0)) ++ if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0) && LTQ_EBU_PCC_ISTAT) + ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_ISTAT) | 0x10, + LTQ_EBU_PCC_ISTAT); + } +@@ -259,17 +259,19 @@ void __init arch_init_irq(void) + if (!ltq_icu_membase) + panic("Failed to remap icu memory"); + +- if (insert_resource(&iomem_resource, <q_eiu_resource) < 0) +- panic("Failed to insert eiu memory"); ++ if (LTQ_EIU_BASE_ADDR) { ++ if (insert_resource(&iomem_resource, <q_eiu_resource) < 0) ++ panic("Failed to insert eiu memory\n"); + +- if (request_mem_region(ltq_eiu_resource.start, +- resource_size(<q_eiu_resource), "eiu") < 0) +- panic("Failed to request eiu memory"); ++ if (request_mem_region(ltq_eiu_resource.start, ++ resource_size(<q_eiu_resource), "eiu") < 0) ++ panic("Failed to request eiu memory\n"); + +- ltq_eiu_membase = ioremap_nocache(ltq_eiu_resource.start, ++ ltq_eiu_membase = ioremap_nocache(ltq_eiu_resource.start, + resource_size(<q_eiu_resource)); +- if (!ltq_eiu_membase) +- panic("Failed to remap eiu memory"); ++ if (!ltq_eiu_membase) ++ panic("Failed to remap eiu memory\n"); ++ } + + /* make sure all irqs are turned off by default */ + for (i = 0; i < 5; i++) +@@ -295,8 +297,8 @@ void __init arch_init_irq(void) + + for (i = INT_NUM_IRQ0; + i <= (INT_NUM_IRQ0 + (5 * INT_NUM_IM_OFFSET)); i++) +- if ((i == LTQ_EIU_IR0) || (i == LTQ_EIU_IR1) || +- (i == LTQ_EIU_IR2)) ++ if (((i == LTQ_EIU_IR0) || (i == LTQ_EIU_IR1) || ++ (i == LTQ_EIU_IR2)) && LTQ_EIU_BASE_ADDR) + irq_set_chip_and_handler(i, <q_eiu_type, + handle_level_irq); + /* EIU3-5 only exist on ar9 and vr9 */ +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0005-MIPS-lantiq-add-basic-support-for-FALC-ON.patch b/target/linux/lantiq/patches-3.3/0005-MIPS-lantiq-add-basic-support-for-FALC-ON.patch new file mode 100644 index 0000000000..d05ebfaad2 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0005-MIPS-lantiq-add-basic-support-for-FALC-ON.patch @@ -0,0 +1,1088 @@ +From 53e05f3689ec846c2a89d16d3f74bac6c7de81ee Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Aug 2011 14:33:04 +0200 +Subject: [PATCH 05/70] MIPS: lantiq: add basic support for FALC-ON + +Adds support for the FALC-ON SoC. This SoC is from the FTTH/GPON SoC family. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +--- + .../include/asm/mach-lantiq/falcon/falcon_irq.h | 268 ++++++++++++++++++++ + arch/mips/include/asm/mach-lantiq/falcon/irq.h | 18 ++ + .../include/asm/mach-lantiq/falcon/lantiq_soc.h | 143 +++++++++++ + arch/mips/include/asm/mach-lantiq/lantiq.h | 1 + + arch/mips/lantiq/Kconfig | 4 + + arch/mips/lantiq/Makefile | 1 + + arch/mips/lantiq/Platform | 1 + + arch/mips/lantiq/falcon/Makefile | 1 + + arch/mips/lantiq/falcon/clk.c | 44 ++++ + arch/mips/lantiq/falcon/devices.c | 87 +++++++ + arch/mips/lantiq/falcon/devices.h | 18 ++ + arch/mips/lantiq/falcon/prom.c | 93 +++++++ + arch/mips/lantiq/falcon/reset.c | 87 +++++++ + arch/mips/lantiq/falcon/sysctrl.c | 183 +++++++++++++ + 14 files changed, 949 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h + create mode 100644 arch/mips/include/asm/mach-lantiq/falcon/irq.h + create mode 100644 arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h + create mode 100644 arch/mips/lantiq/falcon/Makefile + create mode 100644 arch/mips/lantiq/falcon/clk.c + create mode 100644 arch/mips/lantiq/falcon/devices.c + create mode 100644 arch/mips/lantiq/falcon/devices.h + create mode 100644 arch/mips/lantiq/falcon/prom.c + create mode 100644 arch/mips/lantiq/falcon/reset.c + create mode 100644 arch/mips/lantiq/falcon/sysctrl.c + +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h b/arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h +new file mode 100644 +index 0000000..4dc6466 +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/falcon/falcon_irq.h +@@ -0,0 +1,268 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2010 Thomas Langer ++ */ ++ ++#ifndef _FALCON_IRQ__ ++#define _FALCON_IRQ__ ++ ++#define INT_NUM_IRQ0 8 ++#define INT_NUM_IM0_IRL0 (INT_NUM_IRQ0 + 0) ++#define INT_NUM_IM1_IRL0 (INT_NUM_IM0_IRL0 + 32) ++#define INT_NUM_IM2_IRL0 (INT_NUM_IM1_IRL0 + 32) ++#define INT_NUM_IM3_IRL0 (INT_NUM_IM2_IRL0 + 32) ++#define INT_NUM_IM4_IRL0 (INT_NUM_IM3_IRL0 + 32) ++#define INT_NUM_EXTRA_START (INT_NUM_IM4_IRL0 + 32) ++#define INT_NUM_IM_OFFSET (INT_NUM_IM1_IRL0 - INT_NUM_IM0_IRL0) ++ ++#define MIPS_CPU_TIMER_IRQ 7 ++ ++/* HOST IF Event Interrupt */ ++#define FALCON_IRQ_HOST (INT_NUM_IM0_IRL0 + 0) ++/* HOST IF Mailbox0 Receive Interrupt */ ++#define FALCON_IRQ_HOST_MB0_RX (INT_NUM_IM0_IRL0 + 1) ++/* HOST IF Mailbox0 Transmit Interrupt */ ++#define FALCON_IRQ_HOST_MB0_TX (INT_NUM_IM0_IRL0 + 2) ++/* HOST IF Mailbox1 Receive Interrupt */ ++#define FALCON_IRQ_HOST_MB1_RX (INT_NUM_IM0_IRL0 + 3) ++/* HOST IF Mailbox1 Transmit Interrupt */ ++#define FALCON_IRQ_HOST_MB1_TX (INT_NUM_IM0_IRL0 + 4) ++/* I2C Last Single Data Transfer Request */ ++#define FALCON_IRQ_I2C_LSREQ (INT_NUM_IM0_IRL0 + 8) ++/* I2C Single Data Transfer Request */ ++#define FALCON_IRQ_I2C_SREQ (INT_NUM_IM0_IRL0 + 9) ++/* I2C Last Burst Data Transfer Request */ ++#define FALCON_IRQ_I2C_LBREQ (INT_NUM_IM0_IRL0 + 10) ++/* I2C Burst Data Transfer Request */ ++#define FALCON_IRQ_I2C_BREQ (INT_NUM_IM0_IRL0 + 11) ++/* I2C Error Interrupt */ ++#define FALCON_IRQ_I2C_I2C_ERR (INT_NUM_IM0_IRL0 + 12) ++/* I2C Protocol Interrupt */ ++#define FALCON_IRQ_I2C_I2C_P (INT_NUM_IM0_IRL0 + 13) ++/* SSC Transmit Interrupt */ ++#define FALCON_IRQ_SSC_T (INT_NUM_IM0_IRL0 + 14) ++/* SSC Receive Interrupt */ ++#define FALCON_IRQ_SSC_R (INT_NUM_IM0_IRL0 + 15) ++/* SSC Error Interrupt */ ++#define FALCON_IRQ_SSC_E (INT_NUM_IM0_IRL0 + 16) ++/* SSC Frame Interrupt */ ++#define FALCON_IRQ_SSC_F (INT_NUM_IM0_IRL0 + 17) ++/* Advanced Encryption Standard Interrupt */ ++#define FALCON_IRQ_AES_AES (INT_NUM_IM0_IRL0 + 27) ++/* Secure Hash Algorithm Interrupt */ ++#define FALCON_IRQ_SHA_HASH (INT_NUM_IM0_IRL0 + 28) ++/* PCM Receive Interrupt */ ++#define FALCON_IRQ_PCM_RX (INT_NUM_IM0_IRL0 + 29) ++/* PCM Transmit Interrupt */ ++#define FALCON_IRQ_PCM_TX (INT_NUM_IM0_IRL0 + 30) ++/* PCM Transmit Crash Interrupt */ ++#define FALCON_IRQ_PCM_HW2_CRASH (INT_NUM_IM0_IRL0 + 31) ++ ++/* EBU Serial Flash Command Error */ ++#define FALCON_IRQ_EBU_SF_CMDERR (INT_NUM_IM1_IRL0 + 0) ++/* EBU Serial Flash Command Overwrite Error */ ++#define FALCON_IRQ_EBU_SF_COVERR (INT_NUM_IM1_IRL0 + 1) ++/* EBU Serial Flash Busy */ ++#define FALCON_IRQ_EBU_SF_BUSY (INT_NUM_IM1_IRL0 + 2) ++/* External Interrupt from GPIO P0 */ ++#define FALCON_IRQ_GPIO_P0 (INT_NUM_IM1_IRL0 + 4) ++/* External Interrupt from GPIO P1 */ ++#define FALCON_IRQ_GPIO_P1 (INT_NUM_IM1_IRL0 + 5) ++/* External Interrupt from GPIO P2 */ ++#define FALCON_IRQ_GPIO_P2 (INT_NUM_IM1_IRL0 + 6) ++/* External Interrupt from GPIO P3 */ ++#define FALCON_IRQ_GPIO_P3 (INT_NUM_IM1_IRL0 + 7) ++/* External Interrupt from GPIO P4 */ ++#define FALCON_IRQ_GPIO_P4 (INT_NUM_IM1_IRL0 + 8) ++/* 8kHz backup interrupt derived from core-PLL */ ++#define FALCON_IRQ_FSC_BKP (INT_NUM_IM1_IRL0 + 10) ++/* FSC Timer Interrupt 0 */ ++#define FALCON_IRQ_FSCT_CMP0 (INT_NUM_IM1_IRL0 + 11) ++/* FSC Timer Interrupt 1 */ ++#define FALCON_IRQ_FSCT_CMP1 (INT_NUM_IM1_IRL0 + 12) ++/* 8kHz root interrupt derived from GPON interface */ ++#define FALCON_IRQ_FSC_ROOT (INT_NUM_IM1_IRL0 + 13) ++/* Time of Day */ ++#define FALCON_IRQ_TOD (INT_NUM_IM1_IRL0 + 14) ++/* PMA Interrupt from IntNode of the 200MHz Domain */ ++#define FALCON_IRQ_PMA_200M (INT_NUM_IM1_IRL0 + 15) ++/* PMA Interrupt from IntNode of the TX Clk Domain */ ++#define FALCON_IRQ_PMA_TX (INT_NUM_IM1_IRL0 + 16) ++/* PMA Interrupt from IntNode of the RX Clk Domain */ ++#define FALCON_IRQ_PMA_RX (INT_NUM_IM1_IRL0 + 17) ++/* SYS1 Interrupt */ ++#define FALCON_IRQ_SYS1 (INT_NUM_IM1_IRL0 + 20) ++/* SYS GPE Interrupt */ ++#define FALCON_IRQ_SYS_GPE (INT_NUM_IM1_IRL0 + 21) ++/* Watchdog Access Error Interrupt */ ++#define FALCON_IRQ_WDT_AEIR (INT_NUM_IM1_IRL0 + 24) ++/* Watchdog Prewarning Interrupt */ ++#define FALCON_IRQ_WDT_PIR (INT_NUM_IM1_IRL0 + 25) ++/* SBIU interrupt */ ++#define FALCON_IRQ_SBIU0 (INT_NUM_IM1_IRL0 + 27) ++/* FPI Bus Control Unit Interrupt */ ++#define FALCON_IRQ_BCU0 (INT_NUM_IM1_IRL0 + 29) ++/* DDR Controller Interrupt */ ++#define FALCON_IRQ_DDR (INT_NUM_IM1_IRL0 + 30) ++/* Crossbar Error Interrupt */ ++#define FALCON_IRQ_XBAR_ERROR (INT_NUM_IM1_IRL0 + 31) ++ ++/* ICTRLL 0 Interrupt */ ++#define FALCON_IRQ_ICTRLL0 (INT_NUM_IM2_IRL0 + 0) ++/* ICTRLL 1 Interrupt */ ++#define FALCON_IRQ_ICTRLL1 (INT_NUM_IM2_IRL0 + 1) ++/* ICTRLL 2 Interrupt */ ++#define FALCON_IRQ_ICTRLL2 (INT_NUM_IM2_IRL0 + 2) ++/* ICTRLL 3 Interrupt */ ++#define FALCON_IRQ_ICTRLL3 (INT_NUM_IM2_IRL0 + 3) ++/* OCTRLL 0 Interrupt */ ++#define FALCON_IRQ_OCTRLL0 (INT_NUM_IM2_IRL0 + 4) ++/* OCTRLL 1 Interrupt */ ++#define FALCON_IRQ_OCTRLL1 (INT_NUM_IM2_IRL0 + 5) ++/* OCTRLL 2 Interrupt */ ++#define FALCON_IRQ_OCTRLL2 (INT_NUM_IM2_IRL0 + 6) ++/* OCTRLL 3 Interrupt */ ++#define FALCON_IRQ_OCTRLL3 (INT_NUM_IM2_IRL0 + 7) ++/* OCTRLG Interrupt */ ++#define FALCON_IRQ_OCTRLG (INT_NUM_IM2_IRL0 + 9) ++/* IQM Interrupt */ ++#define FALCON_IRQ_IQM (INT_NUM_IM2_IRL0 + 10) ++/* FSQM Interrupt */ ++#define FALCON_IRQ_FSQM (INT_NUM_IM2_IRL0 + 11) ++/* TMU Interrupt */ ++#define FALCON_IRQ_TMU (INT_NUM_IM2_IRL0 + 12) ++/* LINK1 Interrupt */ ++#define FALCON_IRQ_LINK1 (INT_NUM_IM2_IRL0 + 14) ++/* ICTRLC 0 Interrupt */ ++#define FALCON_IRQ_ICTRLC0 (INT_NUM_IM2_IRL0 + 16) ++/* ICTRLC 1 Interrupt */ ++#define FALCON_IRQ_ICTRLC1 (INT_NUM_IM2_IRL0 + 17) ++/* OCTRLC Interrupt */ ++#define FALCON_IRQ_OCTRLC (INT_NUM_IM2_IRL0 + 18) ++/* CONFIG Break Interrupt */ ++#define FALCON_IRQ_CONFIG_BREAK (INT_NUM_IM2_IRL0 + 19) ++/* CONFIG Interrupt */ ++#define FALCON_IRQ_CONFIG (INT_NUM_IM2_IRL0 + 20) ++/* Dispatcher Interrupt */ ++#define FALCON_IRQ_DISP (INT_NUM_IM2_IRL0 + 21) ++/* TBM Interrupt */ ++#define FALCON_IRQ_TBM (INT_NUM_IM2_IRL0 + 22) ++/* GTC Downstream Interrupt */ ++#define FALCON_IRQ_GTC_DS (INT_NUM_IM2_IRL0 + 29) ++/* GTC Upstream Interrupt */ ++#define FALCON_IRQ_GTC_US (INT_NUM_IM2_IRL0 + 30) ++/* EIM Interrupt */ ++#define FALCON_IRQ_EIM (INT_NUM_IM2_IRL0 + 31) ++ ++/* ASC0 Transmit Interrupt */ ++#define FALCON_IRQ_ASC0_T (INT_NUM_IM3_IRL0 + 0) ++/* ASC0 Receive Interrupt */ ++#define FALCON_IRQ_ASC0_R (INT_NUM_IM3_IRL0 + 1) ++/* ASC0 Error Interrupt */ ++#define FALCON_IRQ_ASC0_E (INT_NUM_IM3_IRL0 + 2) ++/* ASC0 Transmit Buffer Interrupt */ ++#define FALCON_IRQ_ASC0_TB (INT_NUM_IM3_IRL0 + 3) ++/* ASC0 Autobaud Start Interrupt */ ++#define FALCON_IRQ_ASC0_ABST (INT_NUM_IM3_IRL0 + 4) ++/* ASC0 Autobaud Detection Interrupt */ ++#define FALCON_IRQ_ASC0_ABDET (INT_NUM_IM3_IRL0 + 5) ++/* ASC1 Modem Status Interrupt */ ++#define FALCON_IRQ_ASC0_MS (INT_NUM_IM3_IRL0 + 6) ++/* ASC0 Soft Flow Control Interrupt */ ++#define FALCON_IRQ_ASC0_SFC (INT_NUM_IM3_IRL0 + 7) ++/* ASC1 Transmit Interrupt */ ++#define FALCON_IRQ_ASC1_T (INT_NUM_IM3_IRL0 + 8) ++/* ASC1 Receive Interrupt */ ++#define FALCON_IRQ_ASC1_R (INT_NUM_IM3_IRL0 + 9) ++/* ASC1 Error Interrupt */ ++#define FALCON_IRQ_ASC1_E (INT_NUM_IM3_IRL0 + 10) ++/* ASC1 Transmit Buffer Interrupt */ ++#define FALCON_IRQ_ASC1_TB (INT_NUM_IM3_IRL0 + 11) ++/* ASC1 Autobaud Start Interrupt */ ++#define FALCON_IRQ_ASC1_ABST (INT_NUM_IM3_IRL0 + 12) ++/* ASC1 Autobaud Detection Interrupt */ ++#define FALCON_IRQ_ASC1_ABDET (INT_NUM_IM3_IRL0 + 13) ++/* ASC1 Modem Status Interrupt */ ++#define FALCON_IRQ_ASC1_MS (INT_NUM_IM3_IRL0 + 14) ++/* ASC1 Soft Flow Control Interrupt */ ++#define FALCON_IRQ_ASC1_SFC (INT_NUM_IM3_IRL0 + 15) ++/* GPTC Timer/Counter 1A Interrupt */ ++#define FALCON_IRQ_GPTC_TC1A (INT_NUM_IM3_IRL0 + 16) ++/* GPTC Timer/Counter 1B Interrupt */ ++#define FALCON_IRQ_GPTC_TC1B (INT_NUM_IM3_IRL0 + 17) ++/* GPTC Timer/Counter 2A Interrupt */ ++#define FALCON_IRQ_GPTC_TC2A (INT_NUM_IM3_IRL0 + 18) ++/* GPTC Timer/Counter 2B Interrupt */ ++#define FALCON_IRQ_GPTC_TC2B (INT_NUM_IM3_IRL0 + 19) ++/* GPTC Timer/Counter 3A Interrupt */ ++#define FALCON_IRQ_GPTC_TC3A (INT_NUM_IM3_IRL0 + 20) ++/* GPTC Timer/Counter 3B Interrupt */ ++#define FALCON_IRQ_GPTC_TC3B (INT_NUM_IM3_IRL0 + 21) ++/* DFEV0, Channel 1 Transmit Interrupt */ ++#define FALCON_IRQ_DFEV0_2TX (INT_NUM_IM3_IRL0 + 26) ++/* DFEV0, Channel 1 Receive Interrupt */ ++#define FALCON_IRQ_DFEV0_2RX (INT_NUM_IM3_IRL0 + 27) ++/* DFEV0, Channel 1 General Purpose Interrupt */ ++#define FALCON_IRQ_DFEV0_2GP (INT_NUM_IM3_IRL0 + 28) ++/* DFEV0, Channel 0 Transmit Interrupt */ ++#define FALCON_IRQ_DFEV0_1TX (INT_NUM_IM3_IRL0 + 29) ++/* DFEV0, Channel 0 Receive Interrupt */ ++#define FALCON_IRQ_DFEV0_1RX (INT_NUM_IM3_IRL0 + 30) ++/* DFEV0, Channel 0 General Purpose Interrupt */ ++#define FALCON_IRQ_DFEV0_1GP (INT_NUM_IM3_IRL0 + 31) ++ ++/* ICTRLL 0 Error */ ++#define FALCON_IRQ_ICTRLL0_ERR (INT_NUM_IM4_IRL0 + 0) ++/* ICTRLL 1 Error */ ++#define FALCON_IRQ_ICTRLL1_ERR (INT_NUM_IM4_IRL0 + 1) ++/* ICTRLL 2 Error */ ++#define FALCON_IRQ_ICTRLL2_ERR (INT_NUM_IM4_IRL0 + 2) ++/* ICTRLL 3 Error */ ++#define FALCON_IRQ_ICTRLL3_ERR (INT_NUM_IM4_IRL0 + 3) ++/* OCTRLL 0 Error */ ++#define FALCON_IRQ_OCTRLL0_ERR (INT_NUM_IM4_IRL0 + 4) ++/* OCTRLL 1 Error */ ++#define FALCON_IRQ_OCTRLL1_ERR (INT_NUM_IM4_IRL0 + 5) ++/* OCTRLL 2 Error */ ++#define FALCON_IRQ_OCTRLL2_ERR (INT_NUM_IM4_IRL0 + 6) ++/* OCTRLL 3 Error */ ++#define FALCON_IRQ_OCTRLL3_ERR (INT_NUM_IM4_IRL0 + 7) ++/* ICTRLG Error */ ++#define FALCON_IRQ_ICTRLG_ERR (INT_NUM_IM4_IRL0 + 8) ++/* OCTRLG Error */ ++#define FALCON_IRQ_OCTRLG_ERR (INT_NUM_IM4_IRL0 + 9) ++/* IQM Error */ ++#define FALCON_IRQ_IQM_ERR (INT_NUM_IM4_IRL0 + 10) ++/* FSQM Error */ ++#define FALCON_IRQ_FSQM_ERR (INT_NUM_IM4_IRL0 + 11) ++/* TMU Error */ ++#define FALCON_IRQ_TMU_ERR (INT_NUM_IM4_IRL0 + 12) ++/* MPS Status Interrupt #0 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR0 (INT_NUM_IM4_IRL0 + 14) ++/* MPS Status Interrupt #1 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR1 (INT_NUM_IM4_IRL0 + 15) ++/* MPS Status Interrupt #2 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR2 (INT_NUM_IM4_IRL0 + 16) ++/* MPS Status Interrupt #3 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR3 (INT_NUM_IM4_IRL0 + 17) ++/* MPS Status Interrupt #4 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR4 (INT_NUM_IM4_IRL0 + 18) ++/* MPS Status Interrupt #5 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR5 (INT_NUM_IM4_IRL0 + 19) ++/* MPS Status Interrupt #6 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR6 (INT_NUM_IM4_IRL0 + 20) ++/* MPS Status Interrupt #7 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR7 (INT_NUM_IM4_IRL0 + 21) ++/* MPS Status Interrupt #8 (VPE1 to VPE0) */ ++#define FALCON_IRQ_MPS_IR8 (INT_NUM_IM4_IRL0 + 22) ++/* VPE0 Exception Level Flag Interrupt */ ++#define FALCON_IRQ_VPE0_EXL (INT_NUM_IM4_IRL0 + 29) ++/* VPE0 Error Level Flag Interrupt */ ++#define FALCON_IRQ_VPE0_ERL (INT_NUM_IM4_IRL0 + 30) ++/* VPE0 Performance Monitoring Counter Interrupt */ ++#define FALCON_IRQ_VPE0_PMCIR (INT_NUM_IM4_IRL0 + 31) ++ ++#endif /* _FALCON_IRQ__ */ +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/irq.h b/arch/mips/include/asm/mach-lantiq/falcon/irq.h +new file mode 100644 +index 0000000..2caccd9 +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/falcon/irq.h +@@ -0,0 +1,18 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ */ ++ ++#ifndef __FALCON_IRQ_H ++#define __FALCON_IRQ_H ++ ++#include ++ ++#define NR_IRQS 328 ++ ++#include_next ++ ++#endif +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +new file mode 100644 +index 0000000..b074748 +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +@@ -0,0 +1,143 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2010 John Crispin ++ */ ++ ++#ifndef _LTQ_FALCON_H__ ++#define _LTQ_FALCON_H__ ++ ++#ifdef CONFIG_SOC_FALCON ++ ++#include ++ ++/* Chip IDs */ ++#define SOC_ID_FALCON 0x01B8 ++ ++/* SoC Types */ ++#define SOC_TYPE_FALCON 0x01 ++ ++/* ASC0/1 - serial port */ ++#define LTQ_ASC0_BASE_ADDR 0x1E100C00 ++#define LTQ_ASC1_BASE_ADDR 0x1E100B00 ++#define LTQ_ASC_SIZE 0x100 ++ ++#define LTQ_ASC_TIR(x) (INT_NUM_IM3_IRL0 + (x * 8)) ++#define LTQ_ASC_RIR(x) (INT_NUM_IM3_IRL0 + (x * 8) + 1) ++#define LTQ_ASC_EIR(x) (INT_NUM_IM3_IRL0 + (x * 8) + 2) ++ ++/* ++ * during early_printk no ioremap possible at this early stage ++ * lets use KSEG1 instead ++ */ ++#define LTQ_EARLY_ASC KSEG1ADDR(LTQ_ASC0_BASE_ADDR) ++ ++/* ICU - interrupt control unit */ ++#define LTQ_ICU_BASE_ADDR 0x1F880200 ++#define LTQ_ICU_SIZE 0x100 ++ ++/* WDT */ ++#define LTQ_WDT_BASE_ADDR 0x1F8803F0 ++#define LTQ_WDT_SIZE 0x10 ++ ++#define LTQ_RST_CAUSE_WDTRST 0x0002 ++ ++/* EBU - external bus unit */ ++#define LTQ_EBU_BASE_ADDR 0x18000000 ++#define LTQ_EBU_SIZE 0x0100 ++ ++#define LTQ_EBU_MODCON 0x000C ++ ++/* GPIO */ ++#define LTQ_GPIO0_BASE_ADDR 0x1D810000 ++#define LTQ_GPIO0_SIZE 0x0080 ++#define LTQ_GPIO1_BASE_ADDR 0x1E800100 ++#define LTQ_GPIO1_SIZE 0x0080 ++#define LTQ_GPIO2_BASE_ADDR 0x1D810100 ++#define LTQ_GPIO2_SIZE 0x0080 ++#define LTQ_GPIO3_BASE_ADDR 0x1E800200 ++#define LTQ_GPIO3_SIZE 0x0080 ++#define LTQ_GPIO4_BASE_ADDR 0x1E800300 ++#define LTQ_GPIO4_SIZE 0x0080 ++#define LTQ_PADCTRL0_BASE_ADDR 0x1DB01000 ++#define LTQ_PADCTRL0_SIZE 0x0100 ++#define LTQ_PADCTRL1_BASE_ADDR 0x1E800400 ++#define LTQ_PADCTRL1_SIZE 0x0100 ++#define LTQ_PADCTRL2_BASE_ADDR 0x1DB02000 ++#define LTQ_PADCTRL2_SIZE 0x0100 ++#define LTQ_PADCTRL3_BASE_ADDR 0x1E800500 ++#define LTQ_PADCTRL3_SIZE 0x0100 ++#define LTQ_PADCTRL4_BASE_ADDR 0x1E800600 ++#define LTQ_PADCTRL4_SIZE 0x0100 ++ ++/* CHIP ID */ ++#define LTQ_STATUS_BASE_ADDR 0x1E802000 ++ ++#define LTQ_FALCON_CHIPID ((u32 *)(KSEG1 + LTQ_STATUS_BASE_ADDR + 0x0c)) ++#define LTQ_FALCON_CHIPTYPE ((u32 *)(KSEG1 + LTQ_STATUS_BASE_ADDR + 0x38)) ++#define LTQ_FALCON_CHIPCONF ((u32 *)(KSEG1 + LTQ_STATUS_BASE_ADDR + 0x40)) ++ ++/* SYSCTL - start/stop/restart/configure/... different parts of the Soc */ ++#define LTQ_SYS1_BASE_ADDR 0x1EF00000 ++#define LTQ_SYS1_SIZE 0x0100 ++#define LTQ_STATUS_BASE_ADDR 0x1E802000 ++#define LTQ_STATUS_SIZE 0x0080 ++#define LTQ_SYS_ETH_BASE_ADDR 0x1DB00000 ++#define LTQ_SYS_ETH_SIZE 0x0100 ++#define LTQ_SYS_GPE_BASE_ADDR 0x1D700000 ++#define LTQ_SYS_GPE_SIZE 0x0100 ++ ++#define SYSCTL_SYS1 0 ++#define SYSCTL_SYSETH 1 ++#define SYSCTL_SYSGPE 2 ++ ++/* Activation Status Register */ ++#define ACTS_ASC1_ACT 0x00000800 ++#define ACTS_P0 0x00010000 ++#define ACTS_P1 0x00010000 ++#define ACTS_P2 0x00020000 ++#define ACTS_P3 0x00020000 ++#define ACTS_P4 0x00040000 ++#define ACTS_PADCTRL0 0x00100000 ++#define ACTS_PADCTRL1 0x00100000 ++#define ACTS_PADCTRL2 0x00200000 ++#define ACTS_PADCTRL3 0x00200000 ++#define ACTS_PADCTRL4 0x00400000 ++ ++extern void ltq_sysctl_activate(int module, unsigned int mask); ++extern void ltq_sysctl_deactivate(int module, unsigned int mask); ++extern void ltq_sysctl_clken(int module, unsigned int mask); ++extern void ltq_sysctl_clkdis(int module, unsigned int mask); ++extern void ltq_sysctl_reboot(int module, unsigned int mask); ++extern int ltq_gpe_is_activated(unsigned int mask); ++ ++/* global register ranges */ ++extern __iomem void *ltq_ebu_membase; ++extern __iomem void *ltq_sys1_membase; ++#define ltq_ebu_w32(x, y) ltq_w32((x), ltq_ebu_membase + (y)) ++#define ltq_ebu_r32(x) ltq_r32(ltq_ebu_membase + (x)) ++#define ltq_ebu_w32_mask(clear, set, reg) \ ++ ltq_ebu_w32((ltq_ebu_r32(reg) & ~(clear)) | (set), reg) ++ ++#define ltq_sys1_w32(x, y) ltq_w32((x), ltq_sys1_membase + (y)) ++#define ltq_sys1_r32(x) ltq_r32(ltq_sys1_membase + (x)) ++#define ltq_sys1_w32_mask(clear, set, reg) \ ++ ltq_sys1_w32((ltq_sys1_r32(reg) & ~(clear)) | (set), reg) ++ ++/* gpio_request wrapper to help configure the pin */ ++extern int ltq_gpio_request(unsigned int pin, unsigned int mux, ++ unsigned int dir, const char *name); ++extern int ltq_gpio_mux_set(unsigned int pin, unsigned int mux); ++ ++/* to keep the irq code generic we need to define these to 0 as falcon ++ has no EIU/EBU */ ++#define LTQ_EIU_BASE_ADDR 0 ++#define LTQ_EBU_PCC_ISTAT 0 ++ ++#define ltq_is_ar9() 0 ++#define ltq_is_vr9() 0 ++ ++#endif /* CONFIG_SOC_FALCON */ ++#endif /* _LTQ_XWAY_H__ */ +diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h +index 66d7300..188de0f 100644 +--- a/arch/mips/include/asm/mach-lantiq/lantiq.h ++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h +@@ -25,6 +25,7 @@ extern unsigned int ltq_get_soc_type(void); + /* clock speeds */ + #define CLOCK_60M 60000000 + #define CLOCK_83M 83333333 ++#define CLOCK_100M 100000000 + #define CLOCK_111M 111111111 + #define CLOCK_133M 133333333 + #define CLOCK_167M 166666667 +diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig +index 3fccf21..cb6b39f 100644 +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -16,8 +16,12 @@ config SOC_XWAY + bool "XWAY" + select SOC_TYPE_XWAY + select HW_HAS_PCI ++ ++config SOC_FALCON ++ bool "FALCON" + endchoice + + source "arch/mips/lantiq/xway/Kconfig" ++source "arch/mips/lantiq/falcon/Kconfig" + + endif +diff --git a/arch/mips/lantiq/Makefile b/arch/mips/lantiq/Makefile +index e5dae0e..7e9c69e 100644 +--- a/arch/mips/lantiq/Makefile ++++ b/arch/mips/lantiq/Makefile +@@ -9,3 +9,4 @@ obj-y := irq.o setup.o clk.o prom.o devices.o + obj-$(CONFIG_EARLY_PRINTK) += early_printk.o + + obj-$(CONFIG_SOC_TYPE_XWAY) += xway/ ++obj-$(CONFIG_SOC_FALCON) += falcon/ +diff --git a/arch/mips/lantiq/Platform b/arch/mips/lantiq/Platform +index f3dff05..b3ec498 100644 +--- a/arch/mips/lantiq/Platform ++++ b/arch/mips/lantiq/Platform +@@ -6,3 +6,4 @@ platform-$(CONFIG_LANTIQ) += lantiq/ + cflags-$(CONFIG_LANTIQ) += -I$(srctree)/arch/mips/include/asm/mach-lantiq + load-$(CONFIG_LANTIQ) = 0xffffffff80002000 + cflags-$(CONFIG_SOC_TYPE_XWAY) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/xway ++cflags-$(CONFIG_SOC_FALCON) += -I$(srctree)/arch/mips/include/asm/mach-lantiq/falcon +diff --git a/arch/mips/lantiq/falcon/Makefile b/arch/mips/lantiq/falcon/Makefile +new file mode 100644 +index 0000000..e9c7455 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/Makefile +@@ -0,0 +1 @@ ++obj-y := clk.o prom.o reset.o sysctrl.o devices.o +diff --git a/arch/mips/lantiq/falcon/clk.c b/arch/mips/lantiq/falcon/clk.c +new file mode 100644 +index 0000000..afe1b52 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/clk.c +@@ -0,0 +1,44 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "devices.h" ++ ++/* CPU0 Clock Control Register */ ++#define LTQ_SYS1_CPU0CC 0x0040 ++/* clock divider bit */ ++#define LTQ_CPU0CC_CPUDIV 0x0001 ++ ++unsigned int ++ltq_get_io_region_clock(void) ++{ ++ return CLOCK_200M; ++} ++EXPORT_SYMBOL(ltq_get_io_region_clock); ++ ++unsigned int ++ltq_get_cpu_hz(void) ++{ ++ if (ltq_sys1_r32(LTQ_SYS1_CPU0CC) & LTQ_CPU0CC_CPUDIV) ++ return CLOCK_200M; ++ else ++ return CLOCK_400M; ++} ++EXPORT_SYMBOL(ltq_get_cpu_hz); ++ ++unsigned int ++ltq_get_fpi_hz(void) ++{ ++ return CLOCK_100M; ++} ++EXPORT_SYMBOL(ltq_get_fpi_hz); +diff --git a/arch/mips/lantiq/falcon/devices.c b/arch/mips/lantiq/falcon/devices.c +new file mode 100644 +index 0000000..c4606f2 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/devices.c +@@ -0,0 +1,87 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++ ++#include ++ ++#include "devices.h" ++ ++/* nand flash */ ++/* address lines used for NAND control signals */ ++#define NAND_ADDR_ALE 0x10000 ++#define NAND_ADDR_CLE 0x20000 ++/* Ready/Busy Status */ ++#define MODCON_STS 0x0002 ++/* Ready/Busy Status Edge */ ++#define MODCON_STSEDGE 0x0004 ++ ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++ ++static int ++falcon_nand_ready(struct mtd_info *mtd) ++{ ++ u32 modcon = ltq_ebu_r32(LTQ_EBU_MODCON); ++ ++ return (((modcon & (MODCON_STS | MODCON_STSEDGE)) == ++ (MODCON_STS | MODCON_STSEDGE))); ++} ++ ++static void ++falcon_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd->priv; ++ unsigned long nandaddr = (unsigned long) this->IO_ADDR_W; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ nandaddr &= ~(NAND_ADDR_ALE | NAND_ADDR_CLE); ++ ++ if (ctrl & NAND_CLE) ++ nandaddr |= NAND_ADDR_CLE; ++ if (ctrl & NAND_ALE) ++ nandaddr |= NAND_ADDR_ALE; ++ ++ this->IO_ADDR_W = (void __iomem *) nandaddr; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, this->IO_ADDR_W); ++} ++ ++static struct platform_nand_data falcon_flash_nand_data = { ++ .chip = { ++ .nr_chips = 1, ++ .chip_delay = 25, ++ .part_probe_types = part_probes, ++ }, ++ .ctrl = { ++ .cmd_ctrl = falcon_hwcontrol, ++ .dev_ready = falcon_nand_ready, ++ } ++}; ++ ++static struct resource ltq_nand_res = ++ MEM_RES("nand", LTQ_FLASH_START, LTQ_FLASH_MAX); ++ ++static struct platform_device ltq_flash_nand = { ++ .name = "gen_nand", ++ .id = -1, ++ .num_resources = 1, ++ .resource = <q_nand_res, ++ .dev = { ++ .platform_data = &falcon_flash_nand_data, ++ }, ++}; ++ ++void __init ++falcon_register_nand(void) ++{ ++ platform_device_register(<q_flash_nand); ++} +diff --git a/arch/mips/lantiq/falcon/devices.h b/arch/mips/lantiq/falcon/devices.h +new file mode 100644 +index 0000000..e802a7c +--- /dev/null ++++ b/arch/mips/lantiq/falcon/devices.h +@@ -0,0 +1,18 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#ifndef _FALCON_DEVICES_H__ ++#define _FALCON_DEVICES_H__ ++ ++#include "../devices.h" ++ ++extern void falcon_register_nand(void); ++ ++#endif +diff --git a/arch/mips/lantiq/falcon/prom.c b/arch/mips/lantiq/falcon/prom.c +new file mode 100644 +index 0000000..b50d6f9 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/prom.c +@@ -0,0 +1,93 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++ ++#include "devices.h" ++ ++#include "../prom.h" ++ ++#define SOC_FALCON "Falcon" ++#define SOC_FALCON_D "Falcon-D" ++#define SOC_FALCON_V "Falcon-V" ++#define SOC_FALCON_M "Falcon-M" ++ ++#define PART_SHIFT 12 ++#define PART_MASK 0x0FFFF000 ++#define REV_SHIFT 28 ++#define REV_MASK 0xF0000000 ++#define SREV_SHIFT 22 ++#define SREV_MASK 0x03C00000 ++#define TYPE_SHIFT 26 ++#define TYPE_MASK 0x3C000000 ++ ++#define MUXC_SIF_RX_PIN 112 ++#define MUXC_SIF_TX_PIN 113 ++ ++/* this parameter allows us enable/disable asc1 via commandline */ ++static int register_asc1; ++static int __init ++ltq_parse_asc1(char *p) ++{ ++ register_asc1 = 1; ++ return 0; ++} ++__setup("use_asc1", ltq_parse_asc1); ++ ++void __init ++ltq_soc_setup(void) ++{ ++ ltq_register_asc(0); ++ ltq_register_wdt(); ++ falcon_register_gpio(); ++ if (register_asc1) { ++ ltq_register_asc(1); ++ if (ltq_gpio_request(MUXC_SIF_RX_PIN, 3, 0, "asc1-rx")) ++ pr_err("failed to request asc1-rx"); ++ if (ltq_gpio_request(MUXC_SIF_TX_PIN, 3, 1, "asc1-tx")) ++ pr_err("failed to request asc1-tx"); ++ ltq_sysctl_activate(SYSCTL_SYS1, ACTS_ASC1_ACT); ++ } ++} ++ ++void __init ++ltq_soc_detect(struct ltq_soc_info *i) ++{ ++ u32 type; ++ i->partnum = (ltq_r32(LTQ_FALCON_CHIPID) & PART_MASK) >> PART_SHIFT; ++ i->rev = (ltq_r32(LTQ_FALCON_CHIPID) & REV_MASK) >> REV_SHIFT; ++ i->srev = ((ltq_r32(LTQ_FALCON_CHIPCONF) & SREV_MASK) >> SREV_SHIFT); ++ sprintf(i->rev_type, "%c%d%d", (i->srev & 0x4) ? ('B') : ('A'), ++ i->rev & 0x7, (i->srev & 0x3) + 1); ++ ++ switch (i->partnum) { ++ case SOC_ID_FALCON: ++ type = (ltq_r32(LTQ_FALCON_CHIPTYPE) & TYPE_MASK) >> TYPE_SHIFT; ++ switch (type) { ++ case 0: ++ i->name = SOC_FALCON_D; ++ break; ++ case 1: ++ i->name = SOC_FALCON_V; ++ break; ++ case 2: ++ i->name = SOC_FALCON_M; ++ break; ++ default: ++ i->name = SOC_FALCON; ++ break; ++ } ++ i->type = SOC_TYPE_FALCON; ++ break; ++ ++ default: ++ unreachable(); ++ break; ++ } ++} +diff --git a/arch/mips/lantiq/falcon/reset.c b/arch/mips/lantiq/falcon/reset.c +new file mode 100644 +index 0000000..cbcadc5 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/reset.c +@@ -0,0 +1,87 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* CPU0 Reset Source Register */ ++#define LTQ_SYS1_CPU0RS 0x0040 ++/* reset cause mask */ ++#define LTQ_CPU0RS_MASK 0x0003 ++ ++int ++ltq_reset_cause(void) ++{ ++ return ltq_sys1_r32(LTQ_SYS1_CPU0RS) & LTQ_CPU0RS_MASK; ++} ++EXPORT_SYMBOL_GPL(ltq_reset_cause); ++ ++#define BOOT_REG_BASE (KSEG1 | 0x1F200000) ++#define BOOT_PW1_REG (BOOT_REG_BASE | 0x20) ++#define BOOT_PW2_REG (BOOT_REG_BASE | 0x24) ++#define BOOT_PW1 0x4C545100 ++#define BOOT_PW2 0x0051544C ++ ++#define WDT_REG_BASE (KSEG1 | 0x1F8803F0) ++#define WDT_PW1 0x00BE0000 ++#define WDT_PW2 0x00DC0000 ++ ++static void ++ltq_machine_restart(char *command) ++{ ++ pr_notice("System restart\n"); ++ local_irq_disable(); ++ ++ /* reboot magic */ ++ ltq_w32(BOOT_PW1, (void *)BOOT_PW1_REG); /* 'LTQ\0' */ ++ ltq_w32(BOOT_PW2, (void *)BOOT_PW2_REG); /* '\0QTL' */ ++ ltq_w32(0, (void *)BOOT_REG_BASE); /* reset Bootreg RVEC */ ++ ++ /* watchdog magic */ ++ ltq_w32(WDT_PW1, (void *)WDT_REG_BASE); ++ ltq_w32(WDT_PW2 | ++ (0x3 << 26) | /* PWL */ ++ (0x2 << 24) | /* CLKDIV */ ++ (0x1 << 31) | /* enable */ ++ (1), /* reload */ ++ (void *)WDT_REG_BASE); ++ unreachable(); ++} ++ ++static void ++ltq_machine_halt(void) ++{ ++ pr_notice("System halted.\n"); ++ local_irq_disable(); ++ unreachable(); ++} ++ ++static void ++ltq_machine_power_off(void) ++{ ++ pr_notice("Please turn off the power now.\n"); ++ local_irq_disable(); ++ unreachable(); ++} ++ ++static int __init ++mips_reboot_setup(void) ++{ ++ _machine_restart = ltq_machine_restart; ++ _machine_halt = ltq_machine_halt; ++ pm_power_off = ltq_machine_power_off; ++ return 0; ++} ++ ++arch_initcall(mips_reboot_setup); +diff --git a/arch/mips/lantiq/falcon/sysctrl.c b/arch/mips/lantiq/falcon/sysctrl.c +new file mode 100644 +index 0000000..905a142 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/sysctrl.c +@@ -0,0 +1,183 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "devices.h" ++ ++/* infrastructure control register */ ++#define SYS1_INFRAC 0x00bc ++/* Configuration fuses for drivers and pll */ ++#define STATUS_CONFIG 0x0040 ++ ++/* GPE frequency selection */ ++#define GPPC_OFFSET 24 ++#define GPEFREQ_MASK 0x00000C0 ++#define GPEFREQ_OFFSET 10 ++/* Clock status register */ ++#define LTQ_SYSCTL_CLKS 0x0000 ++/* Clock enable register */ ++#define LTQ_SYSCTL_CLKEN 0x0004 ++/* Clock clear register */ ++#define LTQ_SYSCTL_CLKCLR 0x0008 ++/* Activation Status Register */ ++#define LTQ_SYSCTL_ACTS 0x0020 ++/* Activation Register */ ++#define LTQ_SYSCTL_ACT 0x0024 ++/* Deactivation Register */ ++#define LTQ_SYSCTL_DEACT 0x0028 ++/* reboot Register */ ++#define LTQ_SYSCTL_RBT 0x002c ++ ++static struct resource ltq_sysctl_res[] = { ++ MEM_RES("sys1", LTQ_SYS1_BASE_ADDR, LTQ_SYS1_SIZE), ++ MEM_RES("syseth", LTQ_SYS_ETH_BASE_ADDR, LTQ_SYS_ETH_SIZE), ++ MEM_RES("sysgpe", LTQ_SYS_GPE_BASE_ADDR, LTQ_SYS_GPE_SIZE), ++}; ++ ++static struct resource ltq_status_res = ++ MEM_RES("status", LTQ_STATUS_BASE_ADDR, LTQ_STATUS_SIZE); ++static struct resource ltq_ebu_res = ++ MEM_RES("ebu", LTQ_EBU_BASE_ADDR, LTQ_EBU_SIZE); ++ ++static void __iomem *ltq_sysctl[3]; ++static void __iomem *ltq_status_membase; ++void __iomem *ltq_sys1_membase; ++void __iomem *ltq_ebu_membase; ++ ++#define ltq_reg_w32(m, x, y) ltq_w32((x), ltq_sysctl[m] + (y)) ++#define ltq_reg_r32(m, x) ltq_r32(ltq_sysctl[m] + (x)) ++#define ltq_reg_w32_mask(m, clear, set, reg) \ ++ ltq_reg_w32(m, (ltq_reg_r32(m, reg) & ~(clear)) | (set), reg) ++ ++#define ltq_status_w32(x, y) ltq_w32((x), ltq_status_membase + (y)) ++#define ltq_status_r32(x) ltq_r32(ltq_status_membase + (x)) ++ ++static inline void ++ltq_sysctl_wait(int module, unsigned int mask, ++ unsigned int test, unsigned int reg) ++{ ++ int err = 1000000; ++ ++ do {} while (--err && ((ltq_reg_r32(module, reg) ++ & mask) != test)); ++ if (!err) ++ pr_err("module de/activation failed %d %08X %08X\n", ++ module, mask, test); ++} ++ ++void ++ltq_sysctl_activate(int module, unsigned int mask) ++{ ++ if (module > SYSCTL_SYSGPE) ++ return; ++ ++ ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKEN); ++ ltq_reg_w32(module, mask, LTQ_SYSCTL_ACT); ++ ltq_sysctl_wait(module, mask, mask, LTQ_SYSCTL_ACTS); ++} ++EXPORT_SYMBOL(ltq_sysctl_activate); ++ ++void ++ltq_sysctl_deactivate(int module, unsigned int mask) ++{ ++ if (module > SYSCTL_SYSGPE) ++ return; ++ ++ ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKCLR); ++ ltq_reg_w32(module, mask, LTQ_SYSCTL_DEACT); ++ ltq_sysctl_wait(module, mask, 0, LTQ_SYSCTL_ACTS); ++} ++EXPORT_SYMBOL(ltq_sysctl_deactivate); ++ ++void ++ltq_sysctl_clken(int module, unsigned int mask) ++{ ++ if (module > SYSCTL_SYSGPE) ++ return; ++ ++ ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKEN); ++ ltq_sysctl_wait(module, mask, mask, LTQ_SYSCTL_CLKS); ++} ++EXPORT_SYMBOL(ltq_sysctl_clken); ++ ++void ++ltq_sysctl_clkdis(int module, unsigned int mask) ++{ ++ if (module > SYSCTL_SYSGPE) ++ return; ++ ++ ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKCLR); ++ ltq_sysctl_wait(module, mask, 0, LTQ_SYSCTL_CLKS); ++} ++EXPORT_SYMBOL(ltq_sysctl_clkdis); ++ ++void ++ltq_sysctl_reboot(int module, unsigned int mask) ++{ ++ unsigned int act; ++ ++ if (module > SYSCTL_SYSGPE) ++ return; ++ ++ act = ltq_reg_r32(module, LTQ_SYSCTL_ACT); ++ if ((~act & mask) != 0) ++ ltq_sysctl_activate(module, ~act & mask); ++ ltq_reg_w32(module, act & mask, LTQ_SYSCTL_RBT); ++ ltq_sysctl_wait(module, mask, mask, LTQ_SYSCTL_ACTS); ++} ++EXPORT_SYMBOL(ltq_sysctl_reboot); ++ ++/* enable the ONU core */ ++static void ++ltq_gpe_enable(void) ++{ ++ unsigned int freq; ++ unsigned int status; ++ ++ /* if if the clock is already enabled */ ++ status = ltq_reg_r32(SYSCTL_SYS1, SYS1_INFRAC); ++ if (status & (1 << (GPPC_OFFSET + 1))) ++ return; ++ ++ if (ltq_status_r32(STATUS_CONFIG) == 0) ++ freq = 1; /* use 625MHz on unfused chip */ ++ else ++ freq = (ltq_status_r32(STATUS_CONFIG) & ++ GPEFREQ_MASK) >> ++ GPEFREQ_OFFSET; ++ ++ /* apply new frequency */ ++ ltq_reg_w32_mask(SYSCTL_SYS1, 7 << (GPPC_OFFSET + 1), ++ freq << (GPPC_OFFSET + 2) , SYS1_INFRAC); ++ udelay(1); ++ ++ /* enable new frequency */ ++ ltq_reg_w32_mask(SYSCTL_SYS1, 0, 1 << (GPPC_OFFSET + 1), SYS1_INFRAC); ++ udelay(1); ++} ++ ++void __init ++ltq_soc_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < 3; i++) ++ ltq_sysctl[i] = ltq_remap_resource(<q_sysctl_res[i]); ++ ++ ltq_sys1_membase = ltq_sysctl[0]; ++ ltq_status_membase = ltq_remap_resource(<q_status_res); ++ ltq_ebu_membase = ltq_remap_resource(<q_ebu_res); ++ ++ ltq_gpe_enable(); ++} +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0006-MIPS-lantiq-add-support-for-FALC-ON-GPIOs.patch b/target/linux/lantiq/patches-3.3/0006-MIPS-lantiq-add-support-for-FALC-ON-GPIOs.patch new file mode 100644 index 0000000000..3536496883 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0006-MIPS-lantiq-add-support-for-FALC-ON-GPIOs.patch @@ -0,0 +1,501 @@ +From 92cd0e8b6247ac8c0db5cdc570543c6caa51b1ab Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Aug 2011 14:35:02 +0200 +Subject: [PATCH 06/70] MIPS: lantiq: add support for FALC-ON GPIOs + +FALC-ON uses a different GPIO core than the other Lantiq SoCs. This patch adds +the new driver. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +--- + arch/mips/lantiq/falcon/Makefile | 2 +- + arch/mips/lantiq/falcon/devices.c | 41 ++++ + arch/mips/lantiq/falcon/devices.h | 2 + + arch/mips/lantiq/falcon/gpio.c | 399 +++++++++++++++++++++++++++++++++++++ + 4 files changed, 443 insertions(+), 1 deletions(-) + create mode 100644 arch/mips/lantiq/falcon/gpio.c + +diff --git a/arch/mips/lantiq/falcon/Makefile b/arch/mips/lantiq/falcon/Makefile +index e9c7455..de72209 100644 +--- a/arch/mips/lantiq/falcon/Makefile ++++ b/arch/mips/lantiq/falcon/Makefile +@@ -1 +1 @@ +-obj-y := clk.o prom.o reset.o sysctrl.o devices.o ++obj-y := clk.o prom.o reset.o sysctrl.o devices.o gpio.o +diff --git a/arch/mips/lantiq/falcon/devices.c b/arch/mips/lantiq/falcon/devices.c +index c4606f2..4f47b44 100644 +--- a/arch/mips/lantiq/falcon/devices.c ++++ b/arch/mips/lantiq/falcon/devices.c +@@ -9,6 +9,7 @@ + + #include + #include ++#include + + #include + +@@ -85,3 +86,43 @@ falcon_register_nand(void) + { + platform_device_register(<q_flash_nand); + } ++ ++/* gpio */ ++#define DECLARE_GPIO_RES(port) \ ++static struct resource falcon_gpio ## port ## _res[] = { \ ++ MEM_RES("gpio"#port, LTQ_GPIO ## port ## _BASE_ADDR, \ ++ LTQ_GPIO ## port ## _SIZE), \ ++ MEM_RES("padctrl"#port, LTQ_PADCTRL ## port ## _BASE_ADDR, \ ++ LTQ_PADCTRL ## port ## _SIZE), \ ++ IRQ_RES("gpio_mux"#port, FALCON_IRQ_GPIO_P ## port) \ ++} ++DECLARE_GPIO_RES(0); ++DECLARE_GPIO_RES(1); ++DECLARE_GPIO_RES(2); ++DECLARE_GPIO_RES(3); ++DECLARE_GPIO_RES(4); ++ ++void __init ++falcon_register_gpio(void) ++{ ++ platform_device_register_simple("falcon_gpio", 0, ++ falcon_gpio0_res, ARRAY_SIZE(falcon_gpio0_res)); ++ platform_device_register_simple("falcon_gpio", 1, ++ falcon_gpio1_res, ARRAY_SIZE(falcon_gpio1_res)); ++ platform_device_register_simple("falcon_gpio", 2, ++ falcon_gpio2_res, ARRAY_SIZE(falcon_gpio2_res)); ++ ltq_sysctl_activate(SYSCTL_SYS1, ACTS_PADCTRL1 | ACTS_P1); ++ ltq_sysctl_activate(SYSCTL_SYSETH, ACTS_PADCTRL0 | ++ ACTS_PADCTRL2 | ACTS_P0 | ACTS_P2); ++} ++ ++void __init ++falcon_register_gpio_extra(void) ++{ ++ platform_device_register_simple("falcon_gpio", 3, ++ falcon_gpio3_res, ARRAY_SIZE(falcon_gpio3_res)); ++ platform_device_register_simple("falcon_gpio", 4, ++ falcon_gpio4_res, ARRAY_SIZE(falcon_gpio4_res)); ++ ltq_sysctl_activate(SYSCTL_SYS1, ++ ACTS_PADCTRL3 | ACTS_PADCTRL4 | ACTS_P3 | ACTS_P4); ++} +diff --git a/arch/mips/lantiq/falcon/devices.h b/arch/mips/lantiq/falcon/devices.h +index e802a7c..18be8b6 100644 +--- a/arch/mips/lantiq/falcon/devices.h ++++ b/arch/mips/lantiq/falcon/devices.h +@@ -14,5 +14,7 @@ + #include "../devices.h" + + extern void falcon_register_nand(void); ++extern void falcon_register_gpio(void); ++extern void falcon_register_gpio_extra(void); + + #endif +diff --git a/arch/mips/lantiq/falcon/gpio.c b/arch/mips/lantiq/falcon/gpio.c +new file mode 100644 +index 0000000..28f8639 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/gpio.c +@@ -0,0 +1,399 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* Multiplexer Control Register */ ++#define LTQ_PADC_MUX(x) (x * 0x4) ++/* Pad Control Availability Register */ ++#define LTQ_PADC_AVAIL 0x000000F0 ++ ++/* Data Output Register */ ++#define LTQ_GPIO_OUT 0x00000000 ++/* Data Input Register */ ++#define LTQ_GPIO_IN 0x00000004 ++/* Direction Register */ ++#define LTQ_GPIO_DIR 0x00000008 ++/* External Interrupt Control Register 0 */ ++#define LTQ_GPIO_EXINTCR0 0x00000018 ++/* External Interrupt Control Register 1 */ ++#define LTQ_GPIO_EXINTCR1 0x0000001C ++/* IRN Capture Register */ ++#define LTQ_GPIO_IRNCR 0x00000020 ++/* IRN Interrupt Configuration Register */ ++#define LTQ_GPIO_IRNCFG 0x0000002C ++/* IRN Interrupt Enable Set Register */ ++#define LTQ_GPIO_IRNRNSET 0x00000030 ++/* IRN Interrupt Enable Clear Register */ ++#define LTQ_GPIO_IRNENCLR 0x00000034 ++/* Output Set Register */ ++#define LTQ_GPIO_OUTSET 0x00000040 ++/* Output Cler Register */ ++#define LTQ_GPIO_OUTCLR 0x00000044 ++/* Direction Clear Register */ ++#define LTQ_GPIO_DIRSET 0x00000048 ++/* Direction Set Register */ ++#define LTQ_GPIO_DIRCLR 0x0000004C ++ ++/* turn a gpio_chip into a falcon_gpio_port */ ++#define ctop(c) container_of(c, struct falcon_gpio_port, gpio_chip) ++/* turn a irq_data into a falcon_gpio_port */ ++#define itop(i) ((struct falcon_gpio_port *) irq_get_chip_data(i->irq)) ++ ++#define ltq_pad_r32(p, reg) ltq_r32(p->pad + reg) ++#define ltq_pad_w32(p, val, reg) ltq_w32(val, p->pad + reg) ++#define ltq_pad_w32_mask(c, clear, set, reg) \ ++ ltq_pad_w32(c, (ltq_pad_r32(c, reg) & ~(clear)) | (set), reg) ++ ++#define ltq_port_r32(p, reg) ltq_r32(p->port + reg) ++#define ltq_port_w32(p, val, reg) ltq_w32(val, p->port + reg) ++#define ltq_port_w32_mask(p, clear, set, reg) \ ++ ltq_port_w32(p, (ltq_port_r32(p, reg) & ~(clear)) | (set), reg) ++ ++#define MAX_PORTS 5 ++#define PINS_PER_PORT 32 ++ ++struct falcon_gpio_port { ++ struct gpio_chip gpio_chip; ++ void __iomem *pad; ++ void __iomem *port; ++ unsigned int irq_base; ++ unsigned int chained_irq; ++}; ++ ++static struct falcon_gpio_port ltq_gpio_port[MAX_PORTS]; ++ ++int gpio_to_irq(unsigned int gpio) ++{ ++ return __gpio_to_irq(gpio); ++} ++EXPORT_SYMBOL(gpio_to_irq); ++ ++int ltq_gpio_mux_set(unsigned int pin, unsigned int mux) ++{ ++ int port = pin / 100; ++ int offset = pin % 100; ++ struct falcon_gpio_port *gpio_port; ++ ++ if ((offset >= PINS_PER_PORT) || (port >= MAX_PORTS)) ++ return -EINVAL; ++ ++ gpio_port = <q_gpio_port[port]; ++ ltq_pad_w32(gpio_port, mux & 0x3, LTQ_PADC_MUX(offset)); ++ ++ return 0; ++} ++EXPORT_SYMBOL(ltq_gpio_mux_set); ++ ++int ltq_gpio_request(unsigned int pin, unsigned int mux, ++ unsigned int dir, const char *name) ++{ ++ int port = pin / 100; ++ int offset = pin % 100; ++ ++ if (offset >= PINS_PER_PORT || port >= MAX_PORTS) ++ return -EINVAL; ++ ++ if (gpio_request(pin, name)) { ++ pr_err("failed to setup lantiq gpio: %s\n", name); ++ return -EBUSY; ++ } ++ ++ if (dir) ++ gpio_direction_output(pin, 1); ++ else ++ gpio_direction_input(pin); ++ ++ return ltq_gpio_mux_set(pin, mux); ++} ++EXPORT_SYMBOL(ltq_gpio_request); ++ ++static int ++falcon_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) ++{ ++ ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_DIRCLR); ++ ++ return 0; ++} ++ ++static void ++falcon_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) ++{ ++ if (value) ++ ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_OUTSET); ++ else ++ ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_OUTCLR); ++} ++ ++static int ++falcon_gpio_direction_output(struct gpio_chip *chip, ++ unsigned int offset, int value) ++{ ++ falcon_gpio_set(chip, offset, value); ++ ltq_port_w32(ctop(chip), 1 << offset, LTQ_GPIO_DIRSET); ++ ++ return 0; ++} ++ ++static int ++falcon_gpio_get(struct gpio_chip *chip, unsigned int offset) ++{ ++ if ((ltq_port_r32(ctop(chip), LTQ_GPIO_DIR) >> offset) & 1) ++ return (ltq_port_r32(ctop(chip), LTQ_GPIO_OUT) >> offset) & 1; ++ else ++ return (ltq_port_r32(ctop(chip), LTQ_GPIO_IN) >> offset) & 1; ++} ++ ++static int ++falcon_gpio_request(struct gpio_chip *chip, unsigned offset) ++{ ++ if ((ltq_pad_r32(ctop(chip), LTQ_PADC_AVAIL) >> offset) & 1) { ++ if (ltq_pad_r32(ctop(chip), LTQ_PADC_MUX(offset)) > 1) ++ return -EBUSY; ++ /* switch on gpio function */ ++ ltq_pad_w32(ctop(chip), 1, LTQ_PADC_MUX(offset)); ++ return 0; ++ } ++ ++ return -ENODEV; ++} ++ ++static void ++falcon_gpio_free(struct gpio_chip *chip, unsigned offset) ++{ ++ if ((ltq_pad_r32(ctop(chip), LTQ_PADC_AVAIL) >> offset) & 1) { ++ if (ltq_pad_r32(ctop(chip), LTQ_PADC_MUX(offset)) > 1) ++ return; ++ /* switch off gpio function */ ++ ltq_pad_w32(ctop(chip), 0, LTQ_PADC_MUX(offset)); ++ } ++} ++ ++static int ++falcon_gpio_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ return ctop(chip)->irq_base + offset; ++} ++ ++static void ++falcon_gpio_disable_irq(struct irq_data *d) ++{ ++ unsigned int offset = d->irq - itop(d)->irq_base; ++ ++ ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNENCLR); ++} ++ ++static void ++falcon_gpio_enable_irq(struct irq_data *d) ++{ ++ unsigned int offset = d->irq - itop(d)->irq_base; ++ ++ if (!ltq_pad_r32(itop(d), LTQ_PADC_MUX(offset)) < 1) ++ /* switch on gpio function */ ++ ltq_pad_w32(itop(d), 1, LTQ_PADC_MUX(offset)); ++ ++ ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNRNSET); ++} ++ ++static void ++falcon_gpio_ack_irq(struct irq_data *d) ++{ ++ unsigned int offset = d->irq - itop(d)->irq_base; ++ ++ ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNCR); ++} ++ ++static void ++falcon_gpio_mask_and_ack_irq(struct irq_data *d) ++{ ++ unsigned int offset = d->irq - itop(d)->irq_base; ++ ++ ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNENCLR); ++ ltq_port_w32(itop(d), 1 << offset, LTQ_GPIO_IRNCR); ++} ++ ++static struct irq_chip falcon_gpio_irq_chip; ++static int ++falcon_gpio_irq_type(struct irq_data *d, unsigned int type) ++{ ++ unsigned int offset = d->irq - itop(d)->irq_base; ++ unsigned int mask = 1 << offset; ++ ++ if ((type & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_NONE) ++ return 0; ++ ++ if ((type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_LEVEL_LOW)) != 0) { ++ /* level triggered */ ++ ltq_port_w32_mask(itop(d), 0, mask, LTQ_GPIO_IRNCFG); ++ irq_set_chip_and_handler_name(d->irq, ++ &falcon_gpio_irq_chip, handle_level_irq, "mux"); ++ } else { ++ /* edge triggered */ ++ ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_IRNCFG); ++ irq_set_chip_and_handler_name(d->irq, ++ &falcon_gpio_irq_chip, handle_simple_irq, "mux"); ++ } ++ ++ if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH) { ++ ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_EXINTCR0); ++ ltq_port_w32_mask(itop(d), 0, mask, LTQ_GPIO_EXINTCR1); ++ } else { ++ if ((type & (IRQ_TYPE_EDGE_RISING | IRQ_TYPE_LEVEL_HIGH)) != 0) ++ /* positive logic: rising edge, high level */ ++ ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_EXINTCR0); ++ else ++ /* negative logic: falling edge, low level */ ++ ltq_port_w32_mask(itop(d), 0, mask, LTQ_GPIO_EXINTCR0); ++ ltq_port_w32_mask(itop(d), mask, 0, LTQ_GPIO_EXINTCR1); ++ } ++ ++ return gpio_direction_input(itop(d)->gpio_chip.base + offset); ++} ++ ++static void ++falcon_gpio_irq_handler(unsigned int irq, struct irq_desc *desc) ++{ ++ struct falcon_gpio_port *gpio_port = irq_desc_get_handler_data(desc); ++ unsigned long irncr; ++ int offset; ++ ++ /* acknowledge interrupt */ ++ irncr = ltq_port_r32(gpio_port, LTQ_GPIO_IRNCR); ++ ltq_port_w32(gpio_port, irncr, LTQ_GPIO_IRNCR); ++ ++ desc->irq_data.chip->irq_ack(&desc->irq_data); ++ ++ for_each_set_bit(offset, &irncr, gpio_port->gpio_chip.ngpio) ++ generic_handle_irq(gpio_port->irq_base + offset); ++} ++ ++static struct irq_chip falcon_gpio_irq_chip = { ++ .name = "gpio_irq_mux", ++ .irq_mask = falcon_gpio_disable_irq, ++ .irq_unmask = falcon_gpio_enable_irq, ++ .irq_ack = falcon_gpio_ack_irq, ++ .irq_mask_ack = falcon_gpio_mask_and_ack_irq, ++ .irq_set_type = falcon_gpio_irq_type, ++}; ++ ++static struct irqaction gpio_cascade = { ++ .handler = no_action, ++ .flags = IRQF_DISABLED, ++ .name = "gpio_cascade", ++}; ++ ++static int ++falcon_gpio_probe(struct platform_device *pdev) ++{ ++ struct falcon_gpio_port *gpio_port; ++ int ret, i; ++ struct resource *gpiores, *padres; ++ int irq; ++ ++ if (pdev->id >= MAX_PORTS) ++ return -ENODEV; ++ ++ gpiores = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ padres = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ irq = platform_get_irq(pdev, 0); ++ if (!gpiores || !padres) ++ return -ENODEV; ++ ++ gpio_port = <q_gpio_port[pdev->id]; ++ gpio_port->gpio_chip.label = "falcon-gpio"; ++ gpio_port->gpio_chip.direction_input = falcon_gpio_direction_input; ++ gpio_port->gpio_chip.direction_output = falcon_gpio_direction_output; ++ gpio_port->gpio_chip.get = falcon_gpio_get; ++ gpio_port->gpio_chip.set = falcon_gpio_set; ++ gpio_port->gpio_chip.request = falcon_gpio_request; ++ gpio_port->gpio_chip.free = falcon_gpio_free; ++ gpio_port->gpio_chip.base = 100 * pdev->id; ++ gpio_port->gpio_chip.ngpio = 32; ++ gpio_port->gpio_chip.dev = &pdev->dev; ++ ++ gpio_port->port = ltq_remap_resource(gpiores); ++ gpio_port->pad = ltq_remap_resource(padres); ++ ++ if (!gpio_port->port || !gpio_port->pad) { ++ dev_err(&pdev->dev, "Could not map io ranges\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (irq > 0) { ++ /* irq_chip support */ ++ gpio_port->gpio_chip.to_irq = falcon_gpio_to_irq; ++ gpio_port->irq_base = INT_NUM_EXTRA_START + (32 * pdev->id); ++ ++ for (i = 0; i < 32; i++) { ++ irq_set_chip_and_handler_name(gpio_port->irq_base + i, ++ &falcon_gpio_irq_chip, handle_simple_irq, ++ "mux"); ++ irq_set_chip_data(gpio_port->irq_base + i, gpio_port); ++ /* set to negative logic (falling edge, low level) */ ++ ltq_port_w32_mask(gpio_port, 0, 1 << i, ++ LTQ_GPIO_EXINTCR0); ++ } ++ ++ gpio_port->chained_irq = irq; ++ setup_irq(irq, &gpio_cascade); ++ irq_set_handler_data(irq, gpio_port); ++ irq_set_chained_handler(irq, falcon_gpio_irq_handler); ++ } ++ ++ ret = gpiochip_add(&gpio_port->gpio_chip); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "Could not register gpiochip %d, %d\n", ++ pdev->id, ret); ++ goto err; ++ } ++ platform_set_drvdata(pdev, gpio_port); ++ return ret; ++ ++err: ++ dev_err(&pdev->dev, "Error in gpio_probe %d, %d\n", pdev->id, ret); ++ if (gpiores) ++ release_resource(gpiores); ++ if (padres) ++ release_resource(padres); ++ ++ if (gpio_port->port) ++ iounmap(gpio_port->port); ++ if (gpio_port->pad) ++ iounmap(gpio_port->pad); ++ return ret; ++} ++ ++static struct platform_driver falcon_gpio_driver = { ++ .probe = falcon_gpio_probe, ++ .driver = { ++ .name = "falcon_gpio", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++int __init ++falcon_gpio_init(void) ++{ ++ int ret; ++ ++ pr_info("FALC(tm) ON GPIO Driver, (C) 2011 Lantiq Deutschland Gmbh\n"); ++ ret = platform_driver_register(&falcon_gpio_driver); ++ if (ret) ++ pr_err("falcon_gpio: Error registering platform driver!"); ++ return ret; ++} ++ ++postcore_initcall(falcon_gpio_init); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0007-MIPS-lantiq-add-support-for-the-EASY98000-evaluation.patch b/target/linux/lantiq/patches-3.3/0007-MIPS-lantiq-add-support-for-the-EASY98000-evaluation.patch new file mode 100644 index 0000000000..71904dfa75 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0007-MIPS-lantiq-add-support-for-the-EASY98000-evaluation.patch @@ -0,0 +1,178 @@ +From fd86db6e998953a8574cfc2f1112343f9cddaccf Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 11 Aug 2011 14:09:35 +0200 +Subject: [PATCH 07/70] MIPS: lantiq: add support for the EASY98000 evaluation + board + +This patch adds the machine code for the EASY9800 evaluation board. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +--- + arch/mips/lantiq/falcon/Kconfig | 11 +++ + arch/mips/lantiq/falcon/Makefile | 1 + + arch/mips/lantiq/falcon/mach-easy98000.c | 110 ++++++++++++++++++++++++++++++ + arch/mips/lantiq/machtypes.h | 5 ++ + 4 files changed, 127 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/lantiq/falcon/Kconfig + create mode 100644 arch/mips/lantiq/falcon/mach-easy98000.c + +diff --git a/arch/mips/lantiq/falcon/Kconfig b/arch/mips/lantiq/falcon/Kconfig +new file mode 100644 +index 0000000..03e999d +--- /dev/null ++++ b/arch/mips/lantiq/falcon/Kconfig +@@ -0,0 +1,11 @@ ++if SOC_FALCON ++ ++menu "MIPS Machine" ++ ++config LANTIQ_MACH_EASY98000 ++ bool "Easy98000" ++ default y ++ ++endmenu ++ ++endif +diff --git a/arch/mips/lantiq/falcon/Makefile b/arch/mips/lantiq/falcon/Makefile +index de72209..56b22eb 100644 +--- a/arch/mips/lantiq/falcon/Makefile ++++ b/arch/mips/lantiq/falcon/Makefile +@@ -1 +1,2 @@ + obj-y := clk.o prom.o reset.o sysctrl.o devices.o gpio.o ++obj-$(CONFIG_LANTIQ_MACH_EASY98000) += mach-easy98000.o +diff --git a/arch/mips/lantiq/falcon/mach-easy98000.c b/arch/mips/lantiq/falcon/mach-easy98000.c +new file mode 100644 +index 0000000..361b8f0 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/mach-easy98000.c +@@ -0,0 +1,110 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2011 Thomas Langer ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "../machtypes.h" ++ ++#include "devices.h" ++ ++static struct mtd_partition easy98000_nor_partitions[] = { ++ { ++ .name = "uboot", ++ .offset = 0x0, ++ .size = 0x40000, ++ }, ++ { ++ .name = "uboot_env", ++ .offset = 0x40000, ++ .size = 0x40000, /* 2 sectors for redundant env. */ ++ }, ++ { ++ .name = "linux", ++ .offset = 0x80000, ++ .size = 0xF80000, /* map only 16 MiB */ ++ }, ++}; ++ ++struct physmap_flash_data easy98000_nor_flash_data = { ++ .nr_parts = ARRAY_SIZE(easy98000_nor_partitions), ++ .parts = easy98000_nor_partitions, ++}; ++ ++/* setup gpio based spi bus/device for access to the eeprom on the board */ ++#define SPI_GPIO_MRST 102 ++#define SPI_GPIO_MTSR 103 ++#define SPI_GPIO_CLK 104 ++#define SPI_GPIO_CS0 105 ++#define SPI_GPIO_CS1 106 ++#define SPI_GPIO_BUS_NUM 1 ++ ++static struct spi_gpio_platform_data easy98000_spi_gpio_data = { ++ .sck = SPI_GPIO_CLK, ++ .mosi = SPI_GPIO_MTSR, ++ .miso = SPI_GPIO_MRST, ++ .num_chipselect = 2, ++}; ++ ++static struct platform_device easy98000_spi_gpio_device = { ++ .name = "spi_gpio", ++ .id = SPI_GPIO_BUS_NUM, ++ .dev.platform_data = &easy98000_spi_gpio_data, ++}; ++ ++static struct spi_eeprom at25160n = { ++ .byte_len = 16 * 1024 / 8, ++ .name = "at25160n", ++ .page_size = 32, ++ .flags = EE_ADDR2, ++}; ++ ++static struct spi_board_info easy98000_spi_gpio_devices __initdata = { ++ .modalias = "at25", ++ .bus_num = SPI_GPIO_BUS_NUM, ++ .max_speed_hz = 1000 * 1000, ++ .mode = SPI_MODE_3, ++ .chip_select = 1, ++ .controller_data = (void *) SPI_GPIO_CS1, ++ .platform_data = &at25160n, ++}; ++ ++static void __init ++easy98000_init_common(void) ++{ ++ spi_register_board_info(&easy98000_spi_gpio_devices, 1); ++ platform_device_register(&easy98000_spi_gpio_device); ++} ++ ++static void __init ++easy98000_init(void) ++{ ++ easy98000_init_common(); ++ ltq_register_nor(&easy98000_nor_flash_data); ++} ++ ++static void __init ++easy98000nand_init(void) ++{ ++ easy98000_init_common(); ++ falcon_register_nand(); ++} ++ ++MIPS_MACHINE(LANTIQ_MACH_EASY98000, ++ "EASY98000", ++ "EASY98000 Eval Board", ++ easy98000_init); ++ ++MIPS_MACHINE(LANTIQ_MACH_EASY98000NAND, ++ "EASY98000NAND", ++ "EASY98000 Eval Board (NAND Flash)", ++ easy98000nand_init); +diff --git a/arch/mips/lantiq/machtypes.h b/arch/mips/lantiq/machtypes.h +index 7e01b8c..dfc6af7 100644 +--- a/arch/mips/lantiq/machtypes.h ++++ b/arch/mips/lantiq/machtypes.h +@@ -15,6 +15,11 @@ enum lantiq_mach_type { + LTQ_MACH_GENERIC = 0, + LTQ_MACH_EASY50712, /* Danube evaluation board */ + LTQ_MACH_EASY50601, /* Amazon SE evaluation board */ ++ ++ /* FALCON */ ++ LANTIQ_MACH_EASY98000, /* Falcon Eval Board, NOR Flash */ ++ LANTIQ_MACH_EASY98000SF, /* Falcon Eval Board, Serial Flash */ ++ LANTIQ_MACH_EASY98000NAND, /* Falcon Eval Board, NAND Flash */ + }; + + #endif +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0008-MIPS-lantiq-fix-early-printk.patch b/target/linux/lantiq/patches-3.3/0008-MIPS-lantiq-fix-early-printk.patch new file mode 100644 index 0000000000..e994dc5f8c --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0008-MIPS-lantiq-fix-early-printk.patch @@ -0,0 +1,68 @@ +From 4e06715641d992bb9d94e0b37d22241b9ec074c6 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 20 Aug 2011 18:55:13 +0200 +Subject: [PATCH 08/70] MIPS: lantiq: fix early printk + +The code was using a 32bit write operations in the early_printk code. This +resulted in 3 zero bytes also being written to the serial port. This patch +changes the memory access to 8bit. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 6 ++++++ + arch/mips/lantiq/early_printk.c | 14 ++++++++------ + 2 files changed, 14 insertions(+), 6 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 87f6d24..e31f52d 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -34,6 +34,12 @@ + #define LTQ_ASC1_BASE_ADDR 0x1E100C00 + #define LTQ_ASC_SIZE 0x400 + ++/* ++ * during early_printk no ioremap is possible ++ * lets use KSEG1 instead ++ */ ++#define LTQ_EARLY_ASC KSEG1ADDR(LTQ_ASC1_BASE_ADDR) ++ + /* RCU - reset control unit */ + #define LTQ_RCU_BASE_ADDR 0x1F203000 + #define LTQ_RCU_SIZE 0x1000 +diff --git a/arch/mips/lantiq/early_printk.c b/arch/mips/lantiq/early_printk.c +index 972e05f..5089075 100644 +--- a/arch/mips/lantiq/early_printk.c ++++ b/arch/mips/lantiq/early_printk.c +@@ -12,11 +12,13 @@ + #include + #include + +-/* no ioremap possible at this early stage, lets use KSEG1 instead */ +-#define LTQ_ASC_BASE KSEG1ADDR(LTQ_ASC1_BASE_ADDR) + #define ASC_BUF 1024 +-#define LTQ_ASC_FSTAT ((u32 *)(LTQ_ASC_BASE + 0x0048)) +-#define LTQ_ASC_TBUF ((u32 *)(LTQ_ASC_BASE + 0x0020)) ++#define LTQ_ASC_FSTAT ((u32 *)(LTQ_EARLY_ASC + 0x0048)) ++#ifdef __BIG_ENDIAN ++#define LTQ_ASC_TBUF ((u32 *)(LTQ_EARLY_ASC + 0x0020 + 3)) ++#else ++#define LTQ_ASC_TBUF ((u32 *)(LTQ_EARLY_ASC + 0x0020)) ++#endif + #define TXMASK 0x3F00 + #define TXOFFSET 8 + +@@ -27,7 +29,7 @@ void prom_putchar(char c) + local_irq_save(flags); + do { } while ((ltq_r32(LTQ_ASC_FSTAT) & TXMASK) >> TXOFFSET); + if (c == '\n') +- ltq_w32('\r', LTQ_ASC_TBUF); +- ltq_w32(c, LTQ_ASC_TBUF); ++ ltq_w8('\r', LTQ_ASC_TBUF); ++ ltq_w8(c, LTQ_ASC_TBUF); + local_irq_restore(flags); + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0009-MIPS-lantiq-fix-cmdline-parsing.patch b/target/linux/lantiq/patches-3.3/0009-MIPS-lantiq-fix-cmdline-parsing.patch new file mode 100644 index 0000000000..261b8e22ec --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0009-MIPS-lantiq-fix-cmdline-parsing.patch @@ -0,0 +1,36 @@ +From 3ea88c1089b88b4860919a9cdea0cc5da4d09e2c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 12 Aug 2011 16:27:38 +0200 +Subject: [PATCH 09/70] MIPS: lantiq: fix cmdline parsing + +The code tested if the KSEG1 mapped address of argv was != 0. We need to use +CPHYSADDR instead to make the conditional actually work. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +--- + arch/mips/lantiq/prom.c | 6 ++++-- + 1 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c +index e3b1e25..acb8921 100644 +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -49,10 +49,12 @@ static void __init prom_init_cmdline(void) + char **argv = (char **) KSEG1ADDR(fw_arg1); + int i; + ++ arcs_cmdline[0] = '\0'; ++ + for (i = 0; i < argc; i++) { +- char *p = (char *) KSEG1ADDR(argv[i]); ++ char *p = (char *) KSEG1ADDR(argv[i]); + +- if (p && *p) { ++ if (CPHYSADDR(p) && *p) { + strlcat(arcs_cmdline, p, sizeof(arcs_cmdline)); + strlcat(arcs_cmdline, " ", sizeof(arcs_cmdline)); + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0010-MIPS-lantiq-fix-STP-gpio-groups.patch b/target/linux/lantiq/patches-3.3/0010-MIPS-lantiq-fix-STP-gpio-groups.patch new file mode 100644 index 0000000000..f3c76a0d6b --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0010-MIPS-lantiq-fix-STP-gpio-groups.patch @@ -0,0 +1,42 @@ +From 8da3264153de296b0d378befd4eb711f20d7843c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 27 Oct 2011 20:06:05 +0200 +Subject: [PATCH 10/70] MIPS: lantiq: fix STP gpio groups + +The STP engine has 3 groups of 8 pins. Only the first was activated by default. +This patch activates the 2 missing groups. + +Signed-off-by: Matti Laakso +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/gpio_stp.c | 7 +++++-- + 1 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c +index 2c78660..cb6f170 100644 +--- a/arch/mips/lantiq/xway/gpio_stp.c ++++ b/arch/mips/lantiq/xway/gpio_stp.c +@@ -35,6 +35,8 @@ + #define LTQ_STP_ADSL_SRC (3 << 24) + + #define LTQ_STP_GROUP0 (1 << 0) ++#define LTQ_STP_GROUP1 (1 << 1) ++#define LTQ_STP_GROUP2 (1 << 2) + + #define LTQ_STP_RISING 0 + #define LTQ_STP_FALLING (1 << 26) +@@ -93,8 +95,9 @@ static int ltq_stp_hw_init(void) + /* rising or falling edge */ + ltq_stp_w32_mask(LTQ_STP_EDGE_MASK, LTQ_STP_FALLING, LTQ_STP_CON0); + +- /* per default stp 15-0 are set */ +- ltq_stp_w32_mask(0, LTQ_STP_GROUP0, LTQ_STP_CON1); ++ /* enable all three led groups */ ++ ltq_stp_w32_mask(0, LTQ_STP_GROUP0 | LTQ_STP_GROUP1 | LTQ_STP_GROUP2, ++ LTQ_STP_CON1); + + /* stp are update periodically by the FPI bus */ + ltq_stp_w32_mask(LTQ_STP_UPD_MASK, LTQ_STP_UPD_FPI, LTQ_STP_CON1); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0011-MIPS-lantiq-fix-pull-gpio-up-resistors-usage.patch b/target/linux/lantiq/patches-3.3/0011-MIPS-lantiq-fix-pull-gpio-up-resistors-usage.patch new file mode 100644 index 0000000000..c89134df87 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0011-MIPS-lantiq-fix-pull-gpio-up-resistors-usage.patch @@ -0,0 +1,48 @@ +From 784e85cf7ce5032c9e5de35505fcc209f2a7891e Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 27 Oct 2011 20:06:30 +0200 +Subject: [PATCH 11/70] MIPS: lantiq: fix pull gpio up resistors usage + +The register that enables a gpios internal pullups was not used. This patch +makes sure the pullups are activated correctly. + +Signed-off-by: Matti Laakso +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/gpio.c | 6 ++++++ + 1 files changed, 6 insertions(+), 0 deletions(-) + +diff --git a/arch/mips/lantiq/xway/gpio.c b/arch/mips/lantiq/xway/gpio.c +index f204f6c..14ff7c7 100644 +--- a/arch/mips/lantiq/xway/gpio.c ++++ b/arch/mips/lantiq/xway/gpio.c +@@ -21,6 +21,8 @@ + #define LTQ_GPIO_ALTSEL0 0x0C + #define LTQ_GPIO_ALTSEL1 0x10 + #define LTQ_GPIO_OD 0x14 ++#define LTQ_GPIO_PUDSEL 0x1C ++#define LTQ_GPIO_PUDEN 0x20 + + #define PINS_PER_PORT 16 + #define MAX_PORTS 3 +@@ -106,6 +108,8 @@ static int ltq_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) + + ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); + ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_DIR, offset); ++ ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_PUDSEL, offset); ++ ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_PUDEN, offset); + + return 0; + } +@@ -117,6 +121,8 @@ static int ltq_gpio_direction_output(struct gpio_chip *chip, + + ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); + ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_DIR, offset); ++ ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_PUDSEL, offset); ++ ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_PUDEN, offset); + ltq_gpio_set(chip, offset, value); + + return 0; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0012-MIPS-lantiq-add-default-configs.patch b/target/linux/lantiq/patches-3.3/0012-MIPS-lantiq-add-default-configs.patch new file mode 100644 index 0000000000..286eb7c2b9 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0012-MIPS-lantiq-add-default-configs.patch @@ -0,0 +1,247 @@ +From fe45fbd99c5b5815a08eccb81ef7fa16d60517cc Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 11 Nov 2011 22:02:27 +0100 +Subject: [PATCH 12/70] MIPS: lantiq: add default configs + +This patch adds the default config for 3 Lantiq SoCs + +* Danube/AR9 (xway) +* Amazon-SE +* Falc-ON + +Signed-off-by: John Crispin +--- + arch/mips/configs/ase_defconfig | 67 +++++++++++++++++++++++++++++++++ + arch/mips/configs/falcon_defconfig | 72 ++++++++++++++++++++++++++++++++++++ + arch/mips/configs/xway_defconfig | 66 +++++++++++++++++++++++++++++++++ + 3 files changed, 205 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/configs/ase_defconfig + create mode 100644 arch/mips/configs/falcon_defconfig + create mode 100644 arch/mips/configs/xway_defconfig + +diff --git a/arch/mips/configs/ase_defconfig b/arch/mips/configs/ase_defconfig +new file mode 100644 +index 0000000..5bb1d93 +--- /dev/null ++++ b/arch/mips/configs/ase_defconfig +@@ -0,0 +1,67 @@ ++CONFIG_LANTIQ=y ++CONFIG_SOC_AMAZON_SE=y ++CONFIG_CPU_MIPS32_R2=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_EXPERIMENTAL=y ++CONFIG_DEFAULT_HOSTNAME="amazon_se" ++CONFIG_SYSVIPC=y ++CONFIG_LOG_BUF_SHIFT=14 ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../root-lantiq/ ../root-lantiq/initramfs-base-files.txt" ++CONFIG_INITRAMFS_ROOT_UID=1000 ++CONFIG_INITRAMFS_ROOT_GID=1000 +++# CONFIG_RD_GZIP is not set ++CONFIG_RD_LZMA=y ++CONFIG_EMBEDDED=y ++CONFIG_SLAB=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_DEFAULT_DEADLINE=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_ARPD=y ++CONFIG_SYN_COOKIES=y ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=y ++CONFIG_NET_SCHED=y ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_MTD=y ++CONFIG_MTD_CMDLINE_PARTS=y ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_CFI=y ++CONFIG_MTD_CFI_ADV_OPTIONS=y ++CONFIG_MTD_CFI_GEOMETRY=y ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_COMPLEX_MAPPINGS=y ++CONFIG_MTD_LANTIQ=y ++CONFIG_MISC_DEVICES=y ++CONFIG_NETDEVICES=y ++CONFIG_MII=y ++CONFIG_LANTIQ_ETOP=y ++CONFIG_PHYLIB=y ++CONFIG_SERIAL_LANTIQ=y ++CONFIG_PINCTRL=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_WATCHDOG=y ++CONFIG_LANTIQ_WDT=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_SUMMARY=y ++CONFIG_JFFS2_FS_XATTR=y ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_STRIP_ASM_SYMS=y ++CONFIG_DEBUG_FS=y +diff --git a/arch/mips/configs/falcon_defconfig b/arch/mips/configs/falcon_defconfig +new file mode 100644 +index 0000000..ce242a8 +--- /dev/null ++++ b/arch/mips/configs/falcon_defconfig +@@ -0,0 +1,72 @@ ++CONFIG_LANTIQ=y ++CONFIG_SOC_FALCON=y ++CONFIG_CPU_MIPS32_R2=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_EXPERIMENTAL=y ++CONFIG_DEFAULT_HOSTNAME="falcon" ++CONFIG_SYSVIPC=y ++CONFIG_LOG_BUF_SHIFT=14 ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../root-lantiq/ ../root-lantiq/initramfs-base-files.txt" ++CONFIG_INITRAMFS_ROOT_UID=1000 ++CONFIG_INITRAMFS_ROOT_GID=1000 +++# CONFIG_RD_GZIP is not set ++CONFIG_RD_LZMA=y ++CONFIG_EMBEDDED=y ++CONFIG_SLAB=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_DEFAULT_DEADLINE=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_ARPD=y ++CONFIG_SYN_COOKIES=y ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=y ++CONFIG_NET_SCHED=y ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_MTD=y ++CONFIG_MTD_CMDLINE_PARTS=y ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_CFI=y ++CONFIG_MTD_CFI_ADV_OPTIONS=y ++CONFIG_MTD_CFI_GEOMETRY=y ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_COMPLEX_MAPPINGS=y ++CONFIG_MTD_LANTIQ=y ++CONFIG_MTD_M25P80=y ++CONFIG_MISC_DEVICES=y ++CONFIG_EEPROM_AT24=y ++CONFIG_NETDEVICES=y ++CONFIG_MII=y ++CONFIG_PHYLIB=y ++CONFIG_SERIAL_LANTIQ=y ++CONFIG_I2C=y ++CONFIG_I2C_FALCON=y ++CONFIG_SPI=y ++CONFIG_SPI_FALCON=y ++CONFIG_PINCTRL=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_WATCHDOG=y ++CONFIG_LANTIQ_WDT=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_SUMMARY=y ++CONFIG_JFFS2_FS_XATTR=y ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_STRIP_ASM_SYMS=y ++CONFIG_DEBUG_FS=y +diff --git a/arch/mips/configs/xway_defconfig b/arch/mips/configs/xway_defconfig +new file mode 100644 +index 0000000..510a964 +--- /dev/null ++++ b/arch/mips/configs/xway_defconfig +@@ -0,0 +1,66 @@ ++CONFIG_LANTIQ=y ++CONFIG_CPU_MIPS32_R2=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_EXPERIMENTAL=y ++CONFIG_DEFAULT_HOSTNAME="danube" ++CONFIG_SYSVIPC=y ++CONFIG_LOG_BUF_SHIFT=14 ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../root-lantiq/ ../root-lantiq/initramfs-base-files.txt" ++CONFIG_INITRAMFS_ROOT_UID=1000 ++CONFIG_INITRAMFS_ROOT_GID=1000 ++# CONFIG_RD_GZIP is not set ++CONFIG_RD_LZMA=y ++CONFIG_EMBEDDED=y ++CONFIG_SLAB=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_DEFAULT_DEADLINE=y ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_ADVANCED_ROUTER=y ++CONFIG_IP_MULTIPLE_TABLES=y ++CONFIG_IP_ROUTE_MULTIPATH=y ++CONFIG_IP_ROUTE_VERBOSE=y ++CONFIG_IP_MROUTE=y ++CONFIG_IP_MROUTE_MULTIPLE_TABLES=y ++CONFIG_ARPD=y ++CONFIG_SYN_COOKIES=y ++CONFIG_NETFILTER=y ++CONFIG_BRIDGE=m ++CONFIG_VLAN_8021Q=y ++CONFIG_NET_SCHED=y ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_MTD=y ++CONFIG_MTD_CMDLINE_PARTS=y ++CONFIG_MTD_CHAR=y ++CONFIG_MTD_BLOCK=y ++CONFIG_MTD_CFI=y ++CONFIG_MTD_CFI_ADV_OPTIONS=y ++CONFIG_MTD_CFI_GEOMETRY=y ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_COMPLEX_MAPPINGS=y ++CONFIG_MTD_LANTIQ=y ++CONFIG_MISC_DEVICES=y ++CONFIG_NETDEVICES=y ++CONFIG_MII=y ++CONFIG_LANTIQ_ETOP=y ++CONFIG_PHYLIB=y ++CONFIG_SERIAL_LANTIQ=y ++CONFIG_PINCTRL=y ++CONFIG_GPIO_SYSFS=y ++CONFIG_WATCHDOG=y ++CONFIG_LANTIQ_WDT=y ++CONFIG_TMPFS=y ++CONFIG_JFFS2_FS=y ++CONFIG_JFFS2_SUMMARY=y ++CONFIG_JFFS2_FS_XATTR=y ++CONFIG_JFFS2_COMPRESSION_OPTIONS=y ++CONFIG_SQUASHFS=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_STRIP_ASM_SYMS=y ++CONFIG_DEBUG_FS=y +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0013-MAINTAINERS-add-entry-for-Lantiq-related-files.patch b/target/linux/lantiq/patches-3.3/0013-MAINTAINERS-add-entry-for-Lantiq-related-files.patch new file mode 100644 index 0000000000..18d7086405 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0013-MAINTAINERS-add-entry-for-Lantiq-related-files.patch @@ -0,0 +1,38 @@ +From adb6262219beb9ba1986dc0216911e2611a6a996 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 10 Nov 2011 19:32:37 +0100 +Subject: [PATCH 13/70] MAINTAINERS: add entry for Lantiq related files + +Adds new entry to MAINTAINERS file for Lantiq SoC related code. + +Signed-off-by: John Crispin +--- + MAINTAINERS | 12 ++++++++++++ + 1 files changed, 12 insertions(+), 0 deletions(-) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 9c63a43..a2bf032 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -4392,6 +4392,18 @@ S: Supported + F: Documentation/mips/ + F: arch/mips/ + ++MIPS/LANTIQ ++M: John Crispin ++M: Thomas Langer ++S: Maintained ++F: arch/mips/lantiq/* ++F: drivers/i2c/busses/i2c-falcon.c ++F: drivers/mtd/maps/lantiq-flash.c ++F: drivers/net/ethernet/lantiq_etop.c ++F: drivers/spi/spi-falcon.c ++F: drivers/tty/serial/lantiq.c ++F: drivers/watchdog/lantiq_wdt.c ++ + MISCELLANEOUS MCA-SUPPORT + M: James Bottomley + S: Maintained +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0014-MIPS-lantiq-enable-oprofile-support-on-lantiq-target.patch b/target/linux/lantiq/patches-3.3/0014-MIPS-lantiq-enable-oprofile-support-on-lantiq-target.patch new file mode 100644 index 0000000000..dfac38fae9 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0014-MIPS-lantiq-enable-oprofile-support-on-lantiq-target.patch @@ -0,0 +1,53 @@ +From 4a6b5278deced0bd79df1521df291fe744c260b5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 24 Aug 2011 13:28:55 +0200 +Subject: [PATCH 14/70] MIPS: lantiq: enable oprofile support on lantiq + targets + +This patch sets the performance counters irq and HAVE_OPROFILE flag for Lantiq +SoCs. + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 1 + + arch/mips/lantiq/irq.c | 5 +++++ + 2 files changed, 6 insertions(+), 0 deletions(-) + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 5ab6e89..8ad52f4 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -229,6 +229,7 @@ config LANTIQ + select SWAP_IO_SPACE + select BOOT_RAW + select HAVE_CLK ++ select HAVE_OPROFILE + select MIPS_MACHINE + + config LASAT +diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c +index f56bcce..ada3874 100644 +--- a/arch/mips/lantiq/irq.c ++++ b/arch/mips/lantiq/irq.c +@@ -40,6 +40,9 @@ + + #define MAX_EIU 6 + ++/* the performance counter */ ++#define LTQ_PERF_IRQ (INT_NUM_IM4_IRL0 + 31) ++ + /* irqs generated by device attached to the EBU need to be acked in + * a special manner + */ +@@ -317,6 +320,8 @@ void __init arch_init_irq(void) + set_c0_status(IE_SW0 | IE_SW1 | IE_IRQ0 | IE_IRQ1 | + IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5); + #endif ++ ++ cp0_perfcount_irq = LTQ_PERF_IRQ; + } + + unsigned int __cpuinit get_c0_compare_int(void) +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0015-NET-MIPS-lantiq-make-etop-ethernet-work-on-ase-ar9.patch b/target/linux/lantiq/patches-3.3/0015-NET-MIPS-lantiq-make-etop-ethernet-work-on-ase-ar9.patch new file mode 100644 index 0000000000..91ead94148 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0015-NET-MIPS-lantiq-make-etop-ethernet-work-on-ase-ar9.patch @@ -0,0 +1,408 @@ +From 204df03dd8524ab3ee8261feab44397dc890a840 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 10 Aug 2011 15:32:16 +0200 +Subject: [PATCH 15/70] NET: MIPS: lantiq: make etop ethernet work on ase/ar9 + +Extend the driver to handle the different DMA channel layout for AR9 and +Amazon-SE SoCs. The patch also adds support for the integrated PHY found +on Amazon-SE and the gigabit switch found inside the AR9. + +Signed-off-by: John Crispin +Cc: netdev@vger.kernel.org +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_irq.h | 22 +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 10 ++ + arch/mips/lantiq/xway/devices.c | 11 +- + drivers/net/ethernet/lantiq_etop.c | 171 ++++++++++++++++++-- + 4 files changed, 174 insertions(+), 40 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +index b4465a8..2a8d5ad 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +@@ -38,26 +38,8 @@ + + #define MIPS_CPU_TIMER_IRQ 7 + +-#define LTQ_DMA_CH0_INT (INT_NUM_IM2_IRL0) +-#define LTQ_DMA_CH1_INT (INT_NUM_IM2_IRL0 + 1) +-#define LTQ_DMA_CH2_INT (INT_NUM_IM2_IRL0 + 2) +-#define LTQ_DMA_CH3_INT (INT_NUM_IM2_IRL0 + 3) +-#define LTQ_DMA_CH4_INT (INT_NUM_IM2_IRL0 + 4) +-#define LTQ_DMA_CH5_INT (INT_NUM_IM2_IRL0 + 5) +-#define LTQ_DMA_CH6_INT (INT_NUM_IM2_IRL0 + 6) +-#define LTQ_DMA_CH7_INT (INT_NUM_IM2_IRL0 + 7) +-#define LTQ_DMA_CH8_INT (INT_NUM_IM2_IRL0 + 8) +-#define LTQ_DMA_CH9_INT (INT_NUM_IM2_IRL0 + 9) +-#define LTQ_DMA_CH10_INT (INT_NUM_IM2_IRL0 + 10) +-#define LTQ_DMA_CH11_INT (INT_NUM_IM2_IRL0 + 11) +-#define LTQ_DMA_CH12_INT (INT_NUM_IM2_IRL0 + 25) +-#define LTQ_DMA_CH13_INT (INT_NUM_IM2_IRL0 + 26) +-#define LTQ_DMA_CH14_INT (INT_NUM_IM2_IRL0 + 27) +-#define LTQ_DMA_CH15_INT (INT_NUM_IM2_IRL0 + 28) +-#define LTQ_DMA_CH16_INT (INT_NUM_IM2_IRL0 + 29) +-#define LTQ_DMA_CH17_INT (INT_NUM_IM2_IRL0 + 30) +-#define LTQ_DMA_CH18_INT (INT_NUM_IM2_IRL0 + 16) +-#define LTQ_DMA_CH19_INT (INT_NUM_IM2_IRL0 + 21) ++#define LTQ_DMA_ETOP ((ltq_is_ase()) ? \ ++ (INT_NUM_IM3_IRL0) : (INT_NUM_IM2_IRL0)) + + #define LTQ_PPE_MBOX_INT (INT_NUM_IM2_IRL0 + 24) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index e31f52d..6983d75 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -82,6 +82,7 @@ + #define LTQ_PMU_SIZE 0x1000 + + #define PMU_DMA 0x0020 ++#define PMU_EPHY 0x0080 + #define PMU_USB 0x8041 + #define PMU_LED 0x0800 + #define PMU_GPT 0x1000 +@@ -93,6 +94,10 @@ + #define LTQ_ETOP_BASE_ADDR 0x1E180000 + #define LTQ_ETOP_SIZE 0x40000 + ++/* GBIT - gigabit switch */ ++#define LTQ_GBIT_BASE_ADDR 0x1E108000 ++#define LTQ_GBIT_SIZE 0x200 ++ + /* DMA */ + #define LTQ_DMA_BASE_ADDR 0x1E104100 + #define LTQ_DMA_SIZE 0x800 +@@ -147,6 +152,11 @@ extern void ltq_pmu_enable(unsigned int module); + extern void ltq_pmu_disable(unsigned int module); + extern void ltq_cgu_enable(unsigned int clk); + ++static inline int ltq_is_ase(void) ++{ ++ return (ltq_get_soc_type() == SOC_TYPE_AMAZON_SE); ++} ++ + static inline int ltq_is_ar9(void) + { + return (ltq_get_soc_type() == SOC_TYPE_AR9); +diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c +index f97e565..eab4644d 100644 +--- a/arch/mips/lantiq/xway/devices.c ++++ b/arch/mips/lantiq/xway/devices.c +@@ -74,18 +74,23 @@ void __init ltq_register_ase_asc(void) + } + + /* ethernet */ +-static struct resource ltq_etop_resources = +- MEM_RES("etop", LTQ_ETOP_BASE_ADDR, LTQ_ETOP_SIZE); ++static struct resource ltq_etop_resources[] = { ++ MEM_RES("etop", LTQ_ETOP_BASE_ADDR, LTQ_ETOP_SIZE), ++ MEM_RES("gbit", LTQ_GBIT_BASE_ADDR, LTQ_GBIT_SIZE), ++}; + + static struct platform_device ltq_etop = { + .name = "ltq_etop", +- .resource = <q_etop_resources, ++ .resource = ltq_etop_resources, + .num_resources = 1, + }; + + void __init + ltq_register_etop(struct ltq_eth_data *eth) + { ++ /* only register the gphy on socs that have one */ ++ if (ltq_is_ar9() | ltq_is_vr9()) ++ ltq_etop.num_resources = 2; + if (eth) { + ltq_etop.dev.platform_data = eth; + platform_device_register(<q_etop); +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index 85e2c6c..e799b88 100644 +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -71,10 +71,43 @@ + #define ETOP_MII_REVERSE 0xe + #define ETOP_PLEN_UNDER 0x40 + #define ETOP_CGEN 0x800 +- +-/* use 2 static channels for TX/RX */ ++#define ETOP_CFG_MII0 0x01 ++ ++#define LTQ_GBIT_MDIO_CTL 0xCC ++#define LTQ_GBIT_MDIO_DATA 0xd0 ++#define LTQ_GBIT_GCTL0 0x68 ++#define LTQ_GBIT_PMAC_HD_CTL 0x8c ++#define LTQ_GBIT_P0_CTL 0x4 ++#define LTQ_GBIT_PMAC_RX_IPG 0xa8 ++ ++#define PMAC_HD_CTL_AS (1 << 19) ++#define PMAC_HD_CTL_RXSH (1 << 22) ++ ++/* Switch Enable (0=disable, 1=enable) */ ++#define GCTL0_SE 0x80000000 ++/* Disable MDIO auto polling (0=disable, 1=enable) */ ++#define PX_CTL_DMDIO 0x00400000 ++ ++/* register information for the gbit's MDIO bus */ ++#define MDIO_XR9_REQUEST 0x00008000 ++#define MDIO_XR9_READ 0x00000800 ++#define MDIO_XR9_WRITE 0x00000400 ++#define MDIO_XR9_REG_MASK 0x1f ++#define MDIO_XR9_ADDR_MASK 0x1f ++#define MDIO_XR9_RD_MASK 0xffff ++#define MDIO_XR9_REG_OFFSET 0 ++#define MDIO_XR9_ADDR_OFFSET 5 ++#define MDIO_XR9_WR_OFFSET 16 ++ ++/* the newer xway socks have a embedded 3/7 port gbit multiplexer */ ++#define ltq_has_gbit() (ltq_is_ar9() || ltq_is_vr9()) ++ ++/* use 2 static channels for TX/RX ++ depending on the SoC we need to use different DMA channels for ethernet */ + #define LTQ_ETOP_TX_CHANNEL 1 +-#define LTQ_ETOP_RX_CHANNEL 6 ++#define LTQ_ETOP_RX_CHANNEL ((ltq_is_ase()) ? (5) : \ ++ ((ltq_has_gbit()) ? (0) : (6))) ++ + #define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL) + #define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL) + +@@ -83,9 +116,15 @@ + #define ltq_etop_w32_mask(x, y, z) \ + ltq_w32_mask(x, y, ltq_etop_membase + (z)) + ++#define ltq_gbit_r32(x) ltq_r32(ltq_gbit_membase + (x)) ++#define ltq_gbit_w32(x, y) ltq_w32(x, ltq_gbit_membase + (y)) ++#define ltq_gbit_w32_mask(x, y, z) \ ++ ltq_w32_mask(x, y, ltq_gbit_membase + (z)) ++ + #define DRV_VERSION "1.0" + + static void __iomem *ltq_etop_membase; ++static void __iomem *ltq_gbit_membase; + + struct ltq_etop_chan { + int idx; +@@ -111,6 +150,9 @@ struct ltq_etop_priv { + spinlock_t lock; + }; + ++static int ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, ++ int phy_reg, u16 phy_data); ++ + static int + ltq_etop_alloc_skb(struct ltq_etop_chan *ch) + { +@@ -212,7 +254,7 @@ static irqreturn_t + ltq_etop_dma_irq(int irq, void *_priv) + { + struct ltq_etop_priv *priv = _priv; +- int ch = irq - LTQ_DMA_CH0_INT; ++ int ch = irq - LTQ_DMA_ETOP; + + napi_schedule(&priv->ch[ch].napi); + return IRQ_HANDLED; +@@ -245,15 +287,43 @@ ltq_etop_hw_exit(struct net_device *dev) + ltq_etop_free_channel(dev, &priv->ch[i]); + } + ++static void ++ltq_etop_gbit_init(void) ++{ ++ ltq_pmu_enable(PMU_SWITCH); ++ ++ ltq_gpio_request(42, 2, 1, "MDIO"); ++ ltq_gpio_request(43, 2, 1, "MDC"); ++ ++ ltq_gbit_w32_mask(0, GCTL0_SE, LTQ_GBIT_GCTL0); ++ /** Disable MDIO auto polling mode */ ++ ltq_gbit_w32_mask(0, PX_CTL_DMDIO, LTQ_GBIT_P0_CTL); ++ /* set 1522 packet size */ ++ ltq_gbit_w32_mask(0x300, 0, LTQ_GBIT_GCTL0); ++ /* disable pmac & dmac headers */ ++ ltq_gbit_w32_mask(PMAC_HD_CTL_AS | PMAC_HD_CTL_RXSH, 0, ++ LTQ_GBIT_PMAC_HD_CTL); ++ /* Due to traffic halt when burst length 8, ++ replace default IPG value with 0x3B */ ++ ltq_gbit_w32(0x3B, LTQ_GBIT_PMAC_RX_IPG); ++} ++ + static int + ltq_etop_hw_init(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); ++ unsigned int mii_mode = priv->pldata->mii_mode; + int i; + + ltq_pmu_enable(PMU_PPE); + +- switch (priv->pldata->mii_mode) { ++ if (ltq_has_gbit()) { ++ ltq_etop_gbit_init(); ++ /* force the etops link to the gbit to MII */ ++ mii_mode = PHY_INTERFACE_MODE_MII; ++ } ++ ++ switch (mii_mode) { + case PHY_INTERFACE_MODE_RMII: + ltq_etop_w32_mask(ETOP_MII_MASK, + ETOP_MII_REVERSE, LTQ_ETOP_CFG); +@@ -265,6 +335,18 @@ ltq_etop_hw_init(struct net_device *dev) + break; + + default: ++ if (ltq_is_ase()) { ++ ltq_pmu_enable(PMU_EPHY); ++ /* disable external MII */ ++ ltq_etop_w32_mask(0, ETOP_CFG_MII0, LTQ_ETOP_CFG); ++ /* enable clock for internal PHY */ ++ ltq_cgu_enable(CGU_EPHY); ++ /* we need to write this magic to the internal phy to ++ make it work */ ++ ltq_etop_mdio_wr(NULL, 0x8, 0x12, 0xC020); ++ pr_info("Selected EPHY mode\n"); ++ break; ++ } + netdev_err(dev, "unknown mii mode %d\n", + priv->pldata->mii_mode); + return -ENOTSUPP; +@@ -276,7 +358,7 @@ ltq_etop_hw_init(struct net_device *dev) + ltq_dma_init_port(DMA_PORT_ETOP); + + for (i = 0; i < MAX_DMA_CHAN; i++) { +- int irq = LTQ_DMA_CH0_INT + i; ++ int irq = LTQ_DMA_ETOP + i; + struct ltq_etop_chan *ch = &priv->ch[i]; + + ch->idx = ch->dma.nr = i; +@@ -340,6 +422,39 @@ static const struct ethtool_ops ltq_etop_ethtool_ops = { + }; + + static int ++ltq_etop_mdio_wr_xr9(struct mii_bus *bus, int phy_addr, ++ int phy_reg, u16 phy_data) ++{ ++ u32 val = MDIO_XR9_REQUEST | MDIO_XR9_WRITE | ++ (phy_data << MDIO_XR9_WR_OFFSET) | ++ ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) | ++ ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET); ++ ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL); ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ return 0; ++} ++ ++static int ++ltq_etop_mdio_rd_xr9(struct mii_bus *bus, int phy_addr, int phy_reg) ++{ ++ u32 val = MDIO_XR9_REQUEST | MDIO_XR9_READ | ++ ((phy_addr & MDIO_XR9_ADDR_MASK) << MDIO_XR9_ADDR_OFFSET) | ++ ((phy_reg & MDIO_XR9_REG_MASK) << MDIO_XR9_REG_OFFSET); ++ ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ ltq_gbit_w32(val, LTQ_GBIT_MDIO_CTL); ++ while (ltq_gbit_r32(LTQ_GBIT_MDIO_CTL) & MDIO_XR9_REQUEST) ++ ; ++ val = ltq_gbit_r32(LTQ_GBIT_MDIO_DATA) & MDIO_XR9_RD_MASK; ++ return val; ++} ++ ++static int + ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, int phy_reg, u16 phy_data) + { + u32 val = MDIO_REQUEST | +@@ -380,14 +495,11 @@ ltq_etop_mdio_probe(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); + struct phy_device *phydev = NULL; +- int phy_addr; + +- for (phy_addr = 0; phy_addr < PHY_MAX_ADDR; phy_addr++) { +- if (priv->mii_bus->phy_map[phy_addr]) { +- phydev = priv->mii_bus->phy_map[phy_addr]; +- break; +- } +- } ++ if (ltq_is_ase()) ++ phydev = priv->mii_bus->phy_map[8]; ++ else ++ phydev = priv->mii_bus->phy_map[0]; + + if (!phydev) { + netdev_err(dev, "no PHY found\n"); +@@ -409,6 +521,9 @@ ltq_etop_mdio_probe(struct net_device *dev) + | SUPPORTED_Autoneg + | SUPPORTED_MII + | SUPPORTED_TP); ++ if (ltq_has_gbit()) ++ phydev->supported &= SUPPORTED_1000baseT_Half ++ | SUPPORTED_1000baseT_Full; + + phydev->advertising = phydev->supported; + priv->phydev = phydev; +@@ -434,8 +549,13 @@ ltq_etop_mdio_init(struct net_device *dev) + } + + priv->mii_bus->priv = dev; +- priv->mii_bus->read = ltq_etop_mdio_rd; +- priv->mii_bus->write = ltq_etop_mdio_wr; ++ if (ltq_has_gbit()) { ++ priv->mii_bus->read = ltq_etop_mdio_rd_xr9; ++ priv->mii_bus->write = ltq_etop_mdio_wr_xr9; ++ } else { ++ priv->mii_bus->read = ltq_etop_mdio_rd; ++ priv->mii_bus->write = ltq_etop_mdio_wr; ++ } + priv->mii_bus->name = "ltq_mii"; + snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%s-%x", + priv->pdev->name, priv->pdev->id); +@@ -526,9 +646,9 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) + struct ltq_etop_priv *priv = netdev_priv(dev); + struct ltq_etop_chan *ch = &priv->ch[(queue << 1) | 1]; + struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; +- int len; + unsigned long flags; + u32 byte_offset; ++ int len; + + len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + +@@ -702,7 +822,7 @@ ltq_etop_probe(struct platform_device *pdev) + { + struct net_device *dev; + struct ltq_etop_priv *priv; +- struct resource *res; ++ struct resource *res, *gbit_res; + int err; + int i; + +@@ -730,6 +850,23 @@ ltq_etop_probe(struct platform_device *pdev) + goto err_out; + } + ++ if (ltq_has_gbit()) { ++ gbit_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!gbit_res) { ++ dev_err(&pdev->dev, "failed to get gbit resource\n"); ++ err = -ENOENT; ++ goto err_out; ++ } ++ ltq_gbit_membase = devm_ioremap_nocache(&pdev->dev, ++ gbit_res->start, resource_size(gbit_res)); ++ if (!ltq_gbit_membase) { ++ dev_err(&pdev->dev, "failed to remap gigabit switch %d\n", ++ pdev->id); ++ err = -ENOMEM; ++ goto err_out; ++ } ++ } ++ + dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4); + strcpy(dev->name, "eth%d"); + dev->netdev_ops = <q_eth_netdev_ops; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0016-NET-MIPS-lantiq-non-existing-phy-was-not-handled-gra.patch b/target/linux/lantiq/patches-3.3/0016-NET-MIPS-lantiq-non-existing-phy-was-not-handled-gra.patch new file mode 100644 index 0000000000..f18827ac0d --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0016-NET-MIPS-lantiq-non-existing-phy-was-not-handled-gra.patch @@ -0,0 +1,67 @@ +From d6a9d52bfcc2d8f163e95920cf3678847bb97f18 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 15 Nov 2011 14:52:21 +0100 +Subject: [PATCH 16/70] NET: MIPS: lantiq: non existing phy was not handled + gracefully + +The code blindly assumed that that a PHY device was present causing a BadVA. +In addition the driver should not fail to load incase no PHY was found. +Instead we print the following line and continue with no attached PHY. + + etop: mdio probe failed + +Signed-off-by: John Crispin +Cc: netdev@vger.kernel.org +--- + drivers/net/ethernet/lantiq_etop.c | 14 ++++++++------ + 1 files changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index e799b88..5a8ca89 100644 +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -614,7 +614,8 @@ ltq_etop_open(struct net_device *dev) + ltq_dma_open(&ch->dma); + napi_enable(&ch->napi); + } +- phy_start(priv->phydev); ++ if (priv->phydev) ++ phy_start(priv->phydev); + netif_tx_start_all_queues(dev); + return 0; + } +@@ -626,7 +627,8 @@ ltq_etop_stop(struct net_device *dev) + int i; + + netif_tx_stop_all_queues(dev); +- phy_stop(priv->phydev); ++ if (priv->phydev) ++ phy_stop(priv->phydev); + for (i = 0; i < MAX_DMA_CHAN; i++) { + struct ltq_etop_chan *ch = &priv->ch[i]; + +@@ -772,9 +774,10 @@ ltq_etop_init(struct net_device *dev) + if (err) + goto err_netdev; + ltq_etop_set_multicast_list(dev); +- err = ltq_etop_mdio_init(dev); +- if (err) +- goto err_netdev; ++ if (!ltq_etop_mdio_init(dev)) ++ dev->ethtool_ops = <q_etop_ethtool_ops; ++ else ++ pr_warn("etop: mdio probe failed\n");; + return 0; + + err_netdev: +@@ -870,7 +873,6 @@ ltq_etop_probe(struct platform_device *pdev) + dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4); + strcpy(dev->name, "eth%d"); + dev->netdev_ops = <q_eth_netdev_ops; +- dev->ethtool_ops = <q_etop_ethtool_ops; + priv = netdev_priv(dev); + priv->res = res; + priv->pdev = pdev; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0017-NET-MIPS-lantiq-return-value-of-request_irq-was-not-.patch b/target/linux/lantiq/patches-3.3/0017-NET-MIPS-lantiq-return-value-of-request_irq-was-not-.patch new file mode 100644 index 0000000000..e3ef066c1e --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0017-NET-MIPS-lantiq-return-value-of-request_irq-was-not-.patch @@ -0,0 +1,71 @@ +From 965d488202829699dd9d089adb4c8b605e9de6cd Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 15 Nov 2011 15:56:06 +0100 +Subject: [PATCH 17/70] NET: MIPS: lantiq: return value of request_irq was not + handled gracefully + +The return values of request_irq() were not checked leading to the following +error message. + +drivers/net/ethernet/lantiq_etop.c: In function 'ltq_etop_hw_init': +drivers/net/ethernet/lantiq_etop.c:368:15: warning: ignoring return value of 'request_irq', declared with attribute warn_unused_result +drivers/net/ethernet/lantiq_etop.c:377:15: warning: ignoring return value of 'request_irq', declared with attribute warn_unused_result + +Signed-off-by: John Crispin +Cc: netdev@vger.kernel.org +--- + drivers/net/ethernet/lantiq_etop.c | 14 ++++++++------ + 1 files changed, 8 insertions(+), 6 deletions(-) + +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index 5a8ca89..daba994 100644 +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -313,6 +313,7 @@ ltq_etop_hw_init(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); + unsigned int mii_mode = priv->pldata->mii_mode; ++ int err = 0; + int i; + + ltq_pmu_enable(PMU_PPE); +@@ -357,7 +358,7 @@ ltq_etop_hw_init(struct net_device *dev) + + ltq_dma_init_port(DMA_PORT_ETOP); + +- for (i = 0; i < MAX_DMA_CHAN; i++) { ++ for (i = 0; i < MAX_DMA_CHAN && !err; i++) { + int irq = LTQ_DMA_ETOP + i; + struct ltq_etop_chan *ch = &priv->ch[i]; + +@@ -365,21 +366,22 @@ ltq_etop_hw_init(struct net_device *dev) + + if (IS_TX(i)) { + ltq_dma_alloc_tx(&ch->dma); +- request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED, ++ err = request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED, + "etop_tx", priv); + } else if (IS_RX(i)) { + ltq_dma_alloc_rx(&ch->dma); + for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM; + ch->dma.desc++) + if (ltq_etop_alloc_skb(ch)) +- return -ENOMEM; ++ err = -ENOMEM; + ch->dma.desc = 0; +- request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED, ++ err = request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED, + "etop_rx", priv); + } +- ch->dma.irq = irq; ++ if (!err) ++ ch->dma.irq = irq; + } +- return 0; ++ return err; + } + + static void +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0018-MIPS-lantiq-use-devres-managed-gpios.patch b/target/linux/lantiq/patches-3.3/0018-MIPS-lantiq-use-devres-managed-gpios.patch new file mode 100644 index 0000000000..4d4e9202ec --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0018-MIPS-lantiq-use-devres-managed-gpios.patch @@ -0,0 +1,282 @@ +From 400943cf88102423ac10a19c56d053d9c1580a77 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 08:37:25 +0100 +Subject: [PATCH 18/70] MIPS: lantiq: use devres managed gpios + +3.2 introduced devm_request_gpio() to allow managed gpios. + +The devres api requires a struct device pointer to work. Add a parameter to ltq_gpio_request() +so that managed gpios can work. + +Signed-off-by: John Crispin +--- + .../include/asm/mach-lantiq/falcon/lantiq_soc.h | 4 +--- + arch/mips/include/asm/mach-lantiq/lantiq.h | 4 ++++ + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 3 --- + arch/mips/lantiq/falcon/gpio.c | 4 ++-- + arch/mips/lantiq/falcon/prom.c | 7 ------- + arch/mips/lantiq/xway/gpio.c | 4 ++-- + arch/mips/lantiq/xway/gpio_stp.c | 13 ++++++++----- + arch/mips/pci/pci-lantiq.c | 18 ++++++++++-------- + drivers/net/ethernet/lantiq_etop.c | 9 ++++++--- + drivers/tty/serial/lantiq.c | 12 ++++++++++++ + 10 files changed, 45 insertions(+), 33 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +index b074748..a5dc06a 100644 +--- a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +@@ -126,9 +126,7 @@ extern __iomem void *ltq_sys1_membase; + #define ltq_sys1_w32_mask(clear, set, reg) \ + ltq_sys1_w32((ltq_sys1_r32(reg) & ~(clear)) | (set), reg) + +-/* gpio_request wrapper to help configure the pin */ +-extern int ltq_gpio_request(unsigned int pin, unsigned int mux, +- unsigned int dir, const char *name); ++/* gpio wrapper to help configure the pin muxing */ + extern int ltq_gpio_mux_set(unsigned int pin, unsigned int mux); + + /* to keep the irq code generic we need to define these to 0 as falcon +diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h +index 188de0f..924b91a 100644 +--- a/arch/mips/include/asm/mach-lantiq/lantiq.h ++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h +@@ -37,6 +37,10 @@ extern unsigned int ltq_get_soc_type(void); + /* spinlock all ebu i/o */ + extern spinlock_t ebu_lock; + ++/* request a non-gpio and set the PIO config */ ++extern int ltq_gpio_request(struct device *dev, unsigned int pin, ++ unsigned int mux, unsigned int dir, const char *name); ++ + /* some irq helpers */ + extern void ltq_disable_irq(struct irq_data *data); + extern void ltq_mask_and_ack_irq(struct irq_data *data); +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 6983d75..6c5b705 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -145,9 +145,6 @@ + extern __iomem void *ltq_ebu_membase; + extern __iomem void *ltq_cgu_membase; + +-/* request a non-gpio and set the PIO config */ +-extern int ltq_gpio_request(unsigned int pin, unsigned int mux, +- unsigned int dir, const char *name); + extern void ltq_pmu_enable(unsigned int module); + extern void ltq_pmu_disable(unsigned int module); + extern void ltq_cgu_enable(unsigned int clk); +diff --git a/arch/mips/lantiq/falcon/gpio.c b/arch/mips/lantiq/falcon/gpio.c +index 28f8639..a44f71b 100644 +--- a/arch/mips/lantiq/falcon/gpio.c ++++ b/arch/mips/lantiq/falcon/gpio.c +@@ -97,7 +97,7 @@ int ltq_gpio_mux_set(unsigned int pin, unsigned int mux) + } + EXPORT_SYMBOL(ltq_gpio_mux_set); + +-int ltq_gpio_request(unsigned int pin, unsigned int mux, ++int ltq_gpio_request(struct device *dev, unsigned int pin, unsigned int mux, + unsigned int dir, const char *name) + { + int port = pin / 100; +@@ -106,7 +106,7 @@ int ltq_gpio_request(unsigned int pin, unsigned int mux, + if (offset >= PINS_PER_PORT || port >= MAX_PORTS) + return -EINVAL; + +- if (gpio_request(pin, name)) { ++ if (devm_gpio_request(dev, pin, name)) { + pr_err("failed to setup lantiq gpio: %s\n", name); + return -EBUSY; + } +diff --git a/arch/mips/lantiq/falcon/prom.c b/arch/mips/lantiq/falcon/prom.c +index b50d6f9..f98b389 100644 +--- a/arch/mips/lantiq/falcon/prom.c ++++ b/arch/mips/lantiq/falcon/prom.c +@@ -27,9 +27,6 @@ + #define TYPE_SHIFT 26 + #define TYPE_MASK 0x3C000000 + +-#define MUXC_SIF_RX_PIN 112 +-#define MUXC_SIF_TX_PIN 113 +- + /* this parameter allows us enable/disable asc1 via commandline */ + static int register_asc1; + static int __init +@@ -48,10 +45,6 @@ ltq_soc_setup(void) + falcon_register_gpio(); + if (register_asc1) { + ltq_register_asc(1); +- if (ltq_gpio_request(MUXC_SIF_RX_PIN, 3, 0, "asc1-rx")) +- pr_err("failed to request asc1-rx"); +- if (ltq_gpio_request(MUXC_SIF_TX_PIN, 3, 1, "asc1-tx")) +- pr_err("failed to request asc1-tx"); + ltq_sysctl_activate(SYSCTL_SYS1, ACTS_ASC1_ACT); + } + } +diff --git a/arch/mips/lantiq/xway/gpio.c b/arch/mips/lantiq/xway/gpio.c +index 14ff7c7..54ec6c9 100644 +--- a/arch/mips/lantiq/xway/gpio.c ++++ b/arch/mips/lantiq/xway/gpio.c +@@ -50,14 +50,14 @@ int irq_to_gpio(unsigned int gpio) + } + EXPORT_SYMBOL(irq_to_gpio); + +-int ltq_gpio_request(unsigned int pin, unsigned int mux, ++int ltq_gpio_request(struct device *dev, unsigned int pin, unsigned int mux, + unsigned int dir, const char *name) + { + int id = 0; + + if (pin >= (MAX_PORTS * PINS_PER_PORT)) + return -EINVAL; +- if (gpio_request(pin, name)) { ++ if (devm_gpio_request(dev, pin, name)) { + pr_err("failed to setup lantiq gpio: %s\n", name); + return -EBUSY; + } +diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c +index cb6f170..e6b4809 100644 +--- a/arch/mips/lantiq/xway/gpio_stp.c ++++ b/arch/mips/lantiq/xway/gpio_stp.c +@@ -80,11 +80,6 @@ static struct gpio_chip ltq_stp_chip = { + + static int ltq_stp_hw_init(void) + { +- /* the 3 pins used to control the external stp */ +- ltq_gpio_request(4, 2, 1, "stp-st"); +- ltq_gpio_request(5, 2, 1, "stp-d"); +- ltq_gpio_request(6, 2, 1, "stp-sh"); +- + /* sane defaults */ + ltq_stp_w32(0, LTQ_STP_AR); + ltq_stp_w32(0, LTQ_STP_CPU0); +@@ -133,6 +128,14 @@ static int __devinit ltq_stp_probe(struct platform_device *pdev) + dev_err(&pdev->dev, "failed to remap STP memory\n"); + return -ENOMEM; + } ++ ++ /* the 3 pins used to control the external stp */ ++ if (ltq_gpio_request(&pdev->dev, 4, 2, 1, "stp-st") || ++ ltq_gpio_request(&pdev->dev, 5, 2, 1, "stp-d") || ++ ltq_gpio_request(&pdev->dev, 6, 2, 1, "stp-sh")) { ++ dev_err(&pdev->dev, "failed to request needed gpios\n"); ++ return -EBUSY; ++ } + ret = gpiochip_add(<q_stp_chip); + if (!ret) + ret = ltq_stp_hw_init(); +diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c +index c001c5a..47b551c 100644 +--- a/arch/mips/pci/pci-lantiq.c ++++ b/arch/mips/pci/pci-lantiq.c +@@ -150,24 +150,26 @@ static u32 ltq_calc_bar11mask(void) + return bar11mask; + } + +-static void ltq_pci_setup_gpio(int gpio) ++static void ltq_pci_setup_gpio(struct device *dev) + { ++ struct ltq_pci_data *conf = (struct ltq_pci_data *) dev->platform_data; + int i; + for (i = 0; i < ARRAY_SIZE(ltq_pci_gpio_map); i++) { +- if (gpio & (1 << i)) { +- ltq_gpio_request(ltq_pci_gpio_map[i].pin, ++ if (conf->gpio & (1 << i)) { ++ ltq_gpio_request(dev, ltq_pci_gpio_map[i].pin, + ltq_pci_gpio_map[i].mux, + ltq_pci_gpio_map[i].dir, + ltq_pci_gpio_map[i].name); + } + } +- ltq_gpio_request(21, 0, 1, "pci-reset"); +- ltq_pci_req_mask = (gpio >> PCI_REQ_SHIFT) & PCI_REQ_MASK; ++ ltq_gpio_request(dev, 21, 0, 1, "pci-reset"); ++ ltq_pci_req_mask = (conf->gpio >> PCI_REQ_SHIFT) & PCI_REQ_MASK; + } + +-static int __devinit ltq_pci_startup(struct ltq_pci_data *conf) ++static int __devinit ltq_pci_startup(struct device *dev) + { + u32 temp_buffer; ++ struct ltq_pci_data *conf = (struct ltq_pci_data *) dev->platform_data; + + /* set clock to 33Mhz */ + if (ltq_is_ar9()) { +@@ -190,7 +192,7 @@ static int __devinit ltq_pci_startup(struct ltq_pci_data *conf) + } + + /* setup pci clock and gpis used by pci */ +- ltq_pci_setup_gpio(conf->gpio); ++ ltq_pci_setup_gpio(dev); + + /* enable auto-switching between PCI and EBU */ + ltq_pci_w32(0xa, PCI_CR_CLK_CTRL); +@@ -275,7 +277,7 @@ static int __devinit ltq_pci_probe(struct platform_device *pdev) + ioremap_nocache(LTQ_PCI_CFG_BASE, LTQ_PCI_CFG_BASE); + ltq_pci_controller.io_map_base = + (unsigned long)ioremap(LTQ_PCI_IO_BASE, LTQ_PCI_IO_SIZE - 1); +- ltq_pci_startup(ltq_pci_data); ++ ltq_pci_startup(&pdev->dev); + register_pci_controller(<q_pci_controller); + + return 0; +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index daba994..ad0fa54 100644 +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -292,9 +292,6 @@ ltq_etop_gbit_init(void) + { + ltq_pmu_enable(PMU_SWITCH); + +- ltq_gpio_request(42, 2, 1, "MDIO"); +- ltq_gpio_request(43, 2, 1, "MDC"); +- + ltq_gbit_w32_mask(0, GCTL0_SE, LTQ_GBIT_GCTL0); + /** Disable MDIO auto polling mode */ + ltq_gbit_w32_mask(0, PX_CTL_DMDIO, LTQ_GBIT_P0_CTL); +@@ -870,6 +867,12 @@ ltq_etop_probe(struct platform_device *pdev) + err = -ENOMEM; + goto err_out; + } ++ if (ltq_gpio_request(&pdev->dev, 42, 2, 1, "MDIO") || ++ ltq_gpio_request(&pdev->dev, 43, 2, 1, "MDC")) { ++ dev_err(&pdev->dev, "failed to request MDIO gpios\n"); ++ err = -EBUSY; ++ goto err_out; ++ } + } + + dev = alloc_etherdev_mq(sizeof(struct ltq_etop_priv), 4); +diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c +index 96c1cac..5d25828 100644 +--- a/drivers/tty/serial/lantiq.c ++++ b/drivers/tty/serial/lantiq.c +@@ -107,6 +107,9 @@ + #define ASCFSTAT_TXFREEMASK 0x3F000000 + #define ASCFSTAT_TXFREEOFF 24 + ++#define MUXC_SIF_RX_PIN 112 ++#define MUXC_SIF_TX_PIN 113 ++ + static void lqasc_tx_chars(struct uart_port *port); + static struct ltq_uart_port *lqasc_port[MAXPORTS]; + static struct uart_driver lqasc_reg; +@@ -529,6 +532,15 @@ lqasc_request_port(struct uart_port *port) + if (port->membase == NULL) + return -ENOMEM; + } ++ if (ltq_is_falcon() && (port->line == 1)) { ++ struct ltq_uart_port *ltq_port = lqasc_port[pdev->id]; ++ if (ltq_gpio_request(&pdev->dev, MUXC_SIF_RX_PIN, ++ 3, 0, "asc1-rx")) ++ return -EBUSY; ++ if (ltq_gpio_request(&pdev->dev, MUXC_SIF_TX_PIN, ++ 3, 1, "asc1-tx")) ++ return -EBUSY; ++ } + return 0; + } + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0019-MIPS-add-clkdev.h.patch b/target/linux/lantiq/patches-3.3/0019-MIPS-add-clkdev.h.patch new file mode 100644 index 0000000000..1cea4dd99f --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0019-MIPS-add-clkdev.h.patch @@ -0,0 +1,49 @@ +From 7396c59a2be411a11c640d79417ff257f1b94eff Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 Feb 2012 20:23:36 +0100 +Subject: [PATCH 19/70] MIPS: add clkdev.h + +For clkdev to work on MIPS we need this file + +include/linux/clkdev.h:#include + +Signed-off-by: John Crispin +--- + arch/mips/include/asm/clkdev.h | 25 +++++++++++++++++++++++++ + 1 files changed, 25 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/include/asm/clkdev.h + +diff --git a/arch/mips/include/asm/clkdev.h b/arch/mips/include/asm/clkdev.h +new file mode 100644 +index 0000000..2624754 +--- /dev/null ++++ b/arch/mips/include/asm/clkdev.h +@@ -0,0 +1,25 @@ ++/* ++ * based on arch/arm/include/asm/clkdev.h ++ * ++ * Copyright (C) 2008 Russell King. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Helper for the clk API to assist looking up a struct clk. ++ */ ++#ifndef __ASM_CLKDEV_H ++#define __ASM_CLKDEV_H ++ ++#include ++ ++#define __clk_get(clk) ({ 1; }) ++#define __clk_put(clk) do { } while (0) ++ ++static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) ++{ ++ return kzalloc(size, GFP_KERNEL); ++} ++ ++#endif +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0020-MIPS-lantiq-helper-functions-for-SoC-detection.patch b/target/linux/lantiq/patches-3.3/0020-MIPS-lantiq-helper-functions-for-SoC-detection.patch new file mode 100644 index 0000000000..d8027da206 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0020-MIPS-lantiq-helper-functions-for-SoC-detection.patch @@ -0,0 +1,59 @@ +From 601e5ea89b1fd6fb8047ea7c23eae6eb1c90145f Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 Feb 2012 14:25:03 +0100 +Subject: [PATCH 20/70] MIPS: lantiq: helper functions for SoC detection + +Add additional functions for runtime soc detection. We need these for the +serial driver. + +Signed-off-by: John Crispin +--- + .../include/asm/mach-lantiq/falcon/lantiq_soc.h | 16 ++++++++++++++-- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 5 +++++ + 2 files changed, 19 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +index a5dc06a..0aa1f16 100644 +--- a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +@@ -134,8 +134,20 @@ extern int ltq_gpio_mux_set(unsigned int pin, unsigned int mux); + #define LTQ_EIU_BASE_ADDR 0 + #define LTQ_EBU_PCC_ISTAT 0 + +-#define ltq_is_ar9() 0 +-#define ltq_is_vr9() 0 ++static inline int ltq_is_ar9(void) ++{ ++ return 0; ++} ++ ++static inline int ltq_is_vr9(void) ++{ ++ return 0; ++} ++ ++static inline int ltq_is_falcon(void) ++{ ++ return 1; ++} + + #endif /* CONFIG_SOC_FALCON */ + #endif /* _LTQ_XWAY_H__ */ +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 6c5b705..45e480c 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -164,5 +164,10 @@ static inline int ltq_is_vr9(void) + return (ltq_get_soc_type() == SOC_TYPE_VR9); + } + ++static inline int ltq_is_falcon(void) ++{ ++ return 0; ++} ++ + #endif /* CONFIG_SOC_TYPE_XWAY */ + #endif /* _LTQ_XWAY_H__ */ +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0021-MIPS-lantiq-convert-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0021-MIPS-lantiq-convert-to-clkdev-api.patch new file mode 100644 index 0000000000..5ee11a2d3f --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0021-MIPS-lantiq-convert-to-clkdev-api.patch @@ -0,0 +1,299 @@ +From 69f1bf02b0c6b132140326320d26aa4e91bc3290 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 08:39:06 +0100 +Subject: [PATCH 21/70] MIPS: lantiq: convert to clkdev api + +* Change setup from HAVE_CLK -> HAVE_MACH_CLKDEV/CLKDEV_LOOKUP +* Add clk_activate/clk_deactivate +* Add better error paths to the clk_*() functions +* Change the way our static clocks are referenced + +Signed-off-by: John Crispin +--- + arch/mips/Kconfig | 3 +- + arch/mips/include/asm/mach-lantiq/lantiq.h | 20 ++---- + arch/mips/lantiq/clk.c | 96 +++++++++++++++------------ + arch/mips/lantiq/clk.h | 52 ++++++++++++++- + arch/mips/lantiq/prom.c | 1 - + 5 files changed, 111 insertions(+), 61 deletions(-) + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 8ad52f4..df4e125 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -228,7 +228,8 @@ config LANTIQ + select ARCH_REQUIRE_GPIOLIB + select SWAP_IO_SPACE + select BOOT_RAW +- select HAVE_CLK ++ select HAVE_MACH_CLKDEV ++ select CLKDEV_LOOKUP + select HAVE_OPROFILE + select MIPS_MACHINE + +diff --git a/arch/mips/include/asm/mach-lantiq/lantiq.h b/arch/mips/include/asm/mach-lantiq/lantiq.h +index 924b91a..622847f 100644 +--- a/arch/mips/include/asm/mach-lantiq/lantiq.h ++++ b/arch/mips/include/asm/mach-lantiq/lantiq.h +@@ -9,6 +9,7 @@ + #define _LANTIQ_H__ + + #include ++#include + #include + + /* generic reg access functions */ +@@ -22,18 +23,6 @@ + extern unsigned int ltq_get_cpu_ver(void); + extern unsigned int ltq_get_soc_type(void); + +-/* clock speeds */ +-#define CLOCK_60M 60000000 +-#define CLOCK_83M 83333333 +-#define CLOCK_100M 100000000 +-#define CLOCK_111M 111111111 +-#define CLOCK_133M 133333333 +-#define CLOCK_167M 166666667 +-#define CLOCK_200M 200000000 +-#define CLOCK_266M 266666666 +-#define CLOCK_333M 333333333 +-#define CLOCK_400M 400000000 +- + /* spinlock all ebu i/o */ + extern spinlock_t ebu_lock; + +@@ -46,6 +35,13 @@ extern void ltq_disable_irq(struct irq_data *data); + extern void ltq_mask_and_ack_irq(struct irq_data *data); + extern void ltq_enable_irq(struct irq_data *data); + ++/* clock handling */ ++extern int clk_activate(struct clk *clk); ++extern void clk_deactivate(struct clk *clk); ++extern struct clk *clk_get_cpu(void); ++extern struct clk *clk_get_fpi(void); ++extern struct clk *clk_get_io(void); ++ + /* find out what caused the last cpu reset */ + extern int ltq_reset_cause(void); + +diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c +index 39eef7f..84a201e 100644 +--- a/arch/mips/lantiq/clk.c ++++ b/arch/mips/lantiq/clk.c +@@ -12,6 +12,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -24,33 +25,29 @@ + #include "clk.h" + #include "prom.h" + +-struct clk { +- const char *name; +- unsigned long rate; +- unsigned long (*get_rate) (void); +-}; ++/* lantiq socs have 3 static clocks */ ++static struct clk cpu_clk_generic[3]; + +-static struct clk *cpu_clk; +-static int cpu_clk_cnt; ++void clkdev_add_static(unsigned long cpu, unsigned long fpi, unsigned long io) ++{ ++ cpu_clk_generic[0].rate = cpu; ++ cpu_clk_generic[1].rate = fpi; ++ cpu_clk_generic[2].rate = io; ++} + +-/* lantiq socs have 3 static clocks */ +-static struct clk cpu_clk_generic[] = { +- { +- .name = "cpu", +- .get_rate = ltq_get_cpu_hz, +- }, { +- .name = "fpi", +- .get_rate = ltq_get_fpi_hz, +- }, { +- .name = "io", +- .get_rate = ltq_get_io_region_clock, +- }, +-}; +- +-void clk_init(void) ++struct clk *clk_get_cpu(void) ++{ ++ return &cpu_clk_generic[0]; ++} ++ ++struct clk *clk_get_fpi(void) + { +- cpu_clk = cpu_clk_generic; +- cpu_clk_cnt = ARRAY_SIZE(cpu_clk_generic); ++ return &cpu_clk_generic[1]; ++} ++ ++struct clk *clk_get_io(void) ++{ ++ return &cpu_clk_generic[2]; + } + + static inline int clk_good(struct clk *clk) +@@ -73,36 +70,49 @@ unsigned long clk_get_rate(struct clk *clk) + } + EXPORT_SYMBOL(clk_get_rate); + +-struct clk *clk_get(struct device *dev, const char *id) ++int clk_enable(struct clk *clk) + { +- int i; ++ if (unlikely(!clk_good(clk))) ++ return -1; ++ ++ if (clk->enable) ++ return clk->enable(clk); + +- for (i = 0; i < cpu_clk_cnt; i++) +- if (!strcmp(id, cpu_clk[i].name)) +- return &cpu_clk[i]; +- BUG(); +- return ERR_PTR(-ENOENT); ++ return -1; + } +-EXPORT_SYMBOL(clk_get); ++EXPORT_SYMBOL(clk_enable); + +-void clk_put(struct clk *clk) ++void clk_disable(struct clk *clk) + { +- /* not used */ ++ if (unlikely(!clk_good(clk))) ++ return; ++ ++ if (clk->disable) ++ clk->disable(clk); + } +-EXPORT_SYMBOL(clk_put); ++EXPORT_SYMBOL(clk_disable); + +-int clk_enable(struct clk *clk) ++int clk_activate(struct clk *clk) + { +- /* not used */ +- return 0; ++ if (unlikely(!clk_good(clk))) ++ return -1; ++ ++ if (clk->activate) ++ return clk->activate(clk); ++ ++ return -1; + } +-EXPORT_SYMBOL(clk_enable); ++EXPORT_SYMBOL(clk_activate); + +-void clk_disable(struct clk *clk) ++void clk_deactivate(struct clk *clk) + { +- /* not used */ ++ if (unlikely(!clk_good(clk))) ++ return; ++ ++ if (clk->deactivate) ++ clk->deactivate(clk); + } +-EXPORT_SYMBOL(clk_disable); ++EXPORT_SYMBOL(clk_deactivate); + + static inline u32 ltq_get_counter_resolution(void) + { +@@ -126,7 +136,7 @@ void __init plat_time_init(void) + + ltq_soc_init(); + +- clk = clk_get(0, "cpu"); ++ clk = clk_get_cpu(); + mips_hpt_frequency = clk_get_rate(clk) / ltq_get_counter_resolution(); + write_c0_compare(read_c0_count()); + pr_info("CPU Clock: %ldMHz\n", clk_get_rate(clk) / 1000000); +diff --git a/arch/mips/lantiq/clk.h b/arch/mips/lantiq/clk.h +index 3328925..d047768 100644 +--- a/arch/mips/lantiq/clk.h ++++ b/arch/mips/lantiq/clk.h +@@ -9,10 +9,54 @@ + #ifndef _LTQ_CLK_H__ + #define _LTQ_CLK_H__ + +-extern void clk_init(void); ++#include + +-extern unsigned long ltq_get_cpu_hz(void); +-extern unsigned long ltq_get_fpi_hz(void); +-extern unsigned long ltq_get_io_region_clock(void); ++/* clock speeds */ ++#define CLOCK_60M 60000000 ++#define CLOCK_62_5M 62500000 ++#define CLOCK_83M 83333333 ++#define CLOCK_83_5M 83500000 ++#define CLOCK_98_304M 98304000 ++#define CLOCK_100M 100000000 ++#define CLOCK_111M 111111111 ++#define CLOCK_125M 125000000 ++#define CLOCK_133M 133333333 ++#define CLOCK_150M 150000000 ++#define CLOCK_166M 166666666 ++#define CLOCK_167M 166666667 ++#define CLOCK_196_608M 196608000 ++#define CLOCK_200M 200000000 ++#define CLOCK_250M 250000000 ++#define CLOCK_266M 266666666 ++#define CLOCK_300M 300000000 ++#define CLOCK_333M 333333333 ++#define CLOCK_393M 393215332 ++#define CLOCK_400M 400000000 ++#define CLOCK_500M 500000000 ++#define CLOCK_600M 600000000 ++ ++struct clk { ++ struct clk_lookup cl; ++ unsigned long rate; ++ unsigned long (*get_rate) (void); ++ unsigned int module; ++ unsigned int bits; ++ int (*enable) (struct clk *clk); ++ void (*disable) (struct clk *clk); ++ int (*activate) (struct clk *clk); ++ void (*deactivate) (struct clk *clk); ++ void (*reboot) (struct clk *clk); ++}; ++ ++extern void clkdev_add_static(unsigned long cpu, unsigned long fpi, ++ unsigned long io); ++ ++extern unsigned long ltq_danube_cpu_hz(void); ++extern unsigned long ltq_danube_fpi_hz(void); ++extern unsigned long ltq_danube_io_region_clock(void); ++ ++extern unsigned long ltq_vr9_cpu_hz(void); ++extern unsigned long ltq_vr9_fpi_hz(void); ++extern unsigned long ltq_vr9_io_region_clock(void); + + #endif +diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c +index acb8921..971554b 100644 +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -103,7 +103,6 @@ EXPORT_SYMBOL(ltq_remap_resource); + void __init prom_init(void) + { + ltq_soc_detect(&soc_info); +- clk_init(); + snprintf(soc_info.sys_type, LTQ_SYS_TYPE_LEN - 1, "%s rev %s", + soc_info.name, soc_info.rev_type); + soc_info.sys_type[LTQ_SYS_TYPE_LEN - 1] = '\0'; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0022-MIPS-lantiq-convert-xway-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0022-MIPS-lantiq-convert-xway-to-clkdev-api.patch new file mode 100644 index 0000000000..790262afd4 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0022-MIPS-lantiq-convert-xway-to-clkdev-api.patch @@ -0,0 +1,736 @@ +From de49a17fd2d9a73b9449f04061bb08eb5d73fe98 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:18:22 +0100 +Subject: [PATCH 22/70] MIPS: lantiq: convert xway to clkdev api + +Unify xway/ase clock code and add clkdev hooks to sysctrl.c + +Signed-off-by: John Crispin +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 13 -- + arch/mips/lantiq/xway/Makefile | 6 +- + arch/mips/lantiq/xway/clk-ase.c | 48 ---- + arch/mips/lantiq/xway/clk-xway.c | 223 ------------------- + arch/mips/lantiq/xway/clk.c | 227 ++++++++++++++++++++ + arch/mips/lantiq/xway/sysctrl.c | 104 ++++++++- + 6 files changed, 325 insertions(+), 296 deletions(-) + delete mode 100644 arch/mips/lantiq/xway/clk-ase.c + delete mode 100644 arch/mips/lantiq/xway/clk-xway.c + create mode 100644 arch/mips/lantiq/xway/clk.c + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 45e480c..e9d2dd4 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -81,15 +81,6 @@ + #define LTQ_PMU_BASE_ADDR 0x1F102000 + #define LTQ_PMU_SIZE 0x1000 + +-#define PMU_DMA 0x0020 +-#define PMU_EPHY 0x0080 +-#define PMU_USB 0x8041 +-#define PMU_LED 0x0800 +-#define PMU_GPT 0x1000 +-#define PMU_PPE 0x2000 +-#define PMU_FPI 0x4000 +-#define PMU_SWITCH 0x10000000 +- + /* ETOP - ethernet */ + #define LTQ_ETOP_BASE_ADDR 0x1E180000 + #define LTQ_ETOP_SIZE 0x40000 +@@ -145,10 +136,6 @@ + extern __iomem void *ltq_ebu_membase; + extern __iomem void *ltq_cgu_membase; + +-extern void ltq_pmu_enable(unsigned int module); +-extern void ltq_pmu_disable(unsigned int module); +-extern void ltq_cgu_enable(unsigned int clk); +- + static inline int ltq_is_ase(void) + { + return (ltq_get_soc_type() == SOC_TYPE_AMAZON_SE); +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 6678402..4dcb96f 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,7 +1,7 @@ +-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o ++obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o + +-obj-$(CONFIG_SOC_XWAY) += clk-xway.o prom-xway.o +-obj-$(CONFIG_SOC_AMAZON_SE) += clk-ase.o prom-ase.o ++obj-$(CONFIG_SOC_XWAY) += prom-xway.o ++obj-$(CONFIG_SOC_AMAZON_SE) += prom-ase.o + + obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o + obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/clk-ase.c b/arch/mips/lantiq/xway/clk-ase.c +deleted file mode 100644 +index 6522583..0000000 +--- a/arch/mips/lantiq/xway/clk-ase.c ++++ /dev/null +@@ -1,48 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2011 John Crispin +- */ +- +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include +- +-/* cgu registers */ +-#define LTQ_CGU_SYS 0x0010 +- +-unsigned int ltq_get_io_region_clock(void) +-{ +- return CLOCK_133M; +-} +-EXPORT_SYMBOL(ltq_get_io_region_clock); +- +-unsigned int ltq_get_fpi_bus_clock(int fpi) +-{ +- return CLOCK_133M; +-} +-EXPORT_SYMBOL(ltq_get_fpi_bus_clock); +- +-unsigned int ltq_get_cpu_hz(void) +-{ +- if (ltq_cgu_r32(LTQ_CGU_SYS) & (1 << 5)) +- return CLOCK_266M; +- else +- return CLOCK_133M; +-} +-EXPORT_SYMBOL(ltq_get_cpu_hz); +- +-unsigned int ltq_get_fpi_hz(void) +-{ +- return CLOCK_133M; +-} +-EXPORT_SYMBOL(ltq_get_fpi_hz); +diff --git a/arch/mips/lantiq/xway/clk-xway.c b/arch/mips/lantiq/xway/clk-xway.c +deleted file mode 100644 +index 696b1a3..0000000 +--- a/arch/mips/lantiq/xway/clk-xway.c ++++ /dev/null +@@ -1,223 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2010 John Crispin +- */ +- +-#include +-#include +-#include +-#include +- +-#include +-#include +-#include +- +-#include +- +-static unsigned int ltq_ram_clocks[] = { +- CLOCK_167M, CLOCK_133M, CLOCK_111M, CLOCK_83M }; +-#define DDR_HZ ltq_ram_clocks[ltq_cgu_r32(LTQ_CGU_SYS) & 0x3] +- +-#define BASIC_FREQUENCY_1 35328000 +-#define BASIC_FREQUENCY_2 36000000 +-#define BASIS_REQUENCY_USB 12000000 +- +-#define GET_BITS(x, msb, lsb) \ +- (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb)) +- +-#define LTQ_CGU_PLL0_CFG 0x0004 +-#define LTQ_CGU_PLL1_CFG 0x0008 +-#define LTQ_CGU_PLL2_CFG 0x000C +-#define LTQ_CGU_SYS 0x0010 +-#define LTQ_CGU_UPDATE 0x0014 +-#define LTQ_CGU_IF_CLK 0x0018 +-#define LTQ_CGU_OSC_CON 0x001C +-#define LTQ_CGU_SMD 0x0020 +-#define LTQ_CGU_CT1SR 0x0028 +-#define LTQ_CGU_CT2SR 0x002C +-#define LTQ_CGU_PCMCR 0x0030 +-#define LTQ_CGU_PCI_CR 0x0034 +-#define LTQ_CGU_PD_PC 0x0038 +-#define LTQ_CGU_FMR 0x003C +- +-#define CGU_PLL0_PHASE_DIVIDER_ENABLE \ +- (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 31)) +-#define CGU_PLL0_BYPASS \ +- (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 30)) +-#define CGU_PLL0_CFG_DSMSEL \ +- (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 28)) +-#define CGU_PLL0_CFG_FRAC_EN \ +- (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 27)) +-#define CGU_PLL1_SRC \ +- (ltq_cgu_r32(LTQ_CGU_PLL1_CFG) & (1 << 31)) +-#define CGU_PLL2_PHASE_DIVIDER_ENABLE \ +- (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & (1 << 20)) +-#define CGU_SYS_FPI_SEL (1 << 6) +-#define CGU_SYS_DDR_SEL 0x3 +-#define CGU_PLL0_SRC (1 << 29) +- +-#define CGU_PLL0_CFG_PLLK GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 26, 17) +-#define CGU_PLL0_CFG_PLLN GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 12, 6) +-#define CGU_PLL0_CFG_PLLM GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 5, 2) +-#define CGU_PLL2_SRC GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 18, 17) +-#define CGU_PLL2_CFG_INPUT_DIV GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 16, 13) +- +-static unsigned int ltq_get_pll0_fdiv(void); +- +-static inline unsigned int get_input_clock(int pll) +-{ +- switch (pll) { +- case 0: +- if (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & CGU_PLL0_SRC) +- return BASIS_REQUENCY_USB; +- else if (CGU_PLL0_PHASE_DIVIDER_ENABLE) +- return BASIC_FREQUENCY_1; +- else +- return BASIC_FREQUENCY_2; +- case 1: +- if (CGU_PLL1_SRC) +- return BASIS_REQUENCY_USB; +- else if (CGU_PLL0_PHASE_DIVIDER_ENABLE) +- return BASIC_FREQUENCY_1; +- else +- return BASIC_FREQUENCY_2; +- case 2: +- switch (CGU_PLL2_SRC) { +- case 0: +- return ltq_get_pll0_fdiv(); +- case 1: +- return CGU_PLL2_PHASE_DIVIDER_ENABLE ? +- BASIC_FREQUENCY_1 : +- BASIC_FREQUENCY_2; +- case 2: +- return BASIS_REQUENCY_USB; +- } +- default: +- return 0; +- } +-} +- +-static inline unsigned int cal_dsm(int pll, unsigned int num, unsigned int den) +-{ +- u64 res, clock = get_input_clock(pll); +- +- res = num * clock; +- do_div(res, den); +- return res; +-} +- +-static inline unsigned int mash_dsm(int pll, unsigned int M, unsigned int N, +- unsigned int K) +-{ +- unsigned int num = ((N + 1) << 10) + K; +- unsigned int den = (M + 1) << 10; +- +- return cal_dsm(pll, num, den); +-} +- +-static inline unsigned int ssff_dsm_1(int pll, unsigned int M, unsigned int N, +- unsigned int K) +-{ +- unsigned int num = ((N + 1) << 11) + K + 512; +- unsigned int den = (M + 1) << 11; +- +- return cal_dsm(pll, num, den); +-} +- +-static inline unsigned int ssff_dsm_2(int pll, unsigned int M, unsigned int N, +- unsigned int K) +-{ +- unsigned int num = K >= 512 ? +- ((N + 1) << 12) + K - 512 : ((N + 1) << 12) + K + 3584; +- unsigned int den = (M + 1) << 12; +- +- return cal_dsm(pll, num, den); +-} +- +-static inline unsigned int dsm(int pll, unsigned int M, unsigned int N, +- unsigned int K, unsigned int dsmsel, unsigned int phase_div_en) +-{ +- if (!dsmsel) +- return mash_dsm(pll, M, N, K); +- else if (!phase_div_en) +- return mash_dsm(pll, M, N, K); +- else +- return ssff_dsm_2(pll, M, N, K); +-} +- +-static inline unsigned int ltq_get_pll0_fosc(void) +-{ +- if (CGU_PLL0_BYPASS) +- return get_input_clock(0); +- else +- return !CGU_PLL0_CFG_FRAC_EN +- ? dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN, 0, +- CGU_PLL0_CFG_DSMSEL, +- CGU_PLL0_PHASE_DIVIDER_ENABLE) +- : dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN, +- CGU_PLL0_CFG_PLLK, CGU_PLL0_CFG_DSMSEL, +- CGU_PLL0_PHASE_DIVIDER_ENABLE); +-} +- +-static unsigned int ltq_get_pll0_fdiv(void) +-{ +- unsigned int div = CGU_PLL2_CFG_INPUT_DIV + 1; +- +- return (ltq_get_pll0_fosc() + (div >> 1)) / div; +-} +- +-unsigned int ltq_get_io_region_clock(void) +-{ +- unsigned int ret = ltq_get_pll0_fosc(); +- +- switch (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & CGU_SYS_DDR_SEL) { +- default: +- case 0: +- return (ret + 1) / 2; +- case 1: +- return (ret * 2 + 2) / 5; +- case 2: +- return (ret + 1) / 3; +- case 3: +- return (ret + 2) / 4; +- } +-} +-EXPORT_SYMBOL(ltq_get_io_region_clock); +- +-unsigned int ltq_get_fpi_bus_clock(int fpi) +-{ +- unsigned int ret = ltq_get_io_region_clock(); +- +- if ((fpi == 2) && (ltq_cgu_r32(LTQ_CGU_SYS) & CGU_SYS_FPI_SEL)) +- ret >>= 1; +- return ret; +-} +-EXPORT_SYMBOL(ltq_get_fpi_bus_clock); +- +-unsigned int ltq_get_cpu_hz(void) +-{ +- switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0xc) { +- case 0: +- return CLOCK_333M; +- case 4: +- return DDR_HZ; +- case 8: +- return DDR_HZ << 1; +- default: +- return DDR_HZ >> 1; +- } +-} +-EXPORT_SYMBOL(ltq_get_cpu_hz); +- +-unsigned int ltq_get_fpi_hz(void) +-{ +- unsigned int ddr_clock = DDR_HZ; +- +- if (ltq_cgu_r32(LTQ_CGU_SYS) & 0x40) +- return ddr_clock >> 1; +- return ddr_clock; +-} +-EXPORT_SYMBOL(ltq_get_fpi_hz); +diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c +new file mode 100644 +index 0000000..f3b50fc +--- /dev/null ++++ b/arch/mips/lantiq/xway/clk.c +@@ -0,0 +1,227 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2010 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include ++ ++#include "../clk.h" ++ ++static unsigned int ltq_ram_clocks[] = { ++ CLOCK_167M, CLOCK_133M, CLOCK_111M, CLOCK_83M }; ++#define DDR_HZ ltq_ram_clocks[ltq_cgu_r32(LTQ_CGU_SYS) & 0x3] ++ ++#define BASIC_FREQUENCY_1 35328000 ++#define BASIC_FREQUENCY_2 36000000 ++#define BASIS_REQUENCY_USB 12000000 ++ ++#define GET_BITS(x, msb, lsb) \ ++ (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb)) ++ ++/* legacy xway clock */ ++#define LTQ_CGU_PLL0_CFG 0x0004 ++#define LTQ_CGU_PLL1_CFG 0x0008 ++#define LTQ_CGU_PLL2_CFG 0x000C ++#define LTQ_CGU_SYS 0x0010 ++#define LTQ_CGU_UPDATE 0x0014 ++#define LTQ_CGU_IF_CLK 0x0018 ++#define LTQ_CGU_OSC_CON 0x001C ++#define LTQ_CGU_SMD 0x0020 ++#define LTQ_CGU_CT1SR 0x0028 ++#define LTQ_CGU_CT2SR 0x002C ++#define LTQ_CGU_PCMCR 0x0030 ++#define LTQ_CGU_PCI_CR 0x0034 ++#define LTQ_CGU_PD_PC 0x0038 ++#define LTQ_CGU_FMR 0x003C ++ ++#define CGU_PLL0_PHASE_DIVIDER_ENABLE \ ++ (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 31)) ++#define CGU_PLL0_BYPASS \ ++ (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 30)) ++#define CGU_PLL0_CFG_DSMSEL \ ++ (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 28)) ++#define CGU_PLL0_CFG_FRAC_EN \ ++ (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & (1 << 27)) ++#define CGU_PLL1_SRC \ ++ (ltq_cgu_r32(LTQ_CGU_PLL1_CFG) & (1 << 31)) ++#define CGU_PLL2_PHASE_DIVIDER_ENABLE \ ++ (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & (1 << 20)) ++#define CGU_SYS_FPI_SEL (1 << 6) ++#define CGU_SYS_DDR_SEL 0x3 ++#define CGU_PLL0_SRC (1 << 29) ++ ++#define CGU_PLL0_CFG_PLLK GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 26, 17) ++#define CGU_PLL0_CFG_PLLN GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 12, 6) ++#define CGU_PLL0_CFG_PLLM GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL0_CFG), 5, 2) ++#define CGU_PLL2_SRC GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 18, 17) ++#define CGU_PLL2_CFG_INPUT_DIV GET_BITS(ltq_cgu_r32(LTQ_CGU_PLL2_CFG), 16, 13) ++ ++/* vr9 clock */ ++#define LTQ_CGU_SYS_VR9 0x0c ++#define LTQ_CGU_IF_CLK_VR9 0x24 ++ ++ ++static unsigned int ltq_get_pll0_fdiv(void); ++ ++static inline unsigned int get_input_clock(int pll) ++{ ++ switch (pll) { ++ case 0: ++ if (ltq_cgu_r32(LTQ_CGU_PLL0_CFG) & CGU_PLL0_SRC) ++ return BASIS_REQUENCY_USB; ++ else if (CGU_PLL0_PHASE_DIVIDER_ENABLE) ++ return BASIC_FREQUENCY_1; ++ else ++ return BASIC_FREQUENCY_2; ++ case 1: ++ if (CGU_PLL1_SRC) ++ return BASIS_REQUENCY_USB; ++ else if (CGU_PLL0_PHASE_DIVIDER_ENABLE) ++ return BASIC_FREQUENCY_1; ++ else ++ return BASIC_FREQUENCY_2; ++ case 2: ++ switch (CGU_PLL2_SRC) { ++ case 0: ++ return ltq_get_pll0_fdiv(); ++ case 1: ++ return CGU_PLL2_PHASE_DIVIDER_ENABLE ? ++ BASIC_FREQUENCY_1 : ++ BASIC_FREQUENCY_2; ++ case 2: ++ return BASIS_REQUENCY_USB; ++ } ++ default: ++ return 0; ++ } ++} ++ ++static inline unsigned int cal_dsm(int pll, unsigned int num, unsigned int den) ++{ ++ u64 res, clock = get_input_clock(pll); ++ ++ res = num * clock; ++ do_div(res, den); ++ return res; ++} ++ ++static inline unsigned int mash_dsm(int pll, unsigned int M, unsigned int N, ++ unsigned int K) ++{ ++ unsigned int num = ((N + 1) << 10) + K; ++ unsigned int den = (M + 1) << 10; ++ ++ return cal_dsm(pll, num, den); ++} ++ ++static inline unsigned int ssff_dsm_1(int pll, unsigned int M, unsigned int N, ++ unsigned int K) ++{ ++ unsigned int num = ((N + 1) << 11) + K + 512; ++ unsigned int den = (M + 1) << 11; ++ ++ return cal_dsm(pll, num, den); ++} ++ ++static inline unsigned int ssff_dsm_2(int pll, unsigned int M, unsigned int N, ++ unsigned int K) ++{ ++ unsigned int num = K >= 512 ? ++ ((N + 1) << 12) + K - 512 : ((N + 1) << 12) + K + 3584; ++ unsigned int den = (M + 1) << 12; ++ ++ return cal_dsm(pll, num, den); ++} ++ ++static inline unsigned int dsm(int pll, unsigned int M, unsigned int N, ++ unsigned int K, unsigned int dsmsel, unsigned int phase_div_en) ++{ ++ if (!dsmsel) ++ return mash_dsm(pll, M, N, K); ++ else if (!phase_div_en) ++ return mash_dsm(pll, M, N, K); ++ else ++ return ssff_dsm_2(pll, M, N, K); ++} ++ ++static inline unsigned int ltq_get_pll0_fosc(void) ++{ ++ if (CGU_PLL0_BYPASS) ++ return get_input_clock(0); ++ else ++ return !CGU_PLL0_CFG_FRAC_EN ++ ? dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN, 0, ++ CGU_PLL0_CFG_DSMSEL, ++ CGU_PLL0_PHASE_DIVIDER_ENABLE) ++ : dsm(0, CGU_PLL0_CFG_PLLM, CGU_PLL0_CFG_PLLN, ++ CGU_PLL0_CFG_PLLK, CGU_PLL0_CFG_DSMSEL, ++ CGU_PLL0_PHASE_DIVIDER_ENABLE); ++} ++ ++static unsigned int ltq_get_pll0_fdiv(void) ++{ ++ unsigned int div = CGU_PLL2_CFG_INPUT_DIV + 1; ++ ++ return (ltq_get_pll0_fosc() + (div >> 1)) / div; ++} ++ ++unsigned long ltq_danube_io_region_clock(void) ++{ ++ unsigned int ret = ltq_get_pll0_fosc(); ++ ++ switch (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & CGU_SYS_DDR_SEL) { ++ default: ++ case 0: ++ return (ret + 1) / 2; ++ case 1: ++ return (ret * 2 + 2) / 5; ++ case 2: ++ return (ret + 1) / 3; ++ case 3: ++ return (ret + 2) / 4; ++ } ++} ++ ++unsigned long ltq_danube_fpi_bus_clock(int fpi) ++{ ++ unsigned long ret = ltq_danube_io_region_clock(); ++ ++ if ((fpi == 2) && (ltq_cgu_r32(LTQ_CGU_SYS) & CGU_SYS_FPI_SEL)) ++ ret >>= 1; ++ return ret; ++} ++ ++unsigned long ltq_danube_cpu_hz(void) ++{ ++ switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0xc) { ++ case 0: ++ return CLOCK_333M; ++ case 4: ++ return DDR_HZ; ++ case 8: ++ return DDR_HZ << 1; ++ default: ++ return DDR_HZ >> 1; ++ } ++} ++ ++unsigned long ltq_danube_fpi_hz(void) ++{ ++ unsigned long ddr_clock = DDR_HZ; ++ ++ if (ltq_cgu_r32(LTQ_CGU_SYS) & 0x40) ++ return ddr_clock >> 1; ++ return ddr_clock; ++} +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 8fd13a1..c5782b5 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -8,17 +8,48 @@ + + #include + #include ++#include + + #include + ++#include "../clk.h" + #include "../devices.h" + + /* clock control register */ + #define LTQ_CGU_IFCCR 0x0018 ++/* system clock register */ ++#define LTQ_CGU_SYS 0x0010 + + /* the enable / disable registers */ + #define LTQ_PMU_PWDCR 0x1C + #define LTQ_PMU_PWDSR 0x20 ++#define LTQ_PMU_PWDCR1 0x24 ++#define LTQ_PMU_PWDSR1 0x28 ++ ++#define PWDCR(x) ((x) ? (LTQ_PMU_PWDCR1) : (LTQ_PMU_PWDCR)) ++#define PWDSR(x) ((x) ? (LTQ_PMU_PWDSR1) : (LTQ_PMU_PWDSR)) ++ ++/* CGU - clock generation unit */ ++#define CGU_EPHY 0x10 ++ ++/* PMU - power management unit */ ++#define PMU_DMA 0x0020 ++#define PMU_SPI 0x0100 ++#define PMU_EPHY 0x0080 ++#define PMU_USB 0x8041 ++#define PMU_STP 0x0800 ++#define PMU_GPT 0x1000 ++#define PMU_PPE 0x2000 ++#define PMU_FPI 0x4000 ++#define PMU_SWITCH 0x10000000 ++#define PMU_AHBS 0x2000 ++#define PMU_AHBM 0x8000 ++#define PMU_PCIE_CLK 0x80000000 ++ ++#define PMU1_PCIE_PHY 0x0001 ++#define PMU1_PCIE_CTL 0x0002 ++#define PMU1_PCIE_MSI 0x0020 ++#define PMU1_PCIE_PDI 0x0010 + + #define ltq_pmu_w32(x, y) ltq_w32((x), ltq_pmu_membase + (y)) + #define ltq_pmu_r32(x) ltq_r32(ltq_pmu_membase + (x)) +@@ -36,28 +67,64 @@ void __iomem *ltq_cgu_membase; + void __iomem *ltq_ebu_membase; + static void __iomem *ltq_pmu_membase; + +-void ltq_cgu_enable(unsigned int clk) ++static int ltq_cgu_enable(struct clk *clk) + { +- ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | clk, LTQ_CGU_IFCCR); ++ ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | clk->bits, LTQ_CGU_IFCCR); ++ return 0; + } + +-void ltq_pmu_enable(unsigned int module) ++static void ltq_cgu_disable(struct clk *clk) ++{ ++ ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~clk->bits, LTQ_CGU_IFCCR); ++} ++ ++static int ltq_pmu_enable(struct clk *clk) + { + int err = 1000000; + +- ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) & ~module, LTQ_PMU_PWDCR); +- do {} while (--err && (ltq_pmu_r32(LTQ_PMU_PWDSR) & module)); ++ ltq_pmu_w32(ltq_pmu_r32(PWDCR(clk->module)) & ~clk->bits, ++ PWDCR(clk->module)); ++ do {} while (--err && (ltq_pmu_r32(PWDSR(clk->module)) & clk->bits)); + + if (!err) + panic("activating PMU module failed!\n"); ++ ++ return 0; + } +-EXPORT_SYMBOL(ltq_pmu_enable); + +-void ltq_pmu_disable(unsigned int module) ++static void ltq_pmu_disable(struct clk *clk) + { +- ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) | module, LTQ_PMU_PWDCR); ++ ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) | clk->bits, LTQ_PMU_PWDCR); ++} ++ ++static inline void clkdev_add_pmu(const char *dev, const char *con, ++ unsigned int module, unsigned int bits) ++{ ++ struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL); ++ ++ clk->cl.dev_id = dev; ++ clk->cl.con_id = con; ++ clk->cl.clk = clk; ++ clk->enable = ltq_pmu_enable; ++ clk->disable = ltq_pmu_disable; ++ clk->module = module; ++ clk->bits = bits; ++ clkdev_add(&clk->cl); ++} ++ ++static inline void clkdev_add_cgu(const char *dev, const char *con, ++ unsigned int bits) ++{ ++ struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL); ++ ++ clk->cl.dev_id = dev; ++ clk->cl.con_id = con; ++ clk->cl.clk = clk; ++ clk->enable = ltq_cgu_enable; ++ clk->disable = ltq_cgu_disable; ++ clk->bits = bits; ++ clkdev_add(&clk->cl); + } +-EXPORT_SYMBOL(ltq_pmu_disable); + + void __init ltq_soc_init(void) + { +@@ -75,4 +142,23 @@ void __init ltq_soc_init(void) + + /* make sure to unprotect the memory region where flash is located */ + ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0); ++ ++ /* add our clocks */ ++ clkdev_add_pmu("ltq_dma", NULL, 0, PMU_DMA); ++ clkdev_add_pmu("ltq_stp", NULL, 0, PMU_STP); ++ clkdev_add_pmu("ltq_spi", NULL, 0, PMU_SPI); ++ clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE); ++ if (ltq_is_ase()) { ++ if (ltq_cgu_r32(LTQ_CGU_SYS) & (1 << 5)) ++ clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M); ++ else ++ clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M); ++ clkdev_add_cgu("ltq_etop", "ephycgu", CGU_EPHY), ++ clkdev_add_pmu("ltq_etop", "ephy", 0, PMU_EPHY); ++ } else { ++ clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), ++ ltq_danube_io_region_clock()); ++ if (ltq_is_ar9()) ++ clkdev_add_pmu("ltq_etop", "switch", 0, PMU_SWITCH); ++ } + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0023-MIPS-lantiq-convert-falcon-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0023-MIPS-lantiq-convert-falcon-to-clkdev-api.patch new file mode 100644 index 0000000000..27660861c2 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0023-MIPS-lantiq-convert-falcon-to-clkdev-api.patch @@ -0,0 +1,244 @@ +From c87a0c58116b2b2d8ccee9bb64df652b563307c4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:19:11 +0100 +Subject: [PATCH 23/70] MIPS: lantiq: convert falcon to clkdev api + +Unify sysctrl/clock code and add clkdev hooks to sysctrl.c + +Signed-off-by: John Crispin +--- + .../include/asm/mach-lantiq/falcon/lantiq_soc.h | 8 +- + arch/mips/lantiq/falcon/Makefile | 2 +- + arch/mips/lantiq/falcon/sysctrl.c | 129 ++++++++++++-------- + 3 files changed, 80 insertions(+), 59 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +index 0aa1f16..120c56c 100644 +--- a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +@@ -95,6 +95,7 @@ + + /* Activation Status Register */ + #define ACTS_ASC1_ACT 0x00000800 ++#define ACTS_I2C_ACT 0x00004000 + #define ACTS_P0 0x00010000 + #define ACTS_P1 0x00010000 + #define ACTS_P2 0x00020000 +@@ -106,13 +107,6 @@ + #define ACTS_PADCTRL3 0x00200000 + #define ACTS_PADCTRL4 0x00400000 + +-extern void ltq_sysctl_activate(int module, unsigned int mask); +-extern void ltq_sysctl_deactivate(int module, unsigned int mask); +-extern void ltq_sysctl_clken(int module, unsigned int mask); +-extern void ltq_sysctl_clkdis(int module, unsigned int mask); +-extern void ltq_sysctl_reboot(int module, unsigned int mask); +-extern int ltq_gpe_is_activated(unsigned int mask); +- + /* global register ranges */ + extern __iomem void *ltq_ebu_membase; + extern __iomem void *ltq_sys1_membase; +diff --git a/arch/mips/lantiq/falcon/Makefile b/arch/mips/lantiq/falcon/Makefile +index 56b22eb..3634154 100644 +--- a/arch/mips/lantiq/falcon/Makefile ++++ b/arch/mips/lantiq/falcon/Makefile +@@ -1,2 +1,2 @@ +-obj-y := clk.o prom.o reset.o sysctrl.o devices.o gpio.o ++obj-y := prom.o reset.o sysctrl.o devices.o gpio.o + obj-$(CONFIG_LANTIQ_MACH_EASY98000) += mach-easy98000.o +diff --git a/arch/mips/lantiq/falcon/sysctrl.c b/arch/mips/lantiq/falcon/sysctrl.c +index 905a142..900f0e5 100644 +--- a/arch/mips/lantiq/falcon/sysctrl.c ++++ b/arch/mips/lantiq/falcon/sysctrl.c +@@ -9,11 +9,13 @@ + + #include + #include ++#include + #include + + #include + + #include "devices.h" ++#include "../clk.h" + + /* infrastructure control register */ + #define SYS1_INFRAC 0x00bc +@@ -38,6 +40,10 @@ + #define LTQ_SYSCTL_DEACT 0x0028 + /* reboot Register */ + #define LTQ_SYSCTL_RBT 0x002c ++/* CPU0 Clock Control Register */ ++#define LTQ_SYS1_CPU0CC 0x0040 ++/* clock divider bit */ ++#define LTQ_CPU0CC_CPUDIV 0x0001 + + static struct resource ltq_sysctl_res[] = { + MEM_RES("sys1", LTQ_SYS1_BASE_ADDR, LTQ_SYS1_SIZE), +@@ -64,79 +70,67 @@ void __iomem *ltq_ebu_membase; + #define ltq_status_r32(x) ltq_r32(ltq_status_membase + (x)) + + static inline void +-ltq_sysctl_wait(int module, unsigned int mask, ++ltq_sysctl_wait(struct clk *clk, + unsigned int test, unsigned int reg) + { + int err = 1000000; + +- do {} while (--err && ((ltq_reg_r32(module, reg) +- & mask) != test)); ++ do {} while (--err && ((ltq_reg_r32(clk->module, reg) ++ & clk->bits) != test)); + if (!err) +- pr_err("module de/activation failed %d %08X %08X\n", +- module, mask, test); ++ pr_err("module de/activation failed %d %08X %08X %08X\n", ++ clk->module, clk->bits, test, ++ ltq_reg_r32(clk->module, reg) & clk->bits); + } + +-void +-ltq_sysctl_activate(int module, unsigned int mask) ++static int ++ltq_sysctl_activate(struct clk *clk) + { +- if (module > SYSCTL_SYSGPE) +- return; +- +- ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKEN); +- ltq_reg_w32(module, mask, LTQ_SYSCTL_ACT); +- ltq_sysctl_wait(module, mask, mask, LTQ_SYSCTL_ACTS); ++ ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKEN); ++ ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_ACT); ++ ltq_sysctl_wait(clk, clk->bits, LTQ_SYSCTL_ACTS); ++ return 0; + } +-EXPORT_SYMBOL(ltq_sysctl_activate); + +-void +-ltq_sysctl_deactivate(int module, unsigned int mask) ++static void ++ltq_sysctl_deactivate(struct clk *clk) + { +- if (module > SYSCTL_SYSGPE) +- return; +- +- ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKCLR); +- ltq_reg_w32(module, mask, LTQ_SYSCTL_DEACT); +- ltq_sysctl_wait(module, mask, 0, LTQ_SYSCTL_ACTS); ++ ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKCLR); ++ ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_DEACT); ++ ltq_sysctl_wait(clk, 0, LTQ_SYSCTL_ACTS); + } +-EXPORT_SYMBOL(ltq_sysctl_deactivate); + +-void +-ltq_sysctl_clken(int module, unsigned int mask) ++static int ++ltq_sysctl_clken(struct clk *clk) + { +- if (module > SYSCTL_SYSGPE) +- return; +- +- ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKEN); +- ltq_sysctl_wait(module, mask, mask, LTQ_SYSCTL_CLKS); ++ ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKEN); ++ ltq_sysctl_wait(clk, clk->bits, LTQ_SYSCTL_CLKS); ++ return 0; + } +-EXPORT_SYMBOL(ltq_sysctl_clken); + +-void +-ltq_sysctl_clkdis(int module, unsigned int mask) ++static void ++ltq_sysctl_clkdis(struct clk *clk) + { +- if (module > SYSCTL_SYSGPE) +- return; +- +- ltq_reg_w32(module, mask, LTQ_SYSCTL_CLKCLR); +- ltq_sysctl_wait(module, mask, 0, LTQ_SYSCTL_CLKS); ++ ltq_reg_w32(clk->module, clk->bits, LTQ_SYSCTL_CLKCLR); ++ ltq_sysctl_wait(clk, 0, LTQ_SYSCTL_CLKS); + } +-EXPORT_SYMBOL(ltq_sysctl_clkdis); + +-void +-ltq_sysctl_reboot(int module, unsigned int mask) ++static void ++ltq_sysctl_reboot(struct clk *clk) + { + unsigned int act; +- +- if (module > SYSCTL_SYSGPE) +- return; +- +- act = ltq_reg_r32(module, LTQ_SYSCTL_ACT); +- if ((~act & mask) != 0) +- ltq_sysctl_activate(module, ~act & mask); +- ltq_reg_w32(module, act & mask, LTQ_SYSCTL_RBT); +- ltq_sysctl_wait(module, mask, mask, LTQ_SYSCTL_ACTS); ++ unsigned int bits; ++ ++ act = ltq_reg_r32(clk->module, LTQ_SYSCTL_ACT); ++ bits = ~act & clk->bits; ++ if (bits != 0) { ++ ltq_reg_w32(clk->module, bits, LTQ_SYSCTL_CLKEN); ++ ltq_reg_w32(clk->module, bits, LTQ_SYSCTL_ACT); ++ ltq_sysctl_wait(clk, bits, LTQ_SYSCTL_ACTS); ++ } ++ ltq_reg_w32(clk->module, act & clk->bits, LTQ_SYSCTL_RBT); ++ ltq_sysctl_wait(clk, clk->bits, LTQ_SYSCTL_ACTS); + } +-EXPORT_SYMBOL(ltq_sysctl_reboot); + + /* enable the ONU core */ + static void +@@ -167,6 +161,24 @@ ltq_gpe_enable(void) + udelay(1); + } + ++static inline void ++clkdev_add_sys(const char *dev, unsigned int module, ++ unsigned int bits) ++{ ++ struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL); ++ ++ clk->cl.dev_id = dev; ++ clk->cl.con_id = NULL; ++ clk->cl.clk = clk; ++ clk->module = module; ++ clk->activate = ltq_sysctl_activate; ++ clk->deactivate = ltq_sysctl_deactivate; ++ clk->enable = ltq_sysctl_clken; ++ clk->disable = ltq_sysctl_clkdis; ++ clk->reboot = ltq_sysctl_reboot; ++ clkdev_add(&clk->cl); ++} ++ + void __init + ltq_soc_init(void) + { +@@ -180,4 +192,19 @@ ltq_soc_init(void) + ltq_ebu_membase = ltq_remap_resource(<q_ebu_res); + + ltq_gpe_enable(); ++ ++ /* get our 3 static rates for cpu, fpi and io clocks */ ++ if (ltq_sys1_r32(LTQ_SYS1_CPU0CC) & LTQ_CPU0CC_CPUDIV) ++ clkdev_add_static(CLOCK_200M, CLOCK_100M, CLOCK_200M); ++ else ++ clkdev_add_static(CLOCK_400M, CLOCK_100M, CLOCK_200M); ++ ++ /* add our clock domains */ ++ clkdev_add_sys("falcon_gpio.0", SYSCTL_SYSETH, ACTS_PADCTRL0 | ACTS_P0); ++ clkdev_add_sys("falcon_gpio.1", SYSCTL_SYS1, ACTS_PADCTRL1 | ACTS_P1); ++ clkdev_add_sys("falcon_gpio.2", SYSCTL_SYSETH, ACTS_PADCTRL2 | ACTS_P2); ++ clkdev_add_sys("falcon_gpio.3", SYSCTL_SYS1, ACTS_PADCTRL3 | ACTS_P3); ++ clkdev_add_sys("falcon_gpio.4", SYSCTL_SYS1, ACTS_PADCTRL4 | ACTS_P4); ++ clkdev_add_sys("ltq_asc.1", SYSCTL_SYS1, ACTS_ASC1_ACT); ++ clkdev_add_sys("falcon_i2c", SYSCTL_SYS1, ACTS_I2C_ACT); + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0024-MIPS-lantiq-convert-dma-driver-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0024-MIPS-lantiq-convert-dma-driver-to-clkdev-api.patch new file mode 100644 index 0000000000..356924f243 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0024-MIPS-lantiq-convert-dma-driver-to-clkdev-api.patch @@ -0,0 +1,65 @@ +From d5904c1dd985d1e0944dd249927ad19c8522a943 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:21:08 +0100 +Subject: [PATCH 24/70] MIPS: lantiq: convert dma driver to clkdev api + +Update from old pmu_{dis,en}able() to ckldev api. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/Makefile.rej | 11 +++++++++++ + arch/mips/lantiq/xway/dma.c | 6 +++++- + 2 files changed, 16 insertions(+), 1 deletions(-) + create mode 100644 arch/mips/lantiq/xway/Makefile.rej + +diff --git a/arch/mips/lantiq/xway/Makefile.rej b/arch/mips/lantiq/xway/Makefile.rej +new file mode 100644 +index 0000000..c0d5b52 +--- /dev/null ++++ b/arch/mips/lantiq/xway/Makefile.rej +@@ -0,0 +1,11 @@ ++--- arch/mips/lantiq/xway/Makefile +++++ arch/mips/lantiq/xway/Makefile ++@@ -1,7 +1,4 @@ ++-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o ++- ++-obj-$(CONFIG_SOC_XWAY) += prom-xway.o ++-obj-$(CONFIG_SOC_AMAZON_SE) += prom-ase.o +++obj-y := prom.o sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o ++ ++ obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o ++ obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/dma.c b/arch/mips/lantiq/xway/dma.c +index 6cf883b..ce86529 100644 +--- a/arch/mips/lantiq/xway/dma.c ++++ b/arch/mips/lantiq/xway/dma.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -216,6 +217,7 @@ EXPORT_SYMBOL_GPL(ltq_dma_init_port); + int __init + ltq_dma_init(void) + { ++ struct clk *clk; + int i; + + /* remap dma register range */ +@@ -224,7 +226,9 @@ ltq_dma_init(void) + panic("Failed to remap dma memory"); + + /* power up and reset the dma engine */ +- ltq_pmu_enable(PMU_DMA); ++ clk = clk_get_sys("ltq_dma", NULL); ++ WARN_ON(!clk); ++ clk_enable(clk); + ltq_dma_w32_mask(0, DMA_RESET, LTQ_DMA_CTRL); + + /* disable all interrupts */ +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0025-MIPS-lantiq-convert-gpio_stp-driver-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0025-MIPS-lantiq-convert-gpio_stp-driver-to-clkdev-api.patch new file mode 100644 index 0000000000..baef8c55a5 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0025-MIPS-lantiq-convert-gpio_stp-driver-to-clkdev-api.patch @@ -0,0 +1,60 @@ +From b4c30090220ae84e03e35363fc2dc6cf3e4b00ed Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:21:33 +0100 +Subject: [PATCH 25/70] MIPS: lantiq: convert gpio_stp driver to clkdev api + +Update from old pmu_{dis,en}able() to ckldev api. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/gpio_stp.c | 12 +++++++++--- + 1 files changed, 9 insertions(+), 3 deletions(-) + +diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c +index e6b4809..da91c5e 100644 +--- a/arch/mips/lantiq/xway/gpio_stp.c ++++ b/arch/mips/lantiq/xway/gpio_stp.c +@@ -15,6 +15,8 @@ + #include + #include + #include ++#include ++#include + + #include + +@@ -78,8 +80,10 @@ static struct gpio_chip ltq_stp_chip = { + .owner = THIS_MODULE, + }; + +-static int ltq_stp_hw_init(void) ++static int ltq_stp_hw_init(struct device *dev) + { ++ struct clk *clk; ++ + /* sane defaults */ + ltq_stp_w32(0, LTQ_STP_AR); + ltq_stp_w32(0, LTQ_STP_CPU0); +@@ -105,7 +109,9 @@ static int ltq_stp_hw_init(void) + */ + ltq_stp_w32_mask(0, LTQ_STP_ADSL_SRC, LTQ_STP_CON0); + +- ltq_pmu_enable(PMU_LED); ++ clk = clk_get(dev, NULL); ++ WARN_ON(IS_ERR(clk)); ++ clk_enable(clk); + return 0; + } + +@@ -138,7 +144,7 @@ static int __devinit ltq_stp_probe(struct platform_device *pdev) + } + ret = gpiochip_add(<q_stp_chip); + if (!ret) +- ret = ltq_stp_hw_init(); ++ ret = ltq_stp_hw_init(&pdev->dev); + + return ret; + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0026-MIPS-lantiq-convert-falcon-gpio-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0026-MIPS-lantiq-convert-falcon-gpio-to-clkdev-api.patch new file mode 100644 index 0000000000..e0fe2113b8 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0026-MIPS-lantiq-convert-falcon-gpio-to-clkdev-api.patch @@ -0,0 +1,73 @@ +From e5bb79e897a95b688a93087f04848a043782a6a7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:22:03 +0100 +Subject: [PATCH 26/70] MIPS: lantiq: convert falcon gpio to clkdev api + +The falcon gpio clocks used to be enabled when registering the platform device. +Move this code into the driver and use clkdev api. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/falcon/devices.c | 5 ----- + arch/mips/lantiq/falcon/gpio.c | 10 ++++++++++ + 2 files changed, 10 insertions(+), 5 deletions(-) + +diff --git a/arch/mips/lantiq/falcon/devices.c b/arch/mips/lantiq/falcon/devices.c +index 4f47b44..6cd7a88 100644 +--- a/arch/mips/lantiq/falcon/devices.c ++++ b/arch/mips/lantiq/falcon/devices.c +@@ -111,9 +111,6 @@ falcon_register_gpio(void) + falcon_gpio1_res, ARRAY_SIZE(falcon_gpio1_res)); + platform_device_register_simple("falcon_gpio", 2, + falcon_gpio2_res, ARRAY_SIZE(falcon_gpio2_res)); +- ltq_sysctl_activate(SYSCTL_SYS1, ACTS_PADCTRL1 | ACTS_P1); +- ltq_sysctl_activate(SYSCTL_SYSETH, ACTS_PADCTRL0 | +- ACTS_PADCTRL2 | ACTS_P0 | ACTS_P2); + } + + void __init +@@ -123,6 +120,4 @@ falcon_register_gpio_extra(void) + falcon_gpio3_res, ARRAY_SIZE(falcon_gpio3_res)); + platform_device_register_simple("falcon_gpio", 4, + falcon_gpio4_res, ARRAY_SIZE(falcon_gpio4_res)); +- ltq_sysctl_activate(SYSCTL_SYS1, +- ACTS_PADCTRL3 | ACTS_PADCTRL4 | ACTS_P3 | ACTS_P4); + } +diff --git a/arch/mips/lantiq/falcon/gpio.c b/arch/mips/lantiq/falcon/gpio.c +index a44f71b..4147d61 100644 +--- a/arch/mips/lantiq/falcon/gpio.c ++++ b/arch/mips/lantiq/falcon/gpio.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -71,6 +72,7 @@ struct falcon_gpio_port { + void __iomem *port; + unsigned int irq_base; + unsigned int chained_irq; ++ struct clk *clk; + }; + + static struct falcon_gpio_port ltq_gpio_port[MAX_PORTS]; +@@ -332,6 +334,14 @@ falcon_gpio_probe(struct platform_device *pdev) + goto err; + } + ++ gpio_port->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(gpio_port->clk)) { ++ dev_err(&pdev->dev, "Could not get clock\n"); ++ ret = PTR_ERR(gpio_port->clk);; ++ goto err; ++ } ++ clk_enable(gpio_port->clk); ++ + if (irq > 0) { + /* irq_chip support */ + gpio_port->gpio_chip.to_irq = falcon_gpio_to_irq; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0027-SERIAL-MIPS-lantiq-convert-serial-driver-to-clkdev-a.patch b/target/linux/lantiq/patches-3.3/0027-SERIAL-MIPS-lantiq-convert-serial-driver-to-clkdev-a.patch new file mode 100644 index 0000000000..426dfcf583 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0027-SERIAL-MIPS-lantiq-convert-serial-driver-to-clkdev-a.patch @@ -0,0 +1,41 @@ +From ded396701b769f4bd3960c353d40488fa5922a85 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 Feb 2012 20:17:16 +0100 +Subject: [PATCH 27/70] SERIAL: MIPS: lantiq: convert serial driver to clkdev + api + +Reference the FPI clock via its new access function. + +Signed-off-by: John Crispin +Cc: linux-serial@vger.kernel.org +--- + drivers/tty/serial/lantiq.c | 6 +++++- + 1 files changed, 5 insertions(+), 1 deletions(-) + +diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c +index 5d25828..1542ad6 100644 +--- a/drivers/tty/serial/lantiq.c ++++ b/drivers/tty/serial/lantiq.c +@@ -540,6 +540,10 @@ lqasc_request_port(struct uart_port *port) + if (ltq_gpio_request(&pdev->dev, MUXC_SIF_TX_PIN, + 3, 1, "asc1-tx")) + return -EBUSY; ++ ltq_port->clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(ltq_port->clk)) ++ return PTR_ERR(ltq_port->clk); ++ clk_enable(ltq_port->clk); + } + return 0; + } +@@ -698,7 +702,7 @@ lqasc_probe(struct platform_device *pdev) + if (lqasc_port[pdev->id] != NULL) + return -EBUSY; + +- clk = clk_get(&pdev->dev, "fpi"); ++ clk = clk_get_fpi(); + if (IS_ERR(clk)) { + pr_err("failed to get fpi clk\n"); + return -ENOENT; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0028-MIPS-lantiq-convert-falcon-debug-uart-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0028-MIPS-lantiq-convert-falcon-debug-uart-to-clkdev-api.patch new file mode 100644 index 0000000000..2fa6991717 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0028-MIPS-lantiq-convert-falcon-debug-uart-to-clkdev-api.patch @@ -0,0 +1,73 @@ +From 7ab075944587e4fe7e2688b1df85452e3d86edb5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 Feb 2012 14:25:21 +0100 +Subject: [PATCH 28/70] MIPS: lantiq: convert falcon debug uart to clkdev api + +On Falcon SoCs we have a secondary serial port that can be used to help +debug the voice core. For the port to work several clocking bits need to +be activated. We convert the code to clkdev api. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/falcon/prom.c | 4 +--- + drivers/tty/serial/lantiq.c | 7 ++++--- + 2 files changed, 5 insertions(+), 6 deletions(-) + +diff --git a/arch/mips/lantiq/falcon/prom.c b/arch/mips/lantiq/falcon/prom.c +index f98b389..2a4eea17 100644 +--- a/arch/mips/lantiq/falcon/prom.c ++++ b/arch/mips/lantiq/falcon/prom.c +@@ -43,10 +43,8 @@ ltq_soc_setup(void) + ltq_register_asc(0); + ltq_register_wdt(); + falcon_register_gpio(); +- if (register_asc1) { ++ if (register_asc1) + ltq_register_asc(1); +- ltq_sysctl_activate(SYSCTL_SYS1, ACTS_ASC1_ACT); +- } + } + + void __init +diff --git a/drivers/tty/serial/lantiq.c b/drivers/tty/serial/lantiq.c +index 1542ad6..82956de 100644 +--- a/drivers/tty/serial/lantiq.c ++++ b/drivers/tty/serial/lantiq.c +@@ -117,6 +117,7 @@ static DEFINE_SPINLOCK(ltq_asc_lock); + + struct ltq_uart_port { + struct uart_port port; ++ struct clk *fpiclk; + struct clk *clk; + unsigned int tx_irq; + unsigned int rx_irq; +@@ -319,7 +320,7 @@ lqasc_startup(struct uart_port *port) + struct ltq_uart_port *ltq_port = to_ltq_uart_port(port); + int retval; + +- port->uartclk = clk_get_rate(ltq_port->clk); ++ port->uartclk = clk_get_rate(ltq_port->fpiclk); + + ltq_w32_mask(ASCCLC_DISS | ASCCLC_RMCMASK, (1 << ASCCLC_RMCOFFSET), + port->membase + LTQ_ASC_CLC); +@@ -646,7 +647,7 @@ lqasc_console_setup(struct console *co, char *options) + + port = <q_port->port; + +- port->uartclk = clk_get_rate(ltq_port->clk); ++ port->uartclk = clk_get_rate(ltq_port->fpiclk); + + if (options) + uart_parse_options(options, &baud, &parity, &bits, &flow); +@@ -731,7 +732,7 @@ lqasc_probe(struct platform_device *pdev) + port->irq = tx_irq; /* unused, just to be backward-compatibe */ + port->mapbase = mmres->start; + +- ltq_port->clk = clk; ++ ltq_port->fpiclk = clk; + + ltq_port->tx_irq = tx_irq; + ltq_port->rx_irq = rx_irq; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0029-NET-MIPS-lantiq-convert-etop-driver-to-clkdev-api.patch b/target/linux/lantiq/patches-3.3/0029-NET-MIPS-lantiq-convert-etop-driver-to-clkdev-api.patch new file mode 100644 index 0000000000..c48fb9bc79 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0029-NET-MIPS-lantiq-convert-etop-driver-to-clkdev-api.patch @@ -0,0 +1,123 @@ +From 118d9f0abd69278cfdd7c5bde4743c0cab9b7618 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:23:00 +0100 +Subject: [PATCH 29/70] NET: MIPS: lantiq: convert etop driver to clkdev api + +Update from old pmu_{dis,en}able() to ckldev api. + +Signed-off-by: John Crispin +Cc: netdev@vger.kernel.org +--- + drivers/net/ethernet/lantiq_etop.c | 49 ++++++++++++++++++++++++++++++----- + 1 files changed, 42 insertions(+), 7 deletions(-) + +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index ad0fa54..8fbb069 100644 +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -36,6 +36,7 @@ + #include + #include + #include ++#include + + #include + +@@ -148,6 +149,11 @@ struct ltq_etop_priv { + int tx_free[MAX_DMA_CHAN >> 1]; + + spinlock_t lock; ++ ++ struct clk *clk_ppe; ++ struct clk *clk_switch; ++ struct clk *clk_ephy; ++ struct clk *clk_ephycgu; + }; + + static int ltq_etop_mdio_wr(struct mii_bus *bus, int phy_addr, +@@ -281,16 +287,27 @@ ltq_etop_hw_exit(struct net_device *dev) + struct ltq_etop_priv *priv = netdev_priv(dev); + int i; + +- ltq_pmu_disable(PMU_PPE); ++ clk_disable(priv->clk_ppe); ++ ++ if (ltq_has_gbit()) ++ clk_disable(priv->clk_switch); ++ ++ if (ltq_is_ase()) { ++ clk_disable(priv->clk_ephy); ++ clk_disable(priv->clk_ephycgu); ++ } ++ + for (i = 0; i < MAX_DMA_CHAN; i++) + if (IS_TX(i) || IS_RX(i)) + ltq_etop_free_channel(dev, &priv->ch[i]); + } + + static void +-ltq_etop_gbit_init(void) ++ltq_etop_gbit_init(struct net_device *dev) + { +- ltq_pmu_enable(PMU_SWITCH); ++ struct ltq_etop_priv *priv = netdev_priv(dev); ++ ++ clk_enable(priv->clk_switch); + + ltq_gbit_w32_mask(0, GCTL0_SE, LTQ_GBIT_GCTL0); + /** Disable MDIO auto polling mode */ +@@ -313,10 +330,10 @@ ltq_etop_hw_init(struct net_device *dev) + int err = 0; + int i; + +- ltq_pmu_enable(PMU_PPE); ++ clk_enable(priv->clk_ppe); + + if (ltq_has_gbit()) { +- ltq_etop_gbit_init(); ++ ltq_etop_gbit_init(dev); + /* force the etops link to the gbit to MII */ + mii_mode = PHY_INTERFACE_MODE_MII; + } +@@ -334,11 +351,11 @@ ltq_etop_hw_init(struct net_device *dev) + + default: + if (ltq_is_ase()) { +- ltq_pmu_enable(PMU_EPHY); ++ clk_enable(priv->clk_ephy); + /* disable external MII */ + ltq_etop_w32_mask(0, ETOP_CFG_MII0, LTQ_ETOP_CFG); + /* enable clock for internal PHY */ +- ltq_cgu_enable(CGU_EPHY); ++ clk_enable(priv->clk_ephycgu); + /* we need to write this magic to the internal phy to + make it work */ + ltq_etop_mdio_wr(NULL, 0x8, 0x12, 0xC020); +@@ -883,6 +900,24 @@ ltq_etop_probe(struct platform_device *pdev) + priv->pdev = pdev; + priv->pldata = dev_get_platdata(&pdev->dev); + priv->netdev = dev; ++ ++ priv->clk_ppe = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk_ppe)) ++ return PTR_ERR(priv->clk_ppe); ++ if (ltq_has_gbit()) { ++ priv->clk_switch = clk_get(&pdev->dev, "switch"); ++ if (IS_ERR(priv->clk_switch)) ++ return PTR_ERR(priv->clk_switch); ++ } ++ if (ltq_is_ase()) { ++ priv->clk_ephy = clk_get(&pdev->dev, "ephy"); ++ if (IS_ERR(priv->clk_ephy)) ++ return PTR_ERR(priv->clk_ephy); ++ priv->clk_ephycgu = clk_get(&pdev->dev, "ephycgu"); ++ if (IS_ERR(priv->clk_ephycgu)) ++ return PTR_ERR(priv->clk_ephycgu); ++ } ++ + spin_lock_init(&priv->lock); + + for (i = 0; i < MAX_DMA_CHAN; i++) { +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0030-WDT-MIPS-lantiq-convert-watchdog-driver-to-clkdev-ap.patch b/target/linux/lantiq/patches-3.3/0030-WDT-MIPS-lantiq-convert-watchdog-driver-to-clkdev-ap.patch new file mode 100644 index 0000000000..c18933be8e --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0030-WDT-MIPS-lantiq-convert-watchdog-driver-to-clkdev-ap.patch @@ -0,0 +1,30 @@ +From 7971cef3f4036da74066aa4ca9b78fd1c9573a6c Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 16 Feb 2012 20:17:50 +0100 +Subject: [PATCH 30/70] WDT: MIPS: lantiq: convert watchdog driver to clkdev + api + +Refrence the IO region clock via its new access function. + +Signed-off-by: John Crispin +Cc: linux-watchdog@vger.kernel.org +--- + drivers/watchdog/lantiq_wdt.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c +index 9c8b10c..fa4866b 100644 +--- a/drivers/watchdog/lantiq_wdt.c ++++ b/drivers/watchdog/lantiq_wdt.c +@@ -206,7 +206,7 @@ ltq_wdt_probe(struct platform_device *pdev) + } + + /* we do not need to enable the clock as it is always running */ +- clk = clk_get(&pdev->dev, "io"); ++ clk = clk_get_io(); + WARN_ON(!clk); + ltq_io_region_clk_rate = clk_get_rate(clk); + clk_put(clk); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0031-MIPS-lantiq-unify-xway-prom-code.patch b/target/linux/lantiq/patches-3.3/0031-MIPS-lantiq-unify-xway-prom-code.patch new file mode 100644 index 0000000000..1022450b67 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0031-MIPS-lantiq-unify-xway-prom-code.patch @@ -0,0 +1,262 @@ +From 707d76cc9dc4ddfcea280b91df83bd726407518a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 11:44:55 +0100 +Subject: [PATCH 31/70] MIPS: lantiq: unify xway prom code + +The xway prom-ase.c and prom-xway.c files are redundant. Unify the 2 files. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/xway/Makefile | 5 +-- + arch/mips/lantiq/xway/Makefile.rej | 11 ----- + arch/mips/lantiq/xway/prom-ase.c | 48 ---------------------- + arch/mips/lantiq/xway/prom-xway.c | 64 ----------------------------- + arch/mips/lantiq/xway/prom.c | 79 ++++++++++++++++++++++++++++++++++++ + 5 files changed, 80 insertions(+), 127 deletions(-) + delete mode 100644 arch/mips/lantiq/xway/Makefile.rej + delete mode 100644 arch/mips/lantiq/xway/prom-ase.c + delete mode 100644 arch/mips/lantiq/xway/prom-xway.c + create mode 100644 arch/mips/lantiq/xway/prom.c + +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 4dcb96f..9d1a0a2 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,7 +1,4 @@ +-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o +- +-obj-$(CONFIG_SOC_XWAY) += prom-xway.o +-obj-$(CONFIG_SOC_AMAZON_SE) += prom-ase.o ++obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o + + obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o + obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/Makefile.rej b/arch/mips/lantiq/xway/Makefile.rej +deleted file mode 100644 +index c0d5b52..0000000 +--- a/arch/mips/lantiq/xway/Makefile.rej ++++ /dev/null +@@ -1,11 +0,0 @@ +---- arch/mips/lantiq/xway/Makefile +-+++ arch/mips/lantiq/xway/Makefile +-@@ -1,7 +1,4 @@ +--obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o +-- +--obj-$(CONFIG_SOC_XWAY) += prom-xway.o +--obj-$(CONFIG_SOC_AMAZON_SE) += prom-ase.o +-+obj-y := prom.o sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o +- +- obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o +- obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/prom-ase.c b/arch/mips/lantiq/xway/prom-ase.c +deleted file mode 100644 +index 3f86a3b..0000000 +--- a/arch/mips/lantiq/xway/prom-ase.c ++++ /dev/null +@@ -1,48 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2010 John Crispin +- */ +- +-#include +-#include +-#include +-#include +- +-#include +- +-#include "devices.h" +-#include "../prom.h" +- +-#define SOC_AMAZON_SE "Amazon_SE" +- +-#define PART_SHIFT 12 +-#define PART_MASK 0x0FFFFFFF +-#define REV_SHIFT 28 +-#define REV_MASK 0xF0000000 +- +-void __init ltq_soc_detect(struct ltq_soc_info *i) +-{ +- i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT; +- i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT; +- sprintf(i->rev_type, "1.%d", i->rev); +- switch (i->partnum) { +- case SOC_ID_AMAZON_SE: +- i->name = SOC_AMAZON_SE; +- i->type = SOC_TYPE_AMAZON_SE; +- break; +- +- default: +- unreachable(); +- break; +- } +-} +- +-void __init ltq_soc_setup(void) +-{ +- ltq_register_ase_asc(); +- ltq_register_gpio(); +- ltq_register_wdt(); +-} +diff --git a/arch/mips/lantiq/xway/prom-xway.c b/arch/mips/lantiq/xway/prom-xway.c +deleted file mode 100644 +index d823a92..0000000 +--- a/arch/mips/lantiq/xway/prom-xway.c ++++ /dev/null +@@ -1,64 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2010 John Crispin +- */ +- +-#include +-#include +-#include +-#include +- +-#include +- +-#include "devices.h" +-#include "../prom.h" +- +-#define SOC_DANUBE "Danube" +-#define SOC_TWINPASS "Twinpass" +-#define SOC_AR9 "AR9" +- +-#define PART_SHIFT 12 +-#define PART_MASK 0x0FFFFFFF +-#define REV_SHIFT 28 +-#define REV_MASK 0xF0000000 +- +-void __init ltq_soc_detect(struct ltq_soc_info *i) +-{ +- i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT; +- i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT; +- sprintf(i->rev_type, "1.%d", i->rev); +- switch (i->partnum) { +- case SOC_ID_DANUBE1: +- case SOC_ID_DANUBE2: +- i->name = SOC_DANUBE; +- i->type = SOC_TYPE_DANUBE; +- break; +- +- case SOC_ID_TWINPASS: +- i->name = SOC_TWINPASS; +- i->type = SOC_TYPE_DANUBE; +- break; +- +- case SOC_ID_ARX188: +- case SOC_ID_ARX168: +- case SOC_ID_ARX182: +- i->name = SOC_AR9; +- i->type = SOC_TYPE_AR9; +- break; +- +- default: +- unreachable(); +- break; +- } +-} +- +-void __init ltq_soc_setup(void) +-{ +- ltq_register_asc(0); +- ltq_register_asc(1); +- ltq_register_gpio(); +- ltq_register_wdt(); +-} +diff --git a/arch/mips/lantiq/xway/prom.c b/arch/mips/lantiq/xway/prom.c +new file mode 100644 +index 0000000..0929acb +--- /dev/null ++++ b/arch/mips/lantiq/xway/prom.c +@@ -0,0 +1,79 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2010 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "../prom.h" ++#include "devices.h" ++ ++#define SOC_DANUBE "Danube" ++#define SOC_TWINPASS "Twinpass" ++#define SOC_AR9 "AR9" ++#define SOC_VR9 "VR9" ++ ++#define PART_SHIFT 12 ++#define PART_MASK 0x0FFFFFFF ++#define REV_SHIFT 28 ++#define REV_MASK 0xF0000000 ++ ++#define SOC_AMAZON_SE "Amazon_SE" ++ ++void __init ltq_soc_detect(struct ltq_soc_info *i) ++{ ++ i->partnum = (ltq_r32(LTQ_MPS_CHIPID) & PART_MASK) >> PART_SHIFT; ++ i->rev = (ltq_r32(LTQ_MPS_CHIPID) & REV_MASK) >> REV_SHIFT; ++ sprintf(i->rev_type, "1.%d", i->rev); ++ switch (i->partnum) { ++ case SOC_ID_DANUBE1: ++ case SOC_ID_DANUBE2: ++ i->name = SOC_DANUBE; ++ i->type = SOC_TYPE_DANUBE; ++ break; ++ ++ case SOC_ID_TWINPASS: ++ i->name = SOC_TWINPASS; ++ i->type = SOC_TYPE_DANUBE; ++ break; ++ ++ case SOC_ID_ARX188: ++ case SOC_ID_ARX168: ++ case SOC_ID_ARX182: ++ i->name = SOC_AR9; ++ i->type = SOC_TYPE_AR9; ++ break; ++ ++ case SOC_ID_AMAZON_SE: ++ i->name = SOC_AMAZON_SE; ++ i->type = SOC_TYPE_AMAZON_SE; ++#ifdef CONFIG_PCI ++ panic("ase is only supported for non pci kernels"); ++#endif ++ break; ++ ++ default: ++ unreachable(); ++ break; ++ } ++} ++ ++void __init ltq_soc_setup(void) ++{ ++ if (ltq_is_ase()) { ++ ltq_register_ase_asc(); ++ } else { ++ ltq_register_asc(0); ++ ltq_register_asc(1); ++ } ++ ltq_register_gpio(); ++ ltq_register_wdt(); ++} +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0032-MIPS-lantiq-add-vr9-support.patch b/target/linux/lantiq/patches-3.3/0032-MIPS-lantiq-add-vr9-support.patch new file mode 100644 index 0000000000..ca803b9050 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0032-MIPS-lantiq-add-vr9-support.patch @@ -0,0 +1,172 @@ +From d1711082beab74da07c2fd330c462ef407cb5579 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 Feb 2012 09:48:11 +0100 +Subject: [PATCH 32/70] MIPS: lantiq: add vr9 support + +VR9 is a VDSL SoC made by Lantiq. It is very similar to the AR9. +This patch adds the clkdev init code and SoC detection for the VR9. + +Signed-off-by: John Crispin +Signed-off-by: Daniel Schwierzeck +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 3 + + arch/mips/lantiq/xway/clk.c | 83 ++++++++++++++++++++ + arch/mips/lantiq/xway/prom.c | 6 ++ + arch/mips/lantiq/xway/sysctrl.c | 12 +++- + 4 files changed, 103 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index e9d2dd4..5d11eb7 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -21,6 +21,9 @@ + #define SOC_ID_ARX188 0x16C + #define SOC_ID_ARX168 0x16D + #define SOC_ID_ARX182 0x16F ++#define SOC_ID_VRX288 0x1C0 /* VRX288 v1.1 */ ++#define SOC_ID_VRX268 0x1C2 /* VRX268 v1.1 */ ++#define SOC_ID_GRX288 0x1C9 /* GRX288 v1.1 */ + + /* SoC Types */ + #define SOC_TYPE_DANUBE 0x01 +diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c +index f3b50fc..3635c9f 100644 +--- a/arch/mips/lantiq/xway/clk.c ++++ b/arch/mips/lantiq/xway/clk.c +@@ -225,3 +225,86 @@ unsigned long ltq_danube_fpi_hz(void) + return ddr_clock >> 1; + return ddr_clock; + } ++ ++unsigned long ltq_vr9_cpu_hz(void) ++{ ++ unsigned int cpu_sel; ++ unsigned long clk; ++ ++ cpu_sel = (ltq_cgu_r32(LTQ_CGU_SYS_VR9) >> 4) & 0xf; ++ ++ switch (cpu_sel) { ++ case 0: ++ clk = CLOCK_600M; ++ break; ++ case 1: ++ clk = CLOCK_500M; ++ break; ++ case 2: ++ clk = CLOCK_393M; ++ break; ++ case 3: ++ clk = CLOCK_333M; ++ break; ++ case 5: ++ case 6: ++ clk = CLOCK_196_608M; ++ break; ++ case 7: ++ clk = CLOCK_167M; ++ break; ++ case 4: ++ case 8: ++ case 9: ++ clk = CLOCK_125M; ++ break; ++ default: ++ clk = 0; ++ break; ++ } ++ ++ return clk; ++} ++ ++unsigned long ltq_vr9_fpi_hz(void) ++{ ++ unsigned int ocp_sel, cpu_clk; ++ unsigned long clk; ++ ++ cpu_clk = ltq_vr9_cpu_hz(); ++ ocp_sel = ltq_cgu_r32(LTQ_CGU_SYS_VR9) & 0x3; ++ ++ switch (ocp_sel) { ++ case 0: ++ /* OCP ratio 1 */ ++ clk = cpu_clk; ++ break; ++ case 2: ++ /* OCP ratio 2 */ ++ clk = cpu_clk / 2; ++ break; ++ case 3: ++ /* OCP ratio 2.5 */ ++ clk = (cpu_clk * 2) / 5; ++ break; ++ case 4: ++ /* OCP ratio 3 */ ++ clk = cpu_clk / 3; ++ break; ++ default: ++ clk = 0; ++ break; ++ } ++ ++ return clk; ++} ++ ++unsigned long ltq_vr9_io_region_clock(void) ++{ ++ return ltq_vr9_fpi_hz(); ++} ++ ++unsigned long ltq_vr9_fpi_bus_clock(int fpi) ++{ ++ return ltq_vr9_fpi_hz(); ++} +diff --git a/arch/mips/lantiq/xway/prom.c b/arch/mips/lantiq/xway/prom.c +index 0929acb..b6f56b7 100644 +--- a/arch/mips/lantiq/xway/prom.c ++++ b/arch/mips/lantiq/xway/prom.c +@@ -60,6 +60,12 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + #endif + break; + ++ case SOC_ID_VRX268: ++ case SOC_ID_VRX288: ++ i->name = SOC_VR9; ++ i->type = SOC_TYPE_VR9; ++ break; ++ + default: + unreachable(); + break; +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index c5782b5..38f02f9 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -147,7 +147,8 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_dma", NULL, 0, PMU_DMA); + clkdev_add_pmu("ltq_stp", NULL, 0, PMU_STP); + clkdev_add_pmu("ltq_spi", NULL, 0, PMU_SPI); +- clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE); ++ if (!ltq_is_vr9()) ++ clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE); + if (ltq_is_ase()) { + if (ltq_cgu_r32(LTQ_CGU_SYS) & (1 << 5)) + clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M); +@@ -155,6 +156,15 @@ void __init ltq_soc_init(void) + clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M); + clkdev_add_cgu("ltq_etop", "ephycgu", CGU_EPHY), + clkdev_add_pmu("ltq_etop", "ephy", 0, PMU_EPHY); ++ } else if (ltq_is_vr9()) { ++ clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(), ++ ltq_vr9_io_region_clock()); ++ clkdev_add_pmu("ltq_pcie", "phy", 1, PMU1_PCIE_PHY); ++ clkdev_add_pmu("ltq_pcie", "bus", 0, PMU_PCIE_CLK); ++ clkdev_add_pmu("ltq_pcie", "msi", 1, PMU1_PCIE_MSI); ++ clkdev_add_pmu("ltq_pcie", "pdi", 1, PMU1_PCIE_PDI); ++ clkdev_add_pmu("ltq_pcie", "ctl", 1, PMU1_PCIE_CTL); ++ clkdev_add_pmu("ltq_pcie", "ahb", 0, PMU_AHBM | PMU_AHBS); + } else { + clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), + ltq_danube_io_region_clock()); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0033-MIPS-lantiq-add-ipi-handlers-to-make-vsmp-work.patch b/target/linux/lantiq/patches-3.3/0033-MIPS-lantiq-add-ipi-handlers-to-make-vsmp-work.patch new file mode 100644 index 0000000000..44d3cab80f --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0033-MIPS-lantiq-add-ipi-handlers-to-make-vsmp-work.patch @@ -0,0 +1,124 @@ +From ec3b7d909fcc78e12625a4dd7ca27f63dddb3fa3 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 21 Feb 2012 21:09:01 +0100 +Subject: [PATCH 33/70] MIPS: lantiq: add ipi handlers to make vsmp work + +Add IPI handlers to the interrupt code. This patch makes MIPS_MT_SMP work +on lantiq SoCs. + +Signed-off-by: John Crispin +--- + arch/mips/lantiq/irq.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ + arch/mips/lantiq/prom.c | 5 ++++ + 2 files changed, 66 insertions(+), 0 deletions(-) + +diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c +index ada3874..f17caec 100644 +--- a/arch/mips/lantiq/irq.c ++++ b/arch/mips/lantiq/irq.c +@@ -9,6 +9,7 @@ + + #include + #include ++#include + + #include + #include +@@ -54,6 +55,14 @@ + #define ltq_eiu_w32(x, y) ltq_w32((x), ltq_eiu_membase + (y)) + #define ltq_eiu_r32(x) ltq_r32(ltq_eiu_membase + (x)) + ++/* our 2 ipi interrupts for VSMP */ ++#define MIPS_CPU_IPI_RESCHED_IRQ 0 ++#define MIPS_CPU_IPI_CALL_IRQ 1 ++ ++#if defined(CONFIG_MIPS_MT_SMP) || defined(CONFIG_MIPS_MT_SMTC) ++int gic_present; ++#endif ++ + static unsigned short ltq_eiu_irq[MAX_EIU] = { + LTQ_EIU_IR0, + LTQ_EIU_IR1, +@@ -219,6 +228,47 @@ static void ltq_hw5_irqdispatch(void) + do_IRQ(MIPS_CPU_TIMER_IRQ); + } + ++#ifdef CONFIG_MIPS_MT_SMP ++void __init arch_init_ipiirq(int irq, struct irqaction *action) ++{ ++ setup_irq(irq, action); ++ irq_set_handler(irq, handle_percpu_irq); ++} ++ ++static void ltq_sw0_irqdispatch(void) ++{ ++ do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ); ++} ++ ++static void ltq_sw1_irqdispatch(void) ++{ ++ do_IRQ(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ); ++} ++static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id) ++{ ++ scheduler_ipi(); ++ return IRQ_HANDLED; ++} ++ ++static irqreturn_t ipi_call_interrupt(int irq, void *dev_id) ++{ ++ smp_call_function_interrupt(); ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction irq_resched = { ++ .handler = ipi_resched_interrupt, ++ .flags = IRQF_PERCPU, ++ .name = "IPI_resched" ++}; ++ ++static struct irqaction irq_call = { ++ .handler = ipi_call_interrupt, ++ .flags = IRQF_PERCPU, ++ .name = "IPI_call" ++}; ++#endif ++ + asmlinkage void plat_irq_dispatch(void) + { + unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM; +@@ -313,6 +363,17 @@ void __init arch_init_irq(void) + irq_set_chip_and_handler(i, <q_irq_type, + handle_level_irq); + ++#if defined(CONFIG_MIPS_MT_SMP) ++ if (cpu_has_vint) { ++ pr_info("Setting up IPI vectored interrupts\n"); ++ set_vi_handler(MIPS_CPU_IPI_RESCHED_IRQ, ltq_sw0_irqdispatch); ++ set_vi_handler(MIPS_CPU_IPI_CALL_IRQ, ltq_sw1_irqdispatch); ++ } ++ arch_init_ipiirq(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_RESCHED_IRQ, ++ &irq_resched); ++ arch_init_ipiirq(MIPS_CPU_IRQ_BASE + MIPS_CPU_IPI_CALL_IRQ, &irq_call); ++#endif ++ + #if !defined(CONFIG_MIPS_MT_SMP) && !defined(CONFIG_MIPS_MT_SMTC) + set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | + IE_IRQ3 | IE_IRQ4 | IE_IRQ5); +diff --git a/arch/mips/lantiq/prom.c b/arch/mips/lantiq/prom.c +index 971554b..00ad59c 100644 +--- a/arch/mips/lantiq/prom.c ++++ b/arch/mips/lantiq/prom.c +@@ -108,4 +108,9 @@ void __init prom_init(void) + soc_info.sys_type[LTQ_SYS_TYPE_LEN - 1] = '\0'; + pr_info("SoC: %s\n", soc_info.sys_type); + prom_init_cmdline(); ++ ++#if defined(CONFIG_MIPS_MT_SMP) ++ if (register_vsmp_smp_ops()) ++ panic("failed to register_vsmp_smp_ops()"); ++#endif + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0034-MIPS-lantiq-add-additional-soc-ids.patch b/target/linux/lantiq/patches-3.3/0034-MIPS-lantiq-add-additional-soc-ids.patch new file mode 100644 index 0000000000..cfbf080546 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0034-MIPS-lantiq-add-additional-soc-ids.patch @@ -0,0 +1,156 @@ +From c02c17b2d2bd9663ded7e9f59d6ef24fba239b33 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 12 Mar 2012 15:23:39 +0100 +Subject: [PATCH 34/70] MIPS: lantiq: add additional soc ids + +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 38 +++++++++++++++---- + arch/mips/lantiq/xway/prom.c | 35 ++++++++++++++++-- + 2 files changed, 61 insertions(+), 12 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 5d11eb7..3f22acb 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -17,20 +17,32 @@ + #define SOC_ID_DANUBE1 0x129 + #define SOC_ID_DANUBE2 0x12B + #define SOC_ID_TWINPASS 0x12D +-#define SOC_ID_AMAZON_SE 0x152 ++#define SOC_ID_AMAZON_SE_1 0x152 /* 50601 */ ++#define SOC_ID_AMAZON_SE_2 0x153 /* 50600 */ + #define SOC_ID_ARX188 0x16C +-#define SOC_ID_ARX168 0x16D ++#define SOC_ID_ARX168_1 0x16D ++#define SOC_ID_ARX168_2 0x16E + #define SOC_ID_ARX182 0x16F +-#define SOC_ID_VRX288 0x1C0 /* VRX288 v1.1 */ +-#define SOC_ID_VRX268 0x1C2 /* VRX268 v1.1 */ +-#define SOC_ID_GRX288 0x1C9 /* GRX288 v1.1 */ ++#define SOC_ID_GRX188 0x170 ++#define SOC_ID_GRX168 0x171 ++ ++#define SOC_ID_VRX288 0x1C0 /* v1.1 */ ++#define SOC_ID_VRX282 0x1C1 /* v1.1 */ ++#define SOC_ID_VRX268 0x1C2 /* v1.1 */ ++#define SOC_ID_GRX268 0x1C8 /* v1.1 */ ++#define SOC_ID_GRX288 0x1C9 /* v1.1 */ ++#define SOC_ID_VRX288_2 0x00B /* v1.2 */ ++#define SOC_ID_VRX268_2 0x00C /* v1.2 */ ++#define SOC_ID_GRX288_2 0x00D /* v1.2 */ ++#define SOC_ID_GRX282_2 0x00E /* v1.2 */ + + /* SoC Types */ + #define SOC_TYPE_DANUBE 0x01 + #define SOC_TYPE_TWINPASS 0x02 + #define SOC_TYPE_AR9 0x03 +-#define SOC_TYPE_VR9 0x04 +-#define SOC_TYPE_AMAZON_SE 0x05 ++#define SOC_TYPE_VR9_1 0x04 /* v1.1 */ ++#define SOC_TYPE_VR9_2 0x05 /* v1.2 */ ++#define SOC_TYPE_AMAZON_SE 0x06 + + /* ASC0/1 - serial port */ + #define LTQ_ASC0_BASE_ADDR 0x1E100400 +@@ -149,9 +161,19 @@ static inline int ltq_is_ar9(void) + return (ltq_get_soc_type() == SOC_TYPE_AR9); + } + ++static inline int ltq_is_vr9_1(void) ++{ ++ return (ltq_get_soc_type() == SOC_TYPE_VR9_1); ++} ++ ++static inline int ltq_is_vr9_2(void) ++{ ++ return (ltq_get_soc_type() == SOC_TYPE_VR9_2); ++} ++ + static inline int ltq_is_vr9(void) + { +- return (ltq_get_soc_type() == SOC_TYPE_VR9); ++ return (ltq_is_vr9_1() || ltq_is_vr9_2()); + } + + static inline int ltq_is_falcon(void) +diff --git a/arch/mips/lantiq/xway/prom.c b/arch/mips/lantiq/xway/prom.c +index b6f56b7..e3dcbbd 100644 +--- a/arch/mips/lantiq/xway/prom.c ++++ b/arch/mips/lantiq/xway/prom.c +@@ -18,7 +18,9 @@ + + #define SOC_DANUBE "Danube" + #define SOC_TWINPASS "Twinpass" ++#define SOC_AMAZON_SE "Amazon_SE" + #define SOC_AR9 "AR9" ++#define SOC_GR9 "GR9" + #define SOC_VR9 "VR9" + + #define PART_SHIFT 12 +@@ -26,7 +28,6 @@ + #define REV_SHIFT 28 + #define REV_MASK 0xF0000000 + +-#define SOC_AMAZON_SE "Amazon_SE" + + void __init ltq_soc_detect(struct ltq_soc_info *i) + { +@@ -46,13 +47,21 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + break; + + case SOC_ID_ARX188: +- case SOC_ID_ARX168: ++ case SOC_ID_ARX168_1: ++ case SOC_ID_ARX168_2: + case SOC_ID_ARX182: + i->name = SOC_AR9; + i->type = SOC_TYPE_AR9; + break; + +- case SOC_ID_AMAZON_SE: ++ case SOC_ID_GRX188: ++ case SOC_ID_GRX168: ++ i->name = SOC_GR9; ++ i->type = SOC_TYPE_AR9; ++ break; ++ ++ case SOC_ID_AMAZON_SE_1: ++ case SOC_ID_AMAZON_SE_2: + i->name = SOC_AMAZON_SE; + i->type = SOC_TYPE_AMAZON_SE; + #ifdef CONFIG_PCI +@@ -60,12 +69,30 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + #endif + break; + ++ case SOC_ID_VRX282: + case SOC_ID_VRX268: + case SOC_ID_VRX288: + i->name = SOC_VR9; +- i->type = SOC_TYPE_VR9; ++ i->type = SOC_TYPE_VR9_1; + break; + ++ case SOC_ID_GRX268: ++ case SOC_ID_GRX288: ++ i->name = SOC_GR9; ++ i->type = SOC_TYPE_VR9_1; ++ break; ++ ++ case SOC_ID_VRX268_2: ++ case SOC_ID_VRX288_2: ++ i->name = SOC_VR9; ++ i->type = SOC_TYPE_VR9_2; ++ break; ++ ++ case SOC_ID_GRX282_2: ++ case SOC_ID_GRX288_2: ++ i->name = SOC_GR9; ++ i->type = SOC_TYPE_VR9_2; ++ + default: + unreachable(); + break; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0035-SPI-MIPS-lantiq-add-FALC-ON-spi-driver.patch b/target/linux/lantiq/patches-3.3/0035-SPI-MIPS-lantiq-add-FALC-ON-spi-driver.patch new file mode 100644 index 0000000000..2b5e694528 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0035-SPI-MIPS-lantiq-add-FALC-ON-spi-driver.patch @@ -0,0 +1,633 @@ +From efa434fed99cee45778d824b34ccabb473a978ec Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 27 Aug 2011 18:12:26 +0200 +Subject: [PATCH 35/70] SPI: MIPS: lantiq: add FALC-ON spi driver + +The external bus unit (EBU) found on the FALC-ON SoC has spi emulation that is +designed for serial flash access. This driver has only been tested with m25p80 +type chips. The hardware has no support for other types of spi peripherals. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +Cc: spi-devel-general@lists.sourceforge.net +--- + arch/mips/lantiq/falcon/devices.c | 13 + + arch/mips/lantiq/falcon/devices.h | 4 + + arch/mips/lantiq/falcon/mach-easy98000.c | 27 ++ + drivers/spi/Kconfig | 4 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-falcon.c | 483 ++++++++++++++++++++++++++++++ + 6 files changed, 532 insertions(+), 0 deletions(-) + create mode 100644 drivers/spi/spi-falcon.c + +diff --git a/arch/mips/lantiq/falcon/devices.c b/arch/mips/lantiq/falcon/devices.c +index 6cd7a88..92ec571 100644 +--- a/arch/mips/lantiq/falcon/devices.c ++++ b/arch/mips/lantiq/falcon/devices.c +@@ -121,3 +121,16 @@ falcon_register_gpio_extra(void) + platform_device_register_simple("falcon_gpio", 4, + falcon_gpio4_res, ARRAY_SIZE(falcon_gpio4_res)); + } ++ ++/* spi flash */ ++static struct platform_device ltq_spi = { ++ .name = "falcon_spi", ++ .num_resources = 0, ++}; ++ ++void __init ++falcon_register_spi_flash(struct spi_board_info *data) ++{ ++ spi_register_board_info(data, 1); ++ platform_device_register(<q_spi); ++} +diff --git a/arch/mips/lantiq/falcon/devices.h b/arch/mips/lantiq/falcon/devices.h +index 18be8b6..5e6f720 100644 +--- a/arch/mips/lantiq/falcon/devices.h ++++ b/arch/mips/lantiq/falcon/devices.h +@@ -11,10 +11,14 @@ + #ifndef _FALCON_DEVICES_H__ + #define _FALCON_DEVICES_H__ + ++#include ++#include ++ + #include "../devices.h" + + extern void falcon_register_nand(void); + extern void falcon_register_gpio(void); + extern void falcon_register_gpio_extra(void); ++extern void falcon_register_spi_flash(struct spi_board_info *data); + + #endif +diff --git a/arch/mips/lantiq/falcon/mach-easy98000.c b/arch/mips/lantiq/falcon/mach-easy98000.c +index 361b8f0..1a7caad 100644 +--- a/arch/mips/lantiq/falcon/mach-easy98000.c ++++ b/arch/mips/lantiq/falcon/mach-easy98000.c +@@ -40,6 +40,21 @@ struct physmap_flash_data easy98000_nor_flash_data = { + .parts = easy98000_nor_partitions, + }; + ++static struct flash_platform_data easy98000_spi_flash_platform_data = { ++ .name = "sflash", ++ .parts = easy98000_nor_partitions, ++ .nr_parts = ARRAY_SIZE(easy98000_nor_partitions) ++}; ++ ++static struct spi_board_info easy98000_spi_flash_data __initdata = { ++ .modalias = "m25p80", ++ .bus_num = 0, ++ .chip_select = 0, ++ .max_speed_hz = 10 * 1000 * 1000, ++ .mode = SPI_MODE_3, ++ .platform_data = &easy98000_spi_flash_platform_data ++}; ++ + /* setup gpio based spi bus/device for access to the eeprom on the board */ + #define SPI_GPIO_MRST 102 + #define SPI_GPIO_MTSR 103 +@@ -93,6 +108,13 @@ easy98000_init(void) + } + + static void __init ++easy98000sf_init(void) ++{ ++ easy98000_init_common(); ++ falcon_register_spi_flash(&easy98000_spi_flash_data); ++} ++ ++static void __init + easy98000nand_init(void) + { + easy98000_init_common(); +@@ -104,6 +126,11 @@ MIPS_MACHINE(LANTIQ_MACH_EASY98000, + "EASY98000 Eval Board", + easy98000_init); + ++MIPS_MACHINE(LANTIQ_MACH_EASY98000SF, ++ "EASY98000SF", ++ "EASY98000 Eval Board (Serial Flash)", ++ easy98000sf_init); ++ + MIPS_MACHINE(LANTIQ_MACH_EASY98000NAND, + "EASY98000NAND", + "EASY98000 Eval Board (NAND Flash)", +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 8293658..41f8336 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -179,6 +179,10 @@ config SPI_MPC52xx + This drivers supports the MPC52xx SPI controller in master SPI + mode. + ++config SPI_FALCON ++ tristate "Falcon SPI controller support" ++ depends on SOC_FALCON ++ + config SPI_MPC52xx_PSC + tristate "Freescale MPC52xx PSC SPI controller" + depends on PPC_MPC52xx && EXPERIMENTAL +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 61c3261..570894c 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -25,6 +25,7 @@ obj-$(CONFIG_SPI_DW_MMIO) += spi-dw-mmio.o + obj-$(CONFIG_SPI_DW_PCI) += spi-dw-midpci.o + spi-dw-midpci-objs := spi-dw-pci.o spi-dw-mid.o + obj-$(CONFIG_SPI_EP93XX) += spi-ep93xx.o ++obj-$(CONFIG_SPI_FALCON) += spi-falcon.o + obj-$(CONFIG_SPI_FSL_LIB) += spi-fsl-lib.o + obj-$(CONFIG_SPI_FSL_ESPI) += spi-fsl-espi.o + obj-$(CONFIG_SPI_FSL_SPI) += spi-fsl-spi.o +diff --git a/drivers/spi/spi-falcon.c b/drivers/spi/spi-falcon.c +new file mode 100644 +index 0000000..447bbaa +--- /dev/null ++++ b/drivers/spi/spi-falcon.c +@@ -0,0 +1,483 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2010 Thomas Langer ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define DRV_NAME "falcon_spi" ++ ++#define FALCON_SPI_XFER_BEGIN (1 << 0) ++#define FALCON_SPI_XFER_END (1 << 1) ++ ++/* Bus Read Configuration Register0 */ ++#define LTQ_BUSRCON0 0x00000010 ++/* Bus Write Configuration Register0 */ ++#define LTQ_BUSWCON0 0x00000018 ++/* Serial Flash Configuration Register */ ++#define LTQ_SFCON 0x00000080 ++/* Serial Flash Time Register */ ++#define LTQ_SFTIME 0x00000084 ++/* Serial Flash Status Register */ ++#define LTQ_SFSTAT 0x00000088 ++/* Serial Flash Command Register */ ++#define LTQ_SFCMD 0x0000008C ++/* Serial Flash Address Register */ ++#define LTQ_SFADDR 0x00000090 ++/* Serial Flash Data Register */ ++#define LTQ_SFDATA 0x00000094 ++/* Serial Flash I/O Control Register */ ++#define LTQ_SFIO 0x00000098 ++/* EBU Clock Control Register */ ++#define LTQ_EBUCC 0x000000C4 ++ ++/* Dummy Phase Length */ ++#define SFCMD_DUMLEN_OFFSET 16 ++#define SFCMD_DUMLEN_MASK 0x000F0000 ++/* Chip Select */ ++#define SFCMD_CS_OFFSET 24 ++#define SFCMD_CS_MASK 0x07000000 ++/* field offset */ ++#define SFCMD_ALEN_OFFSET 20 ++#define SFCMD_ALEN_MASK 0x00700000 ++/* SCK Rise-edge Position */ ++#define SFTIME_SCKR_POS_OFFSET 8 ++#define SFTIME_SCKR_POS_MASK 0x00000F00 ++/* SCK Period */ ++#define SFTIME_SCK_PER_OFFSET 0 ++#define SFTIME_SCK_PER_MASK 0x0000000F ++/* SCK Fall-edge Position */ ++#define SFTIME_SCKF_POS_OFFSET 12 ++#define SFTIME_SCKF_POS_MASK 0x0000F000 ++/* Device Size */ ++#define SFCON_DEV_SIZE_A23_0 0x03000000 ++#define SFCON_DEV_SIZE_MASK 0x0F000000 ++/* Read Data Position */ ++#define SFTIME_RD_POS_MASK 0x000F0000 ++/* Data Output */ ++#define SFIO_UNUSED_WD_MASK 0x0000000F ++/* Command Opcode mask */ ++#define SFCMD_OPC_MASK 0x000000FF ++/* dlen bytes of data to write */ ++#define SFCMD_DIR_WRITE 0x00000100 ++/* Data Length offset */ ++#define SFCMD_DLEN_OFFSET 9 ++/* Command Error */ ++#define SFSTAT_CMD_ERR 0x20000000 ++/* Access Command Pending */ ++#define SFSTAT_CMD_PEND 0x00400000 ++/* Frequency set to 100MHz. */ ++#define EBUCC_EBUDIV_SELF100 0x00000001 ++/* Serial Flash */ ++#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000 ++/* 8-bit multiplexed */ ++#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000 ++/* Serial Flash */ ++#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000 ++/* Chip Select after opcode */ ++#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000 ++ ++struct falcon_spi { ++ u32 sfcmd; /* for caching of opcode, direction, ... */ ++ struct spi_master *master; ++}; ++ ++int ++falcon_spi_xfer(struct spi_device *spi, ++ struct spi_transfer *t, ++ unsigned long flags) ++{ ++ struct device *dev = &spi->dev; ++ struct falcon_spi *priv = spi_master_get_devdata(spi->master); ++ const u8 *txp = t->tx_buf; ++ u8 *rxp = t->rx_buf; ++ unsigned int bytelen = ((8 * t->len + 7) / 8); ++ unsigned int len, alen, dumlen; ++ u32 val; ++ enum { ++ state_init, ++ state_command_prepare, ++ state_write, ++ state_read, ++ state_disable_cs, ++ state_end ++ } state = state_init; ++ ++ do { ++ switch (state) { ++ case state_init: /* detect phase of upper layer sequence */ ++ { ++ /* initial write ? */ ++ if (flags & FALCON_SPI_XFER_BEGIN) { ++ if (!txp) { ++ dev_err(dev, ++ "BEGIN without tx data!\n"); ++ return -1; ++ } ++ /* ++ * Prepare the parts of the sfcmd register, ++ * which should not ++ * change during a sequence! ++ * Only exception are the length fields, ++ * especially alen and dumlen. ++ */ ++ ++ priv->sfcmd = ((spi->chip_select ++ << SFCMD_CS_OFFSET) ++ & SFCMD_CS_MASK); ++ priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED; ++ priv->sfcmd |= *txp; ++ txp++; ++ bytelen--; ++ if (bytelen) { ++ /* ++ * more data: ++ * maybe address and/or dummy ++ */ ++ state = state_command_prepare; ++ break; ++ } else { ++ dev_dbg(dev, "write cmd %02X\n", ++ priv->sfcmd & SFCMD_OPC_MASK); ++ } ++ } ++ /* continued write ? */ ++ if (txp && bytelen) { ++ state = state_write; ++ break; ++ } ++ /* read data? */ ++ if (rxp && bytelen) { ++ state = state_read; ++ break; ++ } ++ /* end of sequence? */ ++ if (flags & FALCON_SPI_XFER_END) ++ state = state_disable_cs; ++ else ++ state = state_end; ++ break; ++ } ++ /* collect tx data for address and dummy phase */ ++ case state_command_prepare: ++ { ++ /* txp is valid, already checked */ ++ val = 0; ++ alen = 0; ++ dumlen = 0; ++ while (bytelen > 0) { ++ if (alen < 3) { ++ val = (val<<8)|(*txp++); ++ alen++; ++ } else if ((dumlen < 15) && (*txp == 0)) { ++ /* ++ * assume dummy bytes are set to 0 ++ * from upper layer ++ */ ++ dumlen++; ++ txp++; ++ } else ++ break; ++ bytelen--; ++ } ++ priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK); ++ priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) | ++ (dumlen << SFCMD_DUMLEN_OFFSET); ++ if (alen > 0) ++ ltq_ebu_w32(val, LTQ_SFADDR); ++ ++ dev_dbg(dev, "write cmd %02X, alen=%d " ++ "(addr=%06X) dumlen=%d\n", ++ priv->sfcmd & SFCMD_OPC_MASK, ++ alen, val, dumlen); ++ ++ if (bytelen > 0) { ++ /* continue with write */ ++ state = state_write; ++ } else if (flags & FALCON_SPI_XFER_END) { ++ /* end of sequence? */ ++ state = state_disable_cs; ++ } else { ++ /* ++ * go to end and expect another ++ * call (read or write) ++ */ ++ state = state_end; ++ } ++ break; ++ } ++ case state_write: ++ { ++ /* txp still valid */ ++ priv->sfcmd |= SFCMD_DIR_WRITE; ++ len = 0; ++ val = 0; ++ do { ++ if (bytelen--) ++ val |= (*txp++) << (8 * len++); ++ if ((flags & FALCON_SPI_XFER_END) ++ && (bytelen == 0)) { ++ priv->sfcmd &= ++ ~SFCMD_KEEP_CS_KEEP_SELECTED; ++ } ++ if ((len == 4) || (bytelen == 0)) { ++ ltq_ebu_w32(val, LTQ_SFDATA); ++ ltq_ebu_w32(priv->sfcmd ++ | (len<sfcmd &= ~(SFCMD_ALEN_MASK ++ | SFCMD_DUMLEN_MASK); ++ } ++ } while (bytelen); ++ state = state_end; ++ break; ++ } ++ case state_read: ++ { ++ /* read data */ ++ priv->sfcmd &= ~SFCMD_DIR_WRITE; ++ do { ++ if ((flags & FALCON_SPI_XFER_END) ++ && (bytelen <= 4)) { ++ priv->sfcmd &= ++ ~SFCMD_KEEP_CS_KEEP_SELECTED; ++ } ++ len = (bytelen > 4) ? 4 : bytelen; ++ bytelen -= len; ++ ltq_ebu_w32(priv->sfcmd ++ |(len<sfcmd &= ~(SFCMD_ALEN_MASK ++ | SFCMD_DUMLEN_MASK); ++ do { ++ val = ltq_ebu_r32(LTQ_SFSTAT); ++ if (val & SFSTAT_CMD_ERR) { ++ /* reset error status */ ++ dev_err(dev, "SFSTAT: CMD_ERR " ++ "(%x)\n", val); ++ ltq_ebu_w32(SFSTAT_CMD_ERR, ++ LTQ_SFSTAT); ++ return -1; ++ } ++ } while (val & SFSTAT_CMD_PEND); ++ val = ltq_ebu_r32(LTQ_SFDATA); ++ do { ++ *rxp = (val & 0xFF); ++ rxp++; ++ val >>= 8; ++ len--; ++ } while (len); ++ } while (bytelen); ++ state = state_end; ++ break; ++ } ++ case state_disable_cs: ++ { ++ priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED; ++ ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET), ++ LTQ_SFCMD); ++ val = ltq_ebu_r32(LTQ_SFSTAT); ++ if (val & SFSTAT_CMD_ERR) { ++ /* reset error status */ ++ dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val); ++ ltq_ebu_w32(SFSTAT_CMD_ERR, LTQ_SFSTAT); ++ return -1; ++ } ++ state = state_end; ++ break; ++ } ++ case state_end: ++ break; ++ } ++ } while (state != state_end); ++ ++ return 0; ++} ++ ++static int ++falcon_spi_setup(struct spi_device *spi) ++{ ++ struct device *dev = &spi->dev; ++ const u32 ebuclk = 100000000; ++ unsigned int i; ++ unsigned long flags; ++ ++ dev_dbg(dev, "setup\n"); ++ ++ if (spi->master->bus_num > 0 || spi->chip_select > 0) ++ return -ENODEV; ++ ++ spin_lock_irqsave(&ebu_lock, flags); ++ ++ if (ebuclk < spi->max_speed_hz) { ++ /* set EBU clock to 100 MHz */ ++ ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, LTQ_EBUCC); ++ i = 1; /* divider */ ++ } else { ++ /* set EBU clock to 50 MHz */ ++ ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, LTQ_EBUCC); ++ ++ /* search for suitable divider */ ++ for (i = 1; i < 7; i++) { ++ if (ebuclk / i <= spi->max_speed_hz) ++ break; ++ } ++ } ++ ++ /* setup period of serial clock */ ++ ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK ++ | SFTIME_SCKR_POS_MASK ++ | SFTIME_SCK_PER_MASK, ++ (i << SFTIME_SCKR_POS_OFFSET) ++ | (i << (SFTIME_SCK_PER_OFFSET + 1)), ++ LTQ_SFTIME); ++ ++ /* ++ * set some bits of unused_wd, to not trigger HOLD/WP ++ * signals on non QUAD flashes ++ */ ++ ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), LTQ_SFIO); ++ ++ ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX, ++ LTQ_BUSRCON0); ++ ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, LTQ_BUSWCON0); ++ /* set address wrap around to maximum for 24-bit addresses */ ++ ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, LTQ_SFCON); ++ ++ spin_unlock_irqrestore(&ebu_lock, flags); ++ ++ return 0; ++} ++ ++static int ++falcon_spi_transfer(struct spi_device *spi, struct spi_message *m) ++{ ++ struct falcon_spi *priv = spi_master_get_devdata(spi->master); ++ struct spi_transfer *t; ++ unsigned long spi_flags; ++ unsigned long flags; ++ int ret = 0; ++ ++ priv->sfcmd = 0; ++ m->actual_length = 0; ++ ++ spi_flags = FALCON_SPI_XFER_BEGIN; ++ list_for_each_entry(t, &m->transfers, transfer_list) { ++ if (list_is_last(&t->transfer_list, &m->transfers)) ++ spi_flags |= FALCON_SPI_XFER_END; ++ ++ spin_lock_irqsave(&ebu_lock, flags); ++ ret = falcon_spi_xfer(spi, t, spi_flags); ++ spin_unlock_irqrestore(&ebu_lock, flags); ++ ++ if (ret) ++ break; ++ ++ m->actual_length += t->len; ++ ++ if (t->delay_usecs || t->cs_change) ++ BUG(); ++ ++ spi_flags = 0; ++ } ++ ++ m->status = ret; ++ m->complete(m->context); ++ ++ return 0; ++} ++ ++static void ++falcon_spi_cleanup(struct spi_device *spi) ++{ ++ struct device *dev = &spi->dev; ++ ++ dev_dbg(dev, "cleanup\n"); ++} ++ ++static int __devinit ++falcon_spi_probe(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct falcon_spi *priv; ++ struct spi_master *master; ++ int ret; ++ ++ dev_dbg(dev, "probing\n"); ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(*priv)); ++ if (!master) { ++ dev_err(dev, "no memory for spi_master\n"); ++ return -ENOMEM; ++ } ++ ++ priv = spi_master_get_devdata(master); ++ priv->master = master; ++ ++ master->mode_bits = SPI_MODE_3; ++ master->num_chipselect = 1; ++ master->bus_num = 0; ++ ++ master->setup = falcon_spi_setup; ++ master->transfer = falcon_spi_transfer; ++ master->cleanup = falcon_spi_cleanup; ++ ++ platform_set_drvdata(pdev, priv); ++ ++ ret = spi_register_master(master); ++ if (ret) ++ spi_master_put(master); ++ ++ return ret; ++} ++ ++static int __devexit ++falcon_spi_remove(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct falcon_spi *priv = platform_get_drvdata(pdev); ++ ++ dev_dbg(dev, "removed\n"); ++ ++ spi_unregister_master(priv->master); ++ ++ return 0; ++} ++ ++static struct platform_driver falcon_spi_driver = { ++ .probe = falcon_spi_probe, ++ .remove = __devexit_p(falcon_spi_remove), ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE ++ } ++}; ++ ++static int __init ++falcon_spi_init(void) ++{ ++ return platform_driver_register(&falcon_spi_driver); ++} ++ ++static void __exit ++falcon_spi_exit(void) ++{ ++ platform_driver_unregister(&falcon_spi_driver); ++} ++ ++module_init(falcon_spi_init); ++module_exit(falcon_spi_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Lantiq Falcon SPI controller driver"); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0036-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch b/target/linux/lantiq/patches-3.3/0036-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch new file mode 100644 index 0000000000..48935385a1 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0036-I2C-MIPS-lantiq-add-FALC-ON-i2c-bus-master.patch @@ -0,0 +1,1225 @@ +From 59e21a2ab9b7554acf2c15dc9ee191e76bebade7 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 4 Nov 2011 16:00:34 +0100 +Subject: [PATCH 36/70] I2C: MIPS: lantiq: add FALC-ON i2c bus master + +This patch adds the driver needed to make the I2C bus work on FALC-ON SoCs. + +Signed-off-by: Thomas Langer +Signed-off-by: John Crispin +Cc: linux-i2c@vger.kernel.org +--- + .../include/asm/mach-lantiq/falcon/lantiq_soc.h | 5 + + arch/mips/lantiq/falcon/clk.c | 44 - + arch/mips/lantiq/falcon/devices.c | 16 + + arch/mips/lantiq/falcon/devices.h | 1 + + arch/mips/lantiq/falcon/mach-easy98000.c | 1 + + drivers/i2c/busses/Kconfig | 10 + + drivers/i2c/busses/Makefile | 1 + + drivers/i2c/busses/i2c-falcon.c | 1040 ++++++++++++++++++++ + 8 files changed, 1074 insertions(+), 44 deletions(-) + delete mode 100644 arch/mips/lantiq/falcon/clk.c + create mode 100644 drivers/i2c/busses/i2c-falcon.c + +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +index 120c56c..fff5ecd 100644 +--- a/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/falcon/lantiq_soc.h +@@ -72,6 +72,10 @@ + #define LTQ_PADCTRL4_BASE_ADDR 0x1E800600 + #define LTQ_PADCTRL4_SIZE 0x0100 + ++/* I2C */ ++#define GPON_I2C_BASE 0x1E200000 ++#define GPON_I2C_SIZE 0x00010000 ++ + /* CHIP ID */ + #define LTQ_STATUS_BASE_ADDR 0x1E802000 + +@@ -106,6 +110,7 @@ + #define ACTS_PADCTRL2 0x00200000 + #define ACTS_PADCTRL3 0x00200000 + #define ACTS_PADCTRL4 0x00400000 ++#define ACTS_I2C_ACT 0x00004000 + + /* global register ranges */ + extern __iomem void *ltq_ebu_membase; +diff --git a/arch/mips/lantiq/falcon/clk.c b/arch/mips/lantiq/falcon/clk.c +deleted file mode 100644 +index afe1b52..0000000 +--- a/arch/mips/lantiq/falcon/clk.c ++++ /dev/null +@@ -1,44 +0,0 @@ +-/* +- * This program is free software; you can redistribute it and/or modify it +- * under the terms of the GNU General Public License version 2 as published +- * by the Free Software Foundation. +- * +- * Copyright (C) 2011 Thomas Langer +- * Copyright (C) 2011 John Crispin +- */ +- +-#include +-#include +- +-#include +- +-#include "devices.h" +- +-/* CPU0 Clock Control Register */ +-#define LTQ_SYS1_CPU0CC 0x0040 +-/* clock divider bit */ +-#define LTQ_CPU0CC_CPUDIV 0x0001 +- +-unsigned int +-ltq_get_io_region_clock(void) +-{ +- return CLOCK_200M; +-} +-EXPORT_SYMBOL(ltq_get_io_region_clock); +- +-unsigned int +-ltq_get_cpu_hz(void) +-{ +- if (ltq_sys1_r32(LTQ_SYS1_CPU0CC) & LTQ_CPU0CC_CPUDIV) +- return CLOCK_200M; +- else +- return CLOCK_400M; +-} +-EXPORT_SYMBOL(ltq_get_cpu_hz); +- +-unsigned int +-ltq_get_fpi_hz(void) +-{ +- return CLOCK_100M; +-} +-EXPORT_SYMBOL(ltq_get_fpi_hz); +diff --git a/arch/mips/lantiq/falcon/devices.c b/arch/mips/lantiq/falcon/devices.c +index 92ec571..e684ed4 100644 +--- a/arch/mips/lantiq/falcon/devices.c ++++ b/arch/mips/lantiq/falcon/devices.c +@@ -134,3 +134,19 @@ falcon_register_spi_flash(struct spi_board_info *data) + spi_register_board_info(data, 1); + platform_device_register(<q_spi); + } ++ ++/* i2c */ ++static struct resource falcon_i2c_resources[] = { ++ MEM_RES("i2c", GPON_I2C_BASE, GPON_I2C_SIZE), ++ IRQ_RES(i2c_lb, FALCON_IRQ_I2C_LBREQ), ++ IRQ_RES(i2c_b, FALCON_IRQ_I2C_BREQ), ++ IRQ_RES(i2c_err, FALCON_IRQ_I2C_I2C_ERR), ++ IRQ_RES(i2c_p, FALCON_IRQ_I2C_I2C_P), ++}; ++ ++void __init ++falcon_register_i2c(void) ++{ ++ platform_device_register_simple("i2c-falcon", 0, ++ falcon_i2c_resources, ARRAY_SIZE(falcon_i2c_resources)); ++} +diff --git a/arch/mips/lantiq/falcon/devices.h b/arch/mips/lantiq/falcon/devices.h +index 5e6f720..d81edbe 100644 +--- a/arch/mips/lantiq/falcon/devices.h ++++ b/arch/mips/lantiq/falcon/devices.h +@@ -20,5 +20,6 @@ extern void falcon_register_nand(void); + extern void falcon_register_gpio(void); + extern void falcon_register_gpio_extra(void); + extern void falcon_register_spi_flash(struct spi_board_info *data); ++extern void falcon_register_i2c(void); + + #endif +diff --git a/arch/mips/lantiq/falcon/mach-easy98000.c b/arch/mips/lantiq/falcon/mach-easy98000.c +index 1a7caad..fc5720d 100644 +--- a/arch/mips/lantiq/falcon/mach-easy98000.c ++++ b/arch/mips/lantiq/falcon/mach-easy98000.c +@@ -98,6 +98,7 @@ easy98000_init_common(void) + { + spi_register_board_info(&easy98000_spi_gpio_devices, 1); + platform_device_register(&easy98000_spi_gpio_device); ++ falcon_register_i2c(); + } + + static void __init +diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig +index 3101dd5..e8d149d 100644 +--- a/drivers/i2c/busses/Kconfig ++++ b/drivers/i2c/busses/Kconfig +@@ -369,6 +369,16 @@ config I2C_DESIGNWARE_PCI + This driver can also be built as a module. If so, the module + will be called i2c-designware-pci. + ++config I2C_FALCON ++ tristate "Falcon I2C interface" ++ depends on SOC_FALCON ++ help ++ If you say yes to this option, support will be included for the ++ Lantiq FALC-ON I2C core. ++ ++ This driver can also be built as a module. If so, the module ++ will be called i2c-falcon. ++ + config I2C_GPIO + tristate "GPIO-based bitbanging I2C" + depends on GENERIC_GPIO +diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile +index fba6da6..36239c8 100644 +--- a/drivers/i2c/busses/Makefile ++++ b/drivers/i2c/busses/Makefile +@@ -37,6 +37,7 @@ obj-$(CONFIG_I2C_DESIGNWARE_PLATFORM) += i2c-designware-platform.o + i2c-designware-platform-objs := i2c-designware-platdrv.o i2c-designware-core.o + obj-$(CONFIG_I2C_DESIGNWARE_PCI) += i2c-designware-pci.o + i2c-designware-pci-objs := i2c-designware-pcidrv.o i2c-designware-core.o ++obj-$(CONFIG_I2C_FALCON) += i2c-falcon.o + obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o + obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o + obj-$(CONFIG_I2C_IBM_IIC) += i2c-ibm_iic.o +diff --git a/drivers/i2c/busses/i2c-falcon.c b/drivers/i2c/busses/i2c-falcon.c +new file mode 100644 +index 0000000..fc4f0eb +--- /dev/null ++++ b/drivers/i2c/busses/i2c-falcon.c +@@ -0,0 +1,1040 @@ ++/* ++ * Lantiq FALC(tm) ON - I2C bus adapter ++ * ++ * Parts based on i2c-designware.c and other i2c drivers from Linux 2.6.33 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ++ * ++ * Copyright (C) 2010 Thomas Langer ++ */ ++ ++/* ++ * CURRENT ISSUES: ++ * - no high speed support ++ * - supports only master mode ++ * - ten bit mode is not tested (no slave devices) ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++/* I2C Identification Register */ ++/* Module ID */ ++#define I2C_ID_ID_MASK 0x0000FF00 ++/* field offset */ ++#define I2C_ID_ID_OFFSET 8 ++/* Revision */ ++#define I2C_ID_REV_MASK 0x000000FF ++/* field offset */ ++#define I2C_ID_REV_OFFSET 0 ++ ++/* I2C Error Interrupt Request Source Status Register */ ++/* TXF_OFL */ ++#define I2C_ERR_IRQSS_TXF_OFL 0x00000008 ++/* TXF_UFL */ ++#define I2C_ERR_IRQSS_TXF_UFL 0x00000004 ++/* RXF_OFL */ ++#define I2C_ERR_IRQSS_RXF_OFL 0x00000002 ++/* RXF_UFL */ ++#define I2C_ERR_IRQSS_RXF_UFL 0x00000001 ++ ++/* I2C Bus Status Register */ ++/* Bus Status */ ++#define I2C_BUS_STAT_BS_MASK 0x00000003 ++/* I2C Bus is free. */ ++#define I2C_BUS_STAT_BS_FREE 0x00000000 ++/* ++ * The device is working as master and has claimed the control ++ * on the I2C-bus (busy master). ++ */ ++#define I2C_BUS_STAT_BS_BM 0x00000002 ++ ++/* I2C Interrupt Clear Register */ ++/* Clear */ ++#define I2C_ICR_BREQ_INT_CLR 0x00000008 ++/* Clear */ ++#define I2C_ICR_LBREQ_INT_CLR 0x00000004 ++ ++/* I2C RUN Control Register */ ++/* Enable */ ++#define I2C_RUN_CTRL_RUN_EN 0x00000001 ++ ++/* I2C Kernel Clock Control Register */ ++/* field offset */ ++#define I2C_CLC_RMC_OFFSET 8 ++/* Enable */ ++#define I2C_IMSC_I2C_P_INT_EN 0x00000020 ++/* Enable */ ++#define I2C_IMSC_I2C_ERR_INT_EN 0x00000010 ++/* Enable */ ++#define I2C_IMSC_BREQ_INT_EN 0x00000008 ++/* Enable */ ++#define I2C_IMSC_LBREQ_INT_EN 0x00000004 ++ ++/* I2C Fractional Divider Configuration Register */ ++/* field offset */ ++#define I2C_FDIV_CFG_INC_OFFSET 16 ++/* field offset */ ++#define I2C_FDIV_CFG_DEC_OFFSET 0 ++ ++/* I2C Fractional Divider (highspeed mode) Configuration Register */ ++/* field offset */ ++#define I2C_FDIV_HIGH_CFG_INC_OFFSET 16 ++/* field offset */ ++#define I2C_FDIV_HIGH_CFG_DEC_OFFSET 0 ++ ++/* I2C Address Register */ ++/* Enable */ ++#define I2C_ADDR_CFG_SOPE_EN 0x00200000 ++/* Enable */ ++#define I2C_ADDR_CFG_SONA_EN 0x00100000 ++/* Enable */ ++#define I2C_ADDR_CFG_MnS_EN 0x00080000 ++ ++/* I2C Protocol Interrupt Request Source Status Register */ ++/* RX */ ++#define I2C_P_IRQSS_RX 0x00000040 ++/* TX_END */ ++#define I2C_P_IRQSS_TX_END 0x00000020 ++/* NACK */ ++#define I2C_P_IRQSS_NACK 0x00000010 ++/* AL */ ++#define I2C_P_IRQSS_AL 0x00000008 ++ ++/* I2C Raw Interrupt Status Register */ ++/* Read: Interrupt occurred. */ ++#define I2C_RIS_I2C_P_INT_INTOCC 0x00000020 ++/* Read: Interrupt occurred. */ ++#define I2C_RIS_I2C_ERR_INT_INTOCC 0x00000010 ++ ++/* I2C End Data Control Register */ ++/* ++ * Set End of Transmission - Note: Do not write '1' to this bit when bus is ++ * free. This will cause an abort after the first byte when a new transfer ++ * is started. ++ */ ++#define I2C_ENDD_CTRL_SETEND 0x00000002 ++/* TX FIFO Flow Control */ ++#define I2C_FIFO_CFG_TXFC 0x00020000 ++/* RX FIFO Flow Control */ ++#define I2C_FIFO_CFG_RXFC 0x00010000 ++/* Word aligned (character alignment of four characters) */ ++#define I2C_FIFO_CFG_TXFA_TXFA2 0x00002000 ++/* Word aligned (character alignment of four characters) */ ++#define I2C_FIFO_CFG_RXFA_RXFA2 0x00000200 ++/* 1 word */ ++#define I2C_FIFO_CFG_TXBS_TXBS0 0x00000000 ++/* 1 word */ ++#define I2C_FIFO_CFG_RXBS_RXBS0 0x00000000 ++ ++ ++/* I2C register structure */ ++struct gpon_reg_i2c { ++ /* I2C Kernel Clock Control Register */ ++ unsigned int clc; /* 0x00000000 */ ++ /* Reserved */ ++ unsigned int res_0; /* 0x00000004 */ ++ /* I2C Identification Register */ ++ unsigned int id; /* 0x00000008 */ ++ /* Reserved */ ++ unsigned int res_1; /* 0x0000000C */ ++ /* ++ * I2C RUN Control Register - This register enables and disables the I2C ++ * peripheral. Before enabling, the I2C has to be configured properly. ++ * After enabling no configuration is possible ++ */ ++ unsigned int run_ctrl; /* 0x00000010 */ ++ /* ++ * I2C End Data Control Register - This register is used to either turn ++ * around the data transmission direction or to address another slave ++ * without sending a stop condition. Also the software can stop the ++ * slave-transmitter by sending a not-accolade when working as ++ * master-receiver or even stop data transmission immediately when ++ * operating as master-transmitter. The writing to the bits of this ++ * control register is only effective when in MASTER RECEIVES BYTES, ++ * MASTER TRANSMITS BYTES, MASTER RESTART or SLAVE RECEIVE BYTES state ++ */ ++ unsigned int endd_ctrl; /* 0x00000014 */ ++ /* ++ * I2C Fractional Divider Configuration Register - These register is ++ * used to program the fractional divider of the I2C bus. Before the ++ * peripheral is switched on by setting the RUN-bit the two (fixed) ++ * values for the two operating frequencies are programmed into these ++ * (configuration) registers. The Register FDIV_HIGH_CFG has the same ++ * layout as I2C_FDIV_CFG. ++ */ ++ unsigned int fdiv_cfg; /* 0x00000018 */ ++ /* ++ * I2C Fractional Divider (highspeed mode) Configuration Register ++ * These register is used to program the fractional divider of the I2C ++ * bus. Before the peripheral is switched on by setting the RUN-bit the ++ * two (fixed) values for the two operating frequencies are programmed ++ * into these (configuration) registers. The Register FDIV_CFG has the ++ * same layout as I2C_FDIV_CFG. ++ */ ++ unsigned int fdiv_high_cfg; /* 0x0000001C */ ++ /* I2C Address Configuration Register */ ++ unsigned int addr_cfg; /* 0x00000020 */ ++ /* ++ * I2C Bus Status Register - This register gives a status information ++ * of the I2C. This additional information can be used by the software ++ * to start proper actions. ++ */ ++ unsigned int bus_stat; /* 0x00000024 */ ++ /* I2C FIFO Configuration Register */ ++ unsigned int fifo_cfg; /* 0x00000028 */ ++ /* I2C Maximum Received Packet Size Register */ ++ unsigned int mrps_ctrl; /* 0x0000002C */ ++ /* I2C Received Packet Size Status Register */ ++ unsigned int rps_stat; /* 0x00000030 */ ++ /* I2C Transmit Packet Size Register */ ++ unsigned int tps_ctrl; /* 0x00000034 */ ++ /* I2C Filled FIFO Stages Status Register */ ++ unsigned int ffs_stat; /* 0x00000038 */ ++ /* Reserved */ ++ unsigned int res_2; /* 0x0000003C */ ++ /* I2C Timing Configuration Register */ ++ unsigned int tim_cfg; /* 0x00000040 */ ++ /* Reserved */ ++ unsigned int res_3[7]; /* 0x00000044 */ ++ /* I2C Error Interrupt Request Source Mask Register */ ++ unsigned int err_irqsm; /* 0x00000060 */ ++ /* I2C Error Interrupt Request Source Status Register */ ++ unsigned int err_irqss; /* 0x00000064 */ ++ /* I2C Error Interrupt Request Source Clear Register */ ++ unsigned int err_irqsc; /* 0x00000068 */ ++ /* Reserved */ ++ unsigned int res_4; /* 0x0000006C */ ++ /* I2C Protocol Interrupt Request Source Mask Register */ ++ unsigned int p_irqsm; /* 0x00000070 */ ++ /* I2C Protocol Interrupt Request Source Status Register */ ++ unsigned int p_irqss; /* 0x00000074 */ ++ /* I2C Protocol Interrupt Request Source Clear Register */ ++ unsigned int p_irqsc; /* 0x00000078 */ ++ /* Reserved */ ++ unsigned int res_5; /* 0x0000007C */ ++ /* I2C Raw Interrupt Status Register */ ++ unsigned int ris; /* 0x00000080 */ ++ /* I2C Interrupt Mask Control Register */ ++ unsigned int imsc; /* 0x00000084 */ ++ /* I2C Masked Interrupt Status Register */ ++ unsigned int mis; /* 0x00000088 */ ++ /* I2C Interrupt Clear Register */ ++ unsigned int icr; /* 0x0000008C */ ++ /* I2C Interrupt Set Register */ ++ unsigned int isr; /* 0x00000090 */ ++ /* I2C DMA Enable Register */ ++ unsigned int dmae; /* 0x00000094 */ ++ /* Reserved */ ++ unsigned int res_6[8154]; /* 0x00000098 */ ++ /* I2C Transmit Data Register */ ++ unsigned int txd; /* 0x00008000 */ ++ /* Reserved */ ++ unsigned int res_7[4095]; /* 0x00008004 */ ++ /* I2C Receive Data Register */ ++ unsigned int rxd; /* 0x0000C000 */ ++ /* Reserved */ ++ unsigned int res_8[4095]; /* 0x0000C004 */ ++}; ++ ++/* mapping for access macros */ ++#define i2c ((struct gpon_reg_i2c *)priv->membase) ++#define reg_r32(reg) __raw_readl(reg) ++#define reg_w32(val, reg) __raw_writel(val, reg) ++#define reg_w32_mask(clear, set, reg) \ ++ reg_w32((reg_r32(reg) & ~(clear)) | (set), reg) ++#define reg_r32_table(reg, idx) reg_r32(&((uint32_t *)®)[idx]) ++#define reg_w32_table(val, reg, idx) reg_w32(val, &((uint32_t *)®)[idx]) ++ ++#define i2c_r32(reg) reg_r32(&i2c->reg) ++#define i2c_w32(val, reg) reg_w32(val, &i2c->reg) ++#define i2c_w32_mask(clear, set, reg) reg_w32_mask(clear, set, &i2c->reg) ++ ++#define DRV_NAME "i2c-falcon" ++#define DRV_VERSION "1.01" ++ ++#define FALCON_I2C_BUSY_TIMEOUT 20 /* ms */ ++ ++#ifdef DEBUG ++#define FALCON_I2C_XFER_TIMEOUT (25 * HZ) ++#else ++#define FALCON_I2C_XFER_TIMEOUT HZ ++#endif ++#if defined(DEBUG) && 0 ++#define PRINTK(arg...) pr_info(arg) ++#else ++#define PRINTK(arg...) do {} while (0) ++#endif ++ ++#define FALCON_I2C_IMSC_DEFAULT_MASK (I2C_IMSC_I2C_P_INT_EN | \ ++ I2C_IMSC_I2C_ERR_INT_EN) ++ ++#define FALCON_I2C_ARB_LOST (1 << 0) ++#define FALCON_I2C_NACK (1 << 1) ++#define FALCON_I2C_RX_UFL (1 << 2) ++#define FALCON_I2C_RX_OFL (1 << 3) ++#define FALCON_I2C_TX_UFL (1 << 4) ++#define FALCON_I2C_TX_OFL (1 << 5) ++ ++struct falcon_i2c { ++ struct mutex mutex; ++ ++ enum { ++ FALCON_I2C_MODE_100 = 1, ++ FALCON_I2C_MODE_400 = 2, ++ FALCON_I2C_MODE_3400 = 3 ++ } mode; /* current speed mode */ ++ ++ struct clk *clk; /* clock input for i2c hardware block */ ++ struct gpon_reg_i2c __iomem *membase; /* base of mapped registers */ ++ int irq_lb, irq_b, irq_err, irq_p; /* last burst, burst, error, ++ protocol IRQs */ ++ ++ struct i2c_adapter adap; ++ struct device *dev; ++ ++ struct completion cmd_complete; ++ ++ /* message transfer data */ ++ /* current message */ ++ struct i2c_msg *current_msg; ++ /* number of messages to handle */ ++ int msgs_num; ++ /* current buffer */ ++ u8 *msg_buf; ++ /* remaining length of current buffer */ ++ u32 msg_buf_len; ++ /* error status of the current transfer */ ++ int msg_err; ++ ++ /* master status codes */ ++ enum { ++ STATUS_IDLE, ++ STATUS_ADDR, /* address phase */ ++ STATUS_WRITE, ++ STATUS_READ, ++ STATUS_READ_END, ++ STATUS_STOP ++ } status; ++}; ++ ++static irqreturn_t falcon_i2c_isr(int irq, void *dev_id); ++ ++static inline void enable_burst_irq(struct falcon_i2c *priv) ++{ ++ i2c_w32_mask(0, I2C_IMSC_LBREQ_INT_EN | I2C_IMSC_BREQ_INT_EN, imsc); ++} ++static inline void disable_burst_irq(struct falcon_i2c *priv) ++{ ++ i2c_w32_mask(I2C_IMSC_LBREQ_INT_EN | I2C_IMSC_BREQ_INT_EN, 0, imsc); ++} ++ ++static void prepare_msg_send_addr(struct falcon_i2c *priv) ++{ ++ struct i2c_msg *msg = priv->current_msg; ++ int rd = !!(msg->flags & I2C_M_RD); ++ u16 addr = msg->addr; ++ ++ /* new i2c_msg */ ++ priv->msg_buf = msg->buf; ++ priv->msg_buf_len = msg->len; ++ if (rd) ++ priv->status = STATUS_READ; ++ else ++ priv->status = STATUS_WRITE; ++ ++ /* send slave address */ ++ if (msg->flags & I2C_M_TEN) { ++ i2c_w32(0xf0 | ((addr & 0x300) >> 7) | rd, txd); ++ i2c_w32(addr & 0xff, txd); ++ } else ++ i2c_w32((addr & 0x7f) << 1 | rd, txd); ++} ++ ++static void set_tx_len(struct falcon_i2c *priv) ++{ ++ struct i2c_msg *msg = priv->current_msg; ++ int len = (msg->flags & I2C_M_TEN) ? 2 : 1; ++ ++ PRINTK("set_tx_len %cX\n", (msg->flags & I2C_M_RD) ? ('R') : ('T')); ++ ++ priv->status = STATUS_ADDR; ++ ++ if (!(msg->flags & I2C_M_RD)) { ++ len += msg->len; ++ } else { ++ /* set maximum received packet size (before rx int!) */ ++ i2c_w32(msg->len, mrps_ctrl); ++ } ++ i2c_w32(len, tps_ctrl); ++ enable_burst_irq(priv); ++} ++ ++static int falcon_i2c_hw_init(struct i2c_adapter *adap) ++{ ++ struct falcon_i2c *priv = i2c_get_adapdata(adap); ++ ++ /* disable bus */ ++ i2c_w32_mask(I2C_RUN_CTRL_RUN_EN, 0, run_ctrl); ++ ++#ifndef DEBUG ++ /* set normal operation clock divider */ ++ i2c_w32(1 << I2C_CLC_RMC_OFFSET, clc); ++#else ++ /* for debugging a higher divider value! */ ++ i2c_w32(0xF0 << I2C_CLC_RMC_OFFSET, clc); ++#endif ++ ++ /* set frequency */ ++ if (priv->mode == FALCON_I2C_MODE_100) { ++ dev_dbg(priv->dev, "set standard mode (100 kHz)\n"); ++ i2c_w32(0, fdiv_high_cfg); ++ i2c_w32((1 << I2C_FDIV_CFG_INC_OFFSET) | ++ (499 << I2C_FDIV_CFG_DEC_OFFSET), ++ fdiv_cfg); ++ } else if (priv->mode == FALCON_I2C_MODE_400) { ++ dev_dbg(priv->dev, "set fast mode (400 kHz)\n"); ++ i2c_w32(0, fdiv_high_cfg); ++ i2c_w32((1 << I2C_FDIV_CFG_INC_OFFSET) | ++ (124 << I2C_FDIV_CFG_DEC_OFFSET), ++ fdiv_cfg); ++ } else if (priv->mode == FALCON_I2C_MODE_3400) { ++ dev_dbg(priv->dev, "set high mode (3.4 MHz)\n"); ++ i2c_w32(0, fdiv_cfg); ++ /* TODO recalculate value for 100MHz input */ ++ i2c_w32((41 << I2C_FDIV_HIGH_CFG_INC_OFFSET) | ++ (152 << I2C_FDIV_HIGH_CFG_DEC_OFFSET), ++ fdiv_high_cfg); ++ } else { ++ dev_warn(priv->dev, "unknown mode\n"); ++ return -ENODEV; ++ } ++ ++ /* configure fifo */ ++ i2c_w32(I2C_FIFO_CFG_TXFC | /* tx fifo as flow controller */ ++ I2C_FIFO_CFG_RXFC | /* rx fifo as flow controller */ ++ I2C_FIFO_CFG_TXFA_TXFA2 | /* tx fifo 4-byte aligned */ ++ I2C_FIFO_CFG_RXFA_RXFA2 | /* rx fifo 4-byte aligned */ ++ I2C_FIFO_CFG_TXBS_TXBS0 | /* tx fifo burst size is 1 word */ ++ I2C_FIFO_CFG_RXBS_RXBS0, /* rx fifo burst size is 1 word */ ++ fifo_cfg); ++ ++ /* configure address */ ++ i2c_w32(I2C_ADDR_CFG_SOPE_EN | /* generate stop when no more data ++ in the fifo */ ++ I2C_ADDR_CFG_SONA_EN | /* generate stop when NA received */ ++ I2C_ADDR_CFG_MnS_EN | /* we are master device */ ++ 0, /* our slave address (not used!) */ ++ addr_cfg); ++ ++ /* enable bus */ ++ i2c_w32_mask(0, I2C_RUN_CTRL_RUN_EN, run_ctrl); ++ ++ return 0; ++} ++ ++static int falcon_i2c_wait_bus_not_busy(struct falcon_i2c *priv) ++{ ++ int timeout = FALCON_I2C_BUSY_TIMEOUT; ++ ++ while ((i2c_r32(bus_stat) & I2C_BUS_STAT_BS_MASK) ++ != I2C_BUS_STAT_BS_FREE) { ++ if (timeout <= 0) { ++ dev_warn(priv->dev, "timeout waiting for bus ready\n"); ++ return -ETIMEDOUT; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ return 0; ++} ++ ++static void falcon_i2c_tx(struct falcon_i2c *priv, int last) ++{ ++ if (priv->msg_buf_len && priv->msg_buf) { ++ i2c_w32(*priv->msg_buf, txd); ++ ++ if (--priv->msg_buf_len) ++ priv->msg_buf++; ++ else ++ priv->msg_buf = NULL; ++ } else ++ last = 1; ++ ++ if (last) ++ disable_burst_irq(priv); ++} ++ ++static void falcon_i2c_rx(struct falcon_i2c *priv, int last) ++{ ++ u32 fifo_stat, timeout; ++ if (priv->msg_buf_len && priv->msg_buf) { ++ timeout = 5000000; ++ do { ++ fifo_stat = i2c_r32(ffs_stat); ++ } while (!fifo_stat && --timeout); ++ if (!timeout) { ++ last = 1; ++ PRINTK("\nrx timeout\n"); ++ goto err; ++ } ++ while (fifo_stat) { ++ *priv->msg_buf = i2c_r32(rxd); ++ if (--priv->msg_buf_len) ++ priv->msg_buf++; ++ else { ++ priv->msg_buf = NULL; ++ last = 1; ++ break; ++ } ++ #if 0 ++ fifo_stat = i2c_r32(ffs_stat); ++ #else ++ /* do not read more than burst size, otherwise no "last ++ burst" is generated and the transaction is blocked! */ ++ fifo_stat = 0; ++ #endif ++ } ++ } else { ++ last = 1; ++ } ++err: ++ if (last) { ++ disable_burst_irq(priv); ++ ++ if (priv->status == STATUS_READ_END) { ++ /* do the STATUS_STOP and complete() here, as sometimes ++ the tx_end is already seen before this is finished */ ++ priv->status = STATUS_STOP; ++ complete(&priv->cmd_complete); ++ } else { ++ i2c_w32(I2C_ENDD_CTRL_SETEND, endd_ctrl); ++ priv->status = STATUS_READ_END; ++ } ++ } ++} ++ ++static void falcon_i2c_xfer_init(struct falcon_i2c *priv) ++{ ++ /* enable interrupts */ ++ i2c_w32(FALCON_I2C_IMSC_DEFAULT_MASK, imsc); ++ ++ /* trigger transfer of first msg */ ++ set_tx_len(priv); ++} ++ ++static void dump_msgs(struct i2c_msg msgs[], int num, int rx) ++{ ++#if defined(DEBUG) ++ int i, j; ++ pr_info("Messages %d %s\n", num, rx ? "out" : "in"); ++ for (i = 0; i < num; i++) { ++ pr_info("%2d %cX Msg(%d) addr=0x%X: ", i, ++ (msgs[i].flags & I2C_M_RD) ? ('R') : ('T'), ++ msgs[i].len, msgs[i].addr); ++ if (!(msgs[i].flags & I2C_M_RD) || rx) { ++ for (j = 0; j < msgs[i].len; j++) ++ printk("%02X ", msgs[i].buf[j]); ++ } ++ printk("\n"); ++ } ++#endif ++} ++ ++static void falcon_i2c_release_bus(struct falcon_i2c *priv) ++{ ++ if ((i2c_r32(bus_stat) & I2C_BUS_STAT_BS_MASK) == I2C_BUS_STAT_BS_BM) ++ i2c_w32(I2C_ENDD_CTRL_SETEND, endd_ctrl); ++} ++ ++static int falcon_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], ++ int num) ++{ ++ struct falcon_i2c *priv = i2c_get_adapdata(adap); ++ int ret; ++ ++ dev_dbg(priv->dev, "xfer %u messages\n", num); ++ dump_msgs(msgs, num, 0); ++ ++ mutex_lock(&priv->mutex); ++ ++ INIT_COMPLETION(priv->cmd_complete); ++ priv->current_msg = msgs; ++ priv->msgs_num = num; ++ priv->msg_err = 0; ++ priv->status = STATUS_IDLE; ++ ++ /* wait for the bus to become ready */ ++ ret = falcon_i2c_wait_bus_not_busy(priv); ++ if (ret) ++ goto done; ++ ++ while (priv->msgs_num) { ++ /* start the transfers */ ++ falcon_i2c_xfer_init(priv); ++ ++ /* wait for transfers to complete */ ++ ret = wait_for_completion_interruptible_timeout( ++ &priv->cmd_complete, FALCON_I2C_XFER_TIMEOUT); ++ if (ret == 0) { ++ dev_err(priv->dev, "controller timed out\n"); ++ falcon_i2c_hw_init(adap); ++ ret = -ETIMEDOUT; ++ goto done; ++ } else if (ret < 0) ++ goto done; ++ ++ if (priv->msg_err) { ++ if (priv->msg_err & FALCON_I2C_NACK) ++ ret = -ENXIO; ++ else ++ ret = -EREMOTEIO; ++ goto done; ++ } ++ if (--priv->msgs_num) ++ priv->current_msg++; ++ } ++ /* no error? */ ++ ret = num; ++ ++done: ++ falcon_i2c_release_bus(priv); ++ ++ mutex_unlock(&priv->mutex); ++ ++ if (ret >= 0) ++ dump_msgs(msgs, num, 1); ++ ++ PRINTK("XFER ret %d\n", ret); ++ return ret; ++} ++ ++static irqreturn_t falcon_i2c_isr_burst(int irq, void *dev_id) ++{ ++ struct falcon_i2c *priv = dev_id; ++ struct i2c_msg *msg = priv->current_msg; ++ int last = (irq == priv->irq_lb); ++ ++ if (last) ++ PRINTK("LB "); ++ else ++ PRINTK("B "); ++ ++ if (msg->flags & I2C_M_RD) { ++ switch (priv->status) { ++ case STATUS_ADDR: ++ PRINTK("X"); ++ prepare_msg_send_addr(priv); ++ disable_burst_irq(priv); ++ break; ++ case STATUS_READ: ++ case STATUS_READ_END: ++ PRINTK("R"); ++ falcon_i2c_rx(priv, last); ++ break; ++ default: ++ disable_burst_irq(priv); ++ PRINTK("Status R %d\n", priv->status); ++ break; ++ } ++ } else { ++ switch (priv->status) { ++ case STATUS_ADDR: ++ PRINTK("x"); ++ prepare_msg_send_addr(priv); ++ break; ++ case STATUS_WRITE: ++ PRINTK("w"); ++ falcon_i2c_tx(priv, last); ++ break; ++ default: ++ disable_burst_irq(priv); ++ PRINTK("Status W %d\n", priv->status); ++ break; ++ } ++ } ++ ++ i2c_w32(I2C_ICR_BREQ_INT_CLR | I2C_ICR_LBREQ_INT_CLR, icr); ++ return IRQ_HANDLED; ++} ++ ++static void falcon_i2c_isr_prot(struct falcon_i2c *priv) ++{ ++ u32 i_pro = i2c_r32(p_irqss); ++ ++ PRINTK("i2c-p"); ++ ++ /* not acknowledge */ ++ if (i_pro & I2C_P_IRQSS_NACK) { ++ priv->msg_err |= FALCON_I2C_NACK; ++ PRINTK(" nack"); ++ } ++ ++ /* arbitration lost */ ++ if (i_pro & I2C_P_IRQSS_AL) { ++ priv->msg_err |= FALCON_I2C_ARB_LOST; ++ PRINTK(" arb-lost"); ++ } ++ /* tx -> rx switch */ ++ if (i_pro & I2C_P_IRQSS_RX) ++ PRINTK(" rx"); ++ ++ /* tx end */ ++ if (i_pro & I2C_P_IRQSS_TX_END) ++ PRINTK(" txend"); ++ PRINTK("\n"); ++ ++ if (!priv->msg_err) { ++ /* tx -> rx switch */ ++ if (i_pro & I2C_P_IRQSS_RX) { ++ priv->status = STATUS_READ; ++ enable_burst_irq(priv); ++ } ++ if (i_pro & I2C_P_IRQSS_TX_END) { ++ if (priv->status == STATUS_READ) ++ priv->status = STATUS_READ_END; ++ else { ++ disable_burst_irq(priv); ++ priv->status = STATUS_STOP; ++ } ++ } ++ } ++ ++ i2c_w32(i_pro, p_irqsc); ++} ++ ++static irqreturn_t falcon_i2c_isr(int irq, void *dev_id) ++{ ++ u32 i_raw, i_err = 0; ++ struct falcon_i2c *priv = dev_id; ++ ++ i_raw = i2c_r32(mis); ++ PRINTK("i_raw 0x%08X\n", i_raw); ++ ++ /* error interrupt */ ++ if (i_raw & I2C_RIS_I2C_ERR_INT_INTOCC) { ++ i_err = i2c_r32(err_irqss); ++ PRINTK("i_err 0x%08X bus_stat 0x%04X\n", ++ i_err, i2c_r32(bus_stat)); ++ ++ /* tx fifo overflow (8) */ ++ if (i_err & I2C_ERR_IRQSS_TXF_OFL) ++ priv->msg_err |= FALCON_I2C_TX_OFL; ++ ++ /* tx fifo underflow (4) */ ++ if (i_err & I2C_ERR_IRQSS_TXF_UFL) ++ priv->msg_err |= FALCON_I2C_TX_UFL; ++ ++ /* rx fifo overflow (2) */ ++ if (i_err & I2C_ERR_IRQSS_RXF_OFL) ++ priv->msg_err |= FALCON_I2C_RX_OFL; ++ ++ /* rx fifo underflow (1) */ ++ if (i_err & I2C_ERR_IRQSS_RXF_UFL) ++ priv->msg_err |= FALCON_I2C_RX_UFL; ++ ++ i2c_w32(i_err, err_irqsc); ++ } ++ ++ /* protocol interrupt */ ++ if (i_raw & I2C_RIS_I2C_P_INT_INTOCC) ++ falcon_i2c_isr_prot(priv); ++ ++ if ((priv->msg_err) || (priv->status == STATUS_STOP)) ++ complete(&priv->cmd_complete); ++ ++ return IRQ_HANDLED; ++} ++ ++static u32 falcon_i2c_functionality(struct i2c_adapter *adap) ++{ ++ return I2C_FUNC_I2C | ++ I2C_FUNC_10BIT_ADDR | ++ I2C_FUNC_SMBUS_EMUL; ++} ++ ++static struct i2c_algorithm falcon_i2c_algorithm = { ++ .master_xfer = falcon_i2c_xfer, ++ .functionality = falcon_i2c_functionality, ++}; ++ ++static int __devinit falcon_i2c_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct falcon_i2c *priv; ++ struct i2c_adapter *adap; ++ struct resource *mmres, *ioarea, ++ *irqres_lb, *irqres_b, *irqres_err, *irqres_p; ++ struct clk *clk; ++ ++ dev_dbg(&pdev->dev, "probing\n"); ++ ++ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ irqres_lb = platform_get_resource_byname(pdev, IORESOURCE_IRQ, ++ "i2c_lb"); ++ irqres_b = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "i2c_b"); ++ irqres_err = platform_get_resource_byname(pdev, IORESOURCE_IRQ, ++ "i2c_err"); ++ irqres_p = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "i2c_p"); ++ ++ if (!mmres || !irqres_lb || !irqres_b || !irqres_err || !irqres_p) { ++ dev_err(&pdev->dev, "no resources\n"); ++ return -ENODEV; ++ } ++ ++ clk = clk_get_fpi(); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "failed to get fpi clk\n"); ++ return -ENOENT; ++ } ++ ++ if (clk_get_rate(clk) != 100000000) { ++ dev_err(&pdev->dev, "input clock is not 100MHz\n"); ++ return -ENOENT; ++ } ++ clk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(clk)) { ++ dev_err(&pdev->dev, "failed to get i2c clk\n"); ++ return -ENOENT; ++ } ++ ++ /* allocate private data */ ++ priv = kzalloc(sizeof(*priv), GFP_KERNEL); ++ if (!priv) { ++ dev_err(&pdev->dev, "can't allocate private data\n"); ++ return -ENOMEM; ++ } ++ ++ adap = &priv->adap; ++ i2c_set_adapdata(adap, priv); ++ adap->owner = THIS_MODULE; ++ adap->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ strlcpy(adap->name, DRV_NAME "-adapter", sizeof(adap->name)); ++ adap->algo = &falcon_i2c_algorithm; ++ ++ priv->mode = FALCON_I2C_MODE_100; ++ priv->clk = clk; ++ priv->dev = &pdev->dev; ++ ++ init_completion(&priv->cmd_complete); ++ mutex_init(&priv->mutex); ++ ++ if (ltq_gpio_request(&pdev->dev, 107, 0, 0, DRV_NAME":sda") || ++ ltq_gpio_request(&pdev->dev, 108, 0, 0, DRV_NAME":scl")) ++ { ++ dev_err(&pdev->dev, "I2C gpios not available\n"); ++ ret = -ENXIO; ++ goto err_free_priv; ++ } ++ ++ ioarea = request_mem_region(mmres->start, resource_size(mmres), ++ pdev->name); ++ ++ if (ioarea == NULL) { ++ dev_err(&pdev->dev, "I2C region already claimed\n"); ++ ret = -ENXIO; ++ goto err_free_gpio; ++ } ++ ++ /* map memory */ ++ priv->membase = ioremap_nocache(mmres->start & ~KSEG1, ++ resource_size(mmres)); ++ if (priv->membase == NULL) { ++ ret = -ENOMEM; ++ goto err_release_region; ++ } ++ ++ priv->irq_lb = irqres_lb->start; ++ ret = request_irq(priv->irq_lb, falcon_i2c_isr_burst, IRQF_DISABLED, ++ irqres_lb->name, priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get last burst IRQ %d\n", ++ irqres_lb->start); ++ ret = -ENODEV; ++ goto err_unmap_mem; ++ } ++ ++ priv->irq_b = irqres_b->start; ++ ret = request_irq(priv->irq_b, falcon_i2c_isr_burst, IRQF_DISABLED, ++ irqres_b->name, priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get burst IRQ %d\n", ++ irqres_b->start); ++ ret = -ENODEV; ++ goto err_free_lb_irq; ++ } ++ ++ priv->irq_err = irqres_err->start; ++ ret = request_irq(priv->irq_err, falcon_i2c_isr, IRQF_DISABLED, ++ irqres_err->name, priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get error IRQ %d\n", ++ irqres_err->start); ++ ret = -ENODEV; ++ goto err_free_b_irq; ++ } ++ ++ priv->irq_p = irqres_p->start; ++ ret = request_irq(priv->irq_p, falcon_i2c_isr, IRQF_DISABLED, ++ irqres_p->name, priv); ++ if (ret) { ++ dev_err(&pdev->dev, "can't get protocol IRQ %d\n", ++ irqres_p->start); ++ ret = -ENODEV; ++ goto err_free_err_irq; ++ } ++ ++ dev_dbg(&pdev->dev, "mapped io-space to %p\n", priv->membase); ++ dev_dbg(&pdev->dev, "use IRQs %d, %d, %d, %d\n", irqres_lb->start, ++ irqres_b->start, irqres_err->start, irqres_p->start); ++ ++ /* add our adapter to the i2c stack */ ++ ret = i2c_add_numbered_adapter(adap); ++ if (ret) { ++ dev_err(&pdev->dev, "can't register I2C adapter\n"); ++ goto err_free_p_irq; ++ } ++ ++ platform_set_drvdata(pdev, priv); ++ i2c_set_adapdata(adap, priv); ++ ++ /* print module version information */ ++ dev_dbg(&pdev->dev, "module id=%u revision=%u\n", ++ (i2c_r32(id) & I2C_ID_ID_MASK) >> I2C_ID_ID_OFFSET, ++ (i2c_r32(id) & I2C_ID_REV_MASK) >> I2C_ID_REV_OFFSET); ++ ++ /* initialize HW */ ++ ret = falcon_i2c_hw_init(adap); ++ if (ret) { ++ dev_err(&pdev->dev, "can't configure adapter\n"); ++ goto err_remove_adapter; ++ } ++ ++ dev_info(&pdev->dev, "version %s\n", DRV_VERSION); ++ ++ return 0; ++ ++err_remove_adapter: ++ i2c_del_adapter(adap); ++ platform_set_drvdata(pdev, NULL); ++ ++err_free_p_irq: ++ free_irq(priv->irq_p, priv); ++ ++err_free_err_irq: ++ free_irq(priv->irq_err, priv); ++ ++err_free_b_irq: ++ free_irq(priv->irq_b, priv); ++ ++err_free_lb_irq: ++ free_irq(priv->irq_lb, priv); ++ ++err_unmap_mem: ++ iounmap(priv->membase); ++ ++err_release_region: ++ release_mem_region(mmres->start, resource_size(mmres)); ++ ++err_free_gpio: ++ gpio_free(108); ++ gpio_free(107); ++ ++err_free_priv: ++ kfree(priv); ++ ++ return ret; ++} ++ ++static int __devexit falcon_i2c_remove(struct platform_device *pdev) ++{ ++ struct falcon_i2c *priv = platform_get_drvdata(pdev); ++ struct resource *mmres; ++ ++ /* disable bus */ ++ i2c_w32_mask(I2C_RUN_CTRL_RUN_EN, 0, run_ctrl); ++ ++ /* remove driver */ ++ platform_set_drvdata(pdev, NULL); ++ i2c_del_adapter(&priv->adap); ++ ++ free_irq(priv->irq_lb, priv); ++ free_irq(priv->irq_b, priv); ++ free_irq(priv->irq_err, priv); ++ free_irq(priv->irq_p, priv); ++ ++ iounmap(priv->membase); ++ ++ gpio_free(108); ++ gpio_free(107); ++ ++ kfree(priv); ++ ++ mmres = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(mmres->start, resource_size(mmres)); ++ ++ dev_dbg(&pdev->dev, "removed\n"); ++ ++ return 0; ++} ++ ++static struct platform_driver falcon_i2c_driver = { ++ .probe = falcon_i2c_probe, ++ .remove = __devexit_p(falcon_i2c_remove), ++ .driver = { ++ .name = DRV_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init falcon_i2c_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&falcon_i2c_driver); ++ ++ if (ret) ++ pr_debug(DRV_NAME ": can't register platform driver\n"); ++ ++ return ret; ++} ++ ++static void __exit falcon_i2c_exit(void) ++{ ++ platform_driver_unregister(&falcon_i2c_driver); ++} ++ ++module_init(falcon_i2c_init); ++module_exit(falcon_i2c_exit); ++ ++MODULE_DESCRIPTION("Lantiq FALC(tm) ON - I2C bus adapter"); ++MODULE_ALIAS("platform:" DRV_NAME); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0037-MIPS-lantiq-add-xway-nand-driver.patch b/target/linux/lantiq/patches-3.3/0037-MIPS-lantiq-add-xway-nand-driver.patch new file mode 100644 index 0000000000..9fc6da04bb --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0037-MIPS-lantiq-add-xway-nand-driver.patch @@ -0,0 +1,310 @@ +From 50a9d37efdfa502fafe48a1de604cd5d84a0d5e9 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 27 Aug 2011 20:08:14 +0200 +Subject: [PATCH 37/70] MIPS: lantiq: add xway nand driver + +This patch adds a nand driver for XWAY SoCs. The patch makes use of the +plat_nand driver. As with the EBU NOR driver merged in 3.0, we have the +endianess swap problem on read. To workaround this problem we make the +read_byte() callback available via the plat_nand driver causing the nand +layer to do byte reads. + +Signed-off-by: John Crispin + +TODO : memory ranges + cs lines + plat dev + ebu2 and not ebu1 ? +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 2 + + arch/mips/lantiq/xway/Makefile | 2 +- + arch/mips/lantiq/xway/devices.h | 1 + + arch/mips/lantiq/xway/nand.c | 216 ++++++++++++++++++++ + drivers/mtd/nand/plat_nand.c | 1 + + include/linux/mtd/nand.h | 1 + + 6 files changed, 222 insertions(+), 1 deletions(-) + create mode 100644 arch/mips/lantiq/xway/nand.c + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index 3f22acb..ab2d236 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -145,6 +145,8 @@ + /* register access macros for EBU and CGU */ + #define ltq_ebu_w32(x, y) ltq_w32((x), ltq_ebu_membase + (y)) + #define ltq_ebu_r32(x) ltq_r32(ltq_ebu_membase + (x)) ++#define ltq_ebu_w32_mask(x, y, z) \ ++ ltq_w32_mask(x, y, ltq_ebu_membase + (z)) + #define ltq_cgu_w32(x, y) ltq_w32((x), ltq_cgu_membase + (y)) + #define ltq_cgu_r32(x) ltq_r32(ltq_cgu_membase + (x)) + +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 9d1a0a2..277aa34 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,4 +1,4 @@ +-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o ++obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o nand.o + + obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o + obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/devices.h b/arch/mips/lantiq/xway/devices.h +index e904934..d825cbd 100644 +--- a/arch/mips/lantiq/xway/devices.h ++++ b/arch/mips/lantiq/xway/devices.h +@@ -16,5 +16,6 @@ extern void ltq_register_gpio(void); + extern void ltq_register_gpio_stp(void); + extern void ltq_register_ase_asc(void); + extern void ltq_register_etop(struct ltq_eth_data *eth); ++extern void xway_register_nand(struct mtd_partition *parts, int count); + + #endif +diff --git a/arch/mips/lantiq/xway/nand.c b/arch/mips/lantiq/xway/nand.c +new file mode 100644 +index 0000000..9ab91d8 +--- /dev/null ++++ b/arch/mips/lantiq/xway/nand.c +@@ -0,0 +1,216 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2010 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++#include "devices.h" ++ ++/* nand registers */ ++#define LTQ_EBU_NAND_WAIT 0xB4 ++#define LTQ_EBU_NAND_ECC0 0xB8 ++#define LTQ_EBU_NAND_ECC_AC 0xBC ++#define LTQ_EBU_NAND_CON 0xB0 ++#define LTQ_EBU_ADDSEL1 0x24 ++ ++/* gpio definitions */ ++#define PIN_ALE 13 ++#define PIN_CLE 24 ++#define PIN_CS1 23 ++#define PIN_RDY 48 /* NFLASH_READY */ ++#define PIN_RD 49 /* NFLASH_READ_N */ ++ ++#define NAND_CMD_ALE (1 << 2) ++#define NAND_CMD_CLE (1 << 3) ++#define NAND_CMD_CS (1 << 4) ++#define NAND_WRITE_CMD_RESET 0xff ++#define NAND_WRITE_CMD (NAND_CMD_CS | NAND_CMD_CLE) ++#define NAND_WRITE_ADDR (NAND_CMD_CS | NAND_CMD_ALE) ++#define NAND_WRITE_DATA (NAND_CMD_CS) ++#define NAND_READ_DATA (NAND_CMD_CS) ++#define NAND_WAIT_WR_C (1 << 3) ++#define NAND_WAIT_RD (0x1) ++ ++#define ADDSEL1_MASK(x) (x << 4) ++#define ADDSEL1_REGEN 1 ++#define BUSCON1_SETUP (1 << 22) ++#define BUSCON1_BCGEN_RES (0x3 << 12) ++#define BUSCON1_WAITWRC2 (2 << 8) ++#define BUSCON1_WAITRDC2 (2 << 6) ++#define BUSCON1_HOLDC1 (1 << 4) ++#define BUSCON1_RECOVC1 (1 << 2) ++#define BUSCON1_CMULT4 1 ++#define NAND_CON_NANDM 1 ++#define NAND_CON_CSMUX (1 << 1) ++#define NAND_CON_CS_P (1 << 4) ++#define NAND_CON_SE_P (1 << 5) ++#define NAND_CON_WP_P (1 << 6) ++#define NAND_CON_PRE_P (1 << 7) ++#define NAND_CON_IN_CS0 0 ++#define NAND_CON_OUT_CS0 0 ++#define NAND_CON_IN_CS1 (1 << 8) ++#define NAND_CON_OUT_CS1 (1 << 10) ++#define NAND_CON_CE (1 << 20) ++ ++#define NAND_BASE_ADDRESS (KSEG1 | 0x14000000) ++ ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++ ++static void xway_select_chip(struct mtd_info *mtd, int chip) ++{ ++ switch (chip) { ++ case -1: ++ ltq_ebu_w32_mask(NAND_CON_CE, 0, LTQ_EBU_NAND_CON); ++ ltq_ebu_w32_mask(NAND_CON_NANDM, 0, LTQ_EBU_NAND_CON); ++ break; ++ case 0: ++ ltq_ebu_w32_mask(0, NAND_CON_NANDM, LTQ_EBU_NAND_CON); ++ ltq_ebu_w32_mask(0, NAND_CON_CE, LTQ_EBU_NAND_CON); ++ /* reset the nand chip */ ++ while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) ++ ; ++ ltq_w32(NAND_WRITE_CMD_RESET, ++ ((u32 *) (NAND_BASE_ADDRESS | NAND_WRITE_CMD))); ++ break; ++ default: ++ BUG(); ++ } ++} ++ ++static void xway_cmd_ctrl(struct mtd_info *mtd, int data, unsigned int ctrl) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ if (ctrl & NAND_CLE) ++ this->IO_ADDR_W = (void __iomem *) ++ (NAND_BASE_ADDRESS | NAND_WRITE_CMD); ++ else if (ctrl & NAND_ALE) ++ this->IO_ADDR_W = (void __iomem *) ++ (NAND_BASE_ADDRESS | NAND_WRITE_ADDR); ++ } ++ ++ if (data != NAND_CMD_NONE) { ++ *(volatile u8*) ((u32) this->IO_ADDR_W) = data; ++ while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) ++ ; ++ } ++} ++ ++static int xway_dev_ready(struct mtd_info *mtd) ++{ ++ return ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_RD; ++} ++ ++void nand_write(unsigned int addr, unsigned int val) ++{ ++ ltq_w32(val, ((u32 *) (NAND_BASE_ADDRESS | addr))); ++ while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) ++ ; ++} ++ ++unsigned char xway_read_byte(struct mtd_info *mtd) ++{ ++ return ltq_r8((void __iomem *)(NAND_BASE_ADDRESS | (NAND_READ_DATA))); ++} ++ ++static void xway_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ { ++ unsigned char res8 = ltq_r8((void __iomem *)(NAND_BASE_ADDRESS | (NAND_READ_DATA))); ++ buf[i] = res8; ++ } ++} ++ ++static void xway_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) ++{ ++ int i; ++ ++ for (i = 0; i < len; i++) ++ { ++ ltq_w8(buf[i], ((u32*)(NAND_BASE_ADDRESS | (NAND_WRITE_DATA)))); ++ while((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0); ++ } ++} ++ ++int xway_probe(struct platform_device *pdev) ++{ ++ /* might need this later ? ++ ltq_gpio_request(PIN_CS1, 2, 1, "NAND_CS1"); ++ */ ++ ltq_gpio_request(&pdev->dev, PIN_CLE, 2, 1, "NAND_CLE"); ++ ltq_gpio_request(&pdev->dev, PIN_ALE, 2, 1, "NAND_ALE"); ++ if (ltq_is_ar9() || ltq_is_vr9()) { ++ ltq_gpio_request(&pdev->dev, PIN_RDY, 2, 0, "NAND_BSY"); ++ ltq_gpio_request(&pdev->dev, PIN_RD, 2, 1, "NAND_RD"); ++ } ++ ++ ltq_ebu_w32((NAND_BASE_ADDRESS & 0x1fffff00) ++ | ADDSEL1_MASK(3) | ADDSEL1_REGEN, LTQ_EBU_ADDSEL1); ++ ++ ltq_ebu_w32(BUSCON1_SETUP | BUSCON1_BCGEN_RES | BUSCON1_WAITWRC2 ++ | BUSCON1_WAITRDC2 | BUSCON1_HOLDC1 | BUSCON1_RECOVC1 ++ | BUSCON1_CMULT4, LTQ_EBU_BUSCON1); ++ ++ ltq_ebu_w32(NAND_CON_NANDM | NAND_CON_CSMUX | NAND_CON_CS_P ++ | NAND_CON_SE_P | NAND_CON_WP_P | NAND_CON_PRE_P ++ | NAND_CON_IN_CS0 | NAND_CON_OUT_CS0, LTQ_EBU_NAND_CON); ++ ++ ltq_w32(NAND_WRITE_CMD_RESET, ++ ((u32 *) (NAND_BASE_ADDRESS | NAND_WRITE_CMD))); ++ while ((ltq_ebu_r32(LTQ_EBU_NAND_WAIT) & NAND_WAIT_WR_C) == 0) ++ ; ++ ++ return 0; ++} ++ ++static struct platform_nand_data falcon_flash_nand_data = { ++ .chip = { ++ .nr_chips = 1, ++ .chip_delay = 30, ++ .part_probe_types = part_probes, ++ }, ++ .ctrl = { ++ .probe = xway_probe, ++ .cmd_ctrl = xway_cmd_ctrl, ++ .dev_ready = xway_dev_ready, ++ .select_chip = xway_select_chip, ++ .read_byte = xway_read_byte, ++ .read_buf = xway_read_buf, ++ .write_buf = xway_write_buf, ++ } ++}; ++ ++static struct resource ltq_nand_res = ++ MEM_RES("nand", 0x14000000, 0x7ffffff); ++ ++static struct platform_device ltq_flash_nand = { ++ .name = "gen_nand", ++ .id = -1, ++ .num_resources = 1, ++ .resource = <q_nand_res, ++ .dev = { ++ .platform_data = &falcon_flash_nand_data, ++ }, ++}; ++ ++void __init xway_register_nand(struct mtd_partition *parts, int count) ++{ ++ falcon_flash_nand_data.chip.partitions = parts; ++ falcon_flash_nand_data.chip.nr_partitions = count; ++ platform_device_register(<q_flash_nand); ++} +diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c +index 7f2da69..916a2ef 100644 +--- a/drivers/mtd/nand/plat_nand.c ++++ b/drivers/mtd/nand/plat_nand.c +@@ -75,6 +75,7 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) + data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.write_buf = pdata->ctrl.write_buf; + data->chip.read_buf = pdata->ctrl.read_buf; ++ data->chip.read_byte = pdata->ctrl.read_byte; + data->chip.chip_delay = pdata->chip.chip_delay; + data->chip.options |= pdata->chip.options; + data->chip.bbt_options |= pdata->chip.bbt_options; +diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h +index 63b5a8b..597f079 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -651,6 +651,7 @@ struct platform_nand_ctrl { + void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); + void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len); ++ unsigned char (*read_byte)(struct mtd_info *mtd); + void *priv; + }; + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0038-SPI-MIPS-lantiq-adds-spi-xway.patch b/target/linux/lantiq/patches-3.3/0038-SPI-MIPS-lantiq-adds-spi-xway.patch new file mode 100644 index 0000000000..df0e185ead --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0038-SPI-MIPS-lantiq-adds-spi-xway.patch @@ -0,0 +1,1151 @@ +From 3e3dd3612431578cac22ed73064a38c0e30c5284 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 10 Oct 2011 22:29:13 +0200 +Subject: [PATCH 38/70] SPI: MIPS: lantiq: adds spi xway + +--- + .../mips/include/asm/mach-lantiq/lantiq_platform.h | 9 + + .../mips/include/asm/mach-lantiq/xway/lantiq_irq.h | 2 + + drivers/spi/Kconfig | 8 + + drivers/spi/Makefile | 1 + + drivers/spi/spi-xway.c | 1068 ++++++++++++++++++++ + 5 files changed, 1088 insertions(+), 0 deletions(-) + create mode 100644 drivers/spi/spi-xway.c + +diff --git a/arch/mips/include/asm/mach-lantiq/lantiq_platform.h b/arch/mips/include/asm/mach-lantiq/lantiq_platform.h +index a305f1d..38ed938 100644 +--- a/arch/mips/include/asm/mach-lantiq/lantiq_platform.h ++++ b/arch/mips/include/asm/mach-lantiq/lantiq_platform.h +@@ -50,4 +50,13 @@ struct ltq_eth_data { + int mii_mode; + }; + ++ ++struct ltq_spi_platform_data { ++ u16 num_chipselect; ++}; ++ ++struct ltq_spi_controller_data { ++ unsigned gpio; ++}; ++ + #endif +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +index 2a8d5ad..b7f10e6 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +@@ -27,6 +27,8 @@ + + #define LTQ_SSC_TIR (INT_NUM_IM0_IRL0 + 15) + #define LTQ_SSC_RIR (INT_NUM_IM0_IRL0 + 14) ++#define LTQ_SSC_TIR_AR9 (INT_NUM_IM0_IRL0 + 14) ++#define LTQ_SSC_RIR_AR9 (INT_NUM_IM0_IRL0 + 15) + #define LTQ_SSC_EIR (INT_NUM_IM0_IRL0 + 16) + + #define LTQ_MEI_DYING_GASP_INT (INT_NUM_IM1_IRL0 + 21) +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index 41f8336..f0eaeeb 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -382,6 +382,14 @@ config SPI_NUC900 + help + SPI driver for Nuvoton NUC900 series ARM SoCs + ++config SPI_XWAY ++ tristate "Lantiq XWAY SPI controller" ++ depends on LANTIQ && SOC_TYPE_XWAY ++ select SPI_BITBANG ++ help ++ This driver supports the Lantiq SoC SPI controller in master ++ mode. ++ + # + # Add new SPI master controllers in alphabetical order above this line + # +diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile +index 570894c..a465d9a 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -59,4 +59,5 @@ obj-$(CONFIG_SPI_TLE62X0) += spi-tle62x0.o + obj-$(CONFIG_SPI_TOPCLIFF_PCH) += spi-topcliff-pch.o + obj-$(CONFIG_SPI_TXX9) += spi-txx9.o + obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o ++obj-$(CONFIG_SPI_XWAY) += spi-xway.o + +diff --git a/drivers/spi/spi-xway.c b/drivers/spi/spi-xway.c +new file mode 100644 +index 0000000..016a6d0 +--- /dev/null ++++ b/drivers/spi/spi-xway.c +@@ -0,0 +1,1068 @@ ++/* ++ * Lantiq SoC SPI controller ++ * ++ * Copyright (C) 2011 Daniel Schwierzeck ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#define LTQ_SPI_CLC 0x00 /* Clock control */ ++#define LTQ_SPI_PISEL 0x04 /* Port input select */ ++#define LTQ_SPI_ID 0x08 /* Identification */ ++#define LTQ_SPI_CON 0x10 /* Control */ ++#define LTQ_SPI_STAT 0x14 /* Status */ ++#define LTQ_SPI_WHBSTATE 0x18 /* Write HW modified state */ ++#define LTQ_SPI_TB 0x20 /* Transmit buffer */ ++#define LTQ_SPI_RB 0x24 /* Receive buffer */ ++#define LTQ_SPI_RXFCON 0x30 /* Receive FIFO control */ ++#define LTQ_SPI_TXFCON 0x34 /* Transmit FIFO control */ ++#define LTQ_SPI_FSTAT 0x38 /* FIFO status */ ++#define LTQ_SPI_BRT 0x40 /* Baudrate timer */ ++#define LTQ_SPI_BRSTAT 0x44 /* Baudrate timer status */ ++#define LTQ_SPI_SFCON 0x60 /* Serial frame control */ ++#define LTQ_SPI_SFSTAT 0x64 /* Serial frame status */ ++#define LTQ_SPI_GPOCON 0x70 /* General purpose output control */ ++#define LTQ_SPI_GPOSTAT 0x74 /* General purpose output status */ ++#define LTQ_SPI_FGPO 0x78 /* Forced general purpose output */ ++#define LTQ_SPI_RXREQ 0x80 /* Receive request */ ++#define LTQ_SPI_RXCNT 0x84 /* Receive count */ ++#define LTQ_SPI_DMACON 0xEC /* DMA control */ ++#define LTQ_SPI_IRNEN 0xF4 /* Interrupt node enable */ ++#define LTQ_SPI_IRNICR 0xF8 /* Interrupt node interrupt capture */ ++#define LTQ_SPI_IRNCR 0xFC /* Interrupt node control */ ++ ++#define LTQ_SPI_CLC_SMC_SHIFT 16 /* Clock divider for sleep mode */ ++#define LTQ_SPI_CLC_SMC_MASK 0xFF ++#define LTQ_SPI_CLC_RMC_SHIFT 8 /* Clock divider for normal run mode */ ++#define LTQ_SPI_CLC_RMC_MASK 0xFF ++#define LTQ_SPI_CLC_DISS BIT(1) /* Disable status bit */ ++#define LTQ_SPI_CLC_DISR BIT(0) /* Disable request bit */ ++ ++#define LTQ_SPI_ID_TXFS_SHIFT 24 /* Implemented TX FIFO size */ ++#define LTQ_SPI_ID_TXFS_MASK 0x3F ++#define LTQ_SPI_ID_RXFS_SHIFT 16 /* Implemented RX FIFO size */ ++#define LTQ_SPI_ID_RXFS_MASK 0x3F ++#define LTQ_SPI_ID_REV_MASK 0x1F /* Hardware revision number */ ++#define LTQ_SPI_ID_CFG BIT(5) /* DMA interface support */ ++ ++#define LTQ_SPI_CON_BM_SHIFT 16 /* Data width selection */ ++#define LTQ_SPI_CON_BM_MASK 0x1F ++#define LTQ_SPI_CON_EM BIT(24) /* Echo mode */ ++#define LTQ_SPI_CON_IDLE BIT(23) /* Idle bit value */ ++#define LTQ_SPI_CON_ENBV BIT(22) /* Enable byte valid control */ ++#define LTQ_SPI_CON_RUEN BIT(12) /* Receive underflow error enable */ ++#define LTQ_SPI_CON_TUEN BIT(11) /* Transmit underflow error enable */ ++#define LTQ_SPI_CON_AEN BIT(10) /* Abort error enable */ ++#define LTQ_SPI_CON_REN BIT(9) /* Receive overflow error enable */ ++#define LTQ_SPI_CON_TEN BIT(8) /* Transmit overflow error enable */ ++#define LTQ_SPI_CON_LB BIT(7) /* Loopback control */ ++#define LTQ_SPI_CON_PO BIT(6) /* Clock polarity control */ ++#define LTQ_SPI_CON_PH BIT(5) /* Clock phase control */ ++#define LTQ_SPI_CON_HB BIT(4) /* Heading control */ ++#define LTQ_SPI_CON_RXOFF BIT(1) /* Switch receiver off */ ++#define LTQ_SPI_CON_TXOFF BIT(0) /* Switch transmitter off */ ++ ++#define LTQ_SPI_STAT_RXBV_MASK 0x7 ++#define LTQ_SPI_STAT_RXBV_SHIFT 28 ++#define LTQ_SPI_STAT_BSY BIT(13) /* Busy flag */ ++#define LTQ_SPI_STAT_RUE BIT(12) /* Receive underflow error flag */ ++#define LTQ_SPI_STAT_TUE BIT(11) /* Transmit underflow error flag */ ++#define LTQ_SPI_STAT_AE BIT(10) /* Abort error flag */ ++#define LTQ_SPI_STAT_RE BIT(9) /* Receive error flag */ ++#define LTQ_SPI_STAT_TE BIT(8) /* Transmit error flag */ ++#define LTQ_SPI_STAT_MS BIT(1) /* Master/slave select bit */ ++#define LTQ_SPI_STAT_EN BIT(0) /* Enable bit */ ++ ++#define LTQ_SPI_WHBSTATE_SETTUE BIT(15) /* Set transmit underflow error flag */ ++#define LTQ_SPI_WHBSTATE_SETAE BIT(14) /* Set abort error flag */ ++#define LTQ_SPI_WHBSTATE_SETRE BIT(13) /* Set receive error flag */ ++#define LTQ_SPI_WHBSTATE_SETTE BIT(12) /* Set transmit error flag */ ++#define LTQ_SPI_WHBSTATE_CLRTUE BIT(11) /* Clear transmit underflow error flag */ ++#define LTQ_SPI_WHBSTATE_CLRAE BIT(10) /* Clear abort error flag */ ++#define LTQ_SPI_WHBSTATE_CLRRE BIT(9) /* Clear receive error flag */ ++#define LTQ_SPI_WHBSTATE_CLRTE BIT(8) /* Clear transmit error flag */ ++#define LTQ_SPI_WHBSTATE_SETME BIT(7) /* Set mode error flag */ ++#define LTQ_SPI_WHBSTATE_CLRME BIT(6) /* Clear mode error flag */ ++#define LTQ_SPI_WHBSTATE_SETRUE BIT(5) /* Set receive underflow error flag */ ++#define LTQ_SPI_WHBSTATE_CLRRUE BIT(4) /* Clear receive underflow error flag */ ++#define LTQ_SPI_WHBSTATE_SETMS BIT(3) /* Set master select bit */ ++#define LTQ_SPI_WHBSTATE_CLRMS BIT(2) /* Clear master select bit */ ++#define LTQ_SPI_WHBSTATE_SETEN BIT(1) /* Set enable bit (operational mode) */ ++#define LTQ_SPI_WHBSTATE_CLREN BIT(0) /* Clear enable bit (config mode */ ++#define LTQ_SPI_WHBSTATE_CLR_ERRORS 0x0F50 ++ ++#define LTQ_SPI_RXFCON_RXFITL_SHIFT 8 /* FIFO interrupt trigger level */ ++#define LTQ_SPI_RXFCON_RXFITL_MASK 0x3F ++#define LTQ_SPI_RXFCON_RXFLU BIT(1) /* FIFO flush */ ++#define LTQ_SPI_RXFCON_RXFEN BIT(0) /* FIFO enable */ ++ ++#define LTQ_SPI_TXFCON_TXFITL_SHIFT 8 /* FIFO interrupt trigger level */ ++#define LTQ_SPI_TXFCON_TXFITL_MASK 0x3F ++#define LTQ_SPI_TXFCON_TXFLU BIT(1) /* FIFO flush */ ++#define LTQ_SPI_TXFCON_TXFEN BIT(0) /* FIFO enable */ ++ ++#define LTQ_SPI_FSTAT_RXFFL_MASK 0x3f ++#define LTQ_SPI_FSTAT_RXFFL_SHIFT 0 ++#define LTQ_SPI_FSTAT_TXFFL_MASK 0x3f ++#define LTQ_SPI_FSTAT_TXFFL_SHIFT 8 ++ ++#define LTQ_SPI_GPOCON_ISCSBN_SHIFT 8 ++#define LTQ_SPI_GPOCON_INVOUTN_SHIFT 0 ++ ++#define LTQ_SPI_FGPO_SETOUTN_SHIFT 8 ++#define LTQ_SPI_FGPO_CLROUTN_SHIFT 0 ++ ++#define LTQ_SPI_RXREQ_RXCNT_MASK 0xFFFF /* Receive count value */ ++#define LTQ_SPI_RXCNT_TODO_MASK 0xFFFF /* Recevie to-do value */ ++ ++#define LTQ_SPI_IRNEN_F BIT(3) /* Frame end interrupt request */ ++#define LTQ_SPI_IRNEN_E BIT(2) /* Error end interrupt request */ ++#define LTQ_SPI_IRNEN_T BIT(1) /* Transmit end interrupt request */ ++#define LTQ_SPI_IRNEN_R BIT(0) /* Receive end interrupt request */ ++#define LTQ_SPI_IRNEN_ALL 0xF ++ ++/* Hard-wired GPIOs used by SPI controller */ ++#define LTQ_SPI_GPIO_DI 16 ++#define LTQ_SPI_GPIO_DO 17 ++#define LTQ_SPI_GPIO_CLK 18 ++ ++struct ltq_spi { ++ struct spi_bitbang bitbang; ++ struct completion done; ++ spinlock_t lock; ++ ++ struct device *dev; ++ void __iomem *base; ++ struct clk *fpiclk; ++ struct clk *spiclk; ++ ++ int status; ++ int irq[3]; ++ ++ const u8 *tx; ++ u8 *rx; ++ u32 tx_cnt; ++ u32 rx_cnt; ++ u32 len; ++ struct spi_transfer *curr_transfer; ++ ++ u32 (*get_tx) (struct ltq_spi *); ++ ++ u16 txfs; ++ u16 rxfs; ++ unsigned dma_support:1; ++ unsigned cfg_mode:1; ++ ++}; ++ ++struct ltq_spi_controller_state { ++ void (*cs_activate) (struct spi_device *); ++ void (*cs_deactivate) (struct spi_device *); ++}; ++ ++struct ltq_spi_irq_map { ++ char *name; ++ irq_handler_t handler; ++}; ++ ++struct ltq_spi_cs_gpio_map { ++ unsigned gpio; ++ unsigned mux; ++}; ++ ++static inline struct ltq_spi *ltq_spi_to_hw(struct spi_device *spi) ++{ ++ return spi_master_get_devdata(spi->master); ++} ++ ++static inline u32 ltq_spi_reg_read(struct ltq_spi *hw, u32 reg) ++{ ++ return ioread32be(hw->base + reg); ++} ++ ++static inline void ltq_spi_reg_write(struct ltq_spi *hw, u32 val, u32 reg) ++{ ++ iowrite32be(val, hw->base + reg); ++} ++ ++static inline void ltq_spi_reg_setbit(struct ltq_spi *hw, u32 bits, u32 reg) ++{ ++ u32 val; ++ ++ val = ltq_spi_reg_read(hw, reg); ++ val |= bits; ++ ltq_spi_reg_write(hw, val, reg); ++} ++ ++static inline void ltq_spi_reg_clearbit(struct ltq_spi *hw, u32 bits, u32 reg) ++{ ++ u32 val; ++ ++ val = ltq_spi_reg_read(hw, reg); ++ val &= ~bits; ++ ltq_spi_reg_write(hw, val, reg); ++} ++ ++static void ltq_spi_hw_enable(struct ltq_spi *hw) ++{ ++ u32 clc; ++ ++ /* Power-up mdule */ ++ clk_enable(hw->spiclk); ++ ++ /* ++ * Set clock divider for run mode to 1 to ++ * run at same frequency as FPI bus ++ */ ++ clc = (1 << LTQ_SPI_CLC_RMC_SHIFT); ++ ltq_spi_reg_write(hw, clc, LTQ_SPI_CLC); ++} ++ ++static void ltq_spi_hw_disable(struct ltq_spi *hw) ++{ ++ /* Set clock divider to 0 and set module disable bit */ ++ ltq_spi_reg_write(hw, LTQ_SPI_CLC_DISS, LTQ_SPI_CLC); ++ ++ /* Power-down mdule */ ++ clk_disable(hw->spiclk); ++} ++ ++static void ltq_spi_reset_fifos(struct ltq_spi *hw) ++{ ++ u32 val; ++ ++ /* ++ * Enable and flush FIFOs. Set interrupt trigger level to ++ * half of FIFO count implemented in hardware. ++ */ ++ if (hw->txfs > 1) { ++ val = hw->txfs << (LTQ_SPI_TXFCON_TXFITL_SHIFT - 1); ++ val |= LTQ_SPI_TXFCON_TXFEN | LTQ_SPI_TXFCON_TXFLU; ++ ltq_spi_reg_write(hw, val, LTQ_SPI_TXFCON); ++ } ++ ++ if (hw->rxfs > 1) { ++ val = hw->rxfs << (LTQ_SPI_RXFCON_RXFITL_SHIFT - 1); ++ val |= LTQ_SPI_RXFCON_RXFEN | LTQ_SPI_RXFCON_RXFLU; ++ ltq_spi_reg_write(hw, val, LTQ_SPI_RXFCON); ++ } ++} ++ ++static inline int ltq_spi_wait_ready(struct ltq_spi *hw) ++{ ++ u32 stat; ++ unsigned long timeout; ++ ++ timeout = jiffies + msecs_to_jiffies(200); ++ ++ do { ++ stat = ltq_spi_reg_read(hw, LTQ_SPI_STAT); ++ if (!(stat & LTQ_SPI_STAT_BSY)) ++ return 0; ++ ++ cond_resched(); ++ } while (!time_after_eq(jiffies, timeout)); ++ ++ dev_err(hw->dev, "SPI wait ready timed out\n"); ++ ++ return -ETIMEDOUT; ++} ++ ++static void ltq_spi_config_mode_set(struct ltq_spi *hw) ++{ ++ if (hw->cfg_mode) ++ return; ++ ++ /* ++ * Putting the SPI module in config mode is only safe if no ++ * transfer is in progress as indicated by busy flag STATE.BSY. ++ */ ++ if (ltq_spi_wait_ready(hw)) { ++ ltq_spi_reset_fifos(hw); ++ hw->status = -ETIMEDOUT; ++ } ++ ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_CLREN, LTQ_SPI_WHBSTATE); ++ ++ hw->cfg_mode = 1; ++} ++ ++static void ltq_spi_run_mode_set(struct ltq_spi *hw) ++{ ++ if (!hw->cfg_mode) ++ return; ++ ++ ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_SETEN, LTQ_SPI_WHBSTATE); ++ ++ hw->cfg_mode = 0; ++} ++ ++static u32 ltq_spi_tx_word_u8(struct ltq_spi *hw) ++{ ++ const u8 *tx = hw->tx; ++ u32 data = *tx++; ++ ++ hw->tx_cnt++; ++ hw->tx++; ++ ++ return data; ++} ++ ++static u32 ltq_spi_tx_word_u16(struct ltq_spi *hw) ++{ ++ const u16 *tx = (u16 *) hw->tx; ++ u32 data = *tx++; ++ ++ hw->tx_cnt += 2; ++ hw->tx += 2; ++ ++ return data; ++} ++ ++static u32 ltq_spi_tx_word_u32(struct ltq_spi *hw) ++{ ++ const u32 *tx = (u32 *) hw->tx; ++ u32 data = *tx++; ++ ++ hw->tx_cnt += 4; ++ hw->tx += 4; ++ ++ return data; ++} ++ ++static void ltq_spi_bits_per_word_set(struct spi_device *spi) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u32 bm; ++ u8 bits_per_word = spi->bits_per_word; ++ ++ /* ++ * Use either default value of SPI device or value ++ * from current transfer. ++ */ ++ if (hw->curr_transfer && hw->curr_transfer->bits_per_word) ++ bits_per_word = hw->curr_transfer->bits_per_word; ++ ++ if (bits_per_word <= 8) ++ hw->get_tx = ltq_spi_tx_word_u8; ++ else if (bits_per_word <= 16) ++ hw->get_tx = ltq_spi_tx_word_u16; ++ else if (bits_per_word <= 32) ++ hw->get_tx = ltq_spi_tx_word_u32; ++ ++ /* CON.BM value = bits_per_word - 1 */ ++ bm = (bits_per_word - 1) << LTQ_SPI_CON_BM_SHIFT; ++ ++ ltq_spi_reg_clearbit(hw, LTQ_SPI_CON_BM_MASK << ++ LTQ_SPI_CON_BM_SHIFT, LTQ_SPI_CON); ++ ltq_spi_reg_setbit(hw, bm, LTQ_SPI_CON); ++} ++ ++static void ltq_spi_speed_set(struct spi_device *spi) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u32 br, max_speed_hz, spi_clk; ++ u32 speed_hz = spi->max_speed_hz; ++ ++ /* ++ * Use either default value of SPI device or value ++ * from current transfer. ++ */ ++ if (hw->curr_transfer && hw->curr_transfer->speed_hz) ++ speed_hz = hw->curr_transfer->speed_hz; ++ ++ /* ++ * SPI module clock is derived from FPI bus clock dependent on ++ * divider value in CLC.RMS which is always set to 1. ++ */ ++ spi_clk = clk_get_rate(hw->fpiclk); ++ ++ /* ++ * Maximum SPI clock frequency in master mode is half of ++ * SPI module clock frequency. Maximum reload value of ++ * baudrate generator BR is 2^16. ++ */ ++ max_speed_hz = spi_clk / 2; ++ if (speed_hz >= max_speed_hz) ++ br = 0; ++ else ++ br = (max_speed_hz / speed_hz) - 1; ++ ++ if (br > 0xFFFF) ++ br = 0xFFFF; ++ ++ ltq_spi_reg_write(hw, br, LTQ_SPI_BRT); ++} ++ ++static void ltq_spi_clockmode_set(struct spi_device *spi) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u32 con; ++ ++ con = ltq_spi_reg_read(hw, LTQ_SPI_CON); ++ ++ /* ++ * SPI mode mapping in CON register: ++ * Mode CPOL CPHA CON.PO CON.PH ++ * 0 0 0 0 1 ++ * 1 0 1 0 0 ++ * 2 1 0 1 1 ++ * 3 1 1 1 0 ++ */ ++ if (spi->mode & SPI_CPHA) ++ con &= ~LTQ_SPI_CON_PH; ++ else ++ con |= LTQ_SPI_CON_PH; ++ ++ if (spi->mode & SPI_CPOL) ++ con |= LTQ_SPI_CON_PO; ++ else ++ con &= ~LTQ_SPI_CON_PO; ++ ++ /* Set heading control */ ++ if (spi->mode & SPI_LSB_FIRST) ++ con &= ~LTQ_SPI_CON_HB; ++ else ++ con |= LTQ_SPI_CON_HB; ++ ++ ltq_spi_reg_write(hw, con, LTQ_SPI_CON); ++} ++ ++static void ltq_spi_xmit_set(struct ltq_spi *hw, struct spi_transfer *t) ++{ ++ u32 con; ++ ++ con = ltq_spi_reg_read(hw, LTQ_SPI_CON); ++ ++ if (t) { ++ if (t->tx_buf && t->rx_buf) { ++ con &= ~(LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF); ++ } else if (t->rx_buf) { ++ con &= ~LTQ_SPI_CON_RXOFF; ++ con |= LTQ_SPI_CON_TXOFF; ++ } else if (t->tx_buf) { ++ con &= ~LTQ_SPI_CON_TXOFF; ++ con |= LTQ_SPI_CON_RXOFF; ++ } ++ } else ++ con |= (LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF); ++ ++ ltq_spi_reg_write(hw, con, LTQ_SPI_CON); ++} ++ ++static void ltq_spi_gpio_cs_activate(struct spi_device *spi) ++{ ++ struct ltq_spi_controller_data *cdata = spi->controller_data; ++ int val = spi->mode & SPI_CS_HIGH ? 1 : 0; ++ ++ gpio_set_value(cdata->gpio, val); ++} ++ ++static void ltq_spi_gpio_cs_deactivate(struct spi_device *spi) ++{ ++ struct ltq_spi_controller_data *cdata = spi->controller_data; ++ int val = spi->mode & SPI_CS_HIGH ? 0 : 1; ++ ++ gpio_set_value(cdata->gpio, val); ++} ++ ++static void ltq_spi_internal_cs_activate(struct spi_device *spi) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u32 fgpo; ++ ++ fgpo = (1 << (spi->chip_select + LTQ_SPI_FGPO_CLROUTN_SHIFT)); ++ ltq_spi_reg_setbit(hw, fgpo, LTQ_SPI_FGPO); ++} ++ ++static void ltq_spi_internal_cs_deactivate(struct spi_device *spi) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u32 fgpo; ++ ++ fgpo = (1 << (spi->chip_select + LTQ_SPI_FGPO_SETOUTN_SHIFT)); ++ ltq_spi_reg_setbit(hw, fgpo, LTQ_SPI_FGPO); ++} ++ ++static void ltq_spi_chipselect(struct spi_device *spi, int cs) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ struct ltq_spi_controller_state *cstate = spi->controller_state; ++ ++ switch (cs) { ++ case BITBANG_CS_ACTIVE: ++ ltq_spi_bits_per_word_set(spi); ++ ltq_spi_speed_set(spi); ++ ltq_spi_clockmode_set(spi); ++ ltq_spi_run_mode_set(hw); ++ ++ cstate->cs_activate(spi); ++ break; ++ ++ case BITBANG_CS_INACTIVE: ++ cstate->cs_deactivate(spi); ++ ++ ltq_spi_config_mode_set(hw); ++ ++ break; ++ } ++} ++ ++static int ltq_spi_setup_transfer(struct spi_device *spi, ++ struct spi_transfer *t) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u8 bits_per_word = spi->bits_per_word; ++ ++ hw->curr_transfer = t; ++ ++ if (t && t->bits_per_word) ++ bits_per_word = t->bits_per_word; ++ ++ if (bits_per_word > 32) ++ return -EINVAL; ++ ++ ltq_spi_config_mode_set(hw); ++ ++ return 0; ++} ++ ++static const struct ltq_spi_cs_gpio_map ltq_spi_cs[] = { ++ { 15, 2 }, ++ { 22, 2 }, ++ { 13, 1 }, ++ { 10, 1 }, ++ { 9, 1 }, ++ { 11, 3 }, ++}; ++ ++static int ltq_spi_setup(struct spi_device *spi) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ struct ltq_spi_controller_data *cdata = spi->controller_data; ++ struct ltq_spi_controller_state *cstate; ++ u32 gpocon, fgpo; ++ int ret; ++ ++ /* Set default word length to 8 if not set */ ++ if (!spi->bits_per_word) ++ spi->bits_per_word = 8; ++ ++ if (spi->bits_per_word > 32) ++ return -EINVAL; ++ ++ if (!spi->controller_state) { ++ cstate = kzalloc(sizeof(struct ltq_spi_controller_state), ++ GFP_KERNEL); ++ if (!cstate) ++ return -ENOMEM; ++ ++ spi->controller_state = cstate; ++ } else ++ return 0; ++ ++ /* ++ * Up to six GPIOs can be connected to the SPI module ++ * via GPIO alternate function to control the chip select lines. ++ * For more flexibility in board layout this driver can also control ++ * the CS lines via GPIO API. If GPIOs should be used, board setup code ++ * have to register the SPI device with struct ltq_spi_controller_data ++ * attached. ++ */ ++ if (cdata && cdata->gpio) { ++ ret = gpio_request(cdata->gpio, "spi-cs"); ++ if (ret) ++ return -EBUSY; ++ ++ ret = spi->mode & SPI_CS_HIGH ? 0 : 1; ++ gpio_direction_output(cdata->gpio, ret); ++ ++ cstate->cs_activate = ltq_spi_gpio_cs_activate; ++ cstate->cs_deactivate = ltq_spi_gpio_cs_deactivate; ++ } else { ++ ret = ltq_gpio_request(&spi->dev, ltq_spi_cs[spi->chip_select].gpio, ++ ltq_spi_cs[spi->chip_select].mux, ++ 1, "spi-cs"); ++ if (ret) ++ return -EBUSY; ++ ++ gpocon = (1 << (spi->chip_select + ++ LTQ_SPI_GPOCON_ISCSBN_SHIFT)); ++ ++ if (spi->mode & SPI_CS_HIGH) ++ gpocon |= (1 << spi->chip_select); ++ ++ fgpo = (1 << (spi->chip_select + LTQ_SPI_FGPO_SETOUTN_SHIFT)); ++ ++ ltq_spi_reg_setbit(hw, gpocon, LTQ_SPI_GPOCON); ++ ltq_spi_reg_setbit(hw, fgpo, LTQ_SPI_FGPO); ++ ++ cstate->cs_activate = ltq_spi_internal_cs_activate; ++ cstate->cs_deactivate = ltq_spi_internal_cs_deactivate; ++ } ++ ++ return 0; ++} ++ ++static void ltq_spi_cleanup(struct spi_device *spi) ++{ ++ struct ltq_spi_controller_data *cdata = spi->controller_data; ++ struct ltq_spi_controller_state *cstate = spi->controller_state; ++ unsigned gpio; ++ ++ if (cdata && cdata->gpio) ++ gpio = cdata->gpio; ++ else ++ gpio = ltq_spi_cs[spi->chip_select].gpio; ++ ++ gpio_free(gpio); ++ kfree(cstate); ++} ++ ++static void ltq_spi_txfifo_write(struct ltq_spi *hw) ++{ ++ u32 fstat, data; ++ u16 fifo_space; ++ ++ /* Determine how much FIFOs are free for TX data */ ++ fstat = ltq_spi_reg_read(hw, LTQ_SPI_FSTAT); ++ fifo_space = hw->txfs - ((fstat >> LTQ_SPI_FSTAT_TXFFL_SHIFT) & ++ LTQ_SPI_FSTAT_TXFFL_MASK); ++ ++ if (!fifo_space) ++ return; ++ ++ while (hw->tx_cnt < hw->len && fifo_space) { ++ data = hw->get_tx(hw); ++ ltq_spi_reg_write(hw, data, LTQ_SPI_TB); ++ fifo_space--; ++ } ++} ++ ++static void ltq_spi_rxfifo_read(struct ltq_spi *hw) ++{ ++ u32 fstat, data, *rx32; ++ u16 fifo_fill; ++ u8 rxbv, shift, *rx8; ++ ++ /* Determine how much FIFOs are filled with RX data */ ++ fstat = ltq_spi_reg_read(hw, LTQ_SPI_FSTAT); ++ fifo_fill = ((fstat >> LTQ_SPI_FSTAT_RXFFL_SHIFT) ++ & LTQ_SPI_FSTAT_RXFFL_MASK); ++ ++ if (!fifo_fill) ++ return; ++ ++ /* ++ * The 32 bit FIFO is always used completely independent from the ++ * bits_per_word value. Thus four bytes have to be read at once ++ * per FIFO. ++ */ ++ rx32 = (u32 *) hw->rx; ++ while (hw->len - hw->rx_cnt >= 4 && fifo_fill) { ++ *rx32++ = ltq_spi_reg_read(hw, LTQ_SPI_RB); ++ hw->rx_cnt += 4; ++ hw->rx += 4; ++ fifo_fill--; ++ } ++ ++ /* ++ * If there are remaining bytes, read byte count from STAT.RXBV ++ * register and read the data byte-wise. ++ */ ++ while (fifo_fill && hw->rx_cnt < hw->len) { ++ rxbv = (ltq_spi_reg_read(hw, LTQ_SPI_STAT) >> ++ LTQ_SPI_STAT_RXBV_SHIFT) & LTQ_SPI_STAT_RXBV_MASK; ++ data = ltq_spi_reg_read(hw, LTQ_SPI_RB); ++ ++ shift = (rxbv - 1) * 8; ++ rx8 = hw->rx; ++ ++ while (rxbv) { ++ *rx8++ = (data >> shift) & 0xFF; ++ rxbv--; ++ shift -= 8; ++ hw->rx_cnt++; ++ hw->rx++; ++ } ++ ++ fifo_fill--; ++ } ++} ++ ++static void ltq_spi_rxreq_set(struct ltq_spi *hw) ++{ ++ u32 rxreq, rxreq_max, rxtodo; ++ ++ rxtodo = ltq_spi_reg_read(hw, LTQ_SPI_RXCNT) & LTQ_SPI_RXCNT_TODO_MASK; ++ ++ /* ++ * In RX-only mode the serial clock is activated only after writing ++ * the expected amount of RX bytes into RXREQ register. ++ * To avoid receive overflows at high clocks it is better to request ++ * only the amount of bytes that fits into all FIFOs. This value ++ * depends on the FIFO size implemented in hardware. ++ */ ++ rxreq = hw->len - hw->rx_cnt; ++ rxreq_max = hw->rxfs << 2; ++ rxreq = min(rxreq_max, rxreq); ++ ++ if (!rxtodo && rxreq) ++ ltq_spi_reg_write(hw, rxreq, LTQ_SPI_RXREQ); ++} ++ ++static inline void ltq_spi_complete(struct ltq_spi *hw) ++{ ++ complete(&hw->done); ++} ++ ++irqreturn_t ltq_spi_tx_irq(int irq, void *data) ++{ ++ struct ltq_spi *hw = data; ++ unsigned long flags; ++ int completed = 0; ++ ++ spin_lock_irqsave(&hw->lock, flags); ++ ++ if (hw->tx_cnt < hw->len) ++ ltq_spi_txfifo_write(hw); ++ ++ if (hw->tx_cnt == hw->len) ++ completed = 1; ++ ++ spin_unlock_irqrestore(&hw->lock, flags); ++ ++ if (completed) ++ ltq_spi_complete(hw); ++ ++ return IRQ_HANDLED; ++} ++ ++irqreturn_t ltq_spi_rx_irq(int irq, void *data) ++{ ++ struct ltq_spi *hw = data; ++ unsigned long flags; ++ int completed = 0; ++ ++ spin_lock_irqsave(&hw->lock, flags); ++ ++ if (hw->rx_cnt < hw->len) { ++ ltq_spi_rxfifo_read(hw); ++ ++ if (hw->tx && hw->tx_cnt < hw->len) ++ ltq_spi_txfifo_write(hw); ++ } ++ ++ if (hw->rx_cnt == hw->len) ++ completed = 1; ++ else if (!hw->tx) ++ ltq_spi_rxreq_set(hw); ++ ++ spin_unlock_irqrestore(&hw->lock, flags); ++ ++ if (completed) ++ ltq_spi_complete(hw); ++ ++ return IRQ_HANDLED; ++} ++ ++irqreturn_t ltq_spi_err_irq(int irq, void *data) ++{ ++ struct ltq_spi *hw = data; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&hw->lock, flags); ++ ++ /* Disable all interrupts */ ++ ltq_spi_reg_clearbit(hw, LTQ_SPI_IRNEN_ALL, LTQ_SPI_IRNEN); ++ ++ /* Clear all error flags */ ++ ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_CLR_ERRORS, LTQ_SPI_WHBSTATE); ++ ++ /* Flush FIFOs */ ++ ltq_spi_reg_setbit(hw, LTQ_SPI_RXFCON_RXFLU, LTQ_SPI_RXFCON); ++ ltq_spi_reg_setbit(hw, LTQ_SPI_TXFCON_TXFLU, LTQ_SPI_TXFCON); ++ ++ hw->status = -EIO; ++ spin_unlock_irqrestore(&hw->lock, flags); ++ ++ ltq_spi_complete(hw); ++ ++ return IRQ_HANDLED; ++} ++ ++static int ltq_spi_txrx_bufs(struct spi_device *spi, struct spi_transfer *t) ++{ ++ struct ltq_spi *hw = ltq_spi_to_hw(spi); ++ u32 irq_flags = 0; ++ ++ hw->tx = t->tx_buf; ++ hw->rx = t->rx_buf; ++ hw->len = t->len; ++ hw->tx_cnt = 0; ++ hw->rx_cnt = 0; ++ hw->status = 0; ++ INIT_COMPLETION(hw->done); ++ ++ ltq_spi_xmit_set(hw, t); ++ ++ /* Enable error interrupts */ ++ ltq_spi_reg_setbit(hw, LTQ_SPI_IRNEN_E, LTQ_SPI_IRNEN); ++ ++ if (hw->tx) { ++ /* Initially fill TX FIFO with as much data as possible */ ++ ltq_spi_txfifo_write(hw); ++ irq_flags |= LTQ_SPI_IRNEN_T; ++ ++ /* Always enable RX interrupt in Full Duplex mode */ ++ if (hw->rx) ++ irq_flags |= LTQ_SPI_IRNEN_R; ++ } else if (hw->rx) { ++ /* Start RX clock */ ++ ltq_spi_rxreq_set(hw); ++ ++ /* Enable RX interrupt to receive data from RX FIFOs */ ++ irq_flags |= LTQ_SPI_IRNEN_R; ++ } ++ ++ /* Enable TX or RX interrupts */ ++ ltq_spi_reg_setbit(hw, irq_flags, LTQ_SPI_IRNEN); ++ wait_for_completion_interruptible(&hw->done); ++ ++ /* Disable all interrupts */ ++ ltq_spi_reg_clearbit(hw, LTQ_SPI_IRNEN_ALL, LTQ_SPI_IRNEN); ++ ++ /* ++ * Return length of current transfer for bitbang utility code if ++ * no errors occured during transmission. ++ */ ++ if (!hw->status) ++ hw->status = hw->len; ++ ++ return hw->status; ++} ++ ++static const struct ltq_spi_irq_map ltq_spi_irqs[] = { ++ { "spi_tx", ltq_spi_tx_irq }, ++ { "spi_rx", ltq_spi_rx_irq }, ++ { "spi_err", ltq_spi_err_irq }, ++}; ++ ++static int __init ltq_spi_probe(struct platform_device *pdev) ++{ ++ struct spi_master *master; ++ struct resource *r; ++ struct ltq_spi *hw; ++ struct ltq_spi_platform_data *pdata = pdev->dev.platform_data; ++ int ret, i; ++ u32 data, id; ++ ++ master = spi_alloc_master(&pdev->dev, sizeof(struct ltq_spi)); ++ if (!master) { ++ dev_err(&pdev->dev, "spi_alloc_master\n"); ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ hw = spi_master_get_devdata(master); ++ ++ r = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (r == NULL) { ++ dev_err(&pdev->dev, "platform_get_resource\n"); ++ ret = -ENOENT; ++ goto err_master; ++ } ++ ++ r = devm_request_mem_region(&pdev->dev, r->start, resource_size(r), ++ pdev->name); ++ if (!r) { ++ dev_err(&pdev->dev, "devm_request_mem_region\n"); ++ ret = -ENXIO; ++ goto err_master; ++ } ++ ++ hw->base = devm_ioremap_nocache(&pdev->dev, r->start, resource_size(r)); ++ if (!hw->base) { ++ dev_err(&pdev->dev, "devm_ioremap_nocache\n"); ++ ret = -ENXIO; ++ goto err_master; ++ } ++ ++ hw->fpiclk = clk_get_fpi(); ++ if (IS_ERR(hw->fpiclk)) { ++ dev_err(&pdev->dev, "clk_get\n"); ++ ret = PTR_ERR(hw->fpiclk); ++ goto err_master; ++ } ++ ++ hw->spiclk = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(hw->spiclk)) { ++ dev_err(&pdev->dev, "clk_get\n"); ++ ret = PTR_ERR(hw->spiclk); ++ goto err_master; ++ } ++ ++ memset(hw->irq, 0, sizeof(hw->irq)); ++ for (i = 0; i < ARRAY_SIZE(ltq_spi_irqs); i++) { ++ ret = platform_get_irq_byname(pdev, ltq_spi_irqs[i].name); ++ if (0 > ret) { ++ dev_err(&pdev->dev, "platform_get_irq_byname\n"); ++ goto err_irq; ++ } ++ ++ hw->irq[i] = ret; ++ ret = request_irq(hw->irq[i], ltq_spi_irqs[i].handler, ++ 0, ltq_spi_irqs[i].name, hw); ++ if (ret) { ++ dev_err(&pdev->dev, "request_irq\n"); ++ goto err_irq; ++ } ++ } ++ ++ hw->bitbang.master = spi_master_get(master); ++ hw->bitbang.chipselect = ltq_spi_chipselect; ++ hw->bitbang.setup_transfer = ltq_spi_setup_transfer; ++ hw->bitbang.txrx_bufs = ltq_spi_txrx_bufs; ++ ++ master->bus_num = pdev->id; ++ master->num_chipselect = pdata->num_chipselect; ++ master->setup = ltq_spi_setup; ++ master->cleanup = ltq_spi_cleanup; ++ ++ hw->dev = &pdev->dev; ++ init_completion(&hw->done); ++ spin_lock_init(&hw->lock); ++ ++ /* Set GPIO alternate functions to SPI */ ++ ltq_gpio_request(&pdev->dev, LTQ_SPI_GPIO_DI, 2, 0, "spi-di"); ++ ltq_gpio_request(&pdev->dev, LTQ_SPI_GPIO_DO, 2, 1, "spi-do"); ++ ltq_gpio_request(&pdev->dev, LTQ_SPI_GPIO_CLK, 2, 1, "spi-clk"); ++ ++ ltq_spi_hw_enable(hw); ++ ++ /* Read module capabilities */ ++ id = ltq_spi_reg_read(hw, LTQ_SPI_ID); ++ hw->txfs = (id >> LTQ_SPI_ID_TXFS_SHIFT) & LTQ_SPI_ID_TXFS_MASK; ++ hw->rxfs = (id >> LTQ_SPI_ID_TXFS_SHIFT) & LTQ_SPI_ID_TXFS_MASK; ++ hw->dma_support = (id & LTQ_SPI_ID_CFG) ? 1 : 0; ++ ++ ltq_spi_config_mode_set(hw); ++ ++ /* Enable error checking, disable TX/RX, set idle value high */ ++ data = LTQ_SPI_CON_RUEN | LTQ_SPI_CON_AEN | ++ LTQ_SPI_CON_TEN | LTQ_SPI_CON_REN | ++ LTQ_SPI_CON_TXOFF | LTQ_SPI_CON_RXOFF | LTQ_SPI_CON_IDLE; ++ ltq_spi_reg_write(hw, data, LTQ_SPI_CON); ++ ++ /* Enable master mode and clear error flags */ ++ ltq_spi_reg_write(hw, LTQ_SPI_WHBSTATE_SETMS | ++ LTQ_SPI_WHBSTATE_CLR_ERRORS, LTQ_SPI_WHBSTATE); ++ ++ /* Reset GPIO/CS registers */ ++ ltq_spi_reg_write(hw, 0x0, LTQ_SPI_GPOCON); ++ ltq_spi_reg_write(hw, 0xFF00, LTQ_SPI_FGPO); ++ ++ /* Enable and flush FIFOs */ ++ ltq_spi_reset_fifos(hw); ++ ++ ret = spi_bitbang_start(&hw->bitbang); ++ if (ret) { ++ dev_err(&pdev->dev, "spi_bitbang_start\n"); ++ goto err_bitbang; ++ } ++ ++ platform_set_drvdata(pdev, hw); ++ ++ pr_info("Lantiq SoC SPI controller rev %u (TXFS %u, RXFS %u, DMA %u)\n", ++ id & LTQ_SPI_ID_REV_MASK, hw->txfs, hw->rxfs, hw->dma_support); ++ ++ return 0; ++ ++err_bitbang: ++ ltq_spi_hw_disable(hw); ++ ++err_irq: ++ clk_put(hw->fpiclk); ++ ++ for (; i > 0; i--) ++ free_irq(hw->irq[i], hw); ++ ++err_master: ++ spi_master_put(master); ++ ++err: ++ return ret; ++} ++ ++static int __exit ltq_spi_remove(struct platform_device *pdev) ++{ ++ struct ltq_spi *hw = platform_get_drvdata(pdev); ++ int ret, i; ++ ++ ret = spi_bitbang_stop(&hw->bitbang); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ ltq_spi_config_mode_set(hw); ++ ltq_spi_hw_disable(hw); ++ ++ for (i = 0; i < ARRAY_SIZE(hw->irq); i++) ++ if (0 < hw->irq[i]) ++ free_irq(hw->irq[i], hw); ++ ++ gpio_free(LTQ_SPI_GPIO_DI); ++ gpio_free(LTQ_SPI_GPIO_DO); ++ gpio_free(LTQ_SPI_GPIO_CLK); ++ ++ clk_put(hw->fpiclk); ++ spi_master_put(hw->bitbang.master); ++ ++ return 0; ++} ++ ++static struct platform_driver ltq_spi_driver = { ++ .driver = { ++ .name = "ltq_spi", ++ .owner = THIS_MODULE, ++ }, ++ .remove = __exit_p(ltq_spi_remove), ++}; ++ ++static int __init ltq_spi_init(void) ++{ ++ return platform_driver_probe(<q_spi_driver, ltq_spi_probe); ++} ++module_init(ltq_spi_init); ++ ++static void __exit ltq_spi_exit(void) ++{ ++ platform_driver_unregister(<q_spi_driver); ++} ++module_exit(ltq_spi_exit); ++ ++MODULE_DESCRIPTION("Lantiq SoC SPI controller driver"); ++MODULE_AUTHOR("Daniel Schwierzeck "); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:ltq-spi"); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0039-NET-adds-driver-for-lantiq-vr9-ethernet.patch b/target/linux/lantiq/patches-3.3/0039-NET-adds-driver-for-lantiq-vr9-ethernet.patch new file mode 100644 index 0000000000..8505db12d6 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0039-NET-adds-driver-for-lantiq-vr9-ethernet.patch @@ -0,0 +1,1470 @@ +From 239505c96aa66b4280b7726850235dcb707d2d91 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 9 Mar 2012 19:03:40 +0100 +Subject: [PATCH 39/70] NET: adds driver for lantiq vr9 ethernet + +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 2 +- + arch/mips/lantiq/xway/devices.c | 20 + + arch/mips/lantiq/xway/devices.h | 1 + + drivers/net/ethernet/Kconfig | 6 + + drivers/net/ethernet/Makefile | 1 + + drivers/net/ethernet/lantiq_vrx200.c | 1358 ++++++++++++++++++++ + 6 files changed, 1387 insertions(+), 1 deletions(-) + create mode 100644 drivers/net/ethernet/lantiq_vrx200.c + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index ab2d236..d1b8cc8 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -102,7 +102,7 @@ + + /* GBIT - gigabit switch */ + #define LTQ_GBIT_BASE_ADDR 0x1E108000 +-#define LTQ_GBIT_SIZE 0x200 ++#define LTQ_GBIT_SIZE 0x4000 + + /* DMA */ + #define LTQ_DMA_BASE_ADDR 0x1E104100 +diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c +index eab4644d..5efa4f3 100644 +--- a/arch/mips/lantiq/xway/devices.c ++++ b/arch/mips/lantiq/xway/devices.c +@@ -83,6 +83,7 @@ static struct platform_device ltq_etop = { + .name = "ltq_etop", + .resource = ltq_etop_resources, + .num_resources = 1, ++ .id = -1, + }; + + void __init +@@ -96,3 +97,22 @@ ltq_register_etop(struct ltq_eth_data *eth) + platform_device_register(<q_etop); + } + } ++ ++/* ethernet */ ++static struct resource ltq_vrx200_resources[] = { ++ MEM_RES("gbit", LTQ_GBIT_BASE_ADDR, LTQ_GBIT_SIZE), ++}; ++ ++static struct platform_device ltq_vrx200 = { ++ .name = "ltq_vrx200", ++ .resource = ltq_vrx200_resources, ++ .num_resources = 1, ++ .id = -1, ++}; ++ ++void __init ++ltq_register_vrx200(struct ltq_eth_data *eth) ++{ ++ ltq_vrx200.dev.platform_data = eth; ++ platform_device_register(<q_vrx200); ++} +diff --git a/arch/mips/lantiq/xway/devices.h b/arch/mips/lantiq/xway/devices.h +index d825cbd..08befd9 100644 +--- a/arch/mips/lantiq/xway/devices.h ++++ b/arch/mips/lantiq/xway/devices.h +@@ -17,5 +17,6 @@ extern void ltq_register_gpio_stp(void); + extern void ltq_register_ase_asc(void); + extern void ltq_register_etop(struct ltq_eth_data *eth); + extern void xway_register_nand(struct mtd_partition *parts, int count); ++extern void ltq_register_vrx200(struct ltq_eth_data *eth); + + #endif +diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig +index 3474a61..e1caa1b 100644 +--- a/drivers/net/ethernet/Kconfig ++++ b/drivers/net/ethernet/Kconfig +@@ -85,6 +85,12 @@ config LANTIQ_ETOP + ---help--- + Support for the MII0 inside the Lantiq SoC + ++config LANTIQ_VRX200 ++ tristate "Lantiq SoC vrx200 driver" ++ depends on SOC_TYPE_XWAY ++ ---help--- ++ Support for the MII0 inside the Lantiq SoC ++ + source "drivers/net/ethernet/marvell/Kconfig" + source "drivers/net/ethernet/mellanox/Kconfig" + source "drivers/net/ethernet/micrel/Kconfig" +diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile +index 08d5f03..0c47dc5 100644 +--- a/drivers/net/ethernet/Makefile ++++ b/drivers/net/ethernet/Makefile +@@ -36,6 +36,7 @@ obj-$(CONFIG_IP1000) += icplus/ + obj-$(CONFIG_JME) += jme.o + obj-$(CONFIG_KORINA) += korina.o + obj-$(CONFIG_LANTIQ_ETOP) += lantiq_etop.o ++obj-$(CONFIG_LANTIQ_VRX200) += lantiq_vrx200.o + obj-$(CONFIG_NET_VENDOR_MARVELL) += marvell/ + obj-$(CONFIG_NET_VENDOR_MELLANOX) += mellanox/ + obj-$(CONFIG_NET_VENDOR_MICREL) += micrel/ +diff --git a/drivers/net/ethernet/lantiq_vrx200.c b/drivers/net/ethernet/lantiq_vrx200.c +new file mode 100644 +index 0000000..d79d380 +--- /dev/null ++++ b/drivers/net/ethernet/lantiq_vrx200.c +@@ -0,0 +1,1358 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2011 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++ ++#define LTQ_SWITCH_BASE 0x1E108000 ++#define LTQ_SWITCH_CORE_BASE LTQ_SWITCH_BASE ++#define LTQ_SWITCH_TOP_PDI_BASE LTQ_SWITCH_CORE_BASE ++#define LTQ_SWITCH_BM_PDI_BASE (LTQ_SWITCH_CORE_BASE + 4 * 0x40) ++#define LTQ_SWITCH_MAC_PDI_0_BASE (LTQ_SWITCH_CORE_BASE + 4 * 0x900) ++#define LTQ_SWITCH_MAC_PDI_X_BASE(x) (LTQ_SWITCH_MAC_PDI_0_BASE + x * 0x30) ++#define LTQ_SWITCH_TOPLEVEL_BASE (LTQ_SWITCH_BASE + 4 * 0xC40) ++#define LTQ_SWITCH_MDIO_PDI_BASE (LTQ_SWITCH_TOPLEVEL_BASE) ++#define LTQ_SWITCH_MII_PDI_BASE (LTQ_SWITCH_TOPLEVEL_BASE + 4 * 0x36) ++#define LTQ_SWITCH_PMAC_PDI_BASE (LTQ_SWITCH_TOPLEVEL_BASE + 4 * 0x82) ++ ++#define LTQ_ETHSW_MAC_CTRL0_PADEN (1 << 8) ++#define LTQ_ETHSW_MAC_CTRL0_FCS (1 << 7) ++#define LTQ_ETHSW_MAC_CTRL1_SHORTPRE (1 << 8) ++#define LTQ_ETHSW_MAC_CTRL2_MLEN (1 << 3) ++#define LTQ_ETHSW_MAC_CTRL2_LCHKL (1 << 2) ++#define LTQ_ETHSW_MAC_CTRL2_LCHKS_DIS 0 ++#define LTQ_ETHSW_MAC_CTRL2_LCHKS_UNTAG 1 ++#define LTQ_ETHSW_MAC_CTRL2_LCHKS_TAG 2 ++#define LTQ_ETHSW_MAC_CTRL6_RBUF_DLY_WP_SHIFT 9 ++#define LTQ_ETHSW_MAC_CTRL6_RXBUF_BYPASS (1 << 6) ++#define LTQ_ETHSW_GLOB_CTRL_SE (1 << 15) ++#define LTQ_ETHSW_MDC_CFG1_MCEN (1 << 8) ++#define LTQ_ETHSW_PMAC_HD_CTL_FC (1 << 10) ++#define LTQ_ETHSW_PMAC_HD_CTL_RC (1 << 4) ++#define LTQ_ETHSW_PMAC_HD_CTL_AC (1 << 2) ++#define ADVERTIZE_MPD (1 << 10) ++ ++#define MDIO_DEVAD_NONE (-1) ++ ++#define LTQ_ETH_RX_BUFFER_CNT PKTBUFSRX ++ ++#define LTQ_MDIO_DRV_NAME "ltq-mdio" ++#define LTQ_ETH_DRV_NAME "ltq-eth" ++ ++#define LTQ_ETHSW_MAX_GMAC 1 ++#define LTQ_ETHSW_PMAC 1 ++ ++#define ltq_setbits(a, set) \ ++ ltq_w32(ltq_r32(a) | (set), a) ++ ++enum ltq_reset_modules { ++ LTQ_RESET_CORE, ++ LTQ_RESET_DMA, ++ LTQ_RESET_ETH, ++ LTQ_RESET_PHY, ++ LTQ_RESET_HARD, ++ LTQ_RESET_SOFT, ++}; ++ ++static inline void ++dbg_ltq_writel(void *a, unsigned int b) ++{ ++ ltq_w32(b, a); ++} ++ ++int ltq_reset_once(enum ltq_reset_modules module, ulong usec); ++ ++struct ltq_ethsw_mac_pdi_x_regs { ++ u32 pstat; /* Port status */ ++ u32 pisr; /* Interrupt status */ ++ u32 pier; /* Interrupt enable */ ++ u32 ctrl_0; /* Control 0 */ ++ u32 ctrl_1; /* Control 1 */ ++ u32 ctrl_2; /* Control 2 */ ++ u32 ctrl_3; /* Control 3 */ ++ u32 ctrl_4; /* Control 4 */ ++ u32 ctrl_5; /* Control 5 */ ++ u32 ctrl_6; /* Control 6 */ ++ u32 bufst; /* TX/RX buffer control */ ++ u32 testen; /* Test enable */ ++}; ++ ++struct ltq_ethsw_mac_pdi_regs { ++ struct ltq_ethsw_mac_pdi_x_regs mac[12]; ++}; ++ ++struct ltq_ethsw_mdio_pdi_regs { ++ u32 glob_ctrl; /* Global control 0 */ ++ u32 rsvd0[7]; ++ u32 mdio_ctrl; /* MDIO control */ ++ u32 mdio_read; /* MDIO read data */ ++ u32 mdio_write; /* MDIO write data */ ++ u32 mdc_cfg_0; /* MDC clock configuration 0 */ ++ u32 mdc_cfg_1; /* MDC clock configuration 1 */ ++ u32 rsvd[3]; ++ u32 phy_addr_5; /* PHY address port 5 */ ++ u32 phy_addr_4; /* PHY address port 4 */ ++ u32 phy_addr_3; /* PHY address port 3 */ ++ u32 phy_addr_2; /* PHY address port 2 */ ++ u32 phy_addr_1; /* PHY address port 1 */ ++ u32 phy_addr_0; /* PHY address port 0 */ ++ u32 mdio_stat_0; /* MDIO PHY polling status port 0 */ ++ u32 mdio_stat_1; /* MDIO PHY polling status port 1 */ ++ u32 mdio_stat_2; /* MDIO PHY polling status port 2 */ ++ u32 mdio_stat_3; /* MDIO PHY polling status port 3 */ ++ u32 mdio_stat_4; /* MDIO PHY polling status port 4 */ ++ u32 mdio_stat_5; /* MDIO PHY polling status port 5 */ ++}; ++ ++struct ltq_ethsw_mii_pdi_regs { ++ u32 mii_cfg0; /* xMII port 0 configuration */ ++ u32 pcdu0; /* Port 0 clock delay configuration */ ++ u32 mii_cfg1; /* xMII port 1 configuration */ ++ u32 pcdu1; /* Port 1 clock delay configuration */ ++ u32 mii_cfg2; /* xMII port 2 configuration */ ++ u32 rsvd0; ++ u32 mii_cfg3; /* xMII port 3 configuration */ ++ u32 rsvd1; ++ u32 mii_cfg4; /* xMII port 4 configuration */ ++ u32 rsvd2; ++ u32 mii_cfg5; /* xMII port 5 configuration */ ++ u32 pcdu5; /* Port 5 clock delay configuration */ ++}; ++ ++struct ltq_ethsw_pmac_pdi_regs { ++ u32 hd_ctl; /* PMAC header control */ ++ u32 tl; /* PMAC type/length */ ++ u32 sa1; /* PMAC source address 1 */ ++ u32 sa2; /* PMAC source address 2 */ ++ u32 sa3; /* PMAC source address 3 */ ++ u32 da1; /* PMAC destination address 1 */ ++ u32 da2; /* PMAC destination address 2 */ ++ u32 da3; /* PMAC destination address 3 */ ++ u32 vlan; /* PMAC VLAN */ ++ u32 rx_ipg; /* PMAC interpacket gap in RX direction */ ++ u32 st_etype; /* PMAC special tag ethertype */ ++ u32 ewan; /* PMAC ethernet WAN group */ ++}; ++ ++struct ltq_mdio_phy_addr_reg { ++ union { ++ struct { ++ unsigned rsvd:1; ++ unsigned lnkst:2; /* Link status control */ ++ unsigned speed:2; /* Speed control */ ++ unsigned fdup:2; /* Full duplex control */ ++ unsigned fcontx:2; /* Flow control mode TX */ ++ unsigned fconrx:2; /* Flow control mode RX */ ++ unsigned addr:5; /* PHY address */ ++ } bits; ++ u16 val; ++ }; ++}; ++ ++enum ltq_mdio_phy_addr_lnkst { ++ LTQ_MDIO_PHY_ADDR_LNKST_AUTO = 0, ++ LTQ_MDIO_PHY_ADDR_LNKST_UP = 1, ++ LTQ_MDIO_PHY_ADDR_LNKST_DOWN = 2, ++}; ++ ++enum ltq_mdio_phy_addr_speed { ++ LTQ_MDIO_PHY_ADDR_SPEED_M10 = 0, ++ LTQ_MDIO_PHY_ADDR_SPEED_M100 = 1, ++ LTQ_MDIO_PHY_ADDR_SPEED_G1 = 2, ++ LTQ_MDIO_PHY_ADDR_SPEED_AUTO = 3, ++}; ++ ++enum ltq_mdio_phy_addr_fdup { ++ LTQ_MDIO_PHY_ADDR_FDUP_AUTO = 0, ++ LTQ_MDIO_PHY_ADDR_FDUP_ENABLE = 1, ++ LTQ_MDIO_PHY_ADDR_FDUP_DISABLE = 3, ++}; ++ ++enum ltq_mdio_phy_addr_fcon { ++ LTQ_MDIO_PHY_ADDR_FCON_AUTO = 0, ++ LTQ_MDIO_PHY_ADDR_FCON_ENABLE = 1, ++ LTQ_MDIO_PHY_ADDR_FCON_DISABLE = 3, ++}; ++ ++struct ltq_mii_mii_cfg_reg { ++ union { ++ struct { ++ unsigned res:1; /* Hardware reset */ ++ unsigned en:1; /* xMII interface enable */ ++ unsigned isol:1; /* xMII interface isolate */ ++ unsigned ldclkdis:1; /* Link down clock disable */ ++ unsigned rsvd:1; ++ unsigned crs:2; /* CRS sensitivity config */ ++ unsigned rgmii_ibs:1; /* RGMII In Band status */ ++ unsigned rmii:1; /* RMII ref clock direction */ ++ unsigned miirate:3; /* xMII interface clock rate */ ++ unsigned miimode:4; /* xMII interface mode */ ++ } bits; ++ u16 val; ++ }; ++}; ++ ++enum ltq_mii_mii_cfg_miirate { ++ LTQ_MII_MII_CFG_MIIRATE_M2P5 = 0, ++ LTQ_MII_MII_CFG_MIIRATE_M25 = 1, ++ LTQ_MII_MII_CFG_MIIRATE_M125 = 2, ++ LTQ_MII_MII_CFG_MIIRATE_M50 = 3, ++ LTQ_MII_MII_CFG_MIIRATE_AUTO = 4, ++}; ++ ++enum ltq_mii_mii_cfg_miimode { ++ LTQ_MII_MII_CFG_MIIMODE_MIIP = 0, ++ LTQ_MII_MII_CFG_MIIMODE_MIIM = 1, ++ LTQ_MII_MII_CFG_MIIMODE_RMIIP = 2, ++ LTQ_MII_MII_CFG_MIIMODE_RMIIM = 3, ++ LTQ_MII_MII_CFG_MIIMODE_RGMII = 4, ++}; ++ ++struct ltq_eth_priv { ++ struct ltq_dma_device *dma_dev; ++ struct mii_dev *bus; ++ struct eth_device *dev; ++ struct phy_device *phymap[LTQ_ETHSW_MAX_GMAC]; ++ int rx_num; ++}; ++ ++enum ltq_mdio_mbusy { ++ LTQ_MDIO_MBUSY_IDLE = 0, ++ LTQ_MDIO_MBUSY_BUSY = 1, ++}; ++ ++enum ltq_mdio_op { ++ LTQ_MDIO_OP_WRITE = 1, ++ LTQ_MDIO_OP_READ = 2, ++}; ++ ++struct ltq_mdio_access { ++ union { ++ struct { ++ unsigned rsvd:3; ++ unsigned mbusy:1; ++ unsigned op:2; ++ unsigned phyad:5; ++ unsigned regad:5; ++ } bits; ++ u16 val; ++ }; ++}; ++ ++enum LTQ_ETH_PORT_FLAGS { ++ LTQ_ETH_PORT_NONE = 0, ++ LTQ_ETH_PORT_PHY = 1, ++ LTQ_ETH_PORT_SWITCH = (1 << 1), ++ LTQ_ETH_PORT_MAC = (1 << 2), ++}; ++ ++struct ltq_eth_port_config { ++ u8 num; ++ u8 phy_addr; ++ u16 flags; ++ phy_interface_t phy_if; ++}; ++ ++struct ltq_eth_board_config { ++ const struct ltq_eth_port_config *ports; ++ int num_ports; ++}; ++ ++static const struct ltq_eth_port_config eth_port_config[] = { ++ /* GMAC0: external Lantiq PEF7071 10/100/1000 PHY for LAN port 0 */ ++ { 0, 0x0, LTQ_ETH_PORT_PHY, PHY_INTERFACE_MODE_RGMII }, ++ /* GMAC1: external Lantiq PEF7071 10/100/1000 PHY for LAN port 1 */ ++ { 1, 0x1, LTQ_ETH_PORT_PHY, PHY_INTERFACE_MODE_RGMII }, ++}; ++ ++static const struct ltq_eth_board_config board_config = { ++ .ports = eth_port_config, ++ .num_ports = ARRAY_SIZE(eth_port_config), ++}; ++ ++static struct ltq_ethsw_mac_pdi_regs *ltq_ethsw_mac_pdi_regs = ++ (struct ltq_ethsw_mac_pdi_regs *) CKSEG1ADDR(LTQ_SWITCH_MAC_PDI_0_BASE); ++ ++static struct ltq_ethsw_mdio_pdi_regs *ltq_ethsw_mdio_pdi_regs = ++ (struct ltq_ethsw_mdio_pdi_regs *) CKSEG1ADDR(LTQ_SWITCH_MDIO_PDI_BASE); ++ ++static struct ltq_ethsw_mii_pdi_regs *ltq_ethsw_mii_pdi_regs = ++ (struct ltq_ethsw_mii_pdi_regs *) CKSEG1ADDR(LTQ_SWITCH_MII_PDI_BASE); ++ ++static struct ltq_ethsw_pmac_pdi_regs *ltq_ethsw_pmac_pdi_regs = ++ (struct ltq_ethsw_pmac_pdi_regs *) CKSEG1ADDR(LTQ_SWITCH_PMAC_PDI_BASE); ++ ++ ++#define MAX_DMA_CHAN 0x8 ++#define MAX_DMA_CRC_LEN 0x4 ++#define MAX_DMA_DATA_LEN 0x600 ++ ++/* use 2 static channels for TX/RX ++ depending on the SoC we need to use different DMA channels for ethernet */ ++#define LTQ_ETOP_TX_CHANNEL 1 ++#define LTQ_ETOP_RX_CHANNEL 0 ++ ++#define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL) ++#define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL) ++ ++#define DRV_VERSION "1.0" ++ ++static void __iomem *ltq_vrx200_membase; ++ ++struct ltq_vrx200_chan { ++ int idx; ++ int tx_free; ++ struct net_device *netdev; ++ struct napi_struct napi; ++ struct ltq_dma_channel dma; ++ struct sk_buff *skb[LTQ_DESC_NUM]; ++}; ++ ++struct ltq_vrx200_priv { ++ struct net_device *netdev; ++ struct ltq_eth_data *pldata; ++ struct resource *res; ++ ++ struct mii_bus *mii_bus; ++ struct phy_device *phydev; ++ ++ struct ltq_vrx200_chan ch[MAX_DMA_CHAN]; ++ int tx_free[MAX_DMA_CHAN >> 1]; ++ ++ spinlock_t lock; ++ ++ struct clk *clk_ppe; ++}; ++ ++static int ltq_vrx200_mdio_wr(struct mii_bus *bus, int phy_addr, ++ int phy_reg, u16 phy_data); ++ ++static int ++ltq_vrx200_alloc_skb(struct ltq_vrx200_chan *ch) ++{ ++ ch->skb[ch->dma.desc] = dev_alloc_skb(MAX_DMA_DATA_LEN); ++ if (!ch->skb[ch->dma.desc]) ++ return -ENOMEM; ++ ch->dma.desc_base[ch->dma.desc].addr = dma_map_single(NULL, ++ ch->skb[ch->dma.desc]->data, MAX_DMA_DATA_LEN, ++ DMA_FROM_DEVICE); ++ ch->dma.desc_base[ch->dma.desc].addr = ++ CPHYSADDR(ch->skb[ch->dma.desc]->data); ++ ch->dma.desc_base[ch->dma.desc].ctl = ++ LTQ_DMA_OWN | LTQ_DMA_RX_OFFSET(NET_IP_ALIGN) | ++ MAX_DMA_DATA_LEN; ++ skb_reserve(ch->skb[ch->dma.desc], NET_IP_ALIGN); ++ return 0; ++} ++ ++static void ++ltq_vrx200_hw_receive(struct ltq_vrx200_chan *ch) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(ch->netdev); ++ struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; ++ struct sk_buff *skb = ch->skb[ch->dma.desc]; ++ int len = (desc->ctl & LTQ_DMA_SIZE_MASK) - MAX_DMA_CRC_LEN; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ if (ltq_vrx200_alloc_skb(ch)) { ++ netdev_err(ch->netdev, ++ "failed to allocate new rx buffer, stopping DMA\n"); ++ ltq_dma_close(&ch->dma); ++ } ++ ch->dma.desc++; ++ ch->dma.desc %= LTQ_DESC_NUM; ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ skb_put(skb, len); ++ skb->dev = ch->netdev; ++ skb->protocol = eth_type_trans(skb, ch->netdev); ++ netif_receive_skb(skb); ++} ++ ++static int ++ltq_vrx200_poll_rx(struct napi_struct *napi, int budget) ++{ ++ struct ltq_vrx200_chan *ch = container_of(napi, ++ struct ltq_vrx200_chan, napi); ++ struct ltq_vrx200_priv *priv = netdev_priv(ch->netdev); ++ int rx = 0; ++ int complete = 0; ++ unsigned long flags; ++ ++ while ((rx < budget) && !complete) { ++ struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; ++ ++ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { ++ ltq_vrx200_hw_receive(ch); ++ rx++; ++ } else { ++ complete = 1; ++ } ++ } ++ if (complete || !rx) { ++ napi_complete(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_ack_irq(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ } ++ return rx; ++} ++ ++static int ++ltq_vrx200_poll_tx(struct napi_struct *napi, int budget) ++{ ++ struct ltq_vrx200_chan *ch = ++ container_of(napi, struct ltq_vrx200_chan, napi); ++ struct ltq_vrx200_priv *priv = netdev_priv(ch->netdev); ++ struct netdev_queue *txq = ++ netdev_get_tx_queue(ch->netdev, ch->idx >> 1); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ while ((ch->dma.desc_base[ch->tx_free].ctl & ++ (LTQ_DMA_OWN | LTQ_DMA_C)) == LTQ_DMA_C) { ++ dev_kfree_skb_any(ch->skb[ch->tx_free]); ++ ch->skb[ch->tx_free] = NULL; ++ memset(&ch->dma.desc_base[ch->tx_free], 0, ++ sizeof(struct ltq_dma_desc)); ++ ch->tx_free++; ++ ch->tx_free %= LTQ_DESC_NUM; ++ } ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ if (netif_tx_queue_stopped(txq)) ++ netif_tx_start_queue(txq); ++ napi_complete(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_ack_irq(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ return 1; ++} ++ ++static irqreturn_t ++ltq_vrx200_dma_irq(int irq, void *_priv) ++{ ++ struct ltq_vrx200_priv *priv = _priv; ++ int ch = irq - LTQ_DMA_ETOP; ++ ++ napi_schedule(&priv->ch[ch].napi); ++ return IRQ_HANDLED; ++} ++ ++static void ++ltq_vrx200_free_channel(struct net_device *dev, struct ltq_vrx200_chan *ch) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ++ ltq_dma_free(&ch->dma); ++ if (ch->dma.irq) ++ free_irq(ch->dma.irq, priv); ++ if (IS_RX(ch->idx)) { ++ int desc; ++ for (desc = 0; desc < LTQ_DESC_NUM; desc++) ++ dev_kfree_skb_any(ch->skb[ch->dma.desc]); ++ } ++} ++ ++static void ++ltq_vrx200_hw_exit(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ int i; ++ ++ clk_disable(priv->clk_ppe); ++ ++ for (i = 0; i < MAX_DMA_CHAN; i++) ++ if (IS_TX(i) || IS_RX(i)) ++ ltq_vrx200_free_channel(dev, &priv->ch[i]); ++} ++ ++static void *ltq_eth_phy_addr_reg(int num) ++{ ++ switch (num) { ++ case 0: ++ return <q_ethsw_mdio_pdi_regs->phy_addr_0; ++ case 1: ++ return <q_ethsw_mdio_pdi_regs->phy_addr_1; ++ case 2: ++ return <q_ethsw_mdio_pdi_regs->phy_addr_2; ++ case 3: ++ return <q_ethsw_mdio_pdi_regs->phy_addr_3; ++ case 4: ++ return <q_ethsw_mdio_pdi_regs->phy_addr_4; ++ case 5: ++ return <q_ethsw_mdio_pdi_regs->phy_addr_5; ++ } ++ ++ return NULL; ++} ++ ++static void *ltq_eth_mii_cfg_reg(int num) ++{ ++ switch (num) { ++ case 0: ++ return <q_ethsw_mii_pdi_regs->mii_cfg0; ++ case 1: ++ return <q_ethsw_mii_pdi_regs->mii_cfg1; ++ case 2: ++ return <q_ethsw_mii_pdi_regs->mii_cfg2; ++ case 3: ++ return <q_ethsw_mii_pdi_regs->mii_cfg3; ++ case 4: ++ return <q_ethsw_mii_pdi_regs->mii_cfg4; ++ case 5: ++ return <q_ethsw_mii_pdi_regs->mii_cfg5; ++ } ++ ++ return NULL; ++} ++ ++static void ltq_eth_gmac_update(struct phy_device *phydev, int num) ++{ ++ struct ltq_mdio_phy_addr_reg phy_addr_reg; ++ struct ltq_mii_mii_cfg_reg mii_cfg_reg; ++ void *phy_addr = ltq_eth_phy_addr_reg(num); ++ void *mii_cfg = ltq_eth_mii_cfg_reg(num); ++ ++ phy_addr_reg.val = ltq_r32(phy_addr); ++ mii_cfg_reg.val = ltq_r32(mii_cfg); ++ ++ phy_addr_reg.bits.addr = phydev->addr; ++ ++ if (phydev->link) ++ phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_UP; ++ else ++ phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_DOWN; ++ ++ switch (phydev->speed) { ++ case SPEED_1000: ++ phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_G1; ++ mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M125; ++ break; ++ case SPEED_100: ++ phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M100; ++ switch (mii_cfg_reg.bits.miimode) { ++ case LTQ_MII_MII_CFG_MIIMODE_RMIIM: ++ case LTQ_MII_MII_CFG_MIIMODE_RMIIP: ++ mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M50; ++ break; ++ default: ++ mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M25; ++ break; ++ } ++ break; ++ default: ++ phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M10; ++ mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M2P5; ++ break; ++ } ++ ++ if (phydev->duplex == DUPLEX_FULL) ++ phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_ENABLE; ++ else ++ phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_DISABLE; ++ ++ dbg_ltq_writel(phy_addr, phy_addr_reg.val); ++ dbg_ltq_writel(mii_cfg, mii_cfg_reg.val); ++ udelay(1); ++} ++ ++ ++static void ltq_eth_port_config(struct ltq_vrx200_priv *priv, ++ const struct ltq_eth_port_config *port) ++{ ++ struct ltq_mii_mii_cfg_reg mii_cfg_reg; ++ void *mii_cfg = ltq_eth_mii_cfg_reg(port->num); ++ int setup_gpio = 0; ++ ++ mii_cfg_reg.val = ltq_r32(mii_cfg); ++ ++ ++ switch (port->num) { ++ case 0: /* xMII0 */ ++ case 1: /* xMII1 */ ++ switch (port->phy_if) { ++ case PHY_INTERFACE_MODE_MII: ++ if (port->flags & LTQ_ETH_PORT_PHY) ++ /* MII MAC mode, connected to external PHY */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_MIIM; ++ else ++ /* MII PHY mode, connected to external MAC */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_MIIP; ++ setup_gpio = 1; ++ break; ++ case PHY_INTERFACE_MODE_RMII: ++ if (port->flags & LTQ_ETH_PORT_PHY) ++ /* RMII MAC mode, connected to external PHY */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_RMIIM; ++ else ++ /* RMII PHY mode, connected to external MAC */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_RMIIP; ++ setup_gpio = 1; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ /* RGMII MAC mode, connected to external PHY */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_RGMII; ++ setup_gpio = 1; ++ break; ++ default: ++ break; ++ } ++ break; ++ case 2: /* internal GPHY0 */ ++ case 3: /* internal GPHY0 */ ++ case 4: /* internal GPHY1 */ ++ switch (port->phy_if) { ++ case PHY_INTERFACE_MODE_MII: ++ case PHY_INTERFACE_MODE_GMII: ++ /* MII MAC mode, connected to internal GPHY */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_MIIM; ++ setup_gpio = 1; ++ break; ++ default: ++ break; ++ } ++ break; ++ case 5: /* internal GPHY1 or xMII2 */ ++ switch (port->phy_if) { ++ case PHY_INTERFACE_MODE_MII: ++ /* MII MAC mode, connected to internal GPHY */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_MIIM; ++ setup_gpio = 1; ++ break; ++ case PHY_INTERFACE_MODE_RGMII: ++ /* RGMII MAC mode, connected to external PHY */ ++ mii_cfg_reg.bits.miimode = ++ LTQ_MII_MII_CFG_MIIMODE_RGMII; ++ setup_gpio = 1; ++ break; ++ default: ++ break; ++ } ++ break; ++ default: ++ break; ++ } ++ ++ /* Enable MII interface */ ++ mii_cfg_reg.bits.en = port->flags ? 1 : 0; ++ dbg_ltq_writel(mii_cfg, mii_cfg_reg.val); ++ ++} ++ ++static void ltq_eth_gmac_init(int num) ++{ ++ struct ltq_mdio_phy_addr_reg phy_addr_reg; ++ struct ltq_mii_mii_cfg_reg mii_cfg_reg; ++ void *phy_addr = ltq_eth_phy_addr_reg(num); ++ void *mii_cfg = ltq_eth_mii_cfg_reg(num); ++ struct ltq_ethsw_mac_pdi_x_regs *mac_pdi_regs; ++ ++ mac_pdi_regs = <q_ethsw_mac_pdi_regs->mac[num]; ++ ++ /* Reset PHY status to link down */ ++ phy_addr_reg.val = ltq_r32(phy_addr); ++ phy_addr_reg.bits.addr = num; ++ phy_addr_reg.bits.lnkst = LTQ_MDIO_PHY_ADDR_LNKST_DOWN; ++ phy_addr_reg.bits.speed = LTQ_MDIO_PHY_ADDR_SPEED_M10; ++ phy_addr_reg.bits.fdup = LTQ_MDIO_PHY_ADDR_FDUP_DISABLE; ++ dbg_ltq_writel(phy_addr, phy_addr_reg.val); ++ ++ /* Reset and disable MII interface */ ++ mii_cfg_reg.val = ltq_r32(mii_cfg); ++ mii_cfg_reg.bits.en = 0; ++ mii_cfg_reg.bits.res = 1; ++ mii_cfg_reg.bits.miirate = LTQ_MII_MII_CFG_MIIRATE_M2P5; ++ dbg_ltq_writel(mii_cfg, mii_cfg_reg.val); ++ ++ /* ++ * Enable padding of short frames, enable frame checksum generation ++ * in transmit direction ++ */ ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_0, LTQ_ETHSW_MAC_CTRL0_PADEN | ++ LTQ_ETHSW_MAC_CTRL0_FCS); ++ ++ /* Set inter packet gap size to 12 bytes */ ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_1, 12); ++ ++ /* ++ * Configure frame length checks: ++ * - allow jumbo frames ++ * - enable long length check ++ * - enable short length without VLAN tags ++ */ ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_2, LTQ_ETHSW_MAC_CTRL2_MLEN | ++ LTQ_ETHSW_MAC_CTRL2_LCHKL | ++ LTQ_ETHSW_MAC_CTRL2_LCHKS_UNTAG); ++} ++ ++ ++static void ltq_eth_pmac_init(void) ++{ ++ struct ltq_ethsw_mac_pdi_x_regs *mac_pdi_regs; ++ ++ mac_pdi_regs = <q_ethsw_mac_pdi_regs->mac[LTQ_ETHSW_PMAC]; ++ ++ /* ++ * Enable padding of short frames, enable frame checksum generation ++ * in transmit direction ++ */ ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_0, LTQ_ETHSW_MAC_CTRL0_PADEN | ++ LTQ_ETHSW_MAC_CTRL0_FCS); ++ ++ /* ++ * Configure frame length checks: ++ * - allow jumbo frames ++ * - enable long length check ++ * - enable short length without VLAN tags ++ */ ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_2, LTQ_ETHSW_MAC_CTRL2_MLEN | ++ LTQ_ETHSW_MAC_CTRL2_LCHKL | ++ LTQ_ETHSW_MAC_CTRL2_LCHKS_UNTAG); ++ ++ /* ++ * Apply workaround for buffer congestion: ++ * - shorten preambel to 1 byte ++ * - set minimum inter packet gap size to 7 bytes ++ * - enable receive buffer bypass mode ++ */ ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_1, LTQ_ETHSW_MAC_CTRL1_SHORTPRE | 7); ++ dbg_ltq_writel(&mac_pdi_regs->ctrl_6, ++ (6 << LTQ_ETHSW_MAC_CTRL6_RBUF_DLY_WP_SHIFT) | ++ LTQ_ETHSW_MAC_CTRL6_RXBUF_BYPASS); ++ ++ /* Set request assertion threshold to 8, IPG counter to 11 */ ++ dbg_ltq_writel(<q_ethsw_pmac_pdi_regs->rx_ipg, 0x8B); ++ ++ /* ++ * Configure frame header control: ++ * - enable reaction on pause frames (flow control) ++ * - remove CRC for packets from PMAC to DMA ++ * - add CRC for packets from DMA to PMAC ++ */ ++ dbg_ltq_writel(<q_ethsw_pmac_pdi_regs->hd_ctl, LTQ_ETHSW_PMAC_HD_CTL_FC | ++ /*LTQ_ETHSW_PMAC_HD_CTL_RC | */LTQ_ETHSW_PMAC_HD_CTL_AC); ++} ++ ++static int ++ltq_vrx200_hw_init(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ int err = 0; ++ int i; ++ ++ netdev_info(dev, "setting up dma\n"); ++ ltq_dma_init_port(DMA_PORT_ETOP); ++ ++ netdev_info(dev, "setting up pmu\n"); ++ clk_enable(priv->clk_ppe); ++ ++ /* Reset ethernet and switch subsystems */ ++ netdev_info(dev, "reset core\n"); ++ ltq_reset_once(BIT(8), 10); ++ ++ /* Enable switch macro */ ++ ltq_setbits(<q_ethsw_mdio_pdi_regs->glob_ctrl, ++ LTQ_ETHSW_GLOB_CTRL_SE); ++ ++ /* Disable MDIO auto-polling for all ports */ ++ dbg_ltq_writel(<q_ethsw_mdio_pdi_regs->mdc_cfg_0, 0); ++ ++ /* ++ * Enable and set MDIO management clock to 2.5 MHz. This is the ++ * maximum clock for FE PHYs. ++ * Formula for clock is: ++ * ++ * 50 MHz ++ * x = ----------- - 1 ++ * 2 * f_MDC ++ */ ++ dbg_ltq_writel(<q_ethsw_mdio_pdi_regs->mdc_cfg_1, ++ LTQ_ETHSW_MDC_CFG1_MCEN | 9); ++ ++ /* Init MAC connected to CPU */ ++ ltq_eth_pmac_init(); ++ ++ /* Init MACs connected to external MII interfaces */ ++ for (i = 0; i < LTQ_ETHSW_MAX_GMAC; i++) ++ ltq_eth_gmac_init(i); ++ ++ for (i = 0; i < MAX_DMA_CHAN && !err; i++) { ++ int irq = LTQ_DMA_ETOP + i; ++ struct ltq_vrx200_chan *ch = &priv->ch[i]; ++ ++ ch->idx = ch->dma.nr = i; ++ ++ if (IS_TX(i)) { ++ ltq_dma_alloc_tx(&ch->dma); ++ err = request_irq(irq, ltq_vrx200_dma_irq, IRQF_DISABLED, ++ "vrx200_tx", priv); ++ } else if (IS_RX(i)) { ++ ltq_dma_alloc_rx(&ch->dma); ++ for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM; ++ ch->dma.desc++) ++ if (ltq_vrx200_alloc_skb(ch)) ++ err = -ENOMEM; ++ ch->dma.desc = 0; ++ err = request_irq(irq, ltq_vrx200_dma_irq, IRQF_DISABLED, ++ "vrx200_rx", priv); ++ } ++ if (!err) ++ ch->dma.irq = irq; ++ } ++ for (i = 0; i < board_config.num_ports; i++) ++ ltq_eth_port_config(priv, &board_config.ports[i]); ++ return err; ++} ++ ++static void ++ltq_vrx200_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) ++{ ++ strcpy(info->driver, "Lantiq ETOP"); ++ strcpy(info->bus_info, "internal"); ++ strcpy(info->version, DRV_VERSION); ++} ++ ++static int ++ltq_vrx200_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ++ return phy_ethtool_gset(priv->phydev, cmd); ++} ++ ++static int ++ltq_vrx200_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ++ return phy_ethtool_sset(priv->phydev, cmd); ++} ++ ++static int ++ltq_vrx200_nway_reset(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ++ return phy_start_aneg(priv->phydev); ++} ++ ++static const struct ethtool_ops ltq_vrx200_ethtool_ops = { ++ .get_drvinfo = ltq_vrx200_get_drvinfo, ++ .get_settings = ltq_vrx200_get_settings, ++ .set_settings = ltq_vrx200_set_settings, ++ .nway_reset = ltq_vrx200_nway_reset, ++}; ++ ++static inline int ltq_mdio_poll(struct mii_bus *bus) ++{ ++ struct ltq_mdio_access acc; ++ unsigned cnt = 10000; ++ ++ while (likely(cnt--)) { ++ acc.val = ltq_r32(<q_ethsw_mdio_pdi_regs->mdio_ctrl); ++ if (!acc.bits.mbusy) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static int ++ltq_vrx200_mdio_wr(struct mii_bus *bus, int addr, int regnum, u16 val) ++{ ++ struct ltq_mdio_access acc; ++ int ret; ++ ++ acc.val = 0; ++ acc.bits.mbusy = LTQ_MDIO_MBUSY_BUSY; ++ acc.bits.op = LTQ_MDIO_OP_WRITE; ++ acc.bits.phyad = addr; ++ acc.bits.regad = regnum; ++ ++ ret = ltq_mdio_poll(bus); ++ if (ret) ++ return ret; ++ ++ dbg_ltq_writel(<q_ethsw_mdio_pdi_regs->mdio_write, val); ++ dbg_ltq_writel(<q_ethsw_mdio_pdi_regs->mdio_ctrl, acc.val); ++ ++ return 0; ++} ++ ++static int ++ltq_vrx200_mdio_rd(struct mii_bus *bus, int addr, int regnum) ++{ ++ struct ltq_mdio_access acc; ++ int ret; ++ ++ acc.val = 0; ++ acc.bits.mbusy = LTQ_MDIO_MBUSY_BUSY; ++ acc.bits.op = LTQ_MDIO_OP_READ; ++ acc.bits.phyad = addr; ++ acc.bits.regad = regnum; ++ ++ ret = ltq_mdio_poll(bus); ++ if (ret) ++ goto timeout; ++ ++ dbg_ltq_writel(<q_ethsw_mdio_pdi_regs->mdio_ctrl, acc.val); ++ ++ ret = ltq_mdio_poll(bus); ++ if (ret) ++ goto timeout; ++ ++ ret = ltq_r32(<q_ethsw_mdio_pdi_regs->mdio_read); ++ ++ return ret; ++timeout: ++ return -1; ++} ++ ++static void ++ltq_vrx200_mdio_link(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ltq_eth_gmac_update(priv->phydev, 0); ++} ++ ++static int ++ltq_vrx200_mdio_probe(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ struct phy_device *phydev = NULL; ++ int val; ++ ++ phydev = priv->mii_bus->phy_map[0]; ++ ++ if (!phydev) { ++ netdev_err(dev, "no PHY found\n"); ++ return -ENODEV; ++ } ++ ++ phydev = phy_connect(dev, dev_name(&phydev->dev), <q_vrx200_mdio_link, ++ 0, 0); ++ ++ if (IS_ERR(phydev)) { ++ netdev_err(dev, "Could not attach to PHY\n"); ++ return PTR_ERR(phydev); ++ } ++ ++ phydev->supported &= (SUPPORTED_10baseT_Half ++ | SUPPORTED_10baseT_Full ++ | SUPPORTED_100baseT_Half ++ | SUPPORTED_100baseT_Full ++ | SUPPORTED_1000baseT_Half ++ | SUPPORTED_1000baseT_Full ++ | SUPPORTED_Autoneg ++ | SUPPORTED_MII ++ | SUPPORTED_TP); ++ phydev->advertising = phydev->supported; ++ priv->phydev = phydev; ++ ++ pr_info("%s: attached PHY [%s] (phy_addr=%s, irq=%d)\n", ++ dev->name, phydev->drv->name, ++ dev_name(&phydev->dev), phydev->irq); ++ ++ val = ltq_vrx200_mdio_rd(priv->mii_bus, MDIO_DEVAD_NONE, MII_CTRL1000); ++ val |= ADVERTIZE_MPD; ++ ltq_vrx200_mdio_wr(priv->mii_bus, MDIO_DEVAD_NONE, MII_CTRL1000, val); ++ ltq_vrx200_mdio_wr(priv->mii_bus, 0, 0, 0x1040); ++ ++ phy_start_aneg(phydev); ++ ++ return 0; ++} ++ ++static int ++ltq_vrx200_mdio_init(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ int i; ++ int err; ++ ++ priv->mii_bus = mdiobus_alloc(); ++ if (!priv->mii_bus) { ++ netdev_err(dev, "failed to allocate mii bus\n"); ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ priv->mii_bus->priv = dev; ++ priv->mii_bus->read = ltq_vrx200_mdio_rd; ++ priv->mii_bus->write = ltq_vrx200_mdio_wr; ++ priv->mii_bus->name = "ltq_mii"; ++ snprintf(priv->mii_bus->id, MII_BUS_ID_SIZE, "%x", 0); ++ priv->mii_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); ++ if (!priv->mii_bus->irq) { ++ err = -ENOMEM; ++ goto err_out_free_mdiobus; ++ } ++ ++ for (i = 0; i < PHY_MAX_ADDR; ++i) ++ priv->mii_bus->irq[i] = PHY_POLL; ++ ++ if (mdiobus_register(priv->mii_bus)) { ++ err = -ENXIO; ++ goto err_out_free_mdio_irq; ++ } ++ ++ if (ltq_vrx200_mdio_probe(dev)) { ++ err = -ENXIO; ++ goto err_out_unregister_bus; ++ } ++ return 0; ++ ++err_out_unregister_bus: ++ mdiobus_unregister(priv->mii_bus); ++err_out_free_mdio_irq: ++ kfree(priv->mii_bus->irq); ++err_out_free_mdiobus: ++ mdiobus_free(priv->mii_bus); ++err_out: ++ return err; ++} ++ ++static void ++ltq_vrx200_mdio_cleanup(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ++ phy_disconnect(priv->phydev); ++ mdiobus_unregister(priv->mii_bus); ++ kfree(priv->mii_bus->irq); ++ mdiobus_free(priv->mii_bus); ++} ++ ++void phy_dump(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ int i; ++ for (i = 0; i < 0x1F; i++) { ++ unsigned int val = ltq_vrx200_mdio_rd(priv->mii_bus, 0, i); ++ printk("%d %4X\n", i, val); ++ } ++} ++ ++static int ++ltq_vrx200_open(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ int i; ++ unsigned long flags; ++ ++ for (i = 0; i < MAX_DMA_CHAN; i++) { ++ struct ltq_vrx200_chan *ch = &priv->ch[i]; ++ ++ if (!IS_TX(i) && (!IS_RX(i))) ++ continue; ++ napi_enable(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_open(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ } ++ if (priv->phydev) { ++ phy_start(priv->phydev); ++ phy_dump(dev); ++ } ++ netif_tx_start_all_queues(dev); ++ return 0; ++} ++ ++static int ++ltq_vrx200_stop(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ int i; ++ unsigned long flags; ++ ++ netif_tx_stop_all_queues(dev); ++ if (priv->phydev) ++ phy_stop(priv->phydev); ++ for (i = 0; i < MAX_DMA_CHAN; i++) { ++ struct ltq_vrx200_chan *ch = &priv->ch[i]; ++ ++ if (!IS_RX(i) && !IS_TX(i)) ++ continue; ++ napi_disable(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_close(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); ++ } ++ return 0; ++} ++ ++static int ++ltq_vrx200_tx(struct sk_buff *skb, struct net_device *dev) ++{ ++ int queue = skb_get_queue_mapping(skb); ++ struct netdev_queue *txq = netdev_get_tx_queue(dev, queue); ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ struct ltq_vrx200_chan *ch = &priv->ch[(queue << 1) | 1]; ++ struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; ++ unsigned long flags; ++ u32 byte_offset; ++ int len; ++ ++ len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; ++ ++ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) { ++ netdev_err(dev, "tx ring full\n"); ++ netif_tx_stop_queue(txq); ++ return NETDEV_TX_BUSY; ++ } ++ ++ /* dma needs to start on a 16 byte aligned address */ ++ byte_offset = CPHYSADDR(skb->data) % 16; ++ ch->skb[ch->dma.desc] = skb; ++ ++ dev->trans_start = jiffies; ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ desc->addr = ((unsigned int) dma_map_single(NULL, skb->data, len, ++ DMA_TO_DEVICE)) - byte_offset; ++ wmb(); ++ desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP | ++ LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK); ++ ch->dma.desc++; ++ ch->dma.desc %= LTQ_DESC_NUM; ++ spin_unlock_irqrestore(&priv->lock, flags); ++ ++ if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN) ++ netif_tx_stop_queue(txq); ++ ++ return NETDEV_TX_OK; ++} ++ ++static int ++ltq_vrx200_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ ++ /* TODO: mii-toll reports "No MII transceiver present!." ?!*/ ++ return phy_mii_ioctl(priv->phydev, rq, cmd); ++} ++ ++static u16 ++ltq_vrx200_select_queue(struct net_device *dev, struct sk_buff *skb) ++{ ++ /* we are currently only using the first queue */ ++ return 0; ++} ++ ++static int ++ltq_vrx200_init(struct net_device *dev) ++{ ++ struct ltq_vrx200_priv *priv = netdev_priv(dev); ++ struct sockaddr mac; ++ int err; ++ ++ ether_setup(dev); ++ dev->watchdog_timeo = 10 * HZ; ++ ++ err = ltq_vrx200_hw_init(dev); ++ if (err) ++ goto err_hw; ++ ++ memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr)); ++ if (!is_valid_ether_addr(mac.sa_data)) { ++ pr_warn("vrx200: invalid MAC, using random\n"); ++ random_ether_addr(mac.sa_data); ++ } ++ eth_mac_addr(dev, &mac); ++ ++ if (!ltq_vrx200_mdio_init(dev)) ++ dev->ethtool_ops = <q_vrx200_ethtool_ops; ++ else ++ pr_warn("vrx200: mdio probe failed\n");; ++ return 0; ++ ++err_hw: ++ ltq_vrx200_hw_exit(dev); ++ return err; ++} ++ ++static void ++ltq_vrx200_tx_timeout(struct net_device *dev) ++{ ++ int err; ++ ++ ltq_vrx200_hw_exit(dev); ++ err = ltq_vrx200_hw_init(dev); ++ if (err) ++ goto err_hw; ++ dev->trans_start = jiffies; ++ netif_wake_queue(dev); ++ return; ++ ++err_hw: ++ ltq_vrx200_hw_exit(dev); ++ netdev_err(dev, "failed to restart vrx200 after TX timeout\n"); ++} ++ ++static const struct net_device_ops ltq_eth_netdev_ops = { ++ .ndo_open = ltq_vrx200_open, ++ .ndo_stop = ltq_vrx200_stop, ++ .ndo_start_xmit = ltq_vrx200_tx, ++ .ndo_change_mtu = eth_change_mtu, ++ .ndo_do_ioctl = ltq_vrx200_ioctl, ++ .ndo_set_mac_address = eth_mac_addr, ++ .ndo_validate_addr = eth_validate_addr, ++ .ndo_select_queue = ltq_vrx200_select_queue, ++ .ndo_init = ltq_vrx200_init, ++ .ndo_tx_timeout = ltq_vrx200_tx_timeout, ++}; ++ ++static int __devinit ++ltq_vrx200_probe(struct platform_device *pdev) ++{ ++ struct net_device *dev; ++ struct ltq_vrx200_priv *priv; ++ struct resource *res; ++ int err; ++ int i; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!res) { ++ dev_err(&pdev->dev, "failed to get vrx200 resource\n"); ++ err = -ENOENT; ++ goto err_out; ++ } ++ ++ res = devm_request_mem_region(&pdev->dev, res->start, ++ resource_size(res), dev_name(&pdev->dev)); ++ if (!res) { ++ dev_err(&pdev->dev, "failed to request vrx200 resource\n"); ++ err = -EBUSY; ++ goto err_out; ++ } ++ ++ ltq_vrx200_membase = devm_ioremap_nocache(&pdev->dev, ++ res->start, resource_size(res)); ++ if (!ltq_vrx200_membase) { ++ dev_err(&pdev->dev, "failed to remap vrx200 engine %d\n", ++ pdev->id); ++ err = -ENOMEM; ++ goto err_out; ++ } ++ ++ if (ltq_gpio_request(&pdev->dev, 42, 2, 1, "MDIO") || ++ ltq_gpio_request(&pdev->dev, 43, 2, 1, "MDC")) { ++ dev_err(&pdev->dev, "failed to request MDIO gpios\n"); ++ err = -EBUSY; ++ goto err_out; ++ } ++ ++ dev = alloc_etherdev_mq(sizeof(struct ltq_vrx200_priv), 4); ++ strcpy(dev->name, "eth%d"); ++ dev->netdev_ops = <q_eth_netdev_ops; ++ priv = netdev_priv(dev); ++ priv->res = res; ++ priv->pldata = dev_get_platdata(&pdev->dev); ++ priv->netdev = dev; ++ ++ priv->clk_ppe = clk_get(&pdev->dev, NULL); ++ if (IS_ERR(priv->clk_ppe)) ++ return PTR_ERR(priv->clk_ppe); ++ ++ spin_lock_init(&priv->lock); ++ ++ for (i = 0; i < MAX_DMA_CHAN; i++) { ++ if (IS_TX(i)) ++ netif_napi_add(dev, &priv->ch[i].napi, ++ ltq_vrx200_poll_tx, 8); ++ else if (IS_RX(i)) ++ netif_napi_add(dev, &priv->ch[i].napi, ++ ltq_vrx200_poll_rx, 32); ++ priv->ch[i].netdev = dev; ++ } ++ ++ err = register_netdev(dev); ++ if (err) ++ goto err_free; ++ ++ platform_set_drvdata(pdev, dev); ++ return 0; ++ ++err_free: ++ kfree(dev); ++err_out: ++ return err; ++} ++ ++static int __devexit ++ltq_vrx200_remove(struct platform_device *pdev) ++{ ++ struct net_device *dev = platform_get_drvdata(pdev); ++ ++ if (dev) { ++ netif_tx_stop_all_queues(dev); ++ ltq_vrx200_hw_exit(dev); ++ ltq_vrx200_mdio_cleanup(dev); ++ unregister_netdev(dev); ++ } ++ return 0; ++} ++ ++static struct platform_driver ltq_mii_driver = { ++ .probe = ltq_vrx200_probe, ++ .remove = __devexit_p(ltq_vrx200_remove), ++ .driver = { ++ .name = "ltq_vrx200", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++module_platform_driver(ltq_mii_driver); ++ ++MODULE_AUTHOR("John Crispin "); ++MODULE_DESCRIPTION("Lantiq SoC ETOP"); ++MODULE_LICENSE("GPL"); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0040-MIPS-NET-several-fixes-to-etop-driver.patch b/target/linux/lantiq/patches-3.3/0040-MIPS-NET-several-fixes-to-etop-driver.patch new file mode 100644 index 0000000000..9fc353442a --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0040-MIPS-NET-several-fixes-to-etop-driver.patch @@ -0,0 +1,445 @@ +From 45af082d0feb0c458d7636f6453702b8dcdfc823 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 21 Mar 2012 18:14:06 +0100 +Subject: [PATCH 40/70] MIPS: NET: several fixes to etop driver + +--- + drivers/net/ethernet/lantiq_etop.c | 208 +++++++++++++++++++----------------- + 1 files changed, 108 insertions(+), 100 deletions(-) + +diff --git a/drivers/net/ethernet/lantiq_etop.c b/drivers/net/ethernet/lantiq_etop.c +index 8fbb069..97ddb09 100644 +--- a/drivers/net/ethernet/lantiq_etop.c ++++ b/drivers/net/ethernet/lantiq_etop.c +@@ -103,15 +103,6 @@ + /* the newer xway socks have a embedded 3/7 port gbit multiplexer */ + #define ltq_has_gbit() (ltq_is_ar9() || ltq_is_vr9()) + +-/* use 2 static channels for TX/RX +- depending on the SoC we need to use different DMA channels for ethernet */ +-#define LTQ_ETOP_TX_CHANNEL 1 +-#define LTQ_ETOP_RX_CHANNEL ((ltq_is_ase()) ? (5) : \ +- ((ltq_has_gbit()) ? (0) : (6))) +- +-#define IS_TX(x) (x == LTQ_ETOP_TX_CHANNEL) +-#define IS_RX(x) (x == LTQ_ETOP_RX_CHANNEL) +- + #define ltq_etop_r32(x) ltq_r32(ltq_etop_membase + (x)) + #define ltq_etop_w32(x, y) ltq_w32(x, ltq_etop_membase + (y)) + #define ltq_etop_w32_mask(x, y, z) \ +@@ -128,8 +119,8 @@ static void __iomem *ltq_etop_membase; + static void __iomem *ltq_gbit_membase; + + struct ltq_etop_chan { +- int idx; + int tx_free; ++ int irq; + struct net_device *netdev; + struct napi_struct napi; + struct ltq_dma_channel dma; +@@ -145,8 +136,8 @@ struct ltq_etop_priv { + struct mii_bus *mii_bus; + struct phy_device *phydev; + +- struct ltq_etop_chan ch[MAX_DMA_CHAN]; +- int tx_free[MAX_DMA_CHAN >> 1]; ++ struct ltq_etop_chan txch; ++ struct ltq_etop_chan rxch; + + spinlock_t lock; + +@@ -207,8 +198,10 @@ ltq_etop_poll_rx(struct napi_struct *napi, int budget) + { + struct ltq_etop_chan *ch = container_of(napi, + struct ltq_etop_chan, napi); ++ struct ltq_etop_priv *priv = netdev_priv(ch->netdev); + int rx = 0; + int complete = 0; ++ unsigned long flags; + + while ((rx < budget) && !complete) { + struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; +@@ -222,7 +215,9 @@ ltq_etop_poll_rx(struct napi_struct *napi, int budget) + } + if (complete || !rx) { + napi_complete(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); + ltq_dma_ack_irq(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); + } + return rx; + } +@@ -234,7 +229,7 @@ ltq_etop_poll_tx(struct napi_struct *napi, int budget) + container_of(napi, struct ltq_etop_chan, napi); + struct ltq_etop_priv *priv = netdev_priv(ch->netdev); + struct netdev_queue *txq = +- netdev_get_tx_queue(ch->netdev, ch->idx >> 1); ++ netdev_get_tx_queue(ch->netdev, ch->dma.nr >> 1); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); +@@ -252,7 +247,9 @@ ltq_etop_poll_tx(struct napi_struct *napi, int budget) + if (netif_tx_queue_stopped(txq)) + netif_tx_start_queue(txq); + napi_complete(&ch->napi); ++ spin_lock_irqsave(&priv->lock, flags); + ltq_dma_ack_irq(&ch->dma); ++ spin_unlock_irqrestore(&priv->lock, flags); + return 1; + } + +@@ -260,9 +257,10 @@ static irqreturn_t + ltq_etop_dma_irq(int irq, void *_priv) + { + struct ltq_etop_priv *priv = _priv; +- int ch = irq - LTQ_DMA_ETOP; +- +- napi_schedule(&priv->ch[ch].napi); ++ if (irq == priv->txch.dma.irq) ++ napi_schedule(&priv->txch.napi); ++ else ++ napi_schedule(&priv->rxch.napi); + return IRQ_HANDLED; + } + +@@ -274,7 +272,7 @@ ltq_etop_free_channel(struct net_device *dev, struct ltq_etop_chan *ch) + ltq_dma_free(&ch->dma); + if (ch->dma.irq) + free_irq(ch->dma.irq, priv); +- if (IS_RX(ch->idx)) { ++ if (ch == &priv->txch) { + int desc; + for (desc = 0; desc < LTQ_DESC_NUM; desc++) + dev_kfree_skb_any(ch->skb[ch->dma.desc]); +@@ -285,7 +283,6 @@ static void + ltq_etop_hw_exit(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; + + clk_disable(priv->clk_ppe); + +@@ -297,9 +294,8 @@ ltq_etop_hw_exit(struct net_device *dev) + clk_disable(priv->clk_ephycgu); + } + +- for (i = 0; i < MAX_DMA_CHAN; i++) +- if (IS_TX(i) || IS_RX(i)) +- ltq_etop_free_channel(dev, &priv->ch[i]); ++ ltq_etop_free_channel(dev, &priv->txch); ++ ltq_etop_free_channel(dev, &priv->rxch); + } + + static void +@@ -327,8 +323,6 @@ ltq_etop_hw_init(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); + unsigned int mii_mode = priv->pldata->mii_mode; +- int err = 0; +- int i; + + clk_enable(priv->clk_ppe); + +@@ -370,31 +364,50 @@ ltq_etop_hw_init(struct net_device *dev) + /* enable crc generation */ + ltq_etop_w32(PPE32_CGEN, LQ_PPE32_ENET_MAC_CFG); + ++ return 0; ++} ++ ++static int ++ltq_etop_dma_init(struct net_device *dev) ++{ ++ struct ltq_etop_priv *priv = netdev_priv(dev); ++ int tx = 1; ++ int rx = ((ltq_is_ase()) ? (5) : \ ++ ((ltq_is_ar9()) ? (0) : (6))); ++ int tx_irq = LTQ_DMA_ETOP + tx; ++ int rx_irq = LTQ_DMA_ETOP + rx; ++ int err; ++ + ltq_dma_init_port(DMA_PORT_ETOP); + +- for (i = 0; i < MAX_DMA_CHAN && !err; i++) { +- int irq = LTQ_DMA_ETOP + i; +- struct ltq_etop_chan *ch = &priv->ch[i]; +- +- ch->idx = ch->dma.nr = i; +- +- if (IS_TX(i)) { +- ltq_dma_alloc_tx(&ch->dma); +- err = request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED, +- "etop_tx", priv); +- } else if (IS_RX(i)) { +- ltq_dma_alloc_rx(&ch->dma); +- for (ch->dma.desc = 0; ch->dma.desc < LTQ_DESC_NUM; +- ch->dma.desc++) +- if (ltq_etop_alloc_skb(ch)) +- err = -ENOMEM; +- ch->dma.desc = 0; +- err = request_irq(irq, ltq_etop_dma_irq, IRQF_DISABLED, +- "etop_rx", priv); ++ priv->txch.dma.nr = tx; ++ ltq_dma_alloc_tx(&priv->txch.dma); ++ err = request_irq(tx_irq, ltq_etop_dma_irq, IRQF_DISABLED, ++ "eth_tx", priv); ++ if (err) { ++ netdev_err(dev, "failed to allocate tx irq\n"); ++ goto err_out; ++ } ++ priv->txch.dma.irq = tx_irq; ++ ++ priv->rxch.dma.nr = rx; ++ ltq_dma_alloc_rx(&priv->rxch.dma); ++ for (priv->rxch.dma.desc = 0; priv->rxch.dma.desc < LTQ_DESC_NUM; ++ priv->rxch.dma.desc++) { ++ if (ltq_etop_alloc_skb(&priv->rxch)) { ++ netdev_err(dev, "failed to allocate skbs\n"); ++ err = -ENOMEM; ++ goto err_out; + } +- if (!err) +- ch->dma.irq = irq; + } ++ priv->rxch.dma.desc = 0; ++ err = request_irq(rx_irq, ltq_etop_dma_irq, IRQF_DISABLED, ++ "eth_rx", priv); ++ if (err) ++ netdev_err(dev, "failed to allocate rx irq\n"); ++ else ++ priv->rxch.dma.irq = rx_irq; ++err_out: + return err; + } + +@@ -411,7 +424,10 @@ ltq_etop_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) + { + struct ltq_etop_priv *priv = netdev_priv(dev); + +- return phy_ethtool_gset(priv->phydev, cmd); ++ if (priv->phydev) ++ return phy_ethtool_gset(priv->phydev, cmd); ++ else ++ return 0; + } + + static int +@@ -419,7 +435,10 @@ ltq_etop_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) + { + struct ltq_etop_priv *priv = netdev_priv(dev); + +- return phy_ethtool_sset(priv->phydev, cmd); ++ if (priv->phydev) ++ return phy_ethtool_sset(priv->phydev, cmd); ++ else ++ return 0; + } + + static int +@@ -427,7 +446,10 @@ ltq_etop_nway_reset(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); + +- return phy_start_aneg(priv->phydev); ++ if (priv->phydev) ++ return phy_start_aneg(priv->phydev); ++ else ++ return 0; + } + + static const struct ethtool_ops ltq_etop_ethtool_ops = { +@@ -620,18 +642,19 @@ static int + ltq_etop_open(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; ++ unsigned long flags; + +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- struct ltq_etop_chan *ch = &priv->ch[i]; ++ napi_enable(&priv->txch.napi); ++ napi_enable(&priv->rxch.napi); ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_open(&priv->txch.dma); ++ ltq_dma_open(&priv->rxch.dma); ++ spin_unlock_irqrestore(&priv->lock, flags); + +- if (!IS_TX(i) && (!IS_RX(i))) +- continue; +- ltq_dma_open(&ch->dma); +- napi_enable(&ch->napi); +- } + if (priv->phydev) + phy_start(priv->phydev); ++ + netif_tx_start_all_queues(dev); + return 0; + } +@@ -640,19 +663,19 @@ static int + ltq_etop_stop(struct net_device *dev) + { + struct ltq_etop_priv *priv = netdev_priv(dev); +- int i; ++ unsigned long flags; + + netif_tx_stop_all_queues(dev); + if (priv->phydev) + phy_stop(priv->phydev); +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- struct ltq_etop_chan *ch = &priv->ch[i]; ++ napi_disable(&priv->txch.napi); ++ napi_disable(&priv->rxch.napi); ++ ++ spin_lock_irqsave(&priv->lock, flags); ++ ltq_dma_close(&priv->txch.dma); ++ ltq_dma_close(&priv->rxch.dma); ++ spin_unlock_irqrestore(&priv->lock, flags); + +- if (!IS_RX(i) && !IS_TX(i)) +- continue; +- napi_disable(&ch->napi); +- ltq_dma_close(&ch->dma); +- } + return 0; + } + +@@ -662,16 +685,16 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) + int queue = skb_get_queue_mapping(skb); + struct netdev_queue *txq = netdev_get_tx_queue(dev, queue); + struct ltq_etop_priv *priv = netdev_priv(dev); +- struct ltq_etop_chan *ch = &priv->ch[(queue << 1) | 1]; +- struct ltq_dma_desc *desc = &ch->dma.desc_base[ch->dma.desc]; ++ struct ltq_dma_desc *desc = ++ &priv->txch.dma.desc_base[priv->txch.dma.desc]; + unsigned long flags; + u32 byte_offset; + int len; + + len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + +- if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ch->skb[ch->dma.desc]) { +- dev_kfree_skb_any(skb); ++ if ((desc->ctl & (LTQ_DMA_OWN | LTQ_DMA_C)) || ++ priv->txch.skb[priv->txch.dma.desc]) { + netdev_err(dev, "tx ring full\n"); + netif_tx_stop_queue(txq); + return NETDEV_TX_BUSY; +@@ -679,7 +702,7 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) + + /* dma needs to start on a 16 byte aligned address */ + byte_offset = CPHYSADDR(skb->data) % 16; +- ch->skb[ch->dma.desc] = skb; ++ priv->txch.skb[priv->txch.dma.desc] = skb; + + dev->trans_start = jiffies; + +@@ -689,11 +712,11 @@ ltq_etop_tx(struct sk_buff *skb, struct net_device *dev) + wmb(); + desc->ctl = LTQ_DMA_OWN | LTQ_DMA_SOP | LTQ_DMA_EOP | + LTQ_DMA_TX_OFFSET(byte_offset) | (len & LTQ_DMA_SIZE_MASK); +- ch->dma.desc++; +- ch->dma.desc %= LTQ_DESC_NUM; ++ priv->txch.dma.desc++; ++ priv->txch.dma.desc %= LTQ_DESC_NUM; + spin_unlock_irqrestore(&priv->lock, flags); + +- if (ch->dma.desc_base[ch->dma.desc].ctl & LTQ_DMA_OWN) ++ if (priv->txch.dma.desc_base[priv->txch.dma.desc].ctl & LTQ_DMA_OWN) + netif_tx_stop_queue(txq); + + return NETDEV_TX_OK; +@@ -778,6 +801,10 @@ ltq_etop_init(struct net_device *dev) + err = ltq_etop_hw_init(dev); + if (err) + goto err_hw; ++ err = ltq_etop_dma_init(dev); ++ if (err) ++ goto err_hw; ++ + ltq_etop_change_mtu(dev, 1500); + + memcpy(&mac, &priv->pldata->mac, sizeof(struct sockaddr)); +@@ -813,6 +840,9 @@ ltq_etop_tx_timeout(struct net_device *dev) + err = ltq_etop_hw_init(dev); + if (err) + goto err_hw; ++ err = ltq_etop_dma_init(dev); ++ if (err) ++ goto err_hw; + dev->trans_start = jiffies; + netif_wake_queue(dev); + return; +@@ -836,14 +866,13 @@ static const struct net_device_ops ltq_eth_netdev_ops = { + .ndo_tx_timeout = ltq_etop_tx_timeout, + }; + +-static int __init ++static int __devinit + ltq_etop_probe(struct platform_device *pdev) + { + struct net_device *dev; + struct ltq_etop_priv *priv; + struct resource *res, *gbit_res; + int err; +- int i; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { +@@ -920,15 +949,10 @@ ltq_etop_probe(struct platform_device *pdev) + + spin_lock_init(&priv->lock); + +- for (i = 0; i < MAX_DMA_CHAN; i++) { +- if (IS_TX(i)) +- netif_napi_add(dev, &priv->ch[i].napi, +- ltq_etop_poll_tx, 8); +- else if (IS_RX(i)) +- netif_napi_add(dev, &priv->ch[i].napi, +- ltq_etop_poll_rx, 32); +- priv->ch[i].netdev = dev; +- } ++ netif_napi_add(dev, &priv->txch.napi, ltq_etop_poll_tx, 8); ++ netif_napi_add(dev, &priv->rxch.napi, ltq_etop_poll_rx, 32); ++ priv->txch.netdev = dev; ++ priv->rxch.netdev = dev; + + err = register_netdev(dev); + if (err) +@@ -958,6 +982,7 @@ ltq_etop_remove(struct platform_device *pdev) + } + + static struct platform_driver ltq_mii_driver = { ++ .probe = ltq_etop_probe, + .remove = __devexit_p(ltq_etop_remove), + .driver = { + .name = "ltq_etop", +@@ -965,24 +990,7 @@ static struct platform_driver ltq_mii_driver = { + }, + }; + +-int __init +-init_ltq_etop(void) +-{ +- int ret = platform_driver_probe(<q_mii_driver, ltq_etop_probe); +- +- if (ret) +- pr_err("ltq_etop: Error registering platfom driver!"); +- return ret; +-} +- +-static void __exit +-exit_ltq_etop(void) +-{ +- platform_driver_unregister(<q_mii_driver); +-} +- +-module_init(init_ltq_etop); +-module_exit(exit_ltq_etop); ++module_platform_driver(ltq_mii_driver); + + MODULE_AUTHOR("John Crispin "); + MODULE_DESCRIPTION("Lantiq SoC ETOP"); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0041-MTD-MIPS-lantiq-use-module_platform_driver-inside-la.patch b/target/linux/lantiq/patches-3.3/0041-MTD-MIPS-lantiq-use-module_platform_driver-inside-la.patch new file mode 100644 index 0000000000..28a20f4235 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0041-MTD-MIPS-lantiq-use-module_platform_driver-inside-la.patch @@ -0,0 +1,64 @@ +From 94ca79b8bd9bf8ec5dcd993e0f004056f12f16f4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 Feb 2012 12:15:25 +0100 +Subject: [PATCH 41/70] MTD: MIPS: lantiq: use module_platform_driver inside + lantiq map driver + +Reduce boilerplate code by converting driver to module_platform_driver. + +Signed-off-by: John Crispin +Cc: linux-mtd@lists.infradead.org +--- + drivers/mtd/maps/lantiq-flash.c | 22 +++------------------- + 1 files changed, 3 insertions(+), 19 deletions(-) + +diff --git a/drivers/mtd/maps/lantiq-flash.c b/drivers/mtd/maps/lantiq-flash.c +index cf7a3cd..ccc6954 100644 +--- a/drivers/mtd/maps/lantiq-flash.c ++++ b/drivers/mtd/maps/lantiq-flash.c +@@ -108,7 +108,7 @@ ltq_copy_to(struct map_info *map, unsigned long to, + spin_unlock_irqrestore(&ebu_lock, flags); + } + +-static int __init ++static int __devinit + ltq_mtd_probe(struct platform_device *pdev) + { + struct physmap_flash_data *ltq_mtd_data = dev_get_platdata(&pdev->dev); +@@ -204,6 +204,7 @@ ltq_mtd_remove(struct platform_device *pdev) + } + + static struct platform_driver ltq_mtd_driver = { ++ .probe = ltq_mtd_probe, + .remove = __devexit_p(ltq_mtd_remove), + .driver = { + .name = "ltq_nor", +@@ -211,24 +212,7 @@ static struct platform_driver ltq_mtd_driver = { + }, + }; + +-static int __init +-init_ltq_mtd(void) +-{ +- int ret = platform_driver_probe(<q_mtd_driver, ltq_mtd_probe); +- +- if (ret) +- pr_err("ltq_nor: error registering platform driver"); +- return ret; +-} +- +-static void __exit +-exit_ltq_mtd(void) +-{ +- platform_driver_unregister(<q_mtd_driver); +-} +- +-module_init(init_ltq_mtd); +-module_exit(exit_ltq_mtd); ++module_platform_driver(ltq_mtd_driver); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("John Crispin "); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0042-WDT-MIPS-lantiq-use-module_platform_driver-inside-la.patch b/target/linux/lantiq/patches-3.3/0042-WDT-MIPS-lantiq-use-module_platform_driver-inside-la.patch new file mode 100644 index 0000000000..07cf338f80 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0042-WDT-MIPS-lantiq-use-module_platform_driver-inside-la.patch @@ -0,0 +1,61 @@ +From 9004f152deddba21ab7b0fa3ba0b243fb5cbd5a1 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 20 Feb 2012 12:16:31 +0100 +Subject: [PATCH 42/70] WDT: MIPS: lantiq: use module_platform_driver inside + lantiq watchdog driver + +Reduce boilerplate code by converting driver to module_platform_driver. + +Signed-off-by: John Crispin +Cc: linux-watchdog@vger.kernel.org +--- + drivers/watchdog/lantiq_wdt.c | 19 +++---------------- + 1 files changed, 3 insertions(+), 16 deletions(-) + +diff --git a/drivers/watchdog/lantiq_wdt.c b/drivers/watchdog/lantiq_wdt.c +index fa4866b..70127b3 100644 +--- a/drivers/watchdog/lantiq_wdt.c ++++ b/drivers/watchdog/lantiq_wdt.c +@@ -182,7 +182,7 @@ static struct miscdevice ltq_wdt_miscdev = { + .fops = <q_wdt_fops, + }; + +-static int __init ++static int __devinit + ltq_wdt_probe(struct platform_device *pdev) + { + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +@@ -227,6 +227,7 @@ ltq_wdt_remove(struct platform_device *pdev) + + + static struct platform_driver ltq_wdt_driver = { ++ .probe = ltq_wdt_probe, + .remove = __devexit_p(ltq_wdt_remove), + .driver = { + .name = "ltq_wdt", +@@ -234,21 +235,7 @@ static struct platform_driver ltq_wdt_driver = { + }, + }; + +-static int __init +-init_ltq_wdt(void) +-{ +- return platform_driver_probe(<q_wdt_driver, ltq_wdt_probe); +-} +- +-static void __exit +-exit_ltq_wdt(void) +-{ +- return platform_driver_unregister(<q_wdt_driver); +-} +- +-module_init(init_ltq_wdt); +-module_exit(exit_ltq_wdt); +- ++module_platform_driver(ltq_wdt_driver); + module_param(nowayout, int, 0); + MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started"); + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0043-MIPS-lantiq-adds-GPTU-driver.patch b/target/linux/lantiq/patches-3.3/0043-MIPS-lantiq-adds-GPTU-driver.patch new file mode 100644 index 0000000000..64008ca6fe --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0043-MIPS-lantiq-adds-GPTU-driver.patch @@ -0,0 +1,1052 @@ +From 468364b5da3a0459a313bb9e477bff031f6a9b09 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 29 Sep 2011 17:16:38 +0200 +Subject: [PATCH 43/70] MIPS: lantiq: adds GPTU driver + +--- + arch/mips/include/asm/mach-lantiq/lantiq_timer.h | 155 ++++ + arch/mips/lantiq/xway/Makefile | 2 +- + arch/mips/lantiq/xway/sysctrl.c | 1 + + arch/mips/lantiq/xway/timer.c | 846 ++++++++++++++++++++++ + 4 files changed, 1003 insertions(+), 1 deletions(-) + create mode 100644 arch/mips/include/asm/mach-lantiq/lantiq_timer.h + create mode 100644 arch/mips/lantiq/xway/timer.c + +diff --git a/arch/mips/include/asm/mach-lantiq/lantiq_timer.h b/arch/mips/include/asm/mach-lantiq/lantiq_timer.h +new file mode 100644 +index 0000000..ef564ab +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/lantiq_timer.h +@@ -0,0 +1,155 @@ ++#ifndef __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ ++#define __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ ++ ++ ++/****************************************************************************** ++ Copyright (c) 2002, Infineon Technologies. All rights reserved. ++ ++ No Warranty ++ Because the program is licensed free of charge, there is no warranty for ++ the program, to the extent permitted by applicable law. Except when ++ otherwise stated in writing the copyright holders and/or other parties ++ provide the program "as is" without warranty of any kind, either ++ expressed or implied, including, but not limited to, the implied ++ warranties of merchantability and fitness for a particular purpose. The ++ entire risk as to the quality and performance of the program is with ++ you. should the program prove defective, you assume the cost of all ++ necessary servicing, repair or correction. ++ ++ In no event unless required by applicable law or agreed to in writing ++ will any copyright holder, or any other party who may modify and/or ++ redistribute the program as permitted above, be liable to you for ++ damages, including any general, special, incidental or consequential ++ damages arising out of the use or inability to use the program ++ (including but not limited to loss of data or data being rendered ++ inaccurate or losses sustained by you or third parties or a failure of ++ the program to operate with any other programs), even if such holder or ++ other party has been advised of the possibility of such damages. ++******************************************************************************/ ++ ++ ++/* ++ * #################################### ++ * Definition ++ * #################################### ++ */ ++ ++/* ++ * Available Timer/Counter Index ++ */ ++#define TIMER(n, X) (n * 2 + (X ? 1 : 0)) ++#define TIMER_ANY 0x00 ++#define TIMER1A TIMER(1, 0) ++#define TIMER1B TIMER(1, 1) ++#define TIMER2A TIMER(2, 0) ++#define TIMER2B TIMER(2, 1) ++#define TIMER3A TIMER(3, 0) ++#define TIMER3B TIMER(3, 1) ++ ++/* ++ * Flag of Timer/Counter ++ * These flags specify the way in which timer is configured. ++ */ ++/* Bit size of timer/counter. */ ++#define TIMER_FLAG_16BIT 0x0000 ++#define TIMER_FLAG_32BIT 0x0001 ++/* Switch between timer and counter. */ ++#define TIMER_FLAG_TIMER 0x0000 ++#define TIMER_FLAG_COUNTER 0x0002 ++/* Stop or continue when overflowing/underflowing. */ ++#define TIMER_FLAG_ONCE 0x0000 ++#define TIMER_FLAG_CYCLIC 0x0004 ++/* Count up or counter down. */ ++#define TIMER_FLAG_UP 0x0000 ++#define TIMER_FLAG_DOWN 0x0008 ++/* Count on specific level or edge. */ ++#define TIMER_FLAG_HIGH_LEVEL_SENSITIVE 0x0000 ++#define TIMER_FLAG_LOW_LEVEL_SENSITIVE 0x0040 ++#define TIMER_FLAG_RISE_EDGE 0x0010 ++#define TIMER_FLAG_FALL_EDGE 0x0020 ++#define TIMER_FLAG_ANY_EDGE 0x0030 ++/* Signal is syncronous to module clock or not. */ ++#define TIMER_FLAG_UNSYNC 0x0000 ++#define TIMER_FLAG_SYNC 0x0080 ++/* Different interrupt handle type. */ ++#define TIMER_FLAG_NO_HANDLE 0x0000 ++#if defined(__KERNEL__) ++ #define TIMER_FLAG_CALLBACK_IN_IRQ 0x0100 ++#endif // defined(__KERNEL__) ++#define TIMER_FLAG_SIGNAL 0x0300 ++/* Internal clock source or external clock source */ ++#define TIMER_FLAG_INT_SRC 0x0000 ++#define TIMER_FLAG_EXT_SRC 0x1000 ++ ++ ++/* ++ * ioctl Command ++ */ ++#define GPTU_REQUEST_TIMER 0x01 /* General method to setup timer/counter. */ ++#define GPTU_FREE_TIMER 0x02 /* Free timer/counter. */ ++#define GPTU_START_TIMER 0x03 /* Start or resume timer/counter. */ ++#define GPTU_STOP_TIMER 0x04 /* Suspend timer/counter. */ ++#define GPTU_GET_COUNT_VALUE 0x05 /* Get current count value. */ ++#define GPTU_CALCULATE_DIVIDER 0x06 /* Calculate timer divider from given freq.*/ ++#define GPTU_SET_TIMER 0x07 /* Simplified method to setup timer. */ ++#define GPTU_SET_COUNTER 0x08 /* Simplified method to setup counter. */ ++ ++/* ++ * Data Type Used to Call ioctl ++ */ ++struct gptu_ioctl_param { ++ unsigned int timer; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and * ++ * GPTU_SET_COUNTER, this field is ID of expected * ++ * timer/counter. If it's zero, a timer/counter would * ++ * be dynamically allocated and ID would be stored in * ++ * this field. * ++ * In command GPTU_GET_COUNT_VALUE, this field is * ++ * ignored. * ++ * In other command, this field is ID of timer/counter * ++ * allocated. */ ++ unsigned int flag; /* In command GPTU_REQUEST_TIMER, GPTU_SET_TIMER, and * ++ * GPTU_SET_COUNTER, this field contains flags to * ++ * specify how to configure timer/counter. * ++ * In command GPTU_START_TIMER, zero indicate start * ++ * and non-zero indicate resume timer/counter. * ++ * In other command, this field is ignored. */ ++ unsigned long value; /* In command GPTU_REQUEST_TIMER, this field contains * ++ * init/reload value. * ++ * In command GPTU_SET_TIMER, this field contains * ++ * frequency (0.001Hz) of timer. * ++ * In command GPTU_GET_COUNT_VALUE, current count * ++ * value would be stored in this field. * ++ * In command GPTU_CALCULATE_DIVIDER, this field * ++ * contains frequency wanted, and after calculation, * ++ * divider would be stored in this field to overwrite * ++ * the frequency. * ++ * In other command, this field is ignored. */ ++ int pid; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, * ++ * if signal is required, this field contains process * ++ * ID to which signal would be sent. * ++ * In other command, this field is ignored. */ ++ int sig; /* In command GPTU_REQUEST_TIMER and GPTU_SET_TIMER, * ++ * if signal is required, this field contains signal * ++ * number which would be sent. * ++ * In other command, this field is ignored. */ ++}; ++ ++/* ++ * #################################### ++ * Data Type ++ * #################################### ++ */ ++typedef void (*timer_callback)(unsigned long arg); ++ ++extern int lq_request_timer(unsigned int, unsigned int, unsigned long, unsigned long, unsigned long); ++extern int lq_free_timer(unsigned int); ++extern int lq_start_timer(unsigned int, int); ++extern int lq_stop_timer(unsigned int); ++extern int lq_reset_counter_flags(u32 timer, u32 flags); ++extern int lq_get_count_value(unsigned int, unsigned long *); ++extern u32 lq_cal_divider(unsigned long); ++extern int lq_set_timer(unsigned int, unsigned int, int, int, unsigned int, unsigned long, unsigned long); ++extern int lq_set_counter(unsigned int timer, unsigned int flag, ++ u32 reload, unsigned long arg1, unsigned long arg2); ++ ++#endif /* __DANUBE_GPTU_DEV_H__2005_07_26__10_19__ */ +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 277aa34..4c3106f 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,4 +1,4 @@ +-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o nand.o ++obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o nand.o timer.o + + obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o + obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 38f02f9..1a2e2d4 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -147,6 +147,7 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_dma", NULL, 0, PMU_DMA); + clkdev_add_pmu("ltq_stp", NULL, 0, PMU_STP); + clkdev_add_pmu("ltq_spi", NULL, 0, PMU_SPI); ++ clkdev_add_pmu("ltq_gptu", NULL, 0, PMU_GPT); + if (!ltq_is_vr9()) + clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE); + if (ltq_is_ase()) { +diff --git a/arch/mips/lantiq/xway/timer.c b/arch/mips/lantiq/xway/timer.c +new file mode 100644 +index 0000000..9794c87 +--- /dev/null ++++ b/arch/mips/lantiq/xway/timer.c +@@ -0,0 +1,846 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include "../clk.h" ++ ++#include ++#include ++#include ++ ++#define MAX_NUM_OF_32BIT_TIMER_BLOCKS 6 ++ ++#ifdef TIMER1A ++#define FIRST_TIMER TIMER1A ++#else ++#define FIRST_TIMER 2 ++#endif ++ ++/* ++ * GPTC divider is set or not. ++ */ ++#define GPTU_CLC_RMC_IS_SET 0 ++ ++/* ++ * Timer Interrupt (IRQ) ++ */ ++/* Must be adjusted when ICU driver is available */ ++#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22) ++ ++/* ++ * Bits Operation ++ */ ++#define GET_BITS(x, msb, lsb) \ ++ (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb)) ++#define SET_BITS(x, msb, lsb, value) \ ++ (((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | \ ++ (((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb))) ++ ++/* ++ * GPTU Register Mapping ++ */ ++#define LQ_GPTU (KSEG1 + 0x1E100A00) ++#define LQ_GPTU_CLC ((volatile u32 *)(LQ_GPTU + 0x0000)) ++#define LQ_GPTU_ID ((volatile u32 *)(LQ_GPTU + 0x0008)) ++#define LQ_GPTU_CON(n, X) ((volatile u32 *)(LQ_GPTU + 0x0010 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_RUN(n, X) ((volatile u32 *)(LQ_GPTU + 0x0018 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_RELOAD(n, X) ((volatile u32 *)(LQ_GPTU + 0x0020 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_COUNT(n, X) ((volatile u32 *)(LQ_GPTU + 0x0028 + ((X) * 4) + ((n) - 1) * 0x0020)) /* X must be either A or B */ ++#define LQ_GPTU_IRNEN ((volatile u32 *)(LQ_GPTU + 0x00F4)) ++#define LQ_GPTU_IRNICR ((volatile u32 *)(LQ_GPTU + 0x00F8)) ++#define LQ_GPTU_IRNCR ((volatile u32 *)(LQ_GPTU + 0x00FC)) ++ ++/* ++ * Clock Control Register ++ */ ++#define GPTU_CLC_SMC GET_BITS(*LQ_GPTU_CLC, 23, 16) ++#define GPTU_CLC_RMC GET_BITS(*LQ_GPTU_CLC, 15, 8) ++#define GPTU_CLC_FSOE (*LQ_GPTU_CLC & (1 << 5)) ++#define GPTU_CLC_EDIS (*LQ_GPTU_CLC & (1 << 3)) ++#define GPTU_CLC_SPEN (*LQ_GPTU_CLC & (1 << 2)) ++#define GPTU_CLC_DISS (*LQ_GPTU_CLC & (1 << 1)) ++#define GPTU_CLC_DISR (*LQ_GPTU_CLC & (1 << 0)) ++ ++#define GPTU_CLC_SMC_SET(value) SET_BITS(0, 23, 16, (value)) ++#define GPTU_CLC_RMC_SET(value) SET_BITS(0, 15, 8, (value)) ++#define GPTU_CLC_FSOE_SET(value) ((value) ? (1 << 5) : 0) ++#define GPTU_CLC_SBWE_SET(value) ((value) ? (1 << 4) : 0) ++#define GPTU_CLC_EDIS_SET(value) ((value) ? (1 << 3) : 0) ++#define GPTU_CLC_SPEN_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_CLC_DISR_SET(value) ((value) ? (1 << 0) : 0) ++ ++/* ++ * ID Register ++ */ ++#define GPTU_ID_ID GET_BITS(*LQ_GPTU_ID, 15, 8) ++#define GPTU_ID_CFG GET_BITS(*LQ_GPTU_ID, 7, 5) ++#define GPTU_ID_REV GET_BITS(*LQ_GPTU_ID, 4, 0) ++ ++/* ++ * Control Register of Timer/Counter nX ++ * n is the index of block (1 based index) ++ * X is either A or B ++ */ ++#define GPTU_CON_SRC_EG(n, X) (*LQ_GPTU_CON(n, X) & (1 << 10)) ++#define GPTU_CON_SRC_EXT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 9)) ++#define GPTU_CON_SYNC(n, X) (*LQ_GPTU_CON(n, X) & (1 << 8)) ++#define GPTU_CON_EDGE(n, X) GET_BITS(*LQ_GPTU_CON(n, X), 7, 6) ++#define GPTU_CON_INV(n, X) (*LQ_GPTU_CON(n, X) & (1 << 5)) ++#define GPTU_CON_EXT(n, X) (*LQ_GPTU_CON(n, A) & (1 << 4)) /* Timer/Counter B does not have this bit */ ++#define GPTU_CON_STP(n, X) (*LQ_GPTU_CON(n, X) & (1 << 3)) ++#define GPTU_CON_CNT(n, X) (*LQ_GPTU_CON(n, X) & (1 << 2)) ++#define GPTU_CON_DIR(n, X) (*LQ_GPTU_CON(n, X) & (1 << 1)) ++#define GPTU_CON_EN(n, X) (*LQ_GPTU_CON(n, X) & (1 << 0)) ++ ++#define GPTU_CON_SRC_EG_SET(value) ((value) ? 0 : (1 << 10)) ++#define GPTU_CON_SRC_EXT_SET(value) ((value) ? (1 << 9) : 0) ++#define GPTU_CON_SYNC_SET(value) ((value) ? (1 << 8) : 0) ++#define GPTU_CON_EDGE_SET(value) SET_BITS(0, 7, 6, (value)) ++#define GPTU_CON_INV_SET(value) ((value) ? (1 << 5) : 0) ++#define GPTU_CON_EXT_SET(value) ((value) ? (1 << 4) : 0) ++#define GPTU_CON_STP_SET(value) ((value) ? (1 << 3) : 0) ++#define GPTU_CON_CNT_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_CON_DIR_SET(value) ((value) ? (1 << 1) : 0) ++ ++#define GPTU_RUN_RL_SET(value) ((value) ? (1 << 2) : 0) ++#define GPTU_RUN_CEN_SET(value) ((value) ? (1 << 1) : 0) ++#define GPTU_RUN_SEN_SET(value) ((value) ? (1 << 0) : 0) ++ ++#define GPTU_IRNEN_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0) ++#define GPTU_IRNCR_TC_SET(n, X, value) ((value) ? (1 << (((n) - 1) * 2 + (X))) : 0) ++ ++#define TIMER_FLAG_MASK_SIZE(x) (x & 0x0001) ++#define TIMER_FLAG_MASK_TYPE(x) (x & 0x0002) ++#define TIMER_FLAG_MASK_STOP(x) (x & 0x0004) ++#define TIMER_FLAG_MASK_DIR(x) (x & 0x0008) ++#define TIMER_FLAG_NONE_EDGE 0x0000 ++#define TIMER_FLAG_MASK_EDGE(x) (x & 0x0030) ++#define TIMER_FLAG_REAL 0x0000 ++#define TIMER_FLAG_INVERT 0x0040 ++#define TIMER_FLAG_MASK_INVERT(x) (x & 0x0040) ++#define TIMER_FLAG_MASK_TRIGGER(x) (x & 0x0070) ++#define TIMER_FLAG_MASK_SYNC(x) (x & 0x0080) ++#define TIMER_FLAG_CALLBACK_IN_HB 0x0200 ++#define TIMER_FLAG_MASK_HANDLE(x) (x & 0x0300) ++#define TIMER_FLAG_MASK_SRC(x) (x & 0x1000) ++ ++struct timer_dev_timer { ++ unsigned int f_irq_on; ++ unsigned int irq; ++ unsigned int flag; ++ unsigned long arg1; ++ unsigned long arg2; ++}; ++ ++struct timer_dev { ++ struct mutex gptu_mutex; ++ unsigned int number_of_timers; ++ unsigned int occupation; ++ unsigned int f_gptu_on; ++ struct timer_dev_timer timer[MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2]; ++}; ++ ++unsigned long ltq_danube_fpi_bus_clock(int fpi); ++unsigned long ltq_vr9_fpi_bus_clock(int fpi); ++ ++unsigned int ltq_get_fpi_bus_clock(int fpi) { ++ if (ltq_is_ase()) ++ return CLOCK_133M; ++ else if (ltq_is_vr9()) ++ return ltq_vr9_fpi_bus_clock(fpi); ++ ++ return ltq_danube_fpi_bus_clock(fpi); ++} ++ ++ ++static long gptu_ioctl(struct file *, unsigned int, unsigned long); ++static int gptu_open(struct inode *, struct file *); ++static int gptu_release(struct inode *, struct file *); ++ ++static struct file_operations gptu_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = gptu_ioctl, ++ .open = gptu_open, ++ .release = gptu_release ++}; ++ ++static struct miscdevice gptu_miscdev = { ++ .minor = MISC_DYNAMIC_MINOR, ++ .name = "gptu", ++ .fops = &gptu_fops, ++}; ++ ++static struct timer_dev timer_dev; ++ ++static irqreturn_t timer_irq_handler(int irq, void *p) ++{ ++ unsigned int timer; ++ unsigned int flag; ++ struct timer_dev_timer *dev_timer = (struct timer_dev_timer *)p; ++ ++ timer = irq - TIMER_INTERRUPT; ++ if (timer < timer_dev.number_of_timers ++ && dev_timer == &timer_dev.timer[timer]) { ++ /* Clear interrupt. */ ++ ltq_w32(1 << timer, LQ_GPTU_IRNCR); ++ ++ /* Call user hanler or signal. */ ++ flag = dev_timer->flag; ++ if (!(timer & 0x01) ++ || TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) { ++ /* 16-bit timer or timer A of 32-bit timer */ ++ switch (TIMER_FLAG_MASK_HANDLE(flag)) { ++ case TIMER_FLAG_CALLBACK_IN_IRQ: ++ case TIMER_FLAG_CALLBACK_IN_HB: ++ if (dev_timer->arg1) ++ (*(timer_callback)dev_timer->arg1)(dev_timer->arg2); ++ break; ++ case TIMER_FLAG_SIGNAL: ++ send_sig((int)dev_timer->arg2, (struct task_struct *)dev_timer->arg1, 0); ++ break; ++ } ++ } ++ } ++ return IRQ_HANDLED; ++} ++ ++static inline void lq_enable_gptu(void) ++{ ++ struct clk *clk = clk_get_sys("ltq_gptu", NULL); ++ clk_enable(clk); ++ ++ //ltq_pmu_enable(PMU_GPT); ++ ++ /* Set divider as 1, disable write protection for SPEN, enable module. */ ++ *LQ_GPTU_CLC = ++ GPTU_CLC_SMC_SET(0x00) | ++ GPTU_CLC_RMC_SET(0x01) | ++ GPTU_CLC_FSOE_SET(0) | ++ GPTU_CLC_SBWE_SET(1) | ++ GPTU_CLC_EDIS_SET(0) | ++ GPTU_CLC_SPEN_SET(0) | ++ GPTU_CLC_DISR_SET(0); ++} ++ ++static inline void lq_disable_gptu(void) ++{ ++ struct clk *clk = clk_get_sys("ltq_gptu", NULL); ++ ltq_w32(0x00, LQ_GPTU_IRNEN); ++ ltq_w32(0xfff, LQ_GPTU_IRNCR); ++ ++ /* Set divider as 0, enable write protection for SPEN, disable module. */ ++ *LQ_GPTU_CLC = ++ GPTU_CLC_SMC_SET(0x00) | ++ GPTU_CLC_RMC_SET(0x00) | ++ GPTU_CLC_FSOE_SET(0) | ++ GPTU_CLC_SBWE_SET(0) | ++ GPTU_CLC_EDIS_SET(0) | ++ GPTU_CLC_SPEN_SET(0) | ++ GPTU_CLC_DISR_SET(1); ++ ++ clk_enable(clk); ++} ++ ++int lq_request_timer(unsigned int timer, unsigned int flag, ++ unsigned long value, unsigned long arg1, unsigned long arg2) ++{ ++ int ret = 0; ++ unsigned int con_reg, irnen_reg; ++ int n, X; ++ ++ if (timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ printk(KERN_INFO "request_timer(%d, 0x%08X, %lu)...", ++ timer, flag, value); ++ ++ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) ++ value &= 0xFFFF; ++ else ++ timer &= ~0x01; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ /* ++ * Allocate timer. ++ */ ++ if (timer < FIRST_TIMER) { ++ unsigned int mask; ++ unsigned int shift; ++ /* This takes care of TIMER1B which is the only choice for Voice TAPI system */ ++ unsigned int offset = TIMER2A; ++ ++ /* ++ * Pick up a free timer. ++ */ ++ if (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT) { ++ mask = 1 << offset; ++ shift = 1; ++ } else { ++ mask = 3 << offset; ++ shift = 2; ++ } ++ for (timer = offset; ++ timer < offset + timer_dev.number_of_timers; ++ timer += shift, mask <<= shift) ++ if (!(timer_dev.occupation & mask)) { ++ timer_dev.occupation |= mask; ++ break; ++ } ++ if (timer >= offset + timer_dev.number_of_timers) { ++ printk("failed![%d]\n", __LINE__); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } else ++ ret = timer; ++ } else { ++ register unsigned int mask; ++ ++ /* ++ * Check if the requested timer is free. ++ */ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if ((timer_dev.occupation & mask)) { ++ printk("failed![%d] mask %#x, timer_dev.occupation %#x\n", ++ __LINE__, mask, timer_dev.occupation); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EBUSY; ++ } else { ++ timer_dev.occupation |= mask; ++ ret = 0; ++ } ++ } ++ ++ /* ++ * Prepare control register value. ++ */ ++ switch (TIMER_FLAG_MASK_EDGE(flag)) { ++ default: ++ case TIMER_FLAG_NONE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x00); ++ break; ++ case TIMER_FLAG_RISE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x01); ++ break; ++ case TIMER_FLAG_FALL_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x02); ++ break; ++ case TIMER_FLAG_ANY_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x03); ++ break; ++ } ++ if (TIMER_FLAG_MASK_TYPE(flag) == TIMER_FLAG_TIMER) ++ con_reg |= ++ TIMER_FLAG_MASK_SRC(flag) == ++ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : ++ GPTU_CON_SRC_EXT_SET(0); ++ else ++ con_reg |= ++ TIMER_FLAG_MASK_SRC(flag) == ++ TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : ++ GPTU_CON_SRC_EG_SET(0); ++ con_reg |= ++ TIMER_FLAG_MASK_SYNC(flag) == ++ TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : ++ GPTU_CON_SYNC_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_INVERT(flag) == ++ TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_SIZE(flag) == ++ TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : ++ GPTU_CON_EXT_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_STOP(flag) == ++ TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0); ++ con_reg |= ++ TIMER_FLAG_MASK_TYPE(flag) == ++ TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : ++ GPTU_CON_CNT_SET(1); ++ con_reg |= ++ TIMER_FLAG_MASK_DIR(flag) == ++ TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0); ++ ++ /* ++ * Fill up running data. ++ */ ++ timer_dev.timer[timer - FIRST_TIMER].flag = flag; ++ timer_dev.timer[timer - FIRST_TIMER].arg1 = arg1; ++ timer_dev.timer[timer - FIRST_TIMER].arg2 = arg2; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flag; ++ ++ /* ++ * Enable GPTU module. ++ */ ++ if (!timer_dev.f_gptu_on) { ++ lq_enable_gptu(); ++ timer_dev.f_gptu_on = 1; ++ } ++ ++ /* ++ * Enable IRQ. ++ */ ++ if (TIMER_FLAG_MASK_HANDLE(flag) != TIMER_FLAG_NO_HANDLE) { ++ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL) ++ timer_dev.timer[timer - FIRST_TIMER].arg1 = ++ (unsigned long) find_task_by_vpid((int) arg1); ++ ++ irnen_reg = 1 << (timer - FIRST_TIMER); ++ ++ if (TIMER_FLAG_MASK_HANDLE(flag) == TIMER_FLAG_SIGNAL ++ || (TIMER_FLAG_MASK_HANDLE(flag) == ++ TIMER_FLAG_CALLBACK_IN_IRQ ++ && timer_dev.timer[timer - FIRST_TIMER].arg1)) { ++ enable_irq(timer_dev.timer[timer - FIRST_TIMER].irq); ++ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 1; ++ } ++ } else ++ irnen_reg = 0; ++ ++ /* ++ * Write config register, reload value and enable interrupt. ++ */ ++ n = timer >> 1; ++ X = timer & 0x01; ++ *LQ_GPTU_CON(n, X) = con_reg; ++ *LQ_GPTU_RELOAD(n, X) = value; ++ /* printk("reload value = %d\n", (u32)value); */ ++ *LQ_GPTU_IRNEN |= irnen_reg; ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ printk("successful!\n"); ++ return ret; ++} ++EXPORT_SYMBOL(lq_request_timer); ++ ++int lq_free_timer(unsigned int timer) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ if (GPTU_CON_EN(n, X)) ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1); ++ ++ *LQ_GPTU_IRNEN &= ~GPTU_IRNEN_TC_SET(n, X, 1); ++ *LQ_GPTU_IRNCR |= GPTU_IRNCR_TC_SET(n, X, 1); ++ ++ if (timer_dev.timer[timer - FIRST_TIMER].f_irq_on) { ++ disable_irq(timer_dev.timer[timer - FIRST_TIMER].irq); ++ timer_dev.timer[timer - FIRST_TIMER].f_irq_on = 0; ++ } ++ ++ timer_dev.occupation &= ~mask; ++ if (!timer_dev.occupation && timer_dev.f_gptu_on) { ++ lq_disable_gptu(); ++ timer_dev.f_gptu_on = 0; ++ } ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_free_timer); ++ ++int lq_start_timer(unsigned int timer, int is_resume) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == ++ TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_RL_SET(!is_resume) | GPTU_RUN_SEN_SET(1); ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_start_timer); ++ ++int lq_stop_timer(unsigned int timer) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER ++ || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_RUN(n, X) = GPTU_RUN_CEN_SET(1); ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_stop_timer); ++ ++int lq_reset_counter_flags(u32 timer, u32 flags) ++{ ++ unsigned int oflag; ++ unsigned int mask, con_reg; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ oflag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(oflag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(oflag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ switch (TIMER_FLAG_MASK_EDGE(flags)) { ++ default: ++ case TIMER_FLAG_NONE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x00); ++ break; ++ case TIMER_FLAG_RISE_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x01); ++ break; ++ case TIMER_FLAG_FALL_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x02); ++ break; ++ case TIMER_FLAG_ANY_EDGE: ++ con_reg = GPTU_CON_EDGE_SET(0x03); ++ break; ++ } ++ if (TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER) ++ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EXT_SET(1) : GPTU_CON_SRC_EXT_SET(0); ++ else ++ con_reg |= TIMER_FLAG_MASK_SRC(flags) == TIMER_FLAG_EXT_SRC ? GPTU_CON_SRC_EG_SET(1) : GPTU_CON_SRC_EG_SET(0); ++ con_reg |= TIMER_FLAG_MASK_SYNC(flags) == TIMER_FLAG_UNSYNC ? GPTU_CON_SYNC_SET(0) : GPTU_CON_SYNC_SET(1); ++ con_reg |= TIMER_FLAG_MASK_INVERT(flags) == TIMER_FLAG_REAL ? GPTU_CON_INV_SET(0) : GPTU_CON_INV_SET(1); ++ con_reg |= TIMER_FLAG_MASK_SIZE(flags) == TIMER_FLAG_16BIT ? GPTU_CON_EXT_SET(0) : GPTU_CON_EXT_SET(1); ++ con_reg |= TIMER_FLAG_MASK_STOP(flags) == TIMER_FLAG_ONCE ? GPTU_CON_STP_SET(1) : GPTU_CON_STP_SET(0); ++ con_reg |= TIMER_FLAG_MASK_TYPE(flags) == TIMER_FLAG_TIMER ? GPTU_CON_CNT_SET(0) : GPTU_CON_CNT_SET(1); ++ con_reg |= TIMER_FLAG_MASK_DIR(flags) == TIMER_FLAG_UP ? GPTU_CON_DIR_SET(1) : GPTU_CON_DIR_SET(0); ++ ++ timer_dev.timer[timer - FIRST_TIMER].flag = flags; ++ if (TIMER_FLAG_MASK_SIZE(flags) != TIMER_FLAG_16BIT) ++ timer_dev.timer[timer - FIRST_TIMER + 1].flag = flags; ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *LQ_GPTU_CON(n, X) = con_reg; ++ smp_wmb(); ++ printk(KERN_INFO "[%s]: counter%d oflags %#x, nflags %#x, GPTU_CON %#x\n", __func__, timer, oflag, flags, *LQ_GPTU_CON(n, X)); ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return 0; ++} ++EXPORT_SYMBOL(lq_reset_counter_flags); ++ ++int lq_get_count_value(unsigned int timer, unsigned long *value) ++{ ++ unsigned int flag; ++ unsigned int mask; ++ int n, X; ++ ++ if (!timer_dev.f_gptu_on) ++ return -EINVAL; ++ ++ if (timer < FIRST_TIMER ++ || timer >= FIRST_TIMER + timer_dev.number_of_timers) ++ return -EINVAL; ++ ++ mutex_lock(&timer_dev.gptu_mutex); ++ ++ flag = timer_dev.timer[timer - FIRST_TIMER].flag; ++ if (TIMER_FLAG_MASK_SIZE(flag) != TIMER_FLAG_16BIT) ++ timer &= ~0x01; ++ ++ mask = (TIMER_FLAG_MASK_SIZE(flag) == TIMER_FLAG_16BIT ? 1 : 3) << timer; ++ if (((timer_dev.occupation & mask) ^ mask)) { ++ mutex_unlock(&timer_dev.gptu_mutex); ++ return -EINVAL; ++ } ++ ++ n = timer >> 1; ++ X = timer & 0x01; ++ ++ *value = *LQ_GPTU_COUNT(n, X); ++ ++ mutex_unlock(&timer_dev.gptu_mutex); ++ ++ return 0; ++} ++EXPORT_SYMBOL(lq_get_count_value); ++ ++u32 lq_cal_divider(unsigned long freq) ++{ ++ u64 module_freq, fpi = ltq_get_fpi_bus_clock(2); ++ u32 clock_divider = 1; ++ module_freq = fpi * 1000; ++ do_div(module_freq, clock_divider * freq); ++ return module_freq; ++} ++EXPORT_SYMBOL(lq_cal_divider); ++ ++int lq_set_timer(unsigned int timer, unsigned int freq, int is_cyclic, ++ int is_ext_src, unsigned int handle_flag, unsigned long arg1, ++ unsigned long arg2) ++{ ++ unsigned long divider; ++ unsigned int flag; ++ ++ divider = lq_cal_divider(freq); ++ if (divider == 0) ++ return -EINVAL; ++ flag = ((divider & ~0xFFFF) ? TIMER_FLAG_32BIT : TIMER_FLAG_16BIT) ++ | (is_cyclic ? TIMER_FLAG_CYCLIC : TIMER_FLAG_ONCE) ++ | (is_ext_src ? TIMER_FLAG_EXT_SRC : TIMER_FLAG_INT_SRC) ++ | TIMER_FLAG_TIMER | TIMER_FLAG_DOWN ++ | TIMER_FLAG_MASK_HANDLE(handle_flag); ++ ++ printk(KERN_INFO "lq_set_timer(%d, %d), divider = %lu\n", ++ timer, freq, divider); ++ return lq_request_timer(timer, flag, divider, arg1, arg2); ++} ++EXPORT_SYMBOL(lq_set_timer); ++ ++int lq_set_counter(unsigned int timer, unsigned int flag, u32 reload, ++ unsigned long arg1, unsigned long arg2) ++{ ++ printk(KERN_INFO "lq_set_counter(%d, %#x, %d)\n", timer, flag, reload); ++ return lq_request_timer(timer, flag, reload, arg1, arg2); ++} ++EXPORT_SYMBOL(lq_set_counter); ++ ++static long gptu_ioctl(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ int ret; ++ struct gptu_ioctl_param param; ++ ++ if (!access_ok(VERIFY_READ, arg, sizeof(struct gptu_ioctl_param))) ++ return -EFAULT; ++ copy_from_user(¶m, (void *) arg, sizeof(param)); ++ ++ if ((((cmd == GPTU_REQUEST_TIMER || cmd == GPTU_SET_TIMER ++ || GPTU_SET_COUNTER) && param.timer < 2) ++ || cmd == GPTU_GET_COUNT_VALUE || cmd == GPTU_CALCULATE_DIVIDER) ++ && !access_ok(VERIFY_WRITE, arg, ++ sizeof(struct gptu_ioctl_param))) ++ return -EFAULT; ++ ++ switch (cmd) { ++ case GPTU_REQUEST_TIMER: ++ ret = lq_request_timer(param.timer, param.flag, param.value, ++ (unsigned long) param.pid, ++ (unsigned long) param.sig); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ case GPTU_FREE_TIMER: ++ ret = lq_free_timer(param.timer); ++ break; ++ case GPTU_START_TIMER: ++ ret = lq_start_timer(param.timer, param.flag); ++ break; ++ case GPTU_STOP_TIMER: ++ ret = lq_stop_timer(param.timer); ++ break; ++ case GPTU_GET_COUNT_VALUE: ++ ret = lq_get_count_value(param.timer, ¶m.value); ++ if (!ret) ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ value, ¶m.value, ++ sizeof(param.value)); ++ break; ++ case GPTU_CALCULATE_DIVIDER: ++ param.value = lq_cal_divider(param.value); ++ if (param.value == 0) ++ ret = -EINVAL; ++ else { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ value, ¶m.value, ++ sizeof(param.value)); ++ ret = 0; ++ } ++ break; ++ case GPTU_SET_TIMER: ++ ret = lq_set_timer(param.timer, param.value, ++ TIMER_FLAG_MASK_STOP(param.flag) != ++ TIMER_FLAG_ONCE ? 1 : 0, ++ TIMER_FLAG_MASK_SRC(param.flag) == ++ TIMER_FLAG_EXT_SRC ? 1 : 0, ++ TIMER_FLAG_MASK_HANDLE(param.flag) == ++ TIMER_FLAG_SIGNAL ? TIMER_FLAG_SIGNAL : ++ TIMER_FLAG_NO_HANDLE, ++ (unsigned long) param.pid, ++ (unsigned long) param.sig); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ case GPTU_SET_COUNTER: ++ lq_set_counter(param.timer, param.flag, param.value, 0, 0); ++ if (ret > 0) { ++ copy_to_user(&((struct gptu_ioctl_param *) arg)-> ++ timer, &ret, sizeof(&ret)); ++ ret = 0; ++ } ++ break; ++ default: ++ ret = -ENOTTY; ++ } ++ ++ return ret; ++} ++ ++static int gptu_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int gptu_release(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++int __init lq_gptu_init(void) ++{ ++ int ret; ++ unsigned int i; ++ ++ ltq_w32(0, LQ_GPTU_IRNEN); ++ ltq_w32(0xfff, LQ_GPTU_IRNCR); ++ ++ memset(&timer_dev, 0, sizeof(timer_dev)); ++ mutex_init(&timer_dev.gptu_mutex); ++ ++ lq_enable_gptu(); ++ timer_dev.number_of_timers = GPTU_ID_CFG * 2; ++ lq_disable_gptu(); ++ if (timer_dev.number_of_timers > MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2) ++ timer_dev.number_of_timers = MAX_NUM_OF_32BIT_TIMER_BLOCKS * 2; ++ printk(KERN_INFO "gptu: totally %d 16-bit timers/counters\n", timer_dev.number_of_timers); ++ ++ ret = misc_register(&gptu_miscdev); ++ if (ret) { ++ printk(KERN_ERR "gptu: can't misc_register, get error %d\n", -ret); ++ return ret; ++ } else { ++ printk(KERN_INFO "gptu: misc_register on minor %d\n", gptu_miscdev.minor); ++ } ++ ++ for (i = 0; i < timer_dev.number_of_timers; i++) { ++ ret = request_irq(TIMER_INTERRUPT + i, timer_irq_handler, IRQF_TIMER, gptu_miscdev.name, &timer_dev.timer[i]); ++ if (ret) { ++ for (; i >= 0; i--) ++ free_irq(TIMER_INTERRUPT + i, &timer_dev.timer[i]); ++ misc_deregister(&gptu_miscdev); ++ printk(KERN_ERR "gptu: failed in requesting irq (%d), get error %d\n", i, -ret); ++ return ret; ++ } else { ++ timer_dev.timer[i].irq = TIMER_INTERRUPT + i; ++ disable_irq(timer_dev.timer[i].irq); ++ printk(KERN_INFO "gptu: succeeded to request irq %d\n", timer_dev.timer[i].irq); ++ } ++ } ++ ++ return 0; ++} ++ ++void __exit lq_gptu_exit(void) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < timer_dev.number_of_timers; i++) { ++ if (timer_dev.timer[i].f_irq_on) ++ disable_irq(timer_dev.timer[i].irq); ++ free_irq(timer_dev.timer[i].irq, &timer_dev.timer[i]); ++ } ++ lq_disable_gptu(); ++ misc_deregister(&gptu_miscdev); ++} ++ ++module_init(lq_gptu_init); ++module_exit(lq_gptu_exit); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0044-MIPS-lantiq-adds-dwc_otg.patch b/target/linux/lantiq/patches-3.3/0044-MIPS-lantiq-adds-dwc_otg.patch new file mode 100644 index 0000000000..ef1c4ff128 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0044-MIPS-lantiq-adds-dwc_otg.patch @@ -0,0 +1,15639 @@ +From 5bd209cb056fcf421710a1cd1521596a242bc569 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 30 Sep 2011 14:37:36 +0200 +Subject: [PATCH 44/70] MIPS: lantiq: adds dwc_otg + +--- + drivers/usb/Kconfig | 2 + + drivers/usb/Makefile | 2 + + drivers/usb/core/hub.c | 4 +- + drivers/usb/dwc_otg/Kconfig | 37 + + drivers/usb/dwc_otg/Makefile | 39 + + drivers/usb/dwc_otg/dwc_otg_attr.c | 802 ++++++++ + drivers/usb/dwc_otg/dwc_otg_attr.h | 67 + + drivers/usb/dwc_otg/dwc_otg_cil.c | 3025 +++++++++++++++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_cil.h | 911 ++++++++++ + drivers/usb/dwc_otg/dwc_otg_cil_ifx.h | 58 + + drivers/usb/dwc_otg/dwc_otg_cil_intr.c | 708 ++++++++ + drivers/usb/dwc_otg/dwc_otg_driver.c | 1274 +++++++++++++ + drivers/usb/dwc_otg/dwc_otg_driver.h | 84 + + drivers/usb/dwc_otg/dwc_otg_hcd.c | 2870 +++++++++++++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_hcd.h | 676 +++++++ + drivers/usb/dwc_otg/dwc_otg_hcd_intr.c | 1841 +++++++++++++++++++ + drivers/usb/dwc_otg/dwc_otg_hcd_queue.c | 794 ++++++++ + drivers/usb/dwc_otg/dwc_otg_ifx.c | 100 + + drivers/usb/dwc_otg/dwc_otg_ifx.h | 85 + + drivers/usb/dwc_otg/dwc_otg_plat.h | 269 +++ + drivers/usb/dwc_otg/dwc_otg_regs.h | 1797 ++++++++++++++++++ + 21 files changed, 15443 insertions(+), 2 deletions(-) + create mode 100644 drivers/usb/dwc_otg/Kconfig + create mode 100644 drivers/usb/dwc_otg/Makefile + create mode 100644 drivers/usb/dwc_otg/dwc_otg_attr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_attr.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil_ifx.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_cil_intr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_driver.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_driver.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd_intr.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_hcd_queue.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_ifx.c + create mode 100644 drivers/usb/dwc_otg/dwc_otg_ifx.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_plat.h + create mode 100644 drivers/usb/dwc_otg/dwc_otg_regs.h + +diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig +index 75823a1..8469e23 100644 +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -130,6 +130,8 @@ source "drivers/usb/wusbcore/Kconfig" + + source "drivers/usb/host/Kconfig" + ++source "drivers/usb/dwc_otg/Kconfig" ++ + source "drivers/usb/musb/Kconfig" + + source "drivers/usb/renesas_usbhs/Kconfig" +diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile +index 53a7bc0..d46b792 100644 +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -32,6 +32,8 @@ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/ + + obj-$(CONFIG_USB_WUSB) += wusbcore/ + ++obj-$(CONFIG_DWC_OTG) += dwc_otg/ ++ + obj-$(CONFIG_USB_ACM) += class/ + obj-$(CONFIG_USB_PRINTER) += class/ + obj-$(CONFIG_USB_WDM) += class/ +diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c +index 265c2f6..5d39022 100644 +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -2938,11 +2938,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1, + udev->ttport = hdev->ttport; + } else if (udev->speed != USB_SPEED_HIGH + && hdev->speed == USB_SPEED_HIGH) { +- if (!hub->tt.hub) { ++/* if (!hub->tt.hub) { + dev_err(&udev->dev, "parent hub has no TT\n"); + retval = -EINVAL; + goto fail; +- } ++ }*/ + udev->tt = &hub->tt; + udev->ttport = port1; + } +diff --git a/drivers/usb/dwc_otg/Kconfig b/drivers/usb/dwc_otg/Kconfig +new file mode 100644 +index 0000000..e018490 +--- /dev/null ++++ b/drivers/usb/dwc_otg/Kconfig +@@ -0,0 +1,37 @@ ++config DWC_OTG ++ tristate "Synopsis DWC_OTG support" ++ depends on USB ++ help ++ This driver supports Synopsis DWC_OTG IP core ++ embebbed on many SOCs (ralink, infineon, etc) ++ ++choice ++ prompt "USB Operation Mode" ++ depends on DWC_OTG ++ default DWC_OTG_HOST_ONLY ++ ++config DWC_OTG_HOST_ONLY ++ bool "HOST ONLY MODE" ++ depends on DWC_OTG ++ ++#config DWC_OTG_DEVICE_ONLY ++# bool "DEVICE ONLY MODE" ++# depends on DWC_OTG ++endchoice ++ ++choice ++ prompt "Platform" ++ depends on DWC_OTG ++ default DWC_OTG_LANTIQ ++ ++config DWC_OTG_LANTIQ ++ bool "Lantiq" ++ depends on LANTIQ ++ help ++ Danube USB Host Controller ++ platform support ++endchoice ++ ++config DWC_OTG_DEBUG ++ bool "Enable debug mode" ++ depends on DWC_OTG +diff --git a/drivers/usb/dwc_otg/Makefile b/drivers/usb/dwc_otg/Makefile +new file mode 100644 +index 0000000..d4d2355 +--- /dev/null ++++ b/drivers/usb/dwc_otg/Makefile +@@ -0,0 +1,39 @@ ++# ++# Makefile for DWC_otg Highspeed USB controller driver ++# ++ ++ifeq ($(CONFIG_DWC_OTG_DEBUG),y) ++EXTRA_CFLAGS += -DDEBUG ++endif ++ ++# Use one of the following flags to compile the software in host-only or ++# device-only mode based on the configuration selected by the user ++ifeq ($(CONFIG_DWC_OTG_HOST_ONLY),y) ++ EXTRA_CFLAGS += -DDWC_OTG_HOST_ONLY -DDWC_HOST_ONLY ++ EXTRA_CFLAGS += -DDWC_OTG_EN_ISOC -DDWC_EN_ISOC ++else ifeq ($(CONFIG_DWC_OTG_DEVICE_ONLY),y) ++ EXTRA_CFLAGS += -DDWC_OTG_DEVICE_ONLY ++else ++ EXTRA_CFLAGS += -DDWC_OTG_MODE ++endif ++ ++# EXTRA_CFLAGS += -DDWC_HS_ELECT_TST ++# EXTRA_CFLAGS += -DDWC_OTG_EXT_CHG_PUMP ++ ++ifeq ($(CONFIG_DWC_OTG_LANTIQ),y) ++ EXTRA_CFLAGS += -Dlinux -D__LINUX__ -DDWC_OTG_IFX -DDWC_OTG_HOST_ONLY -DDWC_HOST_ONLY -D__KERNEL__ ++endif ++ifeq ($(CONFIG_DWC_OTG_LANTIQ),m) ++ EXTRA_CFLAGS += -Dlinux -D__LINUX__ -DDWC_OTG_IFX -DDWC_HOST_ONLY -DMODULE -D__KERNEL__ -DDEBUG ++endif ++ ++obj-$(CONFIG_DWC_OTG) := dwc_otg.o ++dwc_otg-objs := dwc_otg_hcd.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o ++#dwc_otg-objs += dwc_otg_pcd.o dwc_otg_pcd_intr.o ++dwc_otg-objs += dwc_otg_attr.o ++dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o ++dwc_otg-objs += dwc_otg_ifx.o ++dwc_otg-objs += dwc_otg_driver.o ++ ++#obj-$(CONFIG_DWC_OTG_IFX) := dwc_otg_ifx.o ++#dwc_otg_ifx-objs := dwc_otg_ifx.o +diff --git a/drivers/usb/dwc_otg/dwc_otg_attr.c b/drivers/usb/dwc_otg/dwc_otg_attr.c +new file mode 100644 +index 0000000..4675a5c +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_attr.c +@@ -0,0 +1,802 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_attr.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 537387 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. The Linux driver attributes ++ * feature will be used to provide the Linux Diagnostic ++ * Interface. These attributes are accessed through sysfs. ++ */ ++ ++/** @page "Linux Module Attributes" ++ * ++ * The Linux module attributes feature is used to provide the Linux ++ * Diagnostic Interface. These attributes are accessed through sysfs. ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. ++ ++ ++ The following table shows the attributes. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++
Name Description Access
mode Returns the current mode: 0 for device mode, 1 for host mode Read
hnpcapable Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value. Read/Write
srpcapable Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value. Read/Write
hnp Initiates the Host Negotiation Protocol. Read returns the status. Read/Write
srp Initiates the Session Request Protocol. Read returns the status. Read/Write
buspower Gets or sets the Power State of the bus (0 - Off or 1 - On) Read/Write
bussuspend Suspends the USB bus. Read/Write
busconnected Gets the connection status of the bus Read
gotgctl Gets or sets the Core Control Status Register. Read/Write
gusbcfg Gets or sets the Core USB Configuration Register Read/Write
grxfsiz Gets or sets the Receive FIFO Size Register Read/Write
gnptxfsiz Gets or sets the non-periodic Transmit Size Register Read/Write
gpvndctl Gets or sets the PHY Vendor Control Register Read/Write
ggpio Gets the value in the lower 16-bits of the General Purpose IO Register ++ or sets the upper 16 bits. Read/Write
guid Gets or sets the value of the User ID Register Read/Write
gsnpsid Gets the value of the Synopsys ID Regester Read
devspeed Gets or sets the device speed setting in the DCFG register Read/Write
enumspeed Gets the device enumeration Speed. Read
hptxfsiz Gets the value of the Host Periodic Transmit FIFO Read
hprt0 Gets or sets the value in the Host Port Control and Status Register Read/Write
regoffset Sets the register offset for the next Register Access Read/Write
regvalue Gets or sets the value of the register at the offset in the regoffset attribute. Read/Write
remote_wakeup On read, shows the status of Remote Wakeup. On write, initiates a remote ++ wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote ++ Wakeup signalling bit in the Device Control Register is set for 1 ++ milli-second. Read/Write
regdump Dumps the contents of core registers. Read
hcddump Dumps the current HCD state. Read
hcd_frrem Shows the average value of the Frame Remaining ++ field in the Host Frame Number/Frame Remaining register when an SOF interrupt ++ occurs. This can be used to determine the average interrupt latency. Also ++ shows the average Frame Remaining value for start_transfer and the "a" and ++ "b" sample points. The "a" and "b" sample points may be used during debugging ++ bto determine how long it takes to execute a section of the HCD code. Read
rd_reg_test Displays the time required to read the GNPTXFSIZ register many times ++ (the output shows the number of times the register is read). ++ Read
wr_reg_test Displays the time required to write the GNPTXFSIZ register many times ++ (the output shows the number of times the register is written). ++ Read
++ ++ Example usage: ++ To get the current mode: ++ cat /sys/devices/lm0/mode ++ ++ To power down the USB: ++ echo 0 > /sys/devices/lm0/buspower ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include /* permission constants */ ++ ++#include ++ ++#include "dwc_otg_plat.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_driver.h" ++// #include "dwc_otg_pcd.h" ++#include "dwc_otg_hcd.h" ++ ++// 20070316, winder added. ++#ifndef SZ_256K ++#define SZ_256K 0x00040000 ++#endif ++ ++/* ++ * MACROs for defining sysfs attribute ++ */ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t val; \ ++ val = dwc_read_reg32 (_addr_); \ ++ val = (val & (_mask_)) >> _shift_; \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ uint32_t clear = set; \ ++ clear = ((~clear) << _shift_) & _mask_; \ ++ set = (set << _shift_) & _mask_; \ ++ dev_dbg(_dev, "Storing Address=0x%08x Set=0x%08x Clear=0x%08x\n", (uint32_t)_addr_, set, clear); \ ++ dwc_modify_reg32(_addr_, clear, set); \ ++ return count; \ ++} ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_addr_,_mask_,_shift_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++/* ++ * MACROs for defining sysfs attribute for 32-bit registers ++ */ ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t val; \ ++ val = dwc_read_reg32 (_addr_); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev);\ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dev_dbg(_dev, "Storing Address=0x%08x Val=0x%08x\n", (uint32_t)_addr_, val); \ ++ dwc_write_reg32(_addr_, val); \ ++ return count; \ ++} ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_addr_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_addr_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++ ++/** @name Functions for Show/Store of Attributes */ ++/**@{*/ ++ ++/** ++ * Show the register offset of the Register Access. ++ */ ++static ssize_t regoffset_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ return snprintf(buf, sizeof("0xFFFFFFFF\n")+1,"0x%08x\n", otg_dev->reg_offset); ++} ++ ++/** ++ * Set the register offset for the next Register Access Read/Write ++ */ ++static ssize_t regoffset_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ uint32_t offset = simple_strtoul(buf, NULL, 16); ++ //dev_dbg(_dev, "Offset=0x%08x\n", offset); ++ if (offset < SZ_256K ) { ++ otg_dev->reg_offset = offset; ++ } ++ else { ++ dev_err( _dev, "invalid offset\n" ); ++ } ++ ++ return count; ++} ++DEVICE_ATTR(regoffset, S_IRUGO|S_IWUSR, regoffset_show, regoffset_store); ++ ++/** ++ * Show the value of the register at the offset in the reg_offset ++ * attribute. ++ */ ++static ssize_t regvalue_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ uint32_t val; ++ volatile uint32_t *addr; ++ ++ if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base) { ++ /* Calculate the address */ ++ addr = (uint32_t*)(otg_dev->reg_offset + ++ (uint8_t*)otg_dev->base); ++ //dev_dbg(_dev, "@0x%08x\n", (unsigned)addr); ++ val = dwc_read_reg32( addr ); ++ return snprintf(buf, sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n")+1, ++ "Reg@0x%06x = 0x%08x\n", ++ otg_dev->reg_offset, val); ++ } ++ else { ++ dev_err(_dev, "Invalid offset (0x%0x)\n", ++ otg_dev->reg_offset); ++ return sprintf(buf, "invalid offset\n" ); ++ } ++} ++ ++/** ++ * Store the value in the register at the offset in the reg_offset ++ * attribute. ++ * ++ */ ++static ssize_t regvalue_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ volatile uint32_t * addr; ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ //dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val); ++ if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base) { ++ /* Calculate the address */ ++ addr = (uint32_t*)(otg_dev->reg_offset + ++ (uint8_t*)otg_dev->base); ++ //dev_dbg(_dev, "@0x%08x\n", (unsigned)addr); ++ dwc_write_reg32( addr, val ); ++ } ++ else { ++ dev_err(_dev, "Invalid Register Offset (0x%08x)\n", ++ otg_dev->reg_offset); ++ } ++ return count; ++} ++DEVICE_ATTR(regvalue, S_IRUGO|S_IWUSR, regvalue_show, regvalue_store); ++ ++/* ++ * Attributes ++ */ ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<20),20,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable,&(otg_dev->core_if->core_global_regs->gusbcfg),(1<<9),9,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable,&(otg_dev->core_if->core_global_regs->gusbcfg),(1<<8),8,"Mode"); ++ ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected,otg_dev->core_if->host_if->hprt0,0x01,0,"Bus Connected"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl,&(otg_dev->core_if->core_global_regs->gotgctl),"GOTGCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg,&(otg_dev->core_if->core_global_regs->gusbcfg),"GUSBCFG"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz,&(otg_dev->core_if->core_global_regs->grxfsiz),"GRXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz,&(otg_dev->core_if->core_global_regs->gnptxfsiz),"GNPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl,&(otg_dev->core_if->core_global_regs->gpvndctl),"GPVNDCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio,&(otg_dev->core_if->core_global_regs->ggpio),"GGPIO"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(guid,&(otg_dev->core_if->core_global_regs->guid),"GUID"); ++DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid,&(otg_dev->core_if->core_global_regs->gsnpsid),"GSNPSID"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dcfg),0x3,0,"Device Speed"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed,&(otg_dev->core_if->dev_if->dev_global_regs->dsts),0x6,1,"Device Enumeration Speed"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz,&(otg_dev->core_if->core_global_regs->hptxfsiz),"HPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0,otg_dev->core_if->host_if->hprt0,"HPRT0"); ++ ++ ++/** ++ * @todo Add code to initiate the HNP. ++ */ ++/** ++ * Show the HNP status bit ++ */ ++static ssize_t hnp_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ gotgctl_data_t val; ++ val.d32 = dwc_read_reg32 (&(otg_dev->core_if->core_global_regs->gotgctl)); ++ return sprintf (buf, "HstNegScs = 0x%x\n", val.b.hstnegscs); ++} ++ ++/** ++ * Set the HNP Request bit ++ */ ++static ssize_t hnp_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ uint32_t *addr = (uint32_t *)&(otg_dev->core_if->core_global_regs->gotgctl); ++ gotgctl_data_t mem; ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.hnpreq = in; ++ dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); ++ dwc_write_reg32(addr, mem.d32); ++ return count; ++} ++DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store); ++ ++/** ++ * @todo Add code to initiate the SRP. ++ */ ++/** ++ * Show the SRP status bit ++ */ ++static ssize_t srp_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ gotgctl_data_t val; ++ val.d32 = dwc_read_reg32 (&(otg_dev->core_if->core_global_regs->gotgctl)); ++ return sprintf (buf, "SesReqScs = 0x%x\n", val.b.sesreqscs); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif ++} ++ ++/** ++ * Set the SRP Request bit ++ */ ++static ssize_t srp_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ dwc_otg_pcd_initiate_srp(otg_dev->pcd); ++#endif ++ return count; ++} ++DEVICE_ATTR(srp, 0644, srp_show, srp_store); ++ ++/** ++ * @todo Need to do more for power on/off? ++ */ ++/** ++ * Show the Bus Power status ++ */ ++static ssize_t buspower_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ hprt0_data_t val; ++ val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0); ++ return sprintf (buf, "Bus Power = 0x%x\n", val.b.prtpwr); ++} ++ ++ ++/** ++ * Set the Bus Power status ++ */ ++static ssize_t buspower_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ uint32_t on = simple_strtoul(buf, NULL, 16); ++ uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0; ++ hprt0_data_t mem; ++ ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.prtpwr = on; ++ ++ //dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); ++ dwc_write_reg32(addr, mem.d32); ++ ++ return count; ++} ++DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store); ++ ++/** ++ * @todo Need to do more for suspend? ++ */ ++/** ++ * Show the Bus Suspend status ++ */ ++static ssize_t bussuspend_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ hprt0_data_t val; ++ val.d32 = dwc_read_reg32 (otg_dev->core_if->host_if->hprt0); ++ return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp); ++} ++ ++/** ++ * Set the Bus Suspend status ++ */ ++static ssize_t bussuspend_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ uint32_t *addr = (uint32_t *)otg_dev->core_if->host_if->hprt0; ++ hprt0_data_t mem; ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.prtsusp = in; ++ dev_dbg(_dev, "Storing Address=0x%08x Data=0x%08x\n", (uint32_t)addr, mem.d32); ++ dwc_write_reg32(addr, mem.d32); ++ return count; ++} ++DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store); ++ ++/** ++ * Show the status of Remote Wakeup. ++ */ ++static ssize_t remote_wakeup_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ dctl_data_t val; ++ val.d32 = dwc_read_reg32( &otg_dev->core_if->dev_if->dev_global_regs->dctl); ++ return sprintf( buf, "Remote Wakeup = %d Enabled = %d\n", ++ val.b.rmtwkupsig, otg_dev->pcd->remote_wakeup_enable); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif ++} ++ ++/** ++ * Initiate a remote wakeup of the host. The Device control register ++ * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable ++ * flag is set. ++ * ++ */ ++static ssize_t remote_wakeup_store( struct device *_dev, struct device_attribute *attr, const char *buf, ++ size_t count ) ++{ ++#ifndef DWC_HOST_ONLY ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ if (val&1) { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1); ++ } ++ else { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0); ++ } ++#endif ++ return count; ++} ++DEVICE_ATTR(remote_wakeup, S_IRUGO|S_IWUSR, remote_wakeup_show, ++ remote_wakeup_store); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t regdump_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++#ifdef DEBUG ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ printk("%s otg_dev=0x%p\n", __FUNCTION__, otg_dev); ++ ++ dwc_otg_dump_global_registers( otg_dev->core_if); ++ if (dwc_otg_is_host_mode(otg_dev->core_if)) { ++ dwc_otg_dump_host_registers( otg_dev->core_if); ++ } else { ++ dwc_otg_dump_dev_registers( otg_dev->core_if); ++ } ++#endif ++ ++ return sprintf( buf, "Register Dump\n" ); ++} ++ ++DEVICE_ATTR(regdump, S_IRUGO|S_IWUSR, regdump_show, 0); ++ ++/** ++ * Dump the current hcd state. ++ */ ++static ssize_t hcddump_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ dwc_otg_hcd_dump_state(otg_dev->hcd); ++#endif ++ return sprintf( buf, "HCD Dump\n" ); ++} ++ ++DEVICE_ATTR(hcddump, S_IRUGO|S_IWUSR, hcddump_show, 0); ++ ++/** ++ * Dump the average frame remaining at SOF. This can be used to ++ * determine average interrupt latency. Frame remaining is also shown for ++ * start transfer and two additional sample points. ++ */ ++static ssize_t hcd_frrem_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ dwc_otg_hcd_dump_frrem(otg_dev->hcd); ++#endif ++ return sprintf( buf, "HCD Dump Frame Remaining\n" ); ++} ++ ++DEVICE_ATTR(hcd_frrem, S_IRUGO|S_IWUSR, hcd_frrem_show, 0); ++ ++/** ++ * Displays the time required to read the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is read). ++ */ ++#define RW_REG_COUNT 10000000 ++#define MSEC_PER_JIFFIE 1000/HZ ++static ssize_t rd_reg_test_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ int i; ++ int time; ++ int start_jiffies; ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf( buf, "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time ); ++} ++ ++DEVICE_ATTR(rd_reg_test, S_IRUGO|S_IWUSR, rd_reg_test_show, 0); ++ ++/** ++ * Displays the time required to write the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is written). ++ */ ++static ssize_t wr_reg_test_show( struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ int i; ++ int time; ++ int start_jiffies; ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ uint32_t reg_val; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ reg_val = dwc_read_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_write_reg32(&otg_dev->core_if->core_global_regs->gnptxfsiz, reg_val); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf( buf, "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); ++} ++ ++DEVICE_ATTR(wr_reg_test, S_IRUGO|S_IWUSR, wr_reg_test_show, 0); ++/**@}*/ ++ ++/** ++ * Create the device files ++ */ ++void dwc_otg_attr_create (struct device *_dev) ++{ ++ int retval; ++ ++ retval = device_create_file(_dev, &dev_attr_regoffset); ++ retval += device_create_file(_dev, &dev_attr_regvalue); ++ retval += device_create_file(_dev, &dev_attr_mode); ++ retval += device_create_file(_dev, &dev_attr_hnpcapable); ++ retval += device_create_file(_dev, &dev_attr_srpcapable); ++ retval += device_create_file(_dev, &dev_attr_hnp); ++ retval += device_create_file(_dev, &dev_attr_srp); ++ retval += device_create_file(_dev, &dev_attr_buspower); ++ retval += device_create_file(_dev, &dev_attr_bussuspend); ++ retval += device_create_file(_dev, &dev_attr_busconnected); ++ retval += device_create_file(_dev, &dev_attr_gotgctl); ++ retval += device_create_file(_dev, &dev_attr_gusbcfg); ++ retval += device_create_file(_dev, &dev_attr_grxfsiz); ++ retval += device_create_file(_dev, &dev_attr_gnptxfsiz); ++ retval += device_create_file(_dev, &dev_attr_gpvndctl); ++ retval += device_create_file(_dev, &dev_attr_ggpio); ++ retval += device_create_file(_dev, &dev_attr_guid); ++ retval += device_create_file(_dev, &dev_attr_gsnpsid); ++ retval += device_create_file(_dev, &dev_attr_devspeed); ++ retval += device_create_file(_dev, &dev_attr_enumspeed); ++ retval += device_create_file(_dev, &dev_attr_hptxfsiz); ++ retval += device_create_file(_dev, &dev_attr_hprt0); ++ retval += device_create_file(_dev, &dev_attr_remote_wakeup); ++ retval += device_create_file(_dev, &dev_attr_regdump); ++ retval += device_create_file(_dev, &dev_attr_hcddump); ++ retval += device_create_file(_dev, &dev_attr_hcd_frrem); ++ retval += device_create_file(_dev, &dev_attr_rd_reg_test); ++ retval += device_create_file(_dev, &dev_attr_wr_reg_test); ++ ++ if(retval != 0) ++ { ++ DWC_PRINT("cannot create sysfs device files.\n"); ++ // DWC_PRINT("killing own sysfs device files!\n"); ++ dwc_otg_attr_remove(_dev); ++ } ++} ++ ++/** ++ * Remove the device files ++ */ ++void dwc_otg_attr_remove (struct device *_dev) ++{ ++ device_remove_file(_dev, &dev_attr_regoffset); ++ device_remove_file(_dev, &dev_attr_regvalue); ++ device_remove_file(_dev, &dev_attr_mode); ++ device_remove_file(_dev, &dev_attr_hnpcapable); ++ device_remove_file(_dev, &dev_attr_srpcapable); ++ device_remove_file(_dev, &dev_attr_hnp); ++ device_remove_file(_dev, &dev_attr_srp); ++ device_remove_file(_dev, &dev_attr_buspower); ++ device_remove_file(_dev, &dev_attr_bussuspend); ++ device_remove_file(_dev, &dev_attr_busconnected); ++ device_remove_file(_dev, &dev_attr_gotgctl); ++ device_remove_file(_dev, &dev_attr_gusbcfg); ++ device_remove_file(_dev, &dev_attr_grxfsiz); ++ device_remove_file(_dev, &dev_attr_gnptxfsiz); ++ device_remove_file(_dev, &dev_attr_gpvndctl); ++ device_remove_file(_dev, &dev_attr_ggpio); ++ device_remove_file(_dev, &dev_attr_guid); ++ device_remove_file(_dev, &dev_attr_gsnpsid); ++ device_remove_file(_dev, &dev_attr_devspeed); ++ device_remove_file(_dev, &dev_attr_enumspeed); ++ device_remove_file(_dev, &dev_attr_hptxfsiz); ++ device_remove_file(_dev, &dev_attr_hprt0); ++ device_remove_file(_dev, &dev_attr_remote_wakeup); ++ device_remove_file(_dev, &dev_attr_regdump); ++ device_remove_file(_dev, &dev_attr_hcddump); ++ device_remove_file(_dev, &dev_attr_hcd_frrem); ++ device_remove_file(_dev, &dev_attr_rd_reg_test); ++ device_remove_file(_dev, &dev_attr_wr_reg_test); ++} +diff --git a/drivers/usb/dwc_otg/dwc_otg_attr.h b/drivers/usb/dwc_otg/dwc_otg_attr.h +new file mode 100644 +index 0000000..4bbf7df0 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_attr.h +@@ -0,0 +1,67 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_attr.h $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 510275 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_ATTR_H__) ++#define __DWC_OTG_ATTR_H__ ++ ++/** @file ++ * This file contains the interface to the Linux device attributes. ++ */ ++extern struct device_attribute dev_attr_regoffset; ++extern struct device_attribute dev_attr_regvalue; ++ ++extern struct device_attribute dev_attr_mode; ++extern struct device_attribute dev_attr_hnpcapable; ++extern struct device_attribute dev_attr_srpcapable; ++extern struct device_attribute dev_attr_hnp; ++extern struct device_attribute dev_attr_srp; ++extern struct device_attribute dev_attr_buspower; ++extern struct device_attribute dev_attr_bussuspend; ++extern struct device_attribute dev_attr_busconnected; ++extern struct device_attribute dev_attr_gotgctl; ++extern struct device_attribute dev_attr_gusbcfg; ++extern struct device_attribute dev_attr_grxfsiz; ++extern struct device_attribute dev_attr_gnptxfsiz; ++extern struct device_attribute dev_attr_gpvndctl; ++extern struct device_attribute dev_attr_ggpio; ++extern struct device_attribute dev_attr_guid; ++extern struct device_attribute dev_attr_gsnpsid; ++extern struct device_attribute dev_attr_devspeed; ++extern struct device_attribute dev_attr_enumspeed; ++extern struct device_attribute dev_attr_hptxfsiz; ++extern struct device_attribute dev_attr_hprt0; ++ ++void dwc_otg_attr_create (struct device *_dev); ++void dwc_otg_attr_remove (struct device *_dev); ++ ++#endif +diff --git a/drivers/usb/dwc_otg/dwc_otg_cil.c b/drivers/usb/dwc_otg/dwc_otg_cil.c +new file mode 100644 +index 0000000..42c69eb +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil.c +@@ -0,0 +1,3025 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_cil.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 631780 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * The CIL manages the memory map for the core so that the HCD and PCD ++ * don't have to do this separately. It also handles basic tasks like ++ * reading/writing the registers and data FIFOs in the controller. ++ * Some of the data access functions provide encapsulation of several ++ * operations required to perform a task, such as writing multiple ++ * registers to start a transfer. Finally, the CIL performs basic ++ * services that are not specific to either the host or device modes ++ * of operation. These services include management of the OTG Host ++ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A ++ * Diagnostic API is also provided to allow testing of the controller ++ * hardware. ++ * ++ * The Core Interface Layer has the following requirements: ++ * - Provides basic controller operations. ++ * - Minimal use of OS services. ++ * - The OS services used will be abstracted by using inline functions ++ * or macros. ++ * ++ */ ++#include ++ ++#ifdef DEBUG ++#include ++#endif ++ ++#include "dwc_otg_plat.h" ++ ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++/** ++ * This function is called to initialize the DWC_otg CSR data ++ * structures. The register addresses in the device and host ++ * structures are initialized from the base address supplied by the ++ * caller. The calling function must make the OS calls to get the ++ * base address of the DWC_otg controller registers. The core_params ++ * argument holds the parameters that specify how the core should be ++ * configured. ++ * ++ * @param[in] _reg_base_addr Base address of DWC_otg core registers ++ * @param[in] _core_params Pointer to the core configuration parameters ++ * ++ */ ++dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t *_reg_base_addr, ++ dwc_otg_core_params_t *_core_params) ++{ ++ dwc_otg_core_if_t *core_if = 0; ++ dwc_otg_dev_if_t *dev_if = 0; ++ dwc_otg_host_if_t *host_if = 0; ++ uint8_t *reg_base = (uint8_t *)_reg_base_addr; ++ int i = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p,%p)\n", __func__, _reg_base_addr, _core_params); ++ ++ core_if = kmalloc( sizeof(dwc_otg_core_if_t), GFP_KERNEL); ++ if (core_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_core_if_t failed\n"); ++ return 0; ++ } ++ memset(core_if, 0, sizeof(dwc_otg_core_if_t)); ++ ++ core_if->core_params = _core_params; ++ core_if->core_global_regs = (dwc_otg_core_global_regs_t *)reg_base; ++ /* ++ * Allocate the Device Mode structures. ++ */ ++ dev_if = kmalloc( sizeof(dwc_otg_dev_if_t), GFP_KERNEL); ++ if (dev_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n"); ++ kfree( core_if ); ++ return 0; ++ } ++ ++ dev_if->dev_global_regs = ++ (dwc_otg_device_global_regs_t *)(reg_base + DWC_DEV_GLOBAL_REG_OFFSET); ++ ++ for (i=0; iin_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *) ++ (reg_base + DWC_DEV_IN_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ ++ dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *) ++ (reg_base + DWC_DEV_OUT_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n", ++ i, &dev_if->in_ep_regs[i]->diepctl); ++ DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n", ++ i, &dev_if->out_ep_regs[i]->doepctl); ++ } ++ dev_if->speed = 0; // unknown ++ //dev_if->num_eps = MAX_EPS_CHANNELS; ++ //dev_if->num_perio_eps = 0; ++ ++ core_if->dev_if = dev_if; ++ /* ++ * Allocate the Host Mode structures. ++ */ ++ host_if = kmalloc( sizeof(dwc_otg_host_if_t), GFP_KERNEL); ++ if (host_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_host_if_t failed\n"); ++ kfree( dev_if ); ++ kfree( core_if ); ++ return 0; ++ } ++ ++ host_if->host_global_regs = (dwc_otg_host_global_regs_t *) ++ (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET); ++ host_if->hprt0 = (uint32_t*)(reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET); ++ for (i=0; ihc_regs[i] = (dwc_otg_hc_regs_t *) ++ (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET + ++ (i * DWC_OTG_CHAN_REGS_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n", ++ i, &host_if->hc_regs[i]->hcchar); ++ } ++ host_if->num_host_channels = MAX_EPS_CHANNELS; ++ core_if->host_if = host_if; ++ ++ for (i=0; idata_fifo[i] = ++ (uint32_t *)(reg_base + DWC_OTG_DATA_FIFO_OFFSET + ++ (i * DWC_OTG_DATA_FIFO_SIZE)); ++ DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08x\n", ++ i, (unsigned)core_if->data_fifo[i]); ++ } // for loop. ++ ++ core_if->pcgcctl = (uint32_t*)(reg_base + DWC_OTG_PCGCCTL_OFFSET); ++ ++ /* ++ * Store the contents of the hardware configuration registers here for ++ * easy access later. ++ */ ++ core_if->hwcfg1.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg1); ++ core_if->hwcfg2.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg2); ++ core_if->hwcfg3.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg3); ++ core_if->hwcfg4.d32 = dwc_read_reg32(&core_if->core_global_regs->ghwcfg4); ++ ++ DWC_DEBUGPL(DBG_CILV,"hwcfg1=%08x\n",core_if->hwcfg1.d32); ++ DWC_DEBUGPL(DBG_CILV,"hwcfg2=%08x\n",core_if->hwcfg2.d32); ++ DWC_DEBUGPL(DBG_CILV,"hwcfg3=%08x\n",core_if->hwcfg3.d32); ++ DWC_DEBUGPL(DBG_CILV,"hwcfg4=%08x\n",core_if->hwcfg4.d32); ++ ++ ++ DWC_DEBUGPL(DBG_CILV,"op_mode=%0x\n",core_if->hwcfg2.b.op_mode); ++ DWC_DEBUGPL(DBG_CILV,"arch=%0x\n",core_if->hwcfg2.b.architecture); ++ DWC_DEBUGPL(DBG_CILV,"num_dev_ep=%d\n",core_if->hwcfg2.b.num_dev_ep); ++ DWC_DEBUGPL(DBG_CILV,"num_host_chan=%d\n",core_if->hwcfg2.b.num_host_chan); ++ DWC_DEBUGPL(DBG_CILV,"nonperio_tx_q_depth=0x%0x\n",core_if->hwcfg2.b.nonperio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV,"host_perio_tx_q_depth=0x%0x\n",core_if->hwcfg2.b.host_perio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV,"dev_token_q_depth=0x%0x\n",core_if->hwcfg2.b.dev_token_q_depth); ++ ++ DWC_DEBUGPL(DBG_CILV,"Total FIFO SZ=%d\n", core_if->hwcfg3.b.dfifo_depth); ++ DWC_DEBUGPL(DBG_CILV,"xfer_size_cntr_width=%0x\n", core_if->hwcfg3.b.xfer_size_cntr_width); ++ ++ /* ++ * Set the SRP sucess bit for FS-I2c ++ */ ++ core_if->srp_success = 0; ++ core_if->srp_timer_started = 0; ++ ++ return core_if; ++} ++/** ++ * This function frees the structures allocated by dwc_otg_cil_init(). ++ * ++ * @param[in] _core_if The core interface pointer returned from ++ * dwc_otg_cil_init(). ++ * ++ */ ++void dwc_otg_cil_remove( dwc_otg_core_if_t *_core_if ) ++{ ++ /* Disable all interrupts */ ++ dwc_modify_reg32( &_core_if->core_global_regs->gahbcfg, 1, 0); ++ dwc_write_reg32( &_core_if->core_global_regs->gintmsk, 0); ++ ++ if ( _core_if->dev_if ) { ++ kfree( _core_if->dev_if ); ++ } ++ if ( _core_if->host_if ) { ++ kfree( _core_if->host_if ); ++ } ++ kfree( _core_if ); ++} ++ ++/** ++ * This function enables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param[in] _core_if Programming view of DWC_otg controller. ++ */ ++extern void dwc_otg_enable_global_interrupts( dwc_otg_core_if_t *_core_if ) ++{ ++ gahbcfg_data_t ahbcfg = { .d32 = 0}; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ dwc_modify_reg32(&_core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32); ++} ++/** ++ * This function disables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param[in] _core_if Programming view of DWC_otg controller. ++ */ ++extern void dwc_otg_disable_global_interrupts( dwc_otg_core_if_t *_core_if ) ++{ ++ gahbcfg_data_t ahbcfg = { .d32 = 0}; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ dwc_modify_reg32(&_core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); ++} ++ ++/** ++ * This function initializes the commmon interrupts, used in both ++ * device and host modes. ++ * ++ * @param[in] _core_if Programming view of the DWC_otg controller ++ * ++ */ ++static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ _core_if->core_global_regs; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ /* Clear any pending OTG Interrupts */ ++ dwc_write_reg32( &global_regs->gotgint, 0xFFFFFFFF); ++ /* Clear any pending interrupts */ ++ dwc_write_reg32( &global_regs->gintsts, 0xFFFFFFFF); ++ /* ++ * Enable the interrupts in the GINTMSK. ++ */ ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.otgintr = 1; ++ if (!_core_if->dma_enable) { ++ intr_mask.b.rxstsqlvl = 1; ++ } ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.usbsuspend = 1; ++ intr_mask.b.sessreqintr = 1; ++ dwc_write_reg32( &global_regs->gintmsk, intr_mask.d32); ++} ++ ++/** ++ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY ++ * type. ++ */ ++static void init_fslspclksel(dwc_otg_core_if_t *_core_if) ++{ ++ uint32_t val; ++ hcfg_data_t hcfg; ++ ++ if (((_core_if->hwcfg2.b.hs_phy_type == 2) && ++ (_core_if->hwcfg2.b.fs_phy_type == 1) && ++ (_core_if->core_params->ulpi_fs_ls)) || ++ (_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) ++ { ++ /* Full speed PHY */ ++ val = DWC_HCFG_48_MHZ; ++ } else { ++ /* High speed PHY running at full speed or high speed */ ++ val = DWC_HCFG_30_60_MHZ; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val); ++ hcfg.d32 = dwc_read_reg32(&_core_if->host_if->host_global_regs->hcfg); ++ hcfg.b.fslspclksel = val; ++ dwc_write_reg32(&_core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++/** ++ * Initializes the DevSpd field of the DCFG register depending on the PHY type ++ * and the enumeration speed of the device. ++ */ ++static void init_devspd(dwc_otg_core_if_t *_core_if) ++{ ++ uint32_t val; ++ dcfg_data_t dcfg; ++ ++ if (((_core_if->hwcfg2.b.hs_phy_type == 2) && ++ (_core_if->hwcfg2.b.fs_phy_type == 1) && ++ (_core_if->core_params->ulpi_fs_ls)) || ++ (_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) ++ { ++ /* Full speed PHY */ ++ val = 0x3; ++ } else if (_core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ /* High speed PHY running at full speed */ ++ val = 0x1; ++ } else { ++ /* High speed PHY running at high speed */ ++ val = 0x0; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val); ++ dcfg.d32 = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ dwc_write_reg32(&_core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++/** ++ * This function calculates the number of IN EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param _pcd the pcd structure. ++ */ ++static uint32_t calc_num_in_eps(dwc_otg_core_if_t * _core_if) ++{ ++ uint32_t num_in_eps = 0; ++ uint32_t num_eps = _core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = _core_if->hwcfg1.d32 >> 2; ++ uint32_t num_tx_fifos = _core_if->hwcfg4.b.num_in_eps; ++ int i; ++ for (i = 0; i < num_eps; ++i) { ++ if (!(hwcfg1 & 0x1)) ++ num_in_eps++; ++ hwcfg1 >>= 2; ++ } ++ if (_core_if->hwcfg4.b.ded_fifo_en) { ++ num_in_eps = (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps; ++ } ++ return num_in_eps; ++} ++ ++ ++/** ++ * This function calculates the number of OUT EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param _pcd the pcd structure. ++ */ ++static uint32_t calc_num_out_eps(dwc_otg_core_if_t * _core_if) ++{ ++ uint32_t num_out_eps = 0; ++ uint32_t num_eps = _core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = _core_if->hwcfg1.d32 >> 2; ++ int i; ++ for (i = 0; i < num_eps; ++i) { ++ if (!(hwcfg1 & 0x2)) ++ num_out_eps++; ++ hwcfg1 >>= 2; ++ } ++ return num_out_eps; ++} ++/** ++ * This function initializes the DWC_otg controller registers and ++ * prepares the core for device mode or host mode operation. ++ * ++ * @param _core_if Programming view of the DWC_otg controller ++ * ++ */ ++void dwc_otg_core_init(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t * global_regs = _core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ int i = 0; ++ gahbcfg_data_t ahbcfg = { .d32 = 0}; ++ gusbcfg_data_t usbcfg = { .d32 = 0 }; ++ gi2cctl_data_t i2cctl = {.d32 = 0}; ++ ++ DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p)\n",_core_if); ++ ++ /* Common Initialization */ ++ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ DWC_DEBUGPL(DBG_CIL, "USB config register: 0x%08x\n", usbcfg.d32); ++ ++ /* Program the ULPI External VBUS bit if needed */ ++ //usbcfg.b.ulpi_ext_vbus_drv = 1; ++ //usbcfg.b.ulpi_ext_vbus_drv = 0; ++ usbcfg.b.ulpi_ext_vbus_drv = ++ (_core_if->core_params->phy_ulpi_ext_vbus == DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0; ++ ++ /* Set external TS Dline pulsing */ ++ usbcfg.b.term_sel_dl_pulse = (_core_if->core_params->ts_dline == 1) ? 1 : 0; ++ dwc_write_reg32 (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset the Controller */ ++ dwc_otg_core_reset( _core_if ); ++ ++ /* Initialize parameters from Hardware configuration registers. */ ++#if 0 ++ dev_if->num_eps = _core_if->hwcfg2.b.num_dev_ep; ++ dev_if->num_perio_eps = _core_if->hwcfg4.b.num_dev_perio_in_ep; ++#else ++ dev_if->num_in_eps = calc_num_in_eps(_core_if); ++ dev_if->num_out_eps = calc_num_out_eps(_core_if); ++#endif ++ DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n", ++ _core_if->hwcfg4.b.num_dev_perio_in_ep); ++ DWC_DEBUGPL(DBG_CIL, "Is power optimization enabled? %s\n", ++ _core_if->hwcfg4.b.power_optimiz ? "Yes" : "No"); ++ DWC_DEBUGPL(DBG_CIL, "vbus_valid filter enabled? %s\n", ++ _core_if->hwcfg4.b.vbus_valid_filt_en ? "Yes" : "No"); ++ DWC_DEBUGPL(DBG_CIL, "iddig filter enabled? %s\n", ++ _core_if->hwcfg4.b.iddig_filt_en ? "Yes" : "No"); ++ ++ DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n",_core_if->hwcfg4.b.num_dev_perio_in_ep); ++ for (i=0; i < _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) { ++ dev_if->perio_tx_fifo_size[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n", i, ++ dev_if->perio_tx_fifo_size[i]); ++ } ++ for (i = 0; i < _core_if->hwcfg4.b.num_in_eps; i++) { ++ dev_if->tx_fifo_size[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n", i, ++ dev_if->perio_tx_fifo_size[i]); ++ } ++ ++ _core_if->total_fifo_size = _core_if->hwcfg3.b.dfifo_depth; ++ _core_if->rx_fifo_size = dwc_read_reg32(&global_regs->grxfsiz); ++ _core_if->nperio_tx_fifo_size = dwc_read_reg32(&global_regs->gnptxfsiz) >> 16; ++ ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", _core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", _core_if->rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n", _core_if->nperio_tx_fifo_size); ++ ++ /* This programming sequence needs to happen in FS mode before any other ++ * programming occurs */ ++ if ((_core_if->core_params->speed == DWC_SPEED_PARAM_FULL) && ++ (_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* If FS mode with FS PHY */ ++ ++ /* core_init() is now called on every switch so only call the ++ * following for the first time through. */ ++ if (!_core_if->phy_init_done) { ++ _core_if->phy_init_done = 1; ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.physel = 1; ++ dwc_write_reg32 (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset after a PHY select */ ++ dwc_otg_core_reset( _core_if ); ++ } ++ ++ /* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also ++ * do this on HNP Dev/Host mode switches (done in dev_init and ++ * host_init). */ ++ if (dwc_otg_is_host_mode(_core_if)) { ++ DWC_DEBUGPL(DBG_CIL, "host mode\n"); ++ init_fslspclksel(_core_if); ++ } else { ++ DWC_DEBUGPL(DBG_CIL, "device mode\n"); ++ init_devspd(_core_if); ++ } ++ ++ if (_core_if->core_params->i2c_enable) { ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n"); ++ /* Program GUSBCFG.OtgUtmifsSel to I2C */ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.otgutmifssel = 1; ++ dwc_write_reg32 (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Program GI2CCTL.I2CEn */ ++ i2cctl.d32 = dwc_read_reg32(&global_regs->gi2cctl); ++ i2cctl.b.i2cdevaddr = 1; ++ i2cctl.b.i2cen = 0; ++ dwc_write_reg32 (&global_regs->gi2cctl, i2cctl.d32); ++ i2cctl.b.i2cen = 1; ++ dwc_write_reg32 (&global_regs->gi2cctl, i2cctl.d32); ++ } ++ ++ } /* endif speed == DWC_SPEED_PARAM_FULL */ ++ else { ++ /* High speed PHY. */ ++ if (!_core_if->phy_init_done) { ++ _core_if->phy_init_done = 1; ++ DWC_DEBUGPL(DBG_CIL, "High spped PHY\n"); ++ /* HS PHY parameters. These parameters are preserved ++ * during soft reset so only program the first time. Do ++ * a soft reset immediately after setting phyif. */ ++ usbcfg.b.ulpi_utmi_sel = _core_if->core_params->phy_type; ++ if (usbcfg.b.ulpi_utmi_sel == 2) { // winder ++ DWC_DEBUGPL(DBG_CIL, "ULPI\n"); ++ /* ULPI interface */ ++ usbcfg.b.phyif = 0; ++ usbcfg.b.ddrsel = _core_if->core_params->phy_ulpi_ddr; ++ } else { ++ /* UTMI+ interface */ ++ if (_core_if->core_params->phy_utmi_width == 16) { ++ usbcfg.b.phyif = 1; ++ DWC_DEBUGPL(DBG_CIL, "UTMI+ 16\n"); ++ } else { ++ DWC_DEBUGPL(DBG_CIL, "UTMI+ 8\n"); ++ usbcfg.b.phyif = 0; ++ } ++ } ++ dwc_write_reg32( &global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset after setting the PHY parameters */ ++ dwc_otg_core_reset( _core_if ); ++ } ++ } ++ ++ if ((_core_if->hwcfg2.b.hs_phy_type == 2) && ++ (_core_if->hwcfg2.b.fs_phy_type == 1) && ++ (_core_if->core_params->ulpi_fs_ls)) ++ { ++ DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 1; ++ usbcfg.b.ulpi_clk_sus_m = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ } else { ++ DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS=0\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 0; ++ usbcfg.b.ulpi_clk_sus_m = 0; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ } ++ ++ /* Program the GAHBCFG Register.*/ ++ switch (_core_if->hwcfg2.b.architecture){ ++ ++ case DWC_SLAVE_ONLY_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n"); ++ ahbcfg.b.nptxfemplvl_txfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ _core_if->dma_enable = 0; ++ break; ++ ++ case DWC_EXT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n"); ++ ahbcfg.b.hburstlen = _core_if->core_params->dma_burst_size; ++ _core_if->dma_enable = (_core_if->core_params->dma_enable != 0); ++ break; ++ ++ case DWC_INT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n"); ++ //ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR; ++ ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR4; ++ _core_if->dma_enable = (_core_if->core_params->dma_enable != 0); ++ break; ++ } ++ ahbcfg.b.dmaenable = _core_if->dma_enable; ++ dwc_write_reg32(&global_regs->gahbcfg, ahbcfg.d32); ++ _core_if->en_multiple_tx_fifo = _core_if->hwcfg4.b.ded_fifo_en; ++ ++ /* ++ * Program the GUSBCFG register. ++ */ ++ usbcfg.d32 = dwc_read_reg32( &global_regs->gusbcfg ); ++ ++ switch (_core_if->hwcfg2.b.op_mode) { ++ case DWC_MODE_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = (_core_if->core_params->otg_cap == ++ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE); ++ usbcfg.b.srpcap = (_core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_SRP_ONLY_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (_core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (_core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (_core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ } ++ ++ dwc_write_reg32( &global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Enable common interrupts */ ++ dwc_otg_enable_common_interrupts( _core_if ); ++ ++ /* Do device or host intialization based on mode during PCD ++ * and HCD initialization */ ++ if (dwc_otg_is_host_mode( _core_if )) { ++ DWC_DEBUGPL(DBG_ANY, "Host Mode\n" ); ++ _core_if->op_state = A_HOST; ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "Device Mode\n" ); ++ _core_if->op_state = B_PERIPHERAL; ++#ifdef DWC_DEVICE_ONLY ++ dwc_otg_core_dev_init( _core_if ); ++#endif ++ } ++} ++ ++ ++/** ++ * This function enables the Device mode interrupts. ++ * ++ * @param _core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t *_core_if) ++{ ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ dwc_otg_core_global_regs_t * global_regs = _core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ dwc_write_reg32( &global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts */ ++ dwc_write_reg32( &global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts( _core_if ); ++ ++ /* Enable interrupts */ ++ intr_mask.b.usbreset = 1; ++ intr_mask.b.enumdone = 1; ++ //intr_mask.b.epmismatch = 1; ++ intr_mask.b.inepintr = 1; ++ intr_mask.b.outepintr = 1; ++ intr_mask.b.erlysuspend = 1; ++ if (_core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.epmismatch = 1; ++ } ++ ++ /** @todo NGS: Should this be a module parameter? */ ++ intr_mask.b.isooutdrop = 1; ++ intr_mask.b.eopframe = 1; ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ ++ dwc_modify_reg32( &global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__, ++ dwc_read_reg32( &global_regs->gintmsk)); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * device mode. ++ * ++ * @param _core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_dev_init(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ _core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ dwc_otg_core_params_t *params = _core_if->core_params; ++ dcfg_data_t dcfg = {.d32 = 0}; ++ grstctl_t resetctl = { .d32=0 }; ++ int i; ++ uint32_t rx_fifo_size; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t txfifosize; ++ dthrctl_data_t dthrctl; ++ ++ fifosize_data_t ptxfifosize; ++ ++ /* Restart the Phy Clock */ ++ dwc_write_reg32(_core_if->pcgcctl, 0); ++ ++ /* Device configuration register */ ++ init_devspd(_core_if); ++ dcfg.d32 = dwc_read_reg32( &dev_if->dev_global_regs->dcfg); ++ dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80; ++ dwc_write_reg32( &dev_if->dev_global_regs->dcfg, dcfg.d32 ); ++ ++ /* Configure data FIFO sizes */ ++ if ( _core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo ) { ++ ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", _core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", params->dev_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", params->dev_nperio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ rx_fifo_size = params->dev_rx_fifo_size; ++ dwc_write_reg32( &global_regs->grxfsiz, rx_fifo_size ); ++ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ /** Set Periodic Tx FIFO Mask all bits 0 */ ++ _core_if->p_tx_msk = 0; ++ ++ /** Set Tx FIFO Mask all bits 0 */ ++ _core_if->tx_msk = 0; ++ if (_core_if->en_multiple_tx_fifo == 0) { ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ dwc_write_reg32( &global_regs->gnptxfsiz, nptxfifosize.d32 ); ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ ++ /**@todo NGS: Fix Periodic FIFO Sizing! */ ++ /* ++ * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_perio_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz array run from 0 to 14. ++ */ ++ /** @todo Finish debug of this */ ++ ptxfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ for (i = 0; i < _core_if->hwcfg4.b.num_dev_perio_in_ep;i++) { ++ ptxfifosize.b.depth = params->dev_perio_tx_fifo_size[i]; ++ DWC_DEBUGPL(DBG_CIL,"initial dptxfsiz_dieptxf[%d]=%08x\n", ++ i,dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])); ++ dwc_write_reg32(&global_regs->dptxfsiz_dieptxf[i],ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL,"new dptxfsiz_dieptxf[%d]=%08x\n", ++ i,dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])); ++ ptxfifosize.b.startaddr += ptxfifosize.b.depth; ++ } ++ } else { ++ ++ /* ++ * Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz_dieptxf array run from 0 to 14. ++ */ ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ txfifosize.b.startaddr = nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ for (i = 1;i < _core_if->hwcfg4.b.num_dev_perio_in_ep;i++) { ++ txfifosize.b.depth = params->dev_tx_fifo_size[i]; ++ DWC_DEBUGPL(DBG_CIL,"initial dptxfsiz_dieptxf[%d]=%08x\n", ++ i,dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])); ++ dwc_write_reg32(&global_regs->dptxfsiz_dieptxf[i - 1],txfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL,"new dptxfsiz_dieptxf[%d]=%08x\n", ++ i,dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i-1])); ++ txfifosize.b.startaddr += txfifosize.b.depth; ++ } ++ } ++ } ++ /* Flush the FIFOs */ ++ dwc_otg_flush_tx_fifo(_core_if, 0x10); /* all Tx FIFOs */ ++ dwc_otg_flush_rx_fifo(_core_if); ++ ++ /* Flush the Learning Queue. */ ++ resetctl.b.intknqflsh = 1; ++ dwc_write_reg32( &_core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ /* Clear all pending Device Interrupts */ ++ dwc_write_reg32( &dev_if->dev_global_regs->diepmsk, 0 ); ++ dwc_write_reg32( &dev_if->dev_global_regs->doepmsk, 0 ); ++ dwc_write_reg32( &dev_if->dev_global_regs->daint, 0xFFFFFFFF ); ++ dwc_write_reg32( &dev_if->dev_global_regs->daintmsk, 0 ); ++ ++ for (i = 0; i <= dev_if->num_in_eps; i++) { ++ depctl_data_t depctl; ++ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } else { ++ depctl.d32 = 0; ++ } ++ dwc_write_reg32( &dev_if->in_ep_regs[i]->diepctl, depctl.d32); ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->dieptsiz, 0); ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepdma, 0); ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepint, 0xFF); ++ } ++ for (i = 0; i <= dev_if->num_out_eps; i++) { ++ depctl_data_t depctl; ++ depctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } else { ++ depctl.d32 = 0; ++ } ++ dwc_write_reg32( &dev_if->out_ep_regs[i]->doepctl, depctl.d32); ++ ++ //dwc_write_reg32( &dev_if->in_ep_regs[i]->dieptsiz, 0); ++ dwc_write_reg32( &dev_if->out_ep_regs[i]->doeptsiz, 0); ++ //dwc_write_reg32( &dev_if->in_ep_regs[i]->diepdma, 0); ++ dwc_write_reg32( &dev_if->out_ep_regs[i]->doepdma, 0); ++ //dwc_write_reg32( &dev_if->in_ep_regs[i]->diepint, 0xFF); ++ dwc_write_reg32( &dev_if->out_ep_regs[i]->doepint, 0xFF); ++ } ++ ++ if (_core_if->en_multiple_tx_fifo && _core_if->dma_enable) { ++ dev_if->non_iso_tx_thr_en = _core_if->core_params->thr_ctl & 0x1; ++ dev_if->iso_tx_thr_en = (_core_if->core_params->thr_ctl >> 1) & 0x1; ++ dev_if->rx_thr_en = (_core_if->core_params->thr_ctl >> 2) & 0x1; ++ dev_if->rx_thr_length = _core_if->core_params->rx_thr_length; ++ dev_if->tx_thr_length = _core_if->core_params->tx_thr_length; ++ dthrctl.d32 = 0; ++ dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en; ++ dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en; ++ dthrctl.b.tx_thr_len = dev_if->tx_thr_length; ++ dthrctl.b.rx_thr_en = dev_if->rx_thr_en; ++ dthrctl.b.rx_thr_len = dev_if->rx_thr_length; ++ dwc_write_reg32(&dev_if->dev_global_regs->dtknqr3_dthrctl,dthrctl.d32); ++ DWC_DEBUGPL(DBG_CIL, "Non ISO Tx Thr - %d\nISO Tx Thr - %d\n" ++ "Rx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n", ++ dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en, ++ dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len, ++ dthrctl.b.rx_thr_len); ++ } ++ dwc_otg_enable_device_interrupts( _core_if ); ++ { ++ diepmsk_data_t msk = {.d32 = 0}; ++ msk.b.txfifoundrn = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->diepmsk, msk.d32,msk.d32); ++} ++} ++ ++/** ++ * This function enables the Host mode interrupts. ++ * ++ * @param _core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts. */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(_core_if); ++ ++ /* ++ * Enable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ ++ //dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ //dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++} ++ ++/** ++ * This function disables the Host Mode interrupts. ++ * ++ * @param _core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ _core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__); ++ ++ /* ++ * Disable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ intr_mask.b.ptxfempty = 1; ++ intr_mask.b.nptxfempty = 1; ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++} ++ ++#if 0 ++/* currently not used, keep it here as if needed later */ ++static int phy_read(dwc_otg_core_if_t * _core_if, int addr) ++{ ++ u32 val; ++ int timeout = 10; ++ ++ dwc_write_reg32(&_core_if->core_global_regs->gpvndctl, ++ 0x02000000 | (addr << 16)); ++ val = dwc_read_reg32(&_core_if->core_global_regs->gpvndctl); ++ while (((val & 0x08000000) == 0) && (timeout--)) { ++ udelay(1000); ++ val = dwc_read_reg32(&_core_if->core_global_regs->gpvndctl); ++ } ++ val = dwc_read_reg32(&_core_if->core_global_regs->gpvndctl); ++ printk("%s: addr=%02x regval=%02x\n", __func__, addr, val & 0x000000ff); ++ ++ return 0; ++} ++#endif ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * host mode. ++ * ++ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ * request queues. Host channels are reset to ensure that they are ready for ++ * performing transfers. ++ * ++ * @param _core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_host_init(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = _core_if->host_if; ++ dwc_otg_core_params_t *params = _core_if->core_params; ++ hprt0_data_t hprt0 = {.d32 = 0}; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t ptxfifosize; ++ int i; ++ hcchar_data_t hcchar; ++ hcfg_data_t hcfg; ++ dwc_otg_hc_regs_t *hc_regs; ++ int num_channels; ++ gotgctl_data_t gotgctl = {.d32 = 0}; ++ ++ DWC_DEBUGPL(DBG_CILV,"%s(%p)\n", __func__, _core_if); ++ ++ /* Restart the Phy Clock */ ++ dwc_write_reg32(_core_if->pcgcctl, 0); ++ ++ /* Initialize Host Configuration Register */ ++ init_fslspclksel(_core_if); ++ if (_core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg); ++ hcfg.b.fslssupp = 1; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, hcfg.d32); ++ } ++ ++ /* Configure data FIFO sizes */ ++ if (_core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL,"Total FIFO Size=%d\n", _core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"Rx FIFO Size=%d\n", params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"NP Tx FIFO Size=%d\n", params->host_nperio_tx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"P Tx FIFO Size=%d\n", params->host_perio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL,"initial grxfsiz=%08x\n", dwc_read_reg32(&global_regs->grxfsiz)); ++ dwc_write_reg32(&global_regs->grxfsiz, params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL,"new grxfsiz=%08x\n", dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL,"initial gnptxfsiz=%08x\n", dwc_read_reg32(&global_regs->gnptxfsiz)); ++ nptxfifosize.b.depth = params->host_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->host_rx_fifo_size; ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL,"new gnptxfsiz=%08x\n", dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ /* Periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL,"initial hptxfsiz=%08x\n", dwc_read_reg32(&global_regs->hptxfsiz)); ++ ptxfifosize.b.depth = params->host_perio_tx_fifo_size; ++ ptxfifosize.b.startaddr = nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ dwc_write_reg32(&global_regs->hptxfsiz, ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL,"new hptxfsiz=%08x\n", dwc_read_reg32(&global_regs->hptxfsiz)); ++ } ++ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ dwc_modify_reg32( &global_regs->gotgctl, gotgctl.d32, 0); ++ ++ /* Make sure the FIFOs are flushed. */ ++ dwc_otg_flush_tx_fifo(_core_if, 0x10 /* all Tx FIFOs */); ++ dwc_otg_flush_rx_fifo(_core_if); ++ ++ /* Flush out any leftover queued requests. */ ++ num_channels = _core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ hc_regs = _core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ ++ /* Halt all channels to put them into a known state. */ ++ for (i = 0; i < num_channels; i++) { ++ int count = 0; ++ hc_regs = _core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i); ++ do { ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (++count > 200) { ++ DWC_ERROR("%s: Unable to clear halt on channel %d\n", ++ __func__, i); ++ break; ++ } ++ udelay(100); ++ } while (hcchar.b.chen); ++ } ++ ++ /* Turn on the vbus power. */ ++ DWC_PRINT("Init: Port Power? op_state=%d\n", _core_if->op_state); ++ if (_core_if->op_state == A_HOST){ ++ hprt0.d32 = dwc_otg_read_hprt0(_core_if); ++ DWC_PRINT("Init: Power Port (%d)\n", hprt0.b.prtpwr); ++ if (hprt0.b.prtpwr == 0 ) { ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(host_if->hprt0, hprt0.d32); ++ } ++ } ++ ++ dwc_otg_enable_host_interrupts( _core_if ); ++} ++ ++/** ++ * Prepares a host channel for transferring packets to/from a specific ++ * endpoint. The HCCHARn register is set up with the characteristics specified ++ * in _hc. Host channel interrupts that may need to be serviced while this ++ * transfer is in progress are enabled. ++ * ++ * @param _core_if Programming view of DWC_otg controller ++ * @param _hc Information needed to initialize the host channel ++ */ ++void dwc_otg_hc_init(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ uint32_t intr_enable; ++ hcintmsk_data_t hc_intr_mask; ++ gintmsk_data_t gintmsk = {.d32 = 0}; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ ++ uint8_t hc_num = _hc->hc_num; ++ dwc_otg_host_if_t *host_if = _core_if->host_if; ++ dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num]; ++ ++ /* Clear old interrupt conditions for this host channel. */ ++ hc_intr_mask.d32 = 0xFFFFFFFF; ++ hc_intr_mask.b.reserved = 0; ++ dwc_write_reg32(&hc_regs->hcint, hc_intr_mask.d32); ++ ++ /* Enable channel interrupts required for this transfer. */ ++ hc_intr_mask.d32 = 0; ++ hc_intr_mask.b.chhltd = 1; ++ if (_core_if->dma_enable) { ++ hc_intr_mask.b.ahberr = 1; ++ if (_hc->error_state && !_hc->do_split && ++ _hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ hc_intr_mask.b.ack = 1; ++ if (_hc->ep_is_in) { ++ hc_intr_mask.b.datatglerr = 1; ++ if (_hc->ep_type != DWC_OTG_EP_TYPE_INTR) { ++ hc_intr_mask.b.nak = 1; ++ } ++ } ++ } ++ } else { ++ switch (_hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ if (_hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } else { ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.nyet = 1; ++ if (_hc->do_ping) { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (_hc->do_split) { ++ hc_intr_mask.b.nak = 1; ++ if (_hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } ++ else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (_hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ break; ++ case DWC_OTG_EP_TYPE_INTR: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ ++ if (_hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } ++ if (_hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ if (_hc->do_split) { ++ if (_hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } ++ else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ break; ++ case DWC_OTG_EP_TYPE_ISOC: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ hc_intr_mask.b.ack = 1; ++ ++ if (_hc->ep_is_in) { ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.bblerr = 1; ++ } ++ break; ++ } ++ } ++ dwc_write_reg32(&hc_regs->hcintmsk, hc_intr_mask.d32); ++ ++ /* Enable the top level host channel interrupt. */ ++ intr_enable = (1 << hc_num); ++ dwc_modify_reg32(&host_if->host_global_regs->haintmsk, 0, intr_enable); ++ ++ /* Make sure host channel interrupts are enabled. */ ++ gintmsk.b.hcintr = 1; ++ dwc_modify_reg32(&_core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ ++ /* ++ * Program the HCCHARn register with the endpoint characteristics for ++ * the current transfer. ++ */ ++ hcchar.d32 = 0; ++ hcchar.b.devaddr = _hc->dev_addr; ++ hcchar.b.epnum = _hc->ep_num; ++ hcchar.b.epdir = _hc->ep_is_in; ++ hcchar.b.lspddev = (_hc->speed == DWC_OTG_EP_SPEED_LOW); ++ hcchar.b.eptype = _hc->ep_type; ++ hcchar.b.mps = _hc->max_packet; ++ ++ dwc_write_reg32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n", hcchar.b.devaddr); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Num: %d\n", hcchar.b.epnum); ++ DWC_DEBUGPL(DBG_HCDV, " Is In: %d\n", hcchar.b.epdir); ++ DWC_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Type: %d\n", hcchar.b.eptype); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n", hcchar.b.multicnt); ++ ++ /* ++ * Program the HCSPLIT register for SPLITs ++ */ ++ hcsplt.d32 = 0; ++ if (_hc->do_split) { ++ DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", _hc->hc_num, ++ _hc->complete_split ? "CSPLIT" : "SSPLIT"); ++ hcsplt.b.compsplt = _hc->complete_split; ++ hcsplt.b.xactpos = _hc->xact_pos; ++ hcsplt.b.hubaddr = _hc->hub_addr; ++ hcsplt.b.prtaddr = _hc->port_addr; ++ DWC_DEBUGPL(DBG_HCDV, " comp split %d\n", _hc->complete_split); ++ DWC_DEBUGPL(DBG_HCDV, " xact pos %d\n", _hc->xact_pos); ++ DWC_DEBUGPL(DBG_HCDV, " hub addr %d\n", _hc->hub_addr); ++ DWC_DEBUGPL(DBG_HCDV, " port addr %d\n", _hc->port_addr); ++ DWC_DEBUGPL(DBG_HCDV, " is_in %d\n", _hc->ep_is_in); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " xferlen: %d\n", _hc->xfer_len); ++ } ++ dwc_write_reg32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32); ++ ++} ++ ++/** ++ * Attempts to halt a host channel. This function should only be called in ++ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under ++ * normal circumstances in DMA mode, the controller halts the channel when the ++ * transfer is complete or a condition occurs that requires application ++ * intervention. ++ * ++ * In slave mode, checks for a free request queue entry, then sets the Channel ++ * Enable and Channel Disable bits of the Host Channel Characteristics ++ * register of the specified channel to intiate the halt. If there is no free ++ * request queue entry, sets only the Channel Disable bit of the HCCHARn ++ * register to flush requests for this channel. In the latter case, sets a ++ * flag to indicate that the host channel needs to be halted when a request ++ * queue slot is open. ++ * ++ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the ++ * HCCHARn register. The controller ensures there is space in the request ++ * queue before submitting the halt request. ++ * ++ * Some time may elapse before the core flushes any posted requests for this ++ * host channel and halts. The Channel Halted interrupt handler completes the ++ * deactivation of the host channel. ++ * ++ * @param _core_if Controller register interface. ++ * @param _hc Host channel to halt. ++ * @param _halt_status Reason for halting the channel. ++ */ ++void dwc_otg_hc_halt(dwc_otg_core_if_t *_core_if, ++ dwc_hc_t *_hc, ++ dwc_otg_halt_status_e _halt_status) ++{ ++ gnptxsts_data_t nptxsts; ++ hptxsts_data_t hptxsts; ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_core_global_regs_t *global_regs; ++ dwc_otg_host_global_regs_t *host_global_regs; ++ ++ hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; ++ global_regs = _core_if->core_global_regs; ++ host_global_regs = _core_if->host_if->host_global_regs; ++ ++ WARN_ON(_halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ ++ if (_halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ _halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ /* ++ * Disable all channel interrupts except Ch Halted. The QTD ++ * and QH state associated with this transfer has been cleared ++ * (in the case of URB_DEQUEUE), so the channel needs to be ++ * shut down carefully to prevent crashes. ++ */ ++ hcintmsk_data_t hcintmsk; ++ hcintmsk.d32 = 0; ++ hcintmsk.b.chhltd = 1; ++ dwc_write_reg32(&hc_regs->hcintmsk, hcintmsk.d32); ++ ++ /* ++ * Make sure no other interrupts besides halt are currently ++ * pending. Handling another interrupt could cause a crash due ++ * to the QTD and QH state. ++ */ ++ dwc_write_reg32(&hc_regs->hcint, ~hcintmsk.d32); ++ ++ /* ++ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR ++ * even if the channel was already halted for some other ++ * reason. ++ */ ++ _hc->halt_status = _halt_status; ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen == 0) { ++ /* ++ * The channel is either already halted or it hasn't ++ * started yet. In DMA mode, the transfer may halt if ++ * it finishes normally or a condition occurs that ++ * requires driver intervention. Don't want to halt ++ * the channel again. In either Slave or DMA mode, ++ * it's possible that the transfer has been assigned ++ * to a channel, but not started yet when an URB is ++ * dequeued. Don't want to halt a channel that hasn't ++ * started yet. ++ */ ++ return; ++ } ++ } ++ ++ if (_hc->halt_pending) { ++ /* ++ * A halt has already been issued for this channel. This might ++ * happen when a transfer is aborted by a higher level in ++ * the stack. ++ */ ++#ifdef DEBUG ++ DWC_PRINT("*** %s: Channel %d, _hc->halt_pending already set ***\n", ++ __func__, _hc->hc_num); ++ ++/* dwc_otg_dump_global_registers(_core_if); */ ++/* dwc_otg_dump_host_registers(_core_if); */ ++#endif ++ return; ++ } ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ ++ if (!_core_if->dma_enable) { ++ /* Check for space in the request queue to issue the halt. */ ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ _hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ nptxsts.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ if (nptxsts.b.nptxqspcavail == 0) { ++ hcchar.b.chen = 0; ++ } ++ } else { ++ hptxsts.d32 = dwc_read_reg32(&host_global_regs->hptxsts); ++ if ((hptxsts.b.ptxqspcavail == 0) || (_core_if->queuing_high_bandwidth)) { ++ hcchar.b.chen = 0; ++ } ++ } ++ } ++ ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ _hc->halt_status = _halt_status; ++ ++ if (hcchar.b.chen) { ++ _hc->halt_pending = 1; ++ _hc->halt_on_queue = 0; ++ } else { ++ _hc->halt_on_queue = 1; ++ } ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", _hc->halt_pending); ++ DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", _hc->halt_on_queue); ++ DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", _hc->halt_status); ++ ++ return; ++} ++ ++/** ++ * Clears the transfer state for a host channel. This function is normally ++ * called after a transfer is done and the host channel is being released. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _hc Identifies the host channel to clean up. ++ */ ++void dwc_otg_hc_cleanup(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ dwc_otg_hc_regs_t *hc_regs; ++ ++ _hc->xfer_started = 0; ++ ++ /* ++ * Clear channel interrupt enables and any unhandled channel interrupt ++ * conditions. ++ */ ++ hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; ++ dwc_write_reg32(&hc_regs->hcintmsk, 0); ++ dwc_write_reg32(&hc_regs->hcint, 0xFFFFFFFF); ++ ++#ifdef DEBUG ++ del_timer(&_core_if->hc_xfer_timer[_hc->hc_num]); ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, _hc->hc_num, hcchar.d32); ++ } ++ } ++#endif ++} ++ ++/** ++ * Sets the channel property that indicates in which frame a periodic transfer ++ * should occur. This is always set to the _next_ frame. This function has no ++ * effect on non-periodic transfers. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _hc Identifies the host channel to set up and its properties. ++ * @param _hcchar Current value of the HCCHAR register for the specified host ++ * channel. ++ */ ++static inline void hc_set_even_odd_frame(dwc_otg_core_if_t *_core_if, ++ dwc_hc_t *_hc, ++ hcchar_data_t *_hcchar) ++{ ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hfnum_data_t hfnum; ++ hfnum.d32 = dwc_read_reg32(&_core_if->host_if->host_global_regs->hfnum); ++ /* 1 if _next_ frame is odd, 0 if it's even */ ++ _hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++#ifdef DEBUG ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR && _hc->do_split && !_hc->complete_split) { ++ switch (hfnum.b.frnum & 0x7) { ++ case 7: ++ _core_if->hfnum_7_samples++; ++ _core_if->hfnum_7_frrem_accum += hfnum.b.frrem; ++ break; ++ case 0: ++ _core_if->hfnum_0_samples++; ++ _core_if->hfnum_0_frrem_accum += hfnum.b.frrem; ++ break; ++ default: ++ _core_if->hfnum_other_samples++; ++ _core_if->hfnum_other_frrem_accum += hfnum.b.frrem; ++ break; ++ } ++ } ++#endif ++ } ++} ++ ++#ifdef DEBUG ++static void hc_xfer_timeout(unsigned long _ptr) ++{ ++ hc_xfer_info_t *xfer_info = (hc_xfer_info_t *)_ptr; ++ int hc_num = xfer_info->hc->hc_num; ++ DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num); ++ DWC_WARN(" start_hcchar_val 0x%08x\n", xfer_info->core_if->start_hcchar_val[hc_num]); ++} ++#endif ++ ++/* ++ * This function does the setup for a data transfer for a host channel and ++ * starts the transfer. May be called in either Slave mode or DMA mode. In ++ * Slave mode, the caller must ensure that there is sufficient space in the ++ * request queue and Tx Data FIFO. ++ * ++ * For an OUT transfer in Slave mode, it loads a data packet into the ++ * appropriate FIFO. If necessary, additional data packets will be loaded in ++ * the Host ISR. ++ * ++ * For an IN transfer in Slave mode, a data packet is requested. The data ++ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, ++ * additional data packets are requested in the Host ISR. ++ * ++ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ ++ * register along with a packet count of 1 and the channel is enabled. This ++ * causes a single PING transaction to occur. Other fields in HCTSIZ are ++ * simply set to 0 since no data transfer occurs in this case. ++ * ++ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with ++ * all the information required to perform the subsequent data transfer. In ++ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the ++ * controller performs the entire PING protocol, then starts the data ++ * transfer. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _hc Information needed to initialize the host channel. The xfer_len ++ * value may be reduced to accommodate the max widths of the XferSize and ++ * PktCnt fields in the HCTSIZn register. The multi_count value may be changed ++ * to reflect the final xfer_len value. ++ */ ++void dwc_otg_hc_start_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ uint16_t num_packets; ++ uint32_t max_hc_xfer_size = _core_if->core_params->max_transfer_size; ++ uint16_t max_hc_pkt_count = _core_if->core_params->max_packet_count; ++ dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; ++ ++ hctsiz.d32 = 0; ++ ++ if (_hc->do_ping) { ++ if (!_core_if->dma_enable) { ++ dwc_otg_hc_do_ping(_core_if, _hc); ++ _hc->xfer_started = 1; ++ return; ++ } else { ++ hctsiz.b.dopng = 1; ++ } ++ } ++ ++ if (_hc->do_split) { ++ num_packets = 1; ++ ++ if (_hc->complete_split && !_hc->ep_is_in) { ++ /* For CSPLIT OUT Transfer, set the size to 0 so the ++ * core doesn't expect any data written to the FIFO */ ++ _hc->xfer_len = 0; ++ } else if (_hc->ep_is_in || (_hc->xfer_len > _hc->max_packet)) { ++ _hc->xfer_len = _hc->max_packet; ++ } else if (!_hc->ep_is_in && (_hc->xfer_len > 188)) { ++ _hc->xfer_len = 188; ++ } ++ ++ hctsiz.b.xfersize = _hc->xfer_len; ++ } else { ++ /* ++ * Ensure that the transfer length and packet count will fit ++ * in the widths allocated for them in the HCTSIZn register. ++ */ ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure the transfer size is no larger than one ++ * (micro)frame's worth of data. (A check was done ++ * when the periodic transfer was accepted to ensure ++ * that a (micro)frame's worth of data can be ++ * programmed into a channel.) ++ */ ++ uint32_t max_periodic_len = _hc->multi_count * _hc->max_packet; ++ if (_hc->xfer_len > max_periodic_len) { ++ _hc->xfer_len = max_periodic_len; ++ } else { ++ } ++ } else if (_hc->xfer_len > max_hc_xfer_size) { ++ /* Make sure that xfer_len is a multiple of max packet size. */ ++ _hc->xfer_len = max_hc_xfer_size - _hc->max_packet + 1; ++ } ++ ++ if (_hc->xfer_len > 0) { ++ num_packets = (_hc->xfer_len + _hc->max_packet - 1) / _hc->max_packet; ++ if (num_packets > max_hc_pkt_count) { ++ num_packets = max_hc_pkt_count; ++ _hc->xfer_len = num_packets * _hc->max_packet; ++ } ++ } else { ++ /* Need 1 packet for transfer length of 0. */ ++ num_packets = 1; ++ } ++ ++ if (_hc->ep_is_in) { ++ /* Always program an integral # of max packets for IN transfers. */ ++ _hc->xfer_len = num_packets * _hc->max_packet; ++ } ++ ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure that the multi_count field matches the ++ * actual transfer length. ++ */ ++ _hc->multi_count = num_packets; ++ ++ } ++ ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* Set up the initial PID for the transfer. */ ++ if (_hc->speed == DWC_OTG_EP_SPEED_HIGH) { ++ if (_hc->ep_is_in) { ++ if (_hc->multi_count == 1) { ++ _hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } else if (_hc->multi_count == 2) { ++ _hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ } else { ++ _hc->data_pid_start = DWC_OTG_HC_PID_DATA2; ++ } ++ } else { ++ if (_hc->multi_count == 1) { ++ _hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } else { ++ _hc->data_pid_start = DWC_OTG_HC_PID_MDATA; ++ } ++ } ++ } else { ++ _hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } ++ } ++ ++ hctsiz.b.xfersize = _hc->xfer_len; ++ } ++ ++ _hc->start_pkt_count = num_packets; ++ hctsiz.b.pktcnt = num_packets; ++ hctsiz.b.pid = _hc->data_pid_start; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ ++ if (_core_if->dma_enable) { ++#ifdef DEBUG ++if(((uint32_t)_hc->xfer_buff)%4) ++printk("dwc_otg_hc_start_transfer _hc->xfer_buff not 4 byte alignment\n"); ++#endif ++ dwc_write_reg32(&hc_regs->hcdma, (uint32_t)_hc->xfer_buff); ++ } ++ ++ /* Start the split */ ++ if (_hc->do_split) { ++ hcsplt_data_t hcsplt; ++ hcsplt.d32 = dwc_read_reg32 (&hc_regs->hcsplt); ++ hcsplt.b.spltena = 1; ++ dwc_write_reg32(&hc_regs->hcsplt, hcsplt.d32); ++ } ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.multicnt = _hc->multi_count; ++ hc_set_even_odd_frame(_core_if, _hc, &hcchar); ++#ifdef DEBUG ++ _core_if->start_hcchar_val[_hc->hc_num] = hcchar.d32; ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, _hc->hc_num, hcchar.d32); ++ } ++#endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ _hc->xfer_started = 1; ++ _hc->requests++; ++ ++ if (!_core_if->dma_enable && !_hc->ep_is_in && _hc->xfer_len > 0) { ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(_core_if, _hc); ++ } ++ ++#ifdef DEBUG ++ /* Start a timer for this transfer. */ ++ _core_if->hc_xfer_timer[_hc->hc_num].function = hc_xfer_timeout; ++ _core_if->hc_xfer_info[_hc->hc_num].core_if = _core_if; ++ _core_if->hc_xfer_info[_hc->hc_num].hc = _hc; ++ _core_if->hc_xfer_timer[_hc->hc_num].data = (unsigned long)(&_core_if->hc_xfer_info[_hc->hc_num]); ++ _core_if->hc_xfer_timer[_hc->hc_num].expires = jiffies + (HZ*10); ++ add_timer(&_core_if->hc_xfer_timer[_hc->hc_num]); ++#endif ++} ++ ++/** ++ * This function continues a data transfer that was started by previous call ++ * to dwc_otg_hc_start_transfer. The caller must ensure there is ++ * sufficient space in the request queue and Tx Data FIFO. This function ++ * should only be called in Slave mode. In DMA mode, the controller acts ++ * autonomously to complete transfers programmed to a host channel. ++ * ++ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO ++ * if there is any data remaining to be queued. For an IN transfer, another ++ * data packet is always requested. For the SETUP phase of a control transfer, ++ * this function does nothing. ++ * ++ * @return 1 if a new request is queued, 0 if no more requests are required ++ * for this transfer. ++ */ ++int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); ++ ++ if (_hc->do_split) { ++ /* SPLITs always queue just once per channel */ ++ return 0; ++ } else if (_hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ /* SETUPs are queued only once since they can't be NAKed. */ ++ return 0; ++ } else if (_hc->ep_is_in) { ++ /* ++ * Always queue another request for other IN transfers. If ++ * back-to-back INs are issued and NAKs are received for both, ++ * the driver may still be processing the first NAK when the ++ * second NAK is received. When the interrupt handler clears ++ * the NAK interrupt for the first NAK, the second NAK will ++ * not be seen. So we can't depend on the NAK interrupt ++ * handler to requeue a NAKed request. Instead, IN requests ++ * are issued each time this function is called. When the ++ * transfer completes, the extra requests for the channel will ++ * be flushed. ++ */ ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(_core_if, _hc, &hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n", hcchar.d32); ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ _hc->requests++; ++ return 1; ++ } else { ++ /* OUT transfers. */ ++ if (_hc->xfer_count < _hc->xfer_len) { ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(_core_if, _hc, &hcchar); ++ } ++ ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(_core_if, _hc); ++ _hc->requests++; ++ return 1; ++ } else { ++ return 0; ++ } ++ } ++} ++ ++/** ++ * Starts a PING transfer. This function should only be called in Slave mode. ++ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled. ++ */ ++void dwc_otg_hc_do_ping(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ dwc_otg_hc_regs_t *hc_regs = _core_if->host_if->hc_regs[_hc->hc_num]; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _hc->hc_num); ++ ++ hctsiz.d32 = 0; ++ hctsiz.b.dopng = 1; ++ hctsiz.b.pktcnt = 1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++} ++ ++/* ++ * This function writes a packet into the Tx FIFO associated with the Host ++ * Channel. For a channel associated with a non-periodic EP, the non-periodic ++ * Tx FIFO is written. For a channel associated with a periodic EP, the ++ * periodic Tx FIFO is written. This function should only be called in Slave ++ * mode. ++ * ++ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by ++ * then number of bytes written to the Tx FIFO. ++ */ ++void dwc_otg_hc_write_packet(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ uint32_t i; ++ uint32_t remaining_count; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ ++ uint32_t *data_buff = (uint32_t *)(_hc->xfer_buff); ++ uint32_t *data_fifo = _core_if->data_fifo[_hc->hc_num]; ++ ++ remaining_count = _hc->xfer_len - _hc->xfer_count; ++ if (remaining_count > _hc->max_packet) { ++ byte_count = _hc->max_packet; ++ } else { ++ byte_count = remaining_count; ++ } ++ ++ dword_count = (byte_count + 3) / 4; ++ ++ if ((((unsigned long)data_buff) & 0x3) == 0) { ++ /* xfer_buff is DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ dwc_write_reg32(data_fifo, *data_buff); ++ } ++ } else { ++ /* xfer_buff is not DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ dwc_write_reg32(data_fifo, get_unaligned(data_buff)); ++ } ++ } ++ ++ _hc->xfer_count += byte_count; ++ _hc->xfer_buff += byte_count; ++} ++ ++/** ++ * Gets the current USB frame number. This is the frame number from the last ++ * SOF packet. ++ */ ++uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t *_core_if) ++{ ++ dsts_data_t dsts; ++ dsts.d32 = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dsts); ++ ++ /* read current frame/microfreme number from DSTS register */ ++ return dsts.b.soffn; ++} ++ ++/** ++ * This function reads a setup packet from the Rx FIFO into the destination ++ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl) ++ * Interrupt routine when a SETUP packet has been received in Slave mode. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _dest Destination buffer for packet data. ++ */ ++void dwc_otg_read_setup_packet(dwc_otg_core_if_t *_core_if, uint32_t *_dest) ++{ ++ /* Get the 8 bytes of a setup transaction data */ ++ ++ /* Pop 2 DWORDS off the receive data FIFO into memory */ ++ _dest[0] = dwc_read_reg32(_core_if->data_fifo[0]); ++ _dest[1] = dwc_read_reg32(_core_if->data_fifo[0]); ++ //_dest[0] = dwc_read_datafifo32(_core_if->data_fifo[0]); ++ //_dest[1] = dwc_read_datafifo32(_core_if->data_fifo[0]); ++} ++ ++ ++/** ++ * This function enables EP0 OUT to receive SETUP packets and configures EP0 ++ * IN for transmitting packets. It is normally called when the ++ * "Enumeration Done" interrupt occurs. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP0 data. ++ */ ++void dwc_otg_ep0_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ dsts_data_t dsts; ++ depctl_data_t diepctl; ++ depctl_data_t doepctl; ++ dctl_data_t dctl ={.d32=0}; ++ ++ /* Read the Device Status and Endpoint 0 Control registers */ ++ dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts); ++ diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl); ++ doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl); ++ ++ /* Set the MPS of the IN EP based on the enumeration speed */ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_64; ++ break; ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_8; ++ break; ++ } ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); ++ ++ /* Enable OUT EP for receive */ ++ doepctl.b.epena = 1; ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV,"doepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV,"diepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++ dctl.b.cgnpinnak = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ DWC_DEBUGPL(DBG_PCDV,"dctl=%0x\n", ++ dwc_read_reg32(&dev_if->dev_global_regs->dctl)); ++} ++ ++/** ++ * This function activates an EP. The Device EP control register for ++ * the EP is configured as defined in the ep structure. Note: This ++ * function is not used for EP0. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP to activate. ++ */ ++void dwc_otg_ep_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ depctl_data_t depctl; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = {.d32=0}; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, _ep->num, ++ (_ep->is_in?"IN":"OUT")); ++ ++ /* Read DEPCTLn register */ ++ if (_ep->is_in == 1) { ++ addr = &dev_if->in_ep_regs[_ep->num]->diepctl; ++ daintmsk.ep.in = 1<<_ep->num; ++ } else { ++ addr = &dev_if->out_ep_regs[_ep->num]->doepctl; ++ daintmsk.ep.out = 1<<_ep->num; ++ } ++ ++ /* If the EP is already active don't change the EP Control ++ * register. */ ++ depctl.d32 = dwc_read_reg32(addr); ++ if (!depctl.b.usbactep) { ++ depctl.b.mps = _ep->maxpacket; ++ depctl.b.eptype = _ep->type; ++ depctl.b.txfnum = _ep->tx_fifo_num; ++ ++ if (_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ depctl.b.setd0pid = 1; // ??? ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ depctl.b.usbactep = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCDV,"DEPCTL=%08x\n", dwc_read_reg32(addr)); ++ } ++ ++ ++ /* Enable the Interrupt for this EP */ ++ dwc_modify_reg32(&dev_if->dev_global_regs->daintmsk, ++ 0, daintmsk.d32); ++ DWC_DEBUGPL(DBG_PCDV,"DAINTMSK=%0x\n", ++ dwc_read_reg32(&dev_if->dev_global_regs->daintmsk)); ++ _ep->stall_clear_flag = 0; ++ return; ++} ++ ++/** ++ * This function deactivates an EP. This is done by clearing the USB Active ++ * EP bit in the Device EP control register. Note: This function is not used ++ * for EP0. EP0 cannot be deactivated. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP to deactivate. ++ */ ++void dwc_otg_ep_deactivate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ depctl_data_t depctl ={.d32 = 0}; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = {.d32=0}; ++ ++ /* Read DEPCTLn register */ ++ if (_ep->is_in == 1) { ++ addr = &_core_if->dev_if->in_ep_regs[_ep->num]->diepctl; ++ daintmsk.ep.in = 1<<_ep->num; ++ } else { ++ addr = &_core_if->dev_if->out_ep_regs[_ep->num]->doepctl; ++ daintmsk.ep.out = 1<<_ep->num; ++ } ++ ++ depctl.b.usbactep = 0; ++ dwc_write_reg32(addr, depctl.d32); ++ ++ /* Disable the Interrupt for this EP */ ++ dwc_modify_reg32(&_core_if->dev_if->dev_global_regs->daintmsk, ++ daintmsk.d32, 0); ++ ++ return; ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP to start the transfer on. ++ */ ++void dwc_otg_ep_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ /** @todo Refactor this funciton to check the transfer size ++ * count value does not execed the number bits in the Transfer ++ * count register. */ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++#ifdef CHECK_PACKET_COUNTER_WIDTH ++ const uint32_t MAX_XFER_SIZE = ++ _core_if->core_params->max_transfer_size; ++ const uint32_t MAX_PKT_COUNT = ++ _core_if->core_params->max_packet_count; ++ uint32_t num_packets; ++ uint32_t transfer_len; ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ _core_if->dev_if->out_ep_regs[_ep->num]; ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ _core_if->dev_if->in_ep_regs[_ep->num]; ++ gnptxsts_data_t txstatus; ++ ++ int lvl = SET_DEBUG_LEVEL(DBG_PCD); ++ ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p\n", ++ _ep->num, (_ep->is_in?"IN":"OUT"), _ep->xfer_len, ++ _ep->xfer_count, _ep->xfer_buff, _ep->start_xfer_buff); ++ ++ transfer_len = _ep->xfer_len - _ep->xfer_count; ++ if (transfer_len > MAX_XFER_SIZE) { ++ transfer_len = MAX_XFER_SIZE; ++ } ++ if (transfer_len == 0) { ++ num_packets = 1; ++ /* OUT EP to recieve Zero-length packet set transfer ++ * size to maxpacket size. */ ++ if (!_ep->is_in) { ++ transfer_len = _ep->maxpacket; ++ } ++ } else { ++ num_packets = ++ (transfer_len + _ep->maxpacket - 1) / _ep->maxpacket; ++ if (num_packets > MAX_PKT_COUNT) { ++ num_packets = MAX_PKT_COUNT; ++ } ++ } ++ DWC_DEBUGPL(DBG_PCD, "transfer_len=%d #pckt=%d\n", transfer_len, ++ num_packets); ++ ++ deptsiz.b.xfersize = transfer_len; ++ deptsiz.b.pktcnt = num_packets; ++ ++ /* IN endpoint */ ++ if (_ep->is_in == 1) { ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ } else {/* OUT endpoint */ ++ depctl.d32 = dwc_read_reg32(&out_regs->doepctl); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ /* IN endpoint */ ++ if (_ep->is_in == 1) { ++ txstatus.d32 = ++ dwc_read_reg32(&_core_if->core_global_regs->gnptxsts); ++ if (txstatus.b.nptxqspcavail == 0) { ++ DWC_DEBUGPL(DBG_ANY, "TX Queue Full (0x%0x)\n", ++ txstatus.d32); ++ return; ++ } ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (_core_if->dma_enable) { ++ dwc_write_reg32(&in_regs->diepdma, (uint32_t) _ep->xfer_buff); ++ } else { ++ if (_core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32( &_core_if->core_global_regs->gintsts, ++ intr_mask.d32, 0); ++ dwc_modify_reg32( &_core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (_ep->xfer_len > 0 && ++ _ep->type != DWC_OTG_EP_TYPE_ISOC) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = (0x1 << _ep->num); ++ dwc_modify_reg32(&_core_if->dev_if->dev_global_regs-> ++ dtknqr4_fifoemptymsk,0, fifoemptymsk); ++ } ++ } ++ } ++ } else { /* OUT endpoint */ ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ if (_core_if->dma_enable) { ++ dwc_write_reg32(&out_regs->doepdma,(uint32_t) _ep->xfer_buff); ++ } ++ } ++ DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n", ++ dwc_read_reg32(&out_regs->doepctl), ++ dwc_read_reg32(&out_regs->doeptsiz)); ++ DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n", ++ dwc_read_reg32(&_core_if->dev_if->dev_global_regs->daintmsk), ++ dwc_read_reg32(&_core_if->core_global_regs->gintmsk)); ++ ++ SET_DEBUG_LEVEL(lvl); ++#endif ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p\n", ++ _ep->num, (_ep->is_in?"IN":"OUT"), _ep->xfer_len, ++ _ep->xfer_count, _ep->xfer_buff, _ep->start_xfer_buff); ++ ++ /* IN endpoint */ ++ if (_ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t * in_regs = _core_if->dev_if->in_ep_regs[_ep->num]; ++ gnptxsts_data_t gtxstatus; ++ gtxstatus.d32 = dwc_read_reg32(&_core_if->core_global_regs->gnptxsts); ++ if (_core_if->en_multiple_tx_fifo == 0 && ++ gtxstatus.b.nptxqspcavail == 0) { ++#ifdef DEBUG ++ DWC_PRINT("TX Queue Full (0x%0x)\n", gtxstatus.d32); ++#endif ++ //return; ++ MDELAY(100); //james ++ } ++ ++ depctl.d32 = dwc_read_reg32(&(in_regs->diepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz)); ++ ++ /* Zero Length Packet? */ ++ if (_ep->xfer_len == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = _ep->xfer_len; ++ deptsiz.b.pktcnt = (_ep->xfer_len - 1 + _ep->maxpacket) / _ep->maxpacket; ++ } ++ ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (_core_if->dma_enable) { ++#if 1 // winder ++ dma_cache_wback_inv((unsigned long) _ep->xfer_buff, _ep->xfer_len); // winder ++ dwc_write_reg32 (&(in_regs->diepdma), ++ CPHYSADDR((uint32_t)_ep->xfer_buff)); // winder ++#else ++ dwc_write_reg32 (&(in_regs->diepdma), ++ (uint32_t)_ep->dma_addr); ++#endif ++ } else { ++ if (_ep->type != DWC_OTG_EP_TYPE_ISOC) { ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if (_core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32( &_core_if->core_global_regs->gintsts, ++ intr_mask.d32, 0); ++ dwc_modify_reg32( &_core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (_ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << _ep->num; ++ dwc_modify_reg32(&_core_if->dev_if->dev_global_regs-> ++ dtknqr4_fifoemptymsk,0,fifoemptymsk); ++ } ++ } ++ } ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ if (_core_if->dma_enable) { ++ depctl.d32 = dwc_read_reg32 (&_core_if->dev_if->in_ep_regs[0]->diepctl); ++ depctl.b.nextep = _ep->num; ++ dwc_write_reg32 (&_core_if->dev_if->in_ep_regs[0]->diepctl, depctl.d32); ++ ++ } ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t * out_regs = _core_if->dev_if->out_ep_regs[_ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(out_regs->doepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(out_regs->doeptsiz)); ++ ++ /* Program the transfer size and packet count as follows: ++ * ++ * pktcnt = N ++ * xfersize = N * maxpacket ++ */ ++ if (_ep->xfer_len == 0) { ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = _ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ deptsiz.b.pktcnt = (_ep->xfer_len + (_ep->maxpacket - 1)) / _ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * _ep->maxpacket; ++ } ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ ++ DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n", ++ _ep->num, deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (_core_if->dma_enable) { ++#if 1 // winder ++ dwc_write_reg32 (&(out_regs->doepdma), ++ CPHYSADDR((uint32_t)_ep->xfer_buff)); // winder ++#else ++ dwc_write_reg32 (&(out_regs->doepdma), ++ (uint32_t)_ep->dma_addr); ++#endif ++ } ++ ++ if (_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ /** @todo NGS: dpid is read-only. Use setd0pid ++ * or setd1pid. */ ++ if (_ep->even_odd_frame) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n", ++ dwc_read_reg32(&out_regs->doepctl), ++ dwc_read_reg32(&out_regs->doeptsiz)); ++ DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n", ++ dwc_read_reg32(&_core_if->dev_if->dev_global_regs->daintmsk), ++ dwc_read_reg32(&_core_if->core_global_regs->gintmsk)); ++ } ++} ++ ++ ++/** ++ * This function does the setup for a data transfer for EP0 and starts ++ * the transfer. For an IN transfer, the packets will be loaded into ++ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are ++ * unloaded from the Rx FIFO in the ISR. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP0 data. ++ */ ++void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ volatile depctl_data_t depctl; ++ volatile deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p total_len=%d\n", ++ _ep->num, (_ep->is_in?"IN":"OUT"), _ep->xfer_len, ++ _ep->xfer_count, _ep->xfer_buff, _ep->start_xfer_buff, ++ _ep->total_len); ++ _ep->total_len = _ep->xfer_len; ++ ++ /* IN endpoint */ ++ if (_ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t * in_regs = _core_if->dev_if->in_ep_regs[0]; ++ gnptxsts_data_t gtxstatus; ++ gtxstatus.d32 = dwc_read_reg32(&_core_if->core_global_regs->gnptxsts); ++ if (_core_if->en_multiple_tx_fifo == 0 && ++ gtxstatus.b.nptxqspcavail == 0) { ++#ifdef DEBUG ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ DWC_DEBUGPL(DBG_PCD,"DIEPCTL0=%0x\n", ++ dwc_read_reg32(&in_regs->diepctl)); ++ DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n", ++ deptsiz.d32, deptsiz.b.xfersize,deptsiz.b.pktcnt); ++ DWC_PRINT("TX Queue or FIFO Full (0x%0x)\n", gtxstatus.d32); ++#endif /* */ ++ printk("TX Queue or FIFO Full!!!!\n"); // test-only ++ //return; ++ MDELAY(100); //james ++ } ++ ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ ++ /* Zero Length Packet? */ ++ if (_ep->xfer_len == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ if (_ep->xfer_len > _ep->maxpacket) { ++ _ep->xfer_len = _ep->maxpacket; ++ deptsiz.b.xfersize = _ep->maxpacket; ++ } ++ else { ++ deptsiz.b.xfersize = _ep->xfer_len; ++ } ++ deptsiz.b.pktcnt = 1; ++ ++ } ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ DWC_DEBUGPL(DBG_PCDV, "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ _ep->xfer_len, deptsiz.b.xfersize,deptsiz.b.pktcnt, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (_core_if->dma_enable) { ++ dwc_write_reg32(&(in_regs->diepdma), (uint32_t) _ep->dma_addr); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!_core_if->dma_enable) { ++ if (_core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&_core_if->core_global_regs->gintsts, intr_mask.d32, 0); ++ dwc_modify_reg32(&_core_if->core_global_regs->gintmsk, intr_mask.d32, ++ intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (_ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << _ep->num; ++ dwc_modify_reg32(&_core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ ++ } ++ } ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t * out_regs = _core_if->dev_if->out_ep_regs[_ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&out_regs->doepctl); ++ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count as follows: ++ * xfersize = N * (maxpacket + 4 - (maxpacket % 4)) ++ * pktcnt = N */ ++ if (_ep->xfer_len == 0) { ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = _ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ deptsiz.b.pktcnt = (_ep->xfer_len + (_ep->maxpacket - 1)) / _ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * _ep->maxpacket; ++ } ++ ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ DWC_DEBUGPL(DBG_PCDV, "len=%d xfersize=%d pktcnt=%d\n", ++ _ep->xfer_len, deptsiz.b.xfersize,deptsiz.b.pktcnt); ++ ++ if (_core_if->dma_enable) { ++ dwc_write_reg32(&(out_regs->doepdma), (uint32_t) _ep->dma_addr); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32 (&(out_regs->doepctl), depctl.d32); ++ } ++} ++ ++/** ++ * This function continues control IN transfers started by ++ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a ++ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one ++ * bit for the packet count. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP0 data. ++ */ ++void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = { .d32 = 0}; ++ ++ if (_ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ _core_if->dev_if->in_ep_regs[0]; ++ gnptxsts_data_t tx_status = {.d32 = 0}; ++ ++ tx_status.d32 = dwc_read_reg32( &_core_if->core_global_regs->gnptxsts ); ++ /** @todo Should there be check for room in the Tx ++ * Status Queue. If not remove the code above this comment. */ ++ ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = (_ep->total_len - _ep->xfer_count) > _ep->maxpacket ? _ep->maxpacket : ++ (_ep->total_len - _ep->xfer_count); ++ deptsiz.b.pktcnt = 1; ++ _ep->xfer_len += deptsiz.b.xfersize; ++ ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ DWC_DEBUGPL(DBG_PCDV, "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ _ep->xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (_core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ dwc_write_reg32 (&(in_regs->diepdma), ++ CPHYSADDR((uint32_t)_ep->dma_addr)); // winder ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!_core_if->dma_enable) { ++ /* First clear it from GINTSTS */ ++ intr_mask.b.nptxfempty = 1; ++ dwc_write_reg32( &_core_if->core_global_regs->gintsts, ++ intr_mask.d32 ); ++ ++ dwc_modify_reg32( &_core_if->core_global_regs->gintmsk, ++ intr_mask.d32, intr_mask.d32); ++ } ++ ++ } ++ ++} ++ ++#ifdef DEBUG ++void dump_msg(const u8 *buf, unsigned int length) ++{ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ start = 0; ++ while (length > 0) { ++ num = min(length, 16u); ++ p = line; ++ for (i = 0; i < num; ++i) { ++ if (i == 8) ++ *p++ = ' '; ++ sprintf(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ DWC_PRINT( "%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++} ++#else ++static inline void dump_msg(const u8 *buf, unsigned int length) ++{ ++} ++#endif ++ ++/** ++ * This function writes a packet into the Tx FIFO associated with the ++ * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For ++ * periodic EPs the periodic Tx FIFO associated with the EP is written ++ * with all packets for the next micro-frame. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP to write packet for. ++ * @param _dma Indicates if DMA is being used. ++ */ ++void dwc_otg_ep_write_packet(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep, int _dma) ++{ ++ /** ++ * The buffer is padded to DWORD on a per packet basis in ++ * slave/dma mode if the MPS is not DWORD aligned. The last ++ * packet, if short, is also padded to a multiple of DWORD. ++ * ++ * ep->xfer_buff always starts DWORD aligned in memory and is a ++ * multiple of DWORD in length ++ * ++ * ep->xfer_len can be any number of bytes ++ * ++ * ep->xfer_count is a multiple of ep->maxpacket until the last ++ * packet ++ * ++ * FIFO access is DWORD */ ++ ++ uint32_t i; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ uint32_t *fifo; ++ uint32_t *data_buff = (uint32_t *)_ep->xfer_buff; ++ ++ //DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, _core_if, _ep); ++ if (_ep->xfer_count >= _ep->xfer_len) { ++ DWC_WARN("%s() No data for EP%d!!!\n", __func__, _ep->num); ++ return; ++ } ++ ++ /* Find the byte length of the packet either short packet or MPS */ ++ if ((_ep->xfer_len - _ep->xfer_count) < _ep->maxpacket) { ++ byte_count = _ep->xfer_len - _ep->xfer_count; ++ } ++ else { ++ byte_count = _ep->maxpacket; ++ } ++ ++ /* Find the DWORD length, padded by extra bytes as neccessary if MPS ++ * is not a multiple of DWORD */ ++ dword_count = (byte_count + 3) / 4; ++ ++#ifdef VERBOSE ++ dump_msg(_ep->xfer_buff, byte_count); ++#endif ++ if (_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ /**@todo NGS Where are the Periodic Tx FIFO addresses ++ * intialized? What should this be? */ ++ fifo = _core_if->data_fifo[_ep->tx_fifo_num]; ++ } else { ++ fifo = _core_if->data_fifo[_ep->num]; ++ } ++ ++ DWC_DEBUGPL((DBG_PCDV|DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n", ++ fifo, data_buff, *data_buff, byte_count); ++ ++ ++ if (!_dma) { ++ for (i=0; ixfer_count += byte_count; ++ _ep->xfer_buff += byte_count; ++#if 1 // winder, why do we need this?? ++ _ep->dma_addr += byte_count; ++#endif ++} ++ ++/** ++ * Set the EP STALL. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP to set the stall on. ++ */ ++void dwc_otg_ep_set_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, _ep->num, ++ (_ep->is_in?"IN":"OUT")); ++ ++ if (_ep->is_in == 1) { ++ depctl_addr = &(_core_if->dev_if->in_ep_regs[_ep->num]->diepctl); ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ ++ } else { ++ depctl_addr = &(_core_if->dev_if->out_ep_regs[_ep->num]->doepctl); ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* set the stall bit */ ++ depctl.b.stall = 1; ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ } ++ DWC_DEBUGPL(DBG_PCD,"DEPCTL=%0x\n",dwc_read_reg32(depctl_addr)); ++ return; ++} ++ ++/** ++ * Clear the EP STALL. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _ep The EP to clear stall from. ++ */ ++void dwc_otg_ep_clear_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, _ep->num, ++ (_ep->is_in?"IN":"OUT")); ++ ++ if (_ep->is_in == 1) { ++ depctl_addr = &(_core_if->dev_if->in_ep_regs[_ep->num]->diepctl); ++ } else { ++ depctl_addr = &(_core_if->dev_if->out_ep_regs[_ep->num]->doepctl); ++ } ++ ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* clear the stall bits */ ++ depctl.b.stall = 0; ++ ++ /* ++ * USB Spec 9.4.5: For endpoints using data toggle, regardless ++ * of whether an endpoint has the Halt feature set, a ++ * ClearFeature(ENDPOINT_HALT) request always results in the ++ * data toggle being reinitialized to DATA0. ++ */ ++ if (_ep->type == DWC_OTG_EP_TYPE_INTR || ++ _ep->type == DWC_OTG_EP_TYPE_BULK) { ++ depctl.b.setd0pid = 1; /* DATA0 */ ++ } ++ ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCD,"DEPCTL=%0x\n",dwc_read_reg32(depctl_addr)); ++ return; ++} ++ ++/** ++ * This function reads a packet from the Rx FIFO into the destination ++ * buffer. To read SETUP data use dwc_otg_read_setup_packet. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _dest Destination buffer for the packet. ++ * @param _bytes Number of bytes to copy to the destination. ++ */ ++void dwc_otg_read_packet(dwc_otg_core_if_t *_core_if, ++ uint8_t *_dest, ++ uint16_t _bytes) ++{ ++ int i; ++ int word_count = (_bytes + 3) / 4; ++ ++ volatile uint32_t *fifo = _core_if->data_fifo[0]; ++ uint32_t *data_buff = (uint32_t *)_dest; ++ ++ /** ++ * @todo Account for the case where _dest is not dword aligned. This ++ * requires reading data from the FIFO into a uint32_t temp buffer, ++ * then moving it into the data buffer. ++ */ ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__, ++ _core_if, _dest, _bytes); ++ ++ for (i=0; idev_if->dev_global_regs->dcfg; ++ DWC_PRINT("DCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->dev_global_regs->dctl; ++ DWC_PRINT("DCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->dev_global_regs->dsts; ++ DWC_PRINT("DSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->dev_global_regs->diepmsk; ++ DWC_PRINT("DIEPMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->dev_global_regs->doepmsk; ++ DWC_PRINT("DOEPMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->dev_global_regs->daint; ++ DWC_PRINT("DAINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->dev_global_regs->dtknqr1; ++ DWC_PRINT("DTKNQR1 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ if (_core_if->hwcfg2.b.dev_token_q_depth > 6) { ++ addr=&_core_if->dev_if->dev_global_regs->dtknqr2; ++ DWC_PRINT("DTKNQR2 @0x%08X : 0x%08X\n", ++ (uint32_t)addr,dwc_read_reg32(addr)); ++ } ++ ++ addr=&_core_if->dev_if->dev_global_regs->dvbusdis; ++ DWC_PRINT("DVBUSID @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ addr=&_core_if->dev_if->dev_global_regs->dvbuspulse; ++ DWC_PRINT("DVBUSPULSE @0x%08X : 0x%08X\n", ++ (uint32_t)addr,dwc_read_reg32(addr)); ++ ++ if (_core_if->hwcfg2.b.dev_token_q_depth > 14) { ++ addr = &_core_if->dev_if->dev_global_regs->dtknqr3_dthrctl; ++ DWC_PRINT("DTKNQR3 @0x%08X : 0x%08X\n", ++ (uint32_t)addr, dwc_read_reg32(addr)); ++ } ++ ++ if (_core_if->hwcfg2.b.dev_token_q_depth > 22) { ++ addr = &_core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINT("DTKNQR4 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ } ++ for (i = 0; i <= _core_if->dev_if->num_in_eps; i++) { ++ DWC_PRINT("Device IN EP %d Registers\n", i); ++ addr=&_core_if->dev_if->in_ep_regs[i]->diepctl; ++ DWC_PRINT("DIEPCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->in_ep_regs[i]->diepint; ++ DWC_PRINT("DIEPINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->in_ep_regs[i]->dieptsiz; ++ DWC_PRINT("DIETSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->in_ep_regs[i]->diepdma; ++ DWC_PRINT("DIEPDMA @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++addr = &_core_if->dev_if->in_ep_regs[i]->dtxfsts; ++ DWC_PRINT("DTXFSTS @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ } ++ for (i = 0; i <= _core_if->dev_if->num_out_eps; i++) { ++ DWC_PRINT("Device OUT EP %d Registers\n", i); ++ addr=&_core_if->dev_if->out_ep_regs[i]->doepctl; ++ DWC_PRINT("DOEPCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->out_ep_regs[i]->doepfn; ++ DWC_PRINT("DOEPFN @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->out_ep_regs[i]->doepint; ++ DWC_PRINT("DOEPINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->out_ep_regs[i]->doeptsiz; ++ DWC_PRINT("DOETSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->dev_if->out_ep_regs[i]->doepdma; ++ DWC_PRINT("DOEPDMA @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ } ++ return; ++} ++ ++/** ++ * This function reads the host registers and prints them ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_host_registers(dwc_otg_core_if_t *_core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINT("Host Global Registers\n"); ++ addr=&_core_if->host_if->host_global_regs->hcfg; ++ DWC_PRINT("HCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->host_global_regs->hfir; ++ DWC_PRINT("HFIR @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->host_global_regs->hfnum; ++ DWC_PRINT("HFNUM @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->host_global_regs->hptxsts; ++ DWC_PRINT("HPTXSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->host_global_regs->haint; ++ DWC_PRINT("HAINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->host_global_regs->haintmsk; ++ DWC_PRINT("HAINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=_core_if->host_if->hprt0; ++ DWC_PRINT("HPRT0 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ for (i=0; i<_core_if->core_params->host_channels; i++) { ++ DWC_PRINT("Host Channel %d Specific Registers\n", i); ++ addr=&_core_if->host_if->hc_regs[i]->hcchar; ++ DWC_PRINT("HCCHAR @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->hc_regs[i]->hcsplt; ++ DWC_PRINT("HCSPLT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->hc_regs[i]->hcint; ++ DWC_PRINT("HCINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->hc_regs[i]->hcintmsk; ++ DWC_PRINT("HCINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->hc_regs[i]->hctsiz; ++ DWC_PRINT("HCTSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->host_if->hc_regs[i]->hcdma; ++ DWC_PRINT("HCDMA @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ } ++ return; ++} ++ ++/** ++ * This function reads the core global registers and prints them ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_global_registers(dwc_otg_core_if_t *_core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINT("Core Global Registers\n"); ++ addr=&_core_if->core_global_regs->gotgctl; ++ DWC_PRINT("GOTGCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gotgint; ++ DWC_PRINT("GOTGINT @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gahbcfg; ++ DWC_PRINT("GAHBCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gusbcfg; ++ DWC_PRINT("GUSBCFG @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->grstctl; ++ DWC_PRINT("GRSTCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gintsts; ++ DWC_PRINT("GINTSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gintmsk; ++ DWC_PRINT("GINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->grxstsr; ++ DWC_PRINT("GRXSTSR @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ //addr=&_core_if->core_global_regs->grxstsp; ++ //DWC_PRINT("GRXSTSP @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->grxfsiz; ++ DWC_PRINT("GRXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gnptxfsiz; ++ DWC_PRINT("GNPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gnptxsts; ++ DWC_PRINT("GNPTXSTS @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gi2cctl; ++ DWC_PRINT("GI2CCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gpvndctl; ++ DWC_PRINT("GPVNDCTL @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->ggpio; ++ DWC_PRINT("GGPIO @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->guid; ++ DWC_PRINT("GUID @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->gsnpsid; ++ DWC_PRINT("GSNPSID @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg1; ++ DWC_PRINT("GHWCFG1 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg2; ++ DWC_PRINT("GHWCFG2 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg3; ++ DWC_PRINT("GHWCFG3 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg4; ++ DWC_PRINT("GHWCFG4 @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ addr=&_core_if->core_global_regs->hptxfsiz; ++ DWC_PRINT("HPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,dwc_read_reg32(addr)); ++ ++ for (i=0; i<_core_if->hwcfg4.b.num_dev_perio_in_ep; i++) { ++ addr=&_core_if->core_global_regs->dptxfsiz_dieptxf[i]; ++ DWC_PRINT("DPTXFSIZ[%d] @0x%08X : 0x%08X\n",i,(uint32_t)addr,dwc_read_reg32(addr)); ++ } ++ ++} ++#endif ++ ++/** ++ * Flush a Tx FIFO. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _num Tx FIFO to flush. ++ */ ++extern void dwc_otg_flush_tx_fifo( dwc_otg_core_if_t *_core_if, ++ const int _num ) ++{ ++ dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset = { .d32 = 0}; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL|DBG_PCDV), "Flush Tx FIFO %d\n", _num); ++ ++ greset.b.txfflsh = 1; ++ greset.b.txfnum = _num; ++ dwc_write_reg32( &global_regs->grstctl, greset.d32 ); ++ ++ do { ++ greset.d32 = dwc_read_reg32( &global_regs->grstctl); ++ if (++count > 10000){ ++ DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", ++ __func__, greset.d32, ++ dwc_read_reg32( &global_regs->gnptxsts)); ++ break; ++ } ++ ++ udelay(1); ++ } while (greset.b.txfflsh == 1); ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++/** ++ * Flush Rx FIFO. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++extern void dwc_otg_flush_rx_fifo( dwc_otg_core_if_t *_core_if ) ++{ ++ dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset = { .d32 = 0}; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL|DBG_PCDV), "%s\n", __func__); ++ /* ++ * ++ */ ++ greset.b.rxfflsh = 1; ++ dwc_write_reg32( &global_regs->grstctl, greset.d32 ); ++ ++ do { ++ greset.d32 = dwc_read_reg32( &global_regs->grstctl); ++ if (++count > 10000){ ++ DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ break; ++ } ++ } while (greset.b.rxfflsh == 1); ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++/** ++ * Do core a soft reset of the core. Be careful with this because it ++ * resets all the internal state machines of the core. ++ */ ++ ++void dwc_otg_core_reset(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset = { .d32 = 0}; ++ int count = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s\n", __func__); ++ /* Wait for AHB master IDLE state. */ ++ do { ++ UDELAY(10); ++ greset.d32 = dwc_read_reg32( &global_regs->grstctl); ++ if (++count > 100000){ ++ DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x %x\n", __func__, ++ greset.d32, greset.b.ahbidle); ++ return; ++ } ++ } while (greset.b.ahbidle == 0); ++ ++// winder add. ++#if 1 ++ /* Note: Actually, I don't exactly why we need to put delay here. */ ++ MDELAY(100); ++#endif ++ /* Core Soft Reset */ ++ count = 0; ++ greset.b.csftrst = 1; ++ dwc_write_reg32( &global_regs->grstctl, greset.d32 ); ++// winder add. ++#if 1 ++ /* Note: Actually, I don't exactly why we need to put delay here. */ ++ MDELAY(100); ++#endif ++ do { ++ greset.d32 = dwc_read_reg32( &global_regs->grstctl); ++ if (++count > 10000){ ++ DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ break; ++ } ++ udelay(1); ++ } while (greset.b.csftrst == 1); ++ /* Wait for 3 PHY Clocks*/ ++ //DWC_PRINT("100ms\n"); ++ MDELAY(100); ++} ++ ++ ++ ++/** ++ * Register HCD callbacks. The callbacks are used to start and stop ++ * the HCD for interrupt processing. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _cb the HCD callback structure. ++ * @param _p pointer to be passed to callback function (usb_hcd*). ++ */ ++extern void dwc_otg_cil_register_hcd_callbacks( dwc_otg_core_if_t *_core_if, ++ dwc_otg_cil_callbacks_t *_cb, ++ void *_p) ++{ ++ _core_if->hcd_cb = _cb; ++ _cb->p = _p; ++} ++ ++/** ++ * Register PCD callbacks. The callbacks are used to start and stop ++ * the PCD for interrupt processing. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ * @param _cb the PCD callback structure. ++ * @param _p pointer to be passed to callback function (pcd*). ++ */ ++extern void dwc_otg_cil_register_pcd_callbacks( dwc_otg_core_if_t *_core_if, ++ dwc_otg_cil_callbacks_t *_cb, ++ void *_p) ++{ ++ _core_if->pcd_cb = _cb; ++ _cb->p = _p; ++} ++ +diff --git a/drivers/usb/dwc_otg/dwc_otg_cil.h b/drivers/usb/dwc_otg/dwc_otg_cil.h +new file mode 100644 +index 0000000..bbb9516 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil.h +@@ -0,0 +1,911 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_cil.h $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 631780 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_CIL_H__) ++#define __DWC_CIL_H__ ++ ++#include "dwc_otg_plat.h" ++ ++#include "dwc_otg_regs.h" ++#ifdef DEBUG ++#include "linux/timer.h" ++#endif ++ ++/* the OTG capabilities. */ ++#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0 ++#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1 ++#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 ++/* the maximum speed of operation in host and device mode. */ ++#define DWC_SPEED_PARAM_HIGH 0 ++#define DWC_SPEED_PARAM_FULL 1 ++/* the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. */ ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 ++/* the type of PHY interface to use. */ ++#define DWC_PHY_TYPE_PARAM_FS 0 ++#define DWC_PHY_TYPE_PARAM_UTMI 1 ++#define DWC_PHY_TYPE_PARAM_ULPI 2 ++/* whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. */ ++#define DWC_PHY_ULPI_INTERNAL_VBUS 0 ++#define DWC_PHY_ULPI_EXTERNAL_VBUS 1 ++/* EP type. */ ++ ++/** ++ * @file ++ * This file contains the interface to the Core Interface Layer. ++ */ ++ ++/** ++ * The dwc_ep structure represents the state of a single ++ * endpoint when acting in device mode. It contains the data items ++ * needed for an endpoint to be activated and transfer packets. ++ */ ++typedef struct dwc_ep { ++ /** EP number used for register address lookup */ ++ uint8_t num; ++ /** EP direction 0 = OUT */ ++ unsigned is_in : 1; ++ /** EP active. */ ++ unsigned active : 1; ++ ++ /** Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic Tx FIFO ++ If dedicated Tx FIFOs are enabled for all IN Eps - Tx FIFO # FOR IN EPs*/ ++ unsigned tx_fifo_num : 4; ++ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */ ++ unsigned type : 2; ++#define DWC_OTG_EP_TYPE_CONTROL 0 ++#define DWC_OTG_EP_TYPE_ISOC 1 ++#define DWC_OTG_EP_TYPE_BULK 2 ++#define DWC_OTG_EP_TYPE_INTR 3 ++ ++ /** DATA start PID for INTR and BULK EP */ ++ unsigned data_pid_start : 1; ++ /** Frame (even/odd) for ISOC EP */ ++ unsigned even_odd_frame : 1; ++ /** Max Packet bytes */ ++ unsigned maxpacket : 11; ++ ++ /** @name Transfer state */ ++ /** @{ */ ++ ++ /** ++ * Pointer to the beginning of the transfer buffer -- do not modify ++ * during transfer. ++ */ ++ ++ uint32_t dma_addr; ++ ++ uint8_t *start_xfer_buff; ++ /** pointer to the transfer buffer */ ++ uint8_t *xfer_buff; ++ /** Number of bytes to transfer */ ++ unsigned xfer_len : 19; ++ /** Number of bytes transferred. */ ++ unsigned xfer_count : 19; ++ /** Sent ZLP */ ++ unsigned sent_zlp : 1; ++ /** Total len for control transfer */ ++ unsigned total_len : 19; ++ ++ /** stall clear flag */ ++ unsigned stall_clear_flag : 1; ++ ++ /** @} */ ++} dwc_ep_t; ++ ++/* ++ * Reasons for halting a host channel. ++ */ ++typedef enum dwc_otg_halt_status { ++ DWC_OTG_HC_XFER_NO_HALT_STATUS, ++ DWC_OTG_HC_XFER_COMPLETE, ++ DWC_OTG_HC_XFER_URB_COMPLETE, ++ DWC_OTG_HC_XFER_ACK, ++ DWC_OTG_HC_XFER_NAK, ++ DWC_OTG_HC_XFER_NYET, ++ DWC_OTG_HC_XFER_STALL, ++ DWC_OTG_HC_XFER_XACT_ERR, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN, ++ DWC_OTG_HC_XFER_BABBLE_ERR, ++ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR, ++ DWC_OTG_HC_XFER_AHB_ERR, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE, ++ DWC_OTG_HC_XFER_URB_DEQUEUE ++} dwc_otg_halt_status_e; ++ ++/** ++ * Host channel descriptor. This structure represents the state of a single ++ * host channel when acting in host mode. It contains the data items needed to ++ * transfer packets to an endpoint via a host channel. ++ */ ++typedef struct dwc_hc { ++ /** Host channel number used for register address lookup */ ++ uint8_t hc_num; ++ ++ /** Device to access */ ++ unsigned dev_addr : 7; ++ ++ /** EP to access */ ++ unsigned ep_num : 4; ++ ++ /** EP direction. 0: OUT, 1: IN */ ++ unsigned ep_is_in : 1; ++ ++ /** ++ * EP speed. ++ * One of the following values: ++ * - DWC_OTG_EP_SPEED_LOW ++ * - DWC_OTG_EP_SPEED_FULL ++ * - DWC_OTG_EP_SPEED_HIGH ++ */ ++ unsigned speed : 2; ++#define DWC_OTG_EP_SPEED_LOW 0 ++#define DWC_OTG_EP_SPEED_FULL 1 ++#define DWC_OTG_EP_SPEED_HIGH 2 ++ ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - DWC_OTG_EP_TYPE_CONTROL: 0 ++ * - DWC_OTG_EP_TYPE_ISOC: 1 ++ * - DWC_OTG_EP_TYPE_BULK: 2 ++ * - DWC_OTG_EP_TYPE_INTR: 3 ++ */ ++ unsigned ep_type : 2; ++ ++ /** Max packet size in bytes */ ++ unsigned max_packet : 11; ++ ++ /** ++ * PID for initial transaction. ++ * 0: DATA0,
++ * 1: DATA2,
++ * 2: DATA1,
++ * 3: MDATA (non-Control EP), ++ * SETUP (Control EP) ++ */ ++ unsigned data_pid_start : 2; ++#define DWC_OTG_HC_PID_DATA0 0 ++#define DWC_OTG_HC_PID_DATA2 1 ++#define DWC_OTG_HC_PID_DATA1 2 ++#define DWC_OTG_HC_PID_MDATA 3 ++#define DWC_OTG_HC_PID_SETUP 3 ++ ++ /** Number of periodic transactions per (micro)frame */ ++ unsigned multi_count: 2; ++ ++ /** @name Transfer State */ ++ /** @{ */ ++ ++ /** Pointer to the current transfer buffer position. */ ++ uint8_t *xfer_buff; ++ /** Total number of bytes to transfer. */ ++ uint32_t xfer_len; ++ /** Number of bytes transferred so far. */ ++ uint32_t xfer_count; ++ /** Packet count at start of transfer.*/ ++ uint16_t start_pkt_count; ++ ++ /** ++ * Flag to indicate whether the transfer has been started. Set to 1 if ++ * it has been started, 0 otherwise. ++ */ ++ uint8_t xfer_started; ++ ++ /** ++ * Set to 1 to indicate that a PING request should be issued on this ++ * channel. If 0, process normally. ++ */ ++ uint8_t do_ping; ++ ++ /** ++ * Set to 1 to indicate that the error count for this transaction is ++ * non-zero. Set to 0 if the error count is 0. ++ */ ++ uint8_t error_state; ++ ++ /** ++ * Set to 1 to indicate that this channel should be halted the next ++ * time a request is queued for the channel. This is necessary in ++ * slave mode if no request queue space is available when an attempt ++ * is made to halt the channel. ++ */ ++ uint8_t halt_on_queue; ++ ++ /** ++ * Set to 1 if the host channel has been halted, but the core is not ++ * finished flushing queued requests. Otherwise 0. ++ */ ++ uint8_t halt_pending; ++ ++ /** ++ * Reason for halting the host channel. ++ */ ++ dwc_otg_halt_status_e halt_status; ++ ++ /* ++ * Split settings for the host channel ++ */ ++ uint8_t do_split; /**< Enable split for the channel */ ++ uint8_t complete_split; /**< Enable complete split */ ++ uint8_t hub_addr; /**< Address of high speed hub */ ++ ++ uint8_t port_addr; /**< Port of the low/full speed device */ ++ /** Split transaction position ++ * One of the following values: ++ * - DWC_HCSPLIT_XACTPOS_MID ++ * - DWC_HCSPLIT_XACTPOS_BEGIN ++ * - DWC_HCSPLIT_XACTPOS_END ++ * - DWC_HCSPLIT_XACTPOS_ALL */ ++ uint8_t xact_pos; ++ ++ /** Set when the host channel does a short read. */ ++ uint8_t short_read; ++ ++ /** ++ * Number of requests issued for this channel since it was assigned to ++ * the current transfer (not counting PINGs). ++ */ ++ uint8_t requests; ++ ++ /** ++ * Queue Head for the transfer being processed by this channel. ++ */ ++ struct dwc_otg_qh *qh; ++ ++ /** @} */ ++ ++ /** Entry in list of host channels. */ ++ struct list_head hc_list_entry; ++} dwc_hc_t; ++ ++/** ++ * The following parameters may be specified when starting the module. These ++ * parameters define how the DWC_otg controller should be configured. ++ * Parameter values are passed to the CIL initialization function ++ * dwc_otg_cil_init. ++ */ ++ ++typedef struct dwc_otg_core_params ++{ ++ int32_t opt; ++//#define dwc_param_opt_default 1 ++ /** ++ * Specifies the OTG capabilities. The driver will automatically ++ * detect the value for this parameter if none is specified. ++ * 0 - HNP and SRP capable (default) ++ * 1 - SRP Only capable ++ * 2 - No HNP/SRP capable ++ */ ++ int32_t otg_cap; ++#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0 ++#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1 ++#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 ++//#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ++ /** ++ * Specifies whether to use slave or DMA mode for accessing the data ++ * FIFOs. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - Slave ++ * 1 - DMA (default, if available) ++ */ ++ int32_t dma_enable; ++//#define dwc_param_dma_enable_default 1 ++ /** The DMA Burst size (applicable only for External DMA ++ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ */ ++ int32_t dma_burst_size; /* Translate this to GAHBCFG values */ ++//#define dwc_param_dma_burst_size_default 32 ++ /** ++ * Specifies the maximum speed of operation in host and device mode. ++ * The actual speed depends on the speed of the attached device and ++ * the value of phy_type. The actual speed depends on the speed of the ++ * attached device. ++ * 0 - High Speed (default) ++ * 1 - Full Speed ++ */ ++ int32_t speed; ++//#define dwc_param_speed_default 0 ++#define DWC_SPEED_PARAM_HIGH 0 ++#define DWC_SPEED_PARAM_FULL 1 ++ ++ /** Specifies whether low power mode is supported when attached ++ * to a Full Speed or Low Speed device in host mode. ++ * 0 - Don't support low power mode (default) ++ * 1 - Support low power mode ++ */ ++ int32_t host_support_fs_ls_low_power; ++//#define dwc_param_host_support_fs_ls_low_power_default 0 ++ /** Specifies the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. This parameter is applicable only if ++ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS ++ * then defaults to 6 MHZ otherwise 48 MHZ. ++ * ++ * 0 - 48 MHz ++ * 1 - 6 MHz ++ */ ++ int32_t host_ls_low_power_phy_clk; ++//#define dwc_param_host_ls_low_power_phy_clk_default 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 ++ /** ++ * 0 - Use cC FIFO size parameters ++ * 1 - Allow dynamic FIFO sizing (default) ++ */ ++ int32_t enable_dynamic_fifo; ++//#define dwc_param_enable_dynamic_fifo_default 1 ++ /** Total number of 4-byte words in the data FIFO memory. This ++ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ * Tx FIFOs. ++ * 32 to 32768 (default 8192) ++ * Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ */ ++ int32_t data_fifo_size; ++//#define dwc_param_data_fifo_size_default 8192 ++ /** Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1064) ++ */ ++ int32_t dev_rx_fifo_size; ++//#define dwc_param_dev_rx_fifo_size_default 1064 ++ /** Number of 4-byte words in the non-periodic Tx FIFO in device mode ++ * when dynamic FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t dev_nperio_tx_fifo_size; ++//#define dwc_param_dev_nperio_tx_fifo_size_default 1024 ++ /** Number of 4-byte words in each of the periodic Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++//#define dwc_param_dev_perio_tx_fifo_size_default 256 ++ /** Number of 4-byte words in the Rx FIFO in host mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_rx_fifo_size; ++//#define dwc_param_host_rx_fifo_size_default 1024 ++ /** Number of 4-byte words in the non-periodic Tx FIFO in host mode ++ * when Dynamic FIFO sizing is enabled in the core. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_nperio_tx_fifo_size; ++//#define dwc_param_host_nperio_tx_fifo_size_default 1024 ++ /** Number of 4-byte words in the host periodic Tx FIFO when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_perio_tx_fifo_size; ++//#define dwc_param_host_perio_tx_fifo_size_default 1024 ++ /** The maximum transfer size supported in bytes. ++ * 2047 to 65,535 (default 65,535) ++ */ ++ int32_t max_transfer_size; ++//#define dwc_param_max_transfer_size_default 65535 ++ /** The maximum number of packets in a transfer. ++ * 15 to 511 (default 511) ++ */ ++ int32_t max_packet_count; ++//#define dwc_param_max_packet_count_default 511 ++ /** The number of host channel registers to use. ++ * 1 to 16 (default 12) ++ * Note: The FPGA configuration supports a maximum of 12 host channels. ++ */ ++ int32_t host_channels; ++//#define dwc_param_host_channels_default 12 ++ /** The number of endpoints in addition to EP0 available for device ++ * mode operations. ++ * 1 to 15 (default 6 IN and OUT) ++ * Note: The FPGA configuration supports a maximum of 6 IN and OUT ++ * endpoints in addition to EP0. ++ */ ++ int32_t dev_endpoints; ++//#define dwc_param_dev_endpoints_default 6 ++ /** ++ * Specifies the type of PHY interface to use. By default, the driver ++ * will automatically detect the phy_type. ++ * ++ * 0 - Full Speed PHY ++ * 1 - UTMI+ (default) ++ * 2 - ULPI ++ */ ++ int32_t phy_type; ++#define DWC_PHY_TYPE_PARAM_FS 0 ++#define DWC_PHY_TYPE_PARAM_UTMI 1 ++#define DWC_PHY_TYPE_PARAM_ULPI 2 ++//#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI ++ /** ++ * Specifies the UTMI+ Data Width. This parameter is ++ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI ++ * PHY_TYPE, this parameter indicates the data width between ++ * the MAC and the ULPI Wrapper.) Also, this parameter is ++ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set ++ * to "8 and 16 bits", meaning that the core has been ++ * configured to work at either data path width. ++ * ++ * 8 or 16 bits (default 16) ++ */ ++ int32_t phy_utmi_width; ++//#define dwc_param_phy_utmi_width_default 16 ++ /** ++ * Specifies whether the ULPI operates at double or single ++ * data rate. This parameter is only applicable if PHY_TYPE is ++ * ULPI. ++ * ++ * 0 - single data rate ULPI interface with 8 bit wide data ++ * bus (default) ++ * 1 - double data rate ULPI interface with 4 bit wide data ++ * bus ++ */ ++ int32_t phy_ulpi_ddr; ++//#define dwc_param_phy_ulpi_ddr_default 0 ++ /** ++ * Specifies whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. ++ */ ++ int32_t phy_ulpi_ext_vbus; ++#define DWC_PHY_ULPI_INTERNAL_VBUS 0 ++#define DWC_PHY_ULPI_EXTERNAL_VBUS 1 ++//#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS ++ /** ++ * Specifies whether to use the I2Cinterface for full speed PHY. This ++ * parameter is only applicable if PHY_TYPE is FS. ++ * 0 - No (default) ++ * 1 - Yes ++ */ ++ int32_t i2c_enable; ++//#define dwc_param_i2c_enable_default 0 ++ ++ int32_t ulpi_fs_ls; ++//#define dwc_param_ulpi_fs_ls_default 0 ++ ++ int32_t ts_dline; ++//#define dwc_param_ts_dline_default 0 ++ ++ /** ++ * Specifies whether dedicated transmit FIFOs are ++ * enabled for non periodic IN endpoints in device mode ++ * 0 - No ++ * 1 - Yes ++ */ ++ int32_t en_multiple_tx_fifo; ++#define dwc_param_en_multiple_tx_fifo_default 1 ++ ++ /** Number of 4-byte words in each of the Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS]; ++#define dwc_param_dev_tx_fifo_size_default 256 ++ ++ /** Thresholding enable flag- ++ * bit 0 - enable non-ISO Tx thresholding ++ * bit 1 - enable ISO Tx thresholding ++ * bit 2 - enable Rx thresholding ++ */ ++ uint32_t thr_ctl; ++#define dwc_param_thr_ctl_default 0 ++ ++ /** Thresholding length for Tx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t tx_thr_length; ++#define dwc_param_tx_thr_length_default 64 ++ ++ /** Thresholding length for Rx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t rx_thr_length; ++#define dwc_param_rx_thr_length_default 64 ++} dwc_otg_core_params_t; ++ ++#ifdef DEBUG ++struct dwc_otg_core_if; ++typedef struct hc_xfer_info ++{ ++ struct dwc_otg_core_if *core_if; ++ dwc_hc_t *hc; ++} hc_xfer_info_t; ++#endif ++ ++/** ++ * The dwc_otg_core_if structure contains information needed to manage ++ * the DWC_otg controller acting in either host or device mode. It ++ * represents the programming view of the controller as a whole. ++ */ ++typedef struct dwc_otg_core_if ++{ ++ /** Parameters that define how the core should be configured.*/ ++ dwc_otg_core_params_t *core_params; ++ ++ /** Core Global registers starting at offset 000h. */ ++ dwc_otg_core_global_regs_t *core_global_regs; ++ ++ /** Device-specific information */ ++ dwc_otg_dev_if_t *dev_if; ++ /** Host-specific information */ ++ dwc_otg_host_if_t *host_if; ++ ++ /* ++ * Set to 1 if the core PHY interface bits in USBCFG have been ++ * initialized. ++ */ ++ uint8_t phy_init_done; ++ ++ /* ++ * SRP Success flag, set by srp success interrupt in FS I2C mode ++ */ ++ uint8_t srp_success; ++ uint8_t srp_timer_started; ++ ++ /* Common configuration information */ ++ /** Power and Clock Gating Control Register */ ++ volatile uint32_t *pcgcctl; ++#define DWC_OTG_PCGCCTL_OFFSET 0xE00 ++ ++ /** Push/pop addresses for endpoints or host channels.*/ ++ uint32_t *data_fifo[MAX_EPS_CHANNELS]; ++#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 ++#define DWC_OTG_DATA_FIFO_SIZE 0x1000 ++ ++ /** Total RAM for FIFOs (Bytes) */ ++ uint16_t total_fifo_size; ++ /** Size of Rx FIFO (Bytes) */ ++ uint16_t rx_fifo_size; ++ /** Size of Non-periodic Tx FIFO (Bytes) */ ++ uint16_t nperio_tx_fifo_size; ++ ++ /** 1 if DMA is enabled, 0 otherwise. */ ++ uint8_t dma_enable; ++ ++ /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */ ++ uint8_t en_multiple_tx_fifo; ++ ++ /** Set to 1 if multiple packets of a high-bandwidth transfer is in ++ * process of being queued */ ++ uint8_t queuing_high_bandwidth; ++ ++ /** Hardware Configuration -- stored here for convenience.*/ ++ hwcfg1_data_t hwcfg1; ++ hwcfg2_data_t hwcfg2; ++ hwcfg3_data_t hwcfg3; ++ hwcfg4_data_t hwcfg4; ++ ++ /** The operational State, during transations ++ * (a_host>>a_peripherial and b_device=>b_host) this may not ++ * match the core but allows the software to determine ++ * transitions. ++ */ ++ uint8_t op_state; ++ ++ /** ++ * Set to 1 if the HCD needs to be restarted on a session request ++ * interrupt. This is required if no connector ID status change has ++ * occurred since the HCD was last disconnected. ++ */ ++ uint8_t restart_hcd_on_session_req; ++ ++ /** HCD callbacks */ ++ /** A-Device is a_host */ ++#define A_HOST (1) ++ /** A-Device is a_suspend */ ++#define A_SUSPEND (2) ++ /** A-Device is a_peripherial */ ++#define A_PERIPHERAL (3) ++ /** B-Device is operating as a Peripheral. */ ++#define B_PERIPHERAL (4) ++ /** B-Device is operating as a Host. */ ++#define B_HOST (5) ++ ++ /** HCD callbacks */ ++ struct dwc_otg_cil_callbacks *hcd_cb; ++ /** PCD callbacks */ ++ struct dwc_otg_cil_callbacks *pcd_cb; ++ ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t p_tx_msk; ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t tx_msk; ++ ++#ifdef DEBUG ++ uint32_t start_hcchar_val[MAX_EPS_CHANNELS]; ++ ++ hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS]; ++ struct timer_list hc_xfer_timer[MAX_EPS_CHANNELS]; ++ ++#if 1 // winder ++ uint32_t hfnum_7_samples; ++ uint32_t hfnum_7_frrem_accum; ++ uint32_t hfnum_0_samples; ++ uint32_t hfnum_0_frrem_accum; ++ uint32_t hfnum_other_samples; ++ uint32_t hfnum_other_frrem_accum; ++#else ++ uint32_t hfnum_7_samples; ++ uint64_t hfnum_7_frrem_accum; ++ uint32_t hfnum_0_samples; ++ uint64_t hfnum_0_frrem_accum; ++ uint32_t hfnum_other_samples; ++ uint64_t hfnum_other_frrem_accum; ++#endif ++ resource_size_t phys_addr; /* Added to support PLB DMA : phys-virt mapping */ ++#endif ++ ++} dwc_otg_core_if_t; ++ ++/* ++ * The following functions support initialization of the CIL driver component ++ * and the DWC_otg controller. ++ */ ++extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t *_reg_base_addr, ++ dwc_otg_core_params_t *_core_params); ++extern void dwc_otg_cil_remove(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_core_init(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_core_host_init(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_core_dev_init(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_enable_global_interrupts( dwc_otg_core_if_t *_core_if ); ++extern void dwc_otg_disable_global_interrupts( dwc_otg_core_if_t *_core_if ); ++ ++/** @name Device CIL Functions ++ * The following functions support managing the DWC_otg controller in device ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_wakeup(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_read_setup_packet (dwc_otg_core_if_t *_core_if, uint32_t *_dest); ++extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_ep0_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_activate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep, int _dma); ++extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t *_core_if, dwc_ep_t *_ep); ++extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t *_core_if); ++/**@}*/ ++ ++/** @name Host CIL Functions ++ * The following functions support managing the DWC_otg controller in host ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_hc_init(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_halt(dwc_otg_core_if_t *_core_if, ++ dwc_hc_t *_hc, ++ dwc_otg_halt_status_e _halt_status); ++extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc); ++extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t *_core_if); ++extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t *_core_if); ++ ++/** ++ * This function Reads HPRT0 in preparation to modify. It keeps the ++ * WC bits 0 so that if they are read as 1, they won't clear when you ++ * write it back ++ */ ++static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t *_core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0); ++ hprt0.b.prtena = 0; ++ hprt0.b.prtconndet = 0; ++ hprt0.b.prtenchng = 0; ++ hprt0.b.prtovrcurrchng = 0; ++ return hprt0.d32; ++} ++ ++extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t *_core_if); ++/**@}*/ ++ ++/** @name Common CIL Functions ++ * The following functions support managing the DWC_otg controller in either ++ * device or host mode. ++ */ ++/**@{*/ ++ ++extern void dwc_otg_read_packet(dwc_otg_core_if_t *core_if, ++ uint8_t *dest, ++ uint16_t bytes); ++ ++extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t *_core_if); ++ ++extern void dwc_otg_flush_tx_fifo( dwc_otg_core_if_t *_core_if, ++ const int _num ); ++extern void dwc_otg_flush_rx_fifo( dwc_otg_core_if_t *_core_if ); ++extern void dwc_otg_core_reset( dwc_otg_core_if_t *_core_if ); ++ ++#define NP_TXFIFO_EMPTY -1 ++#define MAX_NP_TXREQUEST_Q_SLOTS 8 ++/** ++ * This function returns the endpoint number of the request at ++ * the top of non-periodic TX FIFO, or -1 if the request FIFO is ++ * empty. ++ */ ++static inline int dwc_otg_top_nptxfifo_epnum(dwc_otg_core_if_t *_core_if) { ++ gnptxsts_data_t txstatus = {.d32 = 0}; ++ ++ txstatus.d32 = dwc_read_reg32(&_core_if->core_global_regs->gnptxsts); ++ return (txstatus.b.nptxqspcavail == MAX_NP_TXREQUEST_Q_SLOTS ? ++ -1 : txstatus.b.nptxqtop_chnep); ++} ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t *_core_if) { ++ return (dwc_read_reg32(&_core_if->core_global_regs->gintsts) & ++ dwc_read_reg32(&_core_if->core_global_regs->gintmsk)); ++} ++ ++/** ++ * This function returns the OTG Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_otg_intr (dwc_otg_core_if_t *_core_if) { ++ return (dwc_read_reg32 (&_core_if->core_global_regs->gotgint)); ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the IN endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t *_core_if) { ++ uint32_t v; ++ v = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->daint) & ++ dwc_read_reg32(&_core_if->dev_if->dev_global_regs->daintmsk); ++ return (v & 0xffff); ++ ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the OUT endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t *_core_if) { ++ uint32_t v; ++ v = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->daint) & ++ dwc_read_reg32(&_core_if->dev_if->dev_global_regs->daintmsk); ++ return ((v & 0xffff0000) >> 16); ++} ++ ++/** ++ * This function returns the Device IN EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t *_core_if, ++ dwc_ep_t *_ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ uint32_t v, msk, emp; ++ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk); ++ emp = dwc_read_reg32(&dev_if->dev_global_regs->dtknqr4_fifoemptymsk); ++ msk |= ((emp >> _ep->num) & 0x1) << 7; ++ v = dwc_read_reg32(&dev_if->in_ep_regs[_ep->num]->diepint) & msk; ++/* ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ uint32_t v; ++ v = dwc_read_reg32(&dev_if->in_ep_regs[_ep->num]->diepint) & ++ dwc_read_reg32(&dev_if->dev_global_regs->diepmsk); ++*/ ++ return v; ++} ++/** ++ * This function returns the Device OUT EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t *_core_if, ++ dwc_ep_t *_ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ uint32_t v; ++ v = dwc_read_reg32( &dev_if->out_ep_regs[_ep->num]->doepint) & ++ dwc_read_reg32(&dev_if->dev_global_regs->doepmsk); ++ return v; ++} ++ ++/** ++ * This function returns the Host All Channel Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_host_all_channels_intr (dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_read_reg32 (&_core_if->host_if->host_global_regs->haint)); ++} ++ ++static inline uint32_t dwc_otg_read_host_channel_intr (dwc_otg_core_if_t *_core_if, dwc_hc_t *_hc) ++{ ++ return (dwc_read_reg32 (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint)); ++} ++ ++ ++/** ++ * This function returns the mode of the operation, host or device. ++ * ++ * @return 0 - Device Mode, 1 - Host Mode ++ */ ++static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t *_core_if) { ++ return (dwc_read_reg32( &_core_if->core_global_regs->gintsts ) & 0x1); ++} ++ ++static inline uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_otg_mode(_core_if) != DWC_HOST_MODE); ++} ++static inline uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t *_core_if) ++{ ++ return (dwc_otg_mode(_core_if) == DWC_HOST_MODE); ++} ++ ++extern int32_t dwc_otg_handle_common_intr( dwc_otg_core_if_t *_core_if ); ++ ++ ++/**@}*/ ++ ++/** ++ * DWC_otg CIL callback structure. This structure allows the HCD and ++ * PCD to register functions used for starting and stopping the PCD ++ * and HCD for role change on for a DRD. ++ */ ++typedef struct dwc_otg_cil_callbacks ++{ ++ /** Start function for role change */ ++ int (*start) (void *_p); ++ /** Stop Function for role change */ ++ int (*stop) (void *_p); ++ /** Disconnect Function for role change */ ++ int (*disconnect) (void *_p); ++ /** Resume/Remote wakeup Function */ ++ int (*resume_wakeup) (void *_p); ++ /** Suspend function */ ++ int (*suspend) (void *_p); ++ /** Session Start (SRP) */ ++ int (*session_start) (void *_p); ++ /** Pointer passed to start() and stop() */ ++ void *p; ++} dwc_otg_cil_callbacks_t; ++ ++ ++ ++extern void dwc_otg_cil_register_pcd_callbacks( dwc_otg_core_if_t *_core_if, ++ dwc_otg_cil_callbacks_t *_cb, ++ void *_p); ++extern void dwc_otg_cil_register_hcd_callbacks( dwc_otg_core_if_t *_core_if, ++ dwc_otg_cil_callbacks_t *_cb, ++ void *_p); ++ ++ ++#endif +diff --git a/drivers/usb/dwc_otg/dwc_otg_cil_ifx.h b/drivers/usb/dwc_otg/dwc_otg_cil_ifx.h +new file mode 100644 +index 0000000..b0298ec +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil_ifx.h +@@ -0,0 +1,58 @@ ++/****************************************************************************** ++** ++** FILE NAME : dwc_otg_cil_ifx.h ++** PROJECT : Twinpass/Danube ++** MODULES : DWC OTG USB ++** ++** DATE : 07 Sep. 2007 ++** AUTHOR : Sung Winder ++** DESCRIPTION : Default param value. ++** COPYRIGHT : Copyright (c) 2007 ++** Infineon Technologies AG ++** 2F, No.2, Li-Hsin Rd., Hsinchu Science Park, ++** Hsin-chu City, 300 Taiwan. ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** ++** HISTORY ++** $Date $Author $Comment ++** 12 April 2007 Sung Winder Initiate Version ++*******************************************************************************/ ++#if !defined(__DWC_OTG_CIL_IFX_H__) ++#define __DWC_OTG_CIL_IFX_H__ ++ ++/* ================ Default param value ================== */ ++#define dwc_param_opt_default 1 ++#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE ++#define dwc_param_dma_enable_default 1 ++#define dwc_param_dma_burst_size_default 32 ++#define dwc_param_speed_default DWC_SPEED_PARAM_HIGH ++#define dwc_param_host_support_fs_ls_low_power_default 0 ++#define dwc_param_host_ls_low_power_phy_clk_default DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ ++#define dwc_param_enable_dynamic_fifo_default 1 ++#define dwc_param_data_fifo_size_default 2048 ++#define dwc_param_dev_rx_fifo_size_default 1024 ++#define dwc_param_dev_nperio_tx_fifo_size_default 1024 ++#define dwc_param_dev_perio_tx_fifo_size_default 768 ++#define dwc_param_host_rx_fifo_size_default 640 ++#define dwc_param_host_nperio_tx_fifo_size_default 640 ++#define dwc_param_host_perio_tx_fifo_size_default 768 ++#define dwc_param_max_transfer_size_default 65535 ++#define dwc_param_max_packet_count_default 511 ++#define dwc_param_host_channels_default 16 ++#define dwc_param_dev_endpoints_default 6 ++#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI ++#define dwc_param_phy_utmi_width_default 16 ++#define dwc_param_phy_ulpi_ddr_default 0 ++#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS ++#define dwc_param_i2c_enable_default 0 ++#define dwc_param_ulpi_fs_ls_default 0 ++#define dwc_param_ts_dline_default 0 ++ ++/* ======================================================= */ ++ ++#endif // __DWC_OTG_CIL_IFX_H__ ++ +diff --git a/drivers/usb/dwc_otg/dwc_otg_cil_intr.c b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c +new file mode 100644 +index 0000000..d469ab4 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_cil_intr.c +@@ -0,0 +1,708 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_cil_intr.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 553126 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * This file contains the Common Interrupt handlers. ++ */ ++#include "dwc_otg_plat.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++#ifdef DEBUG ++inline const char *op_state_str( dwc_otg_core_if_t *_core_if ) ++{ ++ return (_core_if->op_state==A_HOST?"a_host": ++ (_core_if->op_state==A_SUSPEND?"a_suspend": ++ (_core_if->op_state==A_PERIPHERAL?"a_peripheral": ++ (_core_if->op_state==B_PERIPHERAL?"b_peripheral": ++ (_core_if->op_state==B_HOST?"b_host": ++ "unknown"))))); ++} ++#endif ++ ++/** This function will log a debug message ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_mode_mismatch_intr (dwc_otg_core_if_t *_core_if) ++{ ++ gintsts_data_t gintsts; ++ DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n", ++ dwc_otg_mode(_core_if) ? "Host" : "Device"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.modemismatch = 1; ++ dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** Start the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_start( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->hcd_cb && _core_if->hcd_cb->start) { ++ _core_if->hcd_cb->start( _core_if->hcd_cb->p ); ++ } ++} ++/** Stop the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_stop( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->hcd_cb && _core_if->hcd_cb->stop) { ++ _core_if->hcd_cb->stop( _core_if->hcd_cb->p ); ++ } ++} ++/** Disconnect the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_disconnect( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->hcd_cb && _core_if->hcd_cb->disconnect) { ++ _core_if->hcd_cb->disconnect( _core_if->hcd_cb->p ); ++ } ++} ++/** Inform the HCD the a New Session has begun. Helper function for ++ * using the HCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_session_start( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->hcd_cb && _core_if->hcd_cb->session_start) { ++ _core_if->hcd_cb->session_start( _core_if->hcd_cb->p ); ++ } ++} ++ ++/** Start the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_start( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->pcd_cb && _core_if->pcd_cb->start ) { ++ _core_if->pcd_cb->start( _core_if->pcd_cb->p ); ++ } ++} ++/** Stop the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_stop( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->pcd_cb && _core_if->pcd_cb->stop ) { ++ _core_if->pcd_cb->stop( _core_if->pcd_cb->p ); ++ } ++} ++/** Suspend the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_suspend( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->pcd_cb && _core_if->pcd_cb->suspend ) { ++ _core_if->pcd_cb->suspend( _core_if->pcd_cb->p ); ++ } ++} ++/** Resume the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_resume( dwc_otg_core_if_t *_core_if ) ++{ ++ if (_core_if->pcd_cb && _core_if->pcd_cb->resume_wakeup ) { ++ _core_if->pcd_cb->resume_wakeup( _core_if->pcd_cb->p ); ++ } ++} ++ ++/** ++ * This function handles the OTG Interrupts. It reads the OTG ++ * Interrupt Register (GOTGINT) to determine what interrupt has ++ * occurred. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t *_core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = ++ _core_if->core_global_regs; ++ gotgint_data_t gotgint; ++ gotgctl_data_t gotgctl; ++ gintmsk_data_t gintmsk; ++ ++ gotgint.d32 = dwc_read_reg32( &global_regs->gotgint); ++ gotgctl.d32 = dwc_read_reg32( &global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32, ++ op_state_str(_core_if)); ++ //DWC_DEBUGPL(DBG_CIL, "gotgctl=%08x\n", gotgctl.d32 ); ++ ++ if (gotgint.b.sesenddet) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Session End Detected++ (%s)\n", ++ op_state_str(_core_if)); ++ gotgctl.d32 = dwc_read_reg32( &global_regs->gotgctl); ++ ++ if (_core_if->op_state == B_HOST) { ++ pcd_start( _core_if ); ++ _core_if->op_state = B_PERIPHERAL; ++ } else { ++ /* If not B_HOST and Device HNP still set. HNP ++ * Did not succeed!*/ ++ if (gotgctl.b.devhnpen) { ++ DWC_DEBUGPL(DBG_ANY, "Session End Detected\n"); ++ DWC_ERROR( "Device Not Connected/Responding!\n" ); ++ } ++ ++ /* If Session End Detected the B-Cable has ++ * been disconnected. */ ++ /* Reset PCD and Gadget driver to a ++ * clean state. */ ++ pcd_stop(_core_if); ++ } ++ gotgctl.d32 = 0; ++ gotgctl.b.devhnpen = 1; ++ dwc_modify_reg32( &global_regs->gotgctl, ++ gotgctl.d32, 0); ++ } ++ if (gotgint.b.sesreqsucstschng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Session Reqeust Success Status Change++\n"); ++ gotgctl.d32 = dwc_read_reg32( &global_regs->gotgctl); ++ if (gotgctl.b.sesreqscs) { ++ if ((_core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (_core_if->core_params->i2c_enable)) { ++ _core_if->srp_success = 1; ++ } ++ else { ++ pcd_resume( _core_if ); ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ dwc_modify_reg32( &global_regs->gotgctl, ++ gotgctl.d32, 0); ++ } ++ } ++ } ++ if (gotgint.b.hstnegsucstschng) { ++ /* Print statements during the HNP interrupt handling ++ * can cause it to fail.*/ ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ if (gotgctl.b.hstnegscs) { ++ if (dwc_otg_is_host_mode(_core_if) ) { ++ _core_if->op_state = B_HOST; ++ /* ++ * Need to disable SOF interrupt immediately. ++ * When switching from device to host, the PCD ++ * interrupt handler won't handle the ++ * interrupt if host mode is already set. The ++ * HCD interrupt handler won't get called if ++ * the HCD state is HALT. This means that the ++ * interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, ++ gintmsk.d32, 0); ++ pcd_stop(_core_if); ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ hcd_start( _core_if ); ++ _core_if->op_state = B_HOST; ++ } ++ } else { ++ gotgctl.d32 = 0; ++ gotgctl.b.hnpreq = 1; ++ gotgctl.b.devhnpen = 1; ++ dwc_modify_reg32( &global_regs->gotgctl, ++ gotgctl.d32, 0); ++ DWC_DEBUGPL( DBG_ANY, "HNP Failed\n"); ++ DWC_ERROR( "Device Not Connected/Responding\n" ); ++ } ++ } ++ if (gotgint.b.hstnegdet) { ++ /* The disconnect interrupt is set at the same time as ++ * Host Negotiation Detected. During the mode ++ * switch all interrupts are cleared so the disconnect ++ * interrupt handler will not get executed. ++ */ ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Host Negotiation Detected++ (%s)\n", ++ (dwc_otg_is_host_mode(_core_if)?"Host":"Device")); ++ if (dwc_otg_is_device_mode(_core_if)){ ++ DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n",_core_if->op_state); ++ hcd_disconnect( _core_if ); ++ pcd_start( _core_if ); ++ _core_if->op_state = A_PERIPHERAL; ++ } else { ++ /* ++ * Need to disable SOF interrupt immediately. When ++ * switching from device to host, the PCD interrupt ++ * handler won't handle the interrupt if host mode is ++ * already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that ++ * the interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, ++ gintmsk.d32, 0); ++ pcd_stop( _core_if ); ++ hcd_start( _core_if ); ++ _core_if->op_state = A_HOST; ++ } ++ } ++ if (gotgint.b.adevtoutchng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "A-Device Timeout Change++\n"); ++ } ++ if (gotgint.b.debdone) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Debounce Done++\n"); ++ } ++ ++ /* Clear GOTGINT */ ++ dwc_write_reg32 (&_core_if->core_global_regs->gotgint, gotgint.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function handles the Connector ID Status Change Interrupt. It ++ * reads the OTG Interrupt Register (GOTCTL) to determine whether this ++ * is a Device to Host Mode transition or a Host Mode to Device ++ * Transition. ++ * ++ * This only occurs when the cable is connected/removed from the PHY ++ * connector. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t *_core_if) ++{ ++ uint32_t count = 0; ++ ++ gintsts_data_t gintsts = { .d32 = 0 }; ++ gintmsk_data_t gintmsk = { .d32 = 0 }; ++ gotgctl_data_t gotgctl = { .d32 = 0 }; ++ ++ /* ++ * Need to disable SOF interrupt immediately. If switching from device ++ * to host, the PCD interrupt handler won't handle the interrupt if ++ * host mode is already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that the interrupt does ++ * not get handled and Linux complains loudly. ++ */ ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&_core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ ++ DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", ++ (dwc_otg_is_host_mode(_core_if)?"Host":"Device")); ++ gotgctl.d32 = dwc_read_reg32(&_core_if->core_global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); ++ ++ /* B-Device connector (Device Mode) */ ++ if (gotgctl.b.conidsts) { ++ /* Wait for switch to device mode. */ ++ while (!dwc_otg_is_device_mode(_core_if) ){ ++ DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(_core_if)?"Host":"Peripheral")); ++ MDELAY(100); ++ if (++count > 10000) *(uint32_t*)NULL=0; ++ } ++ _core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(_core_if); ++ dwc_otg_enable_global_interrupts(_core_if); ++ pcd_start( _core_if ); ++ } else { ++ /* A-Device connector (Host Mode) */ ++ while (!dwc_otg_is_host_mode(_core_if) ) { ++ DWC_PRINT("Waiting for Host Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(_core_if)?"Host":"Peripheral")); ++ MDELAY(100); ++ if (++count > 10000) *(uint32_t*)NULL=0; ++ } ++ _core_if->op_state = A_HOST; ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ dwc_otg_core_init(_core_if); ++ dwc_otg_enable_global_interrupts(_core_if); ++ hcd_start( _core_if ); ++ } ++ ++ /* Set flag and clear interrupt */ ++ gintsts.b.conidstschng = 1; ++ dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device is initiating the Session ++ * Request Protocol to request the host to turn on bus power so a new ++ * session can begin. The handler responds by turning on bus power. If ++ * the DWC_otg controller is in low power mode, the handler brings the ++ * controller out of low power mode before turning on bus power. ++ * ++ * @param _core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_session_req_intr( dwc_otg_core_if_t *_core_if ) ++{ ++#ifndef DWC_HOST_ONLY // winder ++ hprt0_data_t hprt0; ++#endif ++ gintsts_data_t gintsts; ++ ++#ifndef DWC_HOST_ONLY ++ DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); ++ ++ if (dwc_otg_is_device_mode(_core_if) ) { ++ DWC_PRINT("SRP: Device mode\n"); ++ } else { ++ DWC_PRINT("SRP: Host mode\n"); ++ ++ /* Turn on the port power bit. */ ++ hprt0.d32 = dwc_otg_read_hprt0( _core_if ); ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Start the Connection timer. So a message can be displayed ++ * if connect does not occur within 10 seconds. */ ++ hcd_session_start( _core_if ); ++ } ++#endif ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sessreqintr = 1; ++ dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the DWC_otg controller has detected a ++ * resume or remote wakeup sequence. If the DWC_otg controller is in ++ * low power mode, the handler must brings the controller out of low ++ * power mode. The controller automatically begins resume ++ * signaling. The handler schedules a time to stop resume signaling. ++ */ ++int32_t dwc_otg_handle_wakeup_detected_intr( dwc_otg_core_if_t *_core_if ) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); ++ ++ if (dwc_otg_is_device_mode(_core_if) ) { ++ dctl_data_t dctl = {.d32=0}; ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", ++ dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dsts)); ++#ifdef PARTIAL_POWER_DOWN ++ if (_core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32=0}; ++ ++ power.d32 = dwc_read_reg32( _core_if->pcgcctl ); ++ DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32); ++ ++ power.b.stoppclk = 0; ++ dwc_write_reg32( _core_if->pcgcctl, power.d32); ++ ++ power.b.pwrclmp = 0; ++ dwc_write_reg32( _core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 0; ++ dwc_write_reg32( _core_if->pcgcctl, power.d32); ++ } ++#endif ++ /* Clear the Remote Wakeup Signalling */ ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32( &_core_if->dev_if->dev_global_regs->dctl, ++ dctl.d32, 0 ); ++ ++ if (_core_if->pcd_cb && _core_if->pcd_cb->resume_wakeup) { ++ _core_if->pcd_cb->resume_wakeup( _core_if->pcd_cb->p ); ++ } ++ ++ } else { ++ /* ++ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms ++ * so that OPT tests pass with all PHYs). ++ */ ++ hprt0_data_t hprt0 = {.d32=0}; ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(_core_if->pcgcctl, pcgcctl.d32, 0); ++ UDELAY(10); ++ ++ /* Now wait for 70 ms. */ ++ hprt0.d32 = dwc_otg_read_hprt0( _core_if ); ++ DWC_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); ++ MDELAY(70); ++ hprt0.b.prtres = 0; /* Resume */ ++ dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); ++ DWC_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", dwc_read_reg32(_core_if->host_if->hprt0)); ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.wkupintr = 1; ++ dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device has been disconnected from ++ * the root port. ++ */ ++int32_t dwc_otg_handle_disconnect_intr( dwc_otg_core_if_t *_core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", ++ (dwc_otg_is_host_mode(_core_if)?"Host":"Device"), ++ op_state_str(_core_if)); ++ ++/** @todo Consolidate this if statement. */ ++#ifndef DWC_HOST_ONLY ++ if (_core_if->op_state == B_HOST) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ hcd_disconnect( _core_if ); ++ pcd_start( _core_if ); ++ _core_if->op_state = B_PERIPHERAL; ++ } else if (dwc_otg_is_device_mode(_core_if)) { ++ gotgctl_data_t gotgctl = { .d32 = 0 }; ++ gotgctl.d32 = dwc_read_reg32(&_core_if->core_global_regs->gotgctl); ++ if (gotgctl.b.hstsethnpen==1) { ++ /* Do nothing, if HNP in process the OTG ++ * interrupt "Host Negotiation Detected" ++ * interrupt will do the mode switch. ++ */ ++ } else if (gotgctl.b.devhnpen == 0) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ hcd_disconnect( _core_if ); ++ pcd_start( _core_if ); ++ _core_if->op_state = B_PERIPHERAL; ++ } else { ++ DWC_DEBUGPL(DBG_ANY,"!a_peripheral && !devhnpen\n"); ++ } ++ } else { ++ if (_core_if->op_state == A_HOST) { ++ /* A-Cable still connected but device disconnected. */ ++ hcd_disconnect( _core_if ); ++ } ++ } ++#endif ++/* Without OTG, we should use the disconnect function!? winder added.*/ ++#if 1 // NO OTG, so host only!! ++ hcd_disconnect( _core_if ); ++#endif ++ ++ gintsts.d32 = 0; ++ gintsts.b.disconnect = 1; ++ dwc_write_reg32 (&_core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++/** ++ * This interrupt indicates that SUSPEND state has been detected on ++ * the USB. ++ * ++ * For HNP the USB Suspend interrupt signals the change from ++ * "a_peripheral" to "a_host". ++ * ++ * When power management is enabled the core will be put in low power ++ * mode. ++ */ ++int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t *_core_if ) ++{ ++ dsts_data_t dsts; ++ gintsts_data_t gintsts; ++ ++ //805141:.removed DWC_DEBUGPL(DBG_ANY,"USB SUSPEND\n"); ++ ++ if (dwc_otg_is_device_mode( _core_if ) ) { ++ /* Check the Device status register to determine if the Suspend ++ * state is active. */ ++ dsts.d32 = dwc_read_reg32( &_core_if->dev_if->dev_global_regs->dsts); ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); ++ DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " ++ "HWCFG4.power Optimize=%d\n", ++ dsts.b.suspsts, _core_if->hwcfg4.b.power_optimiz); ++ ++ ++#ifdef PARTIAL_POWER_DOWN ++/** @todo Add a module parameter for power management. */ ++ ++ if (dsts.b.suspsts && _core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32=0}; ++ DWC_DEBUGPL(DBG_CIL, "suspend\n"); ++ ++ power.b.pwrclmp = 1; ++ dwc_write_reg32( _core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 1; ++ dwc_modify_reg32( _core_if->pcgcctl, 0, power.d32); ++ ++ power.b.stoppclk = 1; ++ dwc_modify_reg32( _core_if->pcgcctl, 0, power.d32); ++ ++ } else { ++ DWC_DEBUGPL(DBG_ANY,"disconnect?\n"); ++ } ++#endif ++ /* PCD callback for suspend. */ ++ pcd_suspend(_core_if); ++ } else { ++ if (_core_if->op_state == A_PERIPHERAL) { ++ DWC_DEBUGPL(DBG_ANY,"a_peripheral->a_host\n"); ++ /* Clear the a_peripheral flag, back to a_host. */ ++ pcd_stop( _core_if ); ++ hcd_start( _core_if ); ++ _core_if->op_state = A_HOST; ++ } ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ dwc_write_reg32( &_core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t *_core_if) ++{ ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ gintmsk_data_t gintmsk_common = {.d32=0}; ++ gintmsk_common.b.wkupintr = 1; ++ gintmsk_common.b.sessreqintr = 1; ++ gintmsk_common.b.conidstschng = 1; ++ gintmsk_common.b.otgintr = 1; ++ gintmsk_common.b.modemismatch = 1; ++ gintmsk_common.b.disconnect = 1; ++ gintmsk_common.b.usbsuspend = 1; ++ /** @todo: The port interrupt occurs while in device ++ * mode. Added code to CIL to clear the interrupt for now! ++ */ ++ gintmsk_common.b.portintr = 1; ++ ++ gintsts.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintsts); ++ gintmsk.d32 = dwc_read_reg32(&_core_if->core_global_regs->gintmsk); ++#ifdef DEBUG ++ /* if any common interrupts set */ ++ if (gintsts.d32 & gintmsk_common.d32) { ++ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", ++ gintsts.d32, gintmsk.d32); ++ } ++#endif ++ ++ return ((gintsts.d32 & gintmsk.d32 ) & gintmsk_common.d32); ++ ++} ++ ++/** ++ * Common interrupt handler. ++ * ++ * The common interrupts are those that occur in both Host and Device mode. ++ * This handler handles the following interrupts: ++ * - Mode Mismatch Interrupt ++ * - Disconnect Interrupt ++ * - OTG Interrupt ++ * - Connector ID Status Change Interrupt ++ * - Session Request Interrupt. ++ * - Resume / Remote Wakeup Detected Interrupt. ++ * ++ */ ++extern int32_t dwc_otg_handle_common_intr( dwc_otg_core_if_t *_core_if ) ++{ ++ int retval = 0; ++ gintsts_data_t gintsts; ++ ++ gintsts.d32 = dwc_otg_read_common_intr(_core_if); ++ ++ if (gintsts.b.modemismatch) { ++ retval |= dwc_otg_handle_mode_mismatch_intr( _core_if ); ++ } ++ if (gintsts.b.otgintr) { ++ retval |= dwc_otg_handle_otg_intr( _core_if ); ++ } ++ if (gintsts.b.conidstschng) { ++ retval |= dwc_otg_handle_conn_id_status_change_intr( _core_if ); ++ } ++ if (gintsts.b.disconnect) { ++ retval |= dwc_otg_handle_disconnect_intr( _core_if ); ++ } ++ if (gintsts.b.sessreqintr) { ++ retval |= dwc_otg_handle_session_req_intr( _core_if ); ++ } ++ if (gintsts.b.wkupintr) { ++ retval |= dwc_otg_handle_wakeup_detected_intr( _core_if ); ++ } ++ if (gintsts.b.usbsuspend) { ++ retval |= dwc_otg_handle_usb_suspend_intr( _core_if ); ++ } ++ if (gintsts.b.portintr && dwc_otg_is_device_mode(_core_if)) { ++ /* The port interrupt occurs while in device mode with HPRT0 ++ * Port Enable/Disable. ++ */ ++ gintsts.d32 = 0; ++ gintsts.b.portintr = 1; ++ dwc_write_reg32(&_core_if->core_global_regs->gintsts, ++ gintsts.d32); ++ retval |= 1; ++ ++ } ++ return retval; ++} +diff --git a/drivers/usb/dwc_otg/dwc_otg_driver.c b/drivers/usb/dwc_otg/dwc_otg_driver.c +new file mode 100644 +index 0000000..1b0daab +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_driver.c +@@ -0,0 +1,1274 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_driver.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 631780 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * The dwc_otg_driver module provides the initialization and cleanup entry ++ * points for the DWC_otg driver. This module will be dynamically installed ++ * after Linux is booted using the insmod command. When the module is ++ * installed, the dwc_otg_init function is called. When the module is ++ * removed (using rmmod), the dwc_otg_cleanup function is called. ++ * ++ * This module also defines a data structure for the dwc_otg_driver, which is ++ * used in conjunction with the standard ARM lm_device structure. These ++ * structures allow the OTG driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This ++ * has the benefit that Linux can expose attributes of the driver and device ++ * in its special sysfs file system. Users can then read or write files in ++ * this file system to perform diagnostics on the driver components or the ++ * device. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include /* permission constants */ ++#include ++#include ++ ++#include "dwc_otg_plat.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_cil_ifx.h" ++ ++// #include "dwc_otg_pcd.h" // device ++#include "dwc_otg_hcd.h" // host ++ ++#include "dwc_otg_ifx.h" // for Infineon platform specific. ++ ++#define DWC_DRIVER_VERSION "2.60a 22-NOV-2006" ++#define DWC_DRIVER_DESC "HS OTG USB Controller driver" ++ ++const char dwc_driver_name[] = "dwc_otg"; ++ ++static unsigned long dwc_iomem_base = IFX_USB_IOMEM_BASE; ++int dwc_irq = LTQ_USB_INT; ++//int dwc_irq = 54; ++//int dwc_irq = IFXMIPS_USB_OC_INT; ++ ++extern int ifx_usb_hc_init(unsigned long base_addr, int irq); ++extern void ifx_usb_hc_remove(void); ++ ++/*-------------------------------------------------------------------------*/ ++/* Encapsulate the module parameter settings */ ++ ++static dwc_otg_core_params_t dwc_otg_module_params = { ++ .opt = -1, ++ .otg_cap = -1, ++ .dma_enable = -1, ++ .dma_burst_size = -1, ++ .speed = -1, ++ .host_support_fs_ls_low_power = -1, ++ .host_ls_low_power_phy_clk = -1, ++ .enable_dynamic_fifo = -1, ++ .data_fifo_size = -1, ++ .dev_rx_fifo_size = -1, ++ .dev_nperio_tx_fifo_size = -1, ++ .dev_perio_tx_fifo_size = /* dev_perio_tx_fifo_size_1 */ {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, /* 15 */ ++ .host_rx_fifo_size = -1, ++ .host_nperio_tx_fifo_size = -1, ++ .host_perio_tx_fifo_size = -1, ++ .max_transfer_size = -1, ++ .max_packet_count = -1, ++ .host_channels = -1, ++ .dev_endpoints = -1, ++ .phy_type = -1, ++ .phy_utmi_width = -1, ++ .phy_ulpi_ddr = -1, ++ .phy_ulpi_ext_vbus = -1, ++ .i2c_enable = -1, ++ .ulpi_fs_ls = -1, ++ .ts_dline = -1, ++ .en_multiple_tx_fifo = -1, ++ .dev_tx_fifo_size = { /* dev_tx_fifo_size */ ++ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 ++ }, /* 15 */ ++ .thr_ctl = -1, ++ .tx_thr_length = -1, ++ .rx_thr_length = -1, ++}; ++ ++/** ++ * This function shows the Driver Version. ++ */ ++static ssize_t version_show(struct device_driver *dev, char *buf) ++{ ++ return snprintf(buf, sizeof(DWC_DRIVER_VERSION)+2,"%s\n", ++ DWC_DRIVER_VERSION); ++} ++static DRIVER_ATTR(version, S_IRUGO, version_show, NULL); ++ ++/** ++ * Global Debug Level Mask. ++ */ ++uint32_t g_dbg_lvl = 0xff; /* OFF */ ++ ++/** ++ * This function shows the driver Debug Level. ++ */ ++static ssize_t dbg_level_show(struct device_driver *_drv, char *_buf) ++{ ++ return sprintf(_buf, "0x%0x\n", g_dbg_lvl); ++} ++/** ++ * This function stores the driver Debug Level. ++ */ ++static ssize_t dbg_level_store(struct device_driver *_drv, const char *_buf, ++ size_t _count) ++{ ++ g_dbg_lvl = simple_strtoul(_buf, NULL, 16); ++ return _count; ++} ++static DRIVER_ATTR(debuglevel, S_IRUGO|S_IWUSR, dbg_level_show, dbg_level_store); ++ ++/** ++ * This function is called during module intialization to verify that ++ * the module parameters are in a valid state. ++ */ ++static int check_parameters(dwc_otg_core_if_t *core_if) ++{ ++ int i; ++ int retval = 0; ++ ++/* Checks if the parameter is outside of its valid range of values */ ++#define DWC_OTG_PARAM_TEST(_param_,_low_,_high_) \ ++ ((dwc_otg_module_params._param_ < (_low_)) || \ ++ (dwc_otg_module_params._param_ > (_high_))) ++ ++/* If the parameter has been set by the user, check that the parameter value is ++ * within the value range of values. If not, report a module error. */ ++#define DWC_OTG_PARAM_ERR(_param_,_low_,_high_,_string_) \ ++ do { \ ++ if (dwc_otg_module_params._param_ != -1) { \ ++ if (DWC_OTG_PARAM_TEST(_param_,(_low_),(_high_))) { \ ++ DWC_ERROR("`%d' invalid for parameter `%s'\n", \ ++ dwc_otg_module_params._param_, _string_); \ ++ dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \ ++ retval ++; \ ++ } \ ++ } \ ++ } while (0) ++ ++ DWC_OTG_PARAM_ERR(opt,0,1,"opt"); ++ DWC_OTG_PARAM_ERR(otg_cap,0,2,"otg_cap"); ++ DWC_OTG_PARAM_ERR(dma_enable,0,1,"dma_enable"); ++ DWC_OTG_PARAM_ERR(speed,0,1,"speed"); ++ DWC_OTG_PARAM_ERR(host_support_fs_ls_low_power,0,1,"host_support_fs_ls_low_power"); ++ DWC_OTG_PARAM_ERR(host_ls_low_power_phy_clk,0,1,"host_ls_low_power_phy_clk"); ++ DWC_OTG_PARAM_ERR(enable_dynamic_fifo,0,1,"enable_dynamic_fifo"); ++ DWC_OTG_PARAM_ERR(data_fifo_size,32,32768,"data_fifo_size"); ++ DWC_OTG_PARAM_ERR(dev_rx_fifo_size,16,32768,"dev_rx_fifo_size"); ++ DWC_OTG_PARAM_ERR(dev_nperio_tx_fifo_size,16,32768,"dev_nperio_tx_fifo_size"); ++ DWC_OTG_PARAM_ERR(host_rx_fifo_size,16,32768,"host_rx_fifo_size"); ++ DWC_OTG_PARAM_ERR(host_nperio_tx_fifo_size,16,32768,"host_nperio_tx_fifo_size"); ++ DWC_OTG_PARAM_ERR(host_perio_tx_fifo_size,16,32768,"host_perio_tx_fifo_size"); ++ DWC_OTG_PARAM_ERR(max_transfer_size,2047,524288,"max_transfer_size"); ++ DWC_OTG_PARAM_ERR(max_packet_count,15,511,"max_packet_count"); ++ DWC_OTG_PARAM_ERR(host_channels,1,16,"host_channels"); ++ DWC_OTG_PARAM_ERR(dev_endpoints,1,15,"dev_endpoints"); ++ DWC_OTG_PARAM_ERR(phy_type,0,2,"phy_type"); ++ DWC_OTG_PARAM_ERR(phy_ulpi_ddr,0,1,"phy_ulpi_ddr"); ++ DWC_OTG_PARAM_ERR(phy_ulpi_ext_vbus,0,1,"phy_ulpi_ext_vbus"); ++ DWC_OTG_PARAM_ERR(i2c_enable,0,1,"i2c_enable"); ++ DWC_OTG_PARAM_ERR(ulpi_fs_ls,0,1,"ulpi_fs_ls"); ++ DWC_OTG_PARAM_ERR(ts_dline,0,1,"ts_dline"); ++ ++ if (dwc_otg_module_params.dma_burst_size != -1) { ++ if (DWC_OTG_PARAM_TEST(dma_burst_size,1,1) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,4,4) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,8,8) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,16,16) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,32,32) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,64,64) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,128,128) && ++ DWC_OTG_PARAM_TEST(dma_burst_size,256,256)) ++ { ++ DWC_ERROR("`%d' invalid for parameter `dma_burst_size'\n", ++ dwc_otg_module_params.dma_burst_size); ++ dwc_otg_module_params.dma_burst_size = 32; ++ retval ++; ++ } ++ } ++ ++ if (dwc_otg_module_params.phy_utmi_width != -1) { ++ if (DWC_OTG_PARAM_TEST(phy_utmi_width,8,8) && ++ DWC_OTG_PARAM_TEST(phy_utmi_width,16,16)) ++ { ++ DWC_ERROR("`%d' invalid for parameter `phy_utmi_width'\n", ++ dwc_otg_module_params.phy_utmi_width); ++ //dwc_otg_module_params.phy_utmi_width = 16; ++ dwc_otg_module_params.phy_utmi_width = 8; ++ retval ++; ++ } ++ } ++ ++ for (i=0; i<15; i++) { ++ /** @todo should be like above */ ++ //DWC_OTG_PARAM_ERR(dev_perio_tx_fifo_size[i],4,768,"dev_perio_tx_fifo_size"); ++ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) { ++ if (DWC_OTG_PARAM_TEST(dev_perio_tx_fifo_size[i],4,768)) { ++ DWC_ERROR("`%d' invalid for parameter `%s_%d'\n", ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i], "dev_perio_tx_fifo_size", i); ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default; ++ retval ++; ++ } ++ } ++ } ++ ++ DWC_OTG_PARAM_ERR(en_multiple_tx_fifo, 0, 1, "en_multiple_tx_fifo"); ++ for (i = 0; i < 15; i++) { ++ /** @todo should be like above */ ++ //DWC_OTG_PARAM_ERR(dev_tx_fifo_size[i],4,768,"dev_tx_fifo_size"); ++ if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) { ++ if (DWC_OTG_PARAM_TEST(dev_tx_fifo_size[i], 4, 768)) { ++ DWC_ERROR("`%d' invalid for parameter `%s_%d'\n", ++ dwc_otg_module_params.dev_tx_fifo_size[i], ++ "dev_tx_fifo_size", i); ++ dwc_otg_module_params.dev_tx_fifo_size[i] = ++ dwc_param_dev_tx_fifo_size_default; ++ retval++; ++ } ++ } ++ } ++ DWC_OTG_PARAM_ERR(thr_ctl, 0, 7, "thr_ctl"); ++ DWC_OTG_PARAM_ERR(tx_thr_length, 8, 128, "tx_thr_length"); ++ DWC_OTG_PARAM_ERR(rx_thr_length, 8, 128, "rx_thr_length"); ++ ++ /* At this point, all module parameters that have been set by the user ++ * are valid, and those that have not are left unset. Now set their ++ * default values and/or check the parameters against the hardware ++ * configurations of the OTG core. */ ++ ++ ++ ++/* This sets the parameter to the default value if it has not been set by the ++ * user */ ++#define DWC_OTG_PARAM_SET_DEFAULT(_param_) \ ++ ({ \ ++ int changed = 1; \ ++ if (dwc_otg_module_params._param_ == -1) { \ ++ changed = 0; \ ++ dwc_otg_module_params._param_ = dwc_param_##_param_##_default; \ ++ } \ ++ changed; \ ++ }) ++ ++/* This checks the macro agains the hardware configuration to see if it is ++ * valid. It is possible that the default value could be invalid. In this ++ * case, it will report a module error if the user touched the parameter. ++ * Otherwise it will adjust the value without any error. */ ++#define DWC_OTG_PARAM_CHECK_VALID(_param_,_str_,_is_valid_,_set_valid_) \ ++ ({ \ ++ int changed = DWC_OTG_PARAM_SET_DEFAULT(_param_); \ ++ int error = 0; \ ++ if (!(_is_valid_)) { \ ++ if (changed) { \ ++ DWC_ERROR("`%d' invalid for parameter `%s'. Check HW configuration.\n", dwc_otg_module_params._param_,_str_); \ ++ error = 1; \ ++ } \ ++ dwc_otg_module_params._param_ = (_set_valid_); \ ++ } \ ++ error; \ ++ }) ++ ++ /* OTG Cap */ ++ retval += DWC_OTG_PARAM_CHECK_VALID(otg_cap,"otg_cap", ++ ({ ++ int valid; ++ valid = 1; ++ switch (dwc_otg_module_params.otg_cap) { ++ case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE: ++ if (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) valid = 0; ++ break; ++ case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE: ++ if ((core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) && ++ (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) && ++ (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) && ++ (core_if->hwcfg2.b.op_mode != DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ++ { ++ valid = 0; ++ } ++ break; ++ case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE: ++ /* always valid */ ++ break; ++ } ++ valid; ++ }), ++ (((core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) || ++ (core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) || ++ (core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || ++ (core_if->hwcfg2.b.op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ? ++ DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE : ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dma_enable,"dma_enable", ++ ((dwc_otg_module_params.dma_enable == 1) && (core_if->hwcfg2.b.architecture == 0)) ? 0 : 1, ++ 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(opt,"opt", ++ 1, ++ 0); ++ ++ DWC_OTG_PARAM_SET_DEFAULT(dma_burst_size); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_support_fs_ls_low_power, ++ "host_support_fs_ls_low_power", ++ 1, 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(enable_dynamic_fifo, ++ "enable_dynamic_fifo", ++ ((dwc_otg_module_params.enable_dynamic_fifo == 0) || ++ (core_if->hwcfg2.b.dynamic_fifo == 1)), 0); ++ ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(data_fifo_size, ++ "data_fifo_size", ++ (dwc_otg_module_params.data_fifo_size <= core_if->hwcfg3.b.dfifo_depth), ++ core_if->hwcfg3.b.dfifo_depth); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dev_rx_fifo_size, ++ "dev_rx_fifo_size", ++ (dwc_otg_module_params.dev_rx_fifo_size <= dwc_read_reg32(&core_if->core_global_regs->grxfsiz)), ++ dwc_read_reg32(&core_if->core_global_regs->grxfsiz)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dev_nperio_tx_fifo_size, ++ "dev_nperio_tx_fifo_size", ++ (dwc_otg_module_params.dev_nperio_tx_fifo_size <= (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)), ++ (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_rx_fifo_size, ++ "host_rx_fifo_size", ++ (dwc_otg_module_params.host_rx_fifo_size <= dwc_read_reg32(&core_if->core_global_regs->grxfsiz)), ++ dwc_read_reg32(&core_if->core_global_regs->grxfsiz)); ++ ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_nperio_tx_fifo_size, ++ "host_nperio_tx_fifo_size", ++ (dwc_otg_module_params.host_nperio_tx_fifo_size <= (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)), ++ (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_perio_tx_fifo_size, ++ "host_perio_tx_fifo_size", ++ (dwc_otg_module_params.host_perio_tx_fifo_size <= ((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16))), ++ ((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16))); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(max_transfer_size, ++ "max_transfer_size", ++ (dwc_otg_module_params.max_transfer_size < (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))), ++ ((1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11)) - 1)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(max_packet_count, ++ "max_packet_count", ++ (dwc_otg_module_params.max_packet_count < (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))), ++ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_channels, ++ "host_channels", ++ (dwc_otg_module_params.host_channels <= (core_if->hwcfg2.b.num_host_chan + 1)), ++ (core_if->hwcfg2.b.num_host_chan + 1)); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(dev_endpoints, ++ "dev_endpoints", ++ (dwc_otg_module_params.dev_endpoints <= (core_if->hwcfg2.b.num_dev_ep)), ++ core_if->hwcfg2.b.num_dev_ep); ++ ++/* ++ * Define the following to disable the FS PHY Hardware checking. This is for ++ * internal testing only. ++ * ++ * #define NO_FS_PHY_HW_CHECKS ++ */ ++ ++#ifdef NO_FS_PHY_HW_CHECKS ++ retval += DWC_OTG_PARAM_CHECK_VALID(phy_type, ++ "phy_type", 1, 0); ++#else ++ retval += DWC_OTG_PARAM_CHECK_VALID(phy_type, ++ "phy_type", ++ ({ ++ int valid = 0; ++ if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_UTMI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 1) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) ++ { ++ valid = 1; ++ } ++ else if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_ULPI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 2) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) ++ { ++ valid = 1; ++ } ++ else if ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->hwcfg2.b.fs_phy_type == 1)) ++ { ++ valid = 1; ++ } ++ valid; ++ }), ++ ({ ++ int set = DWC_PHY_TYPE_PARAM_FS; ++ if (core_if->hwcfg2.b.hs_phy_type) { ++ if ((core_if->hwcfg2.b.hs_phy_type == 3) || ++ (core_if->hwcfg2.b.hs_phy_type == 1)) { ++ set = DWC_PHY_TYPE_PARAM_UTMI; ++ } ++ else { ++ set = DWC_PHY_TYPE_PARAM_ULPI; ++ } ++ } ++ set; ++ })); ++#endif ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(speed,"speed", ++ (dwc_otg_module_params.speed == 0) && (dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? 0 : 1, ++ dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS ? 1 : 0); ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(host_ls_low_power_phy_clk, ++ "host_ls_low_power_phy_clk", ++ ((dwc_otg_module_params.host_ls_low_power_phy_clk == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ) && (dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? 0 : 1), ++ ((dwc_otg_module_params.phy_type == DWC_PHY_TYPE_PARAM_FS) ? DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ : DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ)); ++ ++ DWC_OTG_PARAM_SET_DEFAULT(phy_ulpi_ddr); ++ DWC_OTG_PARAM_SET_DEFAULT(phy_ulpi_ext_vbus); ++ DWC_OTG_PARAM_SET_DEFAULT(phy_utmi_width); ++ DWC_OTG_PARAM_SET_DEFAULT(ulpi_fs_ls); ++ DWC_OTG_PARAM_SET_DEFAULT(ts_dline); ++ ++#ifdef NO_FS_PHY_HW_CHECKS ++ retval += DWC_OTG_PARAM_CHECK_VALID(i2c_enable, ++ "i2c_enable", 1, 0); ++#else ++ retval += DWC_OTG_PARAM_CHECK_VALID(i2c_enable, ++ "i2c_enable", ++ (dwc_otg_module_params.i2c_enable == 1) && (core_if->hwcfg3.b.i2c == 0) ? 0 : 1, ++ 0); ++#endif ++ ++ for (i=0; i<16; i++) { ++ ++ int changed = 1; ++ int error = 0; ++ ++ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] == -1) { ++ changed = 0; ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_param_dev_perio_tx_fifo_size_default; ++ } ++ if (!(dwc_otg_module_params.dev_perio_tx_fifo_size[i] <= (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i])))) { ++ if (changed) { ++ DWC_ERROR("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", dwc_otg_module_params.dev_perio_tx_fifo_size[i],i); ++ error = 1; ++ } ++ dwc_otg_module_params.dev_perio_tx_fifo_size[i] = dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i]); ++ } ++ retval += error; ++ } ++ ++ retval += DWC_OTG_PARAM_CHECK_VALID(en_multiple_tx_fifo, ++ "en_multiple_tx_fifo", ++ ((dwc_otg_module_params.en_multiple_tx_fifo == 1) && ++ (core_if->hwcfg4.b.ded_fifo_en == 0)) ? 0 : 1, 0); ++ ++ for (i = 0; i < 16; i++) { ++ int changed = 1; ++ int error = 0; ++ if (dwc_otg_module_params.dev_tx_fifo_size[i] == -1) { ++ changed = 0; ++ dwc_otg_module_params.dev_tx_fifo_size[i] = ++ dwc_param_dev_tx_fifo_size_default; ++ } ++ if (!(dwc_otg_module_params.dev_tx_fifo_size[i] <= ++ (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i])))) { ++ if (changed) { ++ DWC_ERROR("%d' invalid for parameter `dev_perio_fifo_size_%d'." ++ "Check HW configuration.\n",dwc_otg_module_params.dev_tx_fifo_size[i],i); ++ error = 1; ++ } ++ dwc_otg_module_params.dev_tx_fifo_size[i] = ++ dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[i]); ++ } ++ retval += error; ++ } ++ DWC_OTG_PARAM_SET_DEFAULT(thr_ctl); ++ DWC_OTG_PARAM_SET_DEFAULT(tx_thr_length); ++ DWC_OTG_PARAM_SET_DEFAULT(rx_thr_length); ++ return retval; ++} // check_parameters ++ ++ ++/** ++ * This function is the top level interrupt handler for the Common ++ * (Device and host modes) interrupts. ++ */ ++static irqreturn_t dwc_otg_common_irq(int _irq, void *_dev) ++{ ++ dwc_otg_device_t *otg_dev = _dev; ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_handle_common_intr( otg_dev->core_if ); ++ ++ mask_and_ack_ifx_irq (_irq); ++ ++ return IRQ_RETVAL(retval); ++} ++ ++ ++/** ++ * This function is called when a DWC_OTG device is unregistered with the ++ * dwc_otg_driver. This happens, for example, when the rmmod command is ++ * executed. The device may or may not be electrically present. If it is ++ * present, the driver stops device processing. Any resources used on behalf ++ * of this device are freed. ++ * ++ * @return ++ */ ++static int ++dwc_otg_driver_remove(struct platform_device *_dev) ++{ ++ //dwc_otg_device_t *otg_dev = dev_get_drvdata(&_dev->dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++ ++ DWC_DEBUGPL(DBG_ANY, "%s(%p)\n", __func__, _dev); ++ ++ if (otg_dev == NULL) { ++ /* Memory allocation for the dwc_otg_device failed. */ ++ return 0; ++ } ++ ++ /* ++ * Free the IRQ ++ */ ++ if (otg_dev->common_irq_installed) { ++ free_irq( otg_dev->irq, otg_dev ); ++ } ++ ++#ifndef DWC_DEVICE_ONLY ++ if (otg_dev->hcd != NULL) { ++ dwc_otg_hcd_remove(&_dev->dev); ++ } ++#endif ++ printk("after removehcd\n"); ++ ++// Note: Integrate HOST and DEVICE(Gadget) is not planned yet. ++#ifndef DWC_HOST_ONLY ++ if (otg_dev->pcd != NULL) { ++ dwc_otg_pcd_remove(otg_dev); ++ } ++#endif ++ if (otg_dev->core_if != NULL) { ++ dwc_otg_cil_remove( otg_dev->core_if ); ++ } ++ printk("after removecil\n"); ++ ++ /* ++ * Remove the device attributes ++ */ ++ dwc_otg_attr_remove(&_dev->dev); ++ printk("after removeattr\n"); ++ ++ /* ++ * Return the memory. ++ */ ++ if (otg_dev->base != NULL) { ++ iounmap(otg_dev->base); ++ } ++ if (otg_dev->phys_addr != 0) { ++ release_mem_region(otg_dev->phys_addr, otg_dev->base_len); ++ } ++ kfree(otg_dev); ++ ++ /* ++ * Clear the drvdata pointer. ++ */ ++ //dev_set_drvdata(&_dev->dev, 0); ++ platform_set_drvdata(_dev, 0); ++ return 0; ++} ++ ++/** ++ * This function is called when an DWC_OTG device is bound to a ++ * dwc_otg_driver. It creates the driver components required to ++ * control the device (CIL, HCD, and PCD) and it initializes the ++ * device. The driver components are stored in a dwc_otg_device ++ * structure. A reference to the dwc_otg_device is saved in the ++ * lm_device. This allows the driver to access the dwc_otg_device ++ * structure on subsequent calls to driver methods for this device. ++ * ++ * @return ++ */ ++static int __devinit ++dwc_otg_driver_probe(struct platform_device *_dev) ++{ ++ int retval = 0; ++ dwc_otg_device_t *dwc_otg_device; ++ int pin = (int)_dev->dev.platform_data; ++ int32_t snpsid; ++ struct resource *res; ++ gusbcfg_data_t usbcfg = {.d32 = 0}; ++ ++ // GPIOs ++ if(pin >= 0) ++ { ++ gpio_request(pin, "usb_power"); ++ gpio_direction_output(pin, 1); ++ gpio_set_value(pin, 1); ++ gpio_export(pin, 0); ++ } ++ dev_dbg(&_dev->dev, "dwc_otg_driver_probe (%p)\n", _dev); ++ ++ dwc_otg_device = kmalloc(sizeof(dwc_otg_device_t), GFP_KERNEL); ++ if (dwc_otg_device == 0) { ++ dev_err(&_dev->dev, "kmalloc of dwc_otg_device failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ memset(dwc_otg_device, 0, sizeof(*dwc_otg_device)); ++ dwc_otg_device->reg_offset = 0xFFFFFFFF; ++ ++ /* ++ * Retrieve the memory and IRQ resources. ++ */ ++ dwc_otg_device->irq = platform_get_irq(_dev, 0); ++ if (dwc_otg_device->irq == 0) { ++ dev_err(&_dev->dev, "no device irq\n"); ++ retval = -ENODEV; ++ goto fail; ++ } ++ dev_dbg(&_dev->dev, "OTG - device irq: %d\n", dwc_otg_device->irq); ++ res = platform_get_resource(_dev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ dev_err(&_dev->dev, "no CSR address\n"); ++ retval = -ENODEV; ++ goto fail; ++ } ++ dev_dbg(&_dev->dev, "OTG - ioresource_mem start0x%08x: end:0x%08x\n", ++ (unsigned)res->start, (unsigned)res->end); ++ dwc_otg_device->phys_addr = res->start; ++ dwc_otg_device->base_len = res->end - res->start + 1; ++ if (request_mem_region(dwc_otg_device->phys_addr, dwc_otg_device->base_len, ++ dwc_driver_name) == NULL) { ++ dev_err(&_dev->dev, "request_mem_region failed\n"); ++ retval = -EBUSY; ++ goto fail; ++ } ++ ++ /* ++ * Map the DWC_otg Core memory into virtual address space. ++ */ ++ dwc_otg_device->base = ioremap_nocache(dwc_otg_device->phys_addr, dwc_otg_device->base_len); ++ if (dwc_otg_device->base == NULL) { ++ dev_err(&_dev->dev, "ioremap() failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ dev_dbg(&_dev->dev, "mapped base=0x%08x\n", (unsigned)dwc_otg_device->base); ++ ++ /* ++ * Attempt to ensure this device is really a DWC_otg Controller. ++ * Read and verify the SNPSID register contents. The value should be ++ * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX". ++ */ ++ snpsid = dwc_read_reg32((uint32_t *)((uint8_t *)dwc_otg_device->base + 0x40)); ++ if ((snpsid & 0xFFFFF000) != 0x4F542000) { ++ dev_err(&_dev->dev, "Bad value for SNPSID: 0x%08x\n", snpsid); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Initialize driver data to point to the global DWC_otg ++ * Device structure. ++ */ ++ platform_set_drvdata(_dev, dwc_otg_device); ++ dev_dbg(&_dev->dev, "dwc_otg_device=0x%p\n", dwc_otg_device); ++ dwc_otg_device->core_if = dwc_otg_cil_init( dwc_otg_device->base, &dwc_otg_module_params); ++ if (dwc_otg_device->core_if == 0) { ++ dev_err(&_dev->dev, "CIL initialization failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ /* ++ * Validate parameter values. ++ */ ++ if (check_parameters(dwc_otg_device->core_if) != 0) { ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ /* Added for PLB DMA phys virt mapping */ ++ //dwc_otg_device->core_if->phys_addr = dwc_otg_device->phys_addr; ++ /* ++ * Create Device Attributes in sysfs ++ */ ++ dwc_otg_attr_create (&_dev->dev); ++ ++ /* ++ * Disable the global interrupt until all the interrupt ++ * handlers are installed. ++ */ ++ dwc_otg_disable_global_interrupts( dwc_otg_device->core_if ); ++ /* ++ * Install the interrupt handler for the common interrupts before ++ * enabling common interrupts in core_init below. ++ */ ++ DWC_DEBUGPL( DBG_CIL, "registering (common) handler for irq%d\n", dwc_otg_device->irq); ++ ++ retval = request_irq((unsigned int)dwc_otg_device->irq, dwc_otg_common_irq, ++ //SA_INTERRUPT|SA_SHIRQ, "dwc_otg", (void *)dwc_otg_device ); ++ IRQF_SHARED, "dwc_otg", (void *)dwc_otg_device ); ++ //IRQF_DISABLED, "dwc_otg", (void *)dwc_otg_device ); ++ if (retval != 0) { ++ DWC_ERROR("request of irq%d failed retval: %d\n", dwc_otg_device->irq, retval); ++ retval = -EBUSY; ++ goto fail; ++ } else { ++ dwc_otg_device->common_irq_installed = 1; ++ } ++ ++ /* ++ * Initialize the DWC_otg core. ++ */ ++ dwc_otg_core_init( dwc_otg_device->core_if ); ++ ++ ++#ifndef DWC_HOST_ONLY // otg device mode. (gadget.) ++ /* ++ * Initialize the PCD ++ */ ++ retval = dwc_otg_pcd_init(dwc_otg_device); ++ if (retval != 0) { ++ DWC_ERROR("dwc_otg_pcd_init failed\n"); ++ dwc_otg_device->pcd = NULL; ++ goto fail; ++ } ++#endif // DWC_HOST_ONLY ++ ++#ifndef DWC_DEVICE_ONLY // otg host mode. (HCD) ++ /* ++ * Initialize the HCD ++ */ ++#if 1 /*fscz*/ ++ /* force_host_mode */ ++ usbcfg.d32 = dwc_read_reg32(&dwc_otg_device->core_if->core_global_regs ->gusbcfg); ++ usbcfg.b.force_host_mode = 1; ++ dwc_write_reg32(&dwc_otg_device->core_if->core_global_regs ->gusbcfg, usbcfg.d32); ++#endif ++ retval = dwc_otg_hcd_init(&_dev->dev, dwc_otg_device); ++ if (retval != 0) { ++ DWC_ERROR("dwc_otg_hcd_init failed\n"); ++ dwc_otg_device->hcd = NULL; ++ goto fail; ++ } ++#endif // DWC_DEVICE_ONLY ++ ++ /* ++ * Enable the global interrupt after all the interrupt ++ * handlers are installed. ++ */ ++ dwc_otg_enable_global_interrupts( dwc_otg_device->core_if ); ++#if 0 /*fscz*/ ++ usbcfg.d32 = dwc_read_reg32(&dwc_otg_device->core_if->core_global_regs ->gusbcfg); ++ usbcfg.b.force_host_mode = 0; ++ dwc_write_reg32(&dwc_otg_device->core_if->core_global_regs ->gusbcfg, usbcfg.d32); ++#endif ++ ++ ++ return 0; ++ ++fail: ++ dwc_otg_driver_remove(_dev); ++ return retval; ++} ++ ++/** ++ * This structure defines the methods to be called by a bus driver ++ * during the lifecycle of a device on that bus. Both drivers and ++ * devices are registered with a bus driver. The bus driver matches ++ * devices to drivers based on information in the device and driver ++ * structures. ++ * ++ * The probe function is called when the bus driver matches a device ++ * to this driver. The remove function is called when a device is ++ * unregistered with the bus driver. ++ */ ++struct platform_driver dwc_otg_driver = { ++ .probe = dwc_otg_driver_probe, ++ .remove = dwc_otg_driver_remove, ++// .suspend = dwc_otg_driver_suspend, ++// .resume = dwc_otg_driver_resume, ++ .driver = { ++ .name = dwc_driver_name, ++ .owner = THIS_MODULE, ++ }, ++}; ++EXPORT_SYMBOL(dwc_otg_driver); ++ ++/** ++ * This function is called when the dwc_otg_driver is installed with the ++ * insmod command. It registers the dwc_otg_driver structure with the ++ * appropriate bus driver. This will cause the dwc_otg_driver_probe function ++ * to be called. In addition, the bus driver will automatically expose ++ * attributes defined for the device and driver in the special sysfs file ++ * system. ++ * ++ * @return ++ */ ++static int __init dwc_otg_init(void) ++{ ++ int retval = 0; ++ ++ printk(KERN_INFO "%s: version %s\n", dwc_driver_name, DWC_DRIVER_VERSION); ++ ++ // ifxmips setup ++ retval = ifx_usb_hc_init(dwc_iomem_base, dwc_irq); ++ if (retval < 0) ++ { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ dwc_otg_power_on(); // ifx only!! ++ ++ ++ retval = platform_driver_register(&dwc_otg_driver); ++ ++ if (retval < 0) { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ goto error1; ++ } ++ ++ retval = driver_create_file(&dwc_otg_driver.driver, &driver_attr_version); ++ if (retval < 0) ++ { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ goto error2; ++ } ++ retval = driver_create_file(&dwc_otg_driver.driver, &driver_attr_debuglevel); ++ if (retval < 0) ++ { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ goto error3; ++ } ++ return retval; ++ ++ ++error3: ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version); ++error2: ++ driver_unregister(&dwc_otg_driver.driver); ++error1: ++ ifx_usb_hc_remove(); ++ return retval; ++} ++module_init(dwc_otg_init); ++ ++/** ++ * This function is called when the driver is removed from the kernel ++ * with the rmmod command. The driver unregisters itself with its bus ++ * driver. ++ * ++ */ ++static void __exit dwc_otg_cleanup(void) ++{ ++ printk(KERN_DEBUG "dwc_otg_cleanup()\n"); ++ ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel); ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version); ++ ++ platform_driver_unregister(&dwc_otg_driver); ++ ifx_usb_hc_remove(); ++ ++ printk(KERN_INFO "%s module removed\n", dwc_driver_name); ++} ++module_exit(dwc_otg_cleanup); ++ ++MODULE_DESCRIPTION(DWC_DRIVER_DESC); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE("GPL"); ++ ++module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444); ++MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None"); ++module_param_named(opt, dwc_otg_module_params.opt, int, 0444); ++MODULE_PARM_DESC(opt, "OPT Mode"); ++module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444); ++MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled"); ++module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int, 0444); ++MODULE_PARM_DESC(dma_burst_size, "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256"); ++module_param_named(speed, dwc_otg_module_params.speed, int, 0444); ++MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed"); ++module_param_named(host_support_fs_ls_low_power, dwc_otg_module_params.host_support_fs_ls_low_power, int, 0444); ++MODULE_PARM_DESC(host_support_fs_ls_low_power, "Support Low Power w/FS or LS 0=Support 1=Don't Support"); ++module_param_named(host_ls_low_power_phy_clk, dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444); ++MODULE_PARM_DESC(host_ls_low_power_phy_clk, "Low Speed Low Power Clock 0=48Mhz 1=6Mhz"); ++module_param_named(enable_dynamic_fifo, dwc_otg_module_params.enable_dynamic_fifo, int, 0444); ++MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing"); ++module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int, 0444); ++MODULE_PARM_DESC(data_fifo_size, "Total number of words in the data FIFO memory 32-32768"); ++module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size, int, 0444); ++MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++module_param_named(dev_nperio_tx_fifo_size, dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(dev_nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768"); ++module_param_named(dev_perio_tx_fifo_size_1, dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_1, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_2, dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_2, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_3, dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_3, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_4, dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_4, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_5, dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_5, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_6, dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_6, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_7, dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_7, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_8, dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_8, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_9, dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_9, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_10, dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_10, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_11, dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_11, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_12, dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_12, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_13, dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_13, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_14, dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_14, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_15, dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_15, "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++module_param_named(host_nperio_tx_fifo_size, dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768"); ++module_param_named(host_perio_tx_fifo_size, dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_perio_tx_fifo_size, "Number of words in the host periodic Tx FIFO 16-32768"); ++module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size, int, 0444); ++/** @todo Set the max to 512K, modify checks */ ++MODULE_PARM_DESC(max_transfer_size, "The maximum transfer size supported in bytes 2047-65535"); ++module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count, int, 0444); ++MODULE_PARM_DESC(max_packet_count, "The maximum number of packets in a transfer 15-511"); ++module_param_named(host_channels, dwc_otg_module_params.host_channels, int, 0444); ++MODULE_PARM_DESC(host_channels, "The number of host channel registers to use 1-16"); ++module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int, 0444); ++MODULE_PARM_DESC(dev_endpoints, "The number of endpoints in addition to EP0 available for device mode 1-15"); ++module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444); ++MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI"); ++module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int, 0444); ++MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits"); ++module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444); ++MODULE_PARM_DESC(phy_ulpi_ddr, "ULPI at double or single data rate 0=Single 1=Double"); ++module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus, int, 0444); ++MODULE_PARM_DESC(phy_ulpi_ext_vbus, "ULPI PHY using internal or external vbus 0=Internal"); ++module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444); ++MODULE_PARM_DESC(i2c_enable, "FS PHY Interface"); ++module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444); ++MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only"); ++module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444); ++MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs"); ++module_param_named(debug, g_dbg_lvl, int, 0444); ++MODULE_PARM_DESC(debug, "0"); ++module_param_named(en_multiple_tx_fifo, ++ dwc_otg_module_params.en_multiple_tx_fifo, int, 0444); ++MODULE_PARM_DESC(en_multiple_tx_fifo, ++ "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled"); ++module_param_named(dev_tx_fifo_size_1, ++ dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_2, ++ dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_3, ++ dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_4, ++ dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_5, ++ dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_6, ++ dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_7, ++ dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_8, ++ dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_9, ++ dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_10, ++ dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_11, ++ dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_12, ++ dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_13, ++ dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_14, ++ dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_15, ++ dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768"); ++module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444); ++MODULE_PARM_DESC(thr_ctl, "Thresholding enable flag bit" ++ "0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled"); ++module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int, 0444); ++MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs"); ++module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int, 0444); ++MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs"); ++module_param_named (iomem_base, dwc_iomem_base, ulong, 0444); ++MODULE_PARM_DESC (dwc_iomem_base, "The base address of the DWC_OTG register."); ++module_param_named (irq, dwc_irq, int, 0444); ++MODULE_PARM_DESC (dwc_irq, "The interrupt number"); ++ ++/** @page "Module Parameters" ++ * ++ * The following parameters may be specified when starting the module. ++ * These parameters define how the DWC_otg controller should be ++ * configured. Parameter values are passed to the CIL initialization ++ * function dwc_otg_cil_init ++ * ++ * Example: modprobe dwc_otg speed=1 otg_cap=1 ++ * ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++*/ +diff --git a/drivers/usb/dwc_otg/dwc_otg_driver.h b/drivers/usb/dwc_otg/dwc_otg_driver.h +new file mode 100644 +index 0000000..7e6940d +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_driver.h +@@ -0,0 +1,84 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_driver.h $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 510275 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_DRIVER_H__) ++#define __DWC_OTG_DRIVER_H__ ++ ++/** @file ++ * This file contains the interface to the Linux driver. ++ */ ++#include "dwc_otg_cil.h" ++ ++/* Type declarations */ ++struct dwc_otg_pcd; ++struct dwc_otg_hcd; ++ ++/** ++ * This structure is a wrapper that encapsulates the driver components used to ++ * manage a single DWC_otg controller. ++ */ ++typedef struct dwc_otg_device ++{ ++ /** Base address returned from ioremap() */ ++ void *base; ++ ++ /** Pointer to the core interface structure. */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Register offset for Diagnostic API.*/ ++ uint32_t reg_offset; ++ ++ /** Pointer to the PCD structure. */ ++ struct dwc_otg_pcd *pcd; ++ ++ /** Pointer to the HCD structure. */ ++ struct dwc_otg_hcd *hcd; ++ ++ /** Flag to indicate whether the common IRQ handler is installed. */ ++ uint8_t common_irq_installed; ++ ++ /** Interrupt request number. */ ++ unsigned int irq; ++ ++ /** Physical address of Control and Status registers, used by ++ * release_mem_region(). ++ */ ++ resource_size_t phys_addr; ++ ++ /** Length of memory region, used by release_mem_region(). */ ++ unsigned long base_len; ++} dwc_otg_device_t; ++ ++//#define dev_dbg(fake, format, arg...) printk(KERN_CRIT __FILE__ ":%d: " format "\n" , __LINE__, ## arg) ++ ++#endif +diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd.c b/drivers/usb/dwc_otg/dwc_otg_hcd.c +new file mode 100644 +index 0000000..ad6bc72 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd.c +@@ -0,0 +1,2870 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 631780 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the implementation of the HCD. In Linux, the HCD ++ * implements the hc_driver API. ++ */ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++#include ++#include "dwc_otg_ifx.h" // for Infineon platform specific. ++extern atomic_t release_later; ++ ++static u64 dma_mask = DMA_BIT_MASK(32); ++ ++static const char dwc_otg_hcd_name [] = "dwc_otg_hcd"; ++static const struct hc_driver dwc_otg_hc_driver = ++{ ++ .description = dwc_otg_hcd_name, ++ .product_desc = "DWC OTG Controller", ++ .hcd_priv_size = sizeof(dwc_otg_hcd_t), ++ .irq = dwc_otg_hcd_irq, ++ .flags = HCD_MEMORY | HCD_USB2, ++ //.reset = ++ .start = dwc_otg_hcd_start, ++ //.suspend = ++ //.resume = ++ .stop = dwc_otg_hcd_stop, ++ .urb_enqueue = dwc_otg_hcd_urb_enqueue, ++ .urb_dequeue = dwc_otg_hcd_urb_dequeue, ++ .endpoint_disable = dwc_otg_hcd_endpoint_disable, ++ .get_frame_number = dwc_otg_hcd_get_frame_number, ++ .hub_status_data = dwc_otg_hcd_hub_status_data, ++ .hub_control = dwc_otg_hcd_hub_control, ++ //.hub_suspend = ++ //.hub_resume = ++}; ++ ++ ++/** ++ * Work queue function for starting the HCD when A-Cable is connected. ++ * The dwc_otg_hcd_start() must be called in a process context. ++ */ ++static void hcd_start_func(struct work_struct *work) ++{ ++ struct dwc_otg_hcd *priv = ++ container_of(work, struct dwc_otg_hcd, start_work); ++ struct usb_hcd *usb_hcd = (struct usb_hcd *)priv->_p; ++ DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, usb_hcd); ++ if (usb_hcd) { ++ dwc_otg_hcd_start(usb_hcd); ++ } ++} ++ ++ ++/** ++ * HCD Callback function for starting the HCD when A-Cable is ++ * connected. ++ * ++ * @param _p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_start_cb(void *_p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_p); ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ hprt0_data_t hprt0; ++ if (core_if->op_state == B_HOST) { ++ /* ++ * Reset the port. During a HNP mode switch the reset ++ * needs to occur within 1ms and have a duration of at ++ * least 50ms. ++ */ ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ((struct usb_hcd *)_p)->self.is_b_host = 1; ++ } else { ++ ((struct usb_hcd *)_p)->self.is_b_host = 0; ++ } ++ /* Need to start the HCD in a non-interrupt context. */ ++ INIT_WORK(&dwc_otg_hcd->start_work, hcd_start_func); ++ dwc_otg_hcd->_p = _p; ++ schedule_work(&dwc_otg_hcd->start_work); ++ return 1; ++} ++ ++ ++/** ++ * HCD Callback function for stopping the HCD. ++ * ++ * @param _p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_stop_cb( void *_p ) ++{ ++ struct usb_hcd *usb_hcd = (struct usb_hcd *)_p; ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p); ++ dwc_otg_hcd_stop( usb_hcd ); ++ return 1; ++} ++static void del_xfer_timers(dwc_otg_hcd_t *_hcd) ++{ ++#ifdef DEBUG ++ int i; ++ int num_channels = _hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ del_timer(&_hcd->core_if->hc_xfer_timer[i]); ++ } ++#endif /* */ ++} ++ ++static void del_timers(dwc_otg_hcd_t *_hcd) ++{ ++ del_xfer_timers(_hcd); ++ del_timer(&_hcd->conn_timer); ++} ++ ++/** ++ * Processes all the URBs in a single list of QHs. Completes them with ++ * -ETIMEDOUT and frees the QTD. ++ */ ++static void kill_urbs_in_qh_list(dwc_otg_hcd_t * _hcd, ++ struct list_head *_qh_list) ++{ ++ struct list_head *qh_item; ++ dwc_otg_qh_t *qh; ++ struct list_head *qtd_item; ++ dwc_otg_qtd_t *qtd; ++ ++ list_for_each(qh_item, _qh_list) { ++ qh = list_entry(qh_item, dwc_otg_qh_t, qh_list_entry); ++ for (qtd_item = qh->qtd_list.next; qtd_item != &qh->qtd_list; ++ qtd_item = qh->qtd_list.next) { ++ qtd = list_entry(qtd_item, dwc_otg_qtd_t, qtd_list_entry); ++ if (qtd->urb != NULL) { ++ dwc_otg_hcd_complete_urb(_hcd, qtd->urb,-ETIMEDOUT); ++ } ++ dwc_otg_hcd_qtd_remove_and_free(qtd); ++ } ++ } ++} ++ ++/** ++ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic ++ * and periodic schedules. The QTD associated with each URB is removed from ++ * the schedule and freed. This function may be called when a disconnect is ++ * detected or when the HCD is being stopped. ++ */ ++static void kill_all_urbs(dwc_otg_hcd_t *_hcd) ++{ ++ kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_deferred); ++ kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_inactive); ++ kill_urbs_in_qh_list(_hcd, &_hcd->non_periodic_sched_active); ++ kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_inactive); ++ kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_ready); ++ kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_assigned); ++ kill_urbs_in_qh_list(_hcd, &_hcd->periodic_sched_queued); ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param _p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_disconnect_cb( void *_p ) ++{ ++ gintsts_data_t intr; ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_p); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p); ++ ++ /* ++ * Set status flags for the hub driver. ++ */ ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 0; ++ ++ /* ++ * Shutdown any transfers in process by clearing the Tx FIFO Empty ++ * interrupt mask and status bits and disabling subsequent host ++ * channel interrupts. ++ */ ++ intr.d32 = 0; ++ intr.b.nptxfempty = 1; ++ intr.b.ptxfempty = 1; ++ intr.b.hcintr = 1; ++ dwc_modify_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gintmsk, intr.d32, 0); ++ dwc_modify_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gintsts, intr.d32, 0); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* ++ * Turn off the vbus power only if the core has transitioned to device ++ * mode. If still in host mode, need to keep power on to detect a ++ * reconnection. ++ */ ++ if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) { ++ if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) { ++ hprt0_data_t hprt0 = { .d32=0 }; ++ DWC_PRINT("Disconnect: PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); ++ } ++ ++ dwc_otg_disable_host_interrupts( dwc_otg_hcd->core_if ); ++ } ++ ++ /* Respond with an error status to all URBs in the schedule. */ ++ kill_all_urbs(dwc_otg_hcd); ++ ++ if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) { ++ /* Clean up any host channels that were in use. */ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ ++ num_channels = dwc_otg_hcd->core_if->core_params->host_channels; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ /* Flush out any channel requests in slave mode. */ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (list_empty(&channel->hc_list_entry)) { ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ } ++ } ++ } ++ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (list_empty(&channel->hc_list_entry)) { ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ /* Halt the channel. */ ++ hcchar.b.chdis = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ ++ dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, channel); ++ list_add_tail(&channel->hc_list_entry, ++ &dwc_otg_hcd->free_hc_list); ++ } ++ } ++ } ++ ++ /* A disconnect will end the session so the B-Device is no ++ * longer a B-host. */ ++ ((struct usb_hcd *)_p)->self.is_b_host = 0; ++ ++ return 1; ++} ++ ++/** ++ * Connection timeout function. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. ++ */ ++void dwc_otg_hcd_connect_timeout( unsigned long _ptr ) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%x)\n", __func__, (int)_ptr); ++ DWC_PRINT( "Connect Timeout\n"); ++ DWC_ERROR( "Device Not Connected/Responding\n" ); ++} ++ ++/** ++ * Start the connection timer. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. The ++ * timer is deleted if a port connect interrupt occurs before the ++ * timer expires. ++ */ ++static void dwc_otg_hcd_start_connect_timer( dwc_otg_hcd_t *_hcd) ++{ ++ init_timer( &_hcd->conn_timer ); ++ _hcd->conn_timer.function = dwc_otg_hcd_connect_timeout; ++ _hcd->conn_timer.data = (unsigned long)0; ++ _hcd->conn_timer.expires = jiffies + (HZ*10); ++ add_timer( &_hcd->conn_timer ); ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param _p void pointer to the struct usb_hcd ++ */ ++static int32_t dwc_otg_hcd_session_start_cb( void *_p ) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_p); ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _p); ++ dwc_otg_hcd_start_connect_timer( dwc_otg_hcd ); ++ return 1; ++} ++ ++/** ++ * HCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t hcd_cil_callbacks = { ++ .start = dwc_otg_hcd_start_cb, ++ .stop = dwc_otg_hcd_stop_cb, ++ .disconnect = dwc_otg_hcd_disconnect_cb, ++ .session_start = dwc_otg_hcd_session_start_cb, ++ .p = 0, ++}; ++ ++ ++/** ++ * Reset tasklet function ++ */ ++static void reset_tasklet_func (unsigned long data) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t*)data; ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ hprt0_data_t hprt0; ++ ++ DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n"); ++ ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ mdelay (60); ++ ++ hprt0.b.prtrst = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++ ++ return; ++} ++ ++static struct tasklet_struct reset_tasklet = { ++ .next = NULL, ++ .state = 0, ++ .count = ATOMIC_INIT(0), ++ .func = reset_tasklet_func, ++ .data = 0, ++}; ++ ++/** ++ * Initializes the HCD. This function allocates memory for and initializes the ++ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the ++ * USB bus with the core and calls the hc_driver->start() function. It returns ++ * a negative error on failure. ++ */ ++int init_hcd_usecs(dwc_otg_hcd_t *_hcd); ++ ++int __devinit dwc_otg_hcd_init(struct device *_dev, dwc_otg_device_t * dwc_otg_device) ++{ ++ struct usb_hcd *hcd = NULL; ++ dwc_otg_hcd_t *dwc_otg_hcd = NULL; ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT\n"); ++ ++ /* ++ * Allocate memory for the base HCD plus the DWC OTG HCD. ++ * Initialize the base HCD. ++ */ ++ hcd = usb_create_hcd(&dwc_otg_hc_driver, _dev, dev_name(_dev)); ++ if (hcd == NULL) { ++ retval = -ENOMEM; ++ goto error1; ++ } ++ dev_set_drvdata(_dev, dwc_otg_device); /* fscz restore */ ++ hcd->regs = otg_dev->base; ++ hcd->rsrc_start = (int)otg_dev->base; ++ ++ hcd->self.otg_port = 1; ++ ++ /* Initialize the DWC OTG HCD. */ ++ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_hcd->core_if = otg_dev->core_if; ++ otg_dev->hcd = dwc_otg_hcd; ++ ++ /* Register the HCD CIL Callbacks */ ++ dwc_otg_cil_register_hcd_callbacks(otg_dev->core_if, ++ &hcd_cil_callbacks, hcd); ++ ++ /* Initialize the non-periodic schedule. */ ++ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_inactive); ++ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_active); ++ INIT_LIST_HEAD(&dwc_otg_hcd->non_periodic_sched_deferred); ++ ++ /* Initialize the periodic schedule. */ ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_inactive); ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_ready); ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_assigned); ++ INIT_LIST_HEAD(&dwc_otg_hcd->periodic_sched_queued); ++ ++ /* ++ * Create a host channel descriptor for each host channel implemented ++ * in the controller. Initialize the channel descriptor array. ++ */ ++ INIT_LIST_HEAD(&dwc_otg_hcd->free_hc_list); ++ num_channels = dwc_otg_hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ channel = kmalloc(sizeof(dwc_hc_t), GFP_KERNEL); ++ if (channel == NULL) { ++ retval = -ENOMEM; ++ DWC_ERROR("%s: host channel allocation failed\n", __func__); ++ goto error2; ++ } ++ memset(channel, 0, sizeof(dwc_hc_t)); ++ channel->hc_num = i; ++ dwc_otg_hcd->hc_ptr_array[i] = channel; ++#ifdef DEBUG ++ init_timer(&dwc_otg_hcd->core_if->hc_xfer_timer[i]); ++#endif ++ ++ DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i, channel); ++ } ++ ++ /* Initialize the Connection timeout timer. */ ++ init_timer( &dwc_otg_hcd->conn_timer ); ++ ++ /* Initialize reset tasklet. */ ++ reset_tasklet.data = (unsigned long) dwc_otg_hcd; ++ dwc_otg_hcd->reset_tasklet = &reset_tasklet; ++ ++ /* Set device flags indicating whether the HCD supports DMA. */ ++ if (otg_dev->core_if->dma_enable) { ++ DWC_PRINT("Using DMA mode\n"); ++ //_dev->dma_mask = (void *)~0; ++ //_dev->coherent_dma_mask = ~0; ++ _dev->dma_mask = &dma_mask; ++ _dev->coherent_dma_mask = DMA_BIT_MASK(32); ++ } else { ++ DWC_PRINT("Using Slave mode\n"); ++ _dev->dma_mask = (void *)0; ++ _dev->coherent_dma_mask = 0; ++ } ++ ++ init_hcd_usecs(dwc_otg_hcd); ++ /* ++ * Finish generic HCD initialization and start the HCD. This function ++ * allocates the DMA buffer pool, registers the USB bus, requests the ++ * IRQ line, and calls dwc_otg_hcd_start method. ++ */ ++ retval = usb_add_hcd(hcd, otg_dev->irq, IRQF_SHARED); ++ if (retval < 0) { ++ goto error2; ++ } ++ ++ /* ++ * Allocate space for storing data on status transactions. Normally no ++ * data is sent, but this space acts as a bit bucket. This must be ++ * done after usb_add_hcd since that function allocates the DMA buffer ++ * pool. ++ */ ++ if (otg_dev->core_if->dma_enable) { ++ dwc_otg_hcd->status_buf = ++ dma_alloc_coherent(_dev, ++ DWC_OTG_HCD_STATUS_BUF_SIZE, ++ &dwc_otg_hcd->status_buf_dma, ++ GFP_KERNEL | GFP_DMA); ++ } else { ++ dwc_otg_hcd->status_buf = kmalloc(DWC_OTG_HCD_STATUS_BUF_SIZE, ++ GFP_KERNEL); ++ } ++ if (dwc_otg_hcd->status_buf == NULL) { ++ retval = -ENOMEM; ++ DWC_ERROR("%s: status_buf allocation failed\n", __func__); ++ goto error3; ++ } ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Initialized HCD, bus=%s, usbbus=%d\n", ++ dev_name(_dev), hcd->self.busnum); ++ ++ return 0; ++ ++ /* Error conditions */ ++error3: ++ usb_remove_hcd(hcd); ++error2: ++ dwc_otg_hcd_free(hcd); ++ usb_put_hcd(hcd); ++error1: ++ return retval; ++} ++ ++/** ++ * Removes the HCD. ++ * Frees memory and resources associated with the HCD and deregisters the bus. ++ */ ++void dwc_otg_hcd_remove(struct device *_dev) ++{ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++ dwc_otg_hcd_t *dwc_otg_hcd = otg_dev->hcd; ++ struct usb_hcd *hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE\n"); ++ ++ /* Turn off all interrupts */ ++ dwc_write_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gintmsk, 0); ++ dwc_modify_reg32 (&dwc_otg_hcd->core_if->core_global_regs->gahbcfg, 1, 0); ++ ++ usb_remove_hcd(hcd); ++ ++ dwc_otg_hcd_free(hcd); ++ ++ usb_put_hcd(hcd); ++ ++ return; ++} ++ ++ ++/* ========================================================================= ++ * Linux HC Driver Functions ++ * ========================================================================= */ ++ ++/** ++ * Initializes dynamic portions of the DWC_otg HCD state. ++ */ ++static void hcd_reinit(dwc_otg_hcd_t *_hcd) ++{ ++ struct list_head *item; ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ ++ _hcd->flags.d32 = 0; ++ ++ _hcd->non_periodic_qh_ptr = &_hcd->non_periodic_sched_active; ++ _hcd->available_host_channels = _hcd->core_if->core_params->host_channels; ++ ++ /* ++ * Put all channels in the free channel list and clean up channel ++ * states. ++ */ ++ item = _hcd->free_hc_list.next; ++ while (item != &_hcd->free_hc_list) { ++ list_del(item); ++ item = _hcd->free_hc_list.next; ++ } ++ num_channels = _hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ channel = _hcd->hc_ptr_array[i]; ++ list_add_tail(&channel->hc_list_entry, &_hcd->free_hc_list); ++ dwc_otg_hc_cleanup(_hcd->core_if, channel); ++ } ++ ++ /* Initialize the DWC core for host mode operation. */ ++ dwc_otg_core_host_init(_hcd->core_if); ++} ++ ++/** Initializes the DWC_otg controller and its root hub and prepares it for host ++ * mode operation. Activates the root port. Returns 0 on success and a negative ++ * error code on failure. */ ++int dwc_otg_hcd_start(struct usb_hcd *_hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); ++ dwc_otg_core_if_t * core_if = dwc_otg_hcd->core_if; ++ struct usb_bus *bus; ++ ++ // int retval; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); ++ ++ bus = hcd_to_bus(_hcd); ++ ++ /* Initialize the bus state. If the core is in Device Mode ++ * HALT the USB bus and return. */ ++ if (dwc_otg_is_device_mode (core_if)) { ++ _hcd->state = HC_STATE_HALT; ++ return 0; ++ } ++ _hcd->state = HC_STATE_RUNNING; ++ ++ /* Initialize and connect root hub if one is not already attached */ ++ if (bus->root_hub) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); ++ /* Inform the HUB driver to resume. */ ++ usb_hcd_resume_root_hub(_hcd); ++ } ++ else { ++#if 0 ++ struct usb_device *udev; ++ udev = usb_alloc_dev(NULL, bus, 0); ++ if (!udev) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error udev alloc\n"); ++ return -ENODEV; ++ } ++ udev->speed = USB_SPEED_HIGH; ++ /* Not needed - VJ ++ if ((retval = usb_hcd_register_root_hub(udev, _hcd)) != 0) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error registering %d\n", retval); ++ return -ENODEV; ++ } ++ */ ++#else ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Error udev alloc\n"); ++#endif ++ } ++ ++ hcd_reinit(dwc_otg_hcd); ++ ++ return 0; ++} ++ ++static void qh_list_free(dwc_otg_hcd_t *_hcd, struct list_head *_qh_list) ++{ ++ struct list_head *item; ++ dwc_otg_qh_t *qh; ++ ++ if (_qh_list->next == NULL) { ++ /* The list hasn't been initialized yet. */ ++ return; ++ } ++ ++ /* Ensure there are no QTDs or URBs left. */ ++ kill_urbs_in_qh_list(_hcd, _qh_list); ++ ++ for (item = _qh_list->next; item != _qh_list; item = _qh_list->next) { ++ qh = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ dwc_otg_hcd_qh_remove_and_free(_hcd, qh); ++ } ++} ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ */ ++void dwc_otg_hcd_stop(struct usb_hcd *_hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); ++ hprt0_data_t hprt0 = { .d32=0 }; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n"); ++ ++ /* Turn off all host-specific interrupts. */ ++ dwc_otg_disable_host_interrupts( dwc_otg_hcd->core_if ); ++ ++ /* ++ * The root hub should be disconnected before this function is called. ++ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) ++ * and the QH lists (via ..._hcd_endpoint_disable). ++ */ ++ ++ /* Turn off the vbus power */ ++ DWC_PRINT("PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0.d32); ++ ++ return; ++} ++ ++ ++/** Returns the current frame number. */ ++int dwc_otg_hcd_get_frame_number(struct usb_hcd *_hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); ++ hfnum_data_t hfnum; ++ ++ hfnum.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if-> ++ host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", hfnum.b.frnum); ++#endif ++ return hfnum.b.frnum; ++} ++ ++/** ++ * Frees secondary storage associated with the dwc_otg_hcd structure contained ++ * in the struct usb_hcd field. ++ */ ++void dwc_otg_hcd_free(struct usb_hcd *_hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); ++ int i; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n"); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* Free memory for QH/QTD lists */ ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_deferred); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued); ++ ++ /* Free memory for the host channels. */ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i]; ++ if (hc != NULL) { ++ DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", i, hc); ++ kfree(hc); ++ } ++ } ++ ++ if (dwc_otg_hcd->core_if->dma_enable) { ++ if (dwc_otg_hcd->status_buf_dma) { ++ dma_free_coherent(_hcd->self.controller, ++ DWC_OTG_HCD_STATUS_BUF_SIZE, ++ dwc_otg_hcd->status_buf, ++ dwc_otg_hcd->status_buf_dma); ++ } ++ } else if (dwc_otg_hcd->status_buf != NULL) { ++ kfree(dwc_otg_hcd->status_buf); ++ } ++ ++ return; ++} ++ ++ ++#ifdef DEBUG ++static void dump_urb_info(struct urb *_urb, char* _fn_name) ++{ ++ DWC_PRINT("%s, urb %p\n", _fn_name, _urb); ++ DWC_PRINT(" Device address: %d\n", usb_pipedevice(_urb->pipe)); ++ DWC_PRINT(" Endpoint: %d, %s\n", usb_pipeendpoint(_urb->pipe), ++ (usb_pipein(_urb->pipe) ? "IN" : "OUT")); ++ DWC_PRINT(" Endpoint type: %s\n", ++ ({char *pipetype; ++ switch (usb_pipetype(_urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CONTROL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break; ++ default: pipetype = "UNKNOWN"; break; ++ }; pipetype;})); ++ DWC_PRINT(" Speed: %s\n", ++ ({char *speed; ++ switch (_urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HIGH"; break; ++ case USB_SPEED_FULL: speed = "FULL"; break; ++ case USB_SPEED_LOW: speed = "LOW"; break; ++ default: speed = "UNKNOWN"; break; ++ }; speed;})); ++ DWC_PRINT(" Max packet size: %d\n", ++ usb_maxpacket(_urb->dev, _urb->pipe, usb_pipeout(_urb->pipe))); ++ DWC_PRINT(" Data buffer length: %d\n", _urb->transfer_buffer_length); ++ DWC_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n", ++ _urb->transfer_buffer, (void *)_urb->transfer_dma); ++ DWC_PRINT(" Setup buffer: %p, Setup DMA: %p\n", ++ _urb->setup_packet, (void *)_urb->setup_dma); ++ DWC_PRINT(" Interval: %d\n", _urb->interval); ++ if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < _urb->number_of_packets; i++) { ++ DWC_PRINT(" ISO Desc %d:\n", i); ++ DWC_PRINT(" offset: %d, length %d\n", ++ _urb->iso_frame_desc[i].offset, ++ _urb->iso_frame_desc[i].length); ++ } ++ } ++} ++ ++static void dump_channel_info(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *qh) ++{ ++ if (qh->channel != NULL) { ++ dwc_hc_t *hc = qh->channel; ++ struct list_head *item; ++ dwc_otg_qh_t *qh_item; ++ int num_channels = _hcd->core_if->core_params->host_channels; ++ int i; ++ ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ ++ hc_regs = _hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcdma = dwc_read_reg32(&hc_regs->hcdma); ++ ++ DWC_PRINT(" Assigned to channel %p:\n", hc); ++ DWC_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ DWC_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINT(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINT(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINT(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINT(" qh: %p\n", hc->qh); ++ DWC_PRINT(" NP inactive sched:\n"); ++ list_for_each(item, &_hcd->non_periodic_sched_inactive) { ++ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINT(" %p\n", qh_item); ++ } DWC_PRINT(" NP active sched:\n"); ++ list_for_each(item, &_hcd->non_periodic_sched_deferred) { ++ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINT(" %p\n", qh_item); ++ } DWC_PRINT(" NP deferred sched:\n"); ++ list_for_each(item, &_hcd->non_periodic_sched_active) { ++ qh_item = list_entry(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINT(" %p\n", qh_item); ++ } DWC_PRINT(" Channels: \n"); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = _hcd->hc_ptr_array[i]; ++ DWC_PRINT(" %2d: %p\n", i, hc); ++ } ++ } ++} ++#endif // DEBUG ++ ++/** Starts processing a USB transfer request specified by a USB Request Block ++ * (URB). mem_flags indicates the type of memory allocation to use while ++ * processing this URB. */ ++int dwc_otg_hcd_urb_enqueue(struct usb_hcd *_hcd, ++ struct urb *_urb, ++ gfp_t _mem_flags) ++{ ++ unsigned long flags; ++ int retval; ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); ++ dwc_otg_qtd_t *qtd; ++ ++ local_irq_save(flags); ++ retval = usb_hcd_link_urb_to_ep(_hcd, _urb); ++ if (retval) { ++ local_irq_restore(flags); ++ return retval; ++ } ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(_urb, "dwc_otg_hcd_urb_enqueue"); ++ } ++#endif // DEBUG ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* No longer connected. */ ++ local_irq_restore(flags); ++ return -ENODEV; ++ } ++ ++ qtd = dwc_otg_hcd_qtd_create (_urb); ++ if (qtd == NULL) { ++ local_irq_restore(flags); ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); ++ return -ENOMEM; ++ } ++ ++ retval = dwc_otg_hcd_qtd_add (qtd, dwc_otg_hcd); ++ if (retval < 0) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " ++ "Error status %d\n", retval); ++ dwc_otg_hcd_qtd_free(qtd); ++ } ++ ++ local_irq_restore (flags); ++ return retval; ++} ++ ++/** Aborts/cancels a USB transfer request. Always returns 0 to indicate ++ * success. */ ++int dwc_otg_hcd_urb_dequeue(struct usb_hcd *_hcd, struct urb *_urb, int _status) ++{ ++ unsigned long flags; ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ dwc_otg_qtd_t *urb_qtd; ++ dwc_otg_qh_t *qh; ++ int retval; ++ //struct usb_host_endpoint *_ep = NULL; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); ++ ++ local_irq_save(flags); ++ ++ retval = usb_hcd_check_unlink_urb(_hcd, _urb, _status); ++ if (retval) { ++ local_irq_restore(flags); ++ return retval; ++ } ++ ++ dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); ++ urb_qtd = (dwc_otg_qtd_t *)_urb->hcpriv; ++ if (urb_qtd == NULL) { ++ printk("urb_qtd is NULL for _urb %08x\n",(unsigned)_urb); ++ goto done; ++ } ++ qh = (dwc_otg_qh_t *) urb_qtd->qtd_qh_ptr; ++ if (qh == NULL) { ++ goto done; ++ } ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(_urb, "dwc_otg_hcd_urb_dequeue"); ++ if (urb_qtd == qh->qtd_in_process) { ++ dump_channel_info(dwc_otg_hcd, qh); ++ } ++ } ++#endif // DEBUG ++ ++ if (urb_qtd == qh->qtd_in_process) { ++ /* The QTD is in process (it has been assigned to a channel). */ ++ ++ if (dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * If still connected (i.e. in host mode), halt the ++ * channel so it can be used for other transfers. If ++ * no longer connected, the host registers can't be ++ * written to halt the channel since the core is in ++ * device mode. ++ */ ++ dwc_otg_hc_halt(dwc_otg_hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ } ++ } ++ ++ /* ++ * Free the QTD and clean up the associated QH. Leave the QH in the ++ * schedule if it has any remaining QTDs. ++ */ ++ dwc_otg_hcd_qtd_remove_and_free(urb_qtd); ++ if (urb_qtd == qh->qtd_in_process) { ++ dwc_otg_hcd_qh_deactivate(dwc_otg_hcd, qh, 0); ++ qh->channel = NULL; ++ qh->qtd_in_process = NULL; ++ } else if (list_empty(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(dwc_otg_hcd, qh); ++ } ++ ++done: ++ local_irq_restore(flags); ++ _urb->hcpriv = NULL; ++ ++ /* Higher layer software sets URB status. */ ++ usb_hcd_unlink_urb_from_ep(_hcd, _urb); ++ usb_hcd_giveback_urb(_hcd, _urb, _status); ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINT("Called usb_hcd_giveback_urb()\n"); ++ DWC_PRINT(" urb->status = %d\n", _urb->status); ++ } ++ ++ return 0; ++} ++ ++ ++/** Frees resources in the DWC_otg controller related to a given endpoint. Also ++ * clears state in the HCD related to the endpoint. Any URBs for the endpoint ++ * must already be dequeued. */ ++void dwc_otg_hcd_endpoint_disable(struct usb_hcd *_hcd, ++ struct usb_host_endpoint *_ep) ++ ++{ ++ dwc_otg_qh_t *qh; ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(_hcd); ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " ++ "endpoint=%d\n", _ep->desc.bEndpointAddress, ++ dwc_ep_addr_to_endpoint(_ep->desc.bEndpointAddress)); ++ ++ qh = (dwc_otg_qh_t *)(_ep->hcpriv); ++ if (qh != NULL) { ++#ifdef DEBUG ++ /** Check that the QTD list is really empty */ ++ if (!list_empty(&qh->qtd_list)) { ++ DWC_WARN("DWC OTG HCD EP DISABLE:" ++ " QTD List for this endpoint is not empty\n"); ++ } ++#endif // DEBUG ++ ++ dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd, qh); ++ _ep->hcpriv = NULL; ++ } ++ ++ return; ++} ++extern int dwc_irq; ++/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if ++ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid ++ * interrupt. ++ * ++ * This function is called by the USB core when an interrupt occurs */ ++irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *_hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); ++ ++ mask_and_ack_ifx_irq (dwc_irq); ++ return IRQ_RETVAL(dwc_otg_hcd_handle_intr(dwc_otg_hcd)); ++} ++ ++/** Creates Status Change bitmap for the root hub and root port. The bitmap is ++ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 ++ * is the status change indicator for the single root port. Returns 1 if either ++ * change indicator is 1, otherwise returns 0. */ ++int dwc_otg_hcd_hub_status_data(struct usb_hcd *_hcd, char *_buf) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); ++ ++ _buf[0] = 0; ++ _buf[0] |= (dwc_otg_hcd->flags.b.port_connect_status_change || ++ dwc_otg_hcd->flags.b.port_reset_change || ++ dwc_otg_hcd->flags.b.port_enable_change || ++ dwc_otg_hcd->flags.b.port_suspend_change || ++ dwc_otg_hcd->flags.b.port_over_current_change) << 1; ++ ++#ifdef DEBUG ++ if (_buf[0]) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:" ++ " Root port status changed\n"); ++ DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", ++ dwc_otg_hcd->flags.b.port_connect_status_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", ++ dwc_otg_hcd->flags.b.port_reset_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", ++ dwc_otg_hcd->flags.b.port_enable_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", ++ dwc_otg_hcd->flags.b.port_suspend_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", ++ dwc_otg_hcd->flags.b.port_over_current_change); ++ } ++#endif // DEBUG ++ return (_buf[0] != 0); ++} ++ ++#ifdef DWC_HS_ELECT_TST ++/* ++ * Quick and dirty hack to implement the HS Electrical Test ++ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. ++ * ++ * This code was copied from our userspace app "hset". It sends a ++ * Get Device Descriptor control sequence in two parts, first the ++ * Setup packet by itself, followed some time later by the In and ++ * Ack packets. Rather than trying to figure out how to add this ++ * functionality to the normal driver code, we just hijack the ++ * hardware, using these two function to drive the hardware ++ * directly. ++ */ ++ ++dwc_otg_core_global_regs_t *global_regs; ++dwc_otg_host_global_regs_t *hc_global_regs; ++dwc_otg_hc_regs_t *hc_regs; ++uint32_t *data_fifo; ++ ++static void do_setup(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ ++ /* Enable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* ++ * Send Setup packet (Get Device Descriptor) ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 1, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ // hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ MDELAY(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 1, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_SETUP; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ /* Fill FIFO with Setup data for Get Device Descriptor */ ++ data_fifo = (uint32_t *)((char *)global_regs + 0x1000); ++ dwc_write_reg32(data_fifo++, 0x01000680); ++ dwc_write_reg32(data_fifo++, 0x00080000); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Disable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++} ++ ++static void do_in_ack(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ host_grxsts_data_t grxsts; ++ ++ /* Enable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* ++ * Receive Control In packet ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 2, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ MDELAY(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 2, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 1; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ //fprintf(stderr, "Got RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer */ ++ if (grxsts.b.bcnt > 0) { ++ int i; ++ int word_count = (grxsts.b.bcnt + 3) / 4; ++ ++ data_fifo = (uint32_t *)((char *)global_regs + 0x1000); ++ ++ for (i = 0; i < word_count; i++) { ++ (void)dwc_read_reg32(data_fifo++); ++ } ++ } ++ ++ //fprintf(stderr, "Received %u bytes\n", (unsigned)grxsts.b.bcnt); ++ break; ++ ++ default: ++ //fprintf(stderr, "** Unexpected GRXSTS packet status 1 **\n"); ++ break; ++ } ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ //fprintf(stderr, "Got RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ break; ++ ++ default: ++ //fprintf(stderr, "** Unexpected GRXSTS packet status 2 **\n"); ++ break; ++ } ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ // usleep(100000); ++ // mdelay(100); ++ MDELAY(1); ++ ++ /* ++ * Send handshake packet ++ */ ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 3, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ MDELAY(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 3, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 0; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Disable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++} ++#endif /* DWC_HS_ELECT_TST */ ++ ++/** Handles hub class-specific requests.*/ ++int dwc_otg_hcd_hub_control(struct usb_hcd *_hcd, ++ u16 _typeReq, ++ u16 _wValue, ++ u16 _wIndex, ++ char *_buf, ++ u16 _wLength) ++{ ++ int retval = 0; ++ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd (_hcd); ++ dwc_otg_core_if_t *core_if = hcd_to_dwc_otg_hcd (_hcd)->core_if; ++ struct usb_hub_descriptor *desc; ++ hprt0_data_t hprt0 = {.d32 = 0}; ++ ++ uint32_t port_status; ++ ++ switch (_typeReq) { ++ case ClearHubFeature: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearHubFeature 0x%x\n", _wValue); ++ switch (_wValue) { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* Nothing required here */ ++ break; ++ default: ++ retval = -EINVAL; ++ DWC_ERROR ("DWC OTG HCD - " ++ "ClearHubFeature request %xh unknown\n", _wValue); ++ } ++ break; ++ case ClearPortFeature: ++ if (!_wIndex || _wIndex > 1) ++ goto error; ++ ++ switch (_wValue) { ++ case USB_PORT_FEAT_ENABLE: ++ DWC_DEBUGPL (DBG_ANY, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtena = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ /* Clear Resume bit */ ++ mdelay (100); ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_POWER: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_INDICATOR: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_INDICATOR\n"); ++ /* Port inidicator not supported */ ++ break; ++ case USB_PORT_FEAT_C_CONNECTION: ++ /* Clears drivers internal connect status change ++ * flag */ ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 0; ++ break; ++ case USB_PORT_FEAT_C_RESET: ++ /* Clears the driver's internal Port Reset Change ++ * flag */ ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); ++ dwc_otg_hcd->flags.b.port_reset_change = 0; ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ /* Clears the driver's internal Port ++ * Enable/Disable Change flag */ ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); ++ dwc_otg_hcd->flags.b.port_enable_change = 0; ++ break; ++ case USB_PORT_FEAT_C_SUSPEND: ++ /* Clears the driver's internal Port Suspend ++ * Change flag, which is set when resume signaling on ++ * the host port is complete */ ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); ++ dwc_otg_hcd->flags.b.port_suspend_change = 0; ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); ++ dwc_otg_hcd->flags.b.port_over_current_change = 0; ++ break; ++ default: ++ retval = -EINVAL; ++ DWC_ERROR ("DWC OTG HCD - " ++ "ClearPortFeature request %xh " ++ "unknown or unsupported\n", _wValue); ++ } ++ break; ++ case GetHubDescriptor: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubDescriptor\n"); ++ desc = (struct usb_hub_descriptor *)_buf; ++ desc->bDescLength = 9; ++ desc->bDescriptorType = 0x29; ++ desc->bNbrPorts = 1; ++ desc->wHubCharacteristics = 0x08; ++ desc->bPwrOn2PwrGood = 1; ++ desc->bHubContrCurrent = 0; ++ desc->u.hs.DeviceRemovable[0] = 0; ++ desc->u.hs.DeviceRemovable[1] = 0xff; ++ break; ++ case GetHubStatus: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubStatus\n"); ++ memset (_buf, 0, 4); ++ break; ++ case GetPortStatus: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetPortStatus\n"); ++ ++ if (!_wIndex || _wIndex > 1) ++ goto error; ++ ++ port_status = 0; ++ ++ if (dwc_otg_hcd->flags.b.port_connect_status_change) ++ port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); ++ ++ if (dwc_otg_hcd->flags.b.port_enable_change) ++ port_status |= (1 << USB_PORT_FEAT_C_ENABLE); ++ ++ if (dwc_otg_hcd->flags.b.port_suspend_change) ++ port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); ++ ++ if (dwc_otg_hcd->flags.b.port_reset_change) ++ port_status |= (1 << USB_PORT_FEAT_C_RESET); ++ ++ if (dwc_otg_hcd->flags.b.port_over_current_change) { ++ DWC_ERROR("Device Not Supported\n"); ++ port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT); ++ } ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ printk("DISCONNECTED PORT\n"); ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return 0's for the remainder of the port status ++ * since the port register can't be read if the core ++ * is in device mode. ++ */ ++#if 1 // winder. ++ *((u32 *) _buf) = cpu_to_le32(port_status); ++#else ++ *((__le32 *) _buf) = cpu_to_le32(port_status); ++#endif ++ break; ++ } ++ ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ DWC_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32); ++ ++ if (hprt0.b.prtconnsts) ++ port_status |= (1 << USB_PORT_FEAT_CONNECTION); ++ ++ if (hprt0.b.prtena) ++ port_status |= (1 << USB_PORT_FEAT_ENABLE); ++ ++ if (hprt0.b.prtsusp) ++ port_status |= (1 << USB_PORT_FEAT_SUSPEND); ++ ++ if (hprt0.b.prtovrcurract) ++ port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); ++ ++ if (hprt0.b.prtrst) ++ port_status |= (1 << USB_PORT_FEAT_RESET); ++ ++ if (hprt0.b.prtpwr) ++ port_status |= (1 << USB_PORT_FEAT_POWER); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) ++ port_status |= USB_PORT_STAT_HIGH_SPEED; ++ ++ else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) ++ port_status |= (1 << USB_PORT_FEAT_LOWSPEED); ++ ++ if (hprt0.b.prttstctl) ++ port_status |= (1 << USB_PORT_FEAT_TEST); ++ ++ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ ++#if 1 // winder. ++ *((u32 *) _buf) = cpu_to_le32(port_status); ++#else ++ *((__le32 *) _buf) = cpu_to_le32(port_status); ++#endif ++ ++ break; ++ case SetHubFeature: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetHubFeature\n"); ++ /* No HUB features supported */ ++ break; ++ case SetPortFeature: ++ if (_wValue != USB_PORT_FEAT_TEST && (!_wIndex || _wIndex > 1)) ++ goto error; ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return without doing anything since the port ++ * register can't be written if the core is in device ++ * mode. ++ */ ++ break; ++ } ++ ++ switch (_wValue) { ++ case USB_PORT_FEAT_SUSPEND: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); ++ if (_hcd->self.otg_port == _wIndex ++ && _hcd->self.b_hnp_enable) { ++ gotgctl_data_t gotgctl = {.d32=0}; ++ gotgctl.b.hstsethnpen = 1; ++ dwc_modify_reg32(&core_if->core_global_regs-> ++ gotgctl, 0, gotgctl.d32); ++ core_if->op_state = A_SUSPEND; ++ } ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtsusp = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ //DWC_PRINT( "SUSPEND: HPRT0=%0x\n", hprt0.d32); ++ /* Suspend the Phy Clock */ ++ { ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ pcgcctl.b.stoppclk = 1; ++ dwc_write_reg32(core_if->pcgcctl, pcgcctl.d32); ++ } ++ ++ /* For HNP the bus must be suspended for at least 200ms.*/ ++ if (_hcd->self.b_hnp_enable) { ++ mdelay(200); ++ //DWC_PRINT( "SUSPEND: wait complete! (%d)\n", _hcd->state); ++ } ++ break; ++ case USB_PORT_FEAT_POWER: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_RESET: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_RESET\n"); ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ /* TODO: Is this for OTG protocol?? ++ * We shoudl remove OTG totally for Danube system. ++ * But, in the future, maybe we need this. ++ */ ++#if 1 // winder ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++#else ++ /* When B-Host the Port reset bit is set in ++ * the Start HCD Callback function, so that ++ * the reset is started within 1ms of the HNP ++ * success interrupt. */ ++ if (!_hcd->self.is_b_host) { ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ } ++#endif ++ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ ++ MDELAY (60); ++ hprt0.b.prtrst = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ ++#ifdef DWC_HS_ELECT_TST ++ case USB_PORT_FEAT_TEST: ++ { ++ uint32_t t; ++ gintmsk_data_t gintmsk; ++ ++ t = (_wIndex >> 8); /* MSB wIndex USB */ ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_TEST %d\n", t); ++ printk("USB_PORT_FEAT_TEST %d\n", t); ++ if (t < 6) { ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prttstctl = t; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ } else { ++ /* Setup global vars with reg addresses (quick and ++ * dirty hack, should be cleaned up) ++ */ ++ global_regs = core_if->core_global_regs; ++ hc_global_regs = core_if->host_if->host_global_regs; ++ hc_regs = (dwc_otg_hc_regs_t *)((char *)global_regs + 0x500); ++ data_fifo = (uint32_t *)((char *)global_regs + 0x1000); ++ ++ if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Drive suspend on the root port */ ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtsusp = 1; ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Drive resume on the root port */ ++ hprt0.d32 = dwc_otg_read_hprt0 (core_if); ++ hprt0.b.prtsusp = 0; ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ mdelay(100); ++ ++ /* Clear the resume bit */ ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); ++ } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); ++ } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = dwc_read_reg32(&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Send the In and Ack packets */ ++ do_in_ack(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs->gintmsk, gintmsk.d32); ++ } ++ } ++ break; ++ } ++#endif /* DWC_HS_ELECT_TST */ ++ ++ case USB_PORT_FEAT_INDICATOR: ++ DWC_DEBUGPL (DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); ++ /* Not supported */ ++ break; ++ default: ++ retval = -EINVAL; ++ DWC_ERROR ("DWC OTG HCD - " ++ "SetPortFeature request %xh " ++ "unknown or unsupported\n", _wValue); ++ break; ++ } ++ break; ++ default: ++error: ++ retval = -EINVAL; ++ DWC_WARN ("DWC OTG HCD - " ++ "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", ++ _typeReq, _wIndex, _wValue); ++ break; ++ } ++ ++ return retval; ++} ++ ++ ++/** ++ * Assigns transactions from a QTD to a free host channel and initializes the ++ * host channel to perform the transactions. The host channel is removed from ++ * the free list. ++ * ++ * @param _hcd The HCD state structure. ++ * @param _qh Transactions from the first QTD for this QH are selected and ++ * assigned to a free host channel. ++ */ ++static void assign_and_init_hc(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) ++{ ++ dwc_hc_t *hc; ++ dwc_otg_qtd_t *qtd; ++ struct urb *urb; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, _hcd, _qh); ++ ++ hc = list_entry(_hcd->free_hc_list.next, dwc_hc_t, hc_list_entry); ++ ++ /* Remove the host channel from the free list. */ ++ list_del_init(&hc->hc_list_entry); ++ ++ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); ++ urb = qtd->urb; ++ _qh->channel = hc; ++ _qh->qtd_in_process = qtd; ++ ++ /* ++ * Use usb_pipedevice to determine device address. This address is ++ * 0 before the SET_ADDRESS command and the correct address afterward. ++ */ ++ hc->dev_addr = usb_pipedevice(urb->pipe); ++ hc->ep_num = usb_pipeendpoint(urb->pipe); ++ ++ if (urb->dev->speed == USB_SPEED_LOW) { ++ hc->speed = DWC_OTG_EP_SPEED_LOW; ++ } else if (urb->dev->speed == USB_SPEED_FULL) { ++ hc->speed = DWC_OTG_EP_SPEED_FULL; ++ } else { ++ hc->speed = DWC_OTG_EP_SPEED_HIGH; ++ } ++ hc->max_packet = dwc_max_packet(_qh->maxp); ++ ++ hc->xfer_started = 0; ++ hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS; ++ hc->error_state = (qtd->error_count > 0); ++ hc->halt_on_queue = 0; ++ hc->halt_pending = 0; ++ hc->requests = 0; ++ ++ /* ++ * The following values may be modified in the transfer type section ++ * below. The xfer_len value may be reduced when the transfer is ++ * started to accommodate the max widths of the XferSize and PktCnt ++ * fields in the HCTSIZn register. ++ */ ++ hc->do_ping = _qh->ping_state; ++ hc->ep_is_in = (usb_pipein(urb->pipe) != 0); ++ hc->data_pid_start = _qh->data_toggle; ++ hc->multi_count = 1; ++ ++ if (_hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)(u32)urb->transfer_dma + urb->actual_length; ++ } else { ++ hc->xfer_buff = (uint8_t *)urb->transfer_buffer + urb->actual_length; ++ } ++ hc->xfer_len = urb->transfer_buffer_length - urb->actual_length; ++ hc->xfer_count = 0; ++ ++ /* ++ * Set the split attributes ++ */ ++ hc->do_split = 0; ++ if (_qh->do_split) { ++ hc->do_split = 1; ++ hc->xact_pos = qtd->isoc_split_pos; ++ hc->complete_split = qtd->complete_split; ++ hc->hub_addr = urb->dev->tt->hub->devnum; ++ hc->port_addr = urb->dev->ttport; ++ } ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ hc->ep_type = DWC_OTG_EP_TYPE_CONTROL; ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n"); ++ hc->do_ping = 0; ++ hc->ep_is_in = 0; ++ hc->data_pid_start = DWC_OTG_HC_PID_SETUP; ++ if (_hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)(u32)urb->setup_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *)urb->setup_packet; ++ } ++ hc->xfer_len = 8; ++ break; ++ case DWC_OTG_CONTROL_DATA: ++ DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); ++ hc->data_pid_start = qtd->data_toggle; ++ break; ++ case DWC_OTG_CONTROL_STATUS: ++ /* ++ * Direction is opposite of data direction or IN if no ++ * data. ++ */ ++ DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n"); ++ if (urb->transfer_buffer_length == 0) { ++ hc->ep_is_in = 1; ++ } else { ++ hc->ep_is_in = (usb_pipein(urb->pipe) != USB_DIR_IN); ++ } ++ if (hc->ep_is_in) { ++ hc->do_ping = 0; ++ } ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ hc->xfer_len = 0; ++ if (_hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)_hcd->status_buf_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *)_hcd->status_buf; ++ } ++ break; ++ } ++ break; ++ case PIPE_BULK: ++ hc->ep_type = DWC_OTG_EP_TYPE_BULK; ++ break; ++ case PIPE_INTERRUPT: ++ hc->ep_type = DWC_OTG_EP_TYPE_INTR; ++ break; ++ case PIPE_ISOCHRONOUS: ++ { ++ struct usb_iso_packet_descriptor *frame_desc; ++ frame_desc = &urb->iso_frame_desc[qtd->isoc_frame_index]; ++ hc->ep_type = DWC_OTG_EP_TYPE_ISOC; ++ if (_hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *)(u32)urb->transfer_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *)urb->transfer_buffer; ++ } ++ hc->xfer_buff += frame_desc->offset + qtd->isoc_split_offset; ++ hc->xfer_len = frame_desc->length - qtd->isoc_split_offset; ++ ++ if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ if (hc->xfer_len <= 188) { ++ hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ } ++ else { ++ hc->xact_pos = DWC_HCSPLIT_XACTPOS_BEGIN; ++ } ++ } ++ } ++ break; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This value may be modified when the transfer is started to ++ * reflect the actual transfer length. ++ */ ++ hc->multi_count = dwc_hb_mult(_qh->maxp); ++ } ++ ++ dwc_otg_hc_init(_hcd->core_if, hc); ++ hc->qh = _qh; ++} ++#define DEBUG_HOST_CHANNELS ++#ifdef DEBUG_HOST_CHANNELS ++static int last_sel_trans_num_per_scheduled = 0; ++module_param(last_sel_trans_num_per_scheduled, int, 0444); ++ ++static int last_sel_trans_num_nonper_scheduled = 0; ++module_param(last_sel_trans_num_nonper_scheduled, int, 0444); ++ ++static int last_sel_trans_num_avail_hc_at_start = 0; ++module_param(last_sel_trans_num_avail_hc_at_start, int, 0444); ++ ++static int last_sel_trans_num_avail_hc_at_end = 0; ++module_param(last_sel_trans_num_avail_hc_at_end, int, 0444); ++#endif /* DEBUG_HOST_CHANNELS */ ++ ++/** ++ * This function selects transactions from the HCD transfer schedule and ++ * assigns them to available host channels. It is called from HCD interrupt ++ * handler functions. ++ * ++ * @param _hcd The HCD state structure. ++ * ++ * @return The types of new transactions that were assigned to host channels. ++ */ ++dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *_hcd) ++{ ++ struct list_head *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int num_channels; ++ unsigned long flags; ++ dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); ++#endif /* */ ++ ++#ifdef DEBUG_HOST_CHANNELS ++ last_sel_trans_num_per_scheduled = 0; ++ last_sel_trans_num_nonper_scheduled = 0; ++ last_sel_trans_num_avail_hc_at_start = _hcd->available_host_channels; ++#endif /* DEBUG_HOST_CHANNELS */ ++ ++ /* Process entries in the periodic ready list. */ ++ num_channels = _hcd->core_if->core_params->host_channels; ++ qh_ptr = _hcd->periodic_sched_ready.next; ++ while (qh_ptr != &_hcd->periodic_sched_ready ++ && !list_empty(&_hcd->free_hc_list)) { ++ ++ // Make sure we leave one channel for non periodic transactions. ++ local_irq_save(flags); ++ if (_hcd->available_host_channels <= 1) { ++ local_irq_restore(flags); ++ break; ++ } ++ _hcd->available_host_channels--; ++ local_irq_restore(flags); ++#ifdef DEBUG_HOST_CHANNELS ++ last_sel_trans_num_per_scheduled++; ++#endif /* DEBUG_HOST_CHANNELS */ ++ ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ assign_and_init_hc(_hcd, qh); ++ ++ /* ++ * Move the QH from the periodic ready schedule to the ++ * periodic assigned schedule. ++ */ ++ qh_ptr = qh_ptr->next; ++ local_irq_save(flags); ++ list_move(&qh->qh_list_entry, &_hcd->periodic_sched_assigned); ++ local_irq_restore(flags); ++ ret_val = DWC_OTG_TRANSACTION_PERIODIC; ++ } ++ ++ /* ++ * Process entries in the deferred portion of the non-periodic list. ++ * A NAK put them here and, at the right time, they need to be ++ * placed on the sched_inactive list. ++ */ ++ qh_ptr = _hcd->non_periodic_sched_deferred.next; ++ while (qh_ptr != &_hcd->non_periodic_sched_deferred) { ++ uint16_t frame_number = ++ dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(_hcd)); ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ qh_ptr = qh_ptr->next; ++ ++ if (dwc_frame_num_le(qh->sched_frame, frame_number)) { ++ // NAK did this ++ /* ++ * Move the QH from the non periodic deferred schedule to ++ * the non periodic inactive schedule. ++ */ ++ local_irq_save(flags); ++ list_move(&qh->qh_list_entry, ++ &_hcd->non_periodic_sched_inactive); ++ local_irq_restore(flags); ++ } ++ } ++ ++ /* ++ * Process entries in the inactive portion of the non-periodic ++ * schedule. Some free host channels may not be used if they are ++ * reserved for periodic transfers. ++ */ ++ qh_ptr = _hcd->non_periodic_sched_inactive.next; ++ num_channels = _hcd->core_if->core_params->host_channels; ++ while (qh_ptr != &_hcd->non_periodic_sched_inactive ++ && !list_empty(&_hcd->free_hc_list)) { ++ ++ local_irq_save(flags); ++ if (_hcd->available_host_channels < 1) { ++ local_irq_restore(flags); ++ break; ++ } ++ _hcd->available_host_channels--; ++ local_irq_restore(flags); ++#ifdef DEBUG_HOST_CHANNELS ++ last_sel_trans_num_nonper_scheduled++; ++#endif /* DEBUG_HOST_CHANNELS */ ++ ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ assign_and_init_hc(_hcd, qh); ++ ++ /* ++ * Move the QH from the non-periodic inactive schedule to the ++ * non-periodic active schedule. ++ */ ++ qh_ptr = qh_ptr->next; ++ local_irq_save(flags); ++ list_move(&qh->qh_list_entry, &_hcd->non_periodic_sched_active); ++ local_irq_restore(flags); ++ ++ if (ret_val == DWC_OTG_TRANSACTION_NONE) { ++ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; ++ } else { ++ ret_val = DWC_OTG_TRANSACTION_ALL; ++ } ++ ++ } ++#ifdef DEBUG_HOST_CHANNELS ++ last_sel_trans_num_avail_hc_at_end = _hcd->available_host_channels; ++#endif /* DEBUG_HOST_CHANNELS */ ++ ++ return ret_val; ++} ++ ++/** ++ * Attempts to queue a single transaction request for a host channel ++ * associated with either a periodic or non-periodic transfer. This function ++ * assumes that there is space available in the appropriate request queue. For ++ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space ++ * is available in the appropriate Tx FIFO. ++ * ++ * @param _hcd The HCD state structure. ++ * @param _hc Host channel descriptor associated with either a periodic or ++ * non-periodic transfer. ++ * @param _fifo_dwords_avail Number of DWORDs available in the periodic Tx ++ * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic ++ * transfers. ++ * ++ * @return 1 if a request is queued and more requests may be needed to ++ * complete the transfer, 0 if no more requests are required for this ++ * transfer, -1 if there is insufficient space in the Tx FIFO. ++ */ ++static int queue_transaction(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ uint16_t _fifo_dwords_avail) ++{ ++ int retval; ++ ++ if (_hcd->core_if->dma_enable) { ++ if (!_hc->xfer_started) { ++ dwc_otg_hc_start_transfer(_hcd->core_if, _hc); ++ _hc->qh->ping_state = 0; ++ } ++ retval = 0; ++ } else if (_hc->halt_pending) { ++ /* Don't queue a request if the channel has been halted. */ ++ retval = 0; ++ } else if (_hc->halt_on_queue) { ++ dwc_otg_hc_halt(_hcd->core_if, _hc, _hc->halt_status); ++ retval = 0; ++ } else if (_hc->do_ping) { ++ if (!_hc->xfer_started) { ++ dwc_otg_hc_start_transfer(_hcd->core_if, _hc); ++ } ++ retval = 0; ++ } else if (!_hc->ep_is_in || ++ _hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ if ((_fifo_dwords_avail * 4) >= _hc->max_packet) { ++ if (!_hc->xfer_started) { ++ dwc_otg_hc_start_transfer(_hcd->core_if, _hc); ++ retval = 1; ++ } else { ++ retval = dwc_otg_hc_continue_transfer(_hcd->core_if, _hc); ++ } ++ } else { ++ retval = -1; ++ } ++ } else { ++ if (!_hc->xfer_started) { ++ dwc_otg_hc_start_transfer(_hcd->core_if, _hc); ++ retval = 1; ++ } else { ++ retval = dwc_otg_hc_continue_transfer(_hcd->core_if, _hc); ++ } ++ } ++ ++ return retval; ++} ++ ++/** ++ * Processes active non-periodic channels and queues transactions for these ++ * channels to the DWC_otg controller. After queueing transactions, the NP Tx ++ * FIFO Empty interrupt is enabled if there are more transactions to queue as ++ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx ++ * FIFO Empty interrupt is disabled. ++ */ ++static void process_non_periodic_channels(dwc_otg_hcd_t *_hcd) ++{ ++ gnptxsts_data_t tx_status; ++ struct list_head *orig_qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ int more_to_do = 0; ++ ++ dwc_otg_core_global_regs_t *global_regs = _hcd->core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ /* ++ * Keep track of the starting point. Skip over the start-of-list ++ * entry. ++ */ ++ if (_hcd->non_periodic_qh_ptr == &_hcd->non_periodic_sched_active) { ++ _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; ++ } ++ orig_qh_ptr = _hcd->non_periodic_qh_ptr; ++ ++ /* ++ * Process once through the active list or until no more space is ++ * available in the request queue or the Tx FIFO. ++ */ ++ do { ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ if (!_hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = list_entry(_hcd->non_periodic_qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ status = queue_transaction(_hcd, qh->channel, tx_status.b.nptxfspcavail); ++ ++ if (status > 0) { ++ more_to_do = 1; ++ } else if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* Advance to next QH, skipping start-of-list entry. */ ++ _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; ++ if (_hcd->non_periodic_qh_ptr == &_hcd->non_periodic_sched_active) { ++ _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; ++ } ++ ++ } while (_hcd->non_periodic_qh_ptr != orig_qh_ptr); ++ ++ if (!_hcd->core_if->dma_enable) { ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ intr_mask.b.nptxfempty = 1; ++ ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ if (more_to_do || no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the non-periodic ++ * Tx FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ } ++} ++ ++/** ++ * Processes periodic channels for the next frame and queues transactions for ++ * these channels to the DWC_otg controller. After queueing transactions, the ++ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions ++ * to queue as Periodic Tx FIFO or request queue space becomes available. ++ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled. ++ */ ++static void process_periodic_channels(dwc_otg_hcd_t *_hcd) ++{ ++ hptxsts_data_t tx_status; ++ struct list_head *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ ++ dwc_otg_host_global_regs_t *host_regs; ++ host_regs = _hcd->core_if->host_if->host_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ ++ qh_ptr = _hcd->periodic_sched_assigned.next; ++ while (qh_ptr != &_hcd->periodic_sched_assigned) { ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ if (tx_status.b.ptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = list_entry(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ ++ /* ++ * Set a flag if we're queuing high-bandwidth in slave mode. ++ * The flag prevents any halts to get into the request queue in ++ * the middle of multiple high-bandwidth packets getting queued. ++ */ ++ if ((!_hcd->core_if->dma_enable) && ++ (qh->channel->multi_count > 1)) ++ { ++ _hcd->core_if->queuing_high_bandwidth = 1; ++ } ++ ++ status = queue_transaction(_hcd, qh->channel, tx_status.b.ptxfspcavail); ++ if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* ++ * In Slave mode, stay on the current transfer until there is ++ * nothing more to do or the high-bandwidth request count is ++ * reached. In DMA mode, only need to queue one request. The ++ * controller automatically handles multiple packets for ++ * high-bandwidth transfers. ++ */ ++ if (_hcd->core_if->dma_enable || ++ (status == 0 || ++ qh->channel->requests == qh->channel->multi_count)) { ++ qh_ptr = qh_ptr->next; ++ /* ++ * Move the QH from the periodic assigned schedule to ++ * the periodic queued schedule. ++ */ ++ list_move(&qh->qh_list_entry, &_hcd->periodic_sched_queued); ++ ++ /* done queuing high bandwidth */ ++ _hcd->core_if->queuing_high_bandwidth = 0; ++ } ++ } ++ ++ if (!_hcd->core_if->dma_enable) { ++ dwc_otg_core_global_regs_t *global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0}; ++ ++ global_regs = _hcd->core_if->core_global_regs; ++ intr_mask.b.ptxfempty = 1; ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ if (!(list_empty(&_hcd->periodic_sched_assigned)) || ++ no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the periodic Tx ++ * FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ } ++} ++ ++/** ++ * This function processes the currently active host channels and queues ++ * transactions for these channels to the DWC_otg controller. It is called ++ * from HCD interrupt handler functions. ++ * ++ * @param _hcd The HCD state structure. ++ * @param _tr_type The type(s) of transactions to queue (non-periodic, ++ * periodic, or both). ++ */ ++void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *_hcd, ++ dwc_otg_transaction_type_e _tr_type) ++{ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n"); ++#endif ++ /* Process host channels associated with periodic transfers. */ ++ if ((_tr_type == DWC_OTG_TRANSACTION_PERIODIC || ++ _tr_type == DWC_OTG_TRANSACTION_ALL) && ++ !list_empty(&_hcd->periodic_sched_assigned)) { ++ ++ process_periodic_channels(_hcd); ++ } ++ ++ /* Process host channels associated with non-periodic transfers. */ ++ if ((_tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC || ++ _tr_type == DWC_OTG_TRANSACTION_ALL)) { ++ if (!list_empty(&_hcd->non_periodic_sched_active)) { ++ process_non_periodic_channels(_hcd); ++ } else { ++ /* ++ * Ensure NP Tx FIFO empty interrupt is disabled when ++ * there are no non-periodic transfers to process. ++ */ ++ gintmsk_data_t gintmsk = {.d32 = 0}; ++ gintmsk.b.nptxfempty = 1; ++ dwc_modify_reg32(&_hcd->core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ } ++ } ++} ++ ++/** ++ * Sets the final status of an URB and returns it to the device driver. Any ++ * required cleanup of the URB is performed. ++ */ ++void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t * _hcd, struct urb *_urb, ++ int _status) ++ __releases(_hcd->lock) ++__acquires(_hcd->lock) ++{ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINT("%s: urb %p, device %d, ep %d %s, status=%d\n", ++ __func__, _urb, usb_pipedevice(_urb->pipe), ++ usb_pipeendpoint(_urb->pipe), ++ usb_pipein(_urb->pipe) ? "IN" : "OUT", _status); ++ if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < _urb->number_of_packets; i++) { ++ DWC_PRINT(" ISO Desc %d status: %d\n", ++ i, _urb->iso_frame_desc[i].status); ++ } ++ } ++ } ++#endif ++ ++ _urb->status = _status; ++ _urb->hcpriv = NULL; ++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(_hcd), _urb); ++ spin_unlock(&_hcd->lock); ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(_hcd), _urb, _status); ++ spin_lock(&_hcd->lock); ++} ++ ++/* ++ * Returns the Queue Head for an URB. ++ */ ++dwc_otg_qh_t *dwc_urb_to_qh(struct urb *_urb) ++{ ++ struct usb_host_endpoint *ep = dwc_urb_to_endpoint(_urb); ++ return (dwc_otg_qh_t *)ep->hcpriv; ++} ++ ++#ifdef DEBUG ++void dwc_print_setup_data (uint8_t *setup) ++{ ++ int i; ++ if (CHK_DEBUG_LEVEL(DBG_HCD)){ ++ DWC_PRINT("Setup Data = MSB "); ++ for (i=7; i>=0; i--) DWC_PRINT ("%02x ", setup[i]); ++ DWC_PRINT("\n"); ++ DWC_PRINT(" bmRequestType Tranfer = %s\n", (setup[0]&0x80) ? "Device-to-Host" : "Host-to-Device"); ++ DWC_PRINT(" bmRequestType Type = "); ++ switch ((setup[0]&0x60) >> 5) { ++ case 0: DWC_PRINT("Standard\n"); break; ++ case 1: DWC_PRINT("Class\n"); break; ++ case 2: DWC_PRINT("Vendor\n"); break; ++ case 3: DWC_PRINT("Reserved\n"); break; ++ } ++ DWC_PRINT(" bmRequestType Recipient = "); ++ switch (setup[0]&0x1f) { ++ case 0: DWC_PRINT("Device\n"); break; ++ case 1: DWC_PRINT("Interface\n"); break; ++ case 2: DWC_PRINT("Endpoint\n"); break; ++ case 3: DWC_PRINT("Other\n"); break; ++ default: DWC_PRINT("Reserved\n"); break; ++ } ++ DWC_PRINT(" bRequest = 0x%0x\n", setup[1]); ++ DWC_PRINT(" wValue = 0x%0x\n", *((uint16_t *)&setup[2])); ++ DWC_PRINT(" wIndex = 0x%0x\n", *((uint16_t *)&setup[4])); ++ DWC_PRINT(" wLength = 0x%0x\n\n", *((uint16_t *)&setup[6])); ++ } ++} ++#endif ++ ++void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *_hcd) { ++#ifdef DEBUG ++#if 0 ++ DWC_PRINT("Frame remaining at SOF:\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->frrem_samples, _hcd->frrem_accum, ++ (_hcd->frrem_samples > 0) ? ++ _hcd->frrem_accum/_hcd->frrem_samples : 0); ++ ++ DWC_PRINT("\n"); ++ DWC_PRINT("Frame remaining at start_transfer (uframe 7):\n"); ++ DWC_PRINT(" samples %u, accum %u, avg %u\n", ++ _hcd->core_if->hfnum_7_samples, _hcd->core_if->hfnum_7_frrem_accum, ++ (_hcd->core_if->hfnum_7_samples > 0) ? ++ _hcd->core_if->hfnum_7_frrem_accum/_hcd->core_if->hfnum_7_samples : 0); ++ DWC_PRINT("Frame remaining at start_transfer (uframe 0):\n"); ++ DWC_PRINT(" samples %u, accum %u, avg %u\n", ++ _hcd->core_if->hfnum_0_samples, _hcd->core_if->hfnum_0_frrem_accum, ++ (_hcd->core_if->hfnum_0_samples > 0) ? ++ _hcd->core_if->hfnum_0_frrem_accum/_hcd->core_if->hfnum_0_samples : 0); ++ DWC_PRINT("Frame remaining at start_transfer (uframe 1-6):\n"); ++ DWC_PRINT(" samples %u, accum %u, avg %u\n", ++ _hcd->core_if->hfnum_other_samples, _hcd->core_if->hfnum_other_frrem_accum, ++ (_hcd->core_if->hfnum_other_samples > 0) ? ++ _hcd->core_if->hfnum_other_frrem_accum/_hcd->core_if->hfnum_other_samples : 0); ++ ++ DWC_PRINT("\n"); ++ DWC_PRINT("Frame remaining at sample point A (uframe 7):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->hfnum_7_samples_a, _hcd->hfnum_7_frrem_accum_a, ++ (_hcd->hfnum_7_samples_a > 0) ? ++ _hcd->hfnum_7_frrem_accum_a/_hcd->hfnum_7_samples_a : 0); ++ DWC_PRINT("Frame remaining at sample point A (uframe 0):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->hfnum_0_samples_a, _hcd->hfnum_0_frrem_accum_a, ++ (_hcd->hfnum_0_samples_a > 0) ? ++ _hcd->hfnum_0_frrem_accum_a/_hcd->hfnum_0_samples_a : 0); ++ DWC_PRINT("Frame remaining at sample point A (uframe 1-6):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->hfnum_other_samples_a, _hcd->hfnum_other_frrem_accum_a, ++ (_hcd->hfnum_other_samples_a > 0) ? ++ _hcd->hfnum_other_frrem_accum_a/_hcd->hfnum_other_samples_a : 0); ++ ++ DWC_PRINT("\n"); ++ DWC_PRINT("Frame remaining at sample point B (uframe 7):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->hfnum_7_samples_b, _hcd->hfnum_7_frrem_accum_b, ++ (_hcd->hfnum_7_samples_b > 0) ? ++ _hcd->hfnum_7_frrem_accum_b/_hcd->hfnum_7_samples_b : 0); ++ DWC_PRINT("Frame remaining at sample point B (uframe 0):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->hfnum_0_samples_b, _hcd->hfnum_0_frrem_accum_b, ++ (_hcd->hfnum_0_samples_b > 0) ? ++ _hcd->hfnum_0_frrem_accum_b/_hcd->hfnum_0_samples_b : 0); ++ DWC_PRINT("Frame remaining at sample point B (uframe 1-6):\n"); ++ DWC_PRINT(" samples %u, accum %llu, avg %llu\n", ++ _hcd->hfnum_other_samples_b, _hcd->hfnum_other_frrem_accum_b, ++ (_hcd->hfnum_other_samples_b > 0) ? ++ _hcd->hfnum_other_frrem_accum_b/_hcd->hfnum_other_samples_b : 0); ++#endif ++#endif ++} ++ ++void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *_hcd) ++{ ++#ifdef DEBUG ++ int num_channels; ++ int i; ++ gnptxsts_data_t np_tx_status; ++ hptxsts_data_t p_tx_status; ++ ++ num_channels = _hcd->core_if->core_params->host_channels; ++ DWC_PRINT("\n"); ++ DWC_PRINT("************************************************************\n"); ++ DWC_PRINT("HCD State:\n"); ++ DWC_PRINT(" Num channels: %d\n", num_channels); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = _hcd->hc_ptr_array[i]; ++ DWC_PRINT(" Channel %d:\n", i); ++ DWC_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINT(" speed: %d\n", hc->speed); ++ DWC_PRINT(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINT(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINT(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINT(" multi_count: %d\n", hc->multi_count); ++ DWC_PRINT(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINT(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINT(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINT(" xfer_count: %d\n", hc->xfer_count); ++ DWC_PRINT(" halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_PRINT(" halt_pending: %d\n", hc->halt_pending); ++ DWC_PRINT(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINT(" do_split: %d\n", hc->do_split); ++ DWC_PRINT(" complete_split: %d\n", hc->complete_split); ++ DWC_PRINT(" hub_addr: %d\n", hc->hub_addr); ++ DWC_PRINT(" port_addr: %d\n", hc->port_addr); ++ DWC_PRINT(" xact_pos: %d\n", hc->xact_pos); ++ DWC_PRINT(" requests: %d\n", hc->requests); ++ DWC_PRINT(" qh: %p\n", hc->qh); ++ if (hc->xfer_started) { ++ hfnum_data_t hfnum; ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); ++ hcchar.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hcchar); ++ hctsiz.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hctsiz); ++ hcint.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&_hcd->core_if->host_if->hc_regs[i]->hcintmsk); ++ DWC_PRINT(" hfnum: 0x%08x\n", hfnum.d32); ++ DWC_PRINT(" hcchar: 0x%08x\n", hcchar.d32); ++ DWC_PRINT(" hctsiz: 0x%08x\n", hctsiz.d32); ++ DWC_PRINT(" hcint: 0x%08x\n", hcint.d32); ++ DWC_PRINT(" hcintmsk: 0x%08x\n", hcintmsk.d32); ++ } ++ if (hc->xfer_started && (hc->qh != NULL) && (hc->qh->qtd_in_process != NULL)) { ++ dwc_otg_qtd_t *qtd; ++ struct urb *urb; ++ qtd = hc->qh->qtd_in_process; ++ urb = qtd->urb; ++ DWC_PRINT(" URB Info:\n"); ++ DWC_PRINT(" qtd: %p, urb: %p\n", qtd, urb); ++ if (urb != NULL) { ++ DWC_PRINT(" Dev: %d, EP: %d %s\n", ++ usb_pipedevice(urb->pipe), usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "IN" : "OUT"); ++ DWC_PRINT(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_PRINT(" transfer_buffer: %p\n", urb->transfer_buffer); ++ DWC_PRINT(" transfer_dma: %p\n", (void *)urb->transfer_dma); ++ DWC_PRINT(" transfer_buffer_length: %d\n", urb->transfer_buffer_length); ++ DWC_PRINT(" actual_length: %d\n", urb->actual_length); ++ } ++ } ++ } ++ //DWC_PRINT(" non_periodic_channels: %d\n", _hcd->non_periodic_channels); ++ //DWC_PRINT(" periodic_channels: %d\n", _hcd->periodic_channels); ++ DWC_PRINT(" available_channels: %d\n", _hcd->available_host_channels); ++ DWC_PRINT(" periodic_usecs: %d\n", _hcd->periodic_usecs); ++ np_tx_status.d32 = dwc_read_reg32(&_hcd->core_if->core_global_regs->gnptxsts); ++ DWC_PRINT(" NP Tx Req Queue Space Avail: %d\n", np_tx_status.b.nptxqspcavail); ++ DWC_PRINT(" NP Tx FIFO Space Avail: %d\n", np_tx_status.b.nptxfspcavail); ++ p_tx_status.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hptxsts); ++ DWC_PRINT(" P Tx Req Queue Space Avail: %d\n", p_tx_status.b.ptxqspcavail); ++ DWC_PRINT(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail); ++ dwc_otg_hcd_dump_frrem(_hcd); ++ dwc_otg_dump_global_registers(_hcd->core_if); ++ dwc_otg_dump_host_registers(_hcd->core_if); ++ DWC_PRINT("************************************************************\n"); ++ DWC_PRINT("\n"); ++#endif ++} ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd.h b/drivers/usb/dwc_otg/dwc_otg_hcd.h +new file mode 100644 +index 0000000..8a20dff +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd.h +@@ -0,0 +1,676 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd.h $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 537387 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++#if !defined(__DWC_HCD_H__) ++#define __DWC_HCD_H__ ++ ++#include ++#include ++#include ++ ++struct lm_device; ++struct dwc_otg_device; ++ ++#include "dwc_otg_cil.h" ++//#include "dwc_otg_ifx.h" // winder ++ ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Host Contoller Driver (HCD). ++ * ++ * The Host Controller Driver (HCD) is responsible for translating requests ++ * from the USB Driver into the appropriate actions on the DWC_otg controller. ++ * It isolates the USBD from the specifics of the controller by providing an ++ * API to the USBD. ++ */ ++ ++/** ++ * Phases for control transfers. ++ */ ++typedef enum dwc_otg_control_phase { ++ DWC_OTG_CONTROL_SETUP, ++ DWC_OTG_CONTROL_DATA, ++ DWC_OTG_CONTROL_STATUS ++} dwc_otg_control_phase_e; ++ ++/** Transaction types. */ ++typedef enum dwc_otg_transaction_type { ++ DWC_OTG_TRANSACTION_NONE, ++ DWC_OTG_TRANSACTION_PERIODIC, ++ DWC_OTG_TRANSACTION_NON_PERIODIC, ++ DWC_OTG_TRANSACTION_ALL ++} dwc_otg_transaction_type_e; ++ ++/** ++ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control, ++ * interrupt, or isochronous transfer. A single QTD is created for each URB ++ * (of one of these types) submitted to the HCD. The transfer associated with ++ * a QTD may require one or multiple transactions. ++ * ++ * A QTD is linked to a Queue Head, which is entered in either the ++ * non-periodic or periodic schedule for execution. When a QTD is chosen for ++ * execution, some or all of its transactions may be executed. After ++ * execution, the state of the QTD is updated. The QTD may be retired if all ++ * its transactions are complete or if an error occurred. Otherwise, it ++ * remains in the schedule so more transactions can be executed later. ++ */ ++struct dwc_otg_qh; ++typedef struct dwc_otg_qtd { ++ /** ++ * Determines the PID of the next data packet for the data phase of ++ * control transfers. Ignored for other transfer types.
++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Current phase for control transfers (Setup, Data, or Status). */ ++ dwc_otg_control_phase_e control_phase; ++ ++ /** Keep track of the current split type ++ * for FS/LS endpoints on a HS Hub */ ++ uint8_t complete_split; ++ ++ /** How many bytes transferred during SSPLIT OUT */ ++ uint32_t ssplit_out_xfer_count; ++ ++ /** ++ * Holds the number of bus errors that have occurred for a transaction ++ * within this transfer. ++ */ ++ uint8_t error_count; ++ ++ /** ++ * Index of the next frame descriptor for an isochronous transfer. A ++ * frame descriptor describes the buffer position and length of the ++ * data to be transferred in the next scheduled (micro)frame of an ++ * isochronous transfer. It also holds status for that transaction. ++ * The frame index starts at 0. ++ */ ++ int isoc_frame_index; ++ ++ /** Position of the ISOC split on full/low speed */ ++ uint8_t isoc_split_pos; ++ ++ /** Position of the ISOC split in the buffer for the current frame */ ++ uint16_t isoc_split_offset; ++ ++ /** URB for this transfer */ ++ struct urb *urb; ++ ++ /** This list of QTDs */ ++ struct list_head qtd_list_entry; ++ ++ /* Field to track the qh pointer */ ++ struct dwc_otg_qh *qtd_qh_ptr; ++} dwc_otg_qtd_t; ++ ++/** ++ * A Queue Head (QH) holds the static characteristics of an endpoint and ++ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may ++ * be entered in either the non-periodic or periodic schedule. ++ */ ++typedef struct dwc_otg_qh { ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - USB_ENDPOINT_XFER_CONTROL ++ * - USB_ENDPOINT_XFER_ISOC ++ * - USB_ENDPOINT_XFER_BULK ++ * - USB_ENDPOINT_XFER_INT ++ */ ++ uint8_t ep_type; ++ uint8_t ep_is_in; ++ ++ /** wMaxPacketSize Field of Endpoint Descriptor. */ ++ uint16_t maxp; ++ ++ /** ++ * Determines the PID of the next data packet for non-control ++ * transfers. Ignored for control transfers.
++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Ping state if 1. */ ++ uint8_t ping_state; ++ ++ /** ++ * List of QTDs for this QH. ++ */ ++ struct list_head qtd_list; ++ ++ /** Host channel currently processing transfers for this QH. */ ++ dwc_hc_t *channel; ++ ++ /** QTD currently assigned to a host channel for this QH. */ ++ dwc_otg_qtd_t *qtd_in_process; ++ ++ /** Full/low speed endpoint on high-speed hub requires split. */ ++ uint8_t do_split; ++ ++ /** @name Periodic schedule information */ ++ /** @{ */ ++ ++ /** Bandwidth in microseconds per (micro)frame. */ ++ uint8_t usecs; ++ ++ /** Interval between transfers in (micro)frames. */ ++ uint16_t interval; ++ ++ /** ++ * (micro)frame to initialize a periodic transfer. The transfer ++ * executes in the following (micro)frame. ++ */ ++ uint16_t sched_frame; ++ ++ /** (micro)frame at which last start split was initialized. */ ++ uint16_t start_split_frame; ++ ++ /** @} */ ++ ++ uint16_t speed; ++ uint16_t frame_usecs[8]; ++ /** Entry for QH in either the periodic or non-periodic schedule. */ ++ struct list_head qh_list_entry; ++} dwc_otg_qh_t; ++ ++/** ++ * This structure holds the state of the HCD, including the non-periodic and ++ * periodic schedules. ++ */ ++typedef struct dwc_otg_hcd { ++ spinlock_t lock; ++ ++ /** DWC OTG Core Interface Layer */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Internal DWC HCD Flags */ ++ volatile union dwc_otg_hcd_internal_flags { ++ uint32_t d32; ++ struct { ++ unsigned port_connect_status_change : 1; ++ unsigned port_connect_status : 1; ++ unsigned port_reset_change : 1; ++ unsigned port_enable_change : 1; ++ unsigned port_suspend_change : 1; ++ unsigned port_over_current_change : 1; ++ unsigned reserved : 27; ++ } b; ++ } flags; ++ ++ /** ++ * Inactive items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are not ++ * currently assigned to a host channel. ++ */ ++ struct list_head non_periodic_sched_inactive; ++ ++ /** ++ * Deferred items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are not ++ * currently assigned to a host channel. ++ * When we get an NAK, the QH goes here. ++ */ ++ struct list_head non_periodic_sched_deferred; ++ ++ /** ++ * Active items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are ++ * currently assigned to a host channel. ++ */ ++ struct list_head non_periodic_sched_active; ++ ++ /** ++ * Pointer to the next Queue Head to process in the active ++ * non-periodic schedule. ++ */ ++ struct list_head *non_periodic_qh_ptr; ++ ++ /** ++ * Inactive items in the periodic schedule. This is a list of QHs for ++ * periodic transfers that are _not_ scheduled for the next frame. ++ * Each QH in the list has an interval counter that determines when it ++ * needs to be scheduled for execution. This scheduling mechanism ++ * allows only a simple calculation for periodic bandwidth used (i.e. ++ * must assume that all periodic transfers may need to execute in the ++ * same frame). However, it greatly simplifies scheduling and should ++ * be sufficient for the vast majority of OTG hosts, which need to ++ * connect to a small number of peripherals at one time. ++ * ++ * Items move from this list to periodic_sched_ready when the QH ++ * interval counter is 0 at SOF. ++ */ ++ struct list_head periodic_sched_inactive; ++ ++ /** ++ * List of periodic QHs that are ready for execution in the next ++ * frame, but have not yet been assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_assigned as host ++ * channels become available during the current frame. ++ */ ++ struct list_head periodic_sched_ready; ++ ++ /** ++ * List of periodic QHs to be executed in the next frame that are ++ * assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_queued as the ++ * transactions for the QH are queued to the DWC_otg controller. ++ */ ++ struct list_head periodic_sched_assigned; ++ ++ /** ++ * List of periodic QHs that have been queued for execution. ++ * ++ * Items move from this list to either periodic_sched_inactive or ++ * periodic_sched_ready when the channel associated with the transfer ++ * is released. If the interval for the QH is 1, the item moves to ++ * periodic_sched_ready because it must be rescheduled for the next ++ * frame. Otherwise, the item moves to periodic_sched_inactive. ++ */ ++ struct list_head periodic_sched_queued; ++ ++ /** ++ * Total bandwidth claimed so far for periodic transfers. This value ++ * is in microseconds per (micro)frame. The assumption is that all ++ * periodic transfers may occur in the same (micro)frame. ++ */ ++ uint16_t periodic_usecs; ++ ++ /** ++ * Total bandwidth claimed so far for all periodic transfers ++ * in a frame. ++ * This will include a mixture of HS and FS transfers. ++ * Units are microseconds per (micro)frame. ++ * We have a budget per frame and have to schedule ++ * transactions accordingly. ++ * Watch out for the fact that things are actually scheduled for the ++ * "next frame". ++ */ ++ uint16_t frame_usecs[8]; ++ ++ /** ++ * Frame number read from the core at SOF. The value ranges from 0 to ++ * DWC_HFNUM_MAX_FRNUM. ++ */ ++ uint16_t frame_number; ++ ++ /** ++ * Free host channels in the controller. This is a list of ++ * dwc_hc_t items. ++ */ ++ struct list_head free_hc_list; ++ ++ /** ++ * Number of available host channels. ++ */ ++ int available_host_channels; ++ ++ /** ++ * Array of pointers to the host channel descriptors. Allows accessing ++ * a host channel descriptor given the host channel number. This is ++ * useful in interrupt handlers. ++ */ ++ dwc_hc_t *hc_ptr_array[MAX_EPS_CHANNELS]; ++ ++ /** ++ * Buffer to use for any data received during the status phase of a ++ * control transfer. Normally no data is transferred during the status ++ * phase. This buffer is used as a bit bucket. ++ */ ++ uint8_t *status_buf; ++ ++ /** ++ * DMA address for status_buf. ++ */ ++ dma_addr_t status_buf_dma; ++#define DWC_OTG_HCD_STATUS_BUF_SIZE 64 ++ ++ /** ++ * Structure to allow starting the HCD in a non-interrupt context ++ * during an OTG role change. ++ */ ++ struct work_struct start_work; ++ struct usb_hcd *_p; ++ ++ /** ++ * Connection timer. An OTG host must display a message if the device ++ * does not connect. Started when the VBus power is turned on via ++ * sysfs attribute "buspower". ++ */ ++ struct timer_list conn_timer; ++ ++ /* Tasket to do a reset */ ++ struct tasklet_struct *reset_tasklet; ++ ++#ifdef DEBUG ++ uint32_t frrem_samples; ++ uint64_t frrem_accum; ++ ++ uint32_t hfnum_7_samples_a; ++ uint64_t hfnum_7_frrem_accum_a; ++ uint32_t hfnum_0_samples_a; ++ uint64_t hfnum_0_frrem_accum_a; ++ uint32_t hfnum_other_samples_a; ++ uint64_t hfnum_other_frrem_accum_a; ++ ++ uint32_t hfnum_7_samples_b; ++ uint64_t hfnum_7_frrem_accum_b; ++ uint32_t hfnum_0_samples_b; ++ uint64_t hfnum_0_frrem_accum_b; ++ uint32_t hfnum_other_samples_b; ++ uint64_t hfnum_other_frrem_accum_b; ++#endif ++ ++} dwc_otg_hcd_t; ++ ++/** Gets the dwc_otg_hcd from a struct usb_hcd */ ++static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd) ++{ ++ return (dwc_otg_hcd_t *)(hcd->hcd_priv); ++} ++ ++/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */ ++static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t *dwc_otg_hcd) ++{ ++ return container_of((void *)dwc_otg_hcd, struct usb_hcd, hcd_priv); ++} ++ ++/** @name HCD Create/Destroy Functions */ ++/** @{ */ ++extern int __devinit dwc_otg_hcd_init(struct device *_dev, dwc_otg_device_t * dwc_otg_device); ++extern void dwc_otg_hcd_remove(struct device *_dev); ++/** @} */ ++ ++/** @name Linux HC Driver API Functions */ ++/** @{ */ ++ ++extern int dwc_otg_hcd_start(struct usb_hcd *hcd); ++extern void dwc_otg_hcd_stop(struct usb_hcd *hcd); ++extern int dwc_otg_hcd_get_frame_number(struct usb_hcd *hcd); ++extern void dwc_otg_hcd_free(struct usb_hcd *hcd); ++ ++extern int dwc_otg_hcd_urb_enqueue(struct usb_hcd *hcd, ++ struct urb *urb, ++ gfp_t mem_flags); ++extern int dwc_otg_hcd_urb_dequeue(struct usb_hcd *hcd, ++ struct urb *urb, ++ int status); ++extern irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd); ++ ++extern void dwc_otg_hcd_endpoint_disable(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep); ++ ++extern int dwc_otg_hcd_hub_status_data(struct usb_hcd *hcd, ++ char *buf); ++extern int dwc_otg_hcd_hub_control(struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, ++ u16 wIndex, ++ char *buf, ++ u16 wLength); ++ ++/** @} */ ++ ++/** @name Transaction Execution Functions */ ++/** @{ */ ++extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t *_hcd); ++extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t *_hcd, ++ dwc_otg_transaction_type_e _tr_type); ++extern void dwc_otg_hcd_complete_urb(dwc_otg_hcd_t *_hcd, struct urb *_urb, ++ int _status); ++/** @} */ ++ ++/** @name Interrupt Handler Functions */ ++/** @{ */ ++extern int32_t dwc_otg_hcd_handle_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_sof_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_port_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_disconnect_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_n_intr (dwc_otg_hcd_t *_dwc_otg_hcd, uint32_t _num); ++extern int32_t dwc_otg_hcd_handle_session_req_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr (dwc_otg_hcd_t *_dwc_otg_hcd); ++/** @} */ ++ ++ ++/** @name Schedule Queue Functions */ ++/** @{ */ ++ ++/* Implemented in dwc_otg_hcd_queue.c */ ++extern dwc_otg_qh_t *dwc_otg_hcd_qh_create (dwc_otg_hcd_t *_hcd, struct urb *_urb); ++extern void dwc_otg_hcd_qh_init (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, struct urb *_urb); ++extern void dwc_otg_hcd_qh_free (dwc_otg_qh_t *_qh); ++extern int dwc_otg_hcd_qh_add (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh); ++extern void dwc_otg_hcd_qh_remove (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh); ++extern void dwc_otg_hcd_qh_deactivate (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, int sched_csplit); ++extern int dwc_otg_hcd_qh_deferr (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, int delay); ++ ++/** Remove and free a QH */ ++static inline void dwc_otg_hcd_qh_remove_and_free (dwc_otg_hcd_t *_hcd, ++ dwc_otg_qh_t *_qh) ++{ ++ dwc_otg_hcd_qh_remove (_hcd, _qh); ++ dwc_otg_hcd_qh_free (_qh); ++} ++ ++/** Allocates memory for a QH structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc (void) ++{ ++#ifdef _SC_BUILD_ ++ return (dwc_otg_qh_t *) kmalloc (sizeof(dwc_otg_qh_t), GFP_ATOMIC); ++#else ++ return (dwc_otg_qh_t *) kmalloc (sizeof(dwc_otg_qh_t), GFP_KERNEL); ++#endif ++} ++ ++extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create (struct urb *urb); ++extern void dwc_otg_hcd_qtd_init (dwc_otg_qtd_t *qtd, struct urb *urb); ++extern int dwc_otg_hcd_qtd_add (dwc_otg_qtd_t *qtd, dwc_otg_hcd_t *dwc_otg_hcd); ++ ++/** Allocates memory for a QTD structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc (void) ++{ ++#ifdef _SC_BUILD_ ++ return (dwc_otg_qtd_t *) kmalloc (sizeof(dwc_otg_qtd_t), GFP_ATOMIC); ++#else ++ return (dwc_otg_qtd_t *) kmalloc (sizeof(dwc_otg_qtd_t), GFP_KERNEL); ++#endif ++} ++ ++/** Frees the memory for a QTD structure. QTD should already be removed from ++ * list. ++ * @param[in] _qtd QTD to free.*/ ++static inline void dwc_otg_hcd_qtd_free (dwc_otg_qtd_t *_qtd) ++{ ++ kfree (_qtd); ++} ++ ++/** Removes a QTD from list. ++ * @param[in] _qtd QTD to remove from list. */ ++static inline void dwc_otg_hcd_qtd_remove (dwc_otg_qtd_t *_qtd) ++{ ++ unsigned long flags; ++ local_irq_save (flags); ++ list_del (&_qtd->qtd_list_entry); ++ local_irq_restore (flags); ++} ++ ++/** Remove and free a QTD */ ++static inline void dwc_otg_hcd_qtd_remove_and_free (dwc_otg_qtd_t *_qtd) ++{ ++ dwc_otg_hcd_qtd_remove (_qtd); ++ dwc_otg_hcd_qtd_free (_qtd); ++} ++ ++/** @} */ ++ ++ ++/** @name Internal Functions */ ++/** @{ */ ++dwc_otg_qh_t *dwc_urb_to_qh(struct urb *_urb); ++void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t *_hcd); ++void dwc_otg_hcd_dump_state(dwc_otg_hcd_t *_hcd); ++/** @} */ ++ ++ ++/** Gets the usb_host_endpoint associated with an URB. */ ++static inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *_urb) ++{ ++ struct usb_device *dev = _urb->dev; ++ int ep_num = usb_pipeendpoint(_urb->pipe); ++ if (usb_pipein(_urb->pipe)) ++ return dev->ep_in[ep_num]; ++ else ++ return dev->ep_out[ep_num]; ++} ++ ++/** ++ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is ++ * qualified with its direction (possible 32 endpoints per device). ++ */ ++#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) \ ++ ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \ ++ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4) ++ ++/** Gets the QH that contains the list_head */ ++#define dwc_list_to_qh(_list_head_ptr_) (container_of(_list_head_ptr_,dwc_otg_qh_t,qh_list_entry)) ++ ++/** Gets the QTD that contains the list_head */ ++#define dwc_list_to_qtd(_list_head_ptr_) (container_of(_list_head_ptr_,dwc_otg_qtd_t,qtd_list_entry)) ++ ++/** Check if QH is non-periodic */ ++#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == USB_ENDPOINT_XFER_BULK) || \ ++ (_qh_ptr_->ep_type == USB_ENDPOINT_XFER_CONTROL)) ++ ++/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */ ++#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) ++ ++/** Packet size for any kind of endpoint descriptor */ ++#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) ++ ++/** ++ * Returns true if _frame1 is less than or equal to _frame2. The comparison is ++ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the ++ * frame number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_le(uint16_t _frame1, uint16_t _frame2) ++{ ++ return ((_frame2 - _frame1) & DWC_HFNUM_MAX_FRNUM) <= ++ (DWC_HFNUM_MAX_FRNUM >> 1); ++} ++ ++/** ++ * Returns true if _frame1 is greater than _frame2. The comparison is done ++ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame ++ * number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_gt(uint16_t _frame1, uint16_t _frame2) ++{ ++ return (_frame1 != _frame2) && ++ (((_frame1 - _frame2) & DWC_HFNUM_MAX_FRNUM) < ++ (DWC_HFNUM_MAX_FRNUM >> 1)); ++} ++ ++/** ++ * Increments _frame by the amount specified by _inc. The addition is done ++ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value. ++ */ ++static inline uint16_t dwc_frame_num_inc(uint16_t _frame, uint16_t _inc) ++{ ++ return (_frame + _inc) & DWC_HFNUM_MAX_FRNUM; ++} ++ ++static inline uint16_t dwc_full_frame_num (uint16_t _frame) ++{ ++ return ((_frame) & DWC_HFNUM_MAX_FRNUM) >> 3; ++} ++ ++static inline uint16_t dwc_micro_frame_num (uint16_t _frame) ++{ ++ return (_frame) & 0x7; ++} ++ ++#ifdef DEBUG ++/** ++ * Macro to sample the remaining PHY clocks left in the current frame. This ++ * may be used during debugging to determine the average time it takes to ++ * execute sections of code. There are two possible sample points, "a" and ++ * "b", so the _letter argument must be one of these values. ++ * ++ * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For ++ * example, "cat /sys/devices/lm0/hcd_frrem". ++ */ ++#define dwc_sample_frrem(_hcd, _qh, _letter) \ ++{ \ ++ hfnum_data_t hfnum; \ ++ dwc_otg_qtd_t *qtd; \ ++ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \ ++ if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \ ++ hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); \ ++ switch (hfnum.b.frnum & 0x7) { \ ++ case 7: \ ++ _hcd->hfnum_7_samples_##_letter++; \ ++ _hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ case 0: \ ++ _hcd->hfnum_0_samples_##_letter++; \ ++ _hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ default: \ ++ _hcd->hfnum_other_samples_##_letter++; \ ++ _hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ } \ ++ } \ ++} ++#else // DEBUG ++#define dwc_sample_frrem(_hcd, _qh, _letter) ++#endif // DEBUG ++#endif // __DWC_HCD_H__ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c +new file mode 100644 +index 0000000..834b5e0 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c +@@ -0,0 +1,1841 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd_intr.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 553126 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++const int erratum_usb09_patched = 0; ++const int deferral_on = 1; ++const int nak_deferral_delay = 8; ++const int nyet_deferral_delay = 1; ++/** @file ++ * This file contains the implementation of the HCD Interrupt handlers. ++ */ ++ ++/** This function handles interrupts for the HCD. */ ++int32_t dwc_otg_hcd_handle_intr (dwc_otg_hcd_t *_dwc_otg_hcd) ++{ ++ int retval = 0; ++ ++ dwc_otg_core_if_t *core_if = _dwc_otg_hcd->core_if; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++#endif ++ ++ /* Check if HOST Mode */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ gintsts.d32 = dwc_otg_read_core_intr(core_if); ++ if (!gintsts.d32) { ++ return 0; ++ } ++ ++#ifdef DEBUG ++ /* Don't print debug message in the interrupt handler on SOF */ ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ DWC_DEBUGPL (DBG_HCD, "\n"); ++#endif ++ ++#ifdef DEBUG ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n", gintsts.d32); ++#endif ++ ++ if (gintsts.b.sofintr) { ++ retval |= dwc_otg_hcd_handle_sof_intr (_dwc_otg_hcd); ++ } ++ if (gintsts.b.rxstsqlvl) { ++ retval |= dwc_otg_hcd_handle_rx_status_q_level_intr (_dwc_otg_hcd); ++ } ++ if (gintsts.b.nptxfempty) { ++ retval |= dwc_otg_hcd_handle_np_tx_fifo_empty_intr (_dwc_otg_hcd); ++ } ++ if (gintsts.b.i2cintr) { ++ /** @todo Implement i2cintr handler. */ ++ } ++ if (gintsts.b.portintr) { ++ retval |= dwc_otg_hcd_handle_port_intr (_dwc_otg_hcd); ++ } ++ if (gintsts.b.hcintr) { ++ retval |= dwc_otg_hcd_handle_hc_intr (_dwc_otg_hcd); ++ } ++ if (gintsts.b.ptxfempty) { ++ retval |= dwc_otg_hcd_handle_perio_tx_fifo_empty_intr (_dwc_otg_hcd); ++ } ++#ifdef DEBUG ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Finished Servicing Interrupts\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n", ++ dwc_read_reg32(&global_regs->gintsts)); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n", ++ dwc_read_reg32(&global_regs->gintmsk)); ++ } ++#endif ++ ++#ifdef DEBUG ++# ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++# endif ++ DWC_DEBUGPL (DBG_HCD, "\n"); ++#endif ++ ++ } ++ ++ return retval; ++} ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++#warning Compiling code to track missed SOFs ++#define FRAME_NUM_ARRAY_SIZE 1000 ++/** ++ * This function is for debug only. ++ */ ++static inline void track_missed_sofs(uint16_t _curr_frame_number) { ++ static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static int frame_num_idx = 0; ++ static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM; ++ static int dumped_frame_num_array = 0; ++ ++ if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) { ++ if ((((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) != _curr_frame_number)) { ++ frame_num_array[frame_num_idx] = _curr_frame_number; ++ last_frame_num_array[frame_num_idx++] = last_frame_num; ++ } ++ } else if (!dumped_frame_num_array) { ++ int i; ++ printk(KERN_EMERG USB_DWC "Frame Last Frame\n"); ++ printk(KERN_EMERG USB_DWC "----- ----------\n"); ++ for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) { ++ printk(KERN_EMERG USB_DWC "0x%04x 0x%04x\n", ++ frame_num_array[i], last_frame_num_array[i]); ++ } ++ dumped_frame_num_array = 1; ++ } ++ last_frame_num = _curr_frame_number; ++} ++#endif ++ ++/** ++ * Handles the start-of-frame interrupt in host mode. Non-periodic ++ * transactions may be queued to the DWC_otg controller for the current ++ * (micro)frame. Periodic transactions may be queued to the controller for the ++ * next (micro)frame. ++ */ ++int32_t dwc_otg_hcd_handle_sof_intr (dwc_otg_hcd_t *_hcd) ++{ ++ hfnum_data_t hfnum; ++ struct list_head *qh_entry; ++ dwc_otg_qh_t *qh; ++ dwc_otg_transaction_type_e tr_type; ++ gintsts_data_t gintsts = {.d32 = 0}; ++ ++ hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n"); ++#endif ++ ++ _hcd->frame_number = hfnum.b.frnum; ++ ++#ifdef DEBUG ++ _hcd->frrem_accum += hfnum.b.frrem; ++ _hcd->frrem_samples++; ++#endif ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++ track_missed_sofs(_hcd->frame_number); ++#endif ++ ++ /* Determine whether any periodic QHs should be executed. */ ++ qh_entry = _hcd->periodic_sched_inactive.next; ++ while (qh_entry != &_hcd->periodic_sched_inactive) { ++ qh = list_entry(qh_entry, dwc_otg_qh_t, qh_list_entry); ++ qh_entry = qh_entry->next; ++ if (dwc_frame_num_le(qh->sched_frame, _hcd->frame_number)) { ++ /* ++ * Move QH to the ready list to be executed next ++ * (micro)frame. ++ */ ++ list_move(&qh->qh_list_entry, &_hcd->periodic_sched_ready); ++ } ++ } ++ ++ tr_type = dwc_otg_hcd_select_transactions(_hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(_hcd, tr_type); ++ } ++ ++ /* Clear interrupt */ ++ gintsts.b.sofintr = 1; ++ dwc_write_reg32(&_hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at ++ * least one packet in the Rx FIFO. The packets are moved from the FIFO to ++ * memory if the DWC_otg controller is operating in Slave mode. */ ++int32_t dwc_otg_hcd_handle_rx_status_q_level_intr (dwc_otg_hcd_t *_dwc_otg_hcd) ++{ ++ host_grxsts_data_t grxsts; ++ dwc_hc_t *hc = NULL; ++ ++ DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n"); ++ ++ grxsts.d32 = dwc_read_reg32(&_dwc_otg_hcd->core_if->core_global_regs->grxstsp); ++ ++ hc = _dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum]; ++ ++ /* Packet Status */ ++ DWC_DEBUGPL(DBG_HCDV, " Ch num = %d\n", grxsts.b.chnum); ++ DWC_DEBUGPL(DBG_HCDV, " Count = %d\n", grxsts.b.bcnt); ++ DWC_DEBUGPL(DBG_HCDV, " DPID = %d, hc.dpid = %d\n", grxsts.b.dpid, hc->data_pid_start); ++ DWC_DEBUGPL(DBG_HCDV, " PStatus = %d\n", grxsts.b.pktsts); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer. */ ++ if (grxsts.b.bcnt > 0) { ++ dwc_otg_read_packet(_dwc_otg_hcd->core_if, ++ hc->xfer_buff, ++ grxsts.b.bcnt); ++ ++ /* Update the HC fields for the next packet received. */ ++ hc->xfer_count += grxsts.b.bcnt; ++ hc->xfer_buff += grxsts.b.bcnt; ++ } ++ ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR: ++ case DWC_GRXSTS_PKTSTS_CH_HALTED: ++ /* Handled in interrupt, just ignore data */ ++ break; ++ default: ++ DWC_ERROR ("RX_STS_Q Interrupt: Unknown status %d\n", grxsts.b.pktsts); ++ break; ++ } ++ ++ return 1; ++} ++ ++/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More ++ * data packets may be written to the FIFO for OUT transfers. More requests ++ * may be written to the non-periodic request queue for IN transfers. This ++ * interrupt is enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr (dwc_otg_hcd_t *_dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(_dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_NON_PERIODIC); ++ return 1; ++} ++ ++/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data ++ * packets may be written to the FIFO for OUT transfers. More requests may be ++ * written to the periodic request queue for IN transfers. This interrupt is ++ * enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr (dwc_otg_hcd_t *_dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(_dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_PERIODIC); ++ return 1; ++} ++ ++/** There are multiple conditions that can cause a port interrupt. This function ++ * determines which interrupt conditions have occurred and handles them ++ * appropriately. */ ++int32_t dwc_otg_hcd_handle_port_intr (dwc_otg_hcd_t *_dwc_otg_hcd) ++{ ++ int retval = 0; ++ hprt0_data_t hprt0; ++ hprt0_data_t hprt0_modify; ++ ++ hprt0.d32 = dwc_read_reg32(_dwc_otg_hcd->core_if->host_if->hprt0); ++ hprt0_modify.d32 = dwc_read_reg32(_dwc_otg_hcd->core_if->host_if->hprt0); ++ ++ /* Clear appropriate bits in HPRT0 to clear the interrupt bit in ++ * GINTSTS */ ++ ++ hprt0_modify.b.prtena = 0; ++ hprt0_modify.b.prtconndet = 0; ++ hprt0_modify.b.prtenchng = 0; ++ hprt0_modify.b.prtovrcurrchng = 0; ++ ++ /* Port Connect Detected ++ * Set flag and clear if detected */ ++ if (hprt0.b.prtconndet) { ++ DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x " ++ "Port Connect Detected--\n", hprt0.d32); ++ _dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ _dwc_otg_hcd->flags.b.port_connect_status = 1; ++ hprt0_modify.b.prtconndet = 1; ++ ++ /* B-Device has connected, Delete the connection timer. */ ++ del_timer( &_dwc_otg_hcd->conn_timer ); ++ ++ /* The Hub driver asserts a reset when it sees port connect ++ * status change flag */ ++ retval |= 1; ++ } ++ ++ /* Port Enable Changed ++ * Clear if detected - Set internal flag if disabled */ ++ if (hprt0.b.prtenchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Enable Changed--\n", hprt0.d32); ++ hprt0_modify.b.prtenchng = 1; ++ if (hprt0.b.prtena == 1) { ++ int do_reset = 0; ++ dwc_otg_core_params_t *params = _dwc_otg_hcd->core_if->core_params; ++ dwc_otg_core_global_regs_t *global_regs = _dwc_otg_hcd->core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = _dwc_otg_hcd->core_if->host_if; ++ ++ /* Check if we need to adjust the PHY clock speed for ++ * low power and adjust it */ ++ if (params->host_support_fs_ls_low_power) ++ { ++ gusbcfg_data_t usbcfg; ++ ++ usbcfg.d32 = dwc_read_reg32 (&global_regs->gusbcfg); ++ ++ if ((hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) || ++ (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_FULL_SPEED)) ++ { ++ /* ++ * Low power ++ */ ++ hcfg_data_t hcfg; ++ if (usbcfg.b.phylpwrclksel == 0) { ++ /* Set PHY low power clock select for FS/LS devices */ ++ usbcfg.b.phylpwrclksel = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ do_reset = 1; ++ } ++ ++ hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg); ++ ++ if ((hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) && ++ (params->host_ls_low_power_phy_clk == ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ)) ++ { ++ /* 6 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY programming HCFG to 6 MHz (Low Power)\n"); ++ if (hcfg.b.fslspclksel != DWC_HCFG_6_MHZ) { ++ hcfg.b.fslspclksel = DWC_HCFG_6_MHZ; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ do_reset = 1; ++ } ++ } ++ else { ++ /* 48 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY programming HCFG to 48 MHz ()\n"); ++ if (hcfg.b.fslspclksel != DWC_HCFG_48_MHZ) { ++ hcfg.b.fslspclksel = DWC_HCFG_48_MHZ; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, ++ hcfg.d32); ++ do_reset = 1; ++ } ++ } ++ } ++ else { ++ /* ++ * Not low power ++ */ ++ if (usbcfg.b.phylpwrclksel == 1) { ++ usbcfg.b.phylpwrclksel = 0; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ do_reset = 1; ++ } ++ } ++ ++ if (do_reset) { ++ tasklet_schedule(_dwc_otg_hcd->reset_tasklet); ++ } ++ } ++ ++ if (!do_reset) { ++ /* Port has been enabled set the reset change flag */ ++ _dwc_otg_hcd->flags.b.port_reset_change = 1; ++ } ++ ++ } else { ++ _dwc_otg_hcd->flags.b.port_enable_change = 1; ++ } ++ retval |= 1; ++ } ++ ++ /** Overcurrent Change Interrupt */ ++ if (hprt0.b.prtovrcurrchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Overcurrent Changed--\n", hprt0.d32); ++ _dwc_otg_hcd->flags.b.port_over_current_change = 1; ++ hprt0_modify.b.prtovrcurrchng = 1; ++ retval |= 1; ++ } ++ ++ /* Clear Port Interrupts */ ++ dwc_write_reg32(_dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32); ++ ++ return retval; ++} ++ ++ ++/** This interrupt indicates that one or more host channels has a pending ++ * interrupt. There are multiple conditions that can cause each host channel ++ * interrupt. This function determines which conditions have occurred for each ++ * host channel interrupt and handles them appropriately. */ ++int32_t dwc_otg_hcd_handle_hc_intr (dwc_otg_hcd_t *_dwc_otg_hcd) ++{ ++ int i; ++ int retval = 0; ++ haint_data_t haint; ++ ++ /* Clear appropriate bits in HCINTn to clear the interrupt bit in ++ * GINTSTS */ ++ ++ haint.d32 = dwc_otg_read_host_all_channels_intr(_dwc_otg_hcd->core_if); ++ ++ for (i=0; i<_dwc_otg_hcd->core_if->core_params->host_channels; i++) { ++ if (haint.b2.chint & (1 << i)) { ++ retval |= dwc_otg_hcd_handle_hc_n_intr (_dwc_otg_hcd, i); ++ } ++ } ++ ++ return retval; ++} ++ ++/* Macro used to clear one channel interrupt */ ++#define clear_hc_int(_hc_regs_,_intr_) \ ++do { \ ++ hcint_data_t hcint_clear = {.d32 = 0}; \ ++ hcint_clear.b._intr_ = 1; \ ++ dwc_write_reg32(&((_hc_regs_)->hcint), hcint_clear.d32); \ ++} while (0) ++ ++/* ++ * Macro used to disable one channel interrupt. Channel interrupts are ++ * disabled when the channel is halted or released by the interrupt handler. ++ * There is no need to handle further interrupts of that type until the ++ * channel is re-assigned. In fact, subsequent handling may cause crashes ++ * because the channel structures are cleaned up when the channel is released. ++ */ ++#define disable_hc_int(_hc_regs_,_intr_) \ ++do { \ ++ hcintmsk_data_t hcintmsk = {.d32 = 0}; \ ++ hcintmsk.b._intr_ = 1; \ ++ dwc_modify_reg32(&((_hc_regs_)->hcintmsk), hcintmsk.d32, 0); \ ++} while (0) ++ ++/** ++ * Gets the actual length of a transfer after the transfer halts. _halt_status ++ * holds the reason for the halt. ++ * ++ * For IN transfers where _halt_status is DWC_OTG_HC_XFER_COMPLETE, ++ * *_short_read is set to 1 upon return if less than the requested ++ * number of bytes were transferred. Otherwise, *_short_read is set to 0 upon ++ * return. _short_read may also be NULL on entry, in which case it remains ++ * unchanged. ++ */ ++static uint32_t get_actual_xfer_length(dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status, ++ int *_short_read) ++{ ++ hctsiz_data_t hctsiz; ++ uint32_t length; ++ ++ if (_short_read != NULL) { ++ *_short_read = 0; ++ } ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ ++ if (_halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ if (_hc->ep_is_in) { ++ length = _hc->xfer_len - hctsiz.b.xfersize; ++ if (_short_read != NULL) { ++ *_short_read = (hctsiz.b.xfersize != 0); ++ } ++ } else if (_hc->qh->do_split) { ++ length = _qtd->ssplit_out_xfer_count; ++ } else { ++ length = _hc->xfer_len; ++ } ++ } else { ++ /* ++ * Must use the hctsiz.pktcnt field to determine how much data ++ * has been transferred. This field reflects the number of ++ * packets that have been transferred via the USB. This is ++ * always an integral number of packets if the transfer was ++ * halted before its normal completion. (Can't use the ++ * hctsiz.xfersize field because that reflects the number of ++ * bytes transferred via the AHB, not the USB). ++ */ ++ length = (_hc->start_pkt_count - hctsiz.b.pktcnt) * _hc->max_packet; ++ } ++ ++ return length; ++} ++ ++/** ++ * Updates the state of the URB after a Transfer Complete interrupt on the ++ * host channel. Updates the actual_length field of the URB based on the ++ * number of bytes transferred via the host channel. Sets the URB status ++ * if the data transfer is finished. ++ * ++ * @return 1 if the data transfer specified by the URB is completely finished, ++ * 0 otherwise. ++ */ ++static int update_urb_state_xfer_comp(dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t * _hc_regs, struct urb *_urb, ++ dwc_otg_qtd_t * _qtd, int *status) ++{ ++ int xfer_done = 0; ++ int short_read = 0; ++ ++ _urb->actual_length += get_actual_xfer_length(_hc, _hc_regs, _qtd, ++ DWC_OTG_HC_XFER_COMPLETE, ++ &short_read); ++ ++ if (short_read || (_urb->actual_length == _urb->transfer_buffer_length)) { ++ xfer_done = 1; ++ if (short_read && (_urb->transfer_flags & URB_SHORT_NOT_OK)) { ++ *status = -EREMOTEIO; ++ } else { ++ *status = 0; ++ } ++ } ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (_hc->ep_is_in ? "IN" : "OUT"), _hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", _hc->xfer_len); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n", hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ _urb->transfer_buffer_length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", _urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " short_read %d, xfer_done %d\n", ++ short_read, xfer_done); ++ } ++#endif ++ ++ return xfer_done; ++} ++ ++/* ++ * Save the starting data toggle for the next transfer. The data toggle is ++ * saved in the QH for non-control transfers and it's saved in the QTD for ++ * control transfers. ++ */ ++static void save_data_toggle(dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd) ++{ ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ ++ if (_hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) { ++ dwc_otg_qh_t *qh = _hc->qh; ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } else { ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ _qtd->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ _qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } ++} ++ ++/** ++ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic ++ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are ++ * still linked to the QH, the QH is added to the end of the inactive ++ * non-periodic schedule. For periodic QHs, removes the QH from the periodic ++ * schedule if no more QTDs are linked to the QH. ++ */ ++static void deactivate_qh(dwc_otg_hcd_t *_hcd, ++ dwc_otg_qh_t *_qh, ++ int free_qtd) ++{ ++ int continue_split = 0; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s(%p,%p,%d)\n", __func__, _hcd, _qh, free_qtd); ++ ++ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); ++ ++ if (qtd->complete_split) { ++ continue_split = 1; ++ } ++ else if ((qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID) || ++ (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END)) ++ { ++ continue_split = 1; ++ } ++ ++ if (free_qtd) { ++ /* ++ * Note that this was previously a call to ++ * dwc_otg_hcd_qtd_remove_and_free(qtd), which frees the qtd. ++ * However, that call frees the qtd memory, and we continue in the ++ * interrupt logic to access it many more times, including writing ++ * to it. With slub debugging on, it is clear that we were writing ++ * to memory we had freed. ++ * Call this instead, and now I have moved the freeing of the memory to ++ * the end of processing this interrupt. ++ */ ++ //dwc_otg_hcd_qtd_remove_and_free(qtd); ++ dwc_otg_hcd_qtd_remove(qtd); ++ ++ continue_split = 0; ++ } ++ ++ _qh->channel = NULL; ++ _qh->qtd_in_process = NULL; ++ dwc_otg_hcd_qh_deactivate(_hcd, _qh, continue_split); ++} ++ ++/** ++ * Updates the state of an Isochronous URB when the transfer is stopped for ++ * any reason. The fields of the current entry in the frame descriptor array ++ * are set based on the transfer state and the input _halt_status. Completes ++ * the Isochronous URB if all the URB frames have been completed. ++ * ++ * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be ++ * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE. ++ */ ++static dwc_otg_halt_status_e ++update_isoc_urb_state(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status) ++{ ++ struct urb *urb = _qtd->urb; ++ dwc_otg_halt_status_e ret_val = _halt_status; ++ struct usb_iso_packet_descriptor *frame_desc; ++ ++ frame_desc = &urb->iso_frame_desc[_qtd->isoc_frame_index]; ++ switch (_halt_status) { ++ case DWC_OTG_HC_XFER_COMPLETE: ++ frame_desc->status = 0; ++ frame_desc->actual_length = ++ get_actual_xfer_length(_hc, _hc_regs, _qtd, ++ _halt_status, NULL); ++ break; ++ case DWC_OTG_HC_XFER_FRAME_OVERRUN: ++ urb->error_count++; ++ if (_hc->ep_is_in) { ++ frame_desc->status = -ENOSR; ++ } else { ++ frame_desc->status = -ECOMM; ++ } ++ frame_desc->actual_length = 0; ++ break; ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ urb->error_count++; ++ frame_desc->status = -EOVERFLOW; ++ /* Don't need to update actual_length in this case. */ ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ urb->error_count++; ++ frame_desc->status = -EPROTO; ++ frame_desc->actual_length = ++ get_actual_xfer_length(_hc, _hc_regs, _qtd, ++ _halt_status, NULL); ++ default: ++ DWC_ERROR("%s: Unhandled _halt_status (%d)\n", __func__, ++ _halt_status); ++ BUG(); ++ break; ++ } ++ ++ if (++_qtd->isoc_frame_index == urb->number_of_packets) { ++ /* ++ * urb->status is not used for isoc transfers. ++ * The individual frame_desc statuses are used instead. ++ */ ++ dwc_otg_hcd_complete_urb(_hcd, urb, 0); ++ ret_val = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ ret_val = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ return ret_val; ++} ++ ++/** ++ * Releases a host channel for use by other transfers. Attempts to select and ++ * queue more transactions since at least one host channel is available. ++ * ++ * @param _hcd The HCD state structure. ++ * @param _hc The host channel to release. ++ * @param _qtd The QTD associated with the host channel. This QTD may be freed ++ * if the transfer is complete or an error has occurred. ++ * @param _halt_status Reason the channel is being released. This status ++ * determines the actions taken by this function. ++ */ ++static void release_channel(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status, ++ int *must_free) ++{ ++ dwc_otg_transaction_type_e tr_type; ++ int free_qtd; ++ dwc_otg_qh_t * _qh; ++ int deact = 1; ++ int retry_delay = 1; ++ unsigned long flags; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n", __func__, ++ _hc->hc_num, _halt_status); ++ ++ switch (_halt_status) { ++ case DWC_OTG_HC_XFER_NYET: ++ case DWC_OTG_HC_XFER_NAK: ++ if (_halt_status == DWC_OTG_HC_XFER_NYET) { ++ retry_delay = nyet_deferral_delay; ++ } else { ++ retry_delay = nak_deferral_delay; ++ } ++ free_qtd = 0; ++ if (deferral_on && _hc->do_split) { ++ _qh = _hc->qh; ++ if (_qh) { ++ deact = dwc_otg_hcd_qh_deferr(_hcd, _qh , retry_delay); ++ } ++ } ++ break; ++ case DWC_OTG_HC_XFER_URB_COMPLETE: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_AHB_ERR: ++ case DWC_OTG_HC_XFER_STALL: ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ if (_qtd->error_count >= 3) { ++ DWC_DEBUGPL(DBG_HCDV, " Complete URB with transaction error\n"); ++ free_qtd = 1; ++ //_qtd->urb->status = -EPROTO; ++ dwc_otg_hcd_complete_urb(_hcd, _qtd->urb, -EPROTO); ++ } else { ++ free_qtd = 0; ++ } ++ break; ++ case DWC_OTG_HC_XFER_URB_DEQUEUE: ++ /* ++ * The QTD has already been removed and the QH has been ++ * deactivated. Don't want to do anything except release the ++ * host channel and try to queue more transfers. ++ */ ++ goto cleanup; ++ case DWC_OTG_HC_XFER_NO_HALT_STATUS: ++ DWC_ERROR("%s: No halt_status, channel %d\n", __func__, _hc->hc_num); ++ free_qtd = 0; ++ break; ++ default: ++ free_qtd = 0; ++ break; ++ } ++ if (free_qtd) { ++ /* Only change must_free to true (do not set to zero here -- it is ++ * pre-initialized to zero). ++ */ ++ *must_free = 1; ++ } ++ if (deact) { ++ deactivate_qh(_hcd, _hc->qh, free_qtd); ++ } ++ cleanup: ++ /* ++ * Release the host channel for use by other transfers. The cleanup ++ * function clears the channel interrupt enables and conditions, so ++ * there's no need to clear the Channel Halted interrupt separately. ++ */ ++ dwc_otg_hc_cleanup(_hcd->core_if, _hc); ++ list_add_tail(&_hc->hc_list_entry, &_hcd->free_hc_list); ++ ++ local_irq_save(flags); ++ _hcd->available_host_channels++; ++ local_irq_restore(flags); ++ /* Try to queue more transfers now that there's a free channel, */ ++ /* unless erratum_usb09_patched is set */ ++ if (!erratum_usb09_patched) { ++ tr_type = dwc_otg_hcd_select_transactions(_hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(_hcd, tr_type); ++ } ++ } ++} ++ ++/** ++ * Halts a host channel. If the channel cannot be halted immediately because ++ * the request queue is full, this function ensures that the FIFO empty ++ * interrupt for the appropriate queue is enabled so that the halt request can ++ * be queued when there is space in the request queue. ++ * ++ * This function may also be called in DMA mode. In that case, the channel is ++ * simply released since the core always halts the channel automatically in ++ * DMA mode. ++ */ ++static void halt_channel(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status, int *must_free) ++{ ++ if (_hcd->core_if->dma_enable) { ++ release_channel(_hcd, _hc, _qtd, _halt_status, must_free); ++ return; ++ } ++ ++ /* Slave mode processing... */ ++ dwc_otg_hc_halt(_hcd->core_if, _hc, _halt_status); ++ ++ if (_hc->halt_on_queue) { ++ gintmsk_data_t gintmsk = {.d32 = 0}; ++ dwc_otg_core_global_regs_t *global_regs; ++ global_regs = _hcd->core_if->core_global_regs; ++ ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ _hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ /* ++ * Make sure the Non-periodic Tx FIFO empty interrupt ++ * is enabled so that the non-periodic schedule will ++ * be processed. ++ */ ++ gintmsk.b.nptxfempty = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } else { ++ /* ++ * Move the QH from the periodic queued schedule to ++ * the periodic assigned schedule. This allows the ++ * halt to be queued when the periodic schedule is ++ * processed. ++ */ ++ list_move(&_hc->qh->qh_list_entry, ++ &_hcd->periodic_sched_assigned); ++ ++ /* ++ * Make sure the Periodic Tx FIFO Empty interrupt is ++ * enabled so that the periodic schedule will be ++ * processed. ++ */ ++ gintmsk.b.ptxfempty = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } ++ } ++} ++ ++/** ++ * Performs common cleanup for non-periodic transfers after a Transfer ++ * Complete interrupt. This function should be called after any endpoint type ++ * specific handling is finished to release the host channel. ++ */ ++static void complete_non_periodic_xfer(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status, int *must_free) ++{ ++ hcint_data_t hcint; ++ ++ _qtd->error_count = 0; ++ ++ hcint.d32 = dwc_read_reg32(&_hc_regs->hcint); ++ if (hcint.b.nyet) { ++ /* ++ * Got a NYET on the last transaction of the transfer. This ++ * means that the endpoint should be in the PING state at the ++ * beginning of the next transfer. ++ */ ++ _hc->qh->ping_state = 1; ++ clear_hc_int(_hc_regs,nyet); ++ } ++ ++ /* ++ * Always halt and release the host channel to make it available for ++ * more transfers. There may still be more phases for a control ++ * transfer or more data packets for a bulk transfer at this point, ++ * but the host channel is still halted. A channel will be reassigned ++ * to the transfer when the non-periodic schedule is processed after ++ * the channel is released. This allows transactions to be queued ++ * properly via dwc_otg_hcd_queue_transactions, which also enables the ++ * Tx FIFO Empty interrupt if necessary. ++ */ ++ if (_hc->ep_is_in) { ++ /* ++ * IN transfers in Slave mode require an explicit disable to ++ * halt the channel. (In DMA mode, this call simply releases ++ * the channel.) ++ */ ++ halt_channel(_hcd, _hc, _qtd, _halt_status, must_free); ++ } else { ++ /* ++ * The channel is automatically disabled by the core for OUT ++ * transfers in Slave mode. ++ */ ++ release_channel(_hcd, _hc, _qtd, _halt_status, must_free); ++ } ++} ++ ++/** ++ * Performs common cleanup for periodic transfers after a Transfer Complete ++ * interrupt. This function should be called after any endpoint type specific ++ * handling is finished to release the host channel. ++ */ ++static void complete_periodic_xfer(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status, int *must_free) ++{ ++ hctsiz_data_t hctsiz; ++ _qtd->error_count = 0; ++ ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ if (!_hc->ep_is_in || hctsiz.b.pktcnt == 0) { ++ /* Core halts channel in these cases. */ ++ release_channel(_hcd, _hc, _qtd, _halt_status, must_free); ++ } else { ++ /* Flush any outstanding requests from the Tx queue. */ ++ halt_channel(_hcd, _hc, _qtd, _halt_status, must_free); ++ } ++} ++ ++/** ++ * Handles a host channel Transfer Complete interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, int *must_free) ++{ ++ int urb_xfer_done; ++ dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ struct urb *urb = _qtd->urb; ++ int pipe_type = usb_pipetype(urb->pipe); ++ int status = -EINPROGRESS; ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transfer Complete--\n", _hc->hc_num); ++ ++ /* ++ * Handle xfer complete on CSPLIT. ++ */ ++ if (_hc->qh->do_split) { ++ _qtd->complete_split = 0; ++ } ++ ++ /* Update the QTD and URB states. */ ++ switch (pipe_type) { ++ case PIPE_CONTROL: ++ switch (_qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ if (urb->transfer_buffer_length > 0) { ++ _qtd->control_phase = DWC_OTG_CONTROL_DATA; ++ } else { ++ _qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ } ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n"); ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ case DWC_OTG_CONTROL_DATA: { ++ urb_xfer_done = update_urb_state_xfer_comp(_hc, _hc_regs,urb, _qtd, &status); ++ if (urb_xfer_done) { ++ _qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n"); ++ } else { ++ save_data_toggle(_hc, _hc_regs, _qtd); ++ } ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ } ++ case DWC_OTG_CONTROL_STATUS: ++ DWC_DEBUGPL(DBG_HCDV, " Control transfer complete\n"); ++ if (status == -EINPROGRESS) { ++ status = 0; ++ } ++ dwc_otg_hcd_complete_urb(_hcd, urb, status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ break; ++ } ++ ++ complete_non_periodic_xfer(_hcd, _hc, _hc_regs, _qtd, ++ halt_status, must_free); ++ break; ++ case PIPE_BULK: ++ DWC_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n"); ++ urb_xfer_done = update_urb_state_xfer_comp(_hc, _hc_regs, urb, _qtd, &status); ++ if (urb_xfer_done) { ++ dwc_otg_hcd_complete_urb(_hcd, urb, status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ save_data_toggle(_hc, _hc_regs, _qtd); ++ complete_non_periodic_xfer(_hcd, _hc, _hc_regs, _qtd,halt_status, must_free); ++ break; ++ case PIPE_INTERRUPT: ++ DWC_DEBUGPL(DBG_HCDV, " Interrupt transfer complete\n"); ++ update_urb_state_xfer_comp(_hc, _hc_regs, urb, _qtd, &status); ++ ++ /* ++ * Interrupt URB is done on the first transfer complete ++ * interrupt. ++ */ ++ dwc_otg_hcd_complete_urb(_hcd, urb, status); ++ save_data_toggle(_hc, _hc_regs, _qtd); ++ complete_periodic_xfer(_hcd, _hc, _hc_regs, _qtd, ++ DWC_OTG_HC_XFER_URB_COMPLETE, must_free); ++ break; ++ case PIPE_ISOCHRONOUS: ++ DWC_DEBUGPL(DBG_HCDV, " Isochronous transfer complete\n"); ++ if (_qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) ++ { ++ halt_status = update_isoc_urb_state(_hcd, _hc, _hc_regs, _qtd, ++ DWC_OTG_HC_XFER_COMPLETE); ++ } ++ complete_periodic_xfer(_hcd, _hc, _hc_regs, _qtd, halt_status, must_free); ++ break; ++ } ++ ++ disable_hc_int(_hc_regs,xfercompl); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel STALL interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_stall_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, int *must_free) ++{ ++ struct urb *urb = _qtd->urb; ++ int pipe_type = usb_pipetype(urb->pipe); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "STALL Received--\n", _hc->hc_num); ++ ++ if (pipe_type == PIPE_CONTROL) { ++ dwc_otg_hcd_complete_urb(_hcd, _qtd->urb, -EPIPE); ++ } ++ ++ if (pipe_type == PIPE_BULK || pipe_type == PIPE_INTERRUPT) { ++ dwc_otg_hcd_complete_urb(_hcd, _qtd->urb, -EPIPE); ++ /* ++ * USB protocol requires resetting the data toggle for bulk ++ * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT) ++ * setup command is issued to the endpoint. Anticipate the ++ * CLEAR_FEATURE command since a STALL has occurred and reset ++ * the data toggle now. ++ */ ++ _hc->qh->data_toggle = 0; ++ } ++ ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_STALL, must_free); ++ disable_hc_int(_hc_regs,stall); ++ ++ return 1; ++} ++ ++/* ++ * Updates the state of the URB when a transfer has been stopped due to an ++ * abnormal condition before the transfer completes. Modifies the ++ * actual_length field of the URB to reflect the number of bytes that have ++ * actually been transferred via the host channel. ++ */ ++static void update_urb_state_xfer_intr(dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ struct urb *_urb, ++ dwc_otg_qtd_t *_qtd, ++ dwc_otg_halt_status_e _halt_status) ++{ ++ uint32_t bytes_transferred = get_actual_xfer_length(_hc, _hc_regs, _qtd, ++ _halt_status, NULL); ++ _urb->actual_length += bytes_transferred; ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (_hc->ep_is_in ? "IN" : "OUT"), _hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " _hc->start_pkt_count %d\n", _hc->start_pkt_count); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.pktcnt %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " _hc->max_packet %d\n", _hc->max_packet); ++ DWC_DEBUGPL(DBG_HCDV, " bytes_transferred %d\n", bytes_transferred); ++ DWC_DEBUGPL(DBG_HCDV, " _urb->actual_length %d\n", _urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " _urb->transfer_buffer_length %d\n", ++ _urb->transfer_buffer_length); ++ } ++#endif ++} ++ ++/** ++ * Handles a host channel NAK interrupt. This handler may be called in either ++ * DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nak_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NAK Received--\n", _hc->hc_num); ++ ++ /* ++ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and ++ * interrupt. Re-start the SSPLIT transfer. ++ */ ++ if (_hc->do_split) { ++ if (_hc->complete_split) { ++ _qtd->error_count = 0; ++ } ++ _qtd->complete_split = 0; ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NAK, must_free); ++ goto handle_nak_done; ++ } ++ ++ switch (usb_pipetype(_qtd->urb->pipe)) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ if (_hcd->core_if->dma_enable && _hc->ep_is_in) { ++ /* ++ * NAK interrupts are enabled on bulk/control IN ++ * transfers in DMA mode for the sole purpose of ++ * resetting the error count after a transaction error ++ * occurs. The core will continue transferring data. ++ */ ++ _qtd->error_count = 0; ++ goto handle_nak_done; ++ } ++ ++ /* ++ * NAK interrupts normally occur during OUT transfers in DMA ++ * or Slave mode. For IN transfers, more requests will be ++ * queued as request queue space is available. ++ */ ++ _qtd->error_count = 0; ++ ++ if (!_hc->qh->ping_state) { ++ update_urb_state_xfer_intr(_hc, _hc_regs, _qtd->urb, ++ _qtd, DWC_OTG_HC_XFER_NAK); ++ save_data_toggle(_hc, _hc_regs, _qtd); ++ if (_qtd->urb->dev->speed == USB_SPEED_HIGH) { ++ _hc->qh->ping_state = 1; ++ } ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will ++ * start/continue. ++ */ ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NAK, must_free); ++ break; ++ case PIPE_INTERRUPT: ++ _qtd->error_count = 0; ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NAK, must_free); ++ break; ++ case PIPE_ISOCHRONOUS: ++ /* Should never get called for isochronous transfers. */ ++ BUG(); ++ break; ++ } ++ ++ handle_nak_done: ++ disable_hc_int(_hc_regs,nak); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel ACK interrupt. This interrupt is enabled when ++ * performing the PING protocol in Slave mode, when errors occur during ++ * either Slave mode or DMA mode, and during Start Split transactions. ++ */ ++static int32_t handle_hc_ack_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "ACK Received--\n", _hc->hc_num); ++ ++ if (_hc->do_split) { ++ /* ++ * Handle ACK on SSPLIT. ++ * ACK should not occur in CSPLIT. ++ */ ++ if ((!_hc->ep_is_in) && (_hc->data_pid_start != DWC_OTG_HC_PID_SETUP)) { ++ _qtd->ssplit_out_xfer_count = _hc->xfer_len; ++ } ++ if (!(_hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !_hc->ep_is_in)) { ++ /* Don't need complete for isochronous out transfers. */ ++ _qtd->complete_split = 1; ++ } ++ ++ /* ISOC OUT */ ++ if ((_hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && !_hc->ep_is_in) { ++ switch (_hc->xact_pos) { ++ case DWC_HCSPLIT_XACTPOS_ALL: ++ break; ++ case DWC_HCSPLIT_XACTPOS_END: ++ _qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ _qtd->isoc_split_offset = 0; ++ break; ++ case DWC_HCSPLIT_XACTPOS_BEGIN: ++ case DWC_HCSPLIT_XACTPOS_MID: ++ /* ++ * For BEGIN or MID, calculate the length for ++ * the next microframe to determine the correct ++ * SSPLIT token, either MID or END. ++ */ ++ do { ++ struct usb_iso_packet_descriptor *frame_desc; ++ ++ frame_desc = &_qtd->urb->iso_frame_desc[_qtd->isoc_frame_index]; ++ _qtd->isoc_split_offset += 188; ++ ++ if ((frame_desc->length - _qtd->isoc_split_offset) <= 188) { ++ _qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_END; ++ } ++ else { ++ _qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_MID; ++ } ++ ++ } while(0); ++ break; ++ } ++ } else { ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_ACK, must_free); ++ } ++ } else { ++ _qtd->error_count = 0; ++ ++ if (_hc->qh->ping_state) { ++ _hc->qh->ping_state = 0; ++ /* ++ * Halt the channel so the transfer can be re-started ++ * from the appropriate point. This only happens in ++ * Slave mode. In DMA mode, the ping_state is cleared ++ * when the transfer is started because the core ++ * automatically executes the PING, then the transfer. ++ */ ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_ACK, must_free); ++ } else { ++ halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); ++ } ++ } ++ ++ /* ++ * If the ACK occurred when _not_ in the PING state, let the channel ++ * continue transferring data after clearing the error count. ++ */ ++ ++ disable_hc_int(_hc_regs,ack); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel NYET interrupt. This interrupt should only occur on ++ * Bulk and Control OUT endpoints and for complete split transactions. If a ++ * NYET occurs at the same time as a Transfer Complete interrupt, it is ++ * handled in the xfercomp interrupt handler, not here. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NYET Received--\n", _hc->hc_num); ++ ++ /* ++ * NYET on CSPLIT ++ * re-do the CSPLIT immediately on non-periodic ++ */ ++ if ((_hc->do_split) && (_hc->complete_split)) { ++ if ((_hc->ep_type == DWC_OTG_EP_TYPE_INTR) || ++ (_hc->ep_type == DWC_OTG_EP_TYPE_ISOC)) { ++ int frnum = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(_hcd)); ++ ++ if (dwc_full_frame_num(frnum) != ++ dwc_full_frame_num(_hc->qh->sched_frame)) { ++ /* ++ * No longer in the same full speed frame. ++ * Treat this as a transaction error. ++ */ ++#if 0 ++ /** @todo Fix system performance so this can ++ * be treated as an error. Right now complete ++ * splits cannot be scheduled precisely enough ++ * due to other system activity, so this error ++ * occurs regularly in Slave mode. ++ */ ++ _qtd->error_count++; ++#endif ++ _qtd->complete_split = 0; ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_XACT_ERR, must_free); ++ /** @todo add support for isoc release */ ++ goto handle_nyet_done; ++ } ++ } ++ ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NYET, must_free); ++ goto handle_nyet_done; ++ } ++ ++ _hc->qh->ping_state = 1; ++ _qtd->error_count = 0; ++ ++ update_urb_state_xfer_intr(_hc, _hc_regs, _qtd->urb, _qtd, ++ DWC_OTG_HC_XFER_NYET); ++ save_data_toggle(_hc, _hc_regs, _qtd); ++ ++ /* ++ * Halt the channel and re-start the transfer so the PING ++ * protocol will start. ++ */ ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_NYET, must_free); ++ ++handle_nyet_done: ++ disable_hc_int(_hc_regs,nyet); ++ clear_hc_int(_hc_regs, nyet); ++ return 1; ++} ++ ++/** ++ * Handles a host channel babble interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_babble_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Babble Error--\n", _hc->hc_num); ++ if (_hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ dwc_otg_hcd_complete_urb(_hcd, _qtd->urb, -EOVERFLOW); ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_BABBLE_ERR, must_free); ++ } else { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(_hcd, _hc, _hc_regs, _qtd, ++ DWC_OTG_HC_XFER_BABBLE_ERR); ++ halt_channel(_hcd, _hc, _qtd, halt_status, must_free); ++ } ++ disable_hc_int(_hc_regs,bblerr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel AHB error interrupt. This handler is only called in ++ * DMA mode. ++ */ ++static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t *_hc, ++ dwc_otg_hc_regs_t *_hc_regs, ++ dwc_otg_qtd_t *_qtd) ++{ ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ struct urb *urb = _qtd->urb; ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "AHB Error--\n", _hc->hc_num); ++ ++ hcchar.d32 = dwc_read_reg32(&_hc_regs->hcchar); ++ hcsplt.d32 = dwc_read_reg32(&_hc_regs->hcsplt); ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ hcdma = dwc_read_reg32(&_hc_regs->hcdma); ++ ++ DWC_ERROR("AHB ERROR, Channel %d\n", _hc->hc_num); ++ DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n"); ++ DWC_ERROR(" Device address: %d\n", usb_pipedevice(urb->pipe)); ++ DWC_ERROR(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ DWC_ERROR(" Endpoint type: %s\n", ++ ({char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CONTROL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break; ++ default: pipetype = "UNKNOWN"; break; ++ }; pipetype;})); ++ DWC_ERROR(" Speed: %s\n", ++ ({char *speed; ++ switch (urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HIGH"; break; ++ case USB_SPEED_FULL: speed = "FULL"; break; ++ case USB_SPEED_LOW: speed = "LOW"; break; ++ default: speed = "UNKNOWN"; break; ++ }; speed;})); ++ DWC_ERROR(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_ERROR(" Data buffer length: %d\n", urb->transfer_buffer_length); ++ DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)(u32)urb->transfer_dma); ++ DWC_ERROR(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)(u32)urb->setup_dma); ++ DWC_ERROR(" Interval: %d\n", urb->interval); ++ ++ dwc_otg_hcd_complete_urb(_hcd, urb, -EIO); ++ ++ /* ++ * Force a channel halt. Don't call halt_channel because that won't ++ * write to the HCCHARn register in DMA mode to force the halt. ++ */ ++ dwc_otg_hc_halt(_hcd->core_if, _hc, DWC_OTG_HC_XFER_AHB_ERR); ++ ++ disable_hc_int(_hc_regs,ahberr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel transaction error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transaction Error--\n", _hc->hc_num); ++ ++ switch (usb_pipetype(_qtd->urb->pipe)) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ _qtd->error_count++; ++ if (!_hc->qh->ping_state) { ++ update_urb_state_xfer_intr(_hc, _hc_regs, _qtd->urb, ++ _qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ save_data_toggle(_hc, _hc_regs, _qtd); ++ if (!_hc->ep_is_in && _qtd->urb->dev->speed == USB_SPEED_HIGH) { ++ _hc->qh->ping_state = 1; ++ } ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will start. ++ */ ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_XACT_ERR, must_free); ++ break; ++ case PIPE_INTERRUPT: ++ _qtd->error_count++; ++ if ((_hc->do_split) && (_hc->complete_split)) { ++ _qtd->complete_split = 0; ++ } ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_XACT_ERR, must_free); ++ break; ++ case PIPE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(_hcd, _hc, _hc_regs, _qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ ++ halt_channel(_hcd, _hc, _qtd, halt_status, must_free); ++ } ++ break; ++ } ++ ++ ++ disable_hc_int(_hc_regs,xacterr); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel frame overrun interrupt. This handler may be called ++ * in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Frame Overrun--\n", _hc->hc_num); ++ ++ switch (usb_pipetype(_qtd->urb->pipe)) { ++ case PIPE_CONTROL: ++ case PIPE_BULK: ++ break; ++ case PIPE_INTERRUPT: ++ halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN, must_free); ++ break; ++ case PIPE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(_hcd, _hc, _hc_regs, _qtd, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ ++ halt_channel(_hcd, _hc, _qtd, halt_status, must_free); ++ } ++ break; ++ } ++ ++ disable_hc_int(_hc_regs,frmovrun); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel data toggle error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Data Toggle Error--\n", _hc->hc_num); ++ ++ if (_hc->ep_is_in) { ++ _qtd->error_count = 0; ++ } else { ++ DWC_ERROR("Data Toggle Error on OUT transfer," ++ "channel %d\n", _hc->hc_num); ++ } ++ ++ disable_hc_int(_hc_regs,datatglerr); ++ ++ return 1; ++} ++ ++#ifdef DEBUG ++/** ++ * This function is for debug only. It checks that a valid halt status is set ++ * and that HCCHARn.chdis is clear. If there's a problem, corrective action is ++ * taken and a warning is issued. ++ * @return 1 if halt status is ok, 0 otherwise. ++ */ ++static inline int halt_status_ok(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hcsplt_data_t hcsplt; ++ ++ if (_hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) { ++ /* ++ * This code is here only as a check. This condition should ++ * never happen. Ignore the halt if it does occur. ++ */ ++ hcchar.d32 = dwc_read_reg32(&_hc_regs->hcchar); ++ hctsiz.d32 = dwc_read_reg32(&_hc_regs->hctsiz); ++ hcint.d32 = dwc_read_reg32(&_hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&_hc_regs->hcintmsk); ++ hcsplt.d32 = dwc_read_reg32(&_hc_regs->hcsplt); ++ DWC_WARN("%s: _hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, " ++ "channel %d, hcchar 0x%08x, hctsiz 0x%08x, " ++ "hcint 0x%08x, hcintmsk 0x%08x, " ++ "hcsplt 0x%08x, qtd->complete_split %d\n", ++ __func__, _hc->hc_num, hcchar.d32, hctsiz.d32, ++ hcint.d32, hcintmsk.d32, ++ hcsplt.d32, _qtd->complete_split); ++ ++ DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n", ++ __func__, _hc->hc_num); ++ DWC_WARN("\n"); ++ clear_hc_int(_hc_regs,chhltd); ++ return 0; ++ } ++ ++ /* ++ * This code is here only as a check. hcchar.chdis should ++ * never be set when the halt interrupt occurs. Halt the ++ * channel again if it does occur. ++ */ ++ hcchar.d32 = dwc_read_reg32(&_hc_regs->hcchar); ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: hcchar.chdis set unexpectedly, " ++ "hcchar 0x%08x, trying to halt again\n", ++ __func__, hcchar.d32); ++ clear_hc_int(_hc_regs,chhltd); ++ _hc->halt_pending = 0; ++ halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); ++ return 0; ++ } ++ ++ return 1; ++} ++#endif ++ ++/** ++ * Handles a host Channel Halted interrupt in DMA mode. This handler ++ * determines the reason the channel halted and proceeds accordingly. ++ */ ++static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ ++ if (_hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ _hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ /* ++ * Just release the channel. A dequeue can happen on a ++ * transfer timeout. In the case of an AHB Error, the channel ++ * was forced to halt because there's no way to gracefully ++ * recover. ++ */ ++ release_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); ++ return; ++ } ++ ++ /* Read the HCINTn register to determine the cause for the halt. */ ++ hcint.d32 = dwc_read_reg32(&_hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&_hc_regs->hcintmsk); ++ ++ if (hcint.b.xfercomp) { ++ /** @todo This is here because of a possible hardware bug. Spec ++ * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT ++ * interrupt w/ACK bit set should occur, but I only see the ++ * XFERCOMP bit, even with it masked out. This is a workaround ++ * for that behavior. Should fix this when hardware is fixed. ++ */ ++ if ((_hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && (!_hc->ep_is_in)) { ++ handle_hc_ack_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } ++ handle_hc_xfercomp_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.stall) { ++ handle_hc_stall_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.xacterr) { ++ /* ++ * Must handle xacterr before nak or ack. Could get a xacterr ++ * at the same time as either of these on a BULK/CONTROL OUT ++ * that started with a PING. The xacterr takes precedence. ++ */ ++ handle_hc_xacterr_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.nyet) { ++ /* ++ * Must handle nyet before nak or ack. Could get a nyet at the ++ * same time as either of those on a BULK/CONTROL OUT that ++ * started with a PING. The nyet takes precedence. ++ */ ++ handle_hc_nyet_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.frmovrun) { ++ handle_hc_frmovrun_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.datatglerr) { ++ handle_hc_datatglerr_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ _hc->qh->data_toggle = 0; ++ halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); ++ } else if (hcint.b.nak && !hcintmsk.b.nak) { ++ /* ++ * If nak is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the nak is handled by ++ * the nak interrupt handler, not here. Handle nak here for ++ * BULK/CONTROL OUT transfers, which halt on a NAK to allow ++ * rewinding the buffer pointer. ++ */ ++ handle_hc_nak_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else if (hcint.b.ack && !hcintmsk.b.ack) { ++ /* ++ * If ack is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the ack is handled by ++ * the ack interrupt handler, not here. Handle ack here for ++ * split transfers. Start splits halt on ACK. ++ */ ++ handle_hc_ack_intr(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else { ++ if (_hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ _hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * A periodic transfer halted with no other channel ++ * interrupts set. Assume it was halted by the core ++ * because it could not be completed in its scheduled ++ * (micro)frame. ++ */ ++#ifdef DEBUG ++ DWC_PRINT("%s: Halt channel %d (assume incomplete periodic transfer)\n", ++ __func__, _hc->hc_num); ++#endif /* */ ++ halt_channel(_hcd, _hc, _qtd, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE, must_free); ++ } else { ++#ifdef DEBUG ++ DWC_ERROR("%s: Channel %d, DMA Mode -- ChHltd set, but reason " ++ "for halting is unknown, nyet %d, hcint 0x%08x, intsts 0x%08x\n", ++ __func__, _hc->hc_num, hcint.b.nyet, hcint.d32, ++ dwc_read_reg32(&_hcd->core_if->core_global_regs->gintsts)); ++#endif ++ halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); ++ } ++ } ++} ++ ++/** ++ * Handles a host channel Channel Halted interrupt. ++ * ++ * In slave mode, this handler is called only when the driver specifically ++ * requests a halt. This occurs during handling other host channel interrupts ++ * (e.g. nak, xacterr, stall, nyet, etc.). ++ * ++ * In DMA mode, this is the interrupt that occurs when the core has finished ++ * processing a transfer on a channel. Other host channel interrupts (except ++ * ahberr) are disabled in DMA mode. ++ */ ++static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t *_hcd, ++ dwc_hc_t * _hc, dwc_otg_hc_regs_t * _hc_regs, dwc_otg_qtd_t * _qtd, int *must_free) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Channel Halted--\n", _hc->hc_num); ++ ++ if (_hcd->core_if->dma_enable) { ++ handle_hc_chhltd_intr_dma(_hcd, _hc, _hc_regs, _qtd, must_free); ++ } else { ++#ifdef DEBUG ++ if (!halt_status_ok(_hcd, _hc, _hc_regs, _qtd, must_free)) { ++ return 1; ++ } ++#endif /* */ ++ release_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); ++ } ++ ++ return 1; ++} ++ ++/** Handles interrupt for a specific Host Channel */ ++int32_t dwc_otg_hcd_handle_hc_n_intr (dwc_otg_hcd_t *_dwc_otg_hcd, uint32_t _num) ++{ ++ int must_free = 0; ++ int retval = 0; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ dwc_hc_t *hc; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", _num); ++ ++ hc = _dwc_otg_hcd->hc_ptr_array[_num]; ++ hc_regs = _dwc_otg_hcd->core_if->host_if->hc_regs[_num]; ++ qtd = list_entry(hc->qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); ++ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ DWC_DEBUGPL(DBG_HCDV, " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", ++ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); ++ hcint.d32 = hcint.d32 & hcintmsk.d32; ++ ++ if (!_dwc_otg_hcd->core_if->dma_enable) { ++ if ((hcint.b.chhltd) && (hcint.d32 != 0x2)) { ++ hcint.b.chhltd = 0; ++ } ++ } ++ ++ if (hcint.b.xfercomp) { ++ retval |= handle_hc_xfercomp_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ /* ++ * If NYET occurred at same time as Xfer Complete, the NYET is ++ * handled by the Xfer Complete interrupt handler. Don't want ++ * to call the NYET interrupt handler in this case. ++ */ ++ hcint.b.nyet = 0; ++ } ++ if (hcint.b.chhltd) { ++ retval |= handle_hc_chhltd_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.ahberr) { ++ retval |= handle_hc_ahberr_intr(_dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.stall) { ++ retval |= handle_hc_stall_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.nak) { ++ retval |= handle_hc_nak_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.ack) { ++ retval |= handle_hc_ack_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.nyet) { ++ retval |= handle_hc_nyet_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.xacterr) { ++ retval |= handle_hc_xacterr_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.bblerr) { ++ retval |= handle_hc_babble_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.frmovrun) { ++ retval |= handle_hc_frmovrun_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ if (hcint.b.datatglerr) { ++ retval |= handle_hc_datatglerr_intr(_dwc_otg_hcd, hc, hc_regs, qtd, &must_free); ++ } ++ ++ /* ++ * Logic to free the qtd here, at the end of the hc intr ++ * processing, if the handling of this interrupt determined ++ * that it needs to be freed. ++ */ ++ if (must_free) { ++ /* Free the qtd here now that we are done using it. */ ++ dwc_otg_hcd_qtd_free(qtd); ++ } ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c +new file mode 100644 +index 0000000..fcb5ce6 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd_queue.c +@@ -0,0 +1,794 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_hcd_queue.c $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 537387 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the functions to manage Queue Heads and Queue ++ * Transfer Descriptors. ++ */ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "dwc_otg_driver.h" ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** ++ * This function allocates and initializes a QH. ++ * ++ * @param _hcd The HCD state structure for the DWC OTG controller. ++ * @param[in] _urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. ++ * ++ * @return Returns pointer to the newly allocated QH, or NULL on error. */ ++dwc_otg_qh_t *dwc_otg_hcd_qh_create (dwc_otg_hcd_t *_hcd, struct urb *_urb) ++{ ++ dwc_otg_qh_t *qh; ++ ++ /* Allocate memory */ ++ /** @todo add memflags argument */ ++ qh = dwc_otg_hcd_qh_alloc (); ++ if (qh == NULL) { ++ return NULL; ++ } ++ ++ dwc_otg_hcd_qh_init (_hcd, qh, _urb); ++ return qh; ++} ++ ++/** Free each QTD in the QH's QTD-list then free the QH. QH should already be ++ * removed from a list. QTD list should already be empty if called from URB ++ * Dequeue. ++ * ++ * @param[in] _qh The QH to free. ++ */ ++void dwc_otg_hcd_qh_free (dwc_otg_qh_t *_qh) ++{ ++ dwc_otg_qtd_t *qtd; ++ struct list_head *pos; ++ unsigned long flags; ++ ++ /* Free each QTD in the QTD list */ ++ local_irq_save (flags); ++ for (pos = _qh->qtd_list.next; ++ pos != &_qh->qtd_list; ++ pos = _qh->qtd_list.next) ++ { ++ list_del (pos); ++ qtd = dwc_list_to_qtd (pos); ++ dwc_otg_hcd_qtd_free (qtd); ++ } ++ local_irq_restore (flags); ++ ++ kfree (_qh); ++ return; ++} ++ ++/** Initializes a QH structure. ++ * ++ * @param[in] _hcd The HCD state structure for the DWC OTG controller. ++ * @param[in] _qh The QH to init. ++ * @param[in] _urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. */ ++#define SCHEDULE_SLOP 10 ++void dwc_otg_hcd_qh_init(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, struct urb *_urb) ++{ ++ memset (_qh, 0, sizeof (dwc_otg_qh_t)); ++ ++ /* Initialize QH */ ++ switch (usb_pipetype(_urb->pipe)) { ++ case PIPE_CONTROL: ++ _qh->ep_type = USB_ENDPOINT_XFER_CONTROL; ++ break; ++ case PIPE_BULK: ++ _qh->ep_type = USB_ENDPOINT_XFER_BULK; ++ break; ++ case PIPE_ISOCHRONOUS: ++ _qh->ep_type = USB_ENDPOINT_XFER_ISOC; ++ break; ++ case PIPE_INTERRUPT: ++ _qh->ep_type = USB_ENDPOINT_XFER_INT; ++ break; ++ } ++ ++ _qh->ep_is_in = usb_pipein(_urb->pipe) ? 1 : 0; ++ ++ _qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ _qh->maxp = usb_maxpacket(_urb->dev, _urb->pipe, !(usb_pipein(_urb->pipe))); ++ INIT_LIST_HEAD(&_qh->qtd_list); ++ INIT_LIST_HEAD(&_qh->qh_list_entry); ++ _qh->channel = NULL; ++ ++ /* FS/LS Enpoint on HS Hub ++ * NOT virtual root hub */ ++ _qh->do_split = 0; ++ _qh->speed = _urb->dev->speed; ++ if (((_urb->dev->speed == USB_SPEED_LOW) || ++ (_urb->dev->speed == USB_SPEED_FULL)) && ++ (_urb->dev->tt) && (_urb->dev->tt->hub) && (_urb->dev->tt->hub->devnum != 1)) { ++ DWC_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n", ++ usb_pipeendpoint(_urb->pipe), _urb->dev->tt->hub->devnum, ++ _urb->dev->ttport); ++ _qh->do_split = 1; ++ } ++ ++ if (_qh->ep_type == USB_ENDPOINT_XFER_INT || ++ _qh->ep_type == USB_ENDPOINT_XFER_ISOC) { ++ /* Compute scheduling parameters once and save them. */ ++ hprt0_data_t hprt; ++ ++ /** @todo Account for split transfers in the bus time. */ ++ int bytecount = dwc_hb_mult(_qh->maxp) * dwc_max_packet(_qh->maxp); ++ _qh->usecs = NS_TO_US(usb_calc_bus_time(_urb->dev->speed, ++ usb_pipein(_urb->pipe), ++ (_qh->ep_type == USB_ENDPOINT_XFER_ISOC),bytecount)); ++ ++ /* Start in a slightly future (micro)frame. */ ++ _qh->sched_frame = dwc_frame_num_inc(_hcd->frame_number, SCHEDULE_SLOP); ++ _qh->interval = _urb->interval; ++#if 0 ++ /* Increase interrupt polling rate for debugging. */ ++ if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ _qh->interval = 8; ++ } ++#endif ++ hprt.d32 = dwc_read_reg32(_hcd->core_if->host_if->hprt0); ++ if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) && ++ ((_urb->dev->speed == USB_SPEED_LOW) || ++ (_urb->dev->speed == USB_SPEED_FULL))) ++ { ++ _qh->interval *= 8; ++ _qh->sched_frame |= 0x7; ++ _qh->start_split_frame = _qh->sched_frame; ++ } ++ } ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", _qh); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n", ++ _urb->dev->devnum); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n", ++ usb_pipeendpoint(_urb->pipe), ++ usb_pipein(_urb->pipe) == USB_DIR_IN ? "IN" : "OUT"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", ++ ({ char *speed; switch (_urb->dev->speed) { ++ case USB_SPEED_LOW: speed = "low"; break; ++ case USB_SPEED_FULL: speed = "full"; break; ++ case USB_SPEED_HIGH: speed = "high"; break; ++ default: speed = "?"; break; ++ }; speed;})); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", ++ ({ char *type; switch (_qh->ep_type) { ++ case USB_ENDPOINT_XFER_ISOC: type = "isochronous"; break; ++ case USB_ENDPOINT_XFER_INT: type = "interrupt"; break; ++ case USB_ENDPOINT_XFER_CONTROL: type = "control"; break; ++ case USB_ENDPOINT_XFER_BULK: type = "bulk"; break; ++ default: type = "?"; break; ++ }; type;})); ++#ifdef DEBUG ++ if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n", ++ _qh->usecs); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n", ++ _qh->interval); ++ } ++#endif ++ ++ return; ++} ++ ++/** ++ * Microframe scheduler ++ * track the total use in hcd->frame_usecs ++ * keep each qh use in qh->frame_usecs ++ * when surrendering the qh then donate the time back ++ */ ++const unsigned short max_uframe_usecs[]={ 100, 100, 100, 100, 100, 100, 30, 0 }; ++ ++/* ++ * called from dwc_otg_hcd.c:dwc_otg_hcd_init ++ */ ++int init_hcd_usecs(dwc_otg_hcd_t *_hcd) ++{ ++ int i; ++ for (i=0; i<8; i++) { ++ _hcd->frame_usecs[i] = max_uframe_usecs[i]; ++ } ++ return 0; ++} ++ ++static int find_single_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) ++{ ++ int i; ++ unsigned short utime; ++ int t_left; ++ int ret; ++ int done; ++ ++ ret = -1; ++ utime = _qh->usecs; ++ t_left = utime; ++ i = 0; ++ done = 0; ++ while (done == 0) { ++ /* At the start _hcd->frame_usecs[i] = max_uframe_usecs[i]; */ ++ if (utime <= _hcd->frame_usecs[i]) { ++ _hcd->frame_usecs[i] -= utime; ++ _qh->frame_usecs[i] += utime; ++ t_left -= utime; ++ ret = i; ++ done = 1; ++ return ret; ++ } else { ++ i++; ++ if (i == 8) { ++ done = 1; ++ ret = -1; ++ } ++ } ++ } ++ return ret; ++} ++ ++/* ++ * use this for FS apps that can span multiple uframes ++ */ ++static int find_multi_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) ++{ ++ int i; ++ int j; ++ unsigned short utime; ++ int t_left; ++ int ret; ++ int done; ++ unsigned short xtime; ++ ++ ret = -1; ++ utime = _qh->usecs; ++ t_left = utime; ++ i = 0; ++ done = 0; ++loop: ++ while (done == 0) { ++ if(_hcd->frame_usecs[i] <= 0) { ++ i++; ++ if (i == 8) { ++ done = 1; ++ ret = -1; ++ } ++ goto loop; ++ } ++ ++ /* ++ * we need n consequtive slots ++ * so use j as a start slot j plus j+1 must be enough time (for now) ++ */ ++ xtime= _hcd->frame_usecs[i]; ++ for (j = i+1 ; j < 8 ; j++ ) { ++ /* ++ * if we add this frame remaining time to xtime we may ++ * be OK, if not we need to test j for a complete frame ++ */ ++ if ((xtime+_hcd->frame_usecs[j]) < utime) { ++ if (_hcd->frame_usecs[j] < max_uframe_usecs[j]) { ++ j = 8; ++ ret = -1; ++ continue; ++ } ++ } ++ if (xtime >= utime) { ++ ret = i; ++ j = 8; /* stop loop with a good value ret */ ++ continue; ++ } ++ /* add the frame time to x time */ ++ xtime += _hcd->frame_usecs[j]; ++ /* we must have a fully available next frame or break */ ++ if ((xtime < utime) ++ && (_hcd->frame_usecs[j] == max_uframe_usecs[j])) { ++ ret = -1; ++ j = 8; /* stop loop with a bad value ret */ ++ continue; ++ } ++ } ++ if (ret >= 0) { ++ t_left = utime; ++ for (j = i; (t_left>0) && (j < 8); j++ ) { ++ t_left -= _hcd->frame_usecs[j]; ++ if ( t_left <= 0 ) { ++ _qh->frame_usecs[j] += _hcd->frame_usecs[j] + t_left; ++ _hcd->frame_usecs[j]= -t_left; ++ ret = i; ++ done = 1; ++ } else { ++ _qh->frame_usecs[j] += _hcd->frame_usecs[j]; ++ _hcd->frame_usecs[j] = 0; ++ } ++ } ++ } else { ++ i++; ++ if (i == 8) { ++ done = 1; ++ ret = -1; ++ } ++ } ++ } ++ return ret; ++} ++ ++static int find_uframe(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) ++{ ++ int ret; ++ ret = -1; ++ ++ if (_qh->speed == USB_SPEED_HIGH) { ++ /* if this is a hs transaction we need a full frame */ ++ ret = find_single_uframe(_hcd, _qh); ++ } else { ++ /* if this is a fs transaction we may need a sequence of frames */ ++ ret = find_multi_uframe(_hcd, _qh); ++ } ++ return ret; ++} ++ ++/** ++ * Checks that the max transfer size allowed in a host channel is large enough ++ * to handle the maximum data transfer in a single (micro)frame for a periodic ++ * transfer. ++ * ++ * @param _hcd The HCD state structure for the DWC OTG controller. ++ * @param _qh QH for a periodic endpoint. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_max_xfer_size(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) ++{ ++ int status; ++ uint32_t max_xfer_size; ++ uint32_t max_channel_xfer_size; ++ ++ status = 0; ++ ++ max_xfer_size = dwc_max_packet(_qh->maxp) * dwc_hb_mult(_qh->maxp); ++ max_channel_xfer_size = _hcd->core_if->core_params->max_transfer_size; ++ ++ if (max_xfer_size > max_channel_xfer_size) { ++ DWC_NOTICE("%s: Periodic xfer length %d > " ++ "max xfer length for channel %d\n", ++ __func__, max_xfer_size, max_channel_xfer_size); ++ status = -ENOSPC; ++ } ++ ++ return status; ++} ++ ++/** ++ * Schedules an interrupt or isochronous transfer in the periodic schedule. ++ * ++ * @param _hcd The HCD state structure for the DWC OTG controller. ++ * @param _qh QH for the periodic transfer. The QH should already contain the ++ * scheduling information. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int schedule_periodic(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) ++{ ++ int status = 0; ++ ++ int frame; ++ status = find_uframe(_hcd, _qh); ++ frame = -1; ++ if (status == 0) { ++ frame = 7; ++ } else { ++ if (status > 0 ) ++ frame = status-1; ++ } ++ ++ /* Set the new frame up */ ++ if (frame > -1) { ++ _qh->sched_frame &= ~0x7; ++ _qh->sched_frame |= (frame & 7); ++ } ++ ++ if (status != -1 ) ++ status = 0; ++ if (status) { ++ DWC_NOTICE("%s: Insufficient periodic bandwidth for " ++ "periodic transfer.\n", __func__); ++ return status; ++ } ++ ++ status = check_max_xfer_size(_hcd, _qh); ++ if (status) { ++ DWC_NOTICE("%s: Channel max transfer size too small " ++ "for periodic transfer.\n", __func__); ++ return status; ++ } ++ ++ /* Always start in the inactive schedule. */ ++ list_add_tail(&_qh->qh_list_entry, &_hcd->periodic_sched_inactive); ++ ++ ++ /* Update claimed usecs per (micro)frame. */ ++ _hcd->periodic_usecs += _qh->usecs; ++ ++ /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */ ++ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_allocated += _qh->usecs / _qh->interval; ++ if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_int_reqs++; ++ DWC_DEBUGPL(DBG_HCD, "Scheduled intr: qh %p, usecs %d, period %d\n", ++ _qh, _qh->usecs, _qh->interval); ++ } else { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_isoc_reqs++; ++ DWC_DEBUGPL(DBG_HCD, "Scheduled isoc: qh %p, usecs %d, period %d\n", ++ _qh, _qh->usecs, _qh->interval); ++ } ++ ++ return status; ++} ++ ++/** ++ * This function adds a QH to either the non periodic or periodic schedule if ++ * it is not already in the schedule. If the QH is already in the schedule, no ++ * action is taken. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_add (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) ++{ ++ unsigned long flags; ++ int status = 0; ++ ++ local_irq_save(flags); ++ ++ if (!list_empty(&_qh->qh_list_entry)) { ++ /* QH already in a schedule. */ ++ goto done; ++ } ++ ++ /* Add the new QH to the appropriate schedule */ ++ if (dwc_qh_is_non_per(_qh)) { ++ /* Always start in the inactive schedule. */ ++ list_add_tail(&_qh->qh_list_entry, &_hcd->non_periodic_sched_inactive); ++ } else { ++ status = schedule_periodic(_hcd, _qh); ++ } ++ ++ done: ++ local_irq_restore(flags); ++ ++ return status; ++} ++ ++/** ++ * This function adds a QH to the non periodic deferred schedule. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_add_deferred(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ if (!list_empty(&_qh->qh_list_entry)) { ++ /* QH already in a schedule. */ ++ goto done; ++ } ++ ++ /* Add the new QH to the non periodic deferred schedule */ ++ if (dwc_qh_is_non_per(_qh)) { ++ list_add_tail(&_qh->qh_list_entry, ++ &_hcd->non_periodic_sched_deferred); ++ } ++done: ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/** ++ * Removes an interrupt or isochronous transfer from the periodic schedule. ++ * ++ * @param _hcd The HCD state structure for the DWC OTG controller. ++ * @param _qh QH for the periodic transfer. ++ */ ++static void deschedule_periodic(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) ++{ ++ int i; ++ list_del_init(&_qh->qh_list_entry); ++ ++ ++ /* Update claimed usecs per (micro)frame. */ ++ _hcd->periodic_usecs -= _qh->usecs; ++ ++ for (i = 0; i < 8; i++) { ++ _hcd->frame_usecs[i] += _qh->frame_usecs[i]; ++ _qh->frame_usecs[i] = 0; ++ } ++ /* Update average periodic bandwidth claimed and # periodic reqs for usbfs. */ ++ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_allocated -= _qh->usecs / _qh->interval; ++ ++ if (_qh->ep_type == USB_ENDPOINT_XFER_INT) { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_int_reqs--; ++ DWC_DEBUGPL(DBG_HCD, "Descheduled intr: qh %p, usecs %d, period %d\n", ++ _qh, _qh->usecs, _qh->interval); ++ } else { ++ hcd_to_bus(dwc_otg_hcd_to_hcd(_hcd))->bandwidth_isoc_reqs--; ++ DWC_DEBUGPL(DBG_HCD, "Descheduled isoc: qh %p, usecs %d, period %d\n", ++ _qh, _qh->usecs, _qh->interval); ++ } ++} ++ ++/** ++ * Removes a QH from either the non-periodic or periodic schedule. Memory is ++ * not freed. ++ * ++ * @param[in] _hcd The HCD state structure. ++ * @param[in] _qh QH to remove from schedule. */ ++void dwc_otg_hcd_qh_remove (dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ if (list_empty(&_qh->qh_list_entry)) { ++ /* QH is not in a schedule. */ ++ goto done; ++ } ++ ++ if (dwc_qh_is_non_per(_qh)) { ++ if (_hcd->non_periodic_qh_ptr == &_qh->qh_list_entry) { ++ _hcd->non_periodic_qh_ptr = _hcd->non_periodic_qh_ptr->next; ++ } ++ list_del_init(&_qh->qh_list_entry); ++ } else { ++ deschedule_periodic(_hcd, _qh); ++ } ++ ++ done: ++ local_irq_restore(flags); ++} ++ ++/** ++ * Defers a QH. For non-periodic QHs, removes the QH from the active ++ * non-periodic schedule. The QH is added to the deferred non-periodic ++ * schedule if any QTDs are still attached to the QH. ++ */ ++int dwc_otg_hcd_qh_deferr(dwc_otg_hcd_t * _hcd, dwc_otg_qh_t * _qh, int delay) ++{ ++ int deact = 1; ++ unsigned long flags; ++ local_irq_save(flags); ++ if (dwc_qh_is_non_per(_qh)) { ++ _qh->sched_frame = ++ dwc_frame_num_inc(_hcd->frame_number, ++ delay); ++ _qh->channel = NULL; ++ _qh->qtd_in_process = NULL; ++ deact = 0; ++ dwc_otg_hcd_qh_remove(_hcd, _qh); ++ if (!list_empty(&_qh->qtd_list)) { ++ /* Add back to deferred non-periodic schedule. */ ++ dwc_otg_hcd_qh_add_deferred(_hcd, _qh); ++ } ++ } ++ local_irq_restore(flags); ++ return deact; ++} ++ ++/** ++ * Deactivates a QH. For non-periodic QHs, removes the QH from the active ++ * non-periodic schedule. The QH is added to the inactive non-periodic ++ * schedule if any QTDs are still attached to the QH. ++ * ++ * For periodic QHs, the QH is removed from the periodic queued schedule. If ++ * there are any QTDs still attached to the QH, the QH is added to either the ++ * periodic inactive schedule or the periodic ready schedule and its next ++ * scheduled frame is calculated. The QH is placed in the ready schedule if ++ * the scheduled frame has been reached already. Otherwise it's placed in the ++ * inactive schedule. If there are no QTDs attached to the QH, the QH is ++ * completely removed from the periodic schedule. ++ */ ++void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t *_hcd, dwc_otg_qh_t *_qh, int sched_next_periodic_split) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ if (dwc_qh_is_non_per(_qh)) { ++ dwc_otg_hcd_qh_remove(_hcd, _qh); ++ if (!list_empty(&_qh->qtd_list)) { ++ /* Add back to inactive non-periodic schedule. */ ++ dwc_otg_hcd_qh_add(_hcd, _qh); ++ } ++ } else { ++ uint16_t frame_number = dwc_otg_hcd_get_frame_number(dwc_otg_hcd_to_hcd(_hcd)); ++ ++ if (_qh->do_split) { ++ /* Schedule the next continuing periodic split transfer */ ++ if (sched_next_periodic_split) { ++ ++ _qh->sched_frame = frame_number; ++ if (dwc_frame_num_le(frame_number, ++ dwc_frame_num_inc(_qh->start_split_frame, 1))) { ++ /* ++ * Allow one frame to elapse after start ++ * split microframe before scheduling ++ * complete split, but DONT if we are ++ * doing the next start split in the ++ * same frame for an ISOC out. ++ */ ++ if ((_qh->ep_type != USB_ENDPOINT_XFER_ISOC) || (_qh->ep_is_in != 0)) { ++ _qh->sched_frame = dwc_frame_num_inc(_qh->sched_frame, 1); ++ } ++ } ++ } else { ++ _qh->sched_frame = dwc_frame_num_inc(_qh->start_split_frame, ++ _qh->interval); ++ if (dwc_frame_num_le(_qh->sched_frame, frame_number)) { ++ _qh->sched_frame = frame_number; ++ } ++ _qh->sched_frame |= 0x7; ++ _qh->start_split_frame = _qh->sched_frame; ++ } ++ } else { ++ _qh->sched_frame = dwc_frame_num_inc(_qh->sched_frame, _qh->interval); ++ if (dwc_frame_num_le(_qh->sched_frame, frame_number)) { ++ _qh->sched_frame = frame_number; ++ } ++ } ++ ++ if (list_empty(&_qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(_hcd, _qh); ++ } else { ++ /* ++ * Remove from periodic_sched_queued and move to ++ * appropriate queue. ++ */ ++ if (dwc_frame_num_le(_qh->sched_frame, frame_number)) { ++ list_move(&_qh->qh_list_entry, ++ &_hcd->periodic_sched_ready); ++ } else { ++ list_move(&_qh->qh_list_entry, ++ &_hcd->periodic_sched_inactive); ++ } ++ } ++ } ++ ++ local_irq_restore(flags); ++} ++ ++/** ++ * This function allocates and initializes a QTD. ++ * ++ * @param[in] _urb The URB to create a QTD from. Each URB-QTD pair will end up ++ * pointing to each other so each pair should have a unique correlation. ++ * ++ * @return Returns pointer to the newly allocated QTD, or NULL on error. */ ++dwc_otg_qtd_t *dwc_otg_hcd_qtd_create (struct urb *_urb) ++{ ++ dwc_otg_qtd_t *qtd; ++ ++ qtd = dwc_otg_hcd_qtd_alloc (); ++ if (qtd == NULL) { ++ return NULL; ++ } ++ ++ dwc_otg_hcd_qtd_init (qtd, _urb); ++ return qtd; ++} ++ ++/** ++ * Initializes a QTD structure. ++ * ++ * @param[in] _qtd The QTD to initialize. ++ * @param[in] _urb The URB to use for initialization. */ ++void dwc_otg_hcd_qtd_init (dwc_otg_qtd_t *_qtd, struct urb *_urb) ++{ ++ memset (_qtd, 0, sizeof (dwc_otg_qtd_t)); ++ _qtd->urb = _urb; ++ if (usb_pipecontrol(_urb->pipe)) { ++ /* ++ * The only time the QTD data toggle is used is on the data ++ * phase of control transfers. This phase always starts with ++ * DATA1. ++ */ ++ _qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ _qtd->control_phase = DWC_OTG_CONTROL_SETUP; ++ } ++ ++ /* start split */ ++ _qtd->complete_split = 0; ++ _qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ _qtd->isoc_split_offset = 0; ++ ++ /* Store the qtd ptr in the urb to reference what QTD. */ ++ _urb->hcpriv = _qtd; ++ return; ++} ++ ++/** ++ * This function adds a QTD to the QTD-list of a QH. It will find the correct ++ * QH to place the QTD into. If it does not find a QH, then it will create a ++ * new QH. If the QH to which the QTD is added is not currently scheduled, it ++ * is placed into the proper schedule based on its EP type. ++ * ++ * @param[in] _qtd The QTD to add ++ * @param[in] _dwc_otg_hcd The DWC HCD structure ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * _qtd, dwc_otg_hcd_t * _dwc_otg_hcd) ++{ ++ struct usb_host_endpoint *ep; ++ dwc_otg_qh_t *qh; ++ unsigned long flags; ++ int retval = 0; ++ struct urb *urb = _qtd->urb; ++ ++ local_irq_save(flags); ++ ++ /* ++ * Get the QH which holds the QTD-list to insert to. Create QH if it ++ * doesn't exist. ++ */ ++ ep = dwc_urb_to_endpoint(urb); ++ qh = (dwc_otg_qh_t *)ep->hcpriv; ++ if (qh == NULL) { ++ qh = dwc_otg_hcd_qh_create (_dwc_otg_hcd, urb); ++ if (qh == NULL) { ++ retval = -1; ++ goto done; ++ } ++ ep->hcpriv = qh; ++ } ++ ++ _qtd->qtd_qh_ptr = qh; ++ retval = dwc_otg_hcd_qh_add(_dwc_otg_hcd, qh); ++ if (retval == 0) { ++ list_add_tail(&_qtd->qtd_list_entry, &qh->qtd_list); ++ } ++ ++ done: ++ local_irq_restore(flags); ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +diff --git a/drivers/usb/dwc_otg/dwc_otg_ifx.c b/drivers/usb/dwc_otg/dwc_otg_ifx.c +new file mode 100644 +index 0000000..0a4c209 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_ifx.c +@@ -0,0 +1,100 @@ ++/****************************************************************************** ++** ++** FILE NAME : dwc_otg_ifx.c ++** PROJECT : Twinpass/Danube ++** MODULES : DWC OTG USB ++** ++** DATE : 12 Auguest 2007 ++** AUTHOR : Sung Winder ++** DESCRIPTION : Platform specific initialization. ++** COPYRIGHT : Copyright (c) 2007 ++** Infineon Technologies AG ++** 2F, No.2, Li-Hsin Rd., Hsinchu Science Park, ++** Hsin-chu City, 300 Taiwan. ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** ++** HISTORY ++** $Date $Author $Comment ++** 12 Auguest 2007 Sung Winder Initiate Version ++*******************************************************************************/ ++#include "dwc_otg_ifx.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++//#include ++#include ++ ++#define IFXMIPS_GPIO_BASE_ADDR (0xBE100B00) ++ ++#define IFXMIPS_GPIO_P0_OUT ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0010)) ++#define IFXMIPS_GPIO_P1_OUT ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0040)) ++#define IFXMIPS_GPIO_P0_IN ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0014)) ++#define IFXMIPS_GPIO_P1_IN ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0044)) ++#define IFXMIPS_GPIO_P0_DIR ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0018)) ++#define IFXMIPS_GPIO_P1_DIR ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0048)) ++#define IFXMIPS_GPIO_P0_ALTSEL0 ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x001C)) ++#define IFXMIPS_GPIO_P1_ALTSEL0 ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x004C)) ++#define IFXMIPS_GPIO_P0_ALTSEL1 ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0020)) ++#define IFXMIPS_GPIO_P1_ALTSEL1 ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0050)) ++#define IFXMIPS_GPIO_P0_OD ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0024)) ++#define IFXMIPS_GPIO_P1_OD ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0054)) ++#define IFXMIPS_GPIO_P0_STOFF ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0028)) ++#define IFXMIPS_GPIO_P1_STOFF ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0058)) ++#define IFXMIPS_GPIO_P0_PUDSEL ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x002C)) ++#define IFXMIPS_GPIO_P1_PUDSEL ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x005C)) ++#define IFXMIPS_GPIO_P0_PUDEN ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0030)) ++#define IFXMIPS_GPIO_P1_PUDEN ((u32 *)(IFXMIPS_GPIO_BASE_ADDR + 0x0060)) ++ ++ ++#define writel ltq_w32 ++#define readl ltq_r32 ++void dwc_otg_power_on (void) ++{ ++ // clear power ++ writel(readl(DANUBE_PMU_PWDCR) | 0x41, DANUBE_PMU_PWDCR); ++ // set clock gating ++ writel(readl(DANUBE_CGU_IFCCR) | 0x30, DANUBE_CGU_IFCCR); ++ // set power ++ writel(readl(DANUBE_PMU_PWDCR) & ~0x1, DANUBE_PMU_PWDCR); ++ writel(readl(DANUBE_PMU_PWDCR) & ~0x40, DANUBE_PMU_PWDCR); ++ writel(readl(DANUBE_PMU_PWDCR) & ~0x8000, DANUBE_PMU_PWDCR); ++ ++#if 1//defined (DWC_HOST_ONLY) ++ // make the hardware be a host controller (default) ++ //clear_bit (DANUBE_USBCFG_HDSEL_BIT, (volatile unsigned long *)DANUBE_RCU_UBSCFG); ++ writel(readl(DANUBE_RCU_UBSCFG) & ~(1< ++#include ++ ++// 20070316, winder added. ++#ifndef SZ_256K ++#define SZ_256K 0x00040000 ++#endif ++ ++extern void dwc_otg_power_on (void); ++ ++/* FIXME: The current Linux-2.6 do not have these header files, but anyway, we need these. */ ++// #include ++// #include ++ ++/* winder, I used the Danube parameter as default. * ++ * We could change this through module param. */ ++#define IFX_USB_IOMEM_BASE 0x1e101000 ++#define IFX_USB_IOMEM_SIZE SZ_256K ++#define IFX_USB_IRQ LTQ_USB_INT ++ ++/** ++ * This function is called to set correct clock gating and power. ++ * For Twinpass/Danube board. ++ */ ++#ifndef DANUBE_RCU_BASE_ADDR ++#define DANUBE_RCU_BASE_ADDR (0xBF203000) ++#endif ++ ++#ifndef DANUBE_CGU ++#define DANUBE_CGU (0xBF103000) ++#endif ++#ifndef DANUBE_CGU_IFCCR ++/***CGU Interface Clock Control Register***/ ++#define DANUBE_CGU_IFCCR ((volatile u32*)(DANUBE_CGU+ 0x0018)) ++#endif ++ ++#ifndef DANUBE_PMU ++#define DANUBE_PMU (KSEG1+0x1F102000) ++#endif ++#ifndef DANUBE_PMU_PWDCR ++/* PMU Power down Control Register */ ++#define DANUBE_PMU_PWDCR ((volatile u32*)(DANUBE_PMU+0x001C)) ++#endif ++ ++ ++#define DANUBE_RCU_UBSCFG ((volatile u32*)(DANUBE_RCU_BASE_ADDR + 0x18)) ++#define DANUBE_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++#define DANUBE_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++#define DANUBE_USBCFG_SLV_END_BIT 9 // 0:little_end, 1:big_end ++ ++extern void ltq_mask_and_ack_irq(struct irq_data *d); ++ ++static void inline mask_and_ack_ifx_irq(int x) ++{ ++ struct irq_data d; ++ d.irq = x; ++ ltq_mask_and_ack_irq(&d); ++} ++#endif //__DWC_OTG_IFX_H__ +diff --git a/drivers/usb/dwc_otg/dwc_otg_plat.h b/drivers/usb/dwc_otg/dwc_otg_plat.h +new file mode 100644 +index 0000000..727d0c4 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_plat.h +@@ -0,0 +1,269 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/platform/dwc_otg_plat.h $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 510301 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_PLAT_H__) ++#define __DWC_OTG_PLAT_H__ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/** ++ * @file ++ * ++ * This file contains the Platform Specific constants, interfaces ++ * (functions and macros) for Linux. ++ * ++ */ ++/*#if !defined(__LINUX__) ++#error "The contents of this file is Linux specific!!!" ++#endif ++*/ ++#include ++#define writel ltq_w32 ++#define readl ltq_r32 ++ ++/** ++ * Reads the content of a register. ++ * ++ * @param _reg address of register to read. ++ * @return contents of the register. ++ * ++ ++ * Usage:
++ * uint32_t dev_ctl = dwc_read_reg32(&dev_regs->dctl); ++ */ ++static __inline__ uint32_t dwc_read_reg32( volatile uint32_t *_reg) ++{ ++ return readl(_reg); ++}; ++ ++/** ++ * Writes a register with a 32 bit value. ++ * ++ * @param _reg address of register to read. ++ * @param _value to write to _reg. ++ * ++ * Usage:
++ * dwc_write_reg32(&dev_regs->dctl, 0); ++ */ ++static __inline__ void dwc_write_reg32( volatile uint32_t *_reg, const uint32_t _value) ++{ ++ writel( _value, _reg ); ++}; ++ ++/** ++ * This function modifies bit values in a register. Using the ++ * algorithm: (reg_contents & ~clear_mask) | set_mask. ++ * ++ * @param _reg address of register to read. ++ * @param _clear_mask bit mask to be cleared. ++ * @param _set_mask bit mask to be set. ++ * ++ * Usage:
++ * // Clear the SOF Interrupt Mask bit and
++ * // set the OTG Interrupt mask bit, leaving all others as they were. ++ * dwc_modify_reg32(&dev_regs->gintmsk, DWC_SOF_INT, DWC_OTG_INT);
++ */ ++static __inline__ ++ void dwc_modify_reg32( volatile uint32_t *_reg, const uint32_t _clear_mask, const uint32_t _set_mask) ++{ ++ writel( (readl(_reg) & ~_clear_mask) | _set_mask, _reg ); ++}; ++ ++ ++/** ++ * Wrapper for the OS micro-second delay function. ++ * @param[in] _usecs Microseconds of delay ++ */ ++static __inline__ void UDELAY( const uint32_t _usecs ) ++{ ++ udelay( _usecs ); ++} ++ ++/** ++ * Wrapper for the OS milli-second delay function. ++ * @param[in] _msecs milliseconds of delay ++ */ ++static __inline__ void MDELAY( const uint32_t _msecs ) ++{ ++ mdelay( _msecs ); ++} ++ ++/** ++ * Wrapper for the Linux spin_lock. On the ARM (Integrator) ++ * spin_lock() is a nop. ++ * ++ * @param _lock Pointer to the spinlock. ++ */ ++static __inline__ void SPIN_LOCK( spinlock_t *_lock ) ++{ ++ spin_lock(_lock); ++} ++ ++/** ++ * Wrapper for the Linux spin_unlock. On the ARM (Integrator) ++ * spin_lock() is a nop. ++ * ++ * @param _lock Pointer to the spinlock. ++ */ ++static __inline__ void SPIN_UNLOCK( spinlock_t *_lock ) ++{ ++ spin_unlock(_lock); ++} ++ ++/** ++ * Wrapper (macro) for the Linux spin_lock_irqsave. On the ARM ++ * (Integrator) spin_lock() is a nop. ++ * ++ * @param _l Pointer to the spinlock. ++ * @param _f unsigned long for irq flags storage. ++ */ ++#define SPIN_LOCK_IRQSAVE( _l, _f ) { \ ++ spin_lock_irqsave(_l,_f); \ ++ } ++ ++/** ++ * Wrapper (macro) for the Linux spin_unlock_irqrestore. On the ARM ++ * (Integrator) spin_lock() is a nop. ++ * ++ * @param _l Pointer to the spinlock. ++ * @param _f unsigned long for irq flags storage. ++ */ ++#define SPIN_UNLOCK_IRQRESTORE( _l,_f ) {\ ++ spin_unlock_irqrestore(_l,_f); \ ++ } ++ ++ ++/* ++ * Debugging support vanishes in non-debug builds. ++ */ ++ ++ ++/** ++ * The Debug Level bit-mask variable. ++ */ ++extern uint32_t g_dbg_lvl; ++/** ++ * Set the Debug Level variable. ++ */ ++static inline uint32_t SET_DEBUG_LEVEL( const uint32_t _new ) ++{ ++ uint32_t old = g_dbg_lvl; ++ g_dbg_lvl = _new; ++ return old; ++} ++ ++/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ ++#define DBG_CIL (0x2) ++/** When debug level has the DBG_CILV bit set, display CIL Verbose debug ++ * messages */ ++#define DBG_CILV (0x20) ++/** When debug level has the DBG_PCD bit set, display PCD (Device) debug ++ * messages */ ++#define DBG_PCD (0x4) ++/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug ++ * messages */ ++#define DBG_PCDV (0x40) ++/** When debug level has the DBG_HCD bit set, display Host debug messages */ ++#define DBG_HCD (0x8) ++/** When debug level has the DBG_HCDV bit set, display Verbose Host debug ++ * messages */ ++#define DBG_HCDV (0x80) ++/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host ++ * mode. */ ++#define DBG_HCD_URB (0x800) ++ ++/** When debug level has any bit set, display debug messages */ ++#define DBG_ANY (0xFF) ++ ++/** All debug messages off */ ++#define DBG_OFF 0 ++ ++/** Prefix string for DWC_DEBUG print macros. */ ++#define USB_DWC "DWC_otg: " ++ ++/** ++ * Print a debug message when the Global debug level variable contains ++ * the bit defined in lvl. ++ * ++ * @param[in] lvl - Debug level, use one of the DBG_ constants above. ++ * @param[in] x - like printf ++ * ++ * Example:

++ * ++ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr); ++ * ++ *
++ * results in:
++ * ++ * usb-DWC_otg: dwc_otg_cil_init(ca867000) ++ * ++ */ ++#ifdef DEBUG ++ ++# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)printk( KERN_DEBUG USB_DWC x ); }while(0) ++# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x ) ++ ++# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl) ++ ++#else ++ ++# define DWC_DEBUGPL(lvl, x...) do{}while(0) ++# define DWC_DEBUGP(x...) ++ ++# define CHK_DEBUG_LEVEL(level) (0) ++ ++#endif /*DEBUG*/ ++ ++/** ++ * Print an Error message. ++ */ ++#define DWC_ERROR(x...) printk( KERN_ERR USB_DWC x ) ++/** ++ * Print a Warning message. ++ */ ++#define DWC_WARN(x...) printk( KERN_WARNING USB_DWC x ) ++/** ++ * Print a notice (normal but significant message). ++ */ ++#define DWC_NOTICE(x...) printk( KERN_NOTICE USB_DWC x ) ++/** ++ * Basic message printing. ++ */ ++#define DWC_PRINT(x...) printk( KERN_INFO USB_DWC x ) ++ ++#endif ++ +diff --git a/drivers/usb/dwc_otg/dwc_otg_regs.h b/drivers/usb/dwc_otg/dwc_otg_regs.h +new file mode 100644 +index 0000000..397a954 +--- /dev/null ++++ b/drivers/usb/dwc_otg/dwc_otg_regs.h +@@ -0,0 +1,1797 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg_ipmate/linux/drivers/dwc_otg_regs.h $ ++ * $Revision: 1.1.1.1 $ ++ * $Date: 2009-04-17 06:15:34 $ ++ * $Change: 631780 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_REGS_H__ ++#define __DWC_OTG_REGS_H__ ++ ++/** ++ * @file ++ * ++ * This file contains the data structures for accessing the DWC_otg core registers. ++ * ++ * The application interfaces with the HS OTG core by reading from and ++ * writing to the Control and Status Register (CSR) space through the ++ * AHB Slave interface. These registers are 32 bits wide, and the ++ * addresses are 32-bit-block aligned. ++ * CSRs are classified as follows: ++ * - Core Global Registers ++ * - Device Mode Registers ++ * - Device Global Registers ++ * - Device Endpoint Specific Registers ++ * - Host Mode Registers ++ * - Host Global Registers ++ * - Host Port CSRs ++ * - Host Channel Specific Registers ++ * ++ * Only the Core Global registers can be accessed in both Device and ++ * Host modes. When the HS OTG core is operating in one mode, either ++ * Device or Host, the application must not access registers from the ++ * other mode. When the core switches from one mode to another, the ++ * registers in the new mode of operation must be reprogrammed as they ++ * would be after a power-on reset. ++ */ ++ ++/****************************************************************************/ ++/** DWC_otg Core registers . ++ * The dwc_otg_core_global_regs structure defines the size ++ * and relative field offsets for the Core Global registers. ++ */ ++typedef struct dwc_otg_core_global_regs ++{ ++ /** OTG Control and Status Register. Offset: 000h */ ++ volatile uint32_t gotgctl; ++ /** OTG Interrupt Register. Offset: 004h */ ++ volatile uint32_t gotgint; ++ /**Core AHB Configuration Register. Offset: 008h */ ++ volatile uint32_t gahbcfg; ++#define DWC_GLBINTRMASK 0x0001 ++#define DWC_DMAENABLE 0x0020 ++#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 ++#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 ++#define DWC_PTXEMPTYLVL_EMPTY 0x0100 ++#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 ++ ++ ++ /**Core USB Configuration Register. Offset: 00Ch */ ++ volatile uint32_t gusbcfg; ++ /**Core Reset Register. Offset: 010h */ ++ volatile uint32_t grstctl; ++ /**Core Interrupt Register. Offset: 014h */ ++ volatile uint32_t gintsts; ++ /**Core Interrupt Mask Register. Offset: 018h */ ++ volatile uint32_t gintmsk; ++ /**Receive Status Queue Read Register (Read Only). Offset: 01Ch */ ++ volatile uint32_t grxstsr; ++ /**Receive Status Queue Read & POP Register (Read Only). Offset: 020h*/ ++ volatile uint32_t grxstsp; ++ /**Receive FIFO Size Register. Offset: 024h */ ++ volatile uint32_t grxfsiz; ++ /**Non Periodic Transmit FIFO Size Register. Offset: 028h */ ++ volatile uint32_t gnptxfsiz; ++ /**Non Periodic Transmit FIFO/Queue Status Register (Read ++ * Only). Offset: 02Ch */ ++ volatile uint32_t gnptxsts; ++ /**I2C Access Register. Offset: 030h */ ++ volatile uint32_t gi2cctl; ++ /**PHY Vendor Control Register. Offset: 034h */ ++ volatile uint32_t gpvndctl; ++ /**General Purpose Input/Output Register. Offset: 038h */ ++ volatile uint32_t ggpio; ++ /**User ID Register. Offset: 03Ch */ ++ volatile uint32_t guid; ++ /**Synopsys ID Register (Read Only). Offset: 040h */ ++ volatile uint32_t gsnpsid; ++ /**User HW Config1 Register (Read Only). Offset: 044h */ ++ volatile uint32_t ghwcfg1; ++ /**User HW Config2 Register (Read Only). Offset: 048h */ ++ volatile uint32_t ghwcfg2; ++#define DWC_SLAVE_ONLY_ARCH 0 ++#define DWC_EXT_DMA_ARCH 1 ++#define DWC_INT_DMA_ARCH 2 ++ ++#define DWC_MODE_HNP_SRP_CAPABLE 0 ++#define DWC_MODE_SRP_ONLY_CAPABLE 1 ++#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 ++#define DWC_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ /**User HW Config3 Register (Read Only). Offset: 04Ch */ ++ volatile uint32_t ghwcfg3; ++ /**User HW Config4 Register (Read Only). Offset: 050h*/ ++ volatile uint32_t ghwcfg4; ++ /** Reserved Offset: 054h-0FFh */ ++ uint32_t reserved[43]; ++ /** Host Periodic Transmit FIFO Size Register. Offset: 100h */ ++ volatile uint32_t hptxfsiz; ++ /** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled, ++ otherwise Device Transmit FIFO#n Register. ++ * Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15). */ ++ //volatile uint32_t dptxfsiz[15]; ++ volatile uint32_t dptxfsiz_dieptxf[15]; ++} dwc_otg_core_global_regs_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Control ++ * and Status Register (GOTGCTL). Set the bits using the bit ++ * fields then write the d32 value to the register. ++ */ ++typedef union gotgctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned reserved31_21 : 11; ++ unsigned currmod : 1; ++ unsigned bsesvld : 1; ++ unsigned asesvld : 1; ++ unsigned reserved17 : 1; ++ unsigned conidsts : 1; ++ unsigned reserved15_12 : 4; ++ unsigned devhnpen : 1; ++ unsigned hstsethnpen : 1; ++ unsigned hnpreq : 1; ++ unsigned hstnegscs : 1; ++ unsigned reserved7_2 : 6; ++ unsigned sesreq : 1; ++ unsigned sesreqscs : 1; ++ } b; ++} gotgctl_data_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Interrupt Register ++ * (GOTGINT). Set/clear the bits using the bit fields then write the d32 ++ * value to the register. ++ */ ++typedef union gotgint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Current Mode */ ++ unsigned reserved31_20 : 12; ++ /** Debounce Done */ ++ unsigned debdone : 1; ++ /** A-Device Timeout Change */ ++ unsigned adevtoutchng : 1; ++ /** Host Negotiation Detected */ ++ unsigned hstnegdet : 1; ++ unsigned reserver16_10 : 7; ++ /** Host Negotiation Success Status Change */ ++ unsigned hstnegsucstschng : 1; ++ /** Session Request Success Status Change */ ++ unsigned sesreqsucstschng : 1; ++ unsigned reserved3_7 : 5; ++ /** Session End Detected */ ++ unsigned sesenddet : 1; ++ /** Current Mode */ ++ unsigned reserved1_0 : 2; ++ } b; ++} gotgint_data_t; ++ ++ ++/** ++ * This union represents the bit fields of the Core AHB Configuration ++ * Register (GAHBCFG). Set/clear the bits using the bit fields then ++ * write the d32 value to the register. ++ */ ++typedef union gahbcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1 ++#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0 ++ unsigned reserved9_31 : 23; ++ unsigned ptxfemplvl : 1; ++ unsigned nptxfemplvl_txfemplvl : 1; ++#define DWC_GAHBCFG_DMAENABLE 1 ++ unsigned reserved : 1; ++ unsigned dmaenable : 1; ++#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7 ++ unsigned hburstlen : 4; ++ unsigned glblintrmsk : 1; ++#define DWC_GAHBCFG_GLBINT_ENABLE 1 ++ ++ } b; ++} gahbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core USB Configuration ++ * Register (GUSBCFG). Set the bits using the bit fields then write ++ * the d32 value to the register. ++ */ ++typedef union gusbcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned corrupt_tx_packet: 1; /*fscz*/ ++ unsigned force_device_mode: 1; ++ unsigned force_host_mode: 1; ++ unsigned reserved23_28 : 6; ++ unsigned term_sel_dl_pulse : 1; ++ unsigned ulpi_int_vbus_indicator : 1; ++ unsigned ulpi_ext_vbus_drv : 1; ++ unsigned ulpi_clk_sus_m : 1; ++ unsigned ulpi_auto_res : 1; ++ unsigned ulpi_fsls : 1; ++ unsigned otgutmifssel : 1; ++ unsigned phylpwrclksel : 1; ++ unsigned nptxfrwnden : 1; ++ unsigned usbtrdtim : 4; ++ unsigned hnpcap : 1; ++ unsigned srpcap : 1; ++ unsigned ddrsel : 1; ++ unsigned physel : 1; ++ unsigned fsintf : 1; ++ unsigned ulpi_utmi_sel : 1; ++ unsigned phyif : 1; ++ unsigned toutcal : 3; ++ } b; ++} gusbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core Reset Register ++ * (GRSTCTL). Set/clear the bits using the bit fields then write the ++ * d32 value to the register. ++ */ ++typedef union grstctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** AHB Master Idle. Indicates the AHB Master State ++ * Machine is in IDLE condition. */ ++ unsigned ahbidle : 1; ++ /** DMA Request Signal. Indicated DMA request is in ++ * probress. Used for debug purpose. */ ++ unsigned dmareq : 1; ++ /** Reserved */ ++ unsigned reserved29_11 : 19; ++ /** TxFIFO Number (TxFNum) (Device and Host). ++ * ++ * This is the FIFO number which needs to be flushed, ++ * using the TxFIFO Flush bit. This field should not ++ * be changed until the TxFIFO Flush bit is cleared by ++ * the core. ++ * - 0x0 : Non Periodic TxFIFO Flush ++ * - 0x1 : Periodic TxFIFO #1 Flush in device mode ++ * or Periodic TxFIFO in host mode ++ * - 0x2 : Periodic TxFIFO #2 Flush in device mode. ++ * - ... ++ * - 0xF : Periodic TxFIFO #15 Flush in device mode ++ * - 0x10: Flush all the Transmit NonPeriodic and ++ * Transmit Periodic FIFOs in the core ++ */ ++ unsigned txfnum : 5; ++ /** TxFIFO Flush (TxFFlsh) (Device and Host). ++ * ++ * This bit is used to selectively flush a single or ++ * all transmit FIFOs. The application must first ++ * ensure that the core is not in the middle of a ++ * transaction.

The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is writing into the TxFIFO nor the MAC ++ * is reading the data out of the FIFO.

The ++ * application should wait until the core clears this ++ * bit, before performing any operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned txfflsh : 1; ++ /** RxFIFO Flush (RxFFlsh) (Device and Host) ++ * ++ * The application can flush the entire Receive FIFO ++ * using this bit.

The application must first ++ * ensure that the core is not in the middle of a ++ * transaction.

The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is reading from the RxFIFO nor the MAC ++ * is writing the data in to the FIFO.

The ++ * application should wait until the bit is cleared ++ * before performing any other operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned rxfflsh : 1; ++ /** In Token Sequence Learning Queue Flush ++ * (INTknQFlsh) (Device Only) ++ */ ++ unsigned intknqflsh : 1; ++ /** Host Frame Counter Reset (Host Only)
++ * ++ * The application can reset the (micro)frame number ++ * counter inside the core, using this bit. When the ++ * (micro)frame counter is reset, the subsequent SOF ++ * sent out by the core, will have a (micro)frame ++ * number of 0. ++ */ ++ unsigned hstfrm : 1; ++ /** Hclk Soft Reset ++ * ++ * The application uses this bit to reset the control logic in ++ * the AHB clock domain. Only AHB clock domain pipelines are ++ * reset. ++ */ ++ unsigned hsftrst : 1; ++ /** Core Soft Reset (CSftRst) (Device and Host) ++ * ++ * The application can flush the control logic in the ++ * entire core using this bit. This bit resets the ++ * pipelines in the AHB Clock domain as well as the ++ * PHY Clock domain. ++ * ++ * The state machines are reset to an IDLE state, the ++ * control bits in the CSRs are cleared, all the ++ * transmit FIFOs and the receive FIFO are flushed. ++ * ++ * The status mask bits that control the generation of ++ * the interrupt, are cleared, to clear the ++ * interrupt. The interrupt status bits are not ++ * cleared, so the application can get the status of ++ * any events that occurred in the core after it has ++ * set this bit. ++ * ++ * Any transactions on the AHB are terminated as soon ++ * as possible following the protocol. Any ++ * transactions on the USB are terminated immediately. ++ * ++ * The configuration settings in the CSRs are ++ * unchanged, so the software doesn't have to ++ * reprogram these registers (Device ++ * Configuration/Host Configuration/Core System ++ * Configuration/Core PHY Configuration). ++ * ++ * The application can write to this bit, any time it ++ * wants to reset the core. This is a self clearing ++ * bit and the core clears this bit after all the ++ * necessary logic is reset in the core, which may ++ * take several clocks, depending on the current state ++ * of the core. ++ */ ++ unsigned csftrst : 1; ++ } b; ++} grstctl_t; ++ ++ ++/** ++ * This union represents the bit fields of the Core Interrupt Mask ++ * Register (GINTMSK). Set/clear the bits using the bit fields then ++ * write the d32 value to the register. ++ */ ++typedef union gintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned wkupintr : 1; ++ unsigned sessreqintr : 1; ++ unsigned disconnect : 1; ++ unsigned conidstschng : 1; ++ unsigned reserved27 : 1; ++ unsigned ptxfempty : 1; ++ unsigned hcintr : 1; ++ unsigned portintr : 1; ++ unsigned reserved22_23 : 2; ++ unsigned incomplisoout : 1; ++ unsigned incomplisoin : 1; ++ unsigned outepintr : 1; ++ unsigned inepintr : 1; ++ unsigned epmismatch : 1; ++ unsigned reserved16 : 1; ++ unsigned eopframe : 1; ++ unsigned isooutdrop : 1; ++ unsigned enumdone : 1; ++ unsigned usbreset : 1; ++ unsigned usbsuspend : 1; ++ unsigned erlysuspend : 1; ++ unsigned i2cintr : 1; ++ unsigned reserved8 : 1; ++ unsigned goutnakeff : 1; ++ unsigned ginnakeff : 1; ++ unsigned nptxfempty : 1; ++ unsigned rxstsqlvl : 1; ++ unsigned sofintr : 1; ++ unsigned otgintr : 1; ++ unsigned modemismatch : 1; ++ unsigned reserved0 : 1; ++ } b; ++} gintmsk_data_t; ++/** ++ * This union represents the bit fields of the Core Interrupt Register ++ * (GINTSTS). Set/clear the bits using the bit fields then write the ++ * d32 value to the register. ++ */ ++typedef union gintsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++#define DWC_SOF_INTR_MASK 0x0008 ++ /** register bits */ ++ struct ++ { ++#define DWC_HOST_MODE 1 ++ unsigned wkupintr : 1; ++ unsigned sessreqintr : 1; ++ unsigned disconnect : 1; ++ unsigned conidstschng : 1; ++ unsigned reserved27 : 1; ++ unsigned ptxfempty : 1; ++ unsigned hcintr : 1; ++ unsigned portintr : 1; ++ unsigned reserved22_23 : 2; ++ unsigned incomplisoout : 1; ++ unsigned incomplisoin : 1; ++ unsigned outepintr : 1; ++ unsigned inepint: 1; ++ unsigned epmismatch : 1; ++ unsigned intokenrx : 1; ++ unsigned eopframe : 1; ++ unsigned isooutdrop : 1; ++ unsigned enumdone : 1; ++ unsigned usbreset : 1; ++ unsigned usbsuspend : 1; ++ unsigned erlysuspend : 1; ++ unsigned i2cintr : 1; ++ unsigned reserved8 : 1; ++ unsigned goutnakeff : 1; ++ unsigned ginnakeff : 1; ++ unsigned nptxfempty : 1; ++ unsigned rxstsqlvl : 1; ++ unsigned sofintr : 1; ++ unsigned otgintr : 1; ++ unsigned modemismatch : 1; ++ unsigned curmode : 1; ++ } b; ++} gintsts_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Device Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 ++ * element then read out the bits using the bit elements. ++ */ ++typedef union device_grxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 7; ++ unsigned fn : 4; ++#define DWC_STS_DATA_UPDT 0x2 // OUT Data Packet ++#define DWC_STS_XFER_COMP 0x3 // OUT Data Transfer Complete ++ ++#define DWC_DSTS_GOUT_NAK 0x1 // Global OUT NAK ++#define DWC_DSTS_SETUP_COMP 0x4 // Setup Phase Complete ++#define DWC_DSTS_SETUP_UPDT 0x6 // SETUP Packet ++ unsigned pktsts : 4; ++ unsigned dpid : 2; ++ unsigned bcnt : 11; ++ unsigned epnum : 4; ++ } b; ++} device_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the d32 ++ * element then read out the bits using the bit elements. ++ */ ++typedef union host_grxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved31_21 : 11; ++#define DWC_GRXSTS_PKTSTS_IN 0x2 ++#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3 ++#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5 ++#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7 ++ unsigned pktsts : 4; ++ unsigned dpid : 2; ++ unsigned bcnt : 11; ++ unsigned chnum : 4; ++ } b; ++} host_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ, ++ * GNPTXFSIZ, DPTXFSIZn). Read the register into the d32 element then ++ * read out the bits using the bit elements. ++ */ ++typedef union fifosize_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned depth : 16; ++ unsigned startaddr : 16; ++ } b; ++} fifosize_data_t; ++ ++/** ++ * This union represents the bit fields in the Non-Periodic Transmit ++ * FIFO/Queue Status Register (GNPTXSTS). Read the register into the ++ * d32 element then read out the bits using the bit ++ * elements. ++ */ ++typedef union gnptxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 1; ++ /** Top of the Non-Periodic Transmit Request Queue ++ * - bits 30:27 - Channel/EP Number ++ * - bits 26:25 - Token Type ++ * - bit 24 - Terminate (Last entry for the selected ++ * channel/EP) ++ * - 2'b00 - IN/OUT ++ * - 2'b01 - Zero Length OUT ++ * - 2'b10 - PING/Complete Split ++ * - 2'b11 - Channel Halt ++ ++ */ ++ unsigned nptxqtop_chnep : 4; ++ unsigned nptxqtop_token : 2; ++ unsigned nptxqtop_terminate : 1; ++ unsigned nptxqspcavail : 8; ++ unsigned nptxfspcavail : 16; ++ } b; ++} gnptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Transmit ++ * FIFO Status Register (DTXFSTS). Read the register into the ++ * d32 element then read out the bits using the bit ++ * elements. ++ */ ++typedef union dtxfsts_data /* fscz */ //* ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 16; ++ unsigned txfspcavail : 16; ++ } b; ++} dtxfsts_data_t; ++ ++/** ++ * This union represents the bit fields in the I2C Control Register ++ * (I2CCTL). Read the register into the d32 element then read out the ++ * bits using the bit elements. ++ */ ++typedef union gi2cctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned bsydne : 1; ++ unsigned rw : 1; ++ unsigned reserved : 2; ++ unsigned i2cdevaddr : 2; ++ unsigned i2csuspctl : 1; ++ unsigned ack : 1; ++ unsigned i2cen : 1; ++ unsigned addr : 7; ++ unsigned regaddr : 8; ++ unsigned rwdata : 8; ++ } b; ++} gi2cctl_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config1 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg1_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ep_dir15 : 2; ++ unsigned ep_dir14 : 2; ++ unsigned ep_dir13 : 2; ++ unsigned ep_dir12 : 2; ++ unsigned ep_dir11 : 2; ++ unsigned ep_dir10 : 2; ++ unsigned ep_dir9 : 2; ++ unsigned ep_dir8 : 2; ++ unsigned ep_dir7 : 2; ++ unsigned ep_dir6 : 2; ++ unsigned ep_dir5 : 2; ++ unsigned ep_dir4 : 2; ++ unsigned ep_dir3 : 2; ++ unsigned ep_dir2 : 2; ++ unsigned ep_dir1 : 2; ++ unsigned ep_dir0 : 2; ++ } b; ++} hwcfg1_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config2 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg2_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /* GHWCFG2 */ ++ unsigned reserved31 : 1; ++ unsigned dev_token_q_depth : 5; ++ unsigned host_perio_tx_q_depth : 2; ++ unsigned nonperio_tx_q_depth : 2; ++ unsigned rx_status_q_depth : 2; ++ unsigned dynamic_fifo : 1; ++ unsigned perio_ep_supported : 1; ++ unsigned num_host_chan : 4; ++ unsigned num_dev_ep : 4; ++ unsigned fs_phy_type : 2; ++#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 ++#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 ++ unsigned hs_phy_type : 2; ++ unsigned point2point : 1; ++ unsigned architecture : 2; ++#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0 ++#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1 ++#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 ++ unsigned op_mode : 3; ++ } b; ++} hwcfg2_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config3 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg3_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /* GHWCFG3 */ ++ unsigned dfifo_depth : 16; ++ unsigned reserved15_13 : 3; ++ unsigned ahb_phy_clock_synch : 1; ++ unsigned synch_reset_type : 1; ++ unsigned optional_features : 1; ++ unsigned vendor_ctrl_if : 1; ++ unsigned i2c : 1; ++ unsigned otg_func : 1; ++ unsigned packet_size_cntr_width : 3; ++ unsigned xfer_size_cntr_width : 4; ++ } b; ++} hwcfg3_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config4 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg4_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++unsigned reserved31_30 : 2; /* fscz */ ++ unsigned num_in_eps : 4; ++ unsigned ded_fifo_en : 1; ++ ++ unsigned session_end_filt_en : 1; ++ unsigned b_valid_filt_en : 1; ++ unsigned a_valid_filt_en : 1; ++ unsigned vbus_valid_filt_en : 1; ++ unsigned iddig_filt_en : 1; ++ unsigned num_dev_mode_ctrl_ep : 4; ++ unsigned utmi_phy_data_width : 2; ++ unsigned min_ahb_freq : 9; ++ unsigned power_optimiz : 1; ++ unsigned num_dev_perio_in_ep : 4; ++ } b; ++} hwcfg4_data_t; ++ ++//////////////////////////////////////////// ++// Device Registers ++/** ++ * Device Global Registers. Offsets 800h-BFFh ++ * ++ * The following structures define the size and relative field offsets ++ * for the Device Mode Registers. ++ * ++ * These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown. ++ */ ++typedef struct dwc_otg_dev_global_regs ++{ ++ /** Device Configuration Register. Offset 800h */ ++ volatile uint32_t dcfg; ++ /** Device Control Register. Offset: 804h */ ++ volatile uint32_t dctl; ++ /** Device Status Register (Read Only). Offset: 808h */ ++ volatile uint32_t dsts; ++ /** Reserved. Offset: 80Ch */ ++ uint32_t unused; ++ /** Device IN Endpoint Common Interrupt Mask ++ * Register. Offset: 810h */ ++ volatile uint32_t diepmsk; ++ /** Device OUT Endpoint Common Interrupt Mask ++ * Register. Offset: 814h */ ++ volatile uint32_t doepmsk; ++ /** Device All Endpoints Interrupt Register. Offset: 818h */ ++ volatile uint32_t daint; ++ /** Device All Endpoints Interrupt Mask Register. Offset: ++ * 81Ch */ ++ volatile uint32_t daintmsk; ++ /** Device IN Token Queue Read Register-1 (Read Only). ++ * Offset: 820h */ ++ volatile uint32_t dtknqr1; ++ /** Device IN Token Queue Read Register-2 (Read Only). ++ * Offset: 824h */ ++ volatile uint32_t dtknqr2; ++ /** Device VBUS discharge Register. Offset: 828h */ ++ volatile uint32_t dvbusdis; ++ /** Device VBUS Pulse Register. Offset: 82Ch */ ++ volatile uint32_t dvbuspulse; ++ /** Device IN Token Queue Read Register-3 (Read Only). ++ * Device Thresholding control register (Read/Write) ++ * Offset: 830h */ ++ volatile uint32_t dtknqr3_dthrctl; ++ /** Device IN Token Queue Read Register-4 (Read Only). / ++ * Device IN EPs empty Inr. Mask Register (Read/Write) ++ * Offset: 834h */ ++ volatile uint32_t dtknqr4_fifoemptymsk; ++} dwc_otg_device_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device Configuration ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. Write the ++ * d32 member to the dcfg register. ++ */ ++typedef union dcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved31_23 : 9; ++ /** In Endpoint Mis-match count */ ++ unsigned epmscnt : 5; ++ unsigned reserved13_17 : 5; ++ /** Periodic Frame Interval */ ++#define DWC_DCFG_FRAME_INTERVAL_80 0 ++#define DWC_DCFG_FRAME_INTERVAL_85 1 ++#define DWC_DCFG_FRAME_INTERVAL_90 2 ++#define DWC_DCFG_FRAME_INTERVAL_95 3 ++ unsigned perfrint : 2; ++ /** Device Addresses */ ++ unsigned devaddr : 7; ++ unsigned reserved3 : 1; ++ /** Non Zero Length Status OUT Handshake */ ++#define DWC_DCFG_SEND_STALL 1 ++ unsigned nzstsouthshk : 1; ++ /** Device Speed */ ++ unsigned devspd : 2; ++ } b; ++} dcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Control ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union dctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 20; ++ /** Power-On Programming Done */ ++ unsigned pwronprgdone : 1; ++ /** Clear Global OUT NAK */ ++ unsigned cgoutnak : 1; ++ /** Set Global OUT NAK */ ++ unsigned sgoutnak : 1; ++ /** Clear Global Non-Periodic IN NAK */ ++ unsigned cgnpinnak : 1; ++ /** Set Global Non-Periodic IN NAK */ ++ unsigned sgnpinnak : 1; ++ /** Test Control */ ++ unsigned tstctl : 3; ++ /** Global OUT NAK Status */ ++ unsigned goutnaksts : 1; ++ /** Global Non-Periodic IN NAK Status */ ++ unsigned gnpinnaksts : 1; ++ /** Soft Disconnect */ ++ unsigned sftdiscon : 1; ++ /** Remote Wakeup */ ++ unsigned rmtwkupsig : 1; ++ } b; ++} dctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Status ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union dsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved22_31 : 10; ++ /** Frame or Microframe Number of the received SOF */ ++ unsigned soffn : 14; ++ unsigned reserved4_7: 4; ++ /** Erratic Error */ ++ unsigned errticerr : 1; ++ /** Enumerated Speed */ ++#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 ++#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 ++#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 ++#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 ++ unsigned enumspd : 2; ++ /** Suspend Status */ ++ unsigned suspsts : 1; ++ } b; ++} dsts_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Device IN EP Interrupt ++ * Register and the Device IN EP Common Mask Register. ++ * ++ * - Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union diepint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved07_31 : 23; ++ unsigned txfifoundrn : 1; ++ /** IN Endpoint HAK Effective mask */ ++ unsigned emptyintr : 1; ++ /** IN Endpoint NAK Effective mask */ ++ unsigned inepnakeff : 1; ++ /** IN Token Received with EP mismatch mask */ ++ unsigned intknepmis : 1; ++ /** IN Token received with TxF Empty mask */ ++ unsigned intktxfemp : 1; ++ /** TimeOUT Handshake mask (non-ISOC EPs) */ ++ unsigned timeout : 1; ++ /** AHB Error mask */ ++ unsigned ahberr : 1; ++ /** Endpoint disable mask */ ++ unsigned epdisabled : 1; ++ /** Transfer complete mask */ ++ unsigned xfercompl : 1; ++ } b; ++} diepint_data_t; ++/** ++ * This union represents the bit fields in the Device IN EP Common ++ * Interrupt Mask Register. ++ */ ++typedef union diepint_data diepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP Interrupt ++ * Registerand Device OUT EP Common Interrupt Mask Register. ++ * ++ * - Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union doepint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved04_31 : 27; ++ /** OUT Token Received when Endpoint Disabled */ ++ unsigned outtknepdis : 1; ++ /** Setup Phase Done (contorl EPs) */ ++ unsigned setup : 1; ++ /** AHB Error */ ++ unsigned ahberr : 1; ++ /** Endpoint disable */ ++ unsigned epdisabled : 1; ++ /** Transfer complete */ ++ unsigned xfercompl : 1; ++ } b; ++} doepint_data_t; ++/** ++ * This union represents the bit fields in the Device OUT EP Common ++ * Interrupt Mask Register. ++ */ ++typedef union doepint_data doepmsk_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Device All EP Interrupt ++ * and Mask Registers. ++ * - Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union daint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** OUT Endpoint bits */ ++ unsigned out : 16; ++ /** IN Endpoint bits */ ++ unsigned in : 16; ++ } ep; ++ struct { ++ /** OUT Endpoint bits */ ++ unsigned outep15 : 1; ++ unsigned outep14 : 1; ++ unsigned outep13 : 1; ++ unsigned outep12 : 1; ++ unsigned outep11 : 1; ++ unsigned outep10 : 1; ++ unsigned outep9 : 1; ++ unsigned outep8 : 1; ++ unsigned outep7 : 1; ++ unsigned outep6 : 1; ++ unsigned outep5 : 1; ++ unsigned outep4 : 1; ++ unsigned outep3 : 1; ++ unsigned outep2 : 1; ++ unsigned outep1 : 1; ++ unsigned outep0 : 1; ++ /** IN Endpoint bits */ ++ unsigned inep15 : 1; ++ unsigned inep14 : 1; ++ unsigned inep13 : 1; ++ unsigned inep12 : 1; ++ unsigned inep11 : 1; ++ unsigned inep10 : 1; ++ unsigned inep9 : 1; ++ unsigned inep8 : 1; ++ unsigned inep7 : 1; ++ unsigned inep6 : 1; ++ unsigned inep5 : 1; ++ unsigned inep4 : 1; ++ unsigned inep3 : 1; ++ unsigned inep2 : 1; ++ unsigned inep1 : 1; ++ unsigned inep0 : 1; ++ } b; ++} daint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN Token Queue ++ * Read Registers. ++ * - Read the register into the d32 member. ++ * - READ-ONLY Register ++ */ ++typedef union dtknq1_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** EP Numbers of IN Tokens 0 ... 4 */ ++ unsigned epnums0_5 : 24; ++ /** write pointer has wrapped. */ ++ unsigned wrap_bit : 1; ++ /** Reserved */ ++ unsigned reserved05_06 : 2; ++ /** In Token Queue Write Pointer */ ++ unsigned intknwptr : 5; ++ }b; ++} dtknq1_data_t; ++ ++/** ++ * This union represents Threshold control Register ++ * - Read and write the register into the d32 member. ++ * - READ-WRITABLE Register ++ */ ++typedef union dthrctl_data //* /*fscz */ ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Reserved */ ++ unsigned reserved26_31 : 6; ++ /** Rx Thr. Length */ ++ unsigned rx_thr_len : 9; ++ /** Rx Thr. Enable */ ++ unsigned rx_thr_en : 1; ++ /** Reserved */ ++ unsigned reserved11_15 : 5; ++ /** Tx Thr. Length */ ++ unsigned tx_thr_len : 9; ++ /** ISO Tx Thr. Enable */ ++ unsigned iso_thr_en : 1; ++ /** non ISO Tx Thr. Enable */ ++ unsigned non_iso_thr_en : 1; ++ ++ }b; ++} dthrctl_data_t; ++ ++/** ++ * Device Logical IN Endpoint-Specific Registers. Offsets ++ * 900h-AFCh ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown. ++ */ ++typedef struct dwc_otg_dev_in_ep_regs ++{ ++ /** Device IN Endpoint Control Register. Offset:900h + ++ * (ep_num * 20h) + 00h */ ++ volatile uint32_t diepctl; ++ /** Reserved. Offset:900h + (ep_num * 20h) + 04h */ ++ uint32_t reserved04; ++ /** Device IN Endpoint Interrupt Register. Offset:900h + ++ * (ep_num * 20h) + 08h */ ++ volatile uint32_t diepint; ++ /** Reserved. Offset:900h + (ep_num * 20h) + 0Ch */ ++ uint32_t reserved0C; ++ /** Device IN Endpoint Transfer Size ++ * Register. Offset:900h + (ep_num * 20h) + 10h */ ++ volatile uint32_t dieptsiz; ++ /** Device IN Endpoint DMA Address Register. Offset:900h + ++ * (ep_num * 20h) + 14h */ ++ volatile uint32_t diepdma; ++ /** Reserved. Offset:900h + (ep_num * 20h) + 18h - 900h + ++ * (ep_num * 20h) + 1Ch*/ ++ volatile uint32_t dtxfsts; ++ /** Reserved. Offset:900h + (ep_num * 20h) + 1Ch - 900h + ++ * (ep_num * 20h) + 1Ch*/ ++ uint32_t reserved18; ++} dwc_otg_dev_in_ep_regs_t; ++ ++/** ++ * Device Logical OUT Endpoint-Specific Registers. Offsets: ++ * B00h-CFCh ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown. ++ */ ++typedef struct dwc_otg_dev_out_ep_regs ++{ ++ /** Device OUT Endpoint Control Register. Offset:B00h + ++ * (ep_num * 20h) + 00h */ ++ volatile uint32_t doepctl; ++ /** Device OUT Endpoint Frame number Register. Offset: ++ * B00h + (ep_num * 20h) + 04h */ ++ volatile uint32_t doepfn; ++ /** Device OUT Endpoint Interrupt Register. Offset:B00h + ++ * (ep_num * 20h) + 08h */ ++ volatile uint32_t doepint; ++ /** Reserved. Offset:B00h + (ep_num * 20h) + 0Ch */ ++ uint32_t reserved0C; ++ /** Device OUT Endpoint Transfer Size Register. Offset: ++ * B00h + (ep_num * 20h) + 10h */ ++ volatile uint32_t doeptsiz; ++ /** Device OUT Endpoint DMA Address Register. Offset:B00h ++ * + (ep_num * 20h) + 14h */ ++ volatile uint32_t doepdma; ++ /** Reserved. Offset:B00h + (ep_num * 20h) + 18h - B00h + ++ * (ep_num * 20h) + 1Ch */ ++ uint32_t unused[2]; ++} dwc_otg_dev_out_ep_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Control ++ * Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union depctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Endpoint Enable */ ++ unsigned epena : 1; ++ /** Endpoint Disable */ ++ unsigned epdis : 1; ++ /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA1 Set Odd ++ * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to odd (micro) frame. ++ */ ++ unsigned setd1pid : 1; ++ /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA0. Set Even ++ * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to even (micro) ++ * frame. ++ */ ++ unsigned setd0pid : 1; ++ /** Set NAK */ ++ unsigned snak : 1; ++ /** Clear NAK */ ++ unsigned cnak : 1; ++ /** Tx Fifo Number ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned txfnum : 4; ++ /** Stall Handshake */ ++ unsigned stall : 1; ++ /** Snoop Mode ++ * OUT EPn/OUT EP0 ++ * IN EPn/IN EP0 - reserved */ ++ unsigned snp : 1; ++ /** Endpoint Type ++ * 2'b00: Control ++ * 2'b01: Isochronous ++ * 2'b10: Bulk ++ * 2'b11: Interrupt */ ++ unsigned eptype : 2; ++ /** NAK Status */ ++ unsigned naksts : 1; ++ /** Endpoint DPID (INTR/Bulk IN and OUT endpoints) ++ * This field contains the PID of the packet going to ++ * be received or transmitted on this endpoint. The ++ * application should program the PID of the first ++ * packet going to be received or transmitted on this ++ * endpoint , after the endpoint is ++ * activated. Application use the SetD1PID and ++ * SetD0PID fields of this register to program either ++ * D0 or D1 PID. ++ * ++ * The encoding for this field is ++ * - 0: D0 ++ * - 1: D1 ++ */ ++ unsigned dpid : 1; ++ /** USB Active Endpoint */ ++ unsigned usbactep : 1; ++ /** Next Endpoint ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned nextep : 4; ++ /** Maximum Packet Size ++ * IN/OUT EPn ++ * IN/OUT EP0 - 2 bits ++ * 2'b00: 64 Bytes ++ * 2'b01: 32 ++ * 2'b10: 16 ++ * 2'b11: 8 */ ++#define DWC_DEP0CTL_MPS_64 0 ++#define DWC_DEP0CTL_MPS_32 1 ++#define DWC_DEP0CTL_MPS_16 2 ++#define DWC_DEP0CTL_MPS_8 3 ++ unsigned mps : 11; ++ } b; ++} depctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Transfer ++ * Size Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union deptsiz_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 1; ++ /** Multi Count - Periodic IN endpoints */ ++ unsigned mc : 2; ++ /** Packet Count */ ++ unsigned pktcnt : 10; ++ /** Transfer size */ ++ unsigned xfersize : 19; ++ } b; ++} deptsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP 0 Transfer ++ * Size Register. Read the register into the d32 member then ++ * set/clear the bits using the bit elements. ++ */ ++typedef union deptsiz0_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved31 : 1; ++ /**Setup Packet Count (DOEPTSIZ0 Only) */ ++ unsigned supcnt : 2; ++ /** Reserved */ ++ unsigned reserved28_20 : 9; ++ /** Packet Count */ ++ unsigned pktcnt : 1; ++ /** Reserved */ ++ unsigned reserved18_7 : 12; ++ /** Transfer size */ ++ unsigned xfersize : 7; ++ } b; ++} deptsiz0_data_t; ++ ++ ++/** Maximum number of Periodic FIFOs */ ++#define MAX_PERIO_FIFOS 15 ++/** Maximum number of TX FIFOs */ ++#define MAX_TX_FIFOS 15 ++/** Maximum number of Endpoints/HostChannels */ ++#define MAX_EPS_CHANNELS 16 ++//#define MAX_EPS_CHANNELS 4 ++ ++/** ++ * The dwc_otg_dev_if structure contains information needed to manage ++ * the DWC_otg controller acting in device mode. It represents the ++ * programming view of the device-specific aspects of the controller. ++ */ ++typedef struct dwc_otg_dev_if { ++ /** Pointer to device Global registers. ++ * Device Global Registers starting at offset 800h ++ */ ++ dwc_otg_device_global_regs_t *dev_global_regs; ++#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 ++ ++ /** ++ * Device Logical IN Endpoint-Specific Registers 900h-AFCh ++ */ ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_IN_EP_REG_OFFSET 0x900 ++#define DWC_EP_REG_OFFSET 0x20 ++ ++ /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 ++ ++ /* Device configuration information*/ ++ uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */ ++ //uint8_t num_eps; /**< Number of EPs range: 0-16 (includes EP0) */ ++ //uint8_t num_perio_eps; /**< # of Periodic EP range: 0-15 */ ++ /*fscz */ ++ uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */ ++ uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/ ++ ++ /** Size of periodic FIFOs (Bytes) */ ++ uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ ++ /** Size of Tx FIFOs (Bytes) */ ++ uint16_t tx_fifo_size[MAX_TX_FIFOS]; ++ ++ /** Thresholding enable flags and length varaiables **/ ++ uint16_t rx_thr_en; ++ uint16_t iso_tx_thr_en; ++ uint16_t non_iso_tx_thr_en; ++ ++ uint16_t rx_thr_length; ++ uint16_t tx_thr_length; ++} dwc_otg_dev_if_t; ++ ++/** ++ * This union represents the bit fields in the Power and Clock Gating Control ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. ++ */ ++typedef union pcgcctl_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned reserved31_05 : 27; ++ /** PHY Suspended */ ++ unsigned physuspended : 1; ++ /** Reset Power Down Modules */ ++ unsigned rstpdwnmodule : 1; ++ /** Power Clamp */ ++ unsigned pwrclmp : 1; ++ /** Gate Hclk */ ++ unsigned gatehclk : 1; ++ /** Stop Pclk */ ++ unsigned stoppclk : 1; ++ } b; ++} pcgcctl_data_t; ++ ++///////////////////////////////////////////////// ++// Host Mode Register Structures ++// ++/** ++ * The Host Global Registers structure defines the size and relative ++ * field offsets for the Host Mode Global Registers. Host Global ++ * Registers offsets 400h-7FFh. ++*/ ++typedef struct dwc_otg_host_global_regs ++{ ++ /** Host Configuration Register. Offset: 400h */ ++ volatile uint32_t hcfg; ++ /** Host Frame Interval Register. Offset: 404h */ ++ volatile uint32_t hfir; ++ /** Host Frame Number / Frame Remaining Register. Offset: 408h */ ++ volatile uint32_t hfnum; ++ /** Reserved. Offset: 40Ch */ ++ uint32_t reserved40C; ++ /** Host Periodic Transmit FIFO/ Queue Status Register. Offset: 410h */ ++ volatile uint32_t hptxsts; ++ /** Host All Channels Interrupt Register. Offset: 414h */ ++ volatile uint32_t haint; ++ /** Host All Channels Interrupt Mask Register. Offset: 418h */ ++ volatile uint32_t haintmsk; ++} dwc_otg_host_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Configuration Register. ++ * Read the register into the d32 member then set/clear the bits using ++ * the bit elements. Write the d32 member to the hcfg register. ++ */ ++typedef union hcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Reserved */ ++ //unsigned reserved31_03 : 29; ++ /** FS/LS Only Support */ ++ unsigned fslssupp : 1; ++ /** FS/LS Phy Clock Select */ ++#define DWC_HCFG_30_60_MHZ 0 ++#define DWC_HCFG_48_MHZ 1 ++#define DWC_HCFG_6_MHZ 2 ++ unsigned fslspclksel : 2; ++ } b; ++} hcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfir_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned reserved : 16; ++ unsigned frint : 16; ++ } b; ++} hfir_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfnum_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned frrem : 16; ++#define DWC_HFNUM_MAX_FRNUM 0x3FFF ++ unsigned frnum : 16; ++ } b; ++} hfnum_data_t; ++ ++typedef union hptxsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Top of the Periodic Transmit Request Queue ++ * - bit 24 - Terminate (last entry for the selected channel) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - Zero length ++ * - 2'b01 - Ping ++ * - 2'b10 - Disable ++ * - bits 30:27 - Channel Number ++ * - bit 31 - Odd/even microframe ++ */ ++ unsigned ptxqtop_odd : 1; ++ unsigned ptxqtop_chnum : 4; ++ unsigned ptxqtop_token : 2; ++ unsigned ptxqtop_terminate : 1; ++ unsigned ptxqspcavail : 8; ++ unsigned ptxfspcavail : 16; ++ } b; ++} hptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Port Control and Status ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hprt0 register. ++ */ ++typedef union hprt0_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved19_31 : 13; ++#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0 ++#define DWC_HPRT0_PRTSPD_FULL_SPEED 1 ++#define DWC_HPRT0_PRTSPD_LOW_SPEED 2 ++ unsigned prtspd : 2; ++ unsigned prttstctl : 4; ++ unsigned prtpwr : 1; ++ unsigned prtlnsts : 2; ++ unsigned reserved9 : 1; ++ unsigned prtrst : 1; ++ unsigned prtsusp : 1; ++ unsigned prtres : 1; ++ unsigned prtovrcurrchng : 1; ++ unsigned prtovrcurract : 1; ++ unsigned prtenchng : 1; ++ unsigned prtena : 1; ++ unsigned prtconndet : 1; ++ unsigned prtconnsts : 1; ++ } b; ++} hprt0_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 16; ++ unsigned ch15 : 1; ++ unsigned ch14 : 1; ++ unsigned ch13 : 1; ++ unsigned ch12 : 1; ++ unsigned ch11 : 1; ++ unsigned ch10 : 1; ++ unsigned ch9 : 1; ++ unsigned ch8 : 1; ++ unsigned ch7 : 1; ++ unsigned ch6 : 1; ++ unsigned ch5 : 1; ++ unsigned ch4 : 1; ++ unsigned ch3 : 1; ++ unsigned ch2 : 1; ++ unsigned ch1 : 1; ++ unsigned ch0 : 1; ++ } b; ++ struct { ++ unsigned reserved : 16; ++ unsigned chint : 16; ++ } b2; ++} haint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved : 16; ++ unsigned ch15 : 1; ++ unsigned ch14 : 1; ++ unsigned ch13 : 1; ++ unsigned ch12 : 1; ++ unsigned ch11 : 1; ++ unsigned ch10 : 1; ++ unsigned ch9 : 1; ++ unsigned ch8 : 1; ++ unsigned ch7 : 1; ++ unsigned ch6 : 1; ++ unsigned ch5 : 1; ++ unsigned ch4 : 1; ++ unsigned ch3 : 1; ++ unsigned ch2 : 1; ++ unsigned ch1 : 1; ++ unsigned ch0 : 1; ++ } b; ++ struct { ++ unsigned reserved : 16; ++ unsigned chint : 16; ++ } b2; ++} haintmsk_data_t; ++ ++/** ++ * Host Channel Specific Registers. 500h-5FCh ++ */ ++typedef struct dwc_otg_hc_regs ++{ ++ /** Host Channel 0 Characteristic Register. Offset: 500h + (chan_num * 20h) + 00h */ ++ volatile uint32_t hcchar; ++ /** Host Channel 0 Split Control Register. Offset: 500h + (chan_num * 20h) + 04h */ ++ volatile uint32_t hcsplt; ++ /** Host Channel 0 Interrupt Register. Offset: 500h + (chan_num * 20h) + 08h */ ++ volatile uint32_t hcint; ++ /** Host Channel 0 Interrupt Mask Register. Offset: 500h + (chan_num * 20h) + 0Ch */ ++ volatile uint32_t hcintmsk; ++ /** Host Channel 0 Transfer Size Register. Offset: 500h + (chan_num * 20h) + 10h */ ++ volatile uint32_t hctsiz; ++ /** Host Channel 0 DMA Address Register. Offset: 500h + (chan_num * 20h) + 14h */ ++ volatile uint32_t hcdma; ++ /** Reserved. Offset: 500h + (chan_num * 20h) + 18h - 500h + (chan_num * 20h) + 1Ch */ ++ uint32_t reserved[2]; ++} dwc_otg_hc_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Characteristics ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hcchar register. ++ */ ++typedef union hcchar_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Channel enable */ ++ unsigned chen : 1; ++ /** Channel disable */ ++ unsigned chdis : 1; ++ /** ++ * Frame to transmit periodic transaction. ++ * 0: even, 1: odd ++ */ ++ unsigned oddfrm : 1; ++ /** Device address */ ++ unsigned devaddr : 7; ++ /** Packets per frame for periodic transfers. 0 is reserved. */ ++ unsigned multicnt : 2; ++ /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ ++ unsigned eptype : 2; ++ /** 0: Full/high speed device, 1: Low speed device */ ++ unsigned lspddev : 1; ++ unsigned reserved : 1; ++ /** 0: OUT, 1: IN */ ++ unsigned epdir : 1; ++ /** Endpoint number */ ++ unsigned epnum : 4; ++ /** Maximum packet size in bytes */ ++ unsigned mps : 11; ++ } b; ++} hcchar_data_t; ++ ++typedef union hcsplt_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Split Enble */ ++ unsigned spltena : 1; ++ /** Reserved */ ++ unsigned reserved : 14; ++ /** Do Complete Split */ ++ unsigned compsplt : 1; ++ /** Transaction Position */ ++#define DWC_HCSPLIT_XACTPOS_MID 0 ++#define DWC_HCSPLIT_XACTPOS_END 1 ++#define DWC_HCSPLIT_XACTPOS_BEGIN 2 ++#define DWC_HCSPLIT_XACTPOS_ALL 3 ++ unsigned xactpos : 2; ++ /** Hub Address */ ++ unsigned hubaddr : 7; ++ /** Port Address */ ++ unsigned prtaddr : 7; ++ } b; ++} hcsplt_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union hcint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Reserved */ ++ unsigned reserved : 21; ++ /** Data Toggle Error */ ++ unsigned datatglerr : 1; ++ /** Frame Overrun */ ++ unsigned frmovrun : 1; ++ /** Babble Error */ ++ unsigned bblerr : 1; ++ /** Transaction Err */ ++ unsigned xacterr : 1; ++ /** NYET Response Received */ ++ unsigned nyet : 1; ++ /** ACK Response Received */ ++ unsigned ack : 1; ++ /** NAK Response Received */ ++ unsigned nak : 1; ++ /** STALL Response Received */ ++ unsigned stall : 1; ++ /** AHB Error */ ++ unsigned ahberr : 1; ++ /** Channel Halted */ ++ unsigned chhltd : 1; ++ /** Transfer Complete */ ++ unsigned xfercomp : 1; ++ } b; ++} hcint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Transfer Size ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hcchar register. ++ */ ++typedef union hctsiz_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Do PING protocol when 1 */ ++ unsigned dopng : 1; ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control), SETUP (Control) ++ */ ++#define DWC_HCTSIZ_DATA0 0 ++#define DWC_HCTSIZ_DATA1 2 ++#define DWC_HCTSIZ_DATA2 1 ++#define DWC_HCTSIZ_MDATA 3 ++#define DWC_HCTSIZ_SETUP 3 ++ unsigned pid : 2; ++ /** Data packets to transfer */ ++ unsigned pktcnt : 10; ++ /** Total transfer size in bytes */ ++ unsigned xfersize : 19; ++ } b; ++} hctsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Interrupt Mask ++ * Register. Read the register into the d32 member then set/clear the ++ * bits using the bit elements. Write the d32 member to the ++ * hcintmsk register. ++ */ ++typedef union hcintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned reserved : 21; ++ unsigned datatglerr : 1; ++ unsigned frmovrun : 1; ++ unsigned bblerr : 1; ++ unsigned xacterr : 1; ++ unsigned nyet : 1; ++ unsigned ack : 1; ++ unsigned nak : 1; ++ unsigned stall : 1; ++ unsigned ahberr : 1; ++ unsigned chhltd : 1; ++ unsigned xfercompl : 1; ++ } b; ++} hcintmsk_data_t; ++ ++/** OTG Host Interface Structure. ++ * ++ * The OTG Host Interface Structure structure contains information ++ * needed to manage the DWC_otg controller acting in host mode. It ++ * represents the programming view of the host-specific aspects of the ++ * controller. ++ */ ++typedef struct dwc_otg_host_if { ++ /** Host Global Registers starting at offset 400h.*/ ++ dwc_otg_host_global_regs_t *host_global_regs; ++#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400 ++ ++ /** Host Port 0 Control and Status Register */ ++ volatile uint32_t *hprt0; ++#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440 ++ ++ ++ /** Host Channel Specific Registers at offsets 500h-5FCh. */ ++ dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS]; ++#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500 ++#define DWC_OTG_CHAN_REGS_OFFSET 0x20 ++ ++ ++ /* Host configuration information */ ++ /** Number of Host Channels (range: 1-16) */ ++ uint8_t num_host_channels; ++ /** Periodic EPs supported (0: no, 1: yes) */ ++ uint8_t perio_eps_supported; ++ /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */ ++ uint16_t perio_tx_fifo_size; ++ ++} dwc_otg_host_if_t; ++ ++#endif +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0045-dwc_otg-remove-bogus-halt_channel.patch b/target/linux/lantiq/patches-3.3/0045-dwc_otg-remove-bogus-halt_channel.patch new file mode 100644 index 0000000000..888e4ccb26 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0045-dwc_otg-remove-bogus-halt_channel.patch @@ -0,0 +1,26 @@ +From 0cb5a616c2f1aaca4e8946155e84982ae96aae4a Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 23 Mar 2012 16:14:33 +0100 +Subject: [PATCH 45/70] dwc_otg: remove bogus halt_channel + +https://lists.openwrt.org/pipermail/openwrt-devel/2012-March/014524.html +--- + drivers/usb/dwc_otg/dwc_otg_hcd_intr.c | 2 -- + 1 files changed, 0 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c +index 834b5e0..f6f3f3d 100644 +--- a/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c ++++ b/drivers/usb/dwc_otg/dwc_otg_hcd_intr.c +@@ -1278,8 +1278,6 @@ static int32_t handle_hc_ack_intr(dwc_otg_hcd_t *_hcd, + * automatically executes the PING, then the transfer. + */ + halt_channel(_hcd, _hc, _qtd, DWC_OTG_HC_XFER_ACK, must_free); +- } else { +- halt_channel(_hcd, _hc, _qtd, _hc->halt_status, must_free); + } + } + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0046-MIPS-adds-ifxhcd.patch b/target/linux/lantiq/patches-3.3/0046-MIPS-adds-ifxhcd.patch new file mode 100644 index 0000000000..fdf5efbda7 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0046-MIPS-adds-ifxhcd.patch @@ -0,0 +1,16672 @@ +From 518d6418815f8ee4189b5ac3455a580fa5f3d3a6 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sun, 11 Mar 2012 15:59:39 +0100 +Subject: [PATCH 46/70] MIPS: adds ifxhcd + +--- + arch/mips/lantiq/xway/Makefile | 2 +- + arch/mips/lantiq/xway/dev-ifxhcd.c | 45 + + arch/mips/lantiq/xway/dev-ifxhcd.h | 17 + + arch/mips/lantiq/xway/sysctrl.c | 2 + + drivers/usb/Kconfig | 2 + + drivers/usb/Makefile | 2 + + drivers/usb/ifxhcd/Kconfig | 58 + + drivers/usb/ifxhcd/Makefile | 85 + + drivers/usb/ifxhcd/TagHistory | 171 ++ + drivers/usb/ifxhcd/ifxhcd.c | 2523 +++++++++++++++++++++++ + drivers/usb/ifxhcd/ifxhcd.h | 628 ++++++ + drivers/usb/ifxhcd/ifxhcd_es.c | 549 +++++ + drivers/usb/ifxhcd/ifxhcd_intr.c | 3742 +++++++++++++++++++++++++++++++++++ + drivers/usb/ifxhcd/ifxhcd_queue.c | 418 ++++ + drivers/usb/ifxhcd/ifxusb_cif.c | 1458 ++++++++++++++ + drivers/usb/ifxhcd/ifxusb_cif.h | 665 +++++++ + drivers/usb/ifxhcd/ifxusb_cif_d.c | 458 +++++ + drivers/usb/ifxhcd/ifxusb_cif_h.c | 846 ++++++++ + drivers/usb/ifxhcd/ifxusb_ctl.c | 1385 +++++++++++++ + drivers/usb/ifxhcd/ifxusb_driver.c | 970 +++++++++ + drivers/usb/ifxhcd/ifxusb_plat.h | 1018 ++++++++++ + drivers/usb/ifxhcd/ifxusb_regs.h | 1420 +++++++++++++ + drivers/usb/ifxhcd/ifxusb_version.h | 5 + + 23 files changed, 16468 insertions(+), 1 deletions(-) + create mode 100644 arch/mips/lantiq/xway/dev-ifxhcd.c + create mode 100644 arch/mips/lantiq/xway/dev-ifxhcd.h + create mode 100644 drivers/usb/ifxhcd/Kconfig + create mode 100644 drivers/usb/ifxhcd/Makefile + create mode 100644 drivers/usb/ifxhcd/TagHistory + create mode 100644 drivers/usb/ifxhcd/ifxhcd.c + create mode 100644 drivers/usb/ifxhcd/ifxhcd.h + create mode 100644 drivers/usb/ifxhcd/ifxhcd_es.c + create mode 100644 drivers/usb/ifxhcd/ifxhcd_intr.c + create mode 100644 drivers/usb/ifxhcd/ifxhcd_queue.c + create mode 100644 drivers/usb/ifxhcd/ifxusb_cif.c + create mode 100644 drivers/usb/ifxhcd/ifxusb_cif.h + create mode 100644 drivers/usb/ifxhcd/ifxusb_cif_d.c + create mode 100644 drivers/usb/ifxhcd/ifxusb_cif_h.c + create mode 100644 drivers/usb/ifxhcd/ifxusb_ctl.c + create mode 100644 drivers/usb/ifxhcd/ifxusb_driver.c + create mode 100644 drivers/usb/ifxhcd/ifxusb_plat.h + create mode 100644 drivers/usb/ifxhcd/ifxusb_regs.h + create mode 100644 drivers/usb/ifxhcd/ifxusb_version.h + +diff --git a/arch/mips/lantiq/xway/Makefile b/arch/mips/lantiq/xway/Makefile +index 4c3106f..c9baf91 100644 +--- a/arch/mips/lantiq/xway/Makefile ++++ b/arch/mips/lantiq/xway/Makefile +@@ -1,4 +1,4 @@ +-obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o nand.o timer.o ++obj-y := sysctrl.o reset.o gpio.o gpio_stp.o gpio_ebu.o devices.o dma.o clk.o prom.o nand.o timer.o dev-ifxhcd.o + + obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o + obj-$(CONFIG_LANTIQ_MACH_EASY50601) += mach-easy50601.o +diff --git a/arch/mips/lantiq/xway/dev-ifxhcd.c b/arch/mips/lantiq/xway/dev-ifxhcd.c +new file mode 100644 +index 0000000..ea08a35 +--- /dev/null ++++ b/arch/mips/lantiq/xway/dev-ifxhcd.c +@@ -0,0 +1,45 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Copyright (C) 2012 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++ ++static u64 dmamask = (u32)0x1fffffff; ++ ++static struct platform_device platform_dev = { ++ .name = "ifxusb_hcd", ++ .dev.dma_mask = &dmamask, ++}; ++ ++int __init ++xway_register_hcd(int *pins) ++{ ++ platform_dev.dev.platform_data = pins; ++ return platform_device_register(&platform_dev); ++} +diff --git a/arch/mips/lantiq/xway/dev-ifxhcd.h b/arch/mips/lantiq/xway/dev-ifxhcd.h +new file mode 100644 +index 0000000..18b3d2d +--- /dev/null ++++ b/arch/mips/lantiq/xway/dev-ifxhcd.h +@@ -0,0 +1,17 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * Copyright (C) 2012 John Crispin ++ */ ++ ++#ifndef _LTQ_DEV_HCD_H__ ++#define _LTQ_DEV_HCD_H__ ++ ++#include ++ ++extern void __init xway_register_hcd(int *pin); ++ ++#endif +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 1a2e2d4..ac7383f 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -166,6 +166,8 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_pcie", "pdi", 1, PMU1_PCIE_PDI); + clkdev_add_pmu("ltq_pcie", "ctl", 1, PMU1_PCIE_CTL); + clkdev_add_pmu("ltq_pcie", "ahb", 0, PMU_AHBM | PMU_AHBS); ++ clkdev_add_pmu("usb0", NULL, 0, (1<<6) | 1); ++ clkdev_add_pmu("usb1", NULL, 0, (1<<26) | (1<<27)); + } else { + clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), + ltq_danube_io_region_clock()); +diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig +index 8469e23..f69cc4a 100644 +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -184,4 +184,6 @@ source "drivers/usb/gadget/Kconfig" + + source "drivers/usb/otg/Kconfig" + ++source "drivers/usb/ifxhcd/Kconfig" ++ + endif # USB_SUPPORT +diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile +index d46b792..7068e99 100644 +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -58,3 +58,5 @@ obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs/ + obj-$(CONFIG_USB_GADGET) += gadget/ + + obj-$(CONFIG_USB_COMMON) += usb-common.o ++ ++obj-$(CONFIG_USB_HOST_IFX) += ifxhcd/ +diff --git a/drivers/usb/ifxhcd/Kconfig b/drivers/usb/ifxhcd/Kconfig +new file mode 100644 +index 0000000..7eb8ceb +--- /dev/null ++++ b/drivers/usb/ifxhcd/Kconfig +@@ -0,0 +1,58 @@ ++ ++config USB_HOST_IFX ++ tristate "Infineon USB Host Controller Driver" ++ depends on USB ++ default n ++ help ++ Infineon USB Host Controller ++ ++config USB_HOST_IFX_B ++ bool "USB host mode on core 1 and 2" ++ depends on USB_HOST_IFX ++ help ++ Both cores run as host ++ ++#config USB_HOST_IFX_1 ++#config USB_HOST_IFX_2 ++ ++#config IFX_DANUBE ++#config IFX_AMAZON_SE ++config IFX_AR9 ++ depends on USB_HOST_IFX ++ bool "AR9" ++ ++config IFX_VR9 ++ depends on USB_HOST_IFX ++ bool "VR9" ++ ++#config USB_HOST_IFX_FORCE_USB11 ++# bool "Forced USB1.1" ++# depends on USB_HOST_IFX ++# default n ++# help ++# force to be USB 1.1 ++ ++#config USB_HOST_IFX_WITH_HS_ELECT_TST ++# bool "With HS_Electrical Test" ++# depends on USB_HOST_IFX ++# default n ++# help ++# With USBIF HSET routines ++ ++#config USB_HOST_IFX_WITH_ISO ++# bool "With ISO transfer" ++# depends on USB_HOST_IFX ++# default n ++# help ++# With USBIF ISO transfer ++ ++config USB_HOST_IFX_UNALIGNED_ADJ ++ bool "Adjust" ++ depends on USB_HOST_IFX ++ help ++ USB_HOST_IFX_UNALIGNED_ADJ ++ ++#config USB_HOST_IFX_UNALIGNED_CHK ++#config USB_HOST_IFX_UNALIGNED_NONE ++ ++ +diff --git a/drivers/usb/ifxhcd/Makefile b/drivers/usb/ifxhcd/Makefile +new file mode 100644 +index 0000000..0a2ac99 +--- /dev/null ++++ b/drivers/usb/ifxhcd/Makefile +@@ -0,0 +1,85 @@ ++ ++# ++# Makefile for USB Core files and filesystem ++# ++ ifxusb_host-objs := ifxusb_driver.o ++ ifxusb_host-objs += ifxusb_ctl.o ++ ifxusb_host-objs += ifxusb_cif.o ++ ifxusb_host-objs += ifxusb_cif_h.o ++ ifxusb_host-objs += ifxhcd.o ++ ifxusb_host-objs += ifxhcd_es.o ++ ifxusb_host-objs += ifxhcd_intr.o ++ ifxusb_host-objs += ifxhcd_queue.o ++ ++ifeq ($(CONFIG_IFX_TWINPASS),y) ++ EXTRA_CFLAGS += -D__IS_TWINPASS__ ++endif ++ifeq ($(CONFIG_IFX_DANUBE),y) ++ EXTRA_CFLAGS += -D__IS_DANUBE__ ++endif ++ifeq ($(CONFIG_IFX_AMAZON_SE),y) ++ EXTRA_CFLAGS += -D__IS_AMAZON_SE__ ++endif ++ifeq ($(CONFIG_IFX_AR9),y) ++ EXTRA_CFLAGS += -D__IS_AR9__ ++endif ++ifeq ($(CONFIG_IFX_AMAZON_S),y) ++ EXTRA_CFLAGS += -D__IS_AR9__ ++endif ++ifeq ($(CONFIG_IFX_VR9),y) ++ EXTRA_CFLAGS += -D__IS_VR9__ ++endif ++ ++ifeq ($(CONFIG_USB_HOST_IFX),y) ++ EXTRA_CFLAGS += -Dlinux -D__LINUX__ ++ EXTRA_CFLAGS += -D__IS_HOST__ ++ EXTRA_CFLAGS += -D__KERNEL__ ++endif ++ ++ifeq ($(CONFIG_USB_HOST_IFX),m) ++ EXTRA_CFLAGS += -Dlinux -D__LINUX__ ++ EXTRA_CFLAGS += -D__IS_HOST__ ++ EXTRA_CFLAGS += -D__KERNEL__ ++endif ++ ++ifeq ($(CONFIG_USB_DEBUG),y) ++ EXTRA_CFLAGS += -D__DEBUG__ ++ EXTRA_CFLAGS += -D__ENABLE_DUMP__ ++endif ++ ++ifeq ($(CONFIG_USB_HOST_IFX_B),y) ++ EXTRA_CFLAGS += -D__IS_DUAL__ ++endif ++ifeq ($(CONFIG_USB_HOST_IFX_1),y) ++ EXTRA_CFLAGS += -D__IS_FIRST__ ++endif ++ifeq ($(CONFIG_USB_HOST_IFX_2),y) ++ EXTRA_CFLAGS += -D__IS_SECOND__ ++endif ++ ++ifeq ($(CONFIG_USB_HOST_IFX_FORCE_USB11),y) ++ EXTRA_CFLAGS += -D__FORCE_USB11__ ++endif ++ifeq ($(CONFIG_USB_HOST_IFX_WITH_HS_ELECT_TST),y) ++ EXTRA_CFLAGS += -D__WITH_HS_ELECT_TST__ ++endif ++ifeq ($(CONFIG_USB_HOST_IFX_WITH_ISO),y) ++ EXTRA_CFLAGS += -D__EN_ISOC__ ++endif ++ifeq ($(CONFIG_USB_HOST_IFX_UNALIGNED_ADJ),y) ++ EXTRA_CFLAGS += -D__UNALIGNED_BUFFER_ADJ__ ++endif ++ifeq ($(CONFIG_USB_HOST_IFX_UNALIGNED_CHK),y) ++ EXTRA_CFLAGS += -D__UNALIGNED_BUFFER_CHK__ ++endif ++ ++# EXTRA_CFLAGS += -D__DYN_SOF_INTR__ ++ EXTRA_CFLAGS += -D__UEIP__ ++# EXTRA_CFLAGS += -D__EN_ISOC__ ++# EXTRA_CFLAGS += -D__EN_ISOC_SPLIT__ ++ ++## 20110628 AVM/WK New flag for less SOF IRQs ++ EXTRA_CFLAGS += -D__USE_TIMER_4_SOF__ ++ ++obj-$(CONFIG_USB_HOST_IFX) += ifxusb_host.o ++ +diff --git a/drivers/usb/ifxhcd/TagHistory b/drivers/usb/ifxhcd/TagHistory +new file mode 100644 +index 0000000..3820d70 +--- /dev/null ++++ b/drivers/usb/ifxhcd/TagHistory +@@ -0,0 +1,171 @@ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://embeddedvm/home/SVN/drivers/usb_host20/tags/5.18-r240-non_musb_ar9_vr9-SOF_Timer_Fixed ++| Erzeugt mit SVN-Tagger Version 3.74. +++----------------------------------------------------------------------+ ++FIX - Korrektur bei der SOF-Timer/IRQ Steuerung. (Bug in Tag 5.17) ++FIX - Fehlerbehandlung an mehreren Stellen korrigiert bzw. eingebaut. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://embeddedvm/home/SVN/drivers/usb_host20/tags/5.17-r237-non_musb_ar9_vr9-2_6_32_41_Kompatibel ++| Erzeugt mit SVN-Tagger Version 3.73. +++----------------------------------------------------------------------+ ++FIX - Kompatiblität zum Update auf Kernel 2.6.32-41. Weiterhin für 28er geeignet. ++ENH - Reduktion der Interrruptlast durch Nutzung eines hrtimers anstatt SOF-IRQ. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.16-r208-non_musb_ar9_vr9-20110421_Zero_Paket_Optimiert ++| Erzeugt mit SVN-Tagger Version 3.66. +++----------------------------------------------------------------------+ ++ ++FIX - VR9 / AR9 - Zero Packet. Optimierung korrigiert. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.15-r205-non_musb_ar9_vr9-20110421_Zero_Paket_WA_funktioniert ++| Erzeugt mit SVN-Tagger Version 3.66. +++----------------------------------------------------------------------+ ++ ++FIX - VR9 / AR9 - "Zero Packet" funktioniert nun wirklich. Letzter Tag hatte einen Bug. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.14-r202-non_musb_ar9_vr9-20110420_Zero_Paket_WA ++| Erzeugt mit SVN-Tagger Version 3.66. +++----------------------------------------------------------------------+ ++ ++FIX - VR9 / AR9 - Zero Packet Workaround: ZLP wird nun geschickt wenn URB_ZERO_PACKET aktiv ist. ++ Wird von LTE Altair Firmware benoetig. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.13-r199-non_musb_ar9_vr9-20110310_Init_Fix ++| Erzeugt mit SVN-Tagger Version 3.64. +++----------------------------------------------------------------------+ ++ ++FIX - VR9 / AR9 - Timing der Initialisierungsphase angepasst zum Kernel 2.6.28 mit UGW-4.3.1. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.12-r184-non_musb_ar9_vr9-20110118_Full_Speed_Fix ++| Erzeugt mit SVN-Tagger Version 3.58. +++----------------------------------------------------------------------+ ++AR9/VR9 (3370,6840,7320): ++Makefile - FIX - (Workaround) Debug Modus hilft gegen Enumerationsfehler bei Full Speed Drucker. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.11-r175-non_musb_ar9_vr9-20101220_VR9_2_Ports_DMA_Fix ++| Erzeugt mit SVN-Tagger Version 3.58. +++----------------------------------------------------------------------+ ++ ++FIX - VR9 - Workaround DMA Burst Size. Wenn beiden USB Ports benutzt werden, geht der USB Host nicht mehr. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.10-r169-non_musb_ar9_vr9-Fix_Spontan_Reboot ++| Erzeugt mit SVN-Tagger Version 3.58. +++----------------------------------------------------------------------+ ++ ++FIX - Endlosschleife führte zu einem spontanen Reboot. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.9-r166-non_musb_ar9_vr9-20101112_deferred_completion ++| Erzeugt mit SVN-Tagger Version 3.58. +++----------------------------------------------------------------------+ ++ ++ENH - Deferred URB Completion Mechanismus eingebaut. Nun ca. 10% schneller bei usb-storage. ++ ++FIX - PING Flow Control gefixt. ++FIX - Channel Halt wird nun immer angerufen. (Split Transaction wurde nicht erfolgreich gestoppt). ++FIX - Spinlock Benutzung verbessert. Mehr Stabilitaet. ++ ++CHG - Ubersetztungsoption __DEBUG__ ist nun abhaengig von CONFIG_USB_DEBUG ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.8-r149-non_musb_ar9_vr9-20100827_LTE_Interrupt_EP_Fix ++| Erzeugt mit SVN-Tagger Version 3.57. +++----------------------------------------------------------------------+ ++AR9/VR9 - FIX - Interrupt Packets gingen verloren, wegen falschem Timing beim OddFrame Bit. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.7-r142-non_musb_ar9_vr9-20100728_Unaligned_Buf_Fix ++| Erzeugt mit SVN-Tagger Version 3.57. +++----------------------------------------------------------------------+ ++FIX - "Unaligned Data" Flag wieder nach Transfer geloescht. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.6-r133-non_musb_ar9_vr9-20100714_Toggle_Datenverlust_Fix ++| Erzeugt mit SVN-Tagger Version 3.57. +++----------------------------------------------------------------------+ ++TL5508 - Einige UMTS Modems funktionierten nicht korrekt an der 7320 (AR9). ++FIX - USB Data Toggle des usbcore benutzen. Datenverlust nach EP-Halt. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.5-r130-non_musb_ar9_vr9-20100712_USB_Ports_abschaltbar ++| Erzeugt mit SVN-Tagger Version 3.57. +++----------------------------------------------------------------------+ ++Power - Fix - Beide USB Port abschaltbar bei rmmod. ++rmmod - FIX - URB_Dequeue funktionierte beim Entladen des Treibers nicht (mehrere Ursachen). ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.4-r126-non_musb_ar9_vr9-20100701_Lost_Interrupt_Workaround ++| Erzeugt mit SVN-Tagger Version 3.57. +++----------------------------------------------------------------------+ ++FIX - Workaround wegen verpasstem Interrupt, bei Full-Speed Interrupt EP. ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.3-r123-non_musb_ar9_vr9-20100630_UMTS_Fixes ++| Erzeugt mit SVN-Tagger Version 3.57. +++----------------------------------------------------------------------+ ++FIX - Full-Speed Interrupt Endpoint hinter Hi-Speed Hub funktioniert nun (UMTS Modems) ++FIX - usb_hcd_link_urb_from_ep API von USBCore muss benutzt werden. ++FIX - Interrupt URBs nicht bei NAK completen. ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.2-r114-non_musb_ar9_vr9-20100520_StickAndSurf_funktioniert ++| Erzeugt mit SVN-Tagger Version 3.56. +++----------------------------------------------------------------------+ ++- Merge mit neuen LANTIQ Sourcen "3.0alpha B100312" ++- Fix - Spin_lock eingebaut, Stick&Surf funktioniert nun ++ ++- DEP - CONFIG_USB_HOST_IFX_WITH_ISO wird nicht unterstuetzt: In der Kernel Config deaktivieren. ++ ++ ++ +++----------------------------------------------------------------------+ ++| TAG: svn://EmbeddedVM/home/SVN/drivers/usb_host20/tags/5.1-r107-non_musb_ar9_vr9-20100505_IFXUSB_Host_mit_Energiemonitor ++| Erzeugt mit SVN-Tagger Version 3.56. +++----------------------------------------------------------------------+ ++USB Host Treiber für AR9 und VR9 ++-------------------------------- ++FIX - Toggle Error nach STALL - Einfacher Workaround - Nun werden Massenspeicherpartitionen erkannt! ++AVM_POWERMETER - USB Energiemonitor Support. ++ ++Bekanntes Problem: Stick and Surf funktioniert nur sporadisch, weil CONTROL_IRQ manchmal ausbleibt. ++ +diff --git a/drivers/usb/ifxhcd/ifxhcd.c b/drivers/usb/ifxhcd/ifxhcd.c +new file mode 100644 +index 0000000..d2ae125 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxhcd.c +@@ -0,0 +1,2523 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxhcd.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : This file contains the structures, constants, and interfaces for ++ ** the Host Contoller Driver (HCD). ++ ** ++ ** The Host Controller Driver (HCD) is responsible for translating requests ++ ** from the USB Driver into the appropriate actions on the IFXUSB controller. ++ ** It isolates the USBD from the specifics of the controller by providing an ++ ** API to the USBD. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxhcd.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the implementation of the HCD. In Linux, ++ the HCD implements the hc_driver API. ++*/ ++ ++#include ++#include "ifxusb_version.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++#include "ifxhcd.h" ++ ++#include ++ ++#ifdef CONFIG_AVM_POWERMETER ++#include ++#endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ ++ ++#ifdef __DEBUG__ ++ static void dump_urb_info(struct urb *_urb, char* _fn_name); ++ static void dump_channel_info(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh); ++#endif ++ ++ ++/*! ++ \brief Sets the final status of an URB and returns it to the device driver. Any ++ required cleanup of the URB is performed. ++ */ ++void ifxhcd_complete_urb(ifxhcd_hcd_t *_ifxhcd, ifxhcd_urbd_t *_urbd, int _status) ++{ ++ struct urb *urb=NULL; ++ unsigned long flags = 0; ++ ++ /*== AVM/BC 20101111 Function called with Lock ==*/ ++ //SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++ if (!list_empty(&_urbd->urbd_list_entry)) ++ list_del_init (&_urbd->urbd_list_entry); ++ ++ if(!_urbd->urb) ++ { ++ IFX_ERROR("%s: invalid urb\n",__func__); ++ /*== AVM/BC 20101111 Function called with Lock ==*/ ++ //SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ return; ++ } ++ ++ urb=_urbd->urb; ++ ++ #ifdef __DEBUG__ ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) ++ { ++ IFX_PRINT("%s: _urbd %p, urb %p, device %d, ep %d %s/%s, status=%d\n", ++ __func__, _urbd,_urbd->urb, usb_pipedevice(_urbd->urb->pipe), ++ usb_pipeendpoint(_urbd->urb->pipe), ++ usb_pipein(_urbd->urb->pipe) ? "IN" : "OUT", ++ (_urbd->is_in) ? "IN" : "OUT", ++ _status); ++ if (_urbd->epqh->ep_type == IFXUSB_EP_TYPE_ISOC) ++ { ++ int i; ++ for (i = 0; i < _urbd->urb->number_of_packets; i++) ++ IFX_PRINT(" ISO Desc %d status: %d\n", i, _urbd->urb->iso_frame_desc[i].status); ++ } ++ } ++ #endif ++ ++ if (!_urbd->epqh) ++ IFX_ERROR("%s: invalid epqd\n",__func__); ++ ++ #if defined(__UNALIGNED_BUFFER_ADJ__) ++ else if(_urbd->is_active) ++ { ++ if( _urbd->epqh->aligned_checked && ++ _urbd->epqh->using_aligned_buf && ++ _urbd->xfer_buff && ++ _urbd->is_in ) ++ memcpy(_urbd->xfer_buff,_urbd->epqh->aligned_buf,_urbd->xfer_len); ++ _urbd->epqh->using_aligned_buf=0; ++ _urbd->epqh->using_aligned_setup=0; ++ _urbd->epqh->aligned_checked=0; ++ } ++ #endif ++ ++ urb->status = _status; ++ urb->hcpriv=NULL; ++ kfree(_urbd); ++ ++ usb_hcd_unlink_urb_from_ep(ifxhcd_to_syshcd(_ifxhcd), urb); ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ ++// usb_hcd_giveback_urb(ifxhcd_to_syshcd(_ifxhcd), urb); ++ usb_hcd_giveback_urb(ifxhcd_to_syshcd(_ifxhcd), urb, _status); ++ ++ /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++} ++ ++/*== AVM/BC 20101111 URB Complete deferred ++ * Must be called with Spinlock ++ */ ++ ++/*! ++ \brief Inserts an urbd structur in the completion list. The urbd will be ++ later completed by select_eps_sub ++ */ ++void defer_ifxhcd_complete_urb(ifxhcd_hcd_t *_ifxhcd, ifxhcd_urbd_t *_urbd, int _status) ++{ ++ ++ _urbd->status = _status; ++ ++ //Unlink Urbd from epqh / Insert it into the complete list ++ list_move_tail(&_urbd->urbd_list_entry, &_ifxhcd->urbd_complete_list); ++ ++} ++ ++/*! ++ \brief Processes all the URBs in a single EPQHs. Completes them with ++ status and frees the URBD. ++ */ ++//static ++void kill_all_urbs_in_epqh(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh, int _status) ++{ ++ struct list_head *urbd_item; ++ ifxhcd_urbd_t *urbd; ++ ++ if(!_epqh) ++ return; ++ ++ for (urbd_item = _epqh->urbd_list.next; ++ urbd_item != &_epqh->urbd_list; ++ urbd_item = _epqh->urbd_list.next) ++ { ++ urbd = list_entry(urbd_item, ifxhcd_urbd_t, urbd_list_entry); ++ ifxhcd_complete_urb(_ifxhcd, urbd, _status); ++ } ++} ++ ++ ++/*! ++ \brief Free all EPS in one Processes all the URBs in a single list of EPQHs. Completes them with ++ -ETIMEDOUT and frees the URBD. ++ */ ++//static ++void epqh_list_free(ifxhcd_hcd_t *_ifxhcd, struct list_head *_epqh_list) ++{ ++ struct list_head *item; ++ ifxhcd_epqh_t *epqh; ++ ++ if (!_epqh_list) ++ return; ++ if (_epqh_list->next == NULL) /* The list hasn't been initialized yet. */ ++ return; ++ ++ /* Ensure there are no URBDs or URBs left. */ ++ for (item = _epqh_list->next; item != _epqh_list; item = _epqh_list->next) ++ { ++ epqh = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ kill_all_urbs_in_epqh(_ifxhcd, epqh, -ETIMEDOUT); ++ ifxhcd_epqh_free(epqh); ++ } ++} ++ ++ ++ ++//static ++void epqh_list_free_all(ifxhcd_hcd_t *_ifxhcd) ++{ ++ unsigned long flags; ++ ++ /*== AVM/BC 20101111 - 2.6.28 Needs Spinlock ==*/ ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_np_active ); ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_np_ready ); ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_intr_active ); ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_intr_ready ); ++ #ifdef __EN_ISOC__ ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_isoc_active ); ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_isoc_ready ); ++ #endif ++ epqh_list_free(_ifxhcd, &_ifxhcd->epqh_stdby ); ++ ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ ++} ++ ++ ++/*! ++ \brief This function is called to handle the disconnection of host port. ++ */ ++int32_t ifxhcd_disconnect(ifxhcd_hcd_t *_ifxhcd) ++{ ++ IFX_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, _ifxhcd); ++ ++ /* Set status flags for the hub driver. */ ++ _ifxhcd->flags.b.port_connect_status_change = 1; ++ _ifxhcd->flags.b.port_connect_status = 0; ++ ++ /* ++ * Shutdown any transfers in process by clearing the Tx FIFO Empty ++ * interrupt mask and status bits and disabling subsequent host ++ * channel interrupts. ++ */ ++ { ++ gint_data_t intr = { .d32 = 0 }; ++ intr.b.nptxfempty = 1; ++ intr.b.ptxfempty = 1; ++ intr.b.hcintr = 1; ++ ifxusb_mreg (&_ifxhcd->core_if.core_global_regs->gintmsk, intr.d32, 0); ++ ifxusb_mreg (&_ifxhcd->core_if.core_global_regs->gintsts, intr.d32, 0); ++ } ++ ++ /* Respond with an error status to all URBs in the schedule. */ ++ epqh_list_free_all(_ifxhcd); ++ ++ /* Clean up any host channels that were in use. */ ++ { ++ int num_channels; ++ ifxhcd_hc_t *channel; ++ ifxusb_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ int i; ++ ++ num_channels = _ifxhcd->core_if.params.host_channels; ++ ++ for (i = 0; i < num_channels; i++) ++ { ++ channel = &_ifxhcd->ifxhc[i]; ++ if (list_empty(&channel->hc_list_entry)) ++ { ++ hc_regs = _ifxhcd->core_if.hc_regs[i]; ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ if (hcchar.b.chen) ++ { ++ /* Halt the channel. */ ++ hcchar.b.chdis = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ } ++ list_add_tail(&channel->hc_list_entry, &_ifxhcd->free_hc_list); ++ ifxhcd_hc_cleanup(&_ifxhcd->core_if, channel); ++ } ++ } ++ } ++ return 1; ++} ++ ++ ++/*! ++ \brief Frees secondary storage associated with the ifxhcd_hcd structure contained ++ in the struct usb_hcd field. ++ */ ++static void ifxhcd_freeextra(struct usb_hcd *_syshcd) ++{ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd); ++ ++ IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD FREE\n"); ++ ++ /* Free memory for EPQH/URBD lists */ ++ epqh_list_free_all(ifxhcd); ++ ++ /* Free memory for the host channels. */ ++ ifxusb_free_buf(ifxhcd->status_buf); ++ return; ++} ++#ifdef __USE_TIMER_4_SOF__ ++static enum hrtimer_restart ifxhcd_timer_func(struct hrtimer *timer) { ++ ifxhcd_hcd_t *ifxhcd = container_of(timer, ifxhcd_hcd_t, hr_timer); ++ ++ ifxhcd_handle_intr(ifxhcd); ++ ++ return HRTIMER_NORESTART; ++} ++#endif ++ ++/*! ++ \brief Initializes the HCD. This function allocates memory for and initializes the ++ static parts of the usb_hcd and ifxhcd_hcd structures. It also registers the ++ USB bus with the core and calls the hc_driver->start() function. It returns ++ a negative error on failure. ++ */ ++int ifxhcd_init(ifxhcd_hcd_t *_ifxhcd) ++{ ++ int retval = 0; ++ struct usb_hcd *syshcd = NULL; ++ ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD INIT\n"); ++ ++ spin_lock_init(&_ifxhcd->lock); ++#ifdef __USE_TIMER_4_SOF__ ++ hrtimer_init(&_ifxhcd->hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); ++ _ifxhcd->hr_timer.function = ifxhcd_timer_func; ++#endif ++ _ifxhcd->hc_driver.description = _ifxhcd->core_if.core_name; ++ _ifxhcd->hc_driver.product_desc = "IFX USB Controller"; ++ //_ifxhcd->hc_driver.hcd_priv_size = sizeof(ifxhcd_hcd_t); ++ _ifxhcd->hc_driver.hcd_priv_size = sizeof(unsigned long); ++ _ifxhcd->hc_driver.irq = ifxhcd_irq; ++ _ifxhcd->hc_driver.flags = HCD_MEMORY | HCD_USB2; ++ _ifxhcd->hc_driver.start = ifxhcd_start; ++ _ifxhcd->hc_driver.stop = ifxhcd_stop; ++ //_ifxhcd->hc_driver.reset = ++ //_ifxhcd->hc_driver.suspend = ++ //_ifxhcd->hc_driver.resume = ++ _ifxhcd->hc_driver.urb_enqueue = ifxhcd_urb_enqueue; ++ _ifxhcd->hc_driver.urb_dequeue = ifxhcd_urb_dequeue; ++ _ifxhcd->hc_driver.endpoint_disable = ifxhcd_endpoint_disable; ++ _ifxhcd->hc_driver.get_frame_number = ifxhcd_get_frame_number; ++ _ifxhcd->hc_driver.hub_status_data = ifxhcd_hub_status_data; ++ _ifxhcd->hc_driver.hub_control = ifxhcd_hub_control; ++ //_ifxhcd->hc_driver.hub_suspend = ++ //_ifxhcd->hc_driver.hub_resume = ++ ++ /* Allocate memory for and initialize the base HCD and */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) ++ syshcd = usb_create_hcd(&_ifxhcd->hc_driver, _ifxhcd->dev, _ifxhcd->core_if.core_name); ++#else ++ syshcd = usb_create_hcd(&_ifxhcd->hc_driver, _ifxhcd->dev, _ifxhcd->dev->bus_id); ++#endif ++ ++ if (syshcd == NULL) ++ { ++ retval = -ENOMEM; ++ goto error1; ++ } ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) ++ syshcd->has_tt = 1; ++#endif ++ ++ syshcd->rsrc_start = (unsigned long)_ifxhcd->core_if.core_global_regs; ++ syshcd->regs = (void *)_ifxhcd->core_if.core_global_regs; ++ syshcd->self.otg_port = 0; ++ ++ //*((unsigned long *)(&(syshcd->hcd_priv)))=(unsigned long)_ifxhcd; ++ //*((unsigned long *)(&(syshcd->hcd_priv[0])))=(unsigned long)_ifxhcd; ++ syshcd->hcd_priv[0]=(unsigned long)_ifxhcd; ++ _ifxhcd->syshcd=syshcd; ++ ++ INIT_LIST_HEAD(&_ifxhcd->epqh_np_active ); ++ INIT_LIST_HEAD(&_ifxhcd->epqh_np_ready ); ++ INIT_LIST_HEAD(&_ifxhcd->epqh_intr_active ); ++ INIT_LIST_HEAD(&_ifxhcd->epqh_intr_ready ); ++ #ifdef __EN_ISOC__ ++ INIT_LIST_HEAD(&_ifxhcd->epqh_isoc_active ); ++ INIT_LIST_HEAD(&_ifxhcd->epqh_isoc_ready ); ++ #endif ++ INIT_LIST_HEAD(&_ifxhcd->epqh_stdby ); ++ INIT_LIST_HEAD(&_ifxhcd->urbd_complete_list); ++ ++ /* ++ * Create a host channel descriptor for each host channel implemented ++ * in the controller. Initialize the channel descriptor array. ++ */ ++ INIT_LIST_HEAD(&_ifxhcd->free_hc_list); ++ { ++ int num_channels = _ifxhcd->core_if.params.host_channels; ++ int i; ++ for (i = 0; i < num_channels; i++) ++ { ++ _ifxhcd->ifxhc[i].hc_num = i; ++ IFX_DEBUGPL(DBG_HCDV, "HCD Added channel #%d\n", i); ++ } ++ } ++ ++ /* Set device flags indicating whether the HCD supports DMA. */ ++ if(_ifxhcd->dev->dma_mask) ++ *(_ifxhcd->dev->dma_mask) = ~0; ++ _ifxhcd->dev->coherent_dma_mask = ~0; ++ ++ /* ++ * Finish generic HCD initialization and start the HCD. This function ++ * allocates the DMA buffer pool, registers the USB bus, requests the ++ * IRQ line, and calls ifxusb_hcd_start method. ++ */ ++// retval = usb_add_hcd(syshcd, _ifxhcd->core_if.irq, SA_INTERRUPT|SA_SHIRQ); ++ retval = usb_add_hcd(syshcd, _ifxhcd->core_if.irq, IRQF_DISABLED | IRQF_SHARED ); ++ if (retval < 0) ++ goto error2; ++ ++ /* ++ * Allocate space for storing data on status transactions. Normally no ++ * data is sent, but this space acts as a bit bucket. This must be ++ * done after usb_add_hcd since that function allocates the DMA buffer ++ * pool. ++ */ ++ _ifxhcd->status_buf = ifxusb_alloc_buf(IFXHCD_STATUS_BUF_SIZE, 1); ++ ++ if (_ifxhcd->status_buf) ++ { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,32) ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Initialized, bus=%s, usbbus=%d\n", _ifxhcd->core_if.core_name, syshcd->self.busnum); ++#else ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Initialized, bus=%s, usbbus=%d\n", _ifxhcd->dev->bus_id, syshcd->self.busnum); ++#endif ++ return 0; ++ } ++ IFX_ERROR("%s: status_buf allocation failed\n", __func__); ++ ++ /* Error conditions */ ++ usb_remove_hcd(syshcd); ++error2: ++ ifxhcd_freeextra(syshcd); ++ usb_put_hcd(syshcd); ++error1: ++ return retval; ++} ++ ++/*! ++ \brief Removes the HCD. ++ Frees memory and resources associated with the HCD and deregisters the bus. ++ */ ++void ifxhcd_remove(ifxhcd_hcd_t *_ifxhcd) ++{ ++ struct usb_hcd *syshcd = ifxhcd_to_syshcd(_ifxhcd); ++ ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD REMOVE\n"); ++ ++/* == AVM/WK 20100709 - Fix: Order changed, disable IRQs not before remove_hcd == */ ++ ++ usb_remove_hcd(syshcd); ++ ++ /* Turn off all interrupts */ ++ ifxusb_wreg (&_ifxhcd->core_if.core_global_regs->gintmsk, 0); ++ ifxusb_mreg (&_ifxhcd->core_if.core_global_regs->gahbcfg, 1, 0); ++ ++ ifxhcd_freeextra(syshcd); ++ ++ usb_put_hcd(syshcd); ++ ++ return; ++} ++ ++ ++/* ========================================================================= ++ * Linux HC Driver Functions ++ * ========================================================================= */ ++ ++/*! ++ \brief Initializes the IFXUSB controller and its root hub and prepares it for host ++ mode operation. Activates the root port. Returns 0 on success and a negative ++ error code on failure. ++ Called by USB stack. ++ */ ++int ifxhcd_start(struct usb_hcd *_syshcd) ++{ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd); ++ ifxusb_core_if_t *core_if = &ifxhcd->core_if; ++ struct usb_bus *bus; ++ ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD START\n"); ++ ++ bus = hcd_to_bus(_syshcd); ++ ++ /* Initialize the bus state. */ ++ _syshcd->state = HC_STATE_RUNNING; ++ ++ /* Initialize and connect root hub if one is not already attached */ ++ if (bus->root_hub) ++ { ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD Has Root Hub\n"); ++ /* Inform the HUB driver to resume. */ ++ usb_hcd_resume_root_hub(_syshcd); ++ } ++ ++ ifxhcd->flags.d32 = 0; ++ ++ /* Put all channels in the free channel list and clean up channel states.*/ ++ { ++ struct list_head *item; ++ item = ifxhcd->free_hc_list.next; ++ while (item != &ifxhcd->free_hc_list) ++ { ++ list_del(item); ++ item = ifxhcd->free_hc_list.next; ++ } ++ } ++ { ++ int num_channels = ifxhcd->core_if.params.host_channels; ++ int i; ++ for (i = 0; i < num_channels; i++) ++ { ++ ifxhcd_hc_t *channel; ++ channel = &ifxhcd->ifxhc[i]; ++ list_add_tail(&channel->hc_list_entry, &ifxhcd->free_hc_list); ++ ifxhcd_hc_cleanup(&ifxhcd->core_if, channel); ++ } ++ } ++ /* Initialize the USB core for host mode operation. */ ++ ++ ifxusb_host_enable_interrupts(core_if); ++ ifxusb_enable_global_interrupts(core_if); ++ ifxusb_phy_power_on (core_if); ++ ++ ifxusb_vbus_init(core_if); ++ ++ /* Turn on the vbus power. */ ++ { ++ hprt0_data_t hprt0; ++ hprt0.d32 = ifxusb_read_hprt0(core_if); ++ ++ IFX_PRINT("Init: Power Port (%d)\n", hprt0.b.prtpwr); ++ if (hprt0.b.prtpwr == 0 ) ++ { ++ hprt0.b.prtpwr = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ ifxusb_vbus_on(core_if); ++ } ++ } ++ return 0; ++} ++ ++ ++/*! ++ \brief Halts the IFXUSB host mode operations in a clean manner. USB transfers are ++ stopped. ++ */ ++void ifxhcd_stop(struct usb_hcd *_syshcd) ++{ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd); ++ hprt0_data_t hprt0 = { .d32=0 }; ++ ++ IFX_DEBUGPL(DBG_HCD, "IFX USB HCD STOP\n"); ++ ++ /* Turn off all interrupts. */ ++ ifxusb_disable_global_interrupts(&ifxhcd->core_if ); ++ ifxusb_host_disable_interrupts(&ifxhcd->core_if ); ++#ifdef __USE_TIMER_4_SOF__ ++ hrtimer_cancel(&ifxhcd->hr_timer); ++#endif ++ /* ++ * The root hub should be disconnected before this function is called. ++ * The disconnect will clear the URBD lists (via ..._hcd_urb_dequeue) ++ * and the EPQH lists (via ..._hcd_endpoint_disable). ++ */ ++ ++ /* Turn off the vbus power */ ++ IFX_PRINT("PortPower off\n"); ++ ++ ifxusb_vbus_off(&ifxhcd->core_if ); ++ ++ ifxusb_vbus_free(&ifxhcd->core_if ); ++ ++ hprt0.b.prtpwr = 0; ++ ifxusb_wreg(ifxhcd->core_if.hprt0, hprt0.d32); ++ return; ++} ++ ++/*! ++ \brief Returns the current frame number ++ */ ++int ifxhcd_get_frame_number(struct usb_hcd *_syshcd) ++{ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd); ++ hfnum_data_t hfnum; ++ ++ hfnum.d32 = ifxusb_rreg(&ifxhcd->core_if.host_global_regs->hfnum); ++ ++ return hfnum.b.frnum; ++} ++ ++/*! ++ \brief Starts processing a USB transfer request specified by a USB Request Block ++ (URB). mem_flags indicates the type of memory allocation to use while ++ processing this URB. ++ */ ++int ifxhcd_urb_enqueue( struct usb_hcd *_syshcd, ++ /*--- struct usb_host_endpoint *_sysep, Parameter im 2.6.28 entfallen ---*/ ++ struct urb *_urb, ++ gfp_t _mem_flags) ++{ ++ int retval = 0; ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd); ++ struct usb_host_endpoint *_sysep = ifxhcd_urb_to_endpoint(_urb); ++ ifxhcd_epqh_t *epqh; ++ ++ #ifdef __DEBUG__ ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) ++ dump_urb_info(_urb, "ifxusb_hcd_urb_enqueue"); ++ #endif //__DEBUG__ ++ ++ if (!ifxhcd->flags.b.port_connect_status) /* No longer connected. */ ++ return -ENODEV; ++ ++ #ifndef __EN_ISOC__ ++ if(usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) ++ { ++ IFX_ERROR("ISOC transfer not supported!!!\n"); ++ return -ENODEV; ++ } ++ #endif ++ ++ retval=ifxhcd_urbd_create (ifxhcd,_urb); ++ ++ if (retval) ++ { ++ IFX_ERROR("IFXUSB HCD URB Enqueue failed creating URBD\n"); ++ return retval; ++ } ++ epqh = (ifxhcd_epqh_t *) _sysep->hcpriv; ++ ifxhcd_epqh_ready(ifxhcd, epqh); ++ ++ select_eps(ifxhcd); ++ //enable_sof(ifxhcd); ++ { ++ gint_data_t gintsts; ++ gintsts.d32=0; ++ gintsts.b.sofintr = 1; ++ ifxusb_mreg(&ifxhcd->core_if.core_global_regs->gintmsk, 0,gintsts.d32); ++ } ++ ++ return retval; ++} ++ ++/*! ++ \brief Aborts/cancels a USB transfer request. Always returns 0 to indicate ++ success. ++ */ ++int ifxhcd_urb_dequeue( struct usb_hcd *_syshcd, ++ struct urb *_urb, int status /* Parameter neu in 2.6.28 */) ++{ ++ unsigned long flags; ++ ifxhcd_hcd_t *ifxhcd; ++ ifxhcd_urbd_t *urbd; ++ ifxhcd_epqh_t *epqh; ++ int is_active=0; ++ int rc; ++ ++ struct usb_host_endpoint *_sysep; ++ ++ IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD URB Dequeue\n"); ++ ++ #ifndef __EN_ISOC__ ++ if(usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) ++ return 0; ++ #endif ++ ++ _sysep = ifxhcd_urb_to_endpoint(_urb); ++ ++ ifxhcd = syshcd_to_ifxhcd(_syshcd); ++ ++ SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); ++ ++ /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ ++ rc = usb_hcd_check_unlink_urb(_syshcd, _urb, status); ++ if (rc) { ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++ return rc; ++ } ++ ++ urbd = (ifxhcd_urbd_t *) _urb->hcpriv; ++ ++ if(_sysep) ++ epqh = (ifxhcd_epqh_t *) _sysep->hcpriv; ++ else ++ epqh = (ifxhcd_epqh_t *) urbd->epqh; ++ ++ if(epqh!=urbd->epqh) ++ IFX_ERROR("%s inconsistant epqh %p %p\n",__func__,epqh,urbd->epqh); ++ ++ #ifdef __DEBUG__ ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) ++ { ++ dump_urb_info(_urb, "ifxhcd_urb_dequeue"); ++ if (epqh->is_active) ++ dump_channel_info(ifxhcd, epqh); ++ } ++ #endif //__DEBUG__ ++ ++ if(!epqh->hc) ++ epqh->is_active=0; ++ else if (!ifxhcd->flags.b.port_connect_status) ++ epqh->is_active=0; ++ else if (epqh->is_active && urbd->is_active) ++ { ++ /*== AVM/WK 20100709 - halt channel only if really started ==*/ ++ //if (epqh->hc->xfer_started && !epqh->hc->wait_for_sof) { ++ /*== AVM/WK 20101112 - halt channel if started ==*/ ++ if (epqh->hc->xfer_started) { ++ /* ++ * If still connected (i.e. in host mode), halt the ++ * channel so it can be used for other transfers. If ++ * no longer connected, the host registers can't be ++ * written to halt the channel since the core is in ++ * device mode. ++ */ ++ /* == 20110803 AVM/WK FIX propagate status == */ ++ if (_urb->status == -EINPROGRESS) { ++ _urb->status = status; ++ } ++ ifxhcd_hc_halt(&ifxhcd->core_if, epqh->hc, HC_XFER_URB_DEQUEUE); ++ epqh->hc = NULL; ++ is_active=1; ++ } ++ } ++ ++ if(is_active) ++ { ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++ } ++ else ++ { ++ list_del_init(&urbd->urbd_list_entry); ++ kfree (urbd); ++ ++ /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ ++ usb_hcd_unlink_urb_from_ep(_syshcd, _urb); ++ ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++ _urb->hcpriv = NULL; ++// usb_hcd_giveback_urb(_syshcd, _urb); ++ usb_hcd_giveback_urb(_syshcd, _urb, status /* neu in 2.6.28 */); ++ select_eps(ifxhcd); ++ } ++ ++ return 0; ++} ++ ++ ++ ++/*! ++ \brief Frees resources in the IFXUSB controller related to a given endpoint. Also ++ clears state in the HCD related to the endpoint. Any URBs for the endpoint ++ must already be dequeued. ++ */ ++void ifxhcd_endpoint_disable( struct usb_hcd *_syshcd, ++ struct usb_host_endpoint *_sysep) ++{ ++ ifxhcd_epqh_t *epqh; ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd(_syshcd); ++ unsigned long flags; ++ ++ int retry = 0; ++ ++ IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD EP DISABLE: _bEndpointAddress=0x%02x, " ++ "endpoint=%d\n", _sysep->desc.bEndpointAddress, ++ ifxhcd_ep_addr_to_endpoint(_sysep->desc.bEndpointAddress)); ++ ++ SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); ++ if((uint32_t)_sysep>=0x80000000 && (uint32_t)_sysep->hcpriv>=(uint32_t)0x80000000) ++ { ++ epqh = (ifxhcd_epqh_t *)(_sysep->hcpriv); ++ if (epqh && epqh->sysep==_sysep) ++ { ++ ++#if 1 /*== AVM/BC 20101111 CHG Option active: Kill URBs when disabling EP ==*/ ++ while (!list_empty(&epqh->urbd_list)) ++ { ++ if (retry++ > 250) ++ { ++ IFX_WARN("IFXUSB HCD EP DISABLE:" ++ " URBD List for this endpoint is not empty\n"); ++ break; ++ } ++ kill_all_urbs_in_epqh(ifxhcd, epqh, -ETIMEDOUT); ++ } ++#else ++ while (!list_empty(&epqh->urbd_list)) ++ { ++ /** Check that the QTD list is really empty */ ++ if (retry++ > 250) ++ { ++ IFX_WARN("IFXUSB HCD EP DISABLE:" ++ " URBD List for this endpoint is not empty\n"); ++ break; ++ } ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++ schedule_timeout_uninterruptible(1); ++ SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); ++ } ++#endif ++ ++ ifxhcd_epqh_free(epqh); ++ _sysep->hcpriv = NULL; ++ } ++ } ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++} ++ ++ ++/*! ++ \brief Handles host mode interrupts for the IFXUSB controller. Returns IRQ_NONE if ++ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid ++ * interrupt. ++ * ++ * This function is called by the USB core when an interrupt occurs ++ */ ++irqreturn_t ifxhcd_irq(struct usb_hcd *_syshcd) ++{ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd); ++ int32_t retval=0; ++ ++ //mask_and_ack_ifx_irq (ifxhcd->core_if.irq); ++ retval = ifxhcd_handle_intr(ifxhcd); ++ return IRQ_RETVAL(retval); ++} ++ ++ ++/*! ++ \brief Handles host mode Over Current Interrupt ++ */ ++irqreturn_t ifxhcd_oc_irq(int _irq , void *_dev) ++{ ++ ifxhcd_hcd_t *ifxhcd = _dev; ++ int32_t retval=1; ++ ++ ifxhcd->flags.b.port_over_current_change = 1; ++ ifxusb_vbus_off(&ifxhcd->core_if); ++ IFX_DEBUGP("OC INTERRUPT # %d\n",ifxhcd->core_if.core_no); ++ ++ //mask_and_ack_ifx_irq (_irq); ++ return IRQ_RETVAL(retval); ++} ++ ++/*! ++ \brief Creates Status Change bitmap for the root hub and root port. The bitmap is ++ returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 ++ is the status change indicator for the single root port. Returns 1 if either ++ change indicator is 1, otherwise returns 0. ++ */ ++int ifxhcd_hub_status_data(struct usb_hcd *_syshcd, char *_buf) ++{ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd); ++ ++ _buf[0] = 0; ++ _buf[0] |= (ifxhcd->flags.b.port_connect_status_change || ++ ifxhcd->flags.b.port_reset_change || ++ ifxhcd->flags.b.port_enable_change || ++ ifxhcd->flags.b.port_suspend_change || ++ ifxhcd->flags.b.port_over_current_change) << 1; ++ ++ #ifdef __DEBUG__ ++ if (_buf[0]) ++ { ++ IFX_DEBUGPL(DBG_HCD, "IFXUSB HCD HUB STATUS DATA:" ++ " Root port status changed\n"); ++ IFX_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", ++ ifxhcd->flags.b.port_connect_status_change); ++ IFX_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", ++ ifxhcd->flags.b.port_reset_change); ++ IFX_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", ++ ifxhcd->flags.b.port_enable_change); ++ IFX_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", ++ ifxhcd->flags.b.port_suspend_change); ++ IFX_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", ++ ifxhcd->flags.b.port_over_current_change); ++ } ++ #endif //__DEBUG__ ++ return (_buf[0] != 0); ++} ++ ++#ifdef __WITH_HS_ELECT_TST__ ++ extern void do_setup(ifxusb_core_if_t *_core_if) ; ++ extern void do_in_ack(ifxusb_core_if_t *_core_if); ++#endif //__WITH_HS_ELECT_TST__ ++ ++/*! ++ \brief Handles hub class-specific requests. ++ */ ++int ifxhcd_hub_control( struct usb_hcd *_syshcd, ++ u16 _typeReq, ++ u16 _wValue, ++ u16 _wIndex, ++ char *_buf, ++ u16 _wLength) ++{ ++ int retval = 0; ++ ++ ifxhcd_hcd_t *ifxhcd = syshcd_to_ifxhcd (_syshcd); ++ ifxusb_core_if_t *core_if = &ifxhcd->core_if; ++ struct usb_hub_descriptor *desc; ++ hprt0_data_t hprt0 = {.d32 = 0}; ++ ++ uint32_t port_status; ++ ++ switch (_typeReq) ++ { ++ case ClearHubFeature: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearHubFeature 0x%x\n", _wValue); ++ switch (_wValue) ++ { ++ case C_HUB_LOCAL_POWER: ++ case C_HUB_OVER_CURRENT: ++ /* Nothing required here */ ++ break; ++ default: ++ retval = -EINVAL; ++ IFX_ERROR ("IFXUSB HCD - " ++ "ClearHubFeature request %xh unknown\n", _wValue); ++ } ++ break; ++ case ClearPortFeature: ++ if (!_wIndex || _wIndex > 1) ++ goto error; ++ ++ switch (_wValue) ++ { ++ case USB_PORT_FEAT_ENABLE: ++ IFX_DEBUGPL (DBG_ANY, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtena = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_SUSPEND: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtres = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ /* Clear Resume bit */ ++ mdelay (100); ++ hprt0.b.prtres = 0; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_POWER: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_POWER\n"); ++ #ifdef __IS_DUAL__ ++ ifxusb_vbus_off(core_if); ++ #else ++ ifxusb_vbus_off(core_if); ++ #endif ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtpwr = 0; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_INDICATOR: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_INDICATOR\n"); ++ /* Port inidicator not supported */ ++ break; ++ case USB_PORT_FEAT_C_CONNECTION: ++ /* Clears drivers internal connect status change ++ * flag */ ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); ++ ifxhcd->flags.b.port_connect_status_change = 0; ++ break; ++ case USB_PORT_FEAT_C_RESET: ++ /* Clears the driver's internal Port Reset Change ++ * flag */ ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); ++ ifxhcd->flags.b.port_reset_change = 0; ++ break; ++ case USB_PORT_FEAT_C_ENABLE: ++ /* Clears the driver's internal Port ++ * Enable/Disable Change flag */ ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); ++ ifxhcd->flags.b.port_enable_change = 0; ++ break; ++ case USB_PORT_FEAT_C_SUSPEND: ++ /* Clears the driver's internal Port Suspend ++ * Change flag, which is set when resume signaling on ++ * the host port is complete */ ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); ++ ifxhcd->flags.b.port_suspend_change = 0; ++ break; ++ case USB_PORT_FEAT_C_OVER_CURRENT: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); ++ ifxhcd->flags.b.port_over_current_change = 0; ++ break; ++ default: ++ retval = -EINVAL; ++ IFX_ERROR ("IFXUSB HCD - " ++ "ClearPortFeature request %xh " ++ "unknown or unsupported\n", _wValue); ++ } ++ break; ++ case GetHubDescriptor: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "GetHubDescriptor\n"); ++ desc = (struct usb_hub_descriptor *)_buf; ++ desc->bDescLength = 9; ++ desc->bDescriptorType = 0x29; ++ desc->bNbrPorts = 1; ++ desc->wHubCharacteristics = 0x08; ++ desc->bPwrOn2PwrGood = 1; ++ desc->bHubContrCurrent = 0; ++// desc->bitmap[0] = 0; ++// desc->bitmap[1] = 0xff; ++ break; ++ case GetHubStatus: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "GetHubStatus\n"); ++ memset (_buf, 0, 4); ++ break; ++ case GetPortStatus: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "GetPortStatus\n"); ++ if (!_wIndex || _wIndex > 1) ++ goto error; ++ ++# ifdef CONFIG_AVM_POWERMETER ++ { ++ /* first port only, but 2 Hosts */ ++ static unsigned char ucOldPower1 = 255; ++ static unsigned char ucOldPower2 = 255; ++ ++ unsigned char ucNewPower = 0; ++ struct usb_device *childdev = _syshcd->self.root_hub->children[0]; ++ ++ if (childdev != NULL) { ++ ucNewPower = (childdev->actconfig != NULL) ++ ? childdev->actconfig->desc.bMaxPower ++ : 50;/* default: 50 means 100 mA*/ ++ } ++ if (_syshcd->self.busnum == 1) { ++ if (ucOldPower1 != ucNewPower) { ++ ucOldPower1 = ucNewPower; ++ printk (KERN_INFO "IFXHCD#1: AVM Powermeter changed to %u mA\n", ucNewPower*2); ++ PowerManagmentRessourceInfo(powerdevice_usb_host, ucNewPower*2); ++ } ++ } else { ++ if (ucOldPower2 != ucNewPower) { ++ ucOldPower2 = ucNewPower; ++ printk (KERN_INFO "IFXHCD#2: AVM Powermeter changed to %u mA\n", ucNewPower*2); ++ PowerManagmentRessourceInfo(powerdevice_usb_host2, ucNewPower*2); ++ } ++ } ++ } ++# endif /*--- #ifdef CONFIG_AVM_POWERMETER ---*/ ++ ++ port_status = 0; ++ if (ifxhcd->flags.b.port_connect_status_change) ++ port_status |= (1 << USB_PORT_FEAT_C_CONNECTION); ++ if (ifxhcd->flags.b.port_enable_change) ++ port_status |= (1 << USB_PORT_FEAT_C_ENABLE); ++ if (ifxhcd->flags.b.port_suspend_change) ++ port_status |= (1 << USB_PORT_FEAT_C_SUSPEND); ++ if (ifxhcd->flags.b.port_reset_change) ++ port_status |= (1 << USB_PORT_FEAT_C_RESET); ++ if (ifxhcd->flags.b.port_over_current_change) ++ { ++ IFX_ERROR("Device Not Supported\n"); ++ port_status |= (1 << USB_PORT_FEAT_C_OVER_CURRENT); ++ } ++ if (!ifxhcd->flags.b.port_connect_status) ++ { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return 0's for the remainder of the port status ++ * since the port register can't be read if the core ++ * is in device mode. ++ */ ++ *((u32 *) _buf) = cpu_to_le32(port_status); ++ break; ++ } ++ ++ hprt0.d32 = ifxusb_rreg(core_if->hprt0); ++ IFX_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32); ++ if (hprt0.b.prtconnsts) ++ port_status |= (1 << USB_PORT_FEAT_CONNECTION); ++ if (hprt0.b.prtena) ++ port_status |= (1 << USB_PORT_FEAT_ENABLE); ++ if (hprt0.b.prtsusp) ++ port_status |= (1 << USB_PORT_FEAT_SUSPEND); ++ if (hprt0.b.prtovrcurract) ++ port_status |= (1 << USB_PORT_FEAT_OVER_CURRENT); ++ if (hprt0.b.prtrst) ++ port_status |= (1 << USB_PORT_FEAT_RESET); ++ if (hprt0.b.prtpwr) ++ port_status |= (1 << USB_PORT_FEAT_POWER); ++/* if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED) ++ port_status |= (1 << USB_PORT_FEAT_HIGHSPEED); ++ else if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_LOW_SPEED) ++ port_status |= (1 << USB_PORT_FEAT_LOWSPEED);*/ ++ if (hprt0.b.prttstctl) ++ port_status |= (1 << USB_PORT_FEAT_TEST); ++ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ ++ *((u32 *) _buf) = cpu_to_le32(port_status); ++ break; ++ case SetHubFeature: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "SetHubFeature\n"); ++ /* No HUB features supported */ ++ break; ++ case SetPortFeature: ++ if (_wValue != USB_PORT_FEAT_TEST && (!_wIndex || _wIndex > 1)) ++ goto error; ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return without doing anything since the port ++ * register can't be written if the core is in device ++ * mode. ++ */ ++ if (!ifxhcd->flags.b.port_connect_status) ++ break; ++ switch (_wValue) ++ { ++ case USB_PORT_FEAT_SUSPEND: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtsusp = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ //IFX_PRINT( "SUSPEND: HPRT0=%0x\n", hprt0.d32); ++ /* Suspend the Phy Clock */ ++ { ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ pcgcctl.b.stoppclk = 1; ++ ifxusb_wreg(core_if->pcgcctl, pcgcctl.d32); ++ } ++ break; ++ case USB_PORT_FEAT_POWER: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_POWER\n"); ++ ifxusb_vbus_on (core_if); ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtpwr = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ break; ++ case USB_PORT_FEAT_RESET: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_RESET\n"); ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtrst = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ ++ MDELAY (60); ++ hprt0.b.prtrst = 0; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ break; ++ #ifdef __WITH_HS_ELECT_TST__ ++ case USB_PORT_FEAT_TEST: ++ { ++ uint32_t t; ++ gint_data_t gintmsk; ++ t = (_wIndex >> 8); /* MSB wIndex USB */ ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_TEST %d\n", t); ++ warn("USB_PORT_FEAT_TEST %d\n", t); ++ if (t < 6) ++ { ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prttstctl = t; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ } ++ else if (t == 6) /* HS_HOST_PORT_SUSPEND_RESUME */ ++ { ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ifxusb_rreg(&core_if->core_global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ ifxusb_wreg(&core_if->core_global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Drive suspend on the root port */ ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtsusp = 1; ++ hprt0.b.prtres = 0; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Drive resume on the root port */ ++ hprt0.d32 = ifxusb_read_hprt0 (core_if); ++ hprt0.b.prtsusp = 0; ++ hprt0.b.prtres = 1; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ mdelay(100); ++ ++ /* Clear the resume bit */ ++ hprt0.b.prtres = 0; ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ ++ /* Restore interrupts */ ++ ifxusb_wreg(&core_if->core_global_regs->gintmsk, gintmsk.d32); ++ } ++ else if (t == 7) /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ ++ { ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ifxusb_rreg(&core_if->core_global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ ifxusb_wreg(&core_if->core_global_regs->gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ mdelay(15000); ++ ++ /* Send the Setup packet */ ++ do_setup(core_if); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Restore interrupts */ ++ ifxusb_wreg(&core_if->core_global_regs->gintmsk, gintmsk.d32); ++ } ++ ++ else if (t == 8) /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ ++ { ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ifxusb_rreg(&core_if->core_global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ ifxusb_wreg(&core_if->core_global_regs->gintmsk, 0); ++ ++ /* Send the Setup packet */ ++ do_setup(core_if); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Send the In and Ack packets */ ++ do_in_ack(core_if); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ mdelay(15000); ++ ++ /* Restore interrupts */ ++ ifxusb_wreg(&core_if->core_global_regs->gintmsk, gintmsk.d32); ++ } ++ } ++ break; ++ #endif //__WITH_HS_ELECT_TST__ ++ case USB_PORT_FEAT_INDICATOR: ++ IFX_DEBUGPL (DBG_HCD, "IFXUSB HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); ++ /* Not supported */ ++ break; ++ default: ++ retval = -EINVAL; ++ IFX_ERROR ("IFXUSB HCD - " ++ "SetPortFeature request %xh " ++ "unknown or unsupported\n", _wValue); ++ } ++ break; ++ default: ++ error: ++ retval = -EINVAL; ++ IFX_WARN ("IFXUSB HCD - " ++ "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", ++ _typeReq, _wIndex, _wValue); ++ } ++ return retval; ++} ++ ++ ++/*! ++ \brief Assigns transactions from a URBD to a free host channel and initializes the ++ host channel to perform the transactions. The host channel is removed from ++ the free list. ++ \param _ifxhcd The HCD state structure. ++ \param _epqh Transactions from the first URBD for this EPQH are selected and assigned to a free host channel. ++ */ ++static int assign_and_init_hc(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) ++{ ++ ifxhcd_hc_t *ifxhc; ++ ifxhcd_urbd_t *urbd; ++ struct urb *urb; ++ ++ IFX_DEBUGPL(DBG_HCDV, "%s(%p,%p)\n", __func__, _ifxhcd, _epqh); ++ ++ if(list_empty(&_epqh->urbd_list)) ++ return 0; ++ ++ ifxhc = list_entry(_ifxhcd->free_hc_list.next, ifxhcd_hc_t, hc_list_entry); ++ /* Remove the host channel from the free list. */ ++ list_del_init(&ifxhc->hc_list_entry); ++ ++ urbd = list_entry(_epqh->urbd_list.next, ifxhcd_urbd_t, urbd_list_entry); ++ urb = urbd->urb; ++ ++ _epqh->hc = ifxhc; ++ _epqh->urbd = urbd; ++ ifxhc->epqh = _epqh; ++ ++ urbd->is_active=1; ++ ++ /* ++ * Use usb_pipedevice to determine device address. This address is ++ * 0 before the SET_ADDRESS command and the correct address afterward. ++ */ ++ ifxhc->dev_addr = usb_pipedevice(urb->pipe); ++ ifxhc->ep_num = usb_pipeendpoint(urb->pipe); ++ ++ ifxhc->xfer_started = 0; ++ ++ if (urb->dev->speed == USB_SPEED_LOW) ifxhc->speed = IFXUSB_EP_SPEED_LOW; ++ else if (urb->dev->speed == USB_SPEED_FULL) ifxhc->speed = IFXUSB_EP_SPEED_FULL; ++ else ifxhc->speed = IFXUSB_EP_SPEED_HIGH; ++ ++ ifxhc->mps = _epqh->mps; ++ ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ++ ifxhc->ep_type = _epqh->ep_type; ++ ++ if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) ++ { ++ ifxhc->control_phase=IFXHCD_CONTROL_SETUP; ++ ifxhc->is_in = 0; ++ ifxhc->data_pid_start = IFXUSB_HC_PID_SETUP; ++ ifxhc->xfer_buff = urbd->setup_buff; ++ ifxhc->xfer_len = 8; ++ ifxhc->xfer_count = 0; ++ ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; ++ } ++ else ++ { ++ ifxhc->is_in = urbd->is_in; ++ ifxhc->xfer_buff = urbd->xfer_buff; ++ ifxhc->xfer_len = urbd->xfer_len; ++ ifxhc->xfer_count = 0; ++ /* == AVM/WK 20100710 Fix - Use toggle of usbcore ==*/ ++ //ifxhc->data_pid_start = _epqh->data_toggle; ++ ifxhc->data_pid_start = usb_gettoggle (urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout (urb->pipe)) ++ ? IFXUSB_HC_PID_DATA1 ++ : IFXUSB_HC_PID_DATA0; ++ if(ifxhc->is_in) ++ ifxhc->short_rw =0; ++ else ++ ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; ++ ++ #ifdef __EN_ISOC__ ++ if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) ++ { ++ struct usb_iso_packet_descriptor *frame_desc; ++ frame_desc = &urb->iso_frame_desc[urbd->isoc_frame_index]; ++ ifxhc->xfer_buff += frame_desc->offset + urbd->isoc_split_offset; ++ ifxhc->xfer_len = frame_desc->length - urbd->isoc_split_offset; ++ if (ifxhc->isoc_xact_pos == IFXUSB_HCSPLIT_XACTPOS_ALL) ++ { ++ if (ifxhc->xfer_len <= 188) ++ ifxhc->isoc_xact_pos = IFXUSB_HCSPLIT_XACTPOS_ALL; ++ else ++ ifxhc->isoc_xact_pos = IFXUSB_HCSPLIT_XACTPOS_BEGIN; ++ } ++ } ++ #endif ++ } ++ ++ ifxhc->do_ping=0; ++ if (_ifxhcd->core_if.snpsid < 0x4f54271a && ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ ifxhc->do_ping=1; ++ ++ ++ /* Set the split attributes */ ++ ifxhc->split = 0; ++ if (_epqh->need_split) { ++ ifxhc->split = 1; ++ ifxhc->hub_addr = urb->dev->tt->hub->devnum; ++ ifxhc->port_addr = urb->dev->ttport; ++ } ++ ++ //ifxhc->uint16_t pkt_count_limit ++ ++ { ++ hcint_data_t hc_intr_mask; ++ uint8_t hc_num = ifxhc->hc_num; ++ ifxusb_hc_regs_t *hc_regs = _ifxhcd->core_if.hc_regs[hc_num]; ++ ++ /* Clear old interrupt conditions for this host channel. */ ++ hc_intr_mask.d32 = 0xFFFFFFFF; ++ hc_intr_mask.b.reserved = 0; ++ ifxusb_wreg(&hc_regs->hcint, hc_intr_mask.d32); ++ ++ /* Enable channel interrupts required for this transfer. */ ++ hc_intr_mask.d32 = 0; ++ hc_intr_mask.b.chhltd = 1; ++ hc_intr_mask.b.ahberr = 1; ++ ++ ifxusb_wreg(&hc_regs->hcintmsk, hc_intr_mask.d32); ++ ++ /* Enable the top level host channel interrupt. */ ++ { ++ uint32_t intr_enable; ++ intr_enable = (1 << hc_num); ++ ifxusb_mreg(&_ifxhcd->core_if.host_global_regs->haintmsk, 0, intr_enable); ++ } ++ ++ /* Make sure host channel interrupts are enabled. */ ++ { ++ gint_data_t gintmsk ={.d32 = 0}; ++ gintmsk.b.hcintr = 1; ++ ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk, 0, gintmsk.d32); ++ } ++ ++ /* ++ * Program the HCCHARn register with the endpoint characteristics for ++ * the current transfer. ++ */ ++ { ++ hcchar_data_t hcchar; ++ ++ hcchar.d32 = 0; ++ hcchar.b.devaddr = ifxhc->dev_addr; ++ hcchar.b.epnum = ifxhc->ep_num; ++ hcchar.b.lspddev = (ifxhc->speed == IFXUSB_EP_SPEED_LOW); ++ hcchar.b.eptype = ifxhc->ep_type; ++ hcchar.b.mps = ifxhc->mps; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ ++ IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, ifxhc->hc_num); ++ IFX_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n" , hcchar.b.devaddr); ++ IFX_DEBUGPL(DBG_HCDV, " Ep Num: %d\n" , hcchar.b.epnum); ++ IFX_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); ++ IFX_DEBUGPL(DBG_HCDV, " Ep Type: %d\n" , hcchar.b.eptype); ++ IFX_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n" , hcchar.b.mps); ++ IFX_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n" , hcchar.b.multicnt); ++ } ++ /* Program the HCSPLIT register for SPLITs */ ++ { ++ hcsplt_data_t hcsplt; ++ ++ hcsplt.d32 = 0; ++ if (ifxhc->split) ++ { ++ IFX_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", ifxhc->hc_num, ++ (ifxhc->split==2) ? "CSPLIT" : "SSPLIT"); ++ hcsplt.b.spltena = 1; ++ hcsplt.b.compsplt = (ifxhc->split==2); ++ #ifdef __EN_ISOC__ ++ if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) ++ hcsplt.b.xactpos = ifxhc->isoc_xact_pos; ++ else ++ #endif ++ hcsplt.b.xactpos = IFXUSB_HCSPLIT_XACTPOS_ALL; ++ hcsplt.b.hubaddr = ifxhc->hub_addr; ++ hcsplt.b.prtaddr = ifxhc->port_addr; ++ IFX_DEBUGPL(DBG_HCDV, " comp split %d\n" , hcsplt.b.compsplt); ++ IFX_DEBUGPL(DBG_HCDV, " xact pos %d\n" , hcsplt.b.xactpos); ++ IFX_DEBUGPL(DBG_HCDV, " hub addr %d\n" , hcsplt.b.hubaddr); ++ IFX_DEBUGPL(DBG_HCDV, " port addr %d\n" , hcsplt.b.prtaddr); ++ IFX_DEBUGPL(DBG_HCDV, " is_in %d\n" , ifxhc->is_in); ++ IFX_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n" , ifxhc->mps); ++ IFX_DEBUGPL(DBG_HCDV, " xferlen: %d\n" , ifxhc->xfer_len); ++ } ++ ifxusb_wreg(&hc_regs->hcsplt, hcsplt.d32); ++ } ++ } ++ ++ ifxhc->nak_retry_r=ifxhc->nak_retry=0; ++ ifxhc->nak_countdown_r=ifxhc->nak_countdown=0; ++ ++ if (ifxhc->split) ++ { ++ if(ifxhc->is_in) ++ { ++ } ++ else ++ { ++ } ++ } ++ else if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) ++ { ++ if(ifxhc->is_in) ++ { ++ } ++ else ++ { ++ } ++ } ++ else if(_epqh->ep_type==IFXUSB_EP_TYPE_BULK) ++ { ++ if(ifxhc->is_in) ++ { ++// ifxhc->nak_retry_r=ifxhc->nak_retry=nak_retry_max; ++// ifxhc->nak_countdown_r=ifxhc->nak_countdown=nak_countdown_max; ++ } ++ else ++ { ++ } ++ } ++ else if(_epqh->ep_type==IFXUSB_EP_TYPE_INTR) ++ { ++ if(ifxhc->is_in) ++ { ++ } ++ else ++ { ++ } ++ } ++ else if(_epqh->ep_type==IFXUSB_EP_TYPE_ISOC) ++ { ++ if(ifxhc->is_in) ++ { ++ } ++ else ++ { ++ } ++ } ++ ++ return 1; ++} ++ ++/*! ++ \brief This function selects transactions from the HCD transfer schedule and ++ assigns them to available host channels. It is called from HCD interrupt ++ handler functions. ++ */ ++static void select_eps_sub(ifxhcd_hcd_t *_ifxhcd) ++{ ++ struct list_head *epqh_ptr; ++ struct list_head *urbd_ptr; ++ ifxhcd_epqh_t *epqh; ++ ifxhcd_urbd_t *urbd; ++ int ret_val=0; ++ ++ /*== AVM/BC 20101111 Function called with Lock ==*/ ++ ++// #ifdef __DEBUG__ ++// IFX_DEBUGPL(DBG_HCD, " ifxhcd_select_ep\n"); ++// #endif ++ ++ /* Process entries in the periodic ready list. */ ++ #ifdef __EN_ISOC__ ++ epqh_ptr = _ifxhcd->epqh_isoc_ready.next; ++ while (epqh_ptr != &_ifxhcd->epqh_isoc_ready && !list_empty(&_ifxhcd->free_hc_list)) ++ { ++ epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_ptr = epqh_ptr->next; ++ if(epqh->period_do) ++ { ++ if(assign_and_init_hc(_ifxhcd, epqh)) ++ { ++ IFX_DEBUGPL(DBG_HCD, " select_eps ISOC\n"); ++ list_move_tail(&epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_active); ++ epqh->is_active=1; ++ ret_val=1; ++ epqh->period_do=0; ++ } ++ } ++ } ++ #endif ++ ++ epqh_ptr = _ifxhcd->epqh_intr_ready.next; ++ while (epqh_ptr != &_ifxhcd->epqh_intr_ready && !list_empty(&_ifxhcd->free_hc_list)) ++ { ++ epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_ptr = epqh_ptr->next; ++ if(epqh->period_do) ++ { ++ if(assign_and_init_hc(_ifxhcd, epqh)) ++ { ++ IFX_DEBUGPL(DBG_HCD, " select_eps INTR\n"); ++ list_move_tail(&epqh->epqh_list_entry, &_ifxhcd->epqh_intr_active); ++ epqh->is_active=1; ++ ret_val=1; ++ epqh->period_do=0; ++ } ++ } ++ } ++ ++ epqh_ptr = _ifxhcd->epqh_np_ready.next; ++ while (epqh_ptr != &_ifxhcd->epqh_np_ready && !list_empty(&_ifxhcd->free_hc_list)) // may need to preserve at lease one for period ++ { ++ epqh = list_entry(epqh_ptr, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_ptr = epqh_ptr->next; ++ if(assign_and_init_hc(_ifxhcd, epqh)) ++ { ++ IFX_DEBUGPL(DBG_HCD, " select_eps CTRL/BULK\n"); ++ list_move_tail(&epqh->epqh_list_entry, &_ifxhcd->epqh_np_active); ++ epqh->is_active=1; ++ ret_val=1; ++ } ++ } ++ if(ret_val) ++ /*== AVM/BC 20101111 Function called with Lock ==*/ ++ process_channels_sub(_ifxhcd); ++ ++ /* AVM/BC 20101111 Urbds completion loop */ ++ while (!list_empty(&_ifxhcd->urbd_complete_list)) ++ { ++ urbd_ptr = _ifxhcd->urbd_complete_list.next; ++ list_del_init(urbd_ptr); ++ ++ urbd = list_entry(urbd_ptr, ifxhcd_urbd_t, urbd_list_entry); ++ ++ ifxhcd_complete_urb(_ifxhcd, urbd, urbd->status); ++ ++ } ++ ++} ++ ++static void select_eps_func(unsigned long data) ++{ ++ unsigned long flags; ++ ++ ifxhcd_hcd_t *ifxhcd; ++ ifxhcd=((ifxhcd_hcd_t *)data); ++ ++ /* AVM/BC 20101111 select_eps_in_use flag removed */ ++ ++ SPIN_LOCK_IRQSAVE(&ifxhcd->lock, flags); ++ ++ /*if(ifxhcd->select_eps_in_use){ ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++ return; ++ } ++ ifxhcd->select_eps_in_use=1; ++ */ ++ ++ select_eps_sub(ifxhcd); ++ ++ //ifxhcd->select_eps_in_use=0; ++ ++ SPIN_UNLOCK_IRQRESTORE(&ifxhcd->lock, flags); ++} ++ ++void select_eps(ifxhcd_hcd_t *_ifxhcd) ++{ ++ if(in_irq()) ++ { ++ if(!_ifxhcd->select_eps.func) ++ { ++ _ifxhcd->select_eps.next = NULL; ++ _ifxhcd->select_eps.state = 0; ++ atomic_set( &_ifxhcd->select_eps.count, 0); ++ _ifxhcd->select_eps.func = select_eps_func; ++ _ifxhcd->select_eps.data = (unsigned long)_ifxhcd; ++ } ++ tasklet_schedule(&_ifxhcd->select_eps); ++ } ++ else ++ { ++ unsigned long flags; ++ ++ /* AVM/BC 20101111 select_eps_in_use flag removed */ ++ ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++ /*if(_ifxhcd->select_eps_in_use){ ++ printk ("select_eps non_irq: busy\n"); ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ return; ++ } ++ _ifxhcd->select_eps_in_use=1; ++ */ ++ ++ select_eps_sub(_ifxhcd); ++ ++ //_ifxhcd->select_eps_in_use=0; ++ ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ } ++} ++ ++/*! ++ \brief ++ */ ++static void process_unaligned( ifxhcd_epqh_t *_epqh) ++{ ++ #if defined(__UNALIGNED_BUFFER_ADJ__) ++ if(!_epqh->aligned_checked) ++ { ++ uint32_t xfer_len; ++ xfer_len=_epqh->urbd->xfer_len; ++ if(_epqh->urbd->is_in && xfer_len<_epqh->mps) ++ xfer_len = _epqh->mps; ++ _epqh->using_aligned_buf=0; ++ ++ if(xfer_len > 0 && ((unsigned long)_epqh->urbd->xfer_buff) & 3) ++ { ++ if( _epqh->aligned_buf ++ && _epqh->aligned_buf_len > 0 ++ && _epqh->aligned_buf_len < xfer_len ++ ) ++ { ++ ifxusb_free_buf(_epqh->aligned_buf); ++ _epqh->aligned_buf=NULL; ++ _epqh->aligned_buf_len=0; ++ } ++ if(! _epqh->aligned_buf || ! _epqh->aligned_buf_len) ++ { ++ _epqh->aligned_buf = ifxusb_alloc_buf(xfer_len, _epqh->urbd->is_in); ++ if(_epqh->aligned_buf) ++ _epqh->aligned_buf_len = xfer_len; ++ } ++ if(_epqh->aligned_buf) ++ { ++ if(!_epqh->urbd->is_in) ++ memcpy(_epqh->aligned_buf, _epqh->urbd->xfer_buff, xfer_len); ++ _epqh->using_aligned_buf=1; ++ _epqh->hc->xfer_buff = _epqh->aligned_buf; ++ } ++ else ++ IFX_WARN("%s():%d\n",__func__,__LINE__); ++ } ++ if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) ++ { ++ _epqh->using_aligned_setup=0; ++ if(((unsigned long)_epqh->urbd->setup_buff) & 3) ++ { ++ if(! _epqh->aligned_setup) ++ _epqh->aligned_setup = ifxusb_alloc_buf(8,0); ++ if(_epqh->aligned_setup) ++ { ++ memcpy(_epqh->aligned_setup, _epqh->urbd->setup_buff, 8); ++ _epqh->using_aligned_setup=1; ++ } ++ else ++ IFX_WARN("%s():%d\n",__func__,__LINE__); ++ _epqh->hc->xfer_buff = _epqh->aligned_setup; ++ } ++ } ++ } ++ #elif defined(__UNALIGNED_BUFFER_CHK__) ++ if(!_epqh->aligned_checked) ++ { ++ if(_epqh->urbd->is_in) ++ { ++ if(_epqh->urbd->xfer_len==0) ++ IFX_WARN("%s():%d IN xfer while length is zero \n",__func__,__LINE__); ++ else{ ++ if(_epqh->urbd->xfer_len < _epqh->mps) ++ IFX_WARN("%s():%d IN xfer while length < mps \n",__func__,__LINE__); ++ ++ if(((unsigned long)_epqh->urbd->xfer_buff) & 3) ++ IFX_WARN("%s():%d IN xfer Buffer UNALIGNED\n",__func__,__LINE__); ++ } ++ } ++ else ++ { ++ if(_epqh->urbd->xfer_len > 0 && (((unsigned long)_epqh->urbd->xfer_buff) & 3) ) ++ IFX_WARN("%s():%d OUT xfer Buffer UNALIGNED\n",__func__,__LINE__); ++ } ++ ++ if(_epqh->ep_type==IFXUSB_EP_TYPE_CTRL) ++ { ++ if(((unsigned long)_epqh->urbd->setup_buff) & 3) ++ IFX_WARN("%s():%d SETUP xfer Buffer UNALIGNED\n",__func__,__LINE__); ++ } ++ } ++ #endif ++ _epqh->aligned_checked=1; ++} ++ ++ ++/*! ++ \brief ++ */ ++void process_channels_sub(ifxhcd_hcd_t *_ifxhcd) ++{ ++ ifxhcd_epqh_t *epqh; ++ struct list_head *epqh_item; ++ struct ifxhcd_hc *hc; ++ ++ #ifdef __EN_ISOC__ ++ if (!list_empty(&_ifxhcd->epqh_isoc_active)) ++ { ++ for (epqh_item = _ifxhcd->epqh_isoc_active.next; ++ epqh_item != &_ifxhcd->epqh_isoc_active; ++ ) ++ { ++ epqh = list_entry(epqh_item, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_item = epqh_item->next; ++ hc=epqh->hc; ++ if(hc && !hc->xfer_started && epqh->period_do) ++ { ++ if(hc->split==0 ++ || hc->split==1 ++ ) ++ { ++ //epqh->ping_state = 0; ++ process_unaligned(epqh); ++ hc->wait_for_sof=epqh->wait_for_sof; ++ epqh->wait_for_sof=0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, hc); ++ epqh->period_do=0; ++ { ++ gint_data_t gintsts = {.d32 = 0}; ++ gintsts.b.sofintr = 1; ++ ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk,0, gintsts.d32); ++ } ++ } ++ } ++ } ++ } ++ #endif ++ ++ if (!list_empty(&_ifxhcd->epqh_intr_active)) ++ { ++ for (epqh_item = _ifxhcd->epqh_intr_active.next; ++ epqh_item != &_ifxhcd->epqh_intr_active; ++ ) ++ { ++ epqh = list_entry(epqh_item, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_item = epqh_item->next; ++ hc=epqh->hc; ++ if(hc && !hc->xfer_started && epqh->period_do) ++ { ++ if(hc->split==0 ++ || hc->split==1 ++ ) ++ { ++ //epqh->ping_state = 0; ++ process_unaligned(epqh); ++ hc->wait_for_sof=epqh->wait_for_sof; ++ epqh->wait_for_sof=0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, hc); ++ epqh->period_do=0; ++#ifdef __USE_TIMER_4_SOF__ ++ /* AVM/WK change: let hc_start decide, if irq is needed */ ++#else ++ { ++ gint_data_t gintsts = {.d32 = 0}; ++ gintsts.b.sofintr = 1; ++ ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk,0, gintsts.d32); ++ } ++#endif ++ } ++ } ++ ++ } ++ } ++ ++ if (!list_empty(&_ifxhcd->epqh_np_active)) ++ { ++ for (epqh_item = _ifxhcd->epqh_np_active.next; ++ epqh_item != &_ifxhcd->epqh_np_active; ++ ) ++ { ++ epqh = list_entry(epqh_item, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_item = epqh_item->next; ++ hc=epqh->hc; ++ if(hc) ++ { ++ if(!hc->xfer_started) ++ { ++ if(hc->split==0 ++ || hc->split==1 ++ //|| hc->split_counter == 0 ++ ) ++ { ++ //epqh->ping_state = 0; ++ process_unaligned(epqh); ++ hc->wait_for_sof=epqh->wait_for_sof; ++ epqh->wait_for_sof=0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, hc); ++ } ++ } ++ } ++ } ++ } ++} ++ ++void process_channels(ifxhcd_hcd_t *_ifxhcd) ++{ ++ unsigned long flags; ++ ++ /* AVM/WK Fix: use spin_lock instead busy flag ++ **/ ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++ //if(_ifxhcd->process_channels_in_use) ++ // return; ++ //_ifxhcd->process_channels_in_use=1; ++ ++ process_channels_sub(_ifxhcd); ++ //_ifxhcd->process_channels_in_use=0; ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++} ++ ++ ++#ifdef __HC_XFER_TIMEOUT__ ++ static void hc_xfer_timeout(unsigned long _ptr) ++ { ++ hc_xfer_info_t *xfer_info = (hc_xfer_info_t *)_ptr; ++ int hc_num = xfer_info->hc->hc_num; ++ IFX_WARN("%s: timeout on channel %d\n", __func__, hc_num); ++ IFX_WARN(" start_hcchar_val 0x%08x\n", xfer_info->hc->start_hcchar_val); ++ } ++#endif ++ ++void ifxhcd_hc_dumb_rx(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc,uint8_t *dump_buf) ++{ ++ ifxusb_hc_regs_t *hc_regs = _core_if->hc_regs[_ifxhc->hc_num]; ++ hctsiz_data_t hctsiz= { .d32=0 }; ++ hcchar_data_t hcchar; ++ ++ ++ _ifxhc->xfer_len = _ifxhc->mps; ++ hctsiz.b.xfersize = _ifxhc->mps; ++ hctsiz.b.pktcnt = 0; ++ hctsiz.b.pid = _ifxhc->data_pid_start; ++ ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32); ++ ++ ifxusb_wreg(&hc_regs->hcdma, (uint32_t)(CPHYSADDR( ((uint32_t)(dump_buf))))); ++ ++ { ++ hcint_data_t hcint= { .d32=0 }; ++// hcint.b.nak =1; ++// hcint.b.nyet=1; ++// hcint.b.ack =1; ++ hcint.d32 =0xFFFFFFFF; ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ } ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ hcchar.b.epdir = 1; ++ IFX_DEBUGPL(DBG_HCDV, " HCCHART: 0x%08x\n", hcchar.d32); ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++} ++ ++/*! ++ \brief This function trigger a data transfer for a host channel and ++ starts the transfer. ++ ++ For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ ++ register along with a packet count of 1 and the channel is enabled. This ++ causes a single PING transaction to occur. Other fields in HCTSIZ are ++ simply set to 0 since no data transfer occurs in this case. ++ ++ For a PING transfer in DMA mode, the HCTSIZ register is initialized with ++ all the information required to perform the subsequent data transfer. In ++ addition, the Do Ping bit is set in the HCTSIZ register. In this case, the ++ controller performs the entire PING protocol, then starts the data ++ transfer. ++ \param _core_if Pointer of core_if structure ++ \param _ifxhc Information needed to initialize the host channel. The xfer_len ++ value may be reduced to accommodate the max widths of the XferSize and ++ PktCnt fields in the HCTSIZn register. The multi_count value may be changed ++ to reflect the final xfer_len value. ++ */ ++void ifxhcd_hc_start(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc) ++{ ++ hctsiz_data_t hctsiz= { .d32=0 }; ++ hcchar_data_t hcchar; ++ uint32_t max_hc_xfer_size = _core_if->params.max_transfer_size; ++ uint16_t max_hc_pkt_count = _core_if->params.max_packet_count; ++ ifxusb_hc_regs_t *hc_regs = _core_if->hc_regs[_ifxhc->hc_num]; ++ hfnum_data_t hfnum; ++ ++ hctsiz.b.dopng = 0; ++// if(_ifxhc->do_ping && !_ifxhc->is_in) hctsiz.b.dopng = 1; ++ ++ _ifxhc->nak_countdown=_ifxhc->nak_countdown_r; ++ ++ /* AVM/BC 20101111 Workaround: Always PING if HI-Speed Out and xfer_len > 0 */ ++ if(/*_ifxhc->do_ping &&*/ ++ (!_ifxhc->is_in) && ++ (_ifxhc->speed == IFXUSB_EP_SPEED_HIGH) && ++ ((_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK) || ((_ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL) && (_ifxhc->control_phase != IFXHCD_CONTROL_SETUP))) && ++ _ifxhc->xfer_len ++ ) ++ hctsiz.b.dopng = 1; ++ ++ _ifxhc->xfer_started = 1; ++ ++ if(_ifxhc->epqh->pkt_count_limit > 0 && _ifxhc->epqh->pkt_count_limit < max_hc_pkt_count ) ++ { ++ max_hc_pkt_count=_ifxhc->epqh->pkt_count_limit; ++ if(max_hc_pkt_count * _ifxhc->mps < max_hc_xfer_size) ++ max_hc_xfer_size = max_hc_pkt_count * _ifxhc->mps; ++ } ++ if (_ifxhc->split > 0) ++ { ++ { ++ gint_data_t gintsts = {.d32 = 0}; ++ gintsts.b.sofintr = 1; ++ ifxusb_mreg(&_core_if->core_global_regs->gintmsk,0, gintsts.d32); ++ } ++ ++ _ifxhc->start_pkt_count = 1; ++ if(!_ifxhc->is_in && _ifxhc->split>1) // OUT CSPLIT ++ _ifxhc->xfer_len = 0; ++ if (_ifxhc->xfer_len > _ifxhc->mps) ++ _ifxhc->xfer_len = _ifxhc->mps; ++ if (_ifxhc->xfer_len > 188) ++ _ifxhc->xfer_len = 188; ++ } ++ else if(_ifxhc->is_in) ++ { ++ _ifxhc->short_rw = 0; ++ if (_ifxhc->xfer_len > 0) ++ { ++ if (_ifxhc->xfer_len > max_hc_xfer_size) ++ _ifxhc->xfer_len = max_hc_xfer_size - _ifxhc->mps + 1; ++ _ifxhc->start_pkt_count = (_ifxhc->xfer_len + _ifxhc->mps - 1) / _ifxhc->mps; ++ if (_ifxhc->start_pkt_count > max_hc_pkt_count) ++ _ifxhc->start_pkt_count = max_hc_pkt_count; ++ } ++ else /* Need 1 packet for transfer length of 0. */ ++ _ifxhc->start_pkt_count = 1; ++ _ifxhc->xfer_len = _ifxhc->start_pkt_count * _ifxhc->mps; ++ } ++ else //non-split out ++ { ++ if (_ifxhc->xfer_len == 0) ++ { ++ /*== AVM/BC WK 20110421 ZERO PACKET Workaround: Is not an error ==*/ ++ //if(_ifxhc->short_rw==0) ++ // printk(KERN_INFO "%s() line %d: ZLP write without short_rw set!\n",__func__,__LINE__); ++ _ifxhc->start_pkt_count = 1; ++ } ++ else ++ { ++ if (_ifxhc->xfer_len > max_hc_xfer_size) ++ { ++ _ifxhc->start_pkt_count = (max_hc_xfer_size / _ifxhc->mps); ++ _ifxhc->xfer_len = _ifxhc->start_pkt_count * _ifxhc->mps; ++ } ++ else ++ { ++ _ifxhc->start_pkt_count = (_ifxhc->xfer_len+_ifxhc->mps-1) / _ifxhc->mps; ++// if(_ifxhc->start_pkt_count * _ifxhc->mps == _ifxhc->xfer_len ) ++// _ifxhc->start_pkt_count += _ifxhc->short_rw; ++ /*== AVM/BC WK 20110421 ZERO PACKET Workaround / check if short_rw is needed ==*/ ++ if(_ifxhc->start_pkt_count * _ifxhc->mps != _ifxhc->xfer_len ) ++ _ifxhc->short_rw = 0; ++ } ++ } ++ } ++ ++ #ifdef __EN_ISOC__ ++ if (_ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC) ++ { ++ /* Set up the initial PID for the transfer. */ ++ #if 1 ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ #else ++ if (_ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ { ++ if (_ifxhc->is_in) ++ { ++ if (_ifxhc->multi_count == 1) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ else if (_ifxhc->multi_count == 2) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA2; ++ } ++ else ++ { ++ if (_ifxhc->multi_count == 1) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_MDATA; ++ } ++ } ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ #endif ++ } ++ #endif ++ ++ hctsiz.b.xfersize = _ifxhc->xfer_len; ++ hctsiz.b.pktcnt = _ifxhc->start_pkt_count; ++ hctsiz.b.pid = _ifxhc->data_pid_start; ++ ++ ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32); ++ ++ ++ IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, _ifxhc->hc_num); ++ IFX_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize); ++ IFX_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n" , hctsiz.b.pktcnt); ++ IFX_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ IFX_DEBUGPL(DBG_HCDV, " DMA: 0x%08x\n", (uint32_t)(CPHYSADDR( ((uint32_t)(_ifxhc->xfer_buff))+ _ifxhc->xfer_count ))); ++ ifxusb_wreg(&hc_regs->hcdma, (uint32_t)(CPHYSADDR( ((uint32_t)(_ifxhc->xfer_buff))+ _ifxhc->xfer_count ))); ++ ++ /* Start the split */ ++ if (_ifxhc->split>0) ++ { ++ hcsplt_data_t hcsplt; ++ hcsplt.d32 = ifxusb_rreg (&hc_regs->hcsplt); ++ hcsplt.b.spltena = 1; ++ if (_ifxhc->split>1) ++ hcsplt.b.compsplt = 1; ++ else ++ hcsplt.b.compsplt = 0; ++ ++ #ifdef __EN_ISOC__ ++ if (_ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC) ++ hcsplt.b.xactpos = _ifxhc->isoc_xact_pos; ++ else ++ #endif ++ hcsplt.b.xactpos = IFXUSB_HCSPLIT_XACTPOS_ALL;// if not ISO ++ ifxusb_wreg(&hc_regs->hcsplt, hcsplt.d32); ++ IFX_DEBUGPL(DBG_HCDV, " SPLIT: XACT_POS:0x%08x\n", hcsplt.d32); ++ } ++ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++// hcchar.b.multicnt = _ifxhc->multi_count; ++ hcchar.b.multicnt = 1; ++ ++ #ifdef __DEBUG__ ++ _ifxhc->start_hcchar_val = hcchar.d32; ++ if (hcchar.b.chdis) ++ IFX_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, _ifxhc->hc_num, hcchar.d32); ++ #endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ hcchar.b.epdir = _ifxhc->is_in; ++ _ifxhc->hcchar=hcchar.d32; ++ ++ IFX_DEBUGPL(DBG_HCDV, " HCCHART: 0x%08x\n", _ifxhc->hcchar); ++ ++ /* == 20110901 AVM/WK Fix: Clear IRQ flags in any case ==*/ ++ { ++ hcint_data_t hcint= { .d32=0 }; ++ hcint.d32 =0xFFFFFFFF; ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ } ++ ++ if(_ifxhc->wait_for_sof==0) ++ { ++ hcint_data_t hcint; ++ ++ hcint.d32=ifxusb_rreg(&hc_regs->hcintmsk); ++ ++ hcint.b.nak =0; ++ hcint.b.ack =0; ++ /* == 20110901 AVM/WK Fix: We don't need NOT YET IRQ ==*/ ++ hcint.b.nyet=0; ++ if(_ifxhc->nak_countdown_r) ++ hcint.b.nak =1; ++ ifxusb_wreg(&hc_regs->hcintmsk, hcint.d32); ++ ++ /* AVM WK / BC 20100827 ++ * MOVED. Oddframe updated inmediatly before write HCChar Register. ++ */ ++ if (_ifxhc->ep_type == IFXUSB_EP_TYPE_INTR || _ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC) ++ { ++ hfnum.d32 = ifxusb_rreg(&_core_if->host_global_regs->hfnum); ++ /* 1 if _next_ frame is odd, 0 if it's even */ ++ hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++ _ifxhc->hcchar=hcchar.d32; ++ } ++ ++ ifxusb_wreg(&hc_regs->hcchar, _ifxhc->hcchar); ++#ifdef __USE_TIMER_4_SOF__ ++ } else { ++ //activate SOF IRQ ++ gint_data_t gintsts = {.d32 = 0}; ++ gintsts.b.sofintr = 1; ++ ifxusb_mreg(&_core_if->core_global_regs->gintmsk,0, gintsts.d32); ++#endif ++ } ++ ++ #ifdef __HC_XFER_TIMEOUT__ ++ /* Start a timer for this transfer. */ ++ init_timer(&_ifxhc->hc_xfer_timer); ++ _ifxhc->hc_xfer_timer.function = hc_xfer_timeout; ++ _ifxhc->hc_xfer_timer.core_if = _core_if; ++ _ifxhc->hc_xfer_timer.hc = _ifxhc; ++ _ifxhc->hc_xfer_timer.data = (unsigned long)(&_ifxhc->hc_xfer_info); ++ _ifxhc->hc_xfer_timer.expires = jiffies + (HZ*10); ++ add_timer(&_ifxhc->hc_xfer_timer); ++ #endif ++} ++ ++/*! ++ \brief Attempts to halt a host channel. This function should only be called ++ to abort a transfer in DMA mode. Under normal circumstances in DMA mode, the ++ controller halts the channel when the transfer is complete or a condition ++ occurs that requires application intervention. ++ ++ In DMA mode, always sets the Channel Enable and Channel Disable bits of the ++ HCCHARn register. The controller ensures there is space in the request ++ queue before submitting the halt request. ++ ++ Some time may elapse before the core flushes any posted requests for this ++ host channel and halts. The Channel Halted interrupt handler completes the ++ deactivation of the host channel. ++ */ ++void ifxhcd_hc_halt(ifxusb_core_if_t *_core_if, ++ ifxhcd_hc_t *_ifxhc, ++ ifxhcd_halt_status_e _halt_status) ++{ ++ hcchar_data_t hcchar; ++ ifxusb_hc_regs_t *hc_regs; ++ ++ hc_regs = _core_if->hc_regs[_ifxhc->hc_num]; ++ ++ WARN_ON(_halt_status == HC_XFER_NO_HALT_STATUS); ++ ++ if (_halt_status == HC_XFER_URB_DEQUEUE || ++ _halt_status == HC_XFER_AHB_ERR) ++ { ++ /* ++ * Disable all channel interrupts except Ch Halted. The URBD ++ * and EPQH state associated with this transfer has been cleared ++ * (in the case of URB_DEQUEUE), so the channel needs to be ++ * shut down carefully to prevent crashes. ++ */ ++ hcint_data_t hcintmsk; ++ hcintmsk.d32 = 0; ++ hcintmsk.b.chhltd = 1; ++ ifxusb_wreg(&hc_regs->hcintmsk, hcintmsk.d32); ++ ++ /* ++ * Make sure no other interrupts besides halt are currently ++ * pending. Handling another interrupt could cause a crash due ++ * to the URBD and EPQH state. ++ */ ++ ifxusb_wreg(&hc_regs->hcint, ~hcintmsk.d32); ++ ++ /* ++ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR ++ * even if the channel was already halted for some other ++ * reason. ++ */ ++ _ifxhc->halt_status = _halt_status; ++ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ if (hcchar.b.chen == 0) ++ { ++ /* ++ * The channel is either already halted or it hasn't ++ * started yet. In DMA mode, the transfer may halt if ++ * it finishes normally or a condition occurs that ++ * requires driver intervention. Don't want to halt ++ * the channel again. In either Slave or DMA mode, ++ * it's possible that the transfer has been assigned ++ * to a channel, but not started yet when an URB is ++ * dequeued. Don't want to halt a channel that hasn't ++ * started yet. ++ */ ++ return; ++ } ++ } ++ ++ if (_ifxhc->halting) ++ { ++ /* ++ * A halt has already been issued for this channel. This might ++ * happen when a transfer is aborted by a higher level in ++ * the stack. ++ */ ++ #ifdef __DEBUG__ ++ IFX_PRINT("*** %s: Channel %d, _hc->halting already set ***\n", ++ __func__, _ifxhc->hc_num); ++ #endif ++ //ifxusb_dump_global_registers(_core_if); */ ++ //ifxusb_dump_host_registers(_core_if); */ ++ return; ++ } ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ /* == AVM/WK 20100709 halt channel only if enabled ==*/ ++ if (hcchar.b.chen) { ++ _ifxhc->halting = 1; ++ hcchar.b.chdis = 1; ++ ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ _ifxhc->halt_status = _halt_status; ++ } ++ ++ IFX_DEBUGPL(DBG_HCDV, "%s: Channel %d\n" , __func__, _ifxhc->hc_num); ++ IFX_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n" , hcchar.d32); ++ IFX_DEBUGPL(DBG_HCDV, " halting: %d\n" , _ifxhc->halting); ++ IFX_DEBUGPL(DBG_HCDV, " halt_status: %d\n" , _ifxhc->halt_status); ++ ++ return; ++} ++ ++/*! ++ \brief Clears a host channel. ++ */ ++void ifxhcd_hc_cleanup(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc) ++{ ++ ifxusb_hc_regs_t *hc_regs; ++ ++ _ifxhc->xfer_started = 0; ++ /* ++ * Clear channel interrupt enables and any unhandled channel interrupt ++ * conditions. ++ */ ++ hc_regs = _core_if->hc_regs[_ifxhc->hc_num]; ++ ifxusb_wreg(&hc_regs->hcintmsk, 0); ++ ifxusb_wreg(&hc_regs->hcint, 0xFFFFFFFF); ++ ++ #ifdef __HC_XFER_TIMEOUT__ ++ del_timer(&_ifxhc->hc_xfer_timer); ++ #endif ++ #ifdef __DEBUG__ ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ if (hcchar.b.chdis) ++ IFX_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", __func__, _ifxhc->hc_num, hcchar.d32); ++ } ++ #endif ++} ++ ++ ++ ++ ++ ++ ++ ++ ++#ifdef __DEBUG__ ++ static void dump_urb_info(struct urb *_urb, char* _fn_name) ++ { ++ IFX_PRINT("%s, urb %p\n" , _fn_name, _urb); ++ IFX_PRINT(" Device address: %d\n", usb_pipedevice(_urb->pipe)); ++ IFX_PRINT(" Endpoint: %d, %s\n" , usb_pipeendpoint(_urb->pipe), ++ (usb_pipein(_urb->pipe) ? "IN" : "OUT")); ++ IFX_PRINT(" Endpoint type: %s\n", ++ ({ char *pipetype; ++ switch (usb_pipetype(_urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CONTROL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTERRUPT"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOCHRONOUS"; break; ++ default: pipetype = "UNKNOWN"; break; ++ }; ++ pipetype; ++ })); ++ IFX_PRINT(" Speed: %s\n", ++ ({ char *speed; ++ switch (_urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HIGH"; break; ++ case USB_SPEED_FULL: speed = "FULL"; break; ++ case USB_SPEED_LOW: speed = "LOW"; break; ++ default: speed = "UNKNOWN"; break; ++ }; ++ speed; ++ })); ++ IFX_PRINT(" Max packet size: %d\n", ++ usb_maxpacket(_urb->dev, _urb->pipe, usb_pipeout(_urb->pipe))); ++ IFX_PRINT(" Data buffer length: %d\n", _urb->transfer_buffer_length); ++ IFX_PRINT(" Transfer buffer: %p, Transfer DMA: %p\n", ++ _urb->transfer_buffer, (void *)_urb->transfer_dma); ++ IFX_PRINT(" Setup buffer: %p, Setup DMA: %p\n", ++ _urb->setup_packet, (void *)_urb->setup_dma); ++ IFX_PRINT(" Interval: %d\n", _urb->interval); ++ if (usb_pipetype(_urb->pipe) == PIPE_ISOCHRONOUS) ++ { ++ int i; ++ for (i = 0; i < _urb->number_of_packets; i++) ++ { ++ IFX_PRINT(" ISO Desc %d:\n", i); ++ IFX_PRINT(" offset: %d, length %d\n", ++ _urb->iso_frame_desc[i].offset, ++ _urb->iso_frame_desc[i].length); ++ } ++ } ++ } ++ ++ static void dump_channel_info(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) ++ { ++ if (_epqh->hc != NULL) ++ { ++ ifxhcd_hc_t *hc = _epqh->hc; ++ struct list_head *item; ++ ifxhcd_epqh_t *epqh_item; ++ ++ ifxusb_hc_regs_t *hc_regs; ++ ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ ++ hc_regs = _ifxhcd->core_if.hc_regs[hc->hc_num]; ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ hcsplt.d32 = ifxusb_rreg(&hc_regs->hcsplt); ++ hctsiz.d32 = ifxusb_rreg(&hc_regs->hctsiz); ++ hcdma = ifxusb_rreg(&hc_regs->hcdma); ++ ++ IFX_PRINT(" Assigned to channel %d:\n" , hc->hc_num); ++ IFX_PRINT(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ IFX_PRINT(" hctsiz 0x%08x, hcdma 0x%08x\n" , hctsiz.d32, hcdma); ++ IFX_PRINT(" dev_addr: %d, ep_num: %d, is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->is_in); ++ IFX_PRINT(" ep_type: %d\n" , hc->ep_type); ++ IFX_PRINT(" max_packet_size: %d\n", hc->mps); ++ IFX_PRINT(" data_pid_start: %d\n" , hc->data_pid_start); ++ IFX_PRINT(" xfer_started: %d\n" , hc->xfer_started); ++ IFX_PRINT(" halt_status: %d\n" , hc->halt_status); ++ IFX_PRINT(" xfer_buff: %p\n" , hc->xfer_buff); ++ IFX_PRINT(" xfer_len: %d\n" , hc->xfer_len); ++ IFX_PRINT(" epqh: %p\n" , hc->epqh); ++ IFX_PRINT(" NP Active:\n"); ++ list_for_each(item, &_ifxhcd->epqh_np_active) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ IFX_PRINT(" NP Ready:\n"); ++ list_for_each(item, &_ifxhcd->epqh_np_ready) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ IFX_PRINT(" INTR Active:\n"); ++ list_for_each(item, &_ifxhcd->epqh_intr_active) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ IFX_PRINT(" INTR Ready:\n"); ++ list_for_each(item, &_ifxhcd->epqh_intr_ready) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ #ifdef __EN_ISOC__ ++ IFX_PRINT(" ISOC Active:\n"); ++ list_for_each(item, &_ifxhcd->epqh_isoc_active) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ IFX_PRINT(" ISOC Ready:\n"); ++ list_for_each(item, &_ifxhcd->epqh_isoc_ready) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ #endif ++ IFX_PRINT(" Standby:\n"); ++ list_for_each(item, &_ifxhcd->epqh_stdby) ++ { ++ epqh_item = list_entry(item, ifxhcd_epqh_t, epqh_list_entry); ++ IFX_PRINT(" %p\n", epqh_item); ++ } ++ } ++ } ++#endif //__DEBUG__ ++ ++ ++/*! ++ \brief This function writes a packet into the Tx FIFO associated with the Host ++ Channel. For a channel associated with a non-periodic EP, the non-periodic ++ Tx FIFO is written. For a channel associated with a periodic EP, the ++ periodic Tx FIFO is written. This function should only be called in Slave ++ mode. ++ ++ Upon return the xfer_buff and xfer_count fields in _hc are incremented by ++ then number of bytes written to the Tx FIFO. ++ */ ++ ++#ifdef __ENABLE_DUMP__ ++ void ifxhcd_dump_state(ifxhcd_hcd_t *_ifxhcd) ++ { ++ int num_channels; ++ int i; ++ num_channels = _ifxhcd->core_if.params.host_channels; ++ IFX_PRINT("\n"); ++ IFX_PRINT("************************************************************\n"); ++ IFX_PRINT("HCD State:\n"); ++ IFX_PRINT(" Num channels: %d\n", num_channels); ++ for (i = 0; i < num_channels; i++) { ++ ifxhcd_hc_t *hc = &_ifxhcd->ifxhc[i]; ++ IFX_PRINT(" Channel %d:\n", hc->hc_num); ++ IFX_PRINT(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->is_in); ++ IFX_PRINT(" speed: %d\n" , hc->speed); ++ IFX_PRINT(" ep_type: %d\n" , hc->ep_type); ++ IFX_PRINT(" mps: %d\n", hc->mps); ++ IFX_PRINT(" data_pid_start: %d\n" , hc->data_pid_start); ++ IFX_PRINT(" xfer_started: %d\n" , hc->xfer_started); ++ IFX_PRINT(" xfer_buff: %p\n" , hc->xfer_buff); ++ IFX_PRINT(" xfer_len: %d\n" , hc->xfer_len); ++ IFX_PRINT(" xfer_count: %d\n" , hc->xfer_count); ++ IFX_PRINT(" halting: %d\n" , hc->halting); ++ IFX_PRINT(" halt_status: %d\n" , hc->halt_status); ++ IFX_PRINT(" split: %d\n" , hc->split); ++ IFX_PRINT(" hub_addr: %d\n" , hc->hub_addr); ++ IFX_PRINT(" port_addr: %d\n" , hc->port_addr); ++ #ifdef __EN_ISOC__ ++ IFX_PRINT(" isoc_xact_pos: %d\n" , hc->isoc_xact_pos); ++ #endif ++ IFX_PRINT(" epqh: %p\n" , hc->epqh); ++ IFX_PRINT(" short_rw: %d\n" , hc->short_rw); ++ IFX_PRINT(" do_ping: %d\n" , hc->do_ping); ++ IFX_PRINT(" control_phase: %d\n" , hc->control_phase); ++ IFX_PRINT(" pkt_count_limit: %d\n", hc->epqh->pkt_count_limit); ++ IFX_PRINT(" start_pkt_count: %d\n" , hc->start_pkt_count); ++ } ++ IFX_PRINT("************************************************************\n"); ++ IFX_PRINT("\n"); ++ } ++#endif //__ENABLE_DUMP__ ++ +diff --git a/drivers/usb/ifxhcd/ifxhcd.h b/drivers/usb/ifxhcd/ifxhcd.h +new file mode 100644 +index 0000000..3a40851 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxhcd.h +@@ -0,0 +1,628 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxhcd.h ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : This file contains the structures, constants, and interfaces for ++ ** the Host Contoller Driver (HCD). ++ ** ++ ** The Host Controller Driver (HCD) is responsible for translating requests ++ ** from the USB Driver into the appropriate actions on the IFXUSB controller. ++ ** It isolates the USBD from the specifics of the controller by providing an ++ ** API to the USBD. ++ ** FUNCTIONS : ++ ** COMPILER : gcc ++ ** REFERENCE : Synopsys DWC-OTG Driver 2.7 ++ ** COPYRIGHT : ++ ** Version Control Section ** ++ ** $Author$ ++ ** $Date$ ++ ** $Revisions$ ++ ** $Log$ Revision history ++*****************************************************************************/ ++ ++/*! ++ \defgroup IFXUSB_HCD HCD Interface ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief The Host Controller Driver (HCD) is responsible for translating requests ++ from the USB Driver into the appropriate actions on the IFXUSB controller. ++ It isolates the USBD from the specifics of the controller by providing an ++ API to the USBD. ++ */ ++ ++ ++/*! ++ \file ifxhcd.h ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the structures, constants, and interfaces for ++ the Host Contoller Driver (HCD). ++ */ ++ ++#if !defined(__IFXHCD_H__) ++#define __IFXHCD_H__ ++ ++#include ++#include ++ ++#ifdef __USE_TIMER_4_SOF__ ++#include ++#endif ++#include ++ ++#include "ifxusb_cif.h" ++#include "ifxusb_plat.h" ++ ++ ++ ++/*! ++ \addtogroup IFXUSB_HCD ++ */ ++/*@{*/ ++ ++/* Phases for control transfers.*/ ++typedef enum ifxhcd_control_phase { ++ IFXHCD_CONTROL_SETUP, ++ IFXHCD_CONTROL_DATA, ++ IFXHCD_CONTROL_STATUS ++} ifxhcd_control_phase_e; ++ ++/* Reasons for halting a host channel. */ ++typedef enum ifxhcd_halt_status ++{ ++ HC_XFER_NO_HALT_STATUS, // Initial ++ HC_XFER_COMPLETE, // Xact complete without error, upward ++ HC_XFER_URB_COMPLETE, // Xfer complete without error, short upward ++ HC_XFER_STALL, // HC stopped abnormally, upward/downward ++ HC_XFER_XACT_ERR, // HC stopped abnormally, upward ++ HC_XFER_FRAME_OVERRUN, // HC stopped abnormally, upward ++ HC_XFER_BABBLE_ERR, // HC stopped abnormally, upward ++ HC_XFER_AHB_ERR, // HC stopped abnormally, upward ++ HC_XFER_DATA_TOGGLE_ERR, ++ HC_XFER_URB_DEQUEUE, // HC stopper manually, downward ++ HC_XFER_NAK // HC stopped by nak monitor, downward ++} ifxhcd_halt_status_e; ++ ++struct ifxhcd_urbd; ++struct ifxhcd_hc ; ++struct ifxhcd_epqh ; ++struct ifxhcd_hcd; ++ ++/*! ++ \brief A URB Descriptor (URBD) holds the state of a bulk, control, ++ interrupt, or isochronous transfer. A single URBD is created for each URB ++ (of one of these types) submitted to the HCD. The transfer associated with ++ a URBD may require one or multiple transactions. ++ ++ A URBD is linked to a EP Queue Head, which is entered in either the ++ isoc, intr or non-periodic schedule for execution. When a URBD is chosen for ++ execution, some or all of its transactions may be executed. After ++ execution, the state of the URBD is updated. The URBD may be retired if all ++ its transactions are complete or if an error occurred. Otherwise, it ++ remains in the schedule so more transactions can be executed later. ++ */ ++typedef struct ifxhcd_urbd { ++ struct list_head urbd_list_entry; // Hook for EPQH->urbd_list and ifxhcd->urbd_complete_list ++ struct urb *urb; /*!< URB for this transfer */ ++ //struct urb { ++ // struct list_head urb_list; ++ // struct list_head anchor_list; ++ // struct usb_anchor * anchor; ++ // struct usb_device * dev; ++ // struct usb_host_endpoint * ep; ++ // unsigned int pipe; ++ // int status; ++ // unsigned int transfer_flags; ++ // void * transfer_buffer; ++ // dma_addr_t transfer_dma; ++ // u32 transfer_buffer_length; ++ // u32 actual_length; ++ // unsigned char * setup_packet; ++ // dma_addr_t setup_dma; ++ // int start_frame; ++ // int number_of_packets; ++ // int interval; ++ // int error_count; ++ // void * context; ++ // usb_complete_t complete; ++ // struct usb_iso_packet_descriptor iso_frame_desc[0]; ++ //}; ++ //urb_list For use by current owner of the URB. ++ //anchor_list membership in the list of an anchor ++ //anchor to anchor URBs to a common mooring ++ //dev Identifies the USB device to perform the request. ++ //ep Points to the endpoint's data structure. Will ++ // eventually replace pipe. ++ //pipe Holds endpoint number, direction, type, and more. ++ // Create these values with the eight macros available; u ++ // sb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is ++ // "ctrl", "bulk", "int" or "iso". For example ++ // usb_sndbulkpipe or usb_rcvintpipe. Endpoint numbers ++ // range from zero to fifteen. Note that "in" endpoint two ++ // is a different endpoint (and pipe) from "out" endpoint ++ // two. The current configuration controls the existence, ++ // type, and maximum packet size of any given endpoint. ++ //status This is read in non-iso completion functions to get ++ // the status of the particular request. ISO requests ++ // only use it to tell whether the URB was unlinked; ++ // detailed status for each frame is in the fields of ++ // the iso_frame-desc. ++ //transfer_flags A variety of flags may be used to affect how URB ++ // submission, unlinking, or operation are handled. ++ // Different kinds of URB can use different flags. ++ // URB_SHORT_NOT_OK ++ // URB_ISO_ASAP ++ // URB_NO_TRANSFER_DMA_MAP ++ // URB_NO_SETUP_DMA_MAP ++ // URB_NO_FSBR ++ // URB_ZERO_PACKET ++ // URB_NO_INTERRUPT ++ //transfer_buffer This identifies the buffer to (or from) which the I/O ++ // request will be performed (unless URB_NO_TRANSFER_DMA_MAP ++ // is set). This buffer must be suitable for DMA; allocate it ++ // with kmalloc or equivalent. For transfers to "in" ++ // endpoints, contents of this buffer will be modified. This ++ // buffer is used for the data stage of control transfers. ++ //transfer_dma When transfer_flags includes URB_NO_TRANSFER_DMA_MAP, the ++ // device driver is saying that it provided this DMA address, ++ // which the host controller driver should use in preference ++ // to the transfer_buffer. ++ //transfer_buffer_length How big is transfer_buffer. The transfer may be broken ++ // up into chunks according to the current maximum packet size ++ // for the endpoint, which is a function of the configuration ++ // and is encoded in the pipe. When the length is zero, neither ++ // transfer_buffer nor transfer_dma is used. ++ //actual_length This is read in non-iso completion functions, and it tells ++ // how many bytes (out of transfer_buffer_length) were transferred. ++ // It will normally be the same as requested, unless either an error ++ // was reported or a short read was performed. The URB_SHORT_NOT_OK ++ // transfer flag may be used to make such short reads be reported ++ // as errors. ++ //setup_packet Only used for control transfers, this points to eight bytes of ++ // setup data. Control transfers always start by sending this data ++ // to the device. Then transfer_buffer is read or written, if needed. ++ //setup_dma For control transfers with URB_NO_SETUP_DMA_MAP set, the device ++ // driver has provided this DMA address for the setup packet. The ++ // host controller driver should use this in preference to setup_packet. ++ //start_frame Returns the initial frame for isochronous transfers. ++ //number_of_packets Lists the number of ISO transfer buffers. ++ //interval Specifies the polling interval for interrupt or isochronous transfers. ++ // The units are frames (milliseconds) for for full and low speed devices, ++ // and microframes (1/8 millisecond) for highspeed ones. ++ //error_count Returns the number of ISO transfers that reported errors. ++ //context For use in completion functions. This normally points to request-specific ++ // driver context. ++ //complete Completion handler. This URB is passed as the parameter to the completion ++ // function. The completion function may then do what it likes with the URB, ++ // including resubmitting or freeing it. ++ //iso_frame_desc[0] Used to provide arrays of ISO transfer buffers and to collect the transfer ++ // status for each buffer. ++ ++ struct ifxhcd_epqh *epqh; ++ // Actual data portion, not SETUP or STATUS in case of CTRL XFER ++ // DMA adjusted ++ uint8_t *setup_buff; /*!< Pointer to the entire transfer buffer. (CPU accessable)*/ ++ uint8_t *xfer_buff; /*!< Pointer to the entire transfer buffer. (CPU accessable)*/ ++ uint32_t xfer_len; /*!< Total number of bytes to transfer in this xfer. */ ++ unsigned is_in :1; ++ unsigned is_active:1; ++ ++ // For ALL XFER ++ uint8_t error_count; /*!< Holds the number of bus errors that have occurred for a transaction ++ within this transfer. ++ */ ++ /*== AVM/BC 20101111 Needed for URB Complete List ==*/ ++ int status; ++ // For ISOC XFER only ++ #ifdef __EN_ISOC__ ++ int isoc_frame_index; /*!< Index of the next frame descriptor for an isochronous transfer. A ++ frame descriptor describes the buffer position and length of the ++ data to be transferred in the next scheduled (micro)frame of an ++ isochronous transfer. It also holds status for that transaction. ++ The frame index starts at 0. ++ */ ++ // For SPLITed ISOC XFER only ++ uint8_t isoc_split_pos; /*!< Position of the ISOC split on full/low speed */ ++ uint16_t isoc_split_offset;/*!< Position of the ISOC split in the buffer for the current frame */ ++ #endif ++} ifxhcd_urbd_t; ++ ++/*! ++ \brief A EP Queue Head (EPQH) holds the static characteristics of an endpoint and ++ maintains a list of transfers (URBDs) for that endpoint. A EPQH structure may ++ be entered in either the isoc, intr or non-periodic schedule. ++ */ ++ ++typedef struct ifxhcd_epqh { ++ struct list_head epqh_list_entry; // Hook for EP Queues ++ struct list_head urbd_list; /*!< List of URBDs for this EPQH. */ ++ struct ifxhcd_hc *hc; /*!< Host channel currently processing transfers for this EPQH. */ ++ struct ifxhcd_urbd *urbd; /*!< URBD currently assigned to a host channel for this EPQH. */ ++ struct usb_host_endpoint *sysep; ++ uint8_t ep_type; /*!< Endpoint type. One of the following values: ++ - IFXUSB_EP_TYPE_CTRL ++ - IFXUSB_EP_TYPE_ISOC ++ - IFXUSB_EP_TYPE_BULK ++ - IFXUSB_EP_TYPE_INTR ++ */ ++ uint16_t mps; /*!< wMaxPacketSize Field of Endpoint Descriptor. */ ++ ++ /* == AVM/WK 20100710 Fix - Use toggle of usbcore ==*/ ++ /*uint8_t data_toggle;*/ /*!< Determines the PID of the next data packet ++ One of the following values: ++ - IFXHCD_HC_PID_DATA0 ++ - IFXHCD_HC_PID_DATA1 ++ */ ++ uint8_t is_active; ++ ++ uint8_t pkt_count_limit; ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ struct timer_list destroy_timer; ++ #endif ++ ++ uint16_t wait_for_sof; ++ uint8_t need_split; /*!< Full/low speed endpoint on high-speed hub requires split. */ ++ uint16_t interval; /*!< Interval between transfers in (micro)frames. (for INTR)*/ ++ ++ uint16_t period_counter; /*!< Interval between transfers in (micro)frames. */ ++ uint8_t period_do; ++ ++ uint8_t aligned_checked; ++ ++ #if defined(__UNALIGNED_BUFFER_ADJ__) ++ uint8_t using_aligned_setup; ++ uint8_t *aligned_setup; ++ uint8_t using_aligned_buf; ++ uint8_t *aligned_buf; ++ unsigned aligned_buf_len : 19; ++ #endif ++ ++ uint8_t *dump_buf; ++} ifxhcd_epqh_t; ++ ++ ++#if defined(__HC_XFER_TIMEOUT__) ++ struct ifxusb_core_if; ++ struct ifxhcd_hc; ++ typedef struct hc_xfer_info ++ { ++ struct ifxusb_core_if *core_if; ++ struct ifxhcd_hc *hc; ++ } hc_xfer_info_t; ++#endif //defined(__HC_XFER_TIMEOUT__) ++ ++ ++/*! ++ \brief Host channel descriptor. This structure represents the state of a single ++ host channel when acting in host mode. It contains the data items needed to ++ transfer packets to an endpoint via a host channel. ++ */ ++typedef struct ifxhcd_hc ++{ ++ struct list_head hc_list_entry ; // Hook to free hc ++ struct ifxhcd_epqh *epqh ; /*!< EP Queue Head for the transfer being processed by this channel. */ ++ ++ uint8_t hc_num ; /*!< Host channel number used for register address lookup */ ++ uint8_t *xfer_buff ; /*!< Pointer to the entire transfer buffer. */ ++ uint32_t xfer_count ; /*!< Number of bytes transferred so far. The offset of the begin of the buf */ ++ uint32_t xfer_len ; /*!< Total number of bytes to transfer in this xfer. */ ++ uint16_t start_pkt_count ; /*!< Packet count at start of transfer. Used to calculate the actual xfer size*/ ++ ifxhcd_halt_status_e halt_status; /*!< Reason for halting the host channel. */ ++ ++ unsigned dev_addr : 7; /*!< Device to access */ ++ unsigned ep_num : 4; /*!< EP to access */ ++ unsigned is_in : 1; /*!< EP direction. 0: OUT, 1: IN */ ++ unsigned speed : 2; /*!< EP speed. */ ++ unsigned ep_type : 2; /*!< Endpoint type. */ ++ unsigned mps :11; /*!< Max packet size in bytes */ ++ unsigned data_pid_start : 2; /*!< PID for initial transaction. */ ++ unsigned do_ping : 1; /*!< Set to 1 to indicate that a PING request should be issued on this ++ channel. If 0, process normally. ++ */ ++ ++ unsigned xfer_started : 1; /*!< Flag to indicate whether the transfer has been started. Set to 1 if ++ it has been started, 0 otherwise. ++ */ ++ unsigned halting : 1; /*!< Set to 1 if the host channel has been halted, but the core is not ++ finished flushing queued requests. Otherwise 0. ++ */ ++ unsigned short_rw : 1; /*!< When Tx, means termination needed. ++ When Rx, indicate Short Read */ ++ /* Split settings for the host channel */ ++ unsigned split : 2; /*!< Split: 0-Non Split, 1-SSPLIT, 2&3 CSPLIT */ ++ ++ /*== AVM/BC 20100701 - Workaround FullSpeed Interrupts with HiSpeed Hub ==*/ ++ unsigned nyet_count; ++ ++ /* nak monitor */ ++ unsigned nak_retry_r : 16; ++ unsigned nak_retry : 16; ++ #define nak_retry_max 40000 ++ unsigned nak_countdown : 8; ++ unsigned nak_countdown_r: 8; ++ #define nak_countdown_max 1 ++ ++ uint16_t wait_for_sof; ++ ifxhcd_control_phase_e control_phase; /*!< Current phase for control transfers (Setup, Data, or Status). */ ++ uint32_t ssplit_out_xfer_count; /*!< How many bytes transferred during SSPLIT OUT */ ++ #ifdef __DEBUG__ ++ uint32_t start_hcchar_val; ++ #endif ++ #ifdef __HC_XFER_TIMEOUT__ ++ hc_xfer_info_t hc_xfer_info; ++ struct timer_list hc_xfer_timer; ++ #endif ++ uint32_t hcchar; ++ ++ /* Split settings for the host channel */ ++ uint8_t hub_addr; /*!< Address of high speed hub */ ++ uint8_t port_addr; /*!< Port of the low/full speed device */ ++ #ifdef __EN_ISOC__ ++ uint8_t isoc_xact_pos; /*!< Split transaction position */ ++ #endif ++} ifxhcd_hc_t; ++ ++ ++/*! ++ \brief This structure holds the state of the HCD, including the non-periodic and ++ periodic schedules. ++ */ ++typedef struct ifxhcd_hcd ++{ ++ struct device *dev; ++ struct hc_driver hc_driver; ++ ifxusb_core_if_t core_if; /*!< Pointer to the core interface structure. */ ++ struct usb_hcd *syshcd; ++ ++ volatile union ifxhcd_internal_flags ++ { ++ uint32_t d32; ++ struct ++ { ++ unsigned port_connect_status_change : 1; ++ unsigned port_connect_status : 1; ++ unsigned port_reset_change : 1; ++ unsigned port_enable_change : 1; ++ unsigned port_suspend_change : 1; ++ unsigned port_over_current_change : 1; ++ unsigned reserved : 27; ++ } b; ++ } flags; /*!< Internal HCD Flags */ ++ ++ struct ifxhcd_hc ifxhc[MAX_EPS_CHANNELS]; /*!< Array of pointers to the host channel descriptors. Allows accessing ++ a host channel descriptor given the host channel number. This is ++ useful in interrupt handlers. ++ */ ++ struct list_head free_hc_list; /*!< Free host channels in the controller. This is a list of ifxhcd_hc_t items. */ ++ uint8_t *status_buf; /*!< Buffer to use for any data received during the status phase of a ++ control transfer. Normally no data is transferred during the status ++ phase. This buffer is used as a bit bucket. ++ */ ++ #define IFXHCD_STATUS_BUF_SIZE 64 ++ ++ struct list_head epqh_np_active; // with URBD, with HC ++ struct list_head epqh_np_ready; // with URBD, No HC ++ ++ struct list_head epqh_intr_active; // with URBD, with HC ++ struct list_head epqh_intr_ready; // with URBD, no pass, No HC ++ ++ #ifdef __EN_ISOC__ ++ struct list_head epqh_isoc_active; // with URBD, with HC ++ struct list_head epqh_isoc_ready; // with URBD, no pass, No HC ++ #endif ++ ++ /*== AVM/BC 20101111 URB Complete List ==*/ ++ struct list_head urbd_complete_list; ++ ++ struct list_head epqh_stdby; ++ ++ /* AVM/BC 20101111 flags removed */ ++ //unsigned process_channels_in_use : 1; ++ //unsigned select_eps_in_use : 1; ++ ++ struct tasklet_struct select_eps; /*!< Tasket to do a reset */ ++ uint32_t lastframe; ++ spinlock_t lock; ++#ifdef __USE_TIMER_4_SOF__ ++ struct hrtimer hr_timer; ++#endif ++} ifxhcd_hcd_t; ++ ++/* Gets the ifxhcd_hcd from a struct usb_hcd */ ++static inline ifxhcd_hcd_t *syshcd_to_ifxhcd(struct usb_hcd *syshcd) ++{ ++ return (ifxhcd_hcd_t *)(syshcd->hcd_priv[0]); ++} ++ ++/* Gets the struct usb_hcd that contains a ifxhcd_hcd_t. */ ++static inline struct usb_hcd *ifxhcd_to_syshcd(ifxhcd_hcd_t *ifxhcd) ++{ ++ return (struct usb_hcd *)(ifxhcd->syshcd); ++} ++ ++/*! \brief HCD Create/Destroy Functions */ ++/*@{*/ ++ extern int ifxhcd_init (ifxhcd_hcd_t *_ifxhcd); ++ extern void ifxhcd_remove(ifxhcd_hcd_t *_ifxhcd); ++/*@}*/ ++ ++/*! \brief Linux HC Driver API Functions */ ++/*@{*/ ++extern int ifxhcd_start(struct usb_hcd *hcd); ++extern void ifxhcd_stop (struct usb_hcd *hcd); ++extern int ifxhcd_get_frame_number(struct usb_hcd *hcd); ++ ++ ++/*! ++ \brief This function does the setup for a data transfer for a host channel and ++ starts the transfer. May be called in either Slave mode or DMA mode. In ++ Slave mode, the caller must ensure that there is sufficient space in the ++ request queue and Tx Data FIFO. ++ ++ For an OUT transfer in Slave mode, it loads a data packet into the ++ appropriate FIFO. If necessary, additional data packets will be loaded in ++ the Host ISR. ++ ++ For an IN transfer in Slave mode, a data packet is requested. The data ++ packets are unloaded from the Rx FIFO in the Host ISR. If necessary, ++ additional data packets are requested in the Host ISR. ++ ++ For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ ++ register along with a packet count of 1 and the channel is enabled. This ++ causes a single PING transaction to occur. Other fields in HCTSIZ are ++ simply set to 0 since no data transfer occurs in this case. ++ ++ For a PING transfer in DMA mode, the HCTSIZ register is initialized with ++ all the information required to perform the subsequent data transfer. In ++ addition, the Do Ping bit is set in the HCTSIZ register. In this case, the ++ controller performs the entire PING protocol, then starts the data ++ transfer. ++ ++ @param _ifxhc Information needed to initialize the host channel. The xfer_len ++ value may be reduced to accommodate the max widths of the XferSize and ++ PktCnt fields in the HCTSIZn register. The multi_count value may be changed ++ to reflect the final xfer_len value. ++ */ ++extern void ifxhcd_hc_start(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc); ++ ++//extern int ifxhcd_urb_enqueue(struct usb_hcd *_syshcd, struct usb_host_endpoint *_sysep, struct urb *_urb, gfp_t mem_flags); ++//extern int ifxhcd_urb_dequeue(struct usb_hcd *_syshcd, struct urb *_urb); ++extern irqreturn_t ifxhcd_irq(struct usb_hcd *_syshcd); ++int ifxhcd_urb_enqueue( struct usb_hcd *_syshcd, ++ /*--- struct usb_host_endpoint *_sysep, Parameter im 2.6.28 entfallen ---*/ ++ struct urb *_urb, ++ gfp_t _mem_flags); ++int ifxhcd_urb_dequeue( struct usb_hcd *_syshcd, ++ struct urb *_urb, int status /* Parameter neu in 2.6.28 */); ++ ++extern void ifxhcd_endpoint_disable(struct usb_hcd *_syshcd, struct usb_host_endpoint *_sysep); ++ ++extern int ifxhcd_hub_status_data(struct usb_hcd *_syshcd, char *_buf); ++extern int ifxhcd_hub_control( struct usb_hcd *_syshcd, ++ u16 _typeReq, ++ u16 _wValue, ++ u16 _wIndex, ++ char *_buf, ++ u16 _wLength); ++ ++/*@}*/ ++ ++/*! \brief Transaction Execution Functions */ ++/*@{*/ ++extern void ifxhcd_complete_urb (ifxhcd_hcd_t *_ifxhcd, ifxhcd_urbd_t *_urbd, int _status); ++ ++/*@}*/ ++ ++/*! \brief Deferred Transaction Execution Functions */ ++/*@{*/ ++ ++/*== AVM/BC 20101111 URB Complete List ==*/ ++extern void defer_ifxhcd_complete_urb (ifxhcd_hcd_t *_ifxhcd, ifxhcd_urbd_t *_urbd, int _status); ++ ++/*! ++ \brief Clears the transfer state for a host channel. This function is normally ++ called after a transfer is done and the host channel is being released. ++ */ ++extern void ifxhcd_hc_cleanup(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc); ++ ++/*! ++ \brief Attempts to halt a host channel. This function should only be called in ++ Slave mode or to abort a transfer in either Slave mode or DMA mode. Under ++ normal circumstances in DMA mode, the controller halts the channel when the ++ transfer is complete or a condition occurs that requires application ++ intervention. ++ ++ In slave mode, checks for a free request queue entry, then sets the Channel ++ Enable and Channel Disable bits of the Host Channel Characteristics ++ register of the specified channel to intiate the halt. If there is no free ++ request queue entry, sets only the Channel Disable bit of the HCCHARn ++ register to flush requests for this channel. In the latter case, sets a ++ flag to indicate that the host channel needs to be halted when a request ++ queue slot is open. ++ ++ In DMA mode, always sets the Channel Enable and Channel Disable bits of the ++ HCCHARn register. The controller ensures there is space in the request ++ queue before submitting the halt request. ++ ++ Some time may elapse before the core flushes any posted requests for this ++ host channel and halts. The Channel Halted interrupt handler completes the ++ deactivation of the host channel. ++ */ ++extern void ifxhcd_hc_halt(ifxusb_core_if_t *_core_if, ++ ifxhcd_hc_t *_ifxhc, ++ ifxhcd_halt_status_e _halt_status); ++ ++/*! ++ \brief Prepares a host channel for transferring packets to/from a specific ++ endpoint. The HCCHARn register is set up with the characteristics specified ++ in _ifxhc. Host channel interrupts that may need to be serviced while this ++ transfer is in progress are enabled. ++ */ ++extern void ifxhcd_hc_init(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc); ++ ++/*! ++ \brief This function is called to handle the disconnection of host port. ++ */ ++int32_t ifxhcd_disconnect(ifxhcd_hcd_t *_ifxhcd); ++/*@}*/ ++ ++/*! \brief Interrupt Handler Functions */ ++/*@{*/ ++extern irqreturn_t ifxhcd_oc_irq(int _irq, void *_dev); ++ ++extern int32_t ifxhcd_handle_oc_intr(ifxhcd_hcd_t *_ifxhcd); ++extern int32_t ifxhcd_handle_intr (ifxhcd_hcd_t *_ifxhcd); ++/*@}*/ ++ ++ ++/*! \brief Schedule Queue Functions */ ++/*@{*/ ++extern ifxhcd_epqh_t *ifxhcd_epqh_create (ifxhcd_hcd_t *_ifxhcd, struct urb *_urb); ++extern void ifxhcd_epqh_free ( ifxhcd_epqh_t *_epqh); ++extern void select_eps (ifxhcd_hcd_t *_ifxhcd); ++extern void process_channels(ifxhcd_hcd_t *_ifxhcd); ++extern void process_channels_sub(ifxhcd_hcd_t *_ifxhcd); ++extern void complete_channel(ifxhcd_hcd_t *_ifxhcd, ifxhcd_hc_t *_ifxhc, ifxhcd_urbd_t *_urbd); ++extern void ifxhcd_epqh_ready(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh); ++extern void ifxhcd_epqh_active(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh); ++extern void ifxhcd_epqh_idle(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh); ++extern void ifxhcd_epqh_idle_periodic(ifxhcd_epqh_t *_epqh); ++extern int ifxhcd_urbd_create (ifxhcd_hcd_t *_ifxhcd,struct urb *_urb); ++/*@}*/ ++ ++/*! \brief Gets the usb_host_endpoint associated with an URB. */ ++static inline struct usb_host_endpoint *ifxhcd_urb_to_endpoint(struct urb *_urb) ++{ ++ struct usb_device *dev = _urb->dev; ++ int ep_num = usb_pipeendpoint(_urb->pipe); ++ ++ return (usb_pipein(_urb->pipe))?(dev->ep_in[ep_num]):(dev->ep_out[ep_num]); ++} ++ ++/*! ++ * \brief Gets the endpoint number from a _bEndpointAddress argument. The endpoint is ++ * qualified with its direction (possible 32 endpoints per device). ++ */ ++#define ifxhcd_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \ ++ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4) ++ ++ ++/* AVM/WK: not needed? ++ ++extern struct usb_device *usb_alloc_dev (struct usb_device *parent, struct usb_bus *, unsigned port); ++extern int usb_add_hcd (struct usb_hcd *syshcd, unsigned int irqnum, unsigned long irqflags); ++extern void usb_remove_hcd (struct usb_hcd *syshcd); ++extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver, struct device *dev, char *bus_name); ++extern void usb_hcd_giveback_urb (struct usb_hcd *syshcd, struct urb *urb); ++extern void usb_put_hcd (struct usb_hcd *syshcd); ++extern long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount); ++ ++*/ ++/** Internal Functions */ ++void ifxhcd_dump_state(ifxhcd_hcd_t *_ifxhcd); ++extern char *syserr(int errno); ++ ++/*@}*//*IFXUSB_HCD*/ ++ ++#endif // __IFXHCD_H__ +diff --git a/drivers/usb/ifxhcd/ifxhcd_es.c b/drivers/usb/ifxhcd/ifxhcd_es.c +new file mode 100644 +index 0000000..ef9e8c0 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxhcd_es.c +@@ -0,0 +1,549 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxhcd_es.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : The file contain function to enable host mode USB-IF Electrical Test function. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxhcd_es.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief The file contain function to enable host mode USB-IF Electrical Test function. ++*/ ++ ++#include ++#include "ifxusb_version.h" ++ ++#include ++ ++#include ++ ++#include ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++#include "ifxhcd.h" ++ ++ ++#ifdef __WITH_HS_ELECT_TST__ ++ /* ++ * Quick and dirty hack to implement the HS Electrical Test ++ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. ++ * ++ * This code was copied from our userspace app "hset". It sends a ++ * Get Device Descriptor control sequence in two parts, first the ++ * Setup packet by itself, followed some time later by the In and ++ * Ack packets. Rather than trying to figure out how to add this ++ * functionality to the normal driver code, we just hijack the ++ * hardware, using these two function to drive the hardware ++ * directly. ++ */ ++ ++ ++ void do_setup(ifxusb_core_if_t *_core_if) ++ { ++ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ifxusb_host_global_regs_t *hc_global_regs = _core_if->host_global_regs; ++ ifxusb_hc_regs_t *hc_regs = _core_if->hc_regs[0]; ++ uint32_t *data_fifo = _core_if->data_fifo[0]; ++ ++ gint_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ ++ ++ /* Enable HAINTs */ ++ ifxusb_wreg(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ ifxusb_wreg(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* ++ * Send Setup packet (Get Device Descriptor) ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 1, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ // hcchar.b.chen = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 1, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = IFXUSB_HC_PID_SETUP; ++ ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ hcchar.b.eptype = IFXUSB_EP_TYPE_CTRL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ ++ /* Fill FIFO with Setup data for Get Device Descriptor */ ++ ifxusb_wreg(data_fifo++, 0x01000680); ++ ifxusb_wreg(data_fifo++, 0x00080000); ++ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Disable HCINTs */ ++ ifxusb_wreg(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ ifxusb_wreg(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ } ++ ++ void do_in_ack(ifxusb_core_if_t *_core_if) ++ { ++ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ifxusb_host_global_regs_t *hc_global_regs = _core_if->host_global_regs; ++ ifxusb_hc_regs_t *hc_regs = _core_if->hc_regs[0]; ++ uint32_t *data_fifo = _core_if->data_fifo[0]; ++ ++ gint_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ grxsts_data_t grxsts; ++ ++ /* Enable HAINTs */ ++ ifxusb_wreg(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ ifxusb_wreg(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* ++ * Receive Control In packet ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 2, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 2, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = IFXUSB_HC_PID_DATA1; ++ ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ hcchar.b.eptype = IFXUSB_EP_TYPE_CTRL; ++ hcchar.b.epdir = 1; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ //fprintf(stderr, "Got RXSTSQLVL intr 1, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = ifxusb_rreg(&global_regs->grxstsp); ++ //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.hb.pktsts) { ++ case IFXUSB_HSTS_DATA_UPDT: ++ /* Read the data into the host buffer */ ++ if (grxsts.hb.bcnt > 0) { ++ int i; ++ int word_count = (grxsts.hb.bcnt + 3) / 4; ++ ++ for (i = 0; i < word_count; i++) { ++ (void)ifxusb_rreg(data_fifo++); ++ } ++ } ++ ++ //fprintf(stderr, "Received %u bytes\n", (unsigned)grxsts.hb.bcnt); ++ break; ++ ++ default: ++ //fprintf(stderr, "** Unexpected GRXSTS packet status 1 **\n"); ++ break; ++ } ++ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ //fprintf(stderr, "Got RXSTSQLVL intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read RXSTS */ ++ grxsts.d32 = ifxusb_rreg(&global_regs->grxstsp); ++ //fprintf(stderr, "GRXSTS: %08x\n", grxsts.d32); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.hb.pktsts) { ++ case IFXUSB_HSTS_XFER_COMP: ++ break; ++ ++ default: ++ //fprintf(stderr, "** Unexpected GRXSTS packet status 2 **\n"); ++ break; ++ } ++ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 2, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ // usleep(100000); ++ // mdelay(100); ++ mdelay(1); ++ ++ /* ++ * Send handshake packet ++ */ ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ //fprintf(stderr, "Channel already enabled 3, HCCHAR = %08x\n", hcchar.d32); ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //if (hcchar.b.chen) { ++ // fprintf(stderr, "** Channel _still_ enabled 3, HCCHAR = %08x **\n", hcchar.d32); ++ //} ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 0; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = IFXUSB_HC_PID_DATA1; ++ ifxusb_wreg(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ hcchar.b.eptype = IFXUSB_EP_TYPE_CTRL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ ifxusb_wreg(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "Waiting for HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ //fprintf(stderr, "Got HCINTR intr 3, GINTSTS = %08x\n", gintsts.d32); ++ ++ /* Disable HCINTs */ ++ ifxusb_wreg(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ ifxusb_wreg(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = ifxusb_rreg(&hc_global_regs->haint); ++ //fprintf(stderr, "HAINT: %08x\n", haint.d32); ++ ++ /* Read HCINT */ ++ hcint.d32 = ifxusb_rreg(&hc_regs->hcint); ++ //fprintf(stderr, "HCINT: %08x\n", hcint.d32); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = ifxusb_rreg(&hc_regs->hcchar); ++ //fprintf(stderr, "HCCHAR: %08x\n", hcchar.d32); ++ ++ /* Clear HCINT */ ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ ifxusb_wreg(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ ifxusb_wreg(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = ifxusb_rreg(&global_regs->gintsts); ++ //fprintf(stderr, "GINTSTS: %08x\n", gintsts.d32); ++ } ++#endif //__WITH_HS_ELECT_TST__ ++ +diff --git a/drivers/usb/ifxhcd/ifxhcd_intr.c b/drivers/usb/ifxhcd/ifxhcd_intr.c +new file mode 100644 +index 0000000..76fe602 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxhcd_intr.c +@@ -0,0 +1,3742 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxhcd_intr.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : This file contains the implementation of the HCD Interrupt handlers. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxhcd_intr.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the implementation of the HCD Interrupt handlers. ++*/ ++ ++ ++#include ++#include "ifxusb_version.h" ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++ ++#include "ifxhcd.h" ++ ++/* AVM/WK 20100520*/ ++#ifdef __EN_ISOC__ ++#error AVM/WK: CONFIG_USB_HOST_IFX_WITH_ISO currently not supported! ++#endif ++ ++/* Macro used to clear one channel interrupt */ ++#define clear_hc_int(_hc_regs_,_intr_) \ ++ do { \ ++ hcint_data_t hcint_clear = {.d32 = 0}; \ ++ hcint_clear.b._intr_ = 1; \ ++ ifxusb_wreg(&((_hc_regs_)->hcint), hcint_clear.d32); \ ++ } while (0) ++ ++/* ++ * Macro used to disable one channel interrupt. Channel interrupts are ++ * disabled when the channel is halted or released by the interrupt handler. ++ * There is no need to handle further interrupts of that type until the ++ * channel is re-assigned. In fact, subsequent handling may cause crashes ++ * because the channel structures are cleaned up when the channel is released. ++ */ ++#define disable_hc_int(_hc_regs_,_intr_) \ ++ do { \ ++ hcint_data_t hcintmsk = {.d32 = 0}; \ ++ hcintmsk.b._intr_ = 1; \ ++ ifxusb_mreg(&((_hc_regs_)->hcintmsk), hcintmsk.d32, 0); \ ++ } while (0) ++ ++#define enable_hc_int(_hc_regs_,_intr_) \ ++ do { \ ++ hcint_data_t hcintmsk = {.d32 = 0}; \ ++ hcintmsk.b._intr_ = 1; \ ++ ifxusb_mreg(&((_hc_regs_)->hcintmsk),0, hcintmsk.d32); \ ++ } while (0) ++ ++/* ++ * Save the starting data toggle for the next transfer. The data toggle is ++ * saved in the QH for non-control transfers and it's saved in the QTD for ++ * control transfers. ++ */ ++uint8_t read_data_toggle(ifxusb_hc_regs_t *_hc_regs) ++{ ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ return(hctsiz.b.pid); ++} ++ ++ ++static void release_channel_dump(ifxhcd_hc_t *ifxhc, ++ struct urb *urb, ++ ifxhcd_epqh_t *epqh, ++ ifxhcd_urbd_t *urbd, ++ ifxhcd_halt_status_e halt_status) ++{ ++ #ifdef __DEBUG__ ++ printk(KERN_INFO); ++ switch (halt_status) ++ { ++ case HC_XFER_NO_HALT_STATUS: ++ printk("HC_XFER_NO_HALT_STATUS");break; ++ case HC_XFER_URB_COMPLETE: ++ printk("HC_XFER_URB_COMPLETE");break; ++ case HC_XFER_AHB_ERR: ++ printk("HC_XFER_AHB_ERR");break; ++ case HC_XFER_STALL: ++ printk("HC_XFER_STALL");break; ++ case HC_XFER_BABBLE_ERR: ++ printk("HC_XFER_BABBLE_ERR");break; ++ case HC_XFER_XACT_ERR: ++ printk("HC_XFER_XACT_ERR");break; ++ case HC_XFER_URB_DEQUEUE: ++ printk("HC_XFER_URB_DEQUEUE");break; ++ case HC_XFER_FRAME_OVERRUN: ++ printk("HC_XFER_FRAME_OVERRUN");break; ++ case HC_XFER_DATA_TOGGLE_ERR: ++ printk("HC_XFER_DATA_TOGGLE_ERR");break; ++ case HC_XFER_NAK: ++ printk("HC_XFER_NAK");break; ++ case HC_XFER_COMPLETE: ++ printk("HC_XFER_COMPLETE");break; ++ default: ++ printk("KNOWN");break; ++ } ++ if(ifxhc) ++ printk("Ch %d %s%s S%d " , ifxhc->hc_num ++ ,(ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL)?"CTRL-": ++ ((ifxhc->ep_type == IFXUSB_EP_TYPE_BULK)?"BULK-": ++ ((ifxhc->ep_type == IFXUSB_EP_TYPE_INTR)?"INTR-": ++ ((ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC)?"ISOC-":"????" ++ ) ++ ) ++ ) ++ ,(ifxhc->is_in)?"IN":"OUT" ++ ,(ifxhc->split) ++ ); ++ else ++ printk(" [NULL HC] "); ++ printk("urb=%p epqh=%p urbd=%p\n",urb,epqh,urbd); ++ ++ if(urb) ++ { ++ printk(KERN_INFO " Device address: %d\n", usb_pipedevice(urb->pipe)); ++ printk(KERN_INFO " Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ printk(KERN_INFO " Endpoint type: %s\n", ++ ({char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CTRL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTR"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOC"; break; ++ default: pipetype = "????"; break; ++ }; pipetype;})); ++ printk(KERN_INFO " Speed: %s\n", ++ ({char *speed; ++ switch (urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HS"; break; ++ case USB_SPEED_FULL: speed = "FS"; break; ++ case USB_SPEED_LOW: speed = "LS"; break; ++ default: speed = "????"; break; ++ }; speed;})); ++ printk(KERN_INFO " Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ printk(KERN_INFO " Data buffer length: %d\n", urb->transfer_buffer_length); ++ printk(KERN_INFO " Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)urb->transfer_dma); ++ printk(KERN_INFO " Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ printk(KERN_INFO " Interval: %d\n", urb->interval); ++ switch (urb->status) ++ { ++ case HC_XFER_NO_HALT_STATUS: ++ printk(KERN_INFO " STATUS:HC_XFER_NO_HALT_STATUS\n");break; ++ case HC_XFER_URB_COMPLETE: ++ printk(KERN_INFO " STATUS:HC_XFER_URB_COMPLETE\n");break; ++ case HC_XFER_AHB_ERR: ++ printk(KERN_INFO " STATUS:HC_XFER_AHB_ERR\n");break; ++ case HC_XFER_STALL: ++ printk(KERN_INFO " STATUS:HC_XFER_STALL\n");break; ++ case HC_XFER_BABBLE_ERR: ++ printk(KERN_INFO " STATUS:HC_XFER_BABBLE_ERR\n");break; ++ case HC_XFER_XACT_ERR: ++ printk(KERN_INFO " STATUS:HC_XFER_XACT_ERR\n");break; ++ case HC_XFER_URB_DEQUEUE: ++ printk(KERN_INFO " STATUS:HC_XFER_URB_DEQUEUE\n");break; ++ case HC_XFER_FRAME_OVERRUN: ++ printk(KERN_INFO " STATUS:HC_XFER_FRAME_OVERRUN\n");break; ++ case HC_XFER_DATA_TOGGLE_ERR: ++ printk(KERN_INFO " STATUS:HC_XFER_DATA_TOGGLE_ERR\n");break; ++ case HC_XFER_COMPLETE: ++ printk(KERN_INFO " STATUS:HC_XFER_COMPLETE\n");break; ++ default: ++ printk(KERN_INFO " STATUS:KNOWN\n");break; ++ } ++ } ++ #endif ++} ++ ++ ++static void release_channel(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxhcd_halt_status_e _halt_status) ++{ ++ ifxusb_hc_regs_t *hc_regs = _ifxhcd->core_if.hc_regs[_ifxhc->hc_num]; ++ struct urb *urb = NULL; ++ ifxhcd_epqh_t *epqh = NULL; ++ ifxhcd_urbd_t *urbd = NULL; ++ ++ IFX_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d\n", ++ __func__, _ifxhc->hc_num, _halt_status); ++ ++ epqh=_ifxhc->epqh; ++ ++ if(!epqh) ++ IFX_ERROR("%s epqh=null\n",__func__); ++ else ++ { ++ urbd=epqh->urbd; ++ if(!urbd) ++ IFX_ERROR("%s urbd=null\n",__func__); ++ else ++ { ++ urb=urbd->urb; ++ if(!urb) ++ IFX_ERROR("%s urb =null\n",__func__); ++ else { ++ /* == AVM/WK 20100710 Fix - Use toggle of usbcore ==*/ ++ unsigned toggle = (read_data_toggle(hc_regs) == IFXUSB_HC_PID_DATA0)? 0: 1; ++ usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout(urb->pipe), toggle); ++ } ++ } ++ //epqh->data_toggle = read_data_toggle(hc_regs); ++ ++ } ++ ++ switch (_halt_status) ++ { ++ case HC_XFER_NO_HALT_STATUS: ++ IFX_ERROR("%s: No halt_status, channel %d\n", __func__, _ifxhc->hc_num); ++ break; ++ case HC_XFER_COMPLETE: ++ IFX_ERROR("%s: Inavalid halt_status HC_XFER_COMPLETE, channel %d\n", __func__, _ifxhc->hc_num); ++ break; ++ case HC_XFER_URB_COMPLETE: ++ case HC_XFER_URB_DEQUEUE: ++ case HC_XFER_AHB_ERR: ++ case HC_XFER_XACT_ERR: ++ case HC_XFER_FRAME_OVERRUN: ++ if(urbd && urb) { ++ /* == 20110803 AVM/WK FIX set status, if still in progress == */ ++ if (urb->status == -EINPROGRESS) { ++ switch (_halt_status) { ++ case HC_XFER_URB_COMPLETE: ++ urb->status = 0; ++ break; ++ case HC_XFER_URB_DEQUEUE: ++ urb->status = -ECONNRESET; ++ break; ++ case HC_XFER_AHB_ERR: ++ case HC_XFER_XACT_ERR: ++ case HC_XFER_FRAME_OVERRUN: ++ urb->status = -EPROTO; ++ break; ++ default: ++ break; ++ } ++ } ++ /*== AVM/BC 20101111 Deferred Complete ==*/ ++ defer_ifxhcd_complete_urb(_ifxhcd, urbd, urb->status); ++ } ++ else ++ { ++ IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); ++ release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); ++ } ++ if(epqh) ++ ifxhcd_epqh_idle(_ifxhcd, epqh); ++ else ++ { ++ IFX_WARN("WARNING %s():%d epqh=%p\n",__func__,__LINE__,epqh); ++ release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); ++ } ++ ++ list_add_tail(&_ifxhc->hc_list_entry, &_ifxhcd->free_hc_list); ++ ifxhcd_hc_cleanup(&_ifxhcd->core_if, _ifxhc); ++ break; ++ case HC_XFER_STALL: ++ release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); ++ if(urbd) ++ /*== AVM/BC 20101111 Deferred Complete ==*/ ++ defer_ifxhcd_complete_urb(_ifxhcd, urbd, -EPIPE); ++ else ++ IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); ++ if(epqh) ++ { ++// epqh->data_toggle = 0; ++ ifxhcd_epqh_idle(_ifxhcd, epqh); ++ } ++ else ++ IFX_WARN("WARNING %s():%d epqh=%p\n",__func__,__LINE__,epqh); ++ list_add_tail(&_ifxhc->hc_list_entry, &_ifxhcd->free_hc_list); ++ ifxhcd_hc_cleanup(&_ifxhcd->core_if, _ifxhc); ++ break; ++ case HC_XFER_NAK: ++ release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); ++ if(urbd) ++ { ++ //ifxhcd_complete_urb(_ifxhcd, urbd, -ETIMEDOUT); ++ urb->status = 0; ++ /*== AVM/BC 20101111 Deferred Complete ==*/ ++ defer_ifxhcd_complete_urb(_ifxhcd, urbd, urb->status); ++ } ++ else ++ IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); ++ if(epqh) ++ ifxhcd_epqh_idle(_ifxhcd, epqh); ++ else ++ IFX_WARN("WARNING %s():%d epqh=%p\n",__func__,__LINE__,epqh); ++ list_add_tail(&_ifxhc->hc_list_entry, &_ifxhcd->free_hc_list); ++ ifxhcd_hc_cleanup(&_ifxhcd->core_if, _ifxhc); ++ break; ++ case HC_XFER_BABBLE_ERR: ++ case HC_XFER_DATA_TOGGLE_ERR: ++ release_channel_dump(_ifxhc,urb,epqh,urbd,_halt_status); ++ if(urbd) ++ /*== AVM/BC 20101111 Deferred Complete ==*/ ++ defer_ifxhcd_complete_urb(_ifxhcd, urbd, -EOVERFLOW); ++ else ++ IFX_WARN("WARNING %s():%d urbd=%p urb=%p\n",__func__,__LINE__,urbd,urb); ++ if(epqh) ++ ifxhcd_epqh_idle(_ifxhcd, epqh); ++ else ++ IFX_WARN("WARNING %s():%d epqh=%p\n",__func__,__LINE__,epqh); ++ list_add_tail(&_ifxhc->hc_list_entry, &_ifxhcd->free_hc_list); ++ ifxhcd_hc_cleanup(&_ifxhcd->core_if, _ifxhc); ++ break; ++ } ++ select_eps(_ifxhcd); ++} ++ ++/* ++ * Updates the state of the URB after a Transfer Complete interrupt on the ++ * host channel. Updates the actual_length field of the URB based on the ++ * number of bytes transferred via the host channel. Sets the URB status ++ * if the data transfer is finished. ++ * ++ * @return 1 if the data transfer specified by the URB is completely finished, ++ * 0 otherwise. ++ */ ++static int update_urb_state_xfer_comp(ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ struct urb *_urb, ++ ifxhcd_urbd_t *_urbd) ++{ ++ int xfer_done = 0; ++ ++ if (_ifxhc->is_in) ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ _urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ if ((hctsiz.b.xfersize != 0) || (_urb->actual_length >= _urb->transfer_buffer_length)) ++ { ++ xfer_done = 1; ++ _urb->status = 0; ++ /* 20110805 AVM/WK Workaround: catch overflow error here, hardware does not */ ++ if (_urb->actual_length > _urb->transfer_buffer_length) { ++ _urb->status = -EOVERFLOW; ++ } ++ #if 0 ++ if (_urb->actual_length < _urb->transfer_buffer_length && _urb->transfer_flags & URB_SHORT_NOT_OK) ++ _urb->status = -EREMOTEIO; ++ #endif ++ } ++ ++ } ++ else ++ { ++ if (_ifxhc->split) ++ _urb->actual_length += _ifxhc->ssplit_out_xfer_count; ++ else ++ _urb->actual_length += _ifxhc->xfer_len; ++ ++ if (_urb->actual_length >= _urb->transfer_buffer_length) ++ { ++ /*== AVM/BC WK 20110421 ZERO PACKET Workaround ==*/ ++ if ((_ifxhc->short_rw == 1) && ( _ifxhc->xfer_len > 0) && ( _ifxhc->xfer_len % _ifxhc->mps == 0 )) ++ { ++ _ifxhc->short_rw = 0; ++ //Transfer not finished. Another iteration for ZLP. ++ } ++ else ++ { ++ xfer_done = 1; ++ } ++ _urb->status = 0; ++ } ++ } ++ ++ #ifdef __DEBUG__ ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ IFX_DEBUGPL(DBG_HCDV, "IFXUSB: %s: %s, channel %d\n", ++ __func__, (_ifxhc->is_in ? "IN" : "OUT"), _ifxhc->hc_num); ++ IFX_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", _ifxhc->xfer_len); ++ IFX_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n", hctsiz.b.xfersize); ++ IFX_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ _urb->transfer_buffer_length); ++ IFX_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", _urb->actual_length); ++ } ++ #endif ++ return xfer_done; ++} ++ ++/*== AVM/BC 20101111 Function called with Lock ==*/ ++ ++void complete_channel(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxhcd_urbd_t *_urbd) ++{ ++ ifxusb_hc_regs_t *hc_regs = _ifxhcd->core_if.hc_regs[_ifxhc->hc_num]; ++ struct urb *urb = NULL; ++ ifxhcd_epqh_t *epqh = NULL; ++ int urb_xfer_done; ++ ++ IFX_DEBUGPL(DBG_HCD, "--Complete Channel %d : \n", _ifxhc->hc_num); ++ ++ if(!_urbd) ++ { ++ IFX_ERROR("ERROR %s():%d urbd=%p\n",__func__,__LINE__,_urbd); ++ return; ++ } ++ ++ urb = _urbd->urb; ++ epqh = _urbd->epqh; ++ ++ if(!urb || !epqh) ++ { ++ IFX_ERROR("ERROR %s():%d urb=%p epqh=%p\n",__func__,__LINE__,urb,epqh); ++ return; ++ } ++ ++ _ifxhc->do_ping=0; ++ ++ if (_ifxhc->split) ++ _ifxhc->split = 1; ++ ++ switch (epqh->ep_type) ++ { ++ case IFXUSB_EP_TYPE_CTRL: ++ switch (_ifxhc->control_phase) ++ { ++ case IFXHCD_CONTROL_SETUP: ++ IFX_DEBUGPL(DBG_HCDV, " Control setup transaction done\n"); ++ if (_urbd->xfer_len > 0) ++ { ++ _ifxhc->control_phase = IFXHCD_CONTROL_DATA; ++ _ifxhc->is_in = _urbd->is_in; ++ _ifxhc->xfer_len = _urbd->xfer_len; ++ #if defined(__UNALIGNED_BUFFER_ADJ__) ++ if(epqh->using_aligned_buf) ++ _ifxhc->xfer_buff = epqh->aligned_buf; ++ else ++ #endif ++ _ifxhc->xfer_buff = _urbd->xfer_buff; ++ } ++ else ++ { ++ _ifxhc->control_phase = IFXHCD_CONTROL_STATUS; ++ _ifxhc->is_in = 1; ++ _ifxhc->xfer_len = 0; ++ _ifxhc->xfer_buff = _ifxhcd->status_buf; ++ } ++ if(_ifxhc->is_in) ++ _ifxhc->short_rw =0; ++ else ++ _ifxhc->short_rw =(urb->transfer_flags & URB_ZERO_PACKET)?1:0; ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ _ifxhc->xfer_count = 0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ /*== AVM/BC 20101111 Lock not needed ==*/ ++ process_channels_sub(_ifxhcd); ++ break; ++ case IFXHCD_CONTROL_DATA: ++ urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); ++ if (urb_xfer_done) ++ { ++ _ifxhc->control_phase = IFXHCD_CONTROL_STATUS; ++ _ifxhc->is_in = (_urbd->is_in)?0:1; ++ _ifxhc->xfer_len = 0; ++ _ifxhc->xfer_count = 0; ++ _ifxhc->xfer_buff = _ifxhcd->status_buf; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ if(_ifxhc->is_in) ++ _ifxhc->short_rw =0; ++ else ++ _ifxhc->short_rw =1; ++ } ++ else // continue ++ { ++ _ifxhc->xfer_len = _urbd->xfer_len - urb->actual_length; ++ _ifxhc->xfer_count = urb->actual_length; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->data_pid_start = read_data_toggle(hc_regs); ++ } ++ /*== AVM/BC 20101111 Lock not needed ==*/ ++ process_channels_sub(_ifxhcd); ++ break; ++ case IFXHCD_CONTROL_STATUS: ++ if (urb->status == -EINPROGRESS) ++ urb->status = 0; ++ release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); ++ break; ++ } ++ break; ++ case IFXUSB_EP_TYPE_BULK: ++ IFX_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n"); ++ urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); ++ if (urb_xfer_done) ++ release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); ++ else ++ { ++ _ifxhc->xfer_len = _urbd->xfer_len - urb->actual_length; ++ _ifxhc->xfer_count = urb->actual_length; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->data_pid_start = read_data_toggle(hc_regs); ++ /*== AVM/BC 20101111 Lock not needed ==*/ ++ process_channels_sub(_ifxhcd); ++ } ++ break; ++ case IFXUSB_EP_TYPE_INTR: ++ urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); ++ release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); ++ break; ++ case IFXUSB_EP_TYPE_ISOC: ++// if (_urbd->isoc_split_pos == IFXUSB_HCSPLIT_XACTPOS_ALL) ++// halt_status = update_isoc_urb_state(_ifxhcd, _ifxhc, hc_regs, _urbd, HC_XFER_COMPLETE); ++// complete_periodic_xfer(_ifxhcd, _ifxhc, hc_regs, _urbd, halt_status); ++ urb_xfer_done = update_urb_state_xfer_comp(_ifxhc, hc_regs, urb, _urbd); ++ release_channel(_ifxhcd,_ifxhc,HC_XFER_URB_COMPLETE); ++ break; ++ } ++} ++ ++ ++ ++void showint(uint32_t val_hcint ++ ,uint32_t val_hcintmsk ++ ,uint32_t val_hctsiz) ++{ ++#ifdef __DEBUG__ ++ hcint_data_t hcint = {.d32 = val_hcint}; ++ hcint_data_t hcintmsk = {.d32 = val_hcintmsk}; ++ ++ printk(KERN_INFO " WITH FLAG: Sz:%08x I:%08X/M:%08X %s%s%s%s%s%s%s%s%s%s\n" ++ ,val_hctsiz,hcint.d32 ,hcintmsk.d32 ++ ,(hcint.b.datatglerr || hcintmsk.b.datatglerr)? ++ ( ++ (hcint.b.datatglerr && hcintmsk.b.datatglerr)?"datatglerr[*/*] ": ++ ( ++ (hcint.b.datatglerr)?"datatglerr[*/] ":"datatglerr[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.frmovrun || hcintmsk.b.frmovrun)? ++ ( ++ (hcint.b.frmovrun && hcintmsk.b.frmovrun)?"frmovrun[*/*] ": ++ ( ++ (hcint.b.frmovrun)?"frmovrun[*/] ":"frmovrun[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.bblerr || hcintmsk.b.bblerr)? ++ ( ++ (hcint.b.bblerr && hcintmsk.b.bblerr)?"bblerr[*/*] ": ++ ( ++ (hcint.b.bblerr)?"bblerr[*/] ":"bblerr[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.xacterr || hcintmsk.b.xacterr)? ++ ( ++ (hcint.b.xacterr && hcintmsk.b.xacterr)?"xacterr[*/*] ": ++ ( ++ (hcint.b.xacterr)?"xacterr[*/] ":"xacterr[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.nyet || hcintmsk.b.nyet)? ++ ( ++ (hcint.b.nyet && hcintmsk.b.nyet)?"nyet[*/*] ": ++ ( ++ (hcint.b.nyet)?"nyet[*/] ":"nyet[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.nak || hcintmsk.b.nak)? ++ ( ++ (hcint.b.nak && hcintmsk.b.nak)?"nak[*/*] ": ++ ( ++ (hcint.b.nak)?"nak[*/] ":"nak[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.ack || hcintmsk.b.ack)? ++ ( ++ (hcint.b.ack && hcintmsk.b.ack)?"ack[*/*] ": ++ ( ++ (hcint.b.ack)?"ack[*/] ":"ack[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.stall || hcintmsk.b.stall)? ++ ( ++ (hcint.b.stall && hcintmsk.b.stall)?"stall[*/*] ": ++ ( ++ (hcint.b.stall)?"stall[*/] ":"stall[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.ahberr || hcintmsk.b.ahberr)? ++ ( ++ (hcint.b.ahberr && hcintmsk.b.ahberr)?"ahberr[*/*] ": ++ ( ++ (hcint.b.ahberr)?"ahberr[*/] ":"ahberr[/*] " ++ ) ++ ) ++ :"" ++ ,(hcint.b.xfercomp || hcintmsk.b.xfercomp)? ++ ( ++ (hcint.b.xfercomp && hcintmsk.b.xfercomp)?"xfercomp[*/*] ": ++ ( ++ (hcint.b.xfercomp)?"xfercomp[*/] ":"xfercomp[/*] " ++ ) ++ ) ++ :"" ++ ); ++#endif ++} ++ ++ ++extern void ifxhcd_hc_dumb_rx(ifxusb_core_if_t *_core_if, ifxhcd_hc_t *_ifxhc,uint8_t *dump_buf); ++ ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_ctrlbulk_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _ifxhc->do_ping = 0; ++ ++ if(_ifxhc->halt_status == HC_XFER_NAK) ++ { ++ if(_ifxhc->nak_retry_r) ++ { ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ _ifxhc->nak_retry--; ++ if(_ifxhc->nak_retry) ++ { ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); ++ } ++ } ++ else ++ { ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ ++ if (hcint.b.xfercomp) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ else if (hcint.b.stall) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ // ZLP shortcut ++ #if 0 ++ if(hctsiz.b.pktcnt==0) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ #endif ++ { ++ // Stall FIFO compensation. ++ #if 0 ++ int sz1,sz2; ++ sz2=_ifxhc->start_pkt_count - hctsiz.b.pktcnt; ++ sz2*=_ifxhc->mps; ++ sz1=_ifxhc->xfer_len - hctsiz.b.xfersize; ++ sz2-=sz1; ++ if(sz2) ++ ifxhcd_hc_dumb_rx(&_ifxhcd->core_if, _ifxhc,_ifxhc->epqh->dump_buf); ++ #endif ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ } ++ return 1; ++ } ++ else if (hcint.b.bblerr) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ ++ // ZLP shortcut ++ #if 0 ++ if(hctsiz.b.pktcnt==0) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ #endif ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if (hcint.b.xacterr) ++ { ++ // ZLP shortcut ++ #if 1 ++ if(hctsiz.b.pktcnt==0) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ #endif ++ { ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ ++ /* 20110803 AVM/WK FIX: Reset error count on any handshake */ ++ if (hcint.b.nak || hcint.b.nyet || hcint.b.ack) { ++ _urbd->error_count = 1; ++ } else { ++ _urbd->error_count++; ++ } ++ ++ if (_urbd->error_count >= 3) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ #if 1 ++ if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ #else ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ #endif ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK IN SPLIT0 FRMOVRUN [should be Period only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ else if(hcint.b.nyet ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK IN SPLIT0 NYET [should be Out only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_ctrlbulk_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++#ifdef __DEBUG__ ++static int first=0; ++#endif ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++#ifdef __DEBUG__ ++if(!first&& _ifxhc->ep_type == IFXUSB_EP_TYPE_BULK ++ &&(hcint.b.stall || hcint.b.datatglerr || hcint.b.frmovrun || hcint.b.bblerr || hcint.b.xacterr) && !hcint.b.ack) ++{ ++ showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ first=1; ++ printk(KERN_INFO " [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X] \n" ++ ,*(_ifxhc->xfer_buff+ 0),*(_ifxhc->xfer_buff+ 1),*(_ifxhc->xfer_buff+ 2),*(_ifxhc->xfer_buff+ 3) ++ ,*(_ifxhc->xfer_buff+ 4),*(_ifxhc->xfer_buff+ 5),*(_ifxhc->xfer_buff+ 6),*(_ifxhc->xfer_buff+ 7) ++ ,*(_ifxhc->xfer_buff+ 8),*(_ifxhc->xfer_buff+ 9),*(_ifxhc->xfer_buff+10),*(_ifxhc->xfer_buff+11) ++ ,*(_ifxhc->xfer_buff+12),*(_ifxhc->xfer_buff+13),*(_ifxhc->xfer_buff+14),*(_ifxhc->xfer_buff+15)); ++ ++ printk(KERN_INFO " [_urbd->urb->actual_length:%08X _ifxhc->start_pkt_count:%08X hctsiz.b.pktcnt:%08X ,_urbd->xfer_len:%08x] \n" ++ ,_urbd->urb->actual_length ++ ,_ifxhc->start_pkt_count ++ ,hctsiz.b.pktcnt ++ ,_urbd->xfer_len); ++} ++#endif ++ ++ if(_ifxhc->halt_status == HC_XFER_NAK) ++ { ++ if(_ifxhc->nak_retry_r) ++ { ++ _ifxhc->nak_retry--; ++ if(_ifxhc->nak_retry) ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); ++ } ++ } ++ else ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ ++ if (hcint.b.xfercomp) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _urbd->error_count =0; ++ if(_ifxhc->xfer_len==0 && !hcint.b.ack && hcint.b.nak) ++ { ++ // Walkaround: When sending ZLP and receive NAK but also issue CMPT intr ++ // Solution: NoSplit: Resend at next SOF ++ // Split : Resend at next SOF with SSPLIT ++ if(hcint.b.nyet && !out_nak_enh) ++ _ifxhc->do_ping = 1; ++ else ++ _ifxhc->do_ping = 0; ++ _ifxhc->xfer_len = 0; ++ _ifxhc->xfer_count = 0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ return 1; ++ } ++ else if (hcint.b.stall) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ ++ // ZLP shortcut ++ #if 1 ++ if(hctsiz.b.pktcnt==0) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ #endif ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ } ++ return 1; ++ } ++ else if (hcint.b.xacterr) ++ { ++ // ZLP shortcut ++ #if 1 ++ if(hctsiz.b.pktcnt==0) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ #endif ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ ++ if (hcint.b.nak || hcint.b.nyet || hcint.b.ack) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =1; ++ enable_hc_int(_hc_regs,ack); ++ enable_hc_int(_hc_regs,nak); ++ enable_hc_int(_hc_regs,nyet); ++ if(!out_nak_enh) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ { ++ _urbd->error_count ++ ; ++ if (_urbd->error_count == 3) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ enable_hc_int(_hc_regs,ack); ++ enable_hc_int(_hc_regs,nak); ++ enable_hc_int(_hc_regs,nyet); ++ _ifxhc->wait_for_sof =1; ++ if(!out_nak_enh) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ } ++ } ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK OUT SPLIT0 BABBLE [should be IN only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _ifxhc->do_ping = 0; ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.nak || hcint.b.nyet) ++ { ++ if(!out_nak_enh) ++ { ++ // ZLP shortcut ++ #if 1 ++ if(hctsiz.b.pktcnt==0) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ #endif ++ { ++ if(!out_nak_enh) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ if(_ifxhc->xfer_len!=0) ++ { ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ } ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ } ++ else if(hcint.b.datatglerr ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK OUT SPLIT0 DATATGLERR [should be IN only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK OUT SPLIT0 FRMOVRUN [should be PERIODIC only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_intr_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _ifxhc->do_ping =0; ++ ++ if(_ifxhc->halt_status == HC_XFER_NAK) ++ { ++ if(_ifxhc->nak_retry_r) ++ { ++ _ifxhc->nak_retry--; ++ if(_ifxhc->nak_retry) ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); ++ } ++ } ++ else ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ ++ if(hcint.b.xfercomp ) ++ { ++ _urbd->error_count =0; ++ //restart INTR immediately ++ #if 1 ++ if(hctsiz.b.pktcnt>0) ++ { ++ // TODO Re-initialize Channel (in next b_interval - 1 uF/F) ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ #endif ++ { ++ _ifxhc->wait_for_sof =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ return 1; ++ } ++ else if (hcint.b.stall) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ ++ // Don't care shortcut ++ #if 0 ++ if(hctsiz.b.pktcnt==0) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ #endif ++ { ++ // Stall FIFO compensation. ++ #if 0 ++ int sz1,sz2; ++ sz2=_ifxhc->start_pkt_count - hctsiz.b.pktcnt; ++ sz2*=_ifxhc->mps; ++ sz1=_ifxhc->xfer_len - hctsiz.b.xfersize; ++ sz2-=sz1; ++ if(sz2) ++ ifxhcd_hc_dumb_rx(&_ifxhcd->core_if, _ifxhc,_ifxhc->epqh->dump_buf); ++ #endif ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ } ++ return 1; ++ } ++ ++ ++ else if (hcint.b.bblerr) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ ++ // Don't care shortcut ++ #if 0 ++ if(hctsiz.b.pktcnt==0) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ #endif ++ { ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ } ++ return 1; ++ } ++ else if (hcint.b.nak || hcint.b.datatglerr || hcint.b.frmovrun) ++ { ++ _urbd->error_count =0; ++ //restart INTR immediately ++ #if 1 ++ if(hctsiz.b.pktcnt>0) ++ { ++ // TODO Re-initialize Channel (in next b_interval - 1 uF/F) ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ #endif ++ { ++ _ifxhc->wait_for_sof =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ return 1; ++ } ++ else if (hcint.b.xacterr) ++ { ++ // ZLP shortcut ++ #if 1 ++ if(hctsiz.b.pktcnt==0) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ #endif ++ { ++ /* 20110803 AVM/WK FIX: Reset error count on any handshake */ ++ if (hcint.b.nak || hcint.b.nyet || hcint.b.ack) { ++ _urbd->error_count = 1; ++ } else { ++ _urbd->error_count++; ++ } ++ ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ } ++ return 1; ++ } ++ else if(hcint.b.nyet ) ++ { ++IFX_WARN("%s() %d Warning INTR IN SPLIT0 NYET [should be OUT only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_intr_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ if(_ifxhc->halt_status == HC_XFER_NAK) ++ { ++ if(_ifxhc->nak_retry_r) ++ { ++ _ifxhc->nak_retry--; ++ if(_ifxhc->nak_retry) ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); ++ } ++ } ++ else ++ { ++ if(_ifxhc->xfer_len!=0) ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ ++ if(hcint.b.xfercomp ) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _urbd->error_count =0; ++ //restart INTR immediately ++ #if 0 ++ if(hctsiz.b.pktcnt>0) ++ { ++ // TODO Re-initialize Channel (in next b_interval - 1 uF/F) ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ if(hcint.b.nyet && !out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ #endif ++ { ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ return 1; ++ } ++ else if (hcint.b.stall) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nyet); ++ disable_hc_int(_hc_regs,nak); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ ++ // Don't care shortcut ++ #if 0 ++ if(hctsiz.b.pktcnt==0) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ #endif ++ { ++ if(_ifxhc->xfer_len!=0)// !_ifxhc->is_in ++ _urbd->urb->actual_length += ((_ifxhc->start_pkt_count - hctsiz.b.pktcnt ) * _ifxhc->mps); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ } ++ return 1; ++ } ++ else if(hcint.b.nak || hcint.b.frmovrun ) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nyet); ++ disable_hc_int(_hc_regs,nak); ++ _urbd->error_count =0; ++ //restart INTR immediately ++ #if 0 ++ if(hctsiz.b.pktcnt>0) ++ { ++ // TODO Re-initialize Channel (in next b_interval - 1 uF/F) ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ #endif ++ { ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ // ZLP shortcut ++ #if 1 ++ if(hctsiz.b.pktcnt==0) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ #endif ++ { ++ /* 20110803 AVM/WK FIX: Reset error count on any handshake */ ++ if (hcint.b.nak || hcint.b.nyet || hcint.b.ack) { ++ _urbd->error_count = 1; ++ } else { ++ _urbd->error_count++; ++ } ++ ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ //_ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ //if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ _ifxhc->wait_for_sof=1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ } ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++IFX_WARN("%s() %d Warning INTR OUT SPLIT0 BABBLEERR [should be IN only]\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++IFX_WARN("%s() %d Warning INTR OUT SPLIT0 DATATGLERR\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_isoc_rx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #if defined(__EN_ISOC__) ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ if (hcint.b.xfercomp || hcint.b.frmovrun) ++ { ++ _urbd->error_count=0; ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _ifxhc->wait_for_sof = 0; ++ if (hcint.b.xfercomp) ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ else ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ } ++ else if (hcint.b.xacterr || hcint.b.bblerr) ++ { ++ #ifndef VR9Skip ++ if(hctsiz.b.pktcnt==0) ++ { ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ { ++ int sz1,sz2; ++ sz2=_ifxhc->start_pkt_count - hctsiz.b.pktcnt; ++ sz2*=_ifxhc->mps; ++ sz1=_ifxhc->xfer_len - hctsiz.b.xfersize; ++ sz2-=sz1; ++ if(sz2) ++ ifxhcd_hc_dumb_rx(&_ifxhcd->core_if, _ifxhc,_ifxhc->epqh->dump_buf); ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 1; ++ enable_hc_int(_hc_regs,ack); ++ enable_hc_int(_hc_regs,nak); ++ enable_hc_int(_hc_regs,nyet); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ } ++ #endif ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ warning ++ } ++ else if(hcint.b.stall ) ++ { ++ warning ++ } ++ #else ++ #endif ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_isoc_tx_nonsplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #if defined(__EN_ISOC__) ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ if (hcint.b.xfercomp) ++ { ++ _urbd->error_count=0; ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _ifxhc->wait_for_sof = 0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ else if (hcint.b.frmovrun) ++ { ++ #ifndef VR9Skip ++ _urbd->error_count=0; ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ #endif ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ warning ++ } ++ else if(hcint.b.bblerr ) ++ { ++ #ifndef VR9Skip ++ if(hctsiz.b.pktcnt==0) ++ { ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ else ++ { ++ int sz1,sz2; ++ sz2=_ifxhc->start_pkt_count - hctsiz.b.pktcnt; ++ sz2*=_ifxhc->mps; ++ sz1=_ifxhc->xfer_len - hctsiz.b.xfersize; ++ sz2-=sz1; ++ if(sz2) ++ ifxhcd_hc_dumb_rx(&_ifxhcd->core_if, _ifxhc,_ifxhc->epqh->dump_buf); ++ _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 1; ++ enable_hc_int(_hc_regs,ack); ++ enable_hc_int(_hc_regs,nak); ++ enable_hc_int(_hc_regs,nyet); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ } ++ #endif ++ } ++ else if(hcint.b.xacterr ) ++ { ++ if(hctsiz.b.pktcnt==0) ++ { ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.stall ) ++ { ++ warning ++ } ++ #else ++ #endif ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_ctrlbulk_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++ _ifxhc->do_ping =0; ++ ++ if (hcint.b.ack) ++ { ++ _urbd->error_count=0; ++ _ifxhc->split=2; ++ _ifxhc->wait_for_sof = 8; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if (hcint.b.nak) ++ { ++ _ifxhc->wait_for_sof = 1; ++ _urbd->error_count = 0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if (hcint.b.xacterr) ++ { ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof =1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.stall ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK IN SPLIT1 HC_XFER_DATA_TOGGLE_ERR\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK IN SPLIT1 HC_XFER_FRAME_OVERRUN\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ else if(hcint.b.nyet ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK IN SPLIT1 NYET\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ } ++ else if(hcint.b.xfercomp ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK IN SPLIT1 COMPLETE\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_ctrlbulk_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++#ifdef __DEBUG__ ++static int first=0; ++#endif ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++#ifdef __DEBUG__ ++ if(!first&& _ifxhc->ep_type == IFXUSB_EP_TYPE_BULK ++ &&(hcint.b.stall || hcint.b.datatglerr || hcint.b.frmovrun || hcint.b.bblerr || hcint.b.xacterr) && !hcint.b.ack) ++ { ++ showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ first=1; ++ printk(KERN_INFO " [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X] \n" ++ ,*(_ifxhc->xfer_buff+ 0),*(_ifxhc->xfer_buff+ 1),*(_ifxhc->xfer_buff+ 2),*(_ifxhc->xfer_buff+ 3) ++ ,*(_ifxhc->xfer_buff+ 4),*(_ifxhc->xfer_buff+ 5),*(_ifxhc->xfer_buff+ 6),*(_ifxhc->xfer_buff+ 7) ++ ,*(_ifxhc->xfer_buff+ 8),*(_ifxhc->xfer_buff+ 9),*(_ifxhc->xfer_buff+10),*(_ifxhc->xfer_buff+11) ++ ,*(_ifxhc->xfer_buff+12),*(_ifxhc->xfer_buff+13),*(_ifxhc->xfer_buff+14),*(_ifxhc->xfer_buff+15)); ++ ++ printk(KERN_INFO " [_urbd->urb->actual_length:%08X _ifxhc->start_pkt_count:%08X hctsiz.b.pktcnt:%08X ,_urbd->xfer_len:%08x] \n" ++ ,_urbd->urb->actual_length ++ ,_ifxhc->start_pkt_count ++ ,hctsiz.b.pktcnt ++ ,_urbd->xfer_len); ++ } ++#endif ++ ++ if (hcint.b.ack ) ++ { ++ _urbd->error_count=0; ++ if (_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK || _ifxhc->control_phase != IFXHCD_CONTROL_SETUP) ++ _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; ++ _ifxhc->split=2; ++ _ifxhc->wait_for_sof =8; ++ _ifxhc->data_pid_start =read_data_toggle(_hc_regs); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nyet) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK OUT SPLIT1 NYET\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count=0; ++ if (_ifxhc->ep_type == IFXUSB_EP_TYPE_BULK || _ifxhc->control_phase != IFXHCD_CONTROL_SETUP) ++ _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; ++ _ifxhc->split=2; ++ _ifxhc->wait_for_sof =1; ++ _ifxhc->data_pid_start =read_data_toggle(_hc_regs); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ _ifxhc->wait_for_sof =1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _urbd->error_count =0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof =1; ++ _ifxhc->do_ping =1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.stall ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++IFX_WARN("%s() %d Warning CTRLBULK OUT SPLIT1 HC_XFER_FRAME_OVERRUN\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ _ifxhc->do_ping =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ else if(hcint.b.xfercomp ) ++ { ++ printk(KERN_INFO "%s() %d Warning CTRLBULK OUT SPLIT1 COMPLETE\n",__func__,__LINE__); ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_intr_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++ _ifxhc->do_ping =0; ++ ++ if (hcint.b.ack ) ++ { ++ /*== AVM/BC 20100701 - Workaround FullSpeed Interrupts with HiSpeed Hub ==*/ ++ _ifxhc->nyet_count=0; ++ ++ _urbd->error_count=0; ++ _ifxhc->split=2; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ _urbd->error_count=0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); ++ _urbd->error_count=hcchar.b.multicnt; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.stall ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++IFX_WARN( "%s() %d Warning INTR IN SPLIT1 DATATGLERR\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.xfercomp ) ++ { ++IFX_WARN("%s() %d Warning INTR IN SPLIT1 COMPLETE\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_intr_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++ if (hcint.b.ack ) ++ { ++ /*== AVM/BC 20100701 - Workaround FullSpeed Interrupts with HiSpeed Hub ==*/ ++ _ifxhc->nyet_count=0; ++ ++ _urbd->error_count=0; ++ _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; ++ _ifxhc->split=2; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nyet) ++ { ++IFX_WARN("%s() %d Warning INTR OUT SPLIT1 NYET\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count=0; ++ _ifxhc->ssplit_out_xfer_count = _ifxhc->xfer_len; ++ _ifxhc->split=2; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->data_pid_start = read_data_toggle(_hc_regs); ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ _urbd->error_count =0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); ++ _urbd->error_count=hcchar.b.multicnt; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ enable_hc_int(_hc_regs,ack); ++ enable_hc_int(_hc_regs,nak); ++ enable_hc_int(_hc_regs,nyet); ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++IFX_WARN("%s() %d Warning INTR IN SPLIT1 DATATGLERR\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_DATA_TOGGLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++IFX_WARN("%s() %d Warning INTR IN SPLIT1 BABBLEERR\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.stall ) ++ { ++IFX_WARN("%s() %d Warning INTR IN SPLIT1 STALL\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof =0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ return 1; ++ } ++ else if(hcint.b.xfercomp ) ++ { ++IFX_WARN("%s() %d Warning INTR IN SPLIT1 COMPLETE\n",__func__,__LINE__); ++showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_isoc_rx_ssplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ if (hcint.b.ack ) ++ { ++ Do Complete Split ++ } ++ else if(hcint.b.frmovrun ) ++ { ++ Rewind Buffer Pointers ++ Retry Start Split (in next b_interval ¡V 1 uF) ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ warning ++ } ++ else if(hcint.b.bblerr ) ++ { ++ warning ++ } ++ else if(hcint.b.xacterr ) ++ { ++ warning ++ } ++ else if(hcint.b.stall ) ++ { ++ warning ++ } ++ else if(hcint.b.nak ) ++ { ++ warning ++ } ++ else if(hcint.b.xfercomp ) ++ { ++ warning ++ } ++ else if(hcint.b.nyet) ++ { ++ warning ++ } ++ #endif ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_isoc_tx_ssplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ if (hcint.b.ack ) ++ { ++ Do Next Start Split (in next b_interval ¡V 1 uF) ++ } ++ else if(hcint.b.frmovrun ) ++ { ++ Do Next Transaction in next frame. ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ warning ++ } ++ else if(hcint.b.bblerr ) ++ { ++ warning ++ } ++ else if(hcint.b.xacterr ) ++ { ++ warning ++ } ++ else if(hcint.b.stall ) ++ { ++ warning ++ } ++ else if(hcint.b.nak ) ++ { ++ warning ++ } ++ else if(hcint.b.xfercomp ) ++ { ++ warning ++ } ++ else if(hcint.b.nyet) ++ { ++ warning ++ } ++ #endif ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_ctrlbulk_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++ _ifxhc->do_ping = 0; ++ ++ if (hcint.b.xfercomp) ++ { ++ _urbd->error_count =0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->split=1; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ else if (hcint.b.nak) ++ { ++ _urbd->error_count=0; ++ ++ _ifxhc->split = 1; ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nyet) ++ { ++ _urbd->error_count=0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.stall || hcint.b.bblerr ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ if (hcint.b.stall) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ else if(hcint.b.bblerr ) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_ctrlbulk_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++#if 1 ++static int first=0; ++#endif ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++#if 1 ++ if(!first&& _ifxhc->ep_type == IFXUSB_EP_TYPE_BULK ++ &&(hcint.b.stall || hcint.b.datatglerr || hcint.b.frmovrun || hcint.b.bblerr || hcint.b.xacterr) && !hcint.b.ack) ++ { ++ showint( hcint.d32,hcintmsk.d32,hctsiz.d32); ++ first=1; ++ printk(KERN_INFO " [%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X] \n" ++ ,*(_ifxhc->xfer_buff+ 0),*(_ifxhc->xfer_buff+ 1),*(_ifxhc->xfer_buff+ 2),*(_ifxhc->xfer_buff+ 3) ++ ,*(_ifxhc->xfer_buff+ 4),*(_ifxhc->xfer_buff+ 5),*(_ifxhc->xfer_buff+ 6),*(_ifxhc->xfer_buff+ 7) ++ ,*(_ifxhc->xfer_buff+ 8),*(_ifxhc->xfer_buff+ 9),*(_ifxhc->xfer_buff+10),*(_ifxhc->xfer_buff+11) ++ ,*(_ifxhc->xfer_buff+12),*(_ifxhc->xfer_buff+13),*(_ifxhc->xfer_buff+14),*(_ifxhc->xfer_buff+15)); ++ ++ printk(KERN_INFO " [_urbd->urb->actual_length:%08X _ifxhc->start_pkt_count:%08X hctsiz.b.pktcnt:%08X ,_urbd->xfer_len:%08x] \n" ++ ,_urbd->urb->actual_length ++ ,_ifxhc->start_pkt_count ++ ,hctsiz.b.pktcnt ++ ,_urbd->xfer_len); ++ } ++#endif ++ ++ if(hcint.b.xfercomp ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->split=1; ++ _ifxhc->do_ping= 0; ++ #if 0 ++ if(_ifxhc->xfer_len==0 && !hcint.b.ack && (hcint.b.nak || hcint.b.nyet)) ++ { ++ // Walkaround: When sending ZLP and receive NYEY or NAK but also issue CMPT intr ++ // Solution: NoSplit: Resend at next SOF ++ // Split : Resend at next SOF with SSPLIT ++ _ifxhc->xfer_len = 0; ++ _ifxhc->xfer_count = 0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ else ++ #endif ++ { ++ _ifxhc->wait_for_sof = 0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ } ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ _urbd->error_count=0; ++ ++ _ifxhc->split = 1; ++ _ifxhc->wait_for_sof = 1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nyet) ++ { ++ //Retry Complete Split ++ // Issue Retry instantly on next SOF, without gothrough process_channels ++ _urbd->error_count=0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 1; ++ _ifxhc->do_ping = 0; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.stall ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ _urbd->error_count++; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = 1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = 1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.frmovrun ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_intr_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _ifxhc->do_ping = 0; ++ ++ if (hcint.b.xfercomp ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->split=1; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->split = 1; ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nyet) ++ { ++ _urbd->error_count=0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 0; ++ ++ /*== AVM/BC 20100701 - Workaround FullSpeed Interrupts with HiSpeed Hub ==*/ ++ _ifxhc->nyet_count++; ++ if(_ifxhc->nyet_count > 2) { ++ _ifxhc->split = 1; ++ _ifxhc->nyet_count = 0; ++ _ifxhc->wait_for_sof = 5; ++ } ++ ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.frmovrun || hcint.b.bblerr || hcint.b.stall ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ if (hcint.b.stall) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ else if(hcint.b.bblerr ) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ else if(hcint.b.frmovrun ) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); ++ _urbd->error_count=hcchar.b.multicnt; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_intr_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ ++ if(hcint.b.xfercomp ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->split=1; ++ _ifxhc->do_ping = 0; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->split = 1; ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.nyet) ++ { ++ _urbd->error_count=0; ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ ++ /*== AVM/BC 20100701 - Workaround FullSpeed Interrupts with HiSpeed Hub ==*/ ++ _ifxhc->nyet_count++; ++ if(_ifxhc->nyet_count > 2) { ++ _ifxhc->split = 1; ++ _ifxhc->nyet_count = 0; ++ _ifxhc->wait_for_sof = 5; ++ } ++ ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.stall || hcint.b.frmovrun) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ if (hcint.b.stall) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ else if(hcint.b.frmovrun ) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_FRAME_OVERRUN); ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); ++ _urbd->error_count=hcchar.b.multicnt; ++ if(_urbd->error_count>=3) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_XACT_ERR); ++ } ++ else ++ { ++ _ifxhc->split=1; ++ _ifxhc->wait_for_sof = _ifxhc->epqh->interval-1; ++ if(!_ifxhc->wait_for_sof) _ifxhc->wait_for_sof=1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ } ++ return 1; ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ if(_ifxhc->data_pid_start == IFXUSB_HC_PID_DATA0) ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA1; ++ else ++ _ifxhc->data_pid_start = IFXUSB_HC_PID_DATA0; ++ _ifxhc->split=1; ++ if(!out_nak_enh ) ++ _ifxhc->do_ping =1; ++ else ++ _ifxhc->do_ping =0; ++ _ifxhc->xfer_len = _urbd->xfer_len - _urbd->urb->actual_length; ++ _ifxhc->xfer_count = _urbd->urb->actual_length; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.bblerr ) ++ { ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->do_ping = 0; ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_BABBLE_ERR); ++ return 1; ++ } ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_isoc_rx_csplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ if(hcint.b.xfercomp ) ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,nyet); ++ _urbd->error_count=0; ++ _ifxhc->wait_for_sof = 0; ++ _ifxhc->split=1; ++ complete_channel(_ifxhcd, _ifxhc, _urbd); ++ return 1; ++ } ++ else if(hcint.b.nak ) ++ { ++ Retry Start Split (in next b_interval ¡V 1 uF) ++ } ++ else if(hcint.b.nyet) ++ { ++ //Do Next Complete Split ++ // Issue Retry instantly on next SOF, without gothrough process_channels ++ _urbd->error_count=0; ++ //disable_hc_int(_hc_regs,ack); ++ //disable_hc_int(_hc_regs,nak); ++ //disable_hc_int(_hc_regs,datatglerr); ++ _ifxhc->halt_status = HC_XFER_NO_HALT_STATUS; ++ _ifxhc->wait_for_sof = 1; ++ ifxhcd_hc_start(&_ifxhcd->core_if, _ifxhc); ++ return 1; ++ } ++ else if(hcint.b.frmovrun || hcint.b.stall || hcint.b.bblerr) ++ { ++ _urbd->error_count=0; ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nyet); ++ disable_hc_int(_hc_regs,nak); ++ _ifxhc->wait_for_sof = 0; ++ ++ //if(hctsiz.b.pktcnt==0) ++ //{ ++ // complete_channel(_ifxhcd, _ifxhc, _urbd); ++ // return 1; ++ //} ++ //else ++ // _urbd->urb->actual_length += (_ifxhc->xfer_len - hctsiz.b.xfersize); ++ if (hcint.b.stall) ++ release_channel(_ifxhcd, _ifxhc, HC_XFER_STALL); ++ else if(hcint.b.frmovrun ) ++ else if(hcint.b.bblerr ) ++ return 1; ++ } ++ else if(hcint.b.xacterr ) ++ { ++ Rewind Buffer Pointers ++ if (HCCHARn.EC = = 3) // ERR response received ++ { ++ Record ERR error ++ Do Next Start Split (in next frame) ++ } ++ else ++ { ++ De-allocate Channel ++ } ++ } ++ else if(hcint.b.datatglerr ) ++ { ++ warning ++ } ++ else if(hcint.b.ack ) ++ { ++ warning ++ } ++ #endif ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++static int32_t chhltd_isoc_tx_csplit(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #if defined(__EN_ISOC__) && defined(__EN_ISOC_SPLIT__) ++ hcint_data_t hcint; ++ hcint_data_t hcintmsk; ++ hctsiz_data_t hctsiz; ++ int out_nak_enh = 0; ++ ++ if (_ifxhcd->core_if.snpsid >= 0x4f54271a && _ifxhc->speed == IFXUSB_EP_SPEED_HIGH) ++ out_nak_enh = 1; ++ ++ hcint.d32 = ifxusb_rreg(&_hc_regs->hcint); ++ hcintmsk.d32 = ifxusb_rreg(&_hc_regs->hcintmsk); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ warning ++ #endif ++ return 0; ++} ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++//////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++static int32_t handle_hc_chhltd_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: Channel Halted--\n", _ifxhc->hc_num); ++ ++ _ifxhc->halting = 0; ++ _ifxhc->xfer_started = 0; ++ ++ if (_ifxhc->halt_status == HC_XFER_URB_DEQUEUE || ++ _ifxhc->halt_status == HC_XFER_AHB_ERR) { ++ /* ++ * Just release the channel. A dequeue can happen on a ++ * transfer timeout. In the case of an AHB Error, the channel ++ * was forced to halt because there's no way to gracefully ++ * recover. ++ */ ++ release_channel(_ifxhcd, _ifxhc, _ifxhc->halt_status); ++ return 1; ++ } ++ ++ if (_ifxhc->ep_type == IFXUSB_EP_TYPE_CTRL || _ifxhc->ep_type == IFXUSB_EP_TYPE_BULK) ++ { ++ if (_ifxhc->split==0) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_ctrlbulk_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_ctrlbulk_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ else if(_ifxhc->split==1) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_ctrlbulk_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_ctrlbulk_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ else if(_ifxhc->split==2) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_ctrlbulk_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_ctrlbulk_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ } ++ else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_INTR) ++ { ++ if (_ifxhc->split==0) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_intr_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_intr_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ else if(_ifxhc->split==1) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_intr_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_intr_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ else if(_ifxhc->split==2) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_intr_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_intr_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ } ++ else if(_ifxhc->ep_type == IFXUSB_EP_TYPE_ISOC) ++ { ++ if (_ifxhc->split==0) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_isoc_rx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_isoc_tx_nonsplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ else if(_ifxhc->split==1) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_isoc_rx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_isoc_tx_ssplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ else if(_ifxhc->split==2) ++ { ++ if(_ifxhc->is_in) ++ return (chhltd_isoc_rx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ else ++ return (chhltd_isoc_tx_csplit(_ifxhcd,_ifxhc,_hc_regs,_urbd)); ++ } ++ } ++ return 0; ++} ++ ++/* ++ * Handles a host channel AHB error interrupt. This handler is only called in ++ * DMA mode. ++ */ ++static void hc_other_intr_dump(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ #ifdef __DEBUG__ ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ struct urb *urb = _urbd->urb; ++ hcchar.d32 = ifxusb_rreg(&_hc_regs->hcchar); ++ hcsplt.d32 = ifxusb_rreg(&_hc_regs->hcsplt); ++ hctsiz.d32 = ifxusb_rreg(&_hc_regs->hctsiz); ++ hcdma = ifxusb_rreg(&_hc_regs->hcdma); ++ ++ IFX_ERROR("Channel %d\n", _ifxhc->hc_num); ++ IFX_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ IFX_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ IFX_ERROR(" Device address: %d\n", usb_pipedevice(urb->pipe)); ++ IFX_ERROR(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ IFX_ERROR(" Endpoint type: %s\n", ++ ({char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: pipetype = "CTRL"; break; ++ case PIPE_BULK: pipetype = "BULK"; break; ++ case PIPE_INTERRUPT: pipetype = "INTR"; break; ++ case PIPE_ISOCHRONOUS: pipetype = "ISOC"; break; ++ default: pipetype = "????"; break; ++ }; pipetype;})); ++ IFX_ERROR(" Speed: %s\n", ++ ({char *speed; ++ switch (urb->dev->speed) { ++ case USB_SPEED_HIGH: speed = "HS"; break; ++ case USB_SPEED_FULL: speed = "FS"; break; ++ case USB_SPEED_LOW: speed = "LS"; break; ++ default: speed = "????"; break; ++ }; speed;})); ++ IFX_ERROR(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ IFX_ERROR(" Data buffer length: %d\n", urb->transfer_buffer_length); ++ IFX_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)urb->transfer_dma); ++ IFX_ERROR(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ IFX_ERROR(" Interval: %d\n", urb->interval); ++ #endif //__DEBUG__ ++} ++ ++/* ++ * Handles a host channel ACK interrupt. This interrupt is enabled when ++ * errors occur, and during Start Split transactions. ++ */ ++static int32_t handle_hc_ack_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ _urbd->error_count=0; ++ if(_ifxhc->nak_countdown_r) ++ { ++ _ifxhc->nak_retry=_ifxhc->nak_retry_r; ++ _ifxhc->nak_countdown=_ifxhc->nak_countdown_r; ++ } ++ else ++ disable_hc_int(_hc_regs,nak); ++ disable_hc_int(_hc_regs,ack); ++ return 1; ++} ++ ++/* ++ * Handles a host channel ACK interrupt. This interrupt is enabled when ++ * errors occur, and during Start Split transactions. ++ */ ++static int32_t handle_hc_nak_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ ++ _urbd->error_count=0; ++ ++ if(_ifxhc->nak_countdown_r) ++ { ++ _ifxhc->nak_countdown--; ++ if(!_ifxhc->nak_countdown) ++ { ++ _ifxhc->nak_countdown=_ifxhc->nak_countdown_r; ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ ifxhcd_hc_halt(&_ifxhcd->core_if, _ifxhc, HC_XFER_NAK); ++ } ++ else ++ enable_hc_int(_hc_regs,ack); ++ } ++ else ++ { ++ disable_hc_int(_hc_regs,ack); ++ disable_hc_int(_hc_regs,nak); ++ } ++ return 1; ++} ++ ++/* ++ * Handles a host channel AHB error interrupt. This handler is only called in ++ * DMA mode. ++ */ ++static int32_t handle_hc_ahberr_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "AHB Error--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ ++ ifxhcd_hc_halt(&_ifxhcd->core_if, _ifxhc, HC_XFER_AHB_ERR); ++ return 1; ++} ++ ++/* ++ * Datatoggle ++ */ ++static int32_t handle_hc_datatglerr_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "DATATOGGLE Error--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ disable_hc_int(_hc_regs,datatglerr); ++ return 1; ++} ++ ++ ++ ++/* ++ * Interrupts which should not been triggered ++ */ ++static int32_t handle_hc_frmovrun_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "FrameOverRun Error--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ disable_hc_int(_hc_regs,frmovrun); ++ return 1; ++} ++ ++static int32_t handle_hc_bblerr_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "BBL Error--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ disable_hc_int(_hc_regs,bblerr); ++ return 1; ++} ++ ++static int32_t handle_hc_xacterr_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "XACT Error--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ disable_hc_int(_hc_regs,xacterr); ++ return 1; ++} ++ ++static int32_t handle_hc_nyet_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "NYET--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ _urbd->error_count=0; ++ disable_hc_int(_hc_regs,nyet); ++ return 1; ++} ++ ++static int32_t handle_hc_stall_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "STALL--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ disable_hc_int(_hc_regs,stall); ++ return 1; ++} ++ ++static int32_t handle_hc_xfercomp_intr(ifxhcd_hcd_t *_ifxhcd, ++ ifxhcd_hc_t *_ifxhc, ++ ifxusb_hc_regs_t *_hc_regs, ++ ifxhcd_urbd_t *_urbd) ++{ ++ IFX_ERROR( "--Host Channel %d Interrupt: " ++ "XFERCOMP--\n", _ifxhc->hc_num); ++ hc_other_intr_dump(_ifxhcd,_ifxhc,_hc_regs,_urbd); ++ disable_hc_int(_hc_regs,xfercomp); ++ return 1; ++} ++ ++ ++ ++/* This interrupt indicates that the specified host channels has a pending ++ * interrupt. There are multiple conditions that can cause each host channel ++ * interrupt. This function determines which conditions have occurred for this ++ * host channel interrupt and handles them appropriately. */ ++static int32_t handle_hc_n_intr (ifxhcd_hcd_t *_ifxhcd, uint32_t _num) ++{ ++ uint32_t hcintval,hcintmsk; ++ hcint_data_t hcint; ++ ifxhcd_hc_t *ifxhc; ++ ifxusb_hc_regs_t *hc_regs; ++ ifxhcd_urbd_t *urbd; ++ unsigned long flags; ++ ++ int retval = 0; ++ ++ IFX_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", _num); ++ ++ /*== AVM/BC 20101111 Lock needed ==*/ ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++ ifxhc = &_ifxhcd->ifxhc[_num]; ++ hc_regs = _ifxhcd->core_if.hc_regs[_num]; ++ ++ hcintval = ifxusb_rreg(&hc_regs->hcint); ++ hcintmsk = ifxusb_rreg(&hc_regs->hcintmsk); ++ hcint.d32 = hcintval & hcintmsk; ++ IFX_DEBUGPL(DBG_HCDV, " 0x%08x & 0x%08x = 0x%08x\n", ++ hcintval, hcintmsk, hcint.d32); ++ ++ urbd = list_entry(ifxhc->epqh->urbd_list.next, ifxhcd_urbd_t, urbd_list_entry); ++ ++ if (hcint.b.datatglerr) ++ retval |= handle_hc_datatglerr_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.frmovrun) ++ retval |= handle_hc_frmovrun_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.bblerr) ++ retval |= handle_hc_bblerr_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.xacterr) ++ retval |= handle_hc_xacterr_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.nyet) ++ retval |= handle_hc_nyet_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.ack) ++ retval |= handle_hc_ack_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.nak) ++ retval |= handle_hc_nak_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.stall) ++ retval |= handle_hc_stall_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ if (hcint.b.ahberr) { ++ clear_hc_int(hc_regs, ahberr); ++ retval |= handle_hc_ahberr_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ } ++ if (hcint.b.chhltd) { ++ /* == 20110901 AVM/WK Fix: Flag must not be cleared after restart of channel ==*/ ++ clear_hc_int(hc_regs, chhltd); ++ retval |= handle_hc_chhltd_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ } ++ if (hcint.b.xfercomp) ++ retval |= handle_hc_xfercomp_intr(_ifxhcd, ifxhc, hc_regs, urbd); ++ ++ /* == 20110901 AVM/WK Fix: Never clear possibly new intvals ==*/ ++ //ifxusb_wreg(&hc_regs->hcint,hcintval); ++ ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ ++ return retval; ++} ++ ++ ++ ++ ++ ++ ++static uint8_t update_interval_counter(ifxhcd_epqh_t *_epqh,uint32_t _diff) ++{ ++ if(_diff>=_epqh->period_counter) ++ { ++ _epqh->period_do=1; ++ if(_diff>_epqh->interval) ++ _epqh->period_counter=1; ++ else ++ _epqh->period_counter=_epqh->period_counter+_epqh->interval-_diff; ++ return 1; ++ } ++ _epqh->period_counter=_epqh->period_counter-_diff; ++ return 0; ++} ++ ++ ++ ++ ++/* ++ * Handles the start-of-frame interrupt in host mode. Non-periodic ++ * transactions may be queued to the DWC_otg controller for the current ++ * (micro)frame. Periodic transactions may be queued to the controller for the ++ * next (micro)frame. ++ */ ++static int32_t handle_sof_intr (ifxhcd_hcd_t *_ifxhcd) ++{ ++ #ifdef __DYN_SOF_INTR__ ++ uint8_t with_count_down=0; ++ #endif ++ uint8_t active_on=0; ++ uint8_t ready_on=0; ++ struct list_head *epqh_entry; ++ ifxhcd_epqh_t *epqh; ++ hfnum_data_t hfnum; ++ uint32_t fndiff; ++ ++ unsigned long flags; ++#ifdef __USE_TIMER_4_SOF__ ++ uint32_t wait_for_sof = 0x10000; ++#endif ++ ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++ { ++ int num_channels; ++ ifxusb_hc_regs_t *hc_regs; ++ int i; ++ num_channels = _ifxhcd->core_if.params.host_channels; ++ ++// AVM/WK moved block here due to use of SOF timer ++ hfnum.d32 = ifxusb_rreg(&_ifxhcd->core_if.host_global_regs->hfnum); ++ fndiff = hfnum.b.frnum; ++ fndiff+= 0x00004000; ++ fndiff-= _ifxhcd->lastframe ; ++ fndiff&= 0x00003FFF; ++ if(!fndiff) fndiff =1; ++ ++ for (i = 0; i < num_channels; i++) ++ { ++ if(_ifxhcd->ifxhc[i].wait_for_sof && _ifxhcd->ifxhc[i].xfer_started) ++ { ++#ifdef __USE_TIMER_4_SOF__ ++ if (_ifxhcd->ifxhc[i].wait_for_sof > fndiff) { ++ _ifxhcd->ifxhc[i].wait_for_sof -= fndiff; ++ } else { ++ _ifxhcd->ifxhc[i].wait_for_sof = 0; ++ } ++#else ++ _ifxhcd->ifxhc[i].wait_for_sof--; ++#endif ++ if(_ifxhcd->ifxhc[i].wait_for_sof==0) ++ { ++ hcint_data_t hcint= { .d32=0 }; ++ hc_regs = _ifxhcd->core_if.hc_regs[i]; ++ ++ hcint.d32 =0xFFFFFFFF; ++ ifxusb_wreg(&hc_regs->hcint, hcint.d32); ++ ++ hcint.d32=ifxusb_rreg(&hc_regs->hcintmsk); ++ hcint.b.nak =0; ++ hcint.b.ack =0; ++ /* == 20110901 AVM/WK Fix: We don't need NOT YET IRQ ==*/ ++ hcint.b.nyet=0; ++ _ifxhcd->ifxhc[i].nak_countdown=_ifxhcd->ifxhc[i].nak_countdown_r; ++ if(_ifxhcd->ifxhc[i].nak_countdown_r) ++ hcint.b.nak =1; ++ ifxusb_wreg(&hc_regs->hcintmsk, hcint.d32); ++ ++ /* AVM WK / BC 20100827 ++ * FIX: Packet was ignored because of wrong Oddframe bit ++ */ ++ if (_ifxhcd->ifxhc[i].ep_type == IFXUSB_EP_TYPE_INTR || _ifxhcd->ifxhc[i].ep_type == IFXUSB_EP_TYPE_ISOC) ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = _ifxhcd->ifxhc[i].hcchar; ++ hfnum.d32 = ifxusb_rreg(&_ifxhcd->core_if.host_global_regs->hfnum); ++ /* 1 if _next_ frame is odd, 0 if it's even */ ++ hcchar.b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++ _ifxhcd->ifxhc[i].hcchar = hcchar.d32; ++ } ++ ++ ifxusb_wreg(&hc_regs->hcchar, _ifxhcd->ifxhc[i].hcchar); ++ ++ } ++ } ++ else ++ _ifxhcd->ifxhc[i].wait_for_sof=0; ++ ++#ifdef __USE_TIMER_4_SOF__ ++ if (_ifxhcd->ifxhc[i].wait_for_sof && (wait_for_sof > _ifxhcd->ifxhc[i].wait_for_sof)) { ++ wait_for_sof = _ifxhcd->ifxhc[i].wait_for_sof; ++ } ++#endif ++ } ++ } ++ ++ // ISOC Active ++ #ifdef __EN_ISOC__ ++ #error ISOC not supported: missing SOF code ++ epqh_entry = _ifxhcd->epqh_isoc_active.next; ++ while (epqh_entry != &_ifxhcd->epqh_isoc_active) ++ { ++ epqh = list_entry(epqh_entry, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_entry = epqh_entry->next; ++ #ifdef __DYN_SOF_INTR__ ++ with_count_down=1; ++ #endif ++ active_on+=update_interval_counter(epqh,fndiff); ++ } ++ ++ // ISOC Ready ++ epqh_entry = _ifxhcd->epqh_isoc_ready.next; ++ while (epqh_entry != &_ifxhcd->epqh_isoc_ready) ++ { ++ epqh = list_entry(epqh_entry, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_entry = epqh_entry->next; ++ #ifdef __DYN_SOF_INTR__ ++ with_count_down=1; ++ #endif ++ ready_on+=update_interval_counter(epqh,fndiff); ++ } ++ #endif ++ ++ // INTR Active ++ epqh_entry = _ifxhcd->epqh_intr_active.next; ++ while (epqh_entry != &_ifxhcd->epqh_intr_active) ++ { ++ epqh = list_entry(epqh_entry, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_entry = epqh_entry->next; ++ #ifdef __DYN_SOF_INTR__ ++ with_count_down=1; ++ #endif ++#ifdef __USE_TIMER_4_SOF__ ++ if (update_interval_counter(epqh,fndiff)) { ++ active_on ++; ++ wait_for_sof = 1; ++ } else { ++ if (epqh->period_counter && (wait_for_sof > epqh->period_counter)) { ++ wait_for_sof = epqh->period_counter; ++ } ++ } ++#else ++ active_on+=update_interval_counter(epqh,fndiff); ++#endif ++ } ++ ++ // INTR Ready ++ epqh_entry = _ifxhcd->epqh_intr_ready.next; ++ while (epqh_entry != &_ifxhcd->epqh_intr_ready) ++ { ++ epqh = list_entry(epqh_entry, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_entry = epqh_entry->next; ++ #ifdef __DYN_SOF_INTR__ ++ with_count_down=1; ++ #endif ++#ifdef __USE_TIMER_4_SOF__ ++ if (update_interval_counter(epqh,fndiff)) { ++ ready_on ++; ++ wait_for_sof = 1; ++ } else { ++ if (epqh->period_counter && (wait_for_sof > epqh->period_counter)) { ++ wait_for_sof = epqh->period_counter; ++ } ++ } ++#else ++ ready_on+=update_interval_counter(epqh,fndiff); ++#endif ++ } ++ ++ // Stdby ++ epqh_entry = _ifxhcd->epqh_stdby.next; ++ while (epqh_entry != &_ifxhcd->epqh_stdby) ++ { ++ epqh = list_entry(epqh_entry, ifxhcd_epqh_t, epqh_list_entry); ++ epqh_entry = epqh_entry->next; ++ if(epqh->period_counter > 0 ) { ++#ifdef __USE_TIMER_4_SOF__ ++ if (epqh->period_counter > fndiff) { ++ epqh->period_counter -= fndiff; ++ } else { ++ epqh->period_counter = 0; ++ } ++#else ++ epqh->period_counter --; ++#endif ++ #ifdef __DYN_SOF_INTR__ ++ with_count_down=1; ++ #endif ++ } ++ if(epqh->period_counter == 0) { ++ ifxhcd_epqh_idle_periodic(epqh); ++ } ++#ifdef __USE_TIMER_4_SOF__ ++ else { ++ if (wait_for_sof > epqh->period_counter) { ++ wait_for_sof = epqh->period_counter; ++ } ++ } ++#endif ++ } ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ ++ if(ready_on) ++ select_eps(_ifxhcd); ++ else if(active_on) ++ process_channels(_ifxhcd); ++ ++ /* Clear interrupt */ ++ { ++ gint_data_t gintsts; ++ gintsts.d32=0; ++ gintsts.b.sofintr = 1; ++ ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); ++ ++ #ifdef __DYN_SOF_INTR__ ++ if(!with_count_down) ++ ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk, gintsts.d32,0); ++ #endif ++#ifdef __USE_TIMER_4_SOF__ ++ wait_for_sof &= 0xFFFF; // reduce to 16 Bits. ++ ++ if(wait_for_sof == 1) { ++ // enable SOF ++ gint_data_t gintsts; ++ gintsts.d32=0; ++ gintsts.b.sofintr = 1; ++ ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk, 0,gintsts.d32); ++ } else { ++ // disable SOF ++ ifxusb_mreg(&_ifxhcd->core_if.core_global_regs->gintmsk, gintsts.d32,0); ++ if (wait_for_sof > 1) { ++ // use timer, not SOF IRQ ++ hprt0_data_t hprt0; ++ ktime_t ktime; ++ hprt0.d32 = ifxusb_read_hprt0 (&_ifxhcd->core_if); ++ if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED) { ++ ktime = ktime_set(0, wait_for_sof * 125 * 1000); /*--- wakeup in n*125usec ---*/ ++ } else { ++ ktime = ktime_set(0, wait_for_sof * (1000*1000)); /*--- wakeup in n*1000usec ---*/ ++ } ++ hrtimer_start(&_ifxhcd->hr_timer, ktime, HRTIMER_MODE_REL); ++ } ++ } ++#endif ++ } ++ _ifxhcd->lastframe=hfnum.b.frnum; ++ return 1; ++} ++ ++ ++ ++/* There are multiple conditions that can cause a port interrupt. This function ++ * determines which interrupt conditions have occurred and handles them ++ * appropriately. */ ++static int32_t handle_port_intr (ifxhcd_hcd_t *_ifxhcd) ++{ ++ int retval = 0; ++ hprt0_data_t hprt0; ++ hprt0_data_t hprt0_modify; ++ ++ hprt0.d32 = ++ hprt0_modify.d32 = ifxusb_rreg(_ifxhcd->core_if.hprt0); ++ ++ /* Clear appropriate bits in HPRT0 to clear the interrupt bit in ++ * GINTSTS */ ++ ++ hprt0_modify.b.prtena = 0; ++ hprt0_modify.b.prtconndet = 0; ++ hprt0_modify.b.prtenchng = 0; ++ hprt0_modify.b.prtovrcurrchng = 0; ++ ++ /* Port Connect Detected ++ * Set flag and clear if detected */ ++ if (hprt0.b.prtconndet) { ++ IFX_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x " ++ "Port Connect Detected--\n", hprt0.d32); ++ _ifxhcd->flags.b.port_connect_status_change = 1; ++ _ifxhcd->flags.b.port_connect_status = 1; ++ hprt0_modify.b.prtconndet = 1; ++ ++ /* The Hub driver asserts a reset when it sees port connect ++ * status change flag */ ++ retval |= 1; ++ } ++ ++ /* Port Enable Changed ++ * Clear if detected - Set internal flag if disabled */ ++ if (hprt0.b.prtenchng) { ++ ++ IFX_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Enable Changed--\n", hprt0.d32); ++ hprt0_modify.b.prtenchng = 1; ++ if (hprt0.b.prtena == 1) ++ /* Port has been enabled set the reset change flag */ ++ _ifxhcd->flags.b.port_reset_change = 1; ++ else ++ _ifxhcd->flags.b.port_enable_change = 1; ++ retval |= 1; ++ } ++ ++ /* Overcurrent Change Interrupt */ ++ ++ if (hprt0.b.prtovrcurrchng) { ++ IFX_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Overcurrent Changed--\n", hprt0.d32); ++ _ifxhcd->flags.b.port_over_current_change = 1; ++ hprt0_modify.b.prtovrcurrchng = 1; ++ retval |= 1; ++ } ++ ++ /* Clear Port Interrupts */ ++ ifxusb_wreg(_ifxhcd->core_if.hprt0, hprt0_modify.d32); ++ return retval; ++} ++ ++/* ++ * This interrupt indicates that SUSPEND state has been detected on ++ * the USB. ++ * No Functioning in Host Mode ++ */ ++static int32_t handle_usb_suspend_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ gint_data_t gintsts; ++ IFX_DEBUGP("USB SUSPEND RECEIVED!\n"); ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/* ++ * This interrupt indicates that the IFXUSB controller has detected a ++ * resume or remote wakeup sequence. If the IFXUSB controller is in ++ * low power mode, the handler must brings the controller out of low ++ * power mode. The controller automatically begins resume ++ * signaling. The handler schedules a time to stop resume signaling. ++ */ ++static int32_t handle_wakeup_detected_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ gint_data_t gintsts; ++ hprt0_data_t hprt0 = {.d32=0}; ++ pcgcctl_data_t pcgcctl = {.d32=0}; ++ ifxusb_core_if_t *core_if = &_ifxhcd->core_if; ++ ++ IFX_DEBUGPL(DBG_ANY, "++Resume and Remote Wakeup Detected Interrupt++\n"); ++ ++ /* ++ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms ++ * so that OPT tests pass with all PHYs). ++ */ ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ ifxusb_mreg(core_if->pcgcctl, pcgcctl.d32, 0); ++ UDELAY(10); ++ ++ /* Now wait for 70 ms. */ ++ hprt0.d32 = ifxusb_read_hprt0( core_if ); ++ IFX_DEBUGPL(DBG_ANY,"Resume: HPRT0=%0x\n", hprt0.d32); ++ MDELAY(70); ++ hprt0.b.prtres = 0; /* Resume */ ++ ifxusb_wreg(core_if->hprt0, hprt0.d32); ++ IFX_DEBUGPL(DBG_ANY,"Clear Resume: HPRT0=%0x\n", ifxusb_rreg(core_if->hprt0)); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.wkupintr = 1; ++ ifxusb_wreg(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/* ++ * This interrupt indicates that a device is initiating the Session ++ * Request Protocol to request the host to turn on bus power so a new ++ * session can begin. The handler responds by turning on bus power. If ++ * the DWC_otg controller is in low power mode, the handler brings the ++ * controller out of low power mode before turning on bus power. ++ */ ++static int32_t handle_session_req_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ /* Clear interrupt */ ++ gint_data_t gintsts = { .d32 = 0 }; ++ gintsts.b.sessreqintr = 1; ++ ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/* ++ * This interrupt indicates that a device has been disconnected from ++ * the root port. ++ */ ++static int32_t handle_disconnect_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ gint_data_t gintsts; ++ ++ ifxhcd_disconnect(_ifxhcd); ++ ++ gintsts.d32 = 0; ++ gintsts.b.disconnect = 1; ++ ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/* ++ * This function handles the Connector ID Status Change Interrupt. It ++ * reads the OTG Interrupt Register (GOTCTL) to determine whether this ++ * is a Device to Host Mode transition or a Host Mode to Device ++ * Transition. ++ * This only occurs when the cable is connected/removed from the PHY ++ * connector. ++ */ ++static int32_t handle_conn_id_status_change_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ gint_data_t gintsts; ++ ++ IFX_WARN("ID Status Change Interrupt: currently in %s mode\n", ++ ifxusb_mode(&_ifxhcd->core_if) ? "Host" : "Device"); ++ ++ gintsts.d32 = 0; ++ gintsts.b.conidstschng = 1; ++ ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++static int32_t handle_otg_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ ifxusb_core_global_regs_t *global_regs = _ifxhcd->core_if.core_global_regs; ++ gotgint_data_t gotgint; ++ gotgint.d32 = ifxusb_rreg( &global_regs->gotgint); ++ /* Clear GOTGINT */ ++ ifxusb_wreg (&global_regs->gotgint, gotgint.d32); ++ return 1; ++} ++ ++/** This function will log a debug message */ ++static int32_t handle_mode_mismatch_intr(ifxhcd_hcd_t *_ifxhcd) ++{ ++ gint_data_t gintsts; ++ ++ IFX_WARN("Mode Mismatch Interrupt: currently in %s mode\n", ++ ifxusb_mode(&_ifxhcd->core_if) ? "Host" : "Device"); ++ gintsts.d32 = 0; ++ gintsts.b.modemismatch = 1; ++ ifxusb_wreg(&_ifxhcd->core_if.core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** This function handles interrupts for the HCD. */ ++int32_t ifxhcd_handle_intr (ifxhcd_hcd_t *_ifxhcd) ++{ ++ int retval = 0; ++ ++ ifxusb_core_if_t *core_if = &_ifxhcd->core_if; ++ /* AVM/BC 20101111 Unnecesary variable removed*/ ++ //gint_data_t gintsts,gintsts2; ++ gint_data_t gintsts; ++ ++ /* Check if HOST Mode */ ++ if (ifxusb_is_device_mode(core_if)) ++ { ++ IFX_ERROR("%s() CRITICAL! IN DEVICE MODE\n", __func__); ++ return 0; ++ } ++ ++ gintsts.d32 = ifxusb_read_core_intr(core_if); ++ ++ if (!gintsts.d32) ++ return 0; ++ ++ //Common INT ++ if (gintsts.b.modemismatch) ++ { ++ retval |= handle_mode_mismatch_intr(_ifxhcd); ++ gintsts.b.modemismatch=0; ++ } ++ if (gintsts.b.otgintr) ++ { ++ retval |= handle_otg_intr(_ifxhcd); ++ gintsts.b.otgintr=0; ++ } ++ if (gintsts.b.conidstschng) ++ { ++ retval |= handle_conn_id_status_change_intr(_ifxhcd); ++ gintsts.b.conidstschng=0; ++ } ++ if (gintsts.b.disconnect) ++ { ++ retval |= handle_disconnect_intr(_ifxhcd); ++ gintsts.b.disconnect=0; ++ } ++ if (gintsts.b.sessreqintr) ++ { ++ retval |= handle_session_req_intr(_ifxhcd); ++ gintsts.b.sessreqintr=0; ++ } ++ if (gintsts.b.wkupintr) ++ { ++ retval |= handle_wakeup_detected_intr(_ifxhcd); ++ gintsts.b.wkupintr=0; ++ } ++ if (gintsts.b.usbsuspend) ++ { ++ retval |= handle_usb_suspend_intr(_ifxhcd); ++ gintsts.b.usbsuspend=0; ++ } ++ ++ //Host Int ++ if (gintsts.b.sofintr) ++ { ++ retval |= handle_sof_intr (_ifxhcd); ++ gintsts.b.sofintr=0; ++ } ++ if (gintsts.b.portintr) ++ { ++ retval |= handle_port_intr (_ifxhcd); ++ gintsts.b.portintr=0; ++ } ++ if (gintsts.b.hcintr) ++ { ++ int i; ++ haint_data_t haint; ++ haint.d32 = ifxusb_read_host_all_channels_intr(core_if); ++ for (i=0; i< core_if->params.host_channels; i++) ++ if (haint.b2.chint & (1 << i)) ++ retval |= handle_hc_n_intr (_ifxhcd, i); ++ gintsts.b.hcintr=0; ++ } ++ return retval; ++} +diff --git a/drivers/usb/ifxhcd/ifxhcd_queue.c b/drivers/usb/ifxhcd/ifxhcd_queue.c +new file mode 100644 +index 0000000..8f9dd25 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxhcd_queue.c +@@ -0,0 +1,418 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxhcd_queue.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : This file contains the functions to manage Queue Heads and Queue ++ ** Transfer Descriptors. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxhcd_queue.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the functions to manage Queue Heads and Queue ++ Transfer Descriptors. ++*/ ++#include ++#include "ifxusb_version.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++#include "ifxhcd.h" ++ ++#ifdef __EPQD_DESTROY_TIMEOUT__ ++ #define epqh_self_destroy_timeout 5 ++ static void eqph_destroy_func(unsigned long _ptr) ++ { ++ ifxhcd_epqh_t *epqh=(ifxhcd_epqh_t *)_ptr; ++ if(epqh) ++ { ++ ifxhcd_epqh_free (epqh); ++ } ++ } ++#endif ++ ++#define SCHEDULE_SLOP 10 ++ ++/*! ++ \brief This function allocates and initializes a EPQH. ++ ++ \param _ifxhcd The HCD state structure for the USB Host controller. ++ \param[in] _urb Holds the information about the device/endpoint that we need ++ to initialize the EPQH. ++ ++ \return Returns pointer to the newly allocated EPQH, or NULL on error. ++ */ ++ifxhcd_epqh_t *ifxhcd_epqh_create (ifxhcd_hcd_t *_ifxhcd, struct urb *_urb) ++{ ++ ifxhcd_epqh_t *epqh; ++ ++ hprt0_data_t hprt0; ++ struct usb_host_endpoint *sysep = ifxhcd_urb_to_endpoint(_urb); ++ ++ /* Allocate memory */ ++// epqh=(ifxhcd_epqh_t *) kmalloc (sizeof(ifxhcd_epqh_t), GFP_KERNEL); ++ epqh=(ifxhcd_epqh_t *) kmalloc (sizeof(ifxhcd_epqh_t), GFP_ATOMIC); ++ ++ if(epqh == NULL) ++ return NULL; ++ ++ memset (epqh, 0, sizeof (ifxhcd_epqh_t)); ++ ++ epqh->sysep=sysep; ++ ++ /* Initialize EPQH */ ++ switch (usb_pipetype(_urb->pipe)) ++ { ++ case PIPE_CONTROL : epqh->ep_type = IFXUSB_EP_TYPE_CTRL; break; ++ case PIPE_BULK : epqh->ep_type = IFXUSB_EP_TYPE_BULK; break; ++ case PIPE_ISOCHRONOUS: epqh->ep_type = IFXUSB_EP_TYPE_ISOC; break; ++ case PIPE_INTERRUPT : epqh->ep_type = IFXUSB_EP_TYPE_INTR; break; ++ } ++ ++ //epqh->data_toggle = IFXUSB_HC_PID_DATA0; ++ ++ epqh->mps = usb_maxpacket(_urb->dev, _urb->pipe, !(usb_pipein(_urb->pipe))); ++ ++ hprt0.d32 = ifxusb_read_hprt0 (&_ifxhcd->core_if); ++ ++ INIT_LIST_HEAD(&epqh->urbd_list); ++ INIT_LIST_HEAD(&epqh->epqh_list_entry); ++ epqh->hc = NULL; ++ ++ epqh->dump_buf = ifxusb_alloc_buf(epqh->mps, 0); ++ ++ /* FS/LS Enpoint on HS Hub ++ * NOT virtual root hub */ ++ epqh->need_split = 0; ++ epqh->pkt_count_limit=0; ++ if(epqh->ep_type == IFXUSB_EP_TYPE_BULK && !(usb_pipein(_urb->pipe)) ) ++ epqh->pkt_count_limit=4; ++ if (hprt0.b.prtspd == IFXUSB_HPRT0_PRTSPD_HIGH_SPEED && ++ ((_urb->dev->speed == USB_SPEED_LOW) || ++ (_urb->dev->speed == USB_SPEED_FULL)) && ++ (_urb->dev->tt) && (_urb->dev->tt->hub->devnum != 1)) ++ { ++ IFX_DEBUGPL(DBG_HCD, "QH init: EP %d: TT found at hub addr %d, for port %d\n", ++ usb_pipeendpoint(_urb->pipe), _urb->dev->tt->hub->devnum, ++ _urb->dev->ttport); ++ epqh->need_split = 1; ++ epqh->pkt_count_limit=1; ++ } ++ ++ if (epqh->ep_type == IFXUSB_EP_TYPE_INTR || ++ epqh->ep_type == IFXUSB_EP_TYPE_ISOC) ++ { ++ /* Compute scheduling parameters once and save them. */ ++ epqh->interval = _urb->interval; ++ if(epqh->need_split) ++ epqh->interval *= 8; ++ } ++ ++ epqh->period_counter=0; ++ epqh->is_active=0; ++ ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ /* Start a timer for this transfer. */ ++ init_timer(&epqh->destroy_timer); ++ epqh->destroy_timer.function = eqph_destroy_func; ++ epqh->destroy_timer.data = (unsigned long)(epqh); ++ #endif ++ ++ #ifdef __DEBUG__ ++ IFX_DEBUGPL(DBG_HCD , "IFXUSB HCD EPQH Initialized\n"); ++ IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - epqh = %p\n", epqh); ++ IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - Device Address = %d EP %d, %s\n", ++ _urb->dev->devnum, ++ usb_pipeendpoint(_urb->pipe), ++ usb_pipein(_urb->pipe) == USB_DIR_IN ? "IN" : "OUT"); ++ IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - Speed = %s\n", ++ ({ char *speed; switch (_urb->dev->speed) { ++ case USB_SPEED_LOW: speed = "low" ; break; ++ case USB_SPEED_FULL: speed = "full"; break; ++ case USB_SPEED_HIGH: speed = "high"; break; ++ default: speed = "?"; break; ++ }; speed;})); ++ IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - Type = %s\n", ++ ({ ++ char *type; switch (epqh->ep_type) ++ { ++ case IFXUSB_EP_TYPE_ISOC: type = "isochronous"; break; ++ case IFXUSB_EP_TYPE_INTR: type = "interrupt" ; break; ++ case IFXUSB_EP_TYPE_CTRL: type = "control" ; break; ++ case IFXUSB_EP_TYPE_BULK: type = "bulk" ; break; ++ default: type = "?"; break; ++ }; ++ type; ++ })); ++ if (epqh->ep_type == IFXUSB_EP_TYPE_INTR) ++ IFX_DEBUGPL(DBG_HCDV, "IFXUSB HCD EPQH - interval = %d\n", epqh->interval); ++ #endif ++ ++ return epqh; ++} ++ ++ ++ ++ ++ ++ ++/*! ++ \brief Free the EPQH. EPQH should already be removed from a list. ++ URBD list should already be empty if called from URB Dequeue. ++ ++ \param[in] _epqh The EPQH to free. ++ */ ++void ifxhcd_epqh_free (ifxhcd_epqh_t *_epqh) ++{ ++ unsigned long flags; ++ ++ if(_epqh->sysep) _epqh->sysep->hcpriv=NULL; ++ _epqh->sysep=NULL; ++ ++ if(!_epqh) ++ return; ++ ++ /* Free each QTD in the QTD list */ ++ local_irq_save (flags); ++ if (!list_empty(&_epqh->urbd_list)) ++ IFX_WARN("%s() invalid epqh state\n",__func__); ++ ++ #if defined(__UNALIGNED_BUFFER_ADJ__) ++ if(_epqh->aligned_buf) ++ ifxusb_free_buf(_epqh->aligned_buf); ++ if(_epqh->aligned_setup) ++ ifxusb_free_buf(_epqh->aligned_setup); ++ #endif ++ ++ if (!list_empty(&_epqh->epqh_list_entry)) ++ list_del_init(&_epqh->epqh_list_entry); ++ ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ del_timer(&_epqh->destroy_timer); ++ #endif ++ if(_epqh->dump_buf) ++ ifxusb_free_buf(_epqh->dump_buf); ++ _epqh->dump_buf=0; ++ ++ ++ kfree (_epqh); ++ local_irq_restore (flags); ++} ++ ++/*! ++ \brief This function adds a EPQH to ++ ++ \return 0 if successful, negative error code otherwise. ++ */ ++void ifxhcd_epqh_ready(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ if (list_empty(&_epqh->epqh_list_entry)) ++ { ++ #ifdef __EN_ISOC__ ++ if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) ++ list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready); ++ else ++ #endif ++ if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) ++ list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready); ++ else ++ list_add_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready); ++ _epqh->is_active=0; ++ } ++ else if(!_epqh->is_active) ++ { ++ #ifdef __EN_ISOC__ ++ if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready); ++ else ++ #endif ++ if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready); ++ else ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready); ++ } ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ del_timer(&_epqh->destroy_timer); ++ #endif ++ local_irq_restore(flags); ++} ++ ++void ifxhcd_epqh_active(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ if (list_empty(&_epqh->epqh_list_entry)) ++ IFX_WARN("%s() invalid epqh state\n",__func__); ++ #ifdef __EN_ISOC__ ++ if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_active); ++ else ++ #endif ++ if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_active); ++ else ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_active); ++ _epqh->is_active=1; ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ del_timer(&_epqh->destroy_timer); ++ #endif ++ local_irq_restore(flags); ++} ++ ++void ifxhcd_epqh_idle(ifxhcd_hcd_t *_ifxhcd, ifxhcd_epqh_t *_epqh) ++{ ++ unsigned long flags; ++ local_irq_save(flags); ++ ++ if (list_empty(&_epqh->urbd_list)) ++ { ++ if(_epqh->ep_type == IFXUSB_EP_TYPE_ISOC || _epqh->ep_type == IFXUSB_EP_TYPE_INTR) ++ { ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_stdby); ++ } ++ else ++ { ++ list_del_init(&_epqh->epqh_list_entry); ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ del_timer(&_epqh->destroy_timer); ++ _epqh->destroy_timer.expires = jiffies + (HZ*epqh_self_destroy_timeout); ++ add_timer(&_epqh->destroy_timer ); ++ #endif ++ } ++ } ++ else ++ { ++ #ifdef __EN_ISOC__ ++ if (_epqh->ep_type == IFXUSB_EP_TYPE_ISOC) ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_isoc_ready); ++ else ++ #endif ++ if(_epqh->ep_type == IFXUSB_EP_TYPE_INTR) ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_intr_ready); ++ else ++ list_move_tail(&_epqh->epqh_list_entry, &_ifxhcd->epqh_np_ready); ++ } ++ _epqh->is_active=0; ++ local_irq_restore(flags); ++} ++ ++ ++void ifxhcd_epqh_idle_periodic(ifxhcd_epqh_t *_epqh) ++{ ++ unsigned long flags; ++ if(_epqh->ep_type != IFXUSB_EP_TYPE_ISOC && _epqh->ep_type != IFXUSB_EP_TYPE_INTR) ++ return; ++ ++ local_irq_save(flags); ++ ++ if (list_empty(&_epqh->epqh_list_entry)) ++ IFX_WARN("%s() invalid epqh state\n",__func__); ++ if (!list_empty(&_epqh->urbd_list)) ++ IFX_WARN("%s() invalid epqh state(not empty)\n",__func__); ++ ++ _epqh->is_active=0; ++ list_del_init(&_epqh->epqh_list_entry); ++ #ifdef __EPQD_DESTROY_TIMEOUT__ ++ del_timer(&_epqh->destroy_timer); ++ _epqh->destroy_timer.expires = jiffies + (HZ*epqh_self_destroy_timeout); ++ add_timer(&_epqh->destroy_timer ); ++ #endif ++ ++ local_irq_restore(flags); ++} ++ ++ ++int ifxhcd_urbd_create (ifxhcd_hcd_t *_ifxhcd,struct urb *_urb) ++{ ++ ifxhcd_urbd_t *urbd; ++ struct usb_host_endpoint *sysep; ++ ifxhcd_epqh_t *epqh; ++ unsigned long flags; ++ /* == AVM/WK 20100714 retval correctly initialized ==*/ ++ int retval = -ENOMEM; ++ ++ /*== AVM/BC 20100630 - Spinlock ==*/ ++ //local_irq_save(flags); ++ SPIN_LOCK_IRQSAVE(&_ifxhcd->lock, flags); ++ ++// urbd = (ifxhcd_urbd_t *) kmalloc (sizeof(ifxhcd_urbd_t), GFP_KERNEL); ++ urbd = (ifxhcd_urbd_t *) kmalloc (sizeof(ifxhcd_urbd_t), GFP_ATOMIC); ++ if (urbd != NULL) /* Initializes a QTD structure.*/ ++ { ++ retval = 0; ++ memset (urbd, 0, sizeof (ifxhcd_urbd_t)); ++ ++ sysep = ifxhcd_urb_to_endpoint(_urb); ++ epqh = (ifxhcd_epqh_t *)sysep->hcpriv; ++ if (epqh == NULL) ++ { ++ epqh = ifxhcd_epqh_create (_ifxhcd, _urb); ++ if (epqh == NULL) ++ { ++ retval = -ENOSPC; ++ kfree(urbd); ++ //local_irq_restore (flags); ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ return retval; ++ } ++ sysep->hcpriv = epqh; ++ } ++ ++ INIT_LIST_HEAD(&urbd->urbd_list_entry); ++ ++ /*== AVM/BC 20100630 - 2.6.28 needs HCD link/unlink URBs ==*/ ++ retval = usb_hcd_link_urb_to_ep(ifxhcd_to_syshcd(_ifxhcd), _urb); ++ ++ if (unlikely(retval)){ ++ kfree(urbd); ++ kfree(epqh); ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ return retval; ++ } ++ ++ list_add_tail(&urbd->urbd_list_entry, &epqh->urbd_list); ++ urbd->urb = _urb; ++ _urb->hcpriv = urbd; ++ ++ urbd->epqh=epqh; ++ urbd->is_in=usb_pipein(_urb->pipe) ? 1 : 0;; ++ ++ urbd->xfer_len=_urb->transfer_buffer_length; ++#define URB_NO_SETUP_DMA_MAP 0 ++ ++ if(urbd->xfer_len>0) ++ { ++ if(_urb->transfer_flags && URB_NO_TRANSFER_DMA_MAP) ++ urbd->xfer_buff = (uint8_t *) (KSEG1ADDR((uint32_t *)_urb->transfer_dma)); ++ else ++ urbd->xfer_buff = (uint8_t *) _urb->transfer_buffer; ++ } ++ if(epqh->ep_type == IFXUSB_EP_TYPE_CTRL) ++ { ++ if(_urb->transfer_flags && URB_NO_SETUP_DMA_MAP) ++ urbd->setup_buff = (uint8_t *) (KSEG1ADDR((uint32_t *)_urb->setup_dma)); ++ else ++ urbd->setup_buff = (uint8_t *) _urb->setup_packet; ++ } ++ } ++ //local_irq_restore (flags); ++ SPIN_UNLOCK_IRQRESTORE(&_ifxhcd->lock, flags); ++ return retval; ++} ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_cif.c b/drivers/usb/ifxhcd/ifxusb_cif.c +new file mode 100644 +index 0000000..10b1292 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_cif.c +@@ -0,0 +1,1458 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_cif.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : The Core Interface provides basic services for accessing and ++ ** managing the IFX USB hardware. These services are used by both the ++ ** Host Controller Driver and the Peripheral Controller Driver. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxusb_cif.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the interface to the IFX USB Core. ++*/ ++ ++#include ++#include ++#include "ifxusb_version.h" ++ ++#include ++#include ++ ++ ++#include ++#include ++#include ++#include ++ ++#if defined(__UEIP__) ++// #include ++// #include ++#endif ++ ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++ ++ ++#ifdef __IS_DEVICE__ ++ #include "ifxpcd.h" ++#endif ++ ++#ifdef __IS_HOST__ ++ #include "ifxhcd.h" ++#endif ++ ++#include ++ ++#include ++ ++#if defined(__UEIP__) ++// #include ++ //#include ++#endif ++ ++//#include ++//#include ++#if defined(__UEIP__) ++// #include ++ //#include ++#endif ++ ++ ++ ++#if defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ #ifndef USB_CTRL_PMU_SETUP ++ #define USB_CTRL_PMU_SETUP(__x) USB0_CTRL_PMU_SETUP(__x) ++ #endif ++ #ifndef USB_PHY_PMU_SETUP ++ #define USB_PHY_PMU_SETUP(__x) USB0_PHY_PMU_SETUP(__x) ++ #endif ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++#endif // defined(__UEIP__) ++ ++/*! ++ \brief This function is called to allocate buffer of specified size. ++ The allocated buffer is mapped into DMA accessable address. ++ \param size Size in BYTE to be allocated ++ \param clear 0: don't do clear after buffer allocated, other: do clear to zero ++ \return 0/NULL: Fail; uncached pointer of allocated buffer ++ */ ++void *ifxusb_alloc_buf(size_t size, int clear) ++{ ++ uint32_t *cached,*uncached; ++ uint32_t totalsize,page; ++ ++ if(!size) ++ return 0; ++ ++ size=(size+3)&0xFFFFFFFC; ++ totalsize=size + 12; ++ page=get_order(totalsize); ++ ++ cached = (void *) __get_free_pages(( GFP_ATOMIC | GFP_DMA), page); ++ ++ if(!cached) ++ { ++ IFX_PRINT("%s Allocation Failed size:%d\n",__func__,size); ++ return NULL; ++ } ++ ++ uncached = (uint32_t *)(KSEG1ADDR(cached)); ++ if(clear) ++ memset(uncached, 0, totalsize); ++ ++ *(uncached+0)=totalsize; ++ *(uncached+1)=page; ++ *(uncached+2)=(uint32_t)cached; ++ return (void *)(uncached+3); ++} ++ ++ ++/*! ++ \brief This function is called to free allocated buffer. ++ \param vaddr the uncached pointer of the buffer ++ */ ++void ifxusb_free_buf(void *vaddr) ++{ ++ uint32_t totalsize,page; ++ uint32_t *cached,*uncached; ++ ++ if(vaddr != NULL) ++ { ++ uncached=vaddr; ++ uncached-=3; ++ totalsize=*(uncached+0); ++ page=*(uncached+1); ++ cached=(uint32_t *)(*(uncached+2)); ++ if(totalsize && page==get_order(totalsize) && cached==(uint32_t *)(KSEG0ADDR(uncached))) ++ { ++ free_pages((unsigned long)cached, page); ++ return; ++ } ++ // the memory is not allocated by ifxusb_alloc_buf. Allowed but must be careful. ++ return; ++ } ++} ++ ++ ++ ++/*! ++ \brief This function is called to initialize the IFXUSB CSR data ++ structures. The register addresses in the device and host ++ structures are initialized from the base address supplied by the ++ caller. The calling function must make the OS calls to get the ++ base address of the IFXUSB controller registers. ++ ++ \param _core_if Pointer of core_if structure ++ \param _irq irq number ++ \param _reg_base_addr Base address of IFXUSB core registers ++ \param _fifo_base_addr Fifo base address ++ \param _fifo_dbg_addr Fifo debug address ++ \return 0: success; ++ */ ++int ifxusb_core_if_init(ifxusb_core_if_t *_core_if, ++ int _irq, ++ uint32_t _reg_base_addr, ++ uint32_t _fifo_base_addr, ++ uint32_t _fifo_dbg_addr) ++{ ++ int retval = 0; ++ uint32_t *reg_base =NULL; ++ uint32_t *fifo_base =NULL; ++ uint32_t *fifo_dbg =NULL; ++ ++ int i; ++ ++ IFX_DEBUGPL(DBG_CILV, "%s(%p,%d,0x%08X,0x%08X,0x%08X)\n", __func__, ++ _core_if, ++ _irq, ++ _reg_base_addr, ++ _fifo_base_addr, ++ _fifo_dbg_addr); ++ ++ if( _core_if == NULL) ++ { ++ IFX_ERROR("%s() invalid _core_if\n", __func__); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ //memset(_core_if, 0, sizeof(ifxusb_core_if_t)); ++ ++ _core_if->irq=_irq; ++ ++ reg_base =ioremap_nocache(_reg_base_addr , IFXUSB_IOMEM_SIZE ); ++ fifo_base =ioremap_nocache(_fifo_base_addr, IFXUSB_FIFOMEM_SIZE); ++ fifo_dbg =ioremap_nocache(_fifo_dbg_addr , IFXUSB_FIFODBG_SIZE); ++ if( reg_base == NULL || fifo_base == NULL || fifo_dbg == NULL) ++ { ++ IFX_ERROR("%s() usb ioremap() failed\n", __func__); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ _core_if->core_global_regs = (ifxusb_core_global_regs_t *)reg_base; ++ ++ /* ++ * Attempt to ensure this device is really a IFXUSB Controller. ++ * Read and verify the SNPSID register contents. The value should be ++ * 0x45F42XXX ++ */ ++ { ++ int32_t snpsid; ++ snpsid = ifxusb_rreg(&_core_if->core_global_regs->gsnpsid); ++ if ((snpsid & 0xFFFFF000) != 0x4F542000) ++ { ++ IFX_ERROR("%s() snpsid error(0x%08x) failed\n", __func__,snpsid); ++ retval = -EINVAL; ++ goto fail; ++ } ++ _core_if->snpsid=snpsid; ++ } ++ ++ #ifdef __IS_HOST__ ++ _core_if->host_global_regs = (ifxusb_host_global_regs_t *) ++ ((uint32_t)reg_base + IFXUSB_HOST_GLOBAL_REG_OFFSET); ++ _core_if->hprt0 = (uint32_t*)((uint32_t)reg_base + IFXUSB_HOST_PORT_REGS_OFFSET); ++ ++ for (i=0; ihc_regs[i] = (ifxusb_hc_regs_t *) ++ ((uint32_t)reg_base + IFXUSB_HOST_CHAN_REGS_OFFSET + ++ (i * IFXUSB_CHAN_REGS_OFFSET)); ++ IFX_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n", ++ i, &_core_if->hc_regs[i]->hcchar); ++ } ++ #endif //__IS_HOST__ ++ ++ #ifdef __IS_DEVICE__ ++ _core_if->dev_global_regs = ++ (ifxusb_device_global_regs_t *)((uint32_t)reg_base + IFXUSB_DEV_GLOBAL_REG_OFFSET); ++ ++ for (i=0; iin_ep_regs[i] = (ifxusb_dev_in_ep_regs_t *) ++ ((uint32_t)reg_base + IFXUSB_DEV_IN_EP_REG_OFFSET + ++ (i * IFXUSB_EP_REG_OFFSET)); ++ _core_if->out_ep_regs[i] = (ifxusb_dev_out_ep_regs_t *) ++ ((uint32_t)reg_base + IFXUSB_DEV_OUT_EP_REG_OFFSET + ++ (i * IFXUSB_EP_REG_OFFSET)); ++ IFX_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p/%p %p/0x%08X/0x%08X\n", ++ i, &_core_if->in_ep_regs[i]->diepctl, _core_if->in_ep_regs[i], ++ reg_base,IFXUSB_DEV_IN_EP_REG_OFFSET,(i * IFXUSB_EP_REG_OFFSET) ++ ); ++ IFX_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p/%p %p/0x%08X/0x%08X\n", ++ i, &_core_if->out_ep_regs[i]->doepctl, _core_if->out_ep_regs[i], ++ reg_base,IFXUSB_DEV_OUT_EP_REG_OFFSET,(i * IFXUSB_EP_REG_OFFSET) ++ ); ++ } ++ #endif //__IS_DEVICE__ ++ ++ /* Setting the FIFO and other Address. */ ++ for (i=0; idata_fifo[i] = fifo_base + (i * IFXUSB_DATA_FIFO_SIZE); ++ IFX_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08x\n", ++ i, (unsigned)_core_if->data_fifo[i]); ++ } ++ ++ _core_if->data_fifo_dbg = fifo_dbg; ++ _core_if->pcgcctl = (uint32_t*)(((uint32_t)reg_base) + IFXUSB_PCGCCTL_OFFSET); ++ ++ /* ++ * Store the contents of the hardware configuration registers here for ++ * easy access later. ++ */ ++ _core_if->hwcfg1.d32 = ifxusb_rreg(&_core_if->core_global_regs->ghwcfg1); ++ _core_if->hwcfg2.d32 = ifxusb_rreg(&_core_if->core_global_regs->ghwcfg2); ++ _core_if->hwcfg3.d32 = ifxusb_rreg(&_core_if->core_global_regs->ghwcfg3); ++ _core_if->hwcfg4.d32 = ifxusb_rreg(&_core_if->core_global_regs->ghwcfg4); ++ ++ IFX_DEBUGPL(DBG_CILV,"hwcfg1=%08x\n",_core_if->hwcfg1.d32); ++ IFX_DEBUGPL(DBG_CILV,"hwcfg2=%08x\n",_core_if->hwcfg2.d32); ++ IFX_DEBUGPL(DBG_CILV,"hwcfg3=%08x\n",_core_if->hwcfg3.d32); ++ IFX_DEBUGPL(DBG_CILV,"hwcfg4=%08x\n",_core_if->hwcfg4.d32); ++ ++ ++ #ifdef __DED_FIFO__ ++ IFX_PRINT("Waiting for PHY Clock Lock!\n"); ++ while(!( ifxusb_rreg(&_core_if->core_global_regs->grxfsiz) & (1<<9))) ++ { ++ } ++ IFX_PRINT("PHY Clock Locked!\n"); ++ //ifxusb_clean_spram(_core_if,128*1024/4); ++ #endif ++ ++ /* Create new workqueue and init works */ ++#if 0 ++ _core_if->wq_usb = create_singlethread_workqueue(_core_if->core_name); ++ ++ if(_core_if->wq_usb == 0) ++ { ++ IFX_DEBUGPL(DBG_CIL, "Creation of wq_usb failed\n"); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ INIT_WORK(&core_if->w_conn_id, w_conn_id_status_change, core_if); ++ INIT_WORK(&core_if->w_wkp, w_wakeup_detected, core_if); ++ #else ++ INIT_WORK(&core_if->w_conn_id, w_conn_id_status_change); ++ INIT_DELAYED_WORK(&core_if->w_wkp, w_wakeup_detected); ++ #endif ++#endif ++ return 0; ++ ++fail: ++ if( reg_base != NULL) iounmap(reg_base ); ++ if( fifo_base != NULL) iounmap(fifo_base); ++ if( fifo_dbg != NULL) iounmap(fifo_dbg ); ++ return retval; ++} ++ ++/*! ++ \brief This function free the mapped address in the IFXUSB CSR data structures. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_core_if_remove(ifxusb_core_if_t *_core_if) ++{ ++ /* Disable all interrupts */ ++ if( _core_if->core_global_regs != NULL) ++ { ++ ifxusb_mreg( &_core_if->core_global_regs->gahbcfg, 1, 0); ++ ifxusb_wreg( &_core_if->core_global_regs->gintmsk, 0); ++ } ++ ++ if( _core_if->core_global_regs != NULL) iounmap(_core_if->core_global_regs ); ++ if( _core_if->data_fifo[0] != NULL) iounmap(_core_if->data_fifo[0] ); ++ if( _core_if->data_fifo_dbg != NULL) iounmap(_core_if->data_fifo_dbg ); ++ ++#if 0 ++ if (_core_if->wq_usb) ++ destroy_workqueue(_core_if->wq_usb); ++#endif ++ memset(_core_if, 0, sizeof(ifxusb_core_if_t)); ++} ++ ++ ++ ++ ++/*! ++ \brief This function enbles the controller's Global Interrupt in the AHB Config register. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_enable_global_interrupts( ifxusb_core_if_t *_core_if ) ++{ ++ gahbcfg_data_t ahbcfg ={ .d32 = 0}; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ ifxusb_mreg(&_core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32); ++} ++ ++/*! ++ \brief This function disables the controller's Global Interrupt in the AHB Config register. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_disable_global_interrupts( ifxusb_core_if_t *_core_if ) ++{ ++ gahbcfg_data_t ahbcfg ={ .d32 = 0}; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ ifxusb_mreg(&_core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); ++} ++ ++ ++ ++ ++/*! ++ \brief Flush Tx and Rx FIFO. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_flush_both_fifo( ifxusb_core_if_t *_core_if ) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset ={ .d32 = 0}; ++ int count = 0; ++ ++ IFX_DEBUGPL((DBG_CIL|DBG_PCDV), "%s\n", __func__); ++ greset.b.rxfflsh = 1; ++ greset.b.txfflsh = 1; ++ greset.b.txfnum = 0x10; ++ greset.b.intknqflsh=1; ++ greset.b.hstfrm=1; ++ ifxusb_wreg( &global_regs->grstctl, greset.d32 ); ++ ++ do ++ { ++ greset.d32 = ifxusb_rreg( &global_regs->grstctl); ++ if (++count > 10000) ++ { ++ IFX_WARN("%s() HANG! GRSTCTL=%0x\n", __func__, greset.d32); ++ break; ++ } ++ } while (greset.b.rxfflsh == 1 || greset.b.txfflsh == 1); ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++/*! ++ \brief Flush a Tx FIFO. ++ \param _core_if Pointer of core_if structure ++ \param _num Tx FIFO to flush. ( 0x10 for ALL TX FIFO ) ++ */ ++void ifxusb_flush_tx_fifo( ifxusb_core_if_t *_core_if, const int _num ) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset ={ .d32 = 0}; ++ int count = 0; ++ ++ IFX_DEBUGPL((DBG_CIL|DBG_PCDV), "Flush Tx FIFO %d\n", _num); ++ ++ greset.b.intknqflsh=1; ++ greset.b.txfflsh = 1; ++ greset.b.txfnum = _num; ++ ifxusb_wreg( &global_regs->grstctl, greset.d32 ); ++ ++ do ++ { ++ greset.d32 = ifxusb_rreg( &global_regs->grstctl); ++ if (++count > 10000&&(_num==0 ||_num==0x10)) ++ { ++ IFX_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", ++ __func__, greset.d32, ++ ifxusb_rreg( &global_regs->gnptxsts)); ++ break; ++ } ++ } while (greset.b.txfflsh == 1); ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++ ++/*! ++ \brief Flush Rx FIFO. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_flush_rx_fifo( ifxusb_core_if_t *_core_if ) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset ={ .d32 = 0}; ++ int count = 0; ++ ++ IFX_DEBUGPL((DBG_CIL|DBG_PCDV), "%s\n", __func__); ++ greset.b.rxfflsh = 1; ++ ifxusb_wreg( &global_regs->grstctl, greset.d32 ); ++ ++ do ++ { ++ greset.d32 = ifxusb_rreg( &global_regs->grstctl); ++ if (++count > 10000) ++ { ++ IFX_WARN("%s() HANG! GRSTCTL=%0x\n", __func__, greset.d32); ++ break; ++ } ++ } while (greset.b.rxfflsh == 1); ++ /* Wait for 3 PHY Clocks*/ ++ UDELAY(1); ++} ++ ++ ++#define SOFT_RESET_DELAY 100 ++ ++/*! ++ \brief Do a soft reset of the core. Be careful with this because it ++ resets all the internal state machines of the core. ++ \param _core_if Pointer of core_if structure ++ */ ++int ifxusb_core_soft_reset(ifxusb_core_if_t *_core_if) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ volatile grstctl_t greset ={ .d32 = 0}; ++ int count = 0; ++ ++ IFX_DEBUGPL(DBG_CILV, "%s\n", __func__); ++ /* Wait for AHB master IDLE state. */ ++ do ++ { ++ UDELAY(10); ++ greset.d32 = ifxusb_rreg( &global_regs->grstctl); ++ if (++count > 100000) ++ { ++ IFX_WARN("%s() HANG! AHB Idle GRSTCTL=%0x %x\n", __func__, ++ greset.d32, greset.b.ahbidle); ++ break; ++ } ++ } while (greset.b.ahbidle == 0); ++ ++ UDELAY(1); ++ ++ /* Core Soft Reset */ ++ count = 0; ++ greset.b.csftrst = 1; ++ ifxusb_wreg( &global_regs->grstctl, greset.d32 ); ++ ++ #ifdef SOFT_RESET_DELAY ++ MDELAY(SOFT_RESET_DELAY); ++ #endif ++ ++ do ++ { ++ UDELAY(10); ++ greset.d32 = ifxusb_rreg( &global_regs->grstctl); ++ if (++count > 100000) ++ { ++ IFX_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n", __func__, greset.d32); ++ return -1; ++ } ++ } while (greset.b.csftrst == 1); ++ ++ #ifdef SOFT_RESET_DELAY ++ MDELAY(SOFT_RESET_DELAY); ++ #endif ++ ++ ++ #if defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ { ++ set_bit (4, VR9_RCU_USBRESET2); ++ MDELAY(50); ++ clear_bit (4, VR9_RCU_USBRESET2); ++ } ++ else ++ { ++ set_bit (5, VR9_RCU_USBRESET2); ++ MDELAY(50); ++ clear_bit (5, VR9_RCU_USBRESET2); ++ } ++ MDELAY(50); ++ #endif //defined(__IS_VR9__) ++ ++ IFX_PRINT("USB core #%d soft-reset\n",_core_if->core_no); ++ ++ return 0; ++} ++ ++/*! ++ \brief Turn on the USB Core Power ++ \param _core_if Pointer of core_if structure ++*/ ++void ifxusb_power_on (ifxusb_core_if_t *_core_if) ++{ ++ struct clk *clk0 = clk_get_sys("usb0", NULL); ++ struct clk *clk1 = clk_get_sys("usb1", NULL); ++ // set clock gating ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ #if defined(__UEIP__) ++ ++ #if defined(__IS_TWINPASS) || defined(__IS_DANUBE__) ++ set_bit (4, (volatile unsigned long *)DANUBE_CGU_IFCCR); ++ set_bit (5, (volatile unsigned long *)DANUBE_CGU_IFCCR); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ // clear_bit (4, (volatile unsigned long *)AMAZON_SE_CGU_IFCCR); ++ clear_bit (5, (volatile unsigned long *)AMAZON_SE_CGU_IFCCR); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ set_bit (0, (volatile unsigned long *)AR9_CGU_IFCCR); ++ set_bit (1, (volatile unsigned long *)AR9_CGU_IFCCR); ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++// set_bit (0, (volatile unsigned long *)VR9_CGU_IFCCR); ++// set_bit (1, (volatile unsigned long *)VR9_CGU_IFCCR); ++ #endif //defined(__IS_VR9__) ++ ++ MDELAY(50); ++ ++ // set power ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ USB_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ //#if defined(__IS_TWINPASS__) ++ // ifxusb_enable_afe_oc(); ++ //#endif ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) || defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ clk_enable(clk0); ++// USB0_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ else ++ clk_enable(clk1); ++// USB1_CTRL_PMU_SETUP(IFX_PMU_ENABLE); ++ #endif //defined(__IS_AR9__) || defined(__IS_VR9__) ++ ++ if(_core_if->core_global_regs) ++ { ++ // PHY configurations. ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++ //ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_VR9__) ++ } ++ #else //defined(__UEIP__) ++ #if defined(__IS_TWINPASS) || defined(__IS_DANUBE__) ++ set_bit (4, (volatile unsigned long *)DANUBE_CGU_IFCCR); ++ set_bit (5, (volatile unsigned long *)DANUBE_CGU_IFCCR); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ // clear_bit (4, (volatile unsigned long *)AMAZON_SE_CGU_IFCCR); ++ clear_bit (5, (volatile unsigned long *)AMAZON_SE_CGU_IFCCR); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ set_bit (0, (volatile unsigned long *)AMAZON_S_CGU_IFCCR); ++ set_bit (1, (volatile unsigned long *)AMAZON_S_CGU_IFCCR); ++ #endif //defined(__IS_AR9__) ++ ++ MDELAY(50); ++ ++ // set power ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ clear_bit (6, (volatile unsigned long *)DANUBE_PMU_PWDCR);//USB ++ clear_bit (9, (volatile unsigned long *)DANUBE_PMU_PWDCR);//DSL ++ clear_bit (15, (volatile unsigned long *)DANUBE_PMU_PWDCR);//AHB ++ #if defined(__IS_TWINPASS__) ++ ifxusb_enable_afe_oc(); ++ #endif ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ clear_bit (6, (volatile unsigned long *)AMAZON_SE_PMU_PWDCR); ++ clear_bit (9, (volatile unsigned long *)AMAZON_SE_PMU_PWDCR); ++ clear_bit (15, (volatile unsigned long *)AMAZON_SE_PMU_PWDCR); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ clear_bit (6, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//USB ++ else ++ clear_bit (27, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//USB ++ clear_bit (9, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//DSL ++ clear_bit (15, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//AHB ++ #endif //defined(__IS_AR9__) ++ ++ if(_core_if->core_global_regs) ++ { ++ // PHY configurations. ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ } ++ ++ #endif //defined(__UEIP__) ++} ++ ++/*! ++ \brief Turn off the USB Core Power ++ \param _core_if Pointer of core_if structure ++*/ ++void ifxusb_power_off (ifxusb_core_if_t *_core_if) ++{ ++ struct clk *clk0 = clk_get_sys("usb0", NULL); ++ struct clk *clk1 = clk_get_sys("usb1", NULL); ++ ifxusb_phy_power_off (_core_if); ++ ++ // set power ++ #if defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ USB_CTRL_PMU_SETUP(IFX_PMU_DISABLE); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) || defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ clk_disable(clk0); ++ //USB0_CTRL_PMU_SETUP(IFX_PMU_DISABLE); ++ else ++ clk_disable(clk1); ++ //USB1_CTRL_PMU_SETUP(IFX_PMU_DISABLE); ++ #endif //defined(__IS_AR9__) || defined(__IS_VR9__) ++ #else //defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ set_bit (6, (volatile unsigned long *)DANUBE_PMU_PWDCR);//USB ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (6, (volatile unsigned long *)AMAZON_SE_PMU_PWDCR);//USB ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ set_bit (6, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//USB ++ else ++ set_bit (27, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//USB ++ #endif //defined(__IS_AR9__) ++ #endif //defined(__UEIP__) ++} ++ ++/*! ++ \brief Turn on the USB PHY Power ++ \param _core_if Pointer of core_if structure ++*/ ++void ifxusb_phy_power_on (ifxusb_core_if_t *_core_if) ++{ ++ struct clk *clk0 = clk_get_sys("usb0", NULL); ++ struct clk *clk1 = clk_get_sys("usb1", NULL); ++ #if defined(__UEIP__) ++ if(_core_if->core_global_regs) ++ { ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9_S__) ++ if(_core_if->core_no==0) ++ set_bit (0, VR9_RCU_USB_ANA_CFG1A); ++ else ++ set_bit (0, VR9_RCU_USB_ANA_CFG1B); ++ #endif //defined(__IS_VR9__) ++ } ++ ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ USB_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) || defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ clk_enable(clk0); ++ //USB0_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++ else ++ clk_enable(clk1); ++ //USB1_PHY_PMU_SETUP(IFX_PMU_ENABLE); ++ #endif //defined(__IS_AR9__) || defined(__IS_VR9__) ++ ++ // PHY configurations. ++ if(_core_if->core_global_regs) ++ { ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9_S__) ++ if(_core_if->core_no==0) ++ set_bit (0, VR9_RCU_USB_ANA_CFG1A); ++ else ++ set_bit (0, VR9_RCU_USB_ANA_CFG1B); ++ #endif //defined(__IS_VR9__) ++ } ++ #else //defined(__UEIP__) ++ // PHY configurations. ++ if(_core_if->core_global_regs) ++ { ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ } ++ ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ clear_bit (0, (volatile unsigned long *)DANUBE_PMU_PWDCR);//PHY ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ clear_bit (0, (volatile unsigned long *)AMAZON_SE_PMU_PWDCR); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ clear_bit (0, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//PHY ++ else ++ clear_bit (26, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//PHY ++ #endif //defined(__IS_AR9__) ++ ++ // PHY configurations. ++ if(_core_if->core_global_regs) ++ { ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ } ++ #endif //defined(__UEIP__) ++} ++ ++ ++/*! ++ \brief Turn off the USB PHY Power ++ \param _core_if Pointer of core_if structure ++*/ ++void ifxusb_phy_power_off (ifxusb_core_if_t *_core_if) ++{ ++ struct clk *clk0 = clk_get_sys("usb0", NULL); ++ struct clk *clk1 = clk_get_sys("usb1", NULL); ++ #if defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ USB_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) || defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) || defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ clk_disable(clk0); ++ //USB0_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++ else ++ clk_disable(clk1); ++ //USB1_PHY_PMU_SETUP(IFX_PMU_DISABLE); ++ #endif // defined(__IS_AR9__) || defined(__IS_VR9__) ++ #else //defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ set_bit (0, (volatile unsigned long *)DANUBE_PMU_PWDCR);//PHY ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (0, (volatile unsigned long *)AMAZON_SE_PMU_PWDCR);//PHY ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ set_bit (0, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//PHY ++ else ++ set_bit (26, (volatile unsigned long *)AMAZON_S_PMU_PWDCR);//PHY ++ #endif //defined(__IS_AR9__) ++ #endif //defined(__UEIP__) ++} ++ ++ ++/*! ++ \brief Reset on the USB Core RCU ++ \param _core_if Pointer of core_if structure ++ */ ++#if defined(__IS_VR9__) ++ int already_hard_reset=0; ++#endif ++void ifxusb_hard_reset(ifxusb_core_if_t *_core_if) ++{ ++ #if defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined (__IS_HOST__) ++ clear_bit (DANUBE_USBCFG_HDSEL_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (DANUBE_USBCFG_HDSEL_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ #endif ++ #endif //defined(__IS_AMAZON_SE__) ++ ++ #if defined(__IS_AMAZON_SE__) ++ #if defined (__IS_HOST__) ++ clear_bit (AMAZON_SE_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (AMAZON_SE_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ #endif ++ #endif //defined(__IS_AMAZON_SE__) ++ ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ #if defined (__IS_HOST__) ++ clear_bit (AR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)AR9_RCU_USB1CFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (AR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)AR9_RCU_USB1CFG); ++ #endif ++ } ++ else ++ { ++ #if defined (__IS_HOST__) ++ clear_bit (AR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)AR9_RCU_USB2CFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (AR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)AR9_RCU_USB2CFG); ++ #endif ++ } ++ #endif //defined(__IS_AR9__) ++ ++ #if defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ { ++ #if defined (__IS_HOST__) ++ clear_bit (VR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)VR9_RCU_USB1CFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (VR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)VR9_RCU_USB1CFG); ++ #endif ++ } ++ else ++ { ++ #if defined (__IS_HOST__) ++ clear_bit (VR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)VR9_RCU_USB2CFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (VR9_USBCFG_HDSEL_BIT, (volatile unsigned long *)VR9_RCU_USB2CFG); ++ #endif ++ } ++ #endif //defined(__IS_VR9__) ++ ++ ++ // set the HC's byte-order to big-endian ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ set_bit (DANUBE_USBCFG_HOST_END_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ clear_bit (DANUBE_USBCFG_SLV_END_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (AMAZON_SE_USBCFG_HOST_END_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ clear_bit (AMAZON_SE_USBCFG_SLV_END_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ set_bit (AR9_USBCFG_HOST_END_BIT, (volatile unsigned long *)AR9_RCU_USB1CFG); ++ clear_bit (AR9_USBCFG_SLV_END_BIT, (volatile unsigned long *)AR9_RCU_USB1CFG); ++ } ++ else ++ { ++ set_bit (AR9_USBCFG_HOST_END_BIT, (volatile unsigned long *)AR9_RCU_USB2CFG); ++ clear_bit (AR9_USBCFG_SLV_END_BIT, (volatile unsigned long *)AR9_RCU_USB2CFG); ++ } ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ { ++ set_bit (VR9_USBCFG_HOST_END_BIT, (volatile unsigned long *)VR9_RCU_USB1CFG); ++ clear_bit (VR9_USBCFG_SLV_END_BIT, (volatile unsigned long *)VR9_RCU_USB1CFG); ++ } ++ else ++ { ++ set_bit (VR9_USBCFG_HOST_END_BIT, (volatile unsigned long *)VR9_RCU_USB2CFG); ++ clear_bit (VR9_USBCFG_SLV_END_BIT, (volatile unsigned long *)VR9_RCU_USB2CFG); ++ } ++ #endif //defined(__IS_VR9__) ++ ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ set_bit (4, DANUBE_RCU_RESET); ++ MDELAY(500); ++ clear_bit (4, DANUBE_RCU_RESET); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (4, AMAZON_SE_RCU_RESET); ++ MDELAY(500); ++ clear_bit (4, AMAZON_SE_RCU_RESET); ++ MDELAY(500); ++ #endif //defined(__IS_AMAZON_SE__) ++ ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ set_bit (4, AR9_RCU_USBRESET); ++ MDELAY(500); ++ clear_bit (4, AR9_RCU_USBRESET); ++ } ++ else ++ { ++ set_bit (28, AR9_RCU_USBRESET); ++ MDELAY(500); ++ clear_bit (28, AR9_RCU_USBRESET); ++ } ++ MDELAY(500); ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++ if(!already_hard_reset) ++ { ++ set_bit (4, VR9_RCU_USBRESET); ++ MDELAY(500); ++ clear_bit (4, VR9_RCU_USBRESET); ++ MDELAY(500); ++ already_hard_reset=1; ++ } ++ #endif //defined(__IS_VR9__) ++ ++ #if defined(__IS_TWINPASS__) ++ ifxusb_enable_afe_oc(); ++ #endif ++ ++ if(_core_if->core_global_regs) ++ { ++ // PHY configurations. ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++ // ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_VR9__) ++ } ++ #else //defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined (__IS_HOST__) ++ clear_bit (DANUBE_USBCFG_HDSEL_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (DANUBE_USBCFG_HDSEL_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ #endif ++ #endif //defined(__IS_AMAZON_SE__) ++ ++ #if defined(__IS_AMAZON_SE__) ++ #if defined (__IS_HOST__) ++ clear_bit (AMAZON_SE_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (AMAZON_SE_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ #endif ++ #endif //defined(__IS_AMAZON_SE__) ++ ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ #if defined (__IS_HOST__) ++ clear_bit (AMAZON_S_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB1CFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (AMAZON_S_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB1CFG); ++ #endif ++ } ++ else ++ { ++ #if defined (__IS_HOST__) ++ clear_bit (AMAZON_S_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB2CFG); ++ #elif defined (__IS_DEVICE__) ++ set_bit (AMAZON_S_USBCFG_HDSEL_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB2CFG); ++ #endif ++ } ++ #endif //defined(__IS_AR9__) ++ ++ // set the HC's byte-order to big-endian ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ set_bit (DANUBE_USBCFG_HOST_END_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ clear_bit (DANUBE_USBCFG_SLV_END_BIT, (volatile unsigned long *)DANUBE_RCU_USBCFG); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (AMAZON_SE_USBCFG_HOST_END_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ clear_bit (AMAZON_SE_USBCFG_SLV_END_BIT, (volatile unsigned long *)AMAZON_SE_RCU_USBCFG); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ set_bit (AMAZON_S_USBCFG_HOST_END_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB1CFG); ++ clear_bit (AMAZON_S_USBCFG_SLV_END_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB1CFG); ++ } ++ else ++ { ++ set_bit (AMAZON_S_USBCFG_HOST_END_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB2CFG); ++ clear_bit (AMAZON_S_USBCFG_SLV_END_BIT, (volatile unsigned long *)AMAZON_S_RCU_USB2CFG); ++ } ++ #endif //defined(__IS_AR9__) ++ ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ set_bit (4, DANUBE_RCU_RESET); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (4, AMAZON_SE_RCU_RESET); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ set_bit (4, AMAZON_S_RCU_USBRESET); ++ } ++ else ++ { ++ set_bit (28, AMAZON_S_RCU_USBRESET); ++ } ++ #endif //defined(__IS_AR9__) ++ ++ MDELAY(500); ++ ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ clear_bit (4, DANUBE_RCU_RESET); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ clear_bit (4, AMAZON_SE_RCU_RESET); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ clear_bit (4, AMAZON_S_RCU_USBRESET); ++ } ++ else ++ { ++ clear_bit (28, AMAZON_S_RCU_USBRESET); ++ } ++ #endif //defined(__IS_AR9__) ++ ++ MDELAY(500); ++ ++ #if defined(__IS_TWINPASS__) ++ ifxusb_enable_afe_oc(); ++ #endif ++ ++ if(_core_if->core_global_regs) ++ { ++ // PHY configurations. ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ ifxusb_wreg (&_core_if->core_global_regs->guid,0x14014); ++ #endif //defined(__IS_AR9__) ++ } ++ #endif //defined(__UEIP__) ++} ++ ++#if defined(__GADGET_LED__) || defined(__HOST_LED__) ++ #if defined(__UEIP__) ++ static void *g_usb_led_trigger = NULL; ++ #endif ++ ++ void ifxusb_led_init(ifxusb_core_if_t *_core_if) ++ { ++ #if defined(__UEIP__) ++ if ( !g_usb_led_trigger ) ++ { ++ ifx_led_trigger_register("usb_link", &g_usb_led_trigger); ++ if ( g_usb_led_trigger != NULL ) ++ { ++ struct ifx_led_trigger_attrib attrib = {0}; ++ attrib.delay_on = 250; ++ attrib.delay_off = 250; ++ attrib.timeout = 2000; ++ attrib.def_value = 1; ++ attrib.flags = IFX_LED_TRIGGER_ATTRIB_DELAY_ON | IFX_LED_TRIGGER_ATTRIB_DELAY_OFF | IFX_LED_TRIGGER_ATTRIB_TIMEOUT | IFX_LED_TRIGGER_ATTRIB_DEF_VALUE; ++ IFX_DEBUGP("Reg USB LED!!\n"); ++ ifx_led_trigger_set_attrib(g_usb_led_trigger, &attrib); ++ } ++ } ++ #endif //defined(__UEIP__) ++ } ++ ++ void ifxusb_led_free(ifxusb_core_if_t *_core_if) ++ { ++ #if defined(__UEIP__) ++ if ( g_usb_led_trigger ) ++ { ++ ifx_led_trigger_deregister(g_usb_led_trigger); ++ g_usb_led_trigger = NULL; ++ } ++ #endif //defined(__UEIP__) ++ } ++ ++ /*! ++ \brief Turn off the USB 5V VBus Power ++ \param _core_if Pointer of core_if structure ++ */ ++ void ifxusb_led(ifxusb_core_if_t *_core_if) ++ { ++ #if defined(__UEIP__) ++ if(g_usb_led_trigger) ++ ifx_led_trigger_activate(g_usb_led_trigger); ++ #else ++ #endif //defined(__UEIP__) ++ } ++#endif // defined(__GADGET_LED__) || defined(__HOST_LED__) ++ ++ ++ ++#if defined(__IS_HOST__) && defined(__DO_OC_INT__) && defined(__DO_OC_INT_ENABLE__) ++/*! ++ \brief Turn on the OC Int ++ */ ++ void ifxusb_oc_int_on() ++ { ++ #if defined(__UEIP__) ++ #else ++ #if defined(__IS_TWINPASS__) ++ irq_enable(DANUBE_USB_OC_INT); ++ #endif ++ #endif //defined(__UEIP__) ++ } ++/*! ++ \brief Turn off the OC Int ++ */ ++ void ifxusb_oc_int_off() ++ { ++ #if defined(__UEIP__) ++ #else ++ #if defined(__IS_TWINPASS__) ++ irq_disable(DANUBE_USB_OC_INT); ++ #endif ++ #endif //defined(__UEIP__) ++ } ++#endif //defined(__IS_HOST__) && defined(__DO_OC_INT__) && defined(__DO_OC_INT_ENABLE__) ++ ++/* internal routines for debugging */ ++void ifxusb_dump_msg(const u8 *buf, unsigned int length) ++{ ++#ifdef __DEBUG__ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ start = 0; ++ while (length > 0) ++ { ++ num = min(length, 16u); ++ p = line; ++ for (i = 0; i < num; ++i) ++ { ++ if (i == 8) ++ *p++ = ' '; ++ sprintf(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ IFX_PRINT( "%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++#endif ++} ++ ++/* This functions reads the SPRAM and prints its content */ ++void ifxusb_dump_spram(ifxusb_core_if_t *_core_if) ++{ ++#ifdef __ENABLE_DUMP__ ++ volatile uint8_t *addr, *start_addr, *end_addr; ++ uint32_t size; ++ IFX_PRINT("SPRAM Data:\n"); ++ start_addr = (void*)_core_if->core_global_regs; ++ IFX_PRINT("Base Address: 0x%8X\n", (uint32_t)start_addr); ++ ++ start_addr = (void*)_core_if->data_fifo_dbg; ++ IFX_PRINT("Starting Address: 0x%8X\n", (uint32_t)start_addr); ++ ++ size=_core_if->hwcfg3.b.dfifo_depth; ++ size<<=2; ++ size+=0x200; ++ size&=0x0003FFFC; ++ ++ end_addr = (void*)_core_if->data_fifo_dbg; ++ end_addr += size; ++ ++ for(addr = start_addr; addr < end_addr; addr+=16) ++ { ++ IFX_PRINT("0x%8X:\t%02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", (uint32_t)addr, ++ addr[ 0], addr[ 1], addr[ 2], addr[ 3], ++ addr[ 4], addr[ 5], addr[ 6], addr[ 7], ++ addr[ 8], addr[ 9], addr[10], addr[11], ++ addr[12], addr[13], addr[14], addr[15] ++ ); ++ } ++ return; ++#endif //__ENABLE_DUMP__ ++} ++ ++ ++ ++ ++/* This function reads the core global registers and prints them */ ++void ifxusb_dump_registers(ifxusb_core_if_t *_core_if) ++{ ++#ifdef __ENABLE_DUMP__ ++ int i; ++ volatile uint32_t *addr; ++ #ifdef __IS_DEVICE__ ++ volatile uint32_t *addri,*addro; ++ #endif ++ ++ IFX_PRINT("Core Global Registers\n"); ++ addr=&_core_if->core_global_regs->gotgctl; ++ IFX_PRINT("GOTGCTL @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gotgint; ++ IFX_PRINT("GOTGINT @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gahbcfg; ++ IFX_PRINT("GAHBCFG @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gusbcfg; ++ IFX_PRINT("GUSBCFG @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->grstctl; ++ IFX_PRINT("GRSTCTL @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gintsts; ++ IFX_PRINT("GINTSTS @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gintmsk; ++ IFX_PRINT("GINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gi2cctl; ++ IFX_PRINT("GI2CCTL @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gpvndctl; ++ IFX_PRINT("GPVNDCTL @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->ggpio; ++ IFX_PRINT("GGPIO @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->guid; ++ IFX_PRINT("GUID @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->gsnpsid; ++ IFX_PRINT("GSNPSID @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg1; ++ IFX_PRINT("GHWCFG1 @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg2; ++ IFX_PRINT("GHWCFG2 @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg3; ++ IFX_PRINT("GHWCFG3 @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->ghwcfg4; ++ IFX_PRINT("GHWCFG4 @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ ++ addr=_core_if->pcgcctl; ++ IFX_PRINT("PCGCCTL @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ ++ addr=&_core_if->core_global_regs->grxfsiz; ++ IFX_PRINT("GRXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ ++ #ifdef __IS_HOST__ ++ addr=&_core_if->core_global_regs->gnptxfsiz; ++ IFX_PRINT("GNPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->core_global_regs->hptxfsiz; ++ IFX_PRINT("HPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ #endif //__IS_HOST__ ++ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_FIFO__ ++ addr=&_core_if->core_global_regs->gnptxfsiz; ++ IFX_PRINT("GNPTXFSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ for (i=0; i<= _core_if->hwcfg4.b.num_in_eps; i++) ++ { ++ addr=&_core_if->core_global_regs->dptxfsiz_dieptxf[i]; ++ IFX_PRINT("DPTXFSIZ[%d] @0x%08X : 0x%08X\n",i,(uint32_t)addr,ifxusb_rreg(addr)); ++ } ++ #else ++ addr=&_core_if->core_global_regs->gnptxfsiz; ++ IFX_PRINT("TXFSIZ[00] @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ for (i=0; i< _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ { ++ addr=&_core_if->core_global_regs->dptxfsiz_dieptxf[i]; ++ IFX_PRINT("TXFSIZ[%02d] @0x%08X : 0x%08X\n",i+1,(uint32_t)addr,ifxusb_rreg(addr)); ++ } ++ #endif ++ #endif //__IS_DEVICE__ ++ ++ #ifdef __IS_HOST__ ++ IFX_PRINT("Host Global Registers\n"); ++ addr=&_core_if->host_global_regs->hcfg; ++ IFX_PRINT("HCFG @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->host_global_regs->hfir; ++ IFX_PRINT("HFIR @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->host_global_regs->hfnum; ++ IFX_PRINT("HFNUM @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->host_global_regs->hptxsts; ++ IFX_PRINT("HPTXSTS @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->host_global_regs->haint; ++ IFX_PRINT("HAINT @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->host_global_regs->haintmsk; ++ IFX_PRINT("HAINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr= _core_if->hprt0; ++ IFX_PRINT("HPRT0 @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ ++ for (i=0; ihc_regs[i]->hcchar; ++ IFX_PRINT("HCCHAR @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->hc_regs[i]->hcsplt; ++ IFX_PRINT("HCSPLT @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->hc_regs[i]->hcint; ++ IFX_PRINT("HCINT @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->hc_regs[i]->hcintmsk; ++ IFX_PRINT("HCINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->hc_regs[i]->hctsiz; ++ IFX_PRINT("HCTSIZ @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->hc_regs[i]->hcdma; ++ IFX_PRINT("HCDMA @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ } ++ #endif //__IS_HOST__ ++ ++ #ifdef __IS_DEVICE__ ++ IFX_PRINT("Device Global Registers\n"); ++ addr=&_core_if->dev_global_regs->dcfg; ++ IFX_PRINT("DCFG @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->dctl; ++ IFX_PRINT("DCTL @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->dsts; ++ IFX_PRINT("DSTS @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->diepmsk; ++ IFX_PRINT("DIEPMSK @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->doepmsk; ++ IFX_PRINT("DOEPMSK @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->daintmsk; ++ IFX_PRINT("DAINTMSK @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->daint; ++ IFX_PRINT("DAINT @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->dvbusdis; ++ IFX_PRINT("DVBUSID @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ addr=&_core_if->dev_global_regs->dvbuspulse; ++ IFX_PRINT("DVBUSPULSE @0x%08X : 0x%08X\n", (uint32_t)addr,ifxusb_rreg(addr)); ++ ++ addr=&_core_if->dev_global_regs->dtknqr1; ++ IFX_PRINT("DTKNQR1 @0x%08X : 0x%08X\n",(uint32_t)addr,ifxusb_rreg(addr)); ++ if (_core_if->hwcfg2.b.dev_token_q_depth > 6) { ++ addr=&_core_if->dev_global_regs->dtknqr2; ++ IFX_PRINT("DTKNQR2 @0x%08X : 0x%08X\n", (uint32_t)addr,ifxusb_rreg(addr)); ++ } ++ ++ if (_core_if->hwcfg2.b.dev_token_q_depth > 14) ++ { ++ addr=&_core_if->dev_global_regs->dtknqr3_dthrctl; ++ IFX_PRINT("DTKNQR3_DTHRCTL @0x%08X : 0x%08X\n", (uint32_t)addr, ifxusb_rreg(addr)); ++ } ++ ++ if (_core_if->hwcfg2.b.dev_token_q_depth > 22) ++ { ++ addr=&_core_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ IFX_PRINT("DTKNQR4 @0x%08X : 0x%08X\n", (uint32_t)addr, ifxusb_rreg(addr)); ++ } ++ ++ //for (i=0; i<= MAX_EPS_CHANNELS; i++) ++ //for (i=0; i<= 10; i++) ++ for (i=0; i<= 3; i++) ++ { ++ IFX_PRINT("Device EP %d Registers\n", i); ++ addri=&_core_if->in_ep_regs[i]->diepctl;addro=&_core_if->out_ep_regs[i]->doepctl; ++ IFX_PRINT("DEPCTL I: 0x%08X O: 0x%08X\n",ifxusb_rreg(addri),ifxusb_rreg(addro)); ++ addro=&_core_if->out_ep_regs[i]->doepfn; ++ IFX_PRINT("DEPFN I: O: 0x%08X\n",ifxusb_rreg(addro)); ++ addri=&_core_if->in_ep_regs[i]->diepint;addro=&_core_if->out_ep_regs[i]->doepint; ++ IFX_PRINT("DEPINT I: 0x%08X O: 0x%08X\n",ifxusb_rreg(addri),ifxusb_rreg(addro)); ++ addri=&_core_if->in_ep_regs[i]->dieptsiz;addro=&_core_if->out_ep_regs[i]->doeptsiz; ++ IFX_PRINT("DETSIZ I: 0x%08X O: 0x%08X\n",ifxusb_rreg(addri),ifxusb_rreg(addro)); ++ addri=&_core_if->in_ep_regs[i]->diepdma;addro=&_core_if->out_ep_regs[i]->doepdma; ++ IFX_PRINT("DEPDMA I: 0x%08X O: 0x%08X\n",ifxusb_rreg(addri),ifxusb_rreg(addro)); ++ addri=&_core_if->in_ep_regs[i]->dtxfsts; ++ IFX_PRINT("DTXFSTS I: 0x%08X\n",ifxusb_rreg(addri) ); ++ addri=&_core_if->in_ep_regs[i]->diepdmab;addro=&_core_if->out_ep_regs[i]->doepdmab; ++ IFX_PRINT("DEPDMAB I: 0x%08X O: 0x%08X\n",ifxusb_rreg(addri),ifxusb_rreg(addro)); ++ } ++ #endif //__IS_DEVICE__ ++#endif //__ENABLE_DUMP__ ++} ++ ++void ifxusb_clean_spram(ifxusb_core_if_t *_core_if,uint32_t dwords) ++{ ++ volatile uint32_t *addr1,*addr2, *start_addr, *end_addr; ++ ++ if(!dwords) ++ return; ++ ++ start_addr = (uint32_t *)_core_if->data_fifo_dbg; ++ ++ end_addr = (uint32_t *)_core_if->data_fifo_dbg; ++ end_addr += dwords; ++ ++ IFX_PRINT("Clearning SPRAM: 0x%8X-0x%8X\n", (uint32_t)start_addr,(uint32_t)end_addr); ++ for(addr1 = start_addr; addr1 < end_addr; addr1+=4) ++ { ++ for(addr2 = addr1; addr2 < addr1+4; addr2++) ++ *addr2=0x00000000; ++ } ++ IFX_PRINT("Clearning SPRAM: 0x%8X-0x%8X Done\n", (uint32_t)start_addr,(uint32_t)end_addr); ++ return; ++} ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_cif.h b/drivers/usb/ifxhcd/ifxusb_cif.h +new file mode 100644 +index 0000000..191781f +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_cif.h +@@ -0,0 +1,665 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_cif.h ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : The Core Interface provides basic services for accessing and ++ ** managing the IFX USB hardware. These services are used by both the ++ ** Host Controller Driver and the Peripheral Controller Driver. ++ ** FUNCTIONS : ++ ** COMPILER : gcc ++ ** REFERENCE : IFX hardware ref handbook for each plateforms ++ ** COPYRIGHT : ++ ** Version Control Section ** ++ ** $Author$ ++ ** $Date$ ++ ** $Revisions$ ++ ** $Log$ Revision history ++*****************************************************************************/ ++ ++/*! ++ \defgroup IFXUSB_DRIVER_V3 IFX USB SS Project ++ \brief IFX USB subsystem V3.x ++ */ ++ ++/*! ++ \defgroup IFXUSB_CIF Core Interface APIs ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief The Core Interface provides basic services for accessing and ++ managing the IFXUSB hardware. These services are used by both the ++ Host Controller Driver and the Peripheral Controller Driver. ++ */ ++ ++ ++/*! ++ \file ifxusb_cif.h ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the interface to the IFX USB Core. ++ */ ++ ++#if !defined(__IFXUSB_CIF_H__) ++#define __IFXUSB_CIF_H__ ++ ++#include ++ ++#include ++#include ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++ ++#ifdef __DEBUG__ ++ #include "linux/timer.h" ++#endif ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#define IFXUSB_PARAM_SPEED_HIGH 0 ++#define IFXUSB_PARAM_SPEED_FULL 1 ++ ++#define IFXUSB_EP_SPEED_LOW 0 ++#define IFXUSB_EP_SPEED_FULL 1 ++#define IFXUSB_EP_SPEED_HIGH 2 ++ ++#define IFXUSB_EP_TYPE_CTRL 0 ++#define IFXUSB_EP_TYPE_ISOC 1 ++#define IFXUSB_EP_TYPE_BULK 2 ++#define IFXUSB_EP_TYPE_INTR 3 ++ ++#define IFXUSB_HC_PID_DATA0 0 ++#define IFXUSB_HC_PID_DATA2 1 ++#define IFXUSB_HC_PID_DATA1 2 ++#define IFXUSB_HC_PID_MDATA 3 ++#define IFXUSB_HC_PID_SETUP 3 ++ ++ ++/*! ++ \addtogroup IFXUSB_CIF ++ */ ++/*@{*/ ++ ++/*! ++ \struct ifxusb_params ++ \brief IFXUSB Parameters structure. ++ This structure is used for both importing from insmod stage and run-time storage. ++ These parameters define how the IFXUSB controller should be configured. ++ */ ++typedef struct ifxusb_params ++{ ++ int32_t dma_burst_size; /*!< The DMA Burst size (applicable only for Internal DMA ++ Mode). 0(for single), 1(incr), 4(incr4), 8(incr8) 16(incr16) ++ */ ++ /* Translate this to GAHBCFG values */ ++ int32_t speed; /*!< Specifies the maximum speed of operation in host and device mode. ++ The actual speed depends on the speed of the attached device and ++ the value of phy_type. The actual speed depends on the speed of the ++ attached device. ++ 0 - High Speed (default) ++ 1 - Full Speed ++ */ ++ ++ int32_t data_fifo_size; /*!< Total number of dwords in the data FIFO memory. This ++ memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ Tx FIFOs. ++ 32 to 32768 ++ */ ++ #ifdef __IS_DEVICE__ ++ int32_t rx_fifo_size; /*!< Number of dwords in the Rx FIFO in device mode. ++ 16 to 32768 ++ */ ++ ++ ++ int32_t tx_fifo_size[MAX_EPS_CHANNELS]; /*!< Number of dwords in each of the Tx FIFOs in device mode. ++ 4 to 768 ++ */ ++ #ifdef __DED_FIFO__ ++ int32_t thr_ctl; /*!< Threshold control on/off */ ++ int32_t tx_thr_length; /*!< Threshold length for Tx */ ++ int32_t rx_thr_length; /*!< Threshold length for Rx*/ ++ #endif ++ #else //__IS_HOST__ ++ int32_t host_channels; /*!< The number of host channel registers to use. ++ 1 to 16 ++ */ ++ ++ int32_t rx_fifo_size; /*!< Number of dwords in the Rx FIFO in host mode. ++ 16 to 32768 ++ */ ++ ++ int32_t nperio_tx_fifo_size;/*!< Number of dwords in the non-periodic Tx FIFO in host mode. ++ 16 to 32768 ++ */ ++ ++ int32_t perio_tx_fifo_size; /*!< Number of dwords in the host periodic Tx FIFO. ++ 16 to 32768 ++ */ ++ #endif //__IS_HOST__ ++ ++ int32_t max_transfer_size; /*!< The maximum transfer size supported in bytes. ++ 2047 to 65,535 ++ */ ++ ++ int32_t max_packet_count; /*!< The maximum number of packets in a transfer. ++ 15 to 511 (default 511) ++ */ ++ int32_t phy_utmi_width; /*!< Specifies the UTMI+ Data Width. ++ 8 or 16 bits (default 16) ++ */ ++ ++ int32_t turn_around_time_hs; /*!< Specifies the Turn-Around time at HS*/ ++ int32_t turn_around_time_fs; /*!< Specifies the Turn-Around time at FS*/ ++ ++ int32_t timeout_cal_hs; /*!< Specifies the Timeout_Calibration at HS*/ ++ int32_t timeout_cal_fs; /*!< Specifies the Timeout_Calibration at FS*/ ++} ifxusb_params_t; ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++/*! ++ \struct ifxusb_core_if ++ \brief The ifx_core_if structure contains information needed to manage ++ the IFX USB controller acting in either host or device mode. It ++ represents the programming view of the controller as a whole. ++ */ ++typedef struct ifxusb_core_if ++{ ++ ifxusb_params_t params; /*!< Run-time Parameters */ ++ ++ uint8_t core_no; /*!< core number (used as id when multi-core case */ ++ char *core_name; /*!< core name used for registration and informative purpose*/ ++ int irq; /*!< irq number this core is hooked */ ++ ++ /***************************************************************** ++ * Structures and pointers to physical register interface. ++ *****************************************************************/ ++ /** Core Global registers starting at offset 000h. */ ++ ifxusb_core_global_regs_t *core_global_regs; /*!< pointer to Core Global Registers, offset at 000h */ ++ ++ /** Host-specific registers */ ++ #ifdef __IS_HOST__ ++ /** Host Global Registers starting at offset 400h.*/ ++ ifxusb_host_global_regs_t *host_global_regs; /*!< pointer to Host Global Registers, offset at 400h */ ++ #define IFXUSB_HOST_GLOBAL_REG_OFFSET 0x400 ++ /** Host Port 0 Control and Status Register */ ++ volatile uint32_t *hprt0; /*!< pointer to HPRT0 Registers, offset at 440h */ ++ #define IFXUSB_HOST_PORT_REGS_OFFSET 0x440 ++ /** Host Channel Specific Registers at offsets 500h-5FCh. */ ++ ifxusb_hc_regs_t *hc_regs[MAX_EPS_CHANNELS]; /*!< pointer to Host-Channel n Registers, offset at 500h */ ++ #define IFXUSB_HOST_CHAN_REGS_OFFSET 0x500 ++ #define IFXUSB_CHAN_REGS_OFFSET 0x20 ++ #endif ++ ++ /** Device-specific registers */ ++ #ifdef __IS_DEVICE__ ++ /** Device Global Registers starting at offset 800h */ ++ ifxusb_device_global_regs_t *dev_global_regs; /*!< pointer to Device Global Registers, offset at 800h */ ++ #define IFXUSB_DEV_GLOBAL_REG_OFFSET 0x800 ++ ++ /** Device Logical IN Endpoint-Specific Registers 900h-AFCh */ ++ ifxusb_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS]; /*!< pointer to Device IN-EP Registers, offset at 900h */ ++ #define IFXUSB_DEV_IN_EP_REG_OFFSET 0x900 ++ #define IFXUSB_EP_REG_OFFSET 0x20 ++ /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ ++ ifxusb_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS];/*!< pointer to Device OUT-EP Registers, offset at 900h */ ++ #define IFXUSB_DEV_OUT_EP_REG_OFFSET 0xB00 ++ #endif ++ ++ /** Power and Clock Gating Control Register */ ++ volatile uint32_t *pcgcctl; /*!< pointer to Power and Clock Gating Control Registers, offset at E00h */ ++ #define IFXUSB_PCGCCTL_OFFSET 0xE00 ++ ++ /** Push/pop addresses for endpoints or host channels.*/ ++ uint32_t *data_fifo[MAX_EPS_CHANNELS]; /*!< pointer to FIFO access windows, offset at 1000h */ ++ #define IFXUSB_DATA_FIFO_OFFSET 0x1000 ++ #define IFXUSB_DATA_FIFO_SIZE 0x1000 ++ ++ uint32_t *data_fifo_dbg; /*!< pointer to FIFO debug windows, offset at 1000h */ ++ ++ /** Hardware Configuration -- stored here for convenience.*/ ++ hwcfg1_data_t hwcfg1; /*!< preserved Hardware Configuration 1 */ ++ hwcfg2_data_t hwcfg2; /*!< preserved Hardware Configuration 2 */ ++ hwcfg3_data_t hwcfg3; /*!< preserved Hardware Configuration 3 */ ++ hwcfg4_data_t hwcfg4; /*!< preserved Hardware Configuration 3 */ ++ uint32_t snpsid; /*!< preserved SNPSID */ ++ ++ /***************************************************************** ++ * Run-time informations. ++ *****************************************************************/ ++ /* Set to 1 if the core PHY interface bits in USBCFG have been initialized. */ ++ uint8_t phy_init_done; /*!< indicated PHY is initialized. */ ++ ++ #ifdef __IS_HOST__ ++ uint8_t queuing_high_bandwidth; /*!< Host mode, Queueing High Bandwidth. */ ++ #endif ++} ifxusb_core_if_t; ++ ++/*@}*//*IFXUSB_CIF*/ ++ ++ ++/*! ++ \fn void *ifxusb_alloc_buf(size_t size, int clear) ++ \brief This function is called to allocate buffer of specified size. ++ The allocated buffer is mapped into DMA accessable address. ++ \param size Size in BYTE to be allocated ++ \param clear 0: don't do clear after buffer allocated, other: do clear to zero ++ \return 0/NULL: Fail; uncached pointer of allocated buffer ++ \ingroup IFXUSB_CIF ++ */ ++extern void *ifxusb_alloc_buf(size_t size, int clear); ++ ++/*! ++ \fn void ifxusb_free_buf(void *vaddr) ++ \brief This function is called to free allocated buffer. ++ \param vaddr the uncached pointer of the buffer ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_free_buf(void *vaddr); ++ ++/*! ++ \fn int ifxusb_core_if_init(ifxusb_core_if_t *_core_if, ++ int _irq, ++ uint32_t _reg_base_addr, ++ uint32_t _fifo_base_addr, ++ uint32_t _fifo_dbg_addr) ++ \brief This function is called to initialize the IFXUSB CSR data ++ structures. The register addresses in the device and host ++ structures are initialized from the base address supplied by the ++ caller. The calling function must make the OS calls to get the ++ base address of the IFXUSB controller registers. ++ \param _core_if Pointer of core_if structure ++ \param _irq irq number ++ \param _reg_base_addr Base address of IFXUSB core registers ++ \param _fifo_base_addr Fifo base address ++ \param _fifo_dbg_addr Fifo debug address ++ \return 0: success; ++ \ingroup IFXUSB_CIF ++ */ ++extern int ifxusb_core_if_init(ifxusb_core_if_t *_core_if, ++ int _irq, ++ uint32_t _reg_base_addr, ++ uint32_t _fifo_base_addr, ++ uint32_t _fifo_dbg_addr); ++ ++ ++/*! ++ \fn void ifxusb_core_if_remove(ifxusb_core_if_t *_core_if) ++ \brief This function free the mapped address in the IFXUSB CSR data structures. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_core_if_remove(ifxusb_core_if_t *_core_if); ++ ++/*! ++ \fn void ifxusb_enable_global_interrupts( ifxusb_core_if_t *_core_if ) ++ \brief This function enbles the controller's Global Interrupt in the AHB Config register. ++ \param _core_if Pointer of core_if structure ++ */ ++extern void ifxusb_enable_global_interrupts( ifxusb_core_if_t *_core_if ); ++ ++/*! ++ \fn void ifxusb_disable_global_interrupts( ifxusb_core_if_t *_core_if ) ++ \brief This function disables the controller's Global Interrupt in the AHB Config register. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_disable_global_interrupts( ifxusb_core_if_t *_core_if ); ++ ++/*! ++ \fn void ifxusb_flush_tx_fifo( ifxusb_core_if_t *_core_if, const int _num ) ++ \brief Flush a Tx FIFO. ++ \param _core_if Pointer of core_if structure ++ \param _num Tx FIFO to flush. ( 0x10 for ALL TX FIFO ) ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_flush_tx_fifo( ifxusb_core_if_t *_core_if, const int _num ); ++ ++/*! ++ \fn void ifxusb_flush_rx_fifo( ifxusb_core_if_t *_core_if ) ++ \brief Flush Rx FIFO. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_flush_rx_fifo( ifxusb_core_if_t *_core_if ); ++ ++/*! ++ \fn void ifxusb_flush_both_fifo( ifxusb_core_if_t *_core_if ) ++ \brief Flush ALL Rx and Tx FIFO. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_flush_both_fifo( ifxusb_core_if_t *_core_if ); ++ ++ ++/*! ++ \fn int ifxusb_core_soft_reset(ifxusb_core_if_t *_core_if) ++ \brief Do core a soft reset of the core. Be careful with this because it ++ resets all the internal state machines of the core. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++extern int ifxusb_core_soft_reset(ifxusb_core_if_t *_core_if); ++ ++ ++/*! ++ \brief Turn on the USB Core Power ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++*/ ++extern void ifxusb_power_on (ifxusb_core_if_t *_core_if); ++ ++/*! ++ \fn void ifxusb_power_off (ifxusb_core_if_t *_core_if) ++ \brief Turn off the USB Core Power ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++*/ ++extern void ifxusb_power_off (ifxusb_core_if_t *_core_if); ++ ++/*! ++ \fn void ifxusb_phy_power_on (ifxusb_core_if_t *_core_if) ++ \brief Turn on the USB PHY Power ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++*/ ++extern void ifxusb_phy_power_on (ifxusb_core_if_t *_core_if); ++ ++/*! ++ \fn void ifxusb_phy_power_off (ifxusb_core_if_t *_core_if) ++ \brief Turn off the USB PHY Power ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++*/ ++extern void ifxusb_phy_power_off (ifxusb_core_if_t *_core_if); ++ ++/*! ++ \fn void ifxusb_hard_reset(ifxusb_core_if_t *_core_if) ++ \brief Reset on the USB Core RCU ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++extern void ifxusb_hard_reset(ifxusb_core_if_t *_core_if); ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ ++#ifdef __IS_HOST__ ++ /*! ++ \fn void ifxusb_host_core_init(ifxusb_core_if_t *_core_if, ifxusb_params_t *_params) ++ \brief This function initializes the IFXUSB controller registers for Host mode. ++ This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ request queues. ++ \param _core_if Pointer of core_if structure ++ \param _params parameters to be set ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_host_core_init(ifxusb_core_if_t *_core_if, ifxusb_params_t *_params); ++ ++ /*! ++ \fn void ifxusb_host_enable_interrupts(ifxusb_core_if_t *_core_if) ++ \brief This function enables the Host mode interrupts. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_host_enable_interrupts(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_host_disable_interrupts(ifxusb_core_if_t *_core_if) ++ \brief This function disables the Host mode interrupts. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_host_disable_interrupts(ifxusb_core_if_t *_core_if); ++ ++ #if defined(__IS_TWINPASS__) ++ extern void ifxusb_enable_afe_oc(void); ++ #endif ++ ++ /*! ++ \fn void ifxusb_vbus_init(ifxusb_core_if_t *_core_if) ++ \brief This function init the VBUS control. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_vbus_init(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_vbus_free(ifxusb_core_if_t *_core_if) ++ \brief This function free the VBUS control. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_vbus_free(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_vbus_on(ifxusb_core_if_t *_core_if) ++ \brief Turn on the USB 5V VBus Power ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_vbus_on(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_vbus_off(ifxusb_core_if_t *_core_if) ++ \brief Turn off the USB 5V VBus Power ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_vbus_off(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn int ifxusb_vbus(ifxusb_core_if_t *_core_if) ++ \brief Read Current VBus status ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern int ifxusb_vbus(ifxusb_core_if_t *_core_if); ++ ++ #if defined(__DO_OC_INT__) && defined(__DO_OC_INT_ENABLE__) ++ /*! ++ \fn void ifxusb_oc_int_on(void) ++ \brief Turn on the OC interrupt ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_oc_int_on(void); ++ ++ /*! ++ \fn void ifxusb_oc_int_off(void) ++ \brief Turn off the OC interrupt ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_oc_int_off(void); ++ #endif //defined(__DO_OC_INT__) && defined(__DO_OC_INT_ENABLE__) ++#endif ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ ++#ifdef __IS_DEVICE__ ++ /*! ++ \fn void ifxusb_dev_enable_interrupts(ifxusb_core_if_t *_core_if) ++ \brief This function enables the Device mode interrupts. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_dev_enable_interrupts(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn uint32_t ifxusb_dev_get_frame_number(ifxusb_core_if_t *_core_if) ++ \brief Gets the current USB frame number. This is the frame number from the last SOF packet. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern uint32_t ifxusb_dev_get_frame_number(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_dev_ep_set_stall(ifxusb_core_if_t *_core_if, uint8_t _epno, uint8_t _is_in) ++ \brief Set the EP STALL. ++ \param _core_if Pointer of core_if structure ++ \param _epno EP number ++ \param _is_in 1: is IN transfer ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_dev_ep_set_stall(ifxusb_core_if_t *_core_if, uint8_t _epno, uint8_t _is_in); ++ ++ /*! ++ \fn void ifxusb_dev_ep_clear_stall(ifxusb_core_if_t *_core_if, uint8_t _epno, uint8_t _ep_type, uint8_t _is_in) ++ \brief Set the EP STALL. ++ \param _core_if Pointer of core_if structure ++ \param _epno EP number ++ \param _ep_type EP Type ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_dev_ep_clear_stall(ifxusb_core_if_t *_core_if, uint8_t _epno, uint8_t _ep_type, uint8_t _is_in); ++ ++ /*! ++ \fn void ifxusb_dev_core_init(ifxusb_core_if_t *_core_if, ifxusb_params_t *_params) ++ \brief This function initializes the IFXUSB controller registers for Device mode. ++ This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ request queues. ++ This function validate the imported parameters and store the result in the CIF structure. ++ After ++ \param _core_if Pointer of core_if structure ++ \param _params structure of inported parameters ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_dev_core_init(ifxusb_core_if_t *_core_if, ifxusb_params_t *_params); ++#endif ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#if defined(__GADGET_LED__) || defined(__HOST_LED__) ++ /*! ++ \fn void ifxusb_led_init(ifxusb_core_if_t *_core_if) ++ \brief This function init the LED control. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_led_init(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_led_free(ifxusb_core_if_t *_core_if) ++ \brief This function free the LED control. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_led_free(ifxusb_core_if_t *_core_if); ++ ++ /*! ++ \fn void ifxusb_led(ifxusb_core_if_t *_core_if) ++ \brief This function trigger the LED access. ++ \param _core_if Pointer of core_if structure ++ \ingroup IFXUSB_CIF ++ */ ++ extern void ifxusb_led(ifxusb_core_if_t *_core_if); ++#endif ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++/* internal routines for debugging */ ++extern void ifxusb_dump_msg(const u8 *buf, unsigned int length); ++extern void ifxusb_dump_spram(ifxusb_core_if_t *_core_if); ++extern void ifxusb_dump_registers(ifxusb_core_if_t *_core_if); ++extern void ifxusb_clean_spram(ifxusb_core_if_t *_core_if,uint32_t dwords); ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++static inline uint32_t ifxusb_read_core_intr(ifxusb_core_if_t *_core_if) ++{ ++ return (ifxusb_rreg(&_core_if->core_global_regs->gintsts) & ++ (ifxusb_rreg(&_core_if->core_global_regs->gintmsk) ++#ifdef __USE_TIMER_4_SOF__ ++ | IFXUSB_SOF_INTR_MASK ++#endif ++ )); ++} ++ ++static inline uint32_t ifxusb_read_otg_intr (ifxusb_core_if_t *_core_if) ++{ ++ return (ifxusb_rreg (&_core_if->core_global_regs->gotgint)); ++} ++ ++static inline uint32_t ifxusb_mode(ifxusb_core_if_t *_core_if) ++{ ++ return (ifxusb_rreg( &_core_if->core_global_regs->gintsts ) & 0x1); ++} ++static inline uint8_t ifxusb_is_device_mode(ifxusb_core_if_t *_core_if) ++{ ++ return (ifxusb_mode(_core_if) != 1); ++} ++static inline uint8_t ifxusb_is_host_mode(ifxusb_core_if_t *_core_if) ++{ ++ return (ifxusb_mode(_core_if) == 1); ++} ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#ifdef __IS_HOST__ ++ static inline uint32_t ifxusb_read_hprt0(ifxusb_core_if_t *_core_if) ++ { ++ hprt0_data_t hprt0; ++ hprt0.d32 = ifxusb_rreg(_core_if->hprt0); ++ hprt0.b.prtena = 0; ++ hprt0.b.prtconndet = 0; ++ hprt0.b.prtenchng = 0; ++ hprt0.b.prtovrcurrchng = 0; ++ return hprt0.d32; ++ } ++ ++ static inline uint32_t ifxusb_read_host_all_channels_intr (ifxusb_core_if_t *_core_if) ++ { ++ return (ifxusb_rreg (&_core_if->host_global_regs->haint)); ++ } ++ ++ static inline uint32_t ifxusb_read_host_channel_intr (ifxusb_core_if_t *_core_if, int hc_num) ++ { ++ return (ifxusb_rreg (&_core_if->hc_regs[hc_num]->hcint)); ++ } ++#endif ++ ++#ifdef __IS_DEVICE__ ++ static inline uint32_t ifxusb_read_dev_all_in_ep_intr(ifxusb_core_if_t *_core_if) ++ { ++ uint32_t v; ++ v = ifxusb_rreg(&_core_if->dev_global_regs->daint) & ++ ifxusb_rreg(&_core_if->dev_global_regs->daintmsk); ++ return (v & 0xffff); ++ } ++ ++ static inline uint32_t ifxusb_read_dev_all_out_ep_intr(ifxusb_core_if_t *_core_if) ++ { ++ uint32_t v; ++ v = ifxusb_rreg(&_core_if->dev_global_regs->daint) & ++ ifxusb_rreg(&_core_if->dev_global_regs->daintmsk); ++ return ((v & 0xffff0000) >> 16); ++ } ++ ++ static inline uint32_t ifxusb_read_dev_in_ep_intr(ifxusb_core_if_t *_core_if, int _ep_num) ++ { ++ uint32_t v; ++ v = ifxusb_rreg(&_core_if->in_ep_regs[_ep_num]->diepint) & ++ ifxusb_rreg(&_core_if->dev_global_regs->diepmsk); ++ return v; ++ } ++ ++ static inline uint32_t ifxusb_read_dev_out_ep_intr(ifxusb_core_if_t *_core_if, int _ep_num) ++ { ++ uint32_t v; ++ v = ifxusb_rreg(&_core_if->out_ep_regs[_ep_num]->doepint) & ++ ifxusb_rreg(&_core_if->dev_global_regs->doepmsk); ++ return v; ++ } ++ ++#endif ++ ++extern void ifxusb_attr_create (void *_dev); ++ ++extern void ifxusb_attr_remove (void *_dev); ++ ++/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#endif // !defined(__IFXUSB_CIF_H__) ++ ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_cif_d.c b/drivers/usb/ifxhcd/ifxusb_cif_d.c +new file mode 100644 +index 0000000..36ab0e7 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_cif_d.c +@@ -0,0 +1,458 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_cif_d.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : The Core Interface provides basic services for accessing and ++ ** managing the IFX USB hardware. These services are used by the ++ ** Peripheral Controller Driver only. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxusb_cif_d.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the interface to the IFX USB Core. ++*/ ++ ++#include ++#include "ifxusb_version.h" ++ ++ ++#include ++#include ++ ++#ifdef __DEBUG__ ++ #include ++#endif ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++ ++#include "ifxpcd.h" ++ ++ ++ ++/*! ++ \brief Initializes the DevSpd field of the DCFG register depending on the PHY type ++ and the enumeration speed of the device. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_dev_init_spd(ifxusb_core_if_t *_core_if) ++{ ++ uint32_t val; ++ dcfg_data_t dcfg; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ if (_core_if->params.speed == IFXUSB_PARAM_SPEED_FULL) ++ /* High speed PHY running at full speed */ ++ val = 0x1; ++ else ++ /* High speed PHY running at high speed and full speed*/ ++ val = 0x0; ++ ++ IFX_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val); ++ dcfg.d32 = ifxusb_rreg(&_core_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ ifxusb_wreg(&_core_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++ ++/*! ++ \brief This function enables the Device mode interrupts. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_dev_enable_interrupts(ifxusb_core_if_t *_core_if) ++{ ++ gint_data_t intr_mask ={ .d32 = 0}; ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ IFX_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Clear any pending OTG Interrupts */ ++ ifxusb_wreg( &global_regs->gotgint, 0xFFFFFFFF); ++ ++ /* Clear any pending interrupts */ ++ ifxusb_wreg( &global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the interrupts in the GINTMSK.*/ ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.usbsuspend = 1; ++ ++ intr_mask.b.usbreset = 1; ++ intr_mask.b.enumdone = 1; ++ intr_mask.b.inepintr = 1; ++ intr_mask.b.outepintr = 1; ++ intr_mask.b.erlysuspend = 1; ++ #ifndef __DED_FIFO__ ++// intr_mask.b.epmismatch = 1; ++ #endif ++ ++ ifxusb_mreg( &global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ IFX_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__, ifxusb_rreg( &global_regs->gintmsk)); ++} ++ ++/*! ++ \brief Gets the current USB frame number. This is the frame number from the last SOF packet. ++ \param _core_if Pointer of core_if structure ++ */ ++uint32_t ifxusb_dev_get_frame_number(ifxusb_core_if_t *_core_if) ++{ ++ dsts_data_t dsts; ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ dsts.d32 = ifxusb_rreg(&_core_if->dev_global_regs->dsts); ++ /* read current frame/microfreme number from DSTS register */ ++ return dsts.b.soffn; ++} ++ ++ ++/*! ++ \brief Set the EP STALL. ++ */ ++void ifxusb_dev_ep_set_stall(ifxusb_core_if_t *_core_if, uint8_t _epno, uint8_t _is_in) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ IFX_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, _epno, (_is_in?"IN":"OUT")); ++ ++ depctl_addr = (_is_in)? (&(_core_if->in_ep_regs [_epno]->diepctl)): ++ (&(_core_if->out_ep_regs[_epno]->doepctl)); ++ depctl.d32 = ifxusb_rreg(depctl_addr); ++ depctl.b.stall = 1; ++ ++ if (_is_in && depctl.b.epena) ++ depctl.b.epdis = 1; ++ ++ ifxusb_wreg(depctl_addr, depctl.d32); ++ IFX_DEBUGPL(DBG_PCD,"DEPCTL=%0x\n",ifxusb_rreg(depctl_addr)); ++ return; ++} ++ ++/*! ++\brief Clear the EP STALL. ++ */ ++void ifxusb_dev_ep_clear_stall(ifxusb_core_if_t *_core_if, uint8_t _epno, uint8_t _ep_type, uint8_t _is_in) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ IFX_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, _epno, (_is_in?"IN":"OUT")); ++ ++ depctl_addr = (_is_in)? (&(_core_if->in_ep_regs [_epno]->diepctl)): ++ (&(_core_if->out_ep_regs[_epno]->doepctl)); ++ ++ depctl.d32 = ifxusb_rreg(depctl_addr); ++ /* clear the stall bits */ ++ depctl.b.stall = 0; ++ ++ /* ++ * USB Spec 9.4.5: For endpoints using data toggle, regardless ++ * of whether an endpoint has the Halt feature set, a ++ * ClearFeature(ENDPOINT_HALT) request always results in the ++ * data toggle being reinitialized to DATA0. ++ */ ++ if (_ep_type == IFXUSB_EP_TYPE_INTR || _ep_type == IFXUSB_EP_TYPE_BULK) ++ depctl.b.setd0pid = 1; /* DATA0 */ ++ ++ ifxusb_wreg(depctl_addr, depctl.d32); ++ IFX_DEBUGPL(DBG_PCD,"DEPCTL=%0x\n",ifxusb_rreg(depctl_addr)); ++ return; ++} ++ ++/*! ++ \brief This function initializes the IFXUSB controller registers for Device mode. ++ This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ request queues. ++ \param _core_if Pointer of core_if structure ++ \param _params parameters to be set ++ */ ++void ifxusb_dev_core_init(ifxusb_core_if_t *_core_if, ifxusb_params_t *_params) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ++ gusbcfg_data_t usbcfg ={.d32 = 0}; ++ gahbcfg_data_t ahbcfg ={.d32 = 0}; ++ dcfg_data_t dcfg ={.d32 = 0}; ++ grstctl_t resetctl ={.d32 = 0}; ++ gotgctl_data_t gotgctl ={.d32 = 0}; ++ ++ uint32_t dir; ++ int i; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ IFX_DEBUGPL(DBG_CILV, "%s(%p)\n",__func__,_core_if); ++ ++ /* Copy Params */ ++ _core_if->params.dma_burst_size = _params->dma_burst_size; ++ _core_if->params.speed = _params->speed; ++ if(_params->max_transfer_size < 2048 || _params->max_transfer_size > ((1 << (_core_if->hwcfg3.b.xfer_size_cntr_width + 11)) - 1) ) ++ _core_if->params.max_transfer_size = ((1 << (_core_if->hwcfg3.b.xfer_size_cntr_width + 11)) - 1); ++ else ++ _core_if->params.max_transfer_size = _params->max_transfer_size; ++ ++ if(_params->max_packet_count < 16 || _params->max_packet_count > ((1 << (_core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1) ) ++ _core_if->params.max_packet_count= ((1 << (_core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1); ++ else ++ _core_if->params.max_packet_count= _params->max_packet_count; ++ _core_if->params.phy_utmi_width = _params->phy_utmi_width; ++ _core_if->params.turn_around_time_hs = _params->turn_around_time_hs; ++ _core_if->params.turn_around_time_fs = _params->turn_around_time_fs; ++ _core_if->params.timeout_cal_hs = _params->timeout_cal_hs; ++ _core_if->params.timeout_cal_fs = _params->timeout_cal_fs; ++ ++ #ifdef __DED_FIFO__ ++ _core_if->params.thr_ctl = _params->thr_ctl; ++ _core_if->params.tx_thr_length = _params->tx_thr_length; ++ _core_if->params.rx_thr_length = _params->rx_thr_length; ++ #endif ++ ++ /* Reset the Controller */ ++ do ++ { ++ while(ifxusb_core_soft_reset( _core_if )) ++ ifxusb_hard_reset(_core_if); ++ } while (ifxusb_is_host_mode(_core_if)); ++ ++ usbcfg.d32 = ifxusb_rreg(&global_regs->gusbcfg); ++ #if 0 ++ #if defined(__DED_FIFO__) ++ usbcfg.b.ForceDevMode = 1; ++ usbcfg.b.ForceHstMode = 0; ++ #endif ++ #endif ++ usbcfg.b.term_sel_dl_pulse = 0; ++ ifxusb_wreg (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* This programming sequence needs to happen in FS mode before any other ++ * programming occurs */ ++ /* High speed PHY. */ ++ if (!_core_if->phy_init_done) ++ { ++ _core_if->phy_init_done = 1; ++ /* HS PHY parameters. These parameters are preserved ++ * during soft reset so only program the first time. Do ++ * a soft reset immediately after setting phyif. */ ++ usbcfg.b.ulpi_utmi_sel = 0; //UTMI+ ++ usbcfg.b.phyif = ( _core_if->params.phy_utmi_width == 16)?1:0; ++ ifxusb_wreg( &global_regs->gusbcfg, usbcfg.d32); ++ /* Reset after setting the PHY parameters */ ++ ifxusb_core_soft_reset( _core_if ); ++ } ++ ++ /* Program the GAHBCFG Register.*/ ++ switch (_core_if->params.dma_burst_size) ++ { ++ case 0 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_SINGLE; ++ break; ++ case 1 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR; ++ break; ++ case 4 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR4; ++ break; ++ case 8 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR8; ++ break; ++ case 16: ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR16; ++ break; ++ } ++ ahbcfg.b.dmaenable = 1; ++ ifxusb_wreg(&global_regs->gahbcfg, ahbcfg.d32); ++ ++ /* Program the GUSBCFG register. */ ++ usbcfg.d32 = ifxusb_rreg( &global_regs->gusbcfg ); ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ ifxusb_wreg( &global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Restart the Phy Clock */ ++ ifxusb_wreg(_core_if->pcgcctl, 0); ++ ++ /* Device configuration register */ ++ ifxusb_dev_init_spd(_core_if); ++ dcfg.d32 = ifxusb_rreg( &_core_if->dev_global_regs->dcfg); ++ dcfg.b.perfrint = IFXUSB_DCFG_FRAME_INTERVAL_80; ++ #if defined(__DED_FIFO__) ++ #if defined(__DESC_DMA__) ++ dcfg.b.descdma = 1; ++ #else ++ dcfg.b.descdma = 0; ++ #endif ++ #endif ++ ++ ifxusb_wreg( &_core_if->dev_global_regs->dcfg, dcfg.d32 ); ++ ++ /* Configure data FIFO sizes */ ++ _core_if->params.data_fifo_size = _core_if->hwcfg3.b.dfifo_depth; ++ _core_if->params.rx_fifo_size = ifxusb_rreg(&global_regs->grxfsiz); ++ IFX_DEBUGPL(DBG_CIL, "Initial: FIFO Size=0x%06X\n" , _core_if->params.data_fifo_size); ++ IFX_DEBUGPL(DBG_CIL, " Rx FIFO Size=0x%06X\n", _core_if->params.rx_fifo_size); ++ ++ _core_if->params.tx_fifo_size[0]= ifxusb_rreg(&global_regs->gnptxfsiz) >> 16; ++ ++ #ifdef __DED_FIFO__ ++ for (i=1; i <= _core_if->hwcfg4.b.num_in_eps; i++) ++ _core_if->params.tx_fifo_size[i] = ++ ifxusb_rreg(&global_regs->dptxfsiz_dieptxf[i-1]) >> 16; ++ #else ++ for (i=0; i < _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ _core_if->params.tx_fifo_size[i+1] = ++ ifxusb_rreg(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ #endif ++ ++ #ifdef __DEBUG__ ++ #ifdef __DED_FIFO__ ++ for (i=0; i <= _core_if->hwcfg4.b.num_in_eps; i++) ++ IFX_DEBUGPL(DBG_CIL, " Tx[%02d] FIFO Size=0x%06X\n",i, _core_if->params.tx_fifo_size[i]); ++ #else ++ IFX_DEBUGPL(DBG_CIL, " NPTx FIFO Size=0x%06X\n", _core_if->params.tx_fifo_size[0]); ++ for (i=0; i < _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ IFX_DEBUGPL(DBG_CIL, " PTx[%02d] FIFO Size=0x%06X\n",i, _core_if->params.tx_fifo_size[i+1]); ++ #endif ++ #endif ++ ++ { ++ fifosize_data_t txfifosize; ++ if(_params->data_fifo_size >=0 && _params->data_fifo_size < _core_if->params.data_fifo_size) ++ _core_if->params.data_fifo_size = _params->data_fifo_size; ++ ++ ++ if(_params->rx_fifo_size >=0 && _params->rx_fifo_size < _core_if->params.rx_fifo_size) ++ _core_if->params.rx_fifo_size = _params->rx_fifo_size; ++ if(_core_if->params.data_fifo_size < _core_if->params.rx_fifo_size) ++ _core_if->params.rx_fifo_size = _core_if->params.data_fifo_size; ++ ifxusb_wreg( &global_regs->grxfsiz, _core_if->params.rx_fifo_size); ++ ++ for (i=0; i < MAX_EPS_CHANNELS; i++) ++ if(_params->tx_fifo_size[i] >=0 && _params->tx_fifo_size[i] < _core_if->params.tx_fifo_size[i]) ++ _core_if->params.tx_fifo_size[i] = _params->tx_fifo_size[i]; ++ ++ txfifosize.b.startaddr = _core_if->params.rx_fifo_size; ++ #ifdef __DED_FIFO__ ++ if(txfifosize.b.startaddr + _core_if->params.tx_fifo_size[0] > _core_if->params.data_fifo_size) ++ _core_if->params.tx_fifo_size[0]= _core_if->params.data_fifo_size - txfifosize.b.startaddr; ++ txfifosize.b.depth=_core_if->params.tx_fifo_size[0]; ++ ifxusb_wreg( &global_regs->gnptxfsiz, txfifosize.d32); ++ txfifosize.b.startaddr += _core_if->params.tx_fifo_size[0]; ++ for (i=1; i <= _core_if->hwcfg4.b.num_in_eps; i++) ++ { ++ if(txfifosize.b.startaddr + _core_if->params.tx_fifo_size[i] > _core_if->params.data_fifo_size) ++ _core_if->params.tx_fifo_size[i]= _core_if->params.data_fifo_size - txfifosize.b.startaddr; ++ txfifosize.b.depth=_core_if->params.tx_fifo_size[i]; ++ ifxusb_wreg( &global_regs->dptxfsiz_dieptxf[i-1], txfifosize.d32); ++ txfifosize.b.startaddr += _core_if->params.tx_fifo_size[i]; ++ } ++ #else ++ if(txfifosize.b.startaddr + _core_if->params.tx_fifo_size[0] > _core_if->params.data_fifo_size) ++ _core_if->params.tx_fifo_size[0]= _core_if->params.data_fifo_size - txfifosize.b.startaddr; ++ txfifosize.b.depth=_core_if->params.tx_fifo_size[0]; ++ ifxusb_wreg( &global_regs->gnptxfsiz, txfifosize.d32); ++ txfifosize.b.startaddr += _core_if->params.tx_fifo_size[0]; ++ for (i=0; i < _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ { ++ if(txfifosize.b.startaddr + _core_if->params.tx_fifo_size[i+1] > _core_if->params.data_fifo_size) ++ _core_if->params.tx_fifo_size[i+1]= _core_if->params.data_fifo_size - txfifosize.b.startaddr; ++ //txfifosize.b.depth=_core_if->params.tx_fifo_size[i+1]; ++ ifxusb_wreg( &global_regs->dptxfsiz_dieptxf[i], txfifosize.d32); ++ txfifosize.b.startaddr += _core_if->params.tx_fifo_size[i+1]; ++ } ++ #endif ++ } ++ ++ #ifdef __DEBUG__ ++ { ++ fifosize_data_t fifosize; ++ IFX_DEBUGPL(DBG_CIL, "Result : FIFO Size=0x%06X\n" , _core_if->params.data_fifo_size); ++ ++ IFX_DEBUGPL(DBG_CIL, " Rx FIFO =0x%06X Sz=0x%06X\n", 0,ifxusb_rreg(&global_regs->grxfsiz)); ++ #ifdef __DED_FIFO__ ++ fifosize.d32=ifxusb_rreg(&global_regs->gnptxfsiz); ++ IFX_DEBUGPL(DBG_CIL, " Tx[00] FIFO =0x%06X Sz=0x%06X\n", fifosize.b.startaddr,fifosize.b.depth); ++ for (i=1; i <= _core_if->hwcfg4.b.num_in_eps; i++) ++ { ++ fifosize.d32=ifxusb_rreg(&global_regs->dptxfsiz_dieptxf[i-1]); ++ IFX_DEBUGPL(DBG_CIL, " Tx[%02d] FIFO 0x%06X Sz=0x%06X\n",i, fifosize.b.startaddr,fifosize.b.depth); ++ } ++ #else ++ fifosize.d32=ifxusb_rreg(&global_regs->gnptxfsiz); ++ IFX_DEBUGPL(DBG_CIL, " NPTx FIFO =0x%06X Sz=0x%06X\n", fifosize.b.startaddr,fifosize.b.depth); ++ for (i=0; i < _core_if->hwcfg4.b.num_dev_perio_in_ep; i++) ++ { ++ fifosize.d32=ifxusb_rreg(&global_regs->dptxfsiz_dieptxf[i]); ++ IFX_DEBUGPL(DBG_CIL, " PTx[%02d] FIFO 0x%06X Sz=0x%06X\n",i, fifosize.b.startaddr,fifosize.b.depth); ++ } ++ #endif ++ } ++ #endif ++ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ ifxusb_mreg( &global_regs->gotgctl, gotgctl.d32, 0); ++ ++ /* Flush the FIFOs */ ++ ifxusb_flush_tx_fifo(_core_if, 0x10); /* all Tx FIFOs */ ++ ifxusb_flush_rx_fifo(_core_if); ++ ++ /* Flush the Learning Queue. */ ++ resetctl.b.intknqflsh = 1; ++ ifxusb_wreg( &global_regs->grstctl, resetctl.d32); ++ ++ /* Clear all pending Device Interrupts */ ++ ifxusb_wreg( &_core_if->dev_global_regs->diepmsk , 0 ); ++ ifxusb_wreg( &_core_if->dev_global_regs->doepmsk , 0 ); ++ ifxusb_wreg( &_core_if->dev_global_regs->daint , 0xFFFFFFFF ); ++ ifxusb_wreg( &_core_if->dev_global_regs->daintmsk, 0 ); ++ ++ dir=_core_if->hwcfg1.d32; ++ for (i=0; i <= _core_if->hwcfg2.b.num_dev_ep ; i++,dir>>=2) ++ { ++ depctl_data_t depctl; ++ if((dir&0x03)==0 || (dir&0x03) ==1) ++ { ++ depctl.d32 = ifxusb_rreg(&_core_if->in_ep_regs[i]->diepctl); ++ if (depctl.b.epena) ++ { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } ++ else ++ depctl.d32 = 0; ++ ifxusb_wreg( &_core_if->in_ep_regs[i]->diepctl, depctl.d32); ++ #ifndef __DESC_DMA__ ++ ifxusb_wreg( &_core_if->in_ep_regs[i]->dieptsiz, 0); ++ #endif ++ ifxusb_wreg( &_core_if->in_ep_regs[i]->diepdma, 0); ++ ifxusb_wreg( &_core_if->in_ep_regs[i]->diepint, 0xFF); ++ } ++ ++ if((dir&0x03)==0 || (dir&0x03) ==2) ++ { ++ depctl.d32 = ifxusb_rreg(&_core_if->out_ep_regs[i]->doepctl); ++ if (depctl.b.epena) ++ { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } ++ else ++ depctl.d32 = 0; ++ ifxusb_wreg( &_core_if->out_ep_regs[i]->doepctl, depctl.d32); ++ #ifndef __DESC_DMA__ ++ ifxusb_wreg( &_core_if->out_ep_regs[i]->doeptsiz, 0); ++ #endif ++ ifxusb_wreg( &_core_if->out_ep_regs[i]->doepdma, 0); ++ ifxusb_wreg( &_core_if->out_ep_regs[i]->doepint, 0xFF); ++ } ++ } ++} ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_cif_h.c b/drivers/usb/ifxhcd/ifxusb_cif_h.c +new file mode 100644 +index 0000000..0f47ecd +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_cif_h.c +@@ -0,0 +1,846 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_cif_h.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : The Core Interface provides basic services for accessing and ++ ** managing the IFX USB hardware. These services are used by the ++ ** Host Controller Driver only. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxusb_cif_h.c ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the interface to the IFX USB Core. ++*/ ++#include ++#include "ifxusb_version.h" ++ ++#include ++#include ++ ++#ifdef __DEBUG__ ++ #include ++#endif ++#include ++#include ++#include ++#if defined(__UEIP__) ++// #include ++#endif ++ ++//#include ++#if defined(__UEIP__) ++// #include ++#endif ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++ ++#include "ifxhcd.h" ++ ++#if !defined(__UEIP__) ++ #undef __USING_LED_AS_GPIO__ ++#endif ++ ++ ++/*! ++ \brief This function enables the Host mode interrupts. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_host_enable_interrupts(ifxusb_core_if_t *_core_if) ++{ ++ gint_data_t intr_mask ={ .d32 = 0}; ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ++ IFX_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Clear any pending OTG Interrupts */ ++ ifxusb_wreg( &global_regs->gotgint, 0xFFFFFFFF); ++ ++ /* Clear any pending interrupts */ ++ ifxusb_wreg( &global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the interrupts in the GINTMSK.*/ ++ ++ /* Common interrupts */ ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.usbsuspend = 1; ++ ++ /* Host interrupts */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ ++ ifxusb_mreg( &global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ IFX_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__, ifxusb_rreg( &global_regs->gintmsk)); ++} ++ ++/*! ++ \brief This function disables the Host mode interrupts. ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_host_disable_interrupts(ifxusb_core_if_t *_core_if) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ++ IFX_DEBUGPL(DBG_CILV, "%s()\n", __func__); ++ ++ #if 1 ++ ifxusb_wreg( &global_regs->gintmsk, 0); ++ #else ++ /* Common interrupts */ ++ { ++ gint_data_t intr_mask ={.d32 = 0}; ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.rxstsqlvl = 1; ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.usbsuspend = 1; ++ ++ /* Host interrupts */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ intr_mask.b.ptxfempty = 1; ++ intr_mask.b.nptxfempty = 1; ++ ifxusb_mreg(&global_regs->gintmsk, intr_mask.d32, 0); ++ } ++ #endif ++} ++ ++/*! ++ \brief This function initializes the IFXUSB controller registers for Host mode. ++ This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ request queues. ++ \param _core_if Pointer of core_if structure ++ \param _params parameters to be set ++ */ ++void ifxusb_host_core_init(ifxusb_core_if_t *_core_if, ifxusb_params_t *_params) ++{ ++ ifxusb_core_global_regs_t *global_regs = _core_if->core_global_regs; ++ ++ gusbcfg_data_t usbcfg ={.d32 = 0}; ++ gahbcfg_data_t ahbcfg ={.d32 = 0}; ++ gotgctl_data_t gotgctl ={.d32 = 0}; ++ ++ int i; ++ ++ IFX_DEBUGPL(DBG_CILV, "%s(%p)\n",__func__,_core_if); ++ ++ /* Copy Params */ ++ ++ _core_if->params.dma_burst_size = _params->dma_burst_size; ++ _core_if->params.speed = _params->speed; ++ _core_if->params.max_transfer_size = _params->max_transfer_size; ++ _core_if->params.max_packet_count = _params->max_packet_count; ++ _core_if->params.phy_utmi_width = _params->phy_utmi_width; ++ _core_if->params.turn_around_time_hs = _params->turn_around_time_hs; ++ _core_if->params.turn_around_time_fs = _params->turn_around_time_fs; ++ _core_if->params.timeout_cal_hs = _params->timeout_cal_hs; ++ _core_if->params.timeout_cal_fs = _params->timeout_cal_fs; ++ ++ /* Reset the Controller */ ++ do ++ { ++ while(ifxusb_core_soft_reset( _core_if )) ++ ifxusb_hard_reset(_core_if); ++ } while (ifxusb_is_device_mode(_core_if)); ++ ++ usbcfg.d32 = ifxusb_rreg(&global_regs->gusbcfg); ++// usbcfg.b.ulpi_ext_vbus_drv = 1; ++ usbcfg.b.term_sel_dl_pulse = 0; ++ ifxusb_wreg (&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* This programming sequence needs to happen in FS mode before any other ++ * programming occurs */ ++ /* High speed PHY. */ ++ if (!_core_if->phy_init_done) ++ { ++ _core_if->phy_init_done = 1; ++ /* HS PHY parameters. These parameters are preserved ++ * during soft reset so only program the first time. Do ++ * a soft reset immediately after setting phyif. */ ++ usbcfg.b.ulpi_utmi_sel = 0; //UTMI+ ++ usbcfg.b.phyif = ( _core_if->params.phy_utmi_width == 16)?1:0; ++ ifxusb_wreg( &global_regs->gusbcfg, usbcfg.d32); ++ /* Reset after setting the PHY parameters */ ++ ifxusb_core_soft_reset( _core_if ); ++ } ++ ++ usbcfg.d32 = ifxusb_rreg(&global_regs->gusbcfg); ++// usbcfg.b.ulpi_fsls = 0; ++// usbcfg.b.ulpi_clk_sus_m = 0; ++ ifxusb_wreg(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Program the GAHBCFG Register.*/ ++ switch (_core_if->params.dma_burst_size) ++ { ++ case 0 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_SINGLE; ++ break; ++ case 1 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR; ++ break; ++ case 4 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR4; ++ break; ++ case 8 : ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR8; ++ break; ++ case 16: ++ ahbcfg.b.hburstlen = IFXUSB_GAHBCFG_INT_DMA_BURST_INCR16; ++ break; ++ } ++ ahbcfg.b.dmaenable = 1; ++ ifxusb_wreg(&global_regs->gahbcfg, ahbcfg.d32); ++ ++ /* Program the GUSBCFG register. */ ++ usbcfg.d32 = ifxusb_rreg( &global_regs->gusbcfg ); ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ ifxusb_wreg( &global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Restart the Phy Clock */ ++ ifxusb_wreg(_core_if->pcgcctl, 0); ++ ++ /* Initialize Host Configuration Register */ ++ { ++ hcfg_data_t hcfg; ++ hcfg.d32 = ifxusb_rreg(&_core_if->host_global_regs->hcfg); ++ hcfg.b.fslspclksel = IFXUSB_HCFG_30_60_MHZ; ++ if (_params->speed == IFXUSB_PARAM_SPEED_FULL) ++ hcfg.b.fslssupp = 1; ++ ifxusb_wreg(&_core_if->host_global_regs->hcfg, hcfg.d32); ++ } ++ ++ _core_if->params.host_channels=(_core_if->hwcfg2.b.num_host_chan + 1); ++ ++ if(_params->host_channels>0 && _params->host_channels < _core_if->params.host_channels) ++ _core_if->params.host_channels = _params->host_channels; ++ ++ /* Configure data FIFO sizes */ ++ _core_if->params.data_fifo_size = _core_if->hwcfg3.b.dfifo_depth; ++ _core_if->params.rx_fifo_size = ifxusb_rreg(&global_regs->grxfsiz); ++ _core_if->params.nperio_tx_fifo_size= ifxusb_rreg(&global_regs->gnptxfsiz) >> 16; ++ _core_if->params.perio_tx_fifo_size = ifxusb_rreg(&global_regs->hptxfsiz) >> 16; ++ IFX_DEBUGPL(DBG_CIL, "Initial: FIFO Size=0x%06X\n" , _core_if->params.data_fifo_size); ++ IFX_DEBUGPL(DBG_CIL, " Rx FIFO Size=0x%06X\n", _core_if->params.rx_fifo_size); ++ IFX_DEBUGPL(DBG_CIL, " NPTx FIFO Size=0x%06X\n", _core_if->params.nperio_tx_fifo_size); ++ IFX_DEBUGPL(DBG_CIL, " PTx FIFO Size=0x%06X\n", _core_if->params.perio_tx_fifo_size); ++ ++ { ++ fifosize_data_t txfifosize; ++ if(_params->data_fifo_size >=0 && _params->data_fifo_size < _core_if->params.data_fifo_size) ++ _core_if->params.data_fifo_size = _params->data_fifo_size; ++ ++ if( _params->rx_fifo_size >= 0 && _params->rx_fifo_size < _core_if->params.rx_fifo_size) ++ _core_if->params.rx_fifo_size = _params->rx_fifo_size; ++ if( _params->nperio_tx_fifo_size >=0 && _params->nperio_tx_fifo_size < _core_if->params.nperio_tx_fifo_size) ++ _core_if->params.nperio_tx_fifo_size = _params->nperio_tx_fifo_size; ++ if( _params->perio_tx_fifo_size >=0 && _params->perio_tx_fifo_size < _core_if->params.perio_tx_fifo_size) ++ _core_if->params.perio_tx_fifo_size = _params->perio_tx_fifo_size; ++ ++ if(_core_if->params.data_fifo_size < _core_if->params.rx_fifo_size) ++ _core_if->params.rx_fifo_size = _core_if->params.data_fifo_size; ++ ifxusb_wreg( &global_regs->grxfsiz, _core_if->params.rx_fifo_size); ++ txfifosize.b.startaddr = _core_if->params.rx_fifo_size; ++ ++ if(txfifosize.b.startaddr + _core_if->params.nperio_tx_fifo_size > _core_if->params.data_fifo_size) ++ _core_if->params.nperio_tx_fifo_size = _core_if->params.data_fifo_size - txfifosize.b.startaddr; ++ txfifosize.b.depth=_core_if->params.nperio_tx_fifo_size; ++ ifxusb_wreg( &global_regs->gnptxfsiz, txfifosize.d32); ++ txfifosize.b.startaddr += _core_if->params.nperio_tx_fifo_size; ++ ++ if(txfifosize.b.startaddr + _core_if->params.perio_tx_fifo_size > _core_if->params.data_fifo_size) ++ _core_if->params.perio_tx_fifo_size = _core_if->params.data_fifo_size - txfifosize.b.startaddr; ++ txfifosize.b.depth=_core_if->params.perio_tx_fifo_size; ++ ifxusb_wreg( &global_regs->hptxfsiz, txfifosize.d32); ++ txfifosize.b.startaddr += _core_if->params.perio_tx_fifo_size; ++ } ++ ++ #ifdef __DEBUG__ ++ { ++ fifosize_data_t fifosize; ++ IFX_DEBUGPL(DBG_CIL, "Result : FIFO Size=0x%06X\n" , _core_if->params.data_fifo_size); ++ ++ fifosize.d32=ifxusb_rreg(&global_regs->grxfsiz); ++ IFX_DEBUGPL(DBG_CIL, " Rx FIFO =0x%06X 0x%06X\n", fifosize.b.startaddr,fifosize.b.depth); ++ fifosize.d32=ifxusb_rreg(&global_regs->gnptxfsiz); ++ IFX_DEBUGPL(DBG_CIL, " NPTx FIFO =0x%06X 0x%06X\n", fifosize.b.startaddr,fifosize.b.depth); ++ fifosize.d32=ifxusb_rreg(&global_regs->hptxfsiz); ++ IFX_DEBUGPL(DBG_CIL, " PTx FIFO =0x%06X 0x%06X\n", fifosize.b.startaddr,fifosize.b.depth); ++ } ++ #endif ++ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ ifxusb_mreg( &global_regs->gotgctl, gotgctl.d32, 0); ++ ++ /* Flush the FIFOs */ ++ ifxusb_flush_tx_fifo(_core_if, 0x10); /* all Tx FIFOs */ ++ ifxusb_flush_rx_fifo(_core_if); ++ ++ for (i = 0; i < _core_if->hwcfg2.b.num_host_chan + 1; i++) ++ { ++ hcchar_data_t hcchar; ++ hcchar.d32 = ifxusb_rreg(&_core_if->hc_regs[i]->hcchar); ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ ifxusb_wreg(&_core_if->hc_regs[i]->hcchar, hcchar.d32); ++ } ++ /* Halt all channels to put them into a known state. */ ++ for (i = 0; i < _core_if->hwcfg2.b.num_host_chan + 1; i++) ++ { ++ hcchar_data_t hcchar; ++ int count = 0; ++ ++ hcchar.d32 = ifxusb_rreg(&_core_if->hc_regs[i]->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ ifxusb_wreg(&_core_if->hc_regs[i]->hcchar, hcchar.d32); ++ ++ IFX_DEBUGPL(DBG_HCDV, "%s: Halt channel %d\n", __func__, i); ++ do{ ++ hcchar.d32 = ifxusb_rreg(&_core_if->hc_regs[i]->hcchar); ++ if (++count > 1000) ++ { ++ IFX_ERROR("%s: Unable to clear halt on channel %d\n", __func__, i); ++ break; ++ } ++ } while (hcchar.b.chen); ++ } ++} ++ ++////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#if defined(__UEIP__) ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ int ifxusb_vbus_status =-1; ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS1) || defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ int ifxusb_vbus1_status =-1; ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS2) || defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ int ifxusb_vbus2_status =-1; ++ #endif ++ ++ #if defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ static void *g_usb_vbus_trigger = NULL; ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ static void *g_usb_vbus1_trigger = NULL; ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ static void *g_usb_vbus2_trigger = NULL; ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ int ifxusb_vbus_gpio_inited=0; ++ #endif ++ ++#else //defined(__UEIP__) ++ int ifxusb_vbus_gpio_inited=0; ++#endif ++ ++////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++void ifxusb_vbus_init(ifxusb_core_if_t *_core_if) ++{ ++ #if defined(__UEIP__) ++ #if defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ if ( !g_usb_vbus_trigger ) ++ { ++ ifx_led_trigger_register("USB_VBUS", &g_usb_vbus_trigger); ++ if ( g_usb_vbus_trigger != NULL ) ++ { ++ struct ifx_led_trigger_attrib attrib = {0}; ++ attrib.delay_on = 0; ++ attrib.delay_off = 0; ++ attrib.timeout = 0; ++ attrib.def_value = 0; ++ attrib.flags = IFX_LED_TRIGGER_ATTRIB_DELAY_ON | IFX_LED_TRIGGER_ATTRIB_DELAY_OFF | IFX_LED_TRIGGER_ATTRIB_TIMEOUT | IFX_LED_TRIGGER_ATTRIB_DEF_VALUE; ++ IFX_DEBUGP("Reg USB power!!\n"); ++ ifx_led_trigger_set_attrib(g_usb_vbus_trigger, &attrib); ++ ifxusb_vbus_status =0; ++ } ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ if(_core_if->core_no==0 && !g_usb_vbus1_trigger ) ++ { ++ ifx_led_trigger_register("USB_VBUS1", &g_usb_vbus1_trigger); ++ if ( g_usb_vbus1_trigger != NULL ) ++ { ++ struct ifx_led_trigger_attrib attrib = {0}; ++ attrib.delay_on = 0; ++ attrib.delay_off = 0; ++ attrib.timeout = 0; ++ attrib.def_value = 0; ++ attrib.flags = IFX_LED_TRIGGER_ATTRIB_DELAY_ON | IFX_LED_TRIGGER_ATTRIB_DELAY_OFF | IFX_LED_TRIGGER_ATTRIB_TIMEOUT | IFX_LED_TRIGGER_ATTRIB_DEF_VALUE; ++ IFX_DEBUGP("Reg USB1 power!!\n"); ++ ifx_led_trigger_set_attrib(g_usb_vbus1_trigger, &attrib); ++ ifxusb_vbus1_status =0; ++ } ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ if(_core_if->core_no==1 && !g_usb_vbus2_trigger ) ++ { ++ ifx_led_trigger_register("USB_VBUS2", &g_usb_vbus2_trigger); ++ if ( g_usb_vbus2_trigger != NULL ) ++ { ++ struct ifx_led_trigger_attrib attrib = {0}; ++ attrib.delay_on = 0; ++ attrib.delay_off = 0; ++ attrib.timeout = 0; ++ attrib.def_value = 0; ++ attrib.flags = IFX_LED_TRIGGER_ATTRIB_DELAY_ON | IFX_LED_TRIGGER_ATTRIB_DELAY_OFF | IFX_LED_TRIGGER_ATTRIB_TIMEOUT | IFX_LED_TRIGGER_ATTRIB_DEF_VALUE; ++ IFX_DEBUGP("Reg USB2 power!!\n"); ++ ifx_led_trigger_set_attrib(g_usb_vbus2_trigger, &attrib); ++ ifxusb_vbus2_status =0; ++ } ++ } ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ /* == 20100712 AVM/WK use gpio_inited as bitmask == */ ++ if(ifxusb_vbus_gpio_inited == 0) ++ { ++ if(!ifx_gpio_register(IFX_GPIO_MODULE_USB)) ++ { ++ IFX_DEBUGP("Register USB VBus through GPIO OK!!\n"); ++ #ifdef IFX_GPIO_USB_VBUS ++ ifxusb_vbus_status =0; ++ #endif //IFX_GPIO_USB_VBUS ++ #ifdef IFX_GPIO_USB_VBUS1 ++ ifxusb_vbus1_status=0; ++ #endif //IFX_GPIO_USB_VBUS1 ++ #ifdef IFX_GPIO_USB_VBUS2 ++ ifxusb_vbus2_status=0; ++ #endif //IFX_GPIO_USB_VBUS2 ++ ifxusb_vbus_gpio_inited|= (1<<_core_if->core_no); ++ } ++ else ++ IFX_PRINT("Register USB VBus Failed!!\n"); ++ } else { ++ ifxusb_vbus_gpio_inited|= (1<<_core_if->core_no); ++ } ++ #endif //defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ #endif //defined(__UEIP__) ++} ++ ++void ifxusb_vbus_free(ifxusb_core_if_t *_core_if) ++{ ++ #if defined(__UEIP__) ++ #if defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ if ( g_usb_vbus_trigger ) ++ { ++ ifx_led_trigger_deregister(g_usb_vbus_trigger); ++ g_usb_vbus_trigger = NULL; ++ ifxusb_vbus_status =-1; ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ if(_core_if->core_no==0 && g_usb_vbus1_trigger ) ++ { ++ ifx_led_trigger_deregister(g_usb_vbus1_trigger); ++ g_usb_vbus1_trigger = NULL; ++ ifxusb_vbus1_status =-1; ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ if(_core_if->core_no==1 && g_usb_vbus2_trigger ) ++ { ++ ifx_led_trigger_deregister(g_usb_vbus2_trigger); ++ g_usb_vbus2_trigger = NULL; ++ ifxusb_vbus2_status =-1; ++ } ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ /* == 20100712 AVM/WK use gpio_inited as bitmask == */ ++ if((ifxusb_vbus_gpio_inited & (1<<_core_if->core_no)) == ifxusb_vbus_gpio_inited) ++ { ++ ifx_gpio_deregister(IFX_GPIO_MODULE_USB); ++ #ifdef IFX_GPIO_USB_VBUS ++ ifxusb_vbus_status =-1; ++ #endif //IFX_GPIO_USB_VBUS ++ #ifdef IFX_GPIO_USB_VBUS1 ++ ifxusb_vbus1_status=-1; ++ #endif //IFX_GPIO_USB_VBUS1 ++ #ifdef IFX_GPIO_USB_VBUS2 ++ ifxusb_vbus2_status=-1; ++ #endif //IFX_GPIO_USB_VBUS2 ++ } ++ ifxusb_vbus_gpio_inited &= ~(1<<_core_if->core_no); ++ #endif //defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ #endif //defined(__UEIP__) ++} ++ ++ ++/*! ++ \brief Turn on the USB 5V VBus Power ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_vbus_on(ifxusb_core_if_t *_core_if) ++{ ++ IFX_DEBUGP("SENDING VBus POWER UP\n"); ++ #if defined(__UEIP__) ++ #if defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ if ( g_usb_vbus_trigger && ifxusb_vbus_status==0) ++ { ++ ifx_led_trigger_activate(g_usb_vbus_trigger); ++ IFX_DEBUGP("Enable USB power!!\n"); ++ ifxusb_vbus_status=1; ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ if(_core_if->core_no==0 && g_usb_vbus1_trigger && ifxusb_vbus1_status==0) ++ { ++ ifx_led_trigger_activate(g_usb_vbus1_trigger); ++ IFX_DEBUGP("Enable USB1 power!!\n"); ++ ifxusb_vbus1_status=1; ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ if(_core_if->core_no==1 && g_usb_vbus2_trigger && ifxusb_vbus2_status==0) ++ { ++ ifx_led_trigger_activate(g_usb_vbus2_trigger); ++ IFX_DEBUGP("Enable USB2 power!!\n"); ++ ifxusb_vbus2_status=1; ++ } ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ if(ifxusb_vbus_gpio_inited) ++ { ++ #if defined(IFX_GPIO_USB_VBUS) ++ if(ifxusb_vbus_status==0) ++ { ++ ifx_gpio_output_set(IFX_GPIO_USB_VBUS,IFX_GPIO_MODULE_USB); ++ ifxusb_vbus_status=1; ++ } ++ #endif ++ #if defined(IFX_GPIO_USB_VBUS1) ++ if(_core_if->core_no==0 && ifxusb_vbus1_status==0) ++ { ++ ifx_gpio_output_set(IFX_GPIO_USB_VBUS1,IFX_GPIO_MODULE_USB); ++ ifxusb_vbus1_status=1; ++ } ++ #endif ++ #if defined(IFX_GPIO_USB_VBUS2) ++ if(_core_if->core_no==1 && ifxusb_vbus2_status==0) ++ { ++ ifx_gpio_output_set(IFX_GPIO_USB_VBUS2,IFX_GPIO_MODULE_USB); ++ ifxusb_vbus2_status=1; ++ } ++ #endif ++ } ++ #endif //defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ #else ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_vbus_status=1; ++ //usb_set_vbus_on(); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ set_bit (4, (volatile unsigned long *)AMAZON_SE_GPIO_P0_OUT); ++ ifxusb_vbus_status=1; ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ if (bsp_port_reserve_pin(1, 13, PORT_MODULE_USB) != 0) ++ { ++ IFX_PRINT("Can't enable USB1 5.5V power!!\n"); ++ return; ++ } ++ bsp_port_clear_altsel0(1, 13, PORT_MODULE_USB); ++ bsp_port_clear_altsel1(1, 13, PORT_MODULE_USB); ++ bsp_port_set_dir_out(1, 13, PORT_MODULE_USB); ++ bsp_port_set_pudsel(1, 13, PORT_MODULE_USB); ++ bsp_port_set_puden(1, 13, PORT_MODULE_USB); ++ bsp_port_set_output(1, 13, PORT_MODULE_USB); ++ IFX_DEBUGP("Enable USB1 power!!\n"); ++ ifxusb_vbus1_status=1; ++ } ++ else ++ { ++ if (bsp_port_reserve_pin(3, 4, PORT_MODULE_USB) != 0) ++ { ++ IFX_PRINT("Can't enable USB2 5.5V power!!\n"); ++ return; ++ } ++ bsp_port_clear_altsel0(3, 4, PORT_MODULE_USB); ++ bsp_port_clear_altsel1(3, 4, PORT_MODULE_USB); ++ bsp_port_set_dir_out(3, 4, PORT_MODULE_USB); ++ bsp_port_set_pudsel(3, 4, PORT_MODULE_USB); ++ bsp_port_set_puden(3, 4, PORT_MODULE_USB); ++ bsp_port_set_output(3, 4, PORT_MODULE_USB); ++ IFX_DEBUGP("Enable USB2 power!!\n"); ++ ifxusb_vbus2_status=1; ++ } ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ { ++ ifxusb_vbus1_status=1; ++ } ++ else ++ { ++ ifxusb_vbus2_status=1; ++ } ++ #endif //defined(__IS_VR9__) ++ #endif //defined(__UEIP__) ++} ++ ++ ++/*! ++ \brief Turn off the USB 5V VBus Power ++ \param _core_if Pointer of core_if structure ++ */ ++void ifxusb_vbus_off(ifxusb_core_if_t *_core_if) ++{ ++ IFX_DEBUGP("SENDING VBus POWER OFF\n"); ++ ++ #if defined(__UEIP__) ++ #if defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ if ( g_usb_vbus_trigger && ifxusb_vbus_status==1) ++ { ++ ifx_led_trigger_deactivate(g_usb_vbus_trigger); ++ IFX_DEBUGP("Disable USB power!!\n"); ++ ifxusb_vbus_status=0; ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ if(_core_if->core_no==0 && g_usb_vbus1_trigger && ifxusb_vbus1_status==1) ++ { ++ ifx_led_trigger_deactivate(g_usb_vbus1_trigger); ++ IFX_DEBUGP("Disable USB1 power!!\n"); ++ ifxusb_vbus1_status=0; ++ } ++ #endif ++ #if defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ if(_core_if->core_no==1 && g_usb_vbus2_trigger && ifxusb_vbus2_status==1) ++ { ++ ifx_led_trigger_deactivate(g_usb_vbus2_trigger); ++ IFX_DEBUGP("Disable USB2 power!!\n"); ++ ifxusb_vbus2_status=0; ++ } ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ if(ifxusb_vbus_gpio_inited) ++ { ++ #if defined(IFX_GPIO_USB_VBUS) ++ if(ifxusb_vbus_status==1) ++ { ++ ifx_gpio_output_clear(IFX_GPIO_USB_VBUS,IFX_GPIO_MODULE_USB); ++ ifxusb_vbus_status=0; ++ } ++ #endif ++ #if defined(IFX_GPIO_USB_VBUS1) ++ if(_core_if->core_no==0 && ifxusb_vbus1_status==1) ++ { ++ ifx_gpio_output_clear(IFX_GPIO_USB_VBUS1,IFX_GPIO_MODULE_USB); ++ ifxusb_vbus1_status=0; ++ } ++ #endif ++ #if defined(IFX_GPIO_USB_VBUS2) ++ if(_core_if->core_no==1 && ifxusb_vbus2_status==1) ++ { ++ ifx_gpio_output_clear(IFX_GPIO_USB_VBUS2,IFX_GPIO_MODULE_USB); ++ ifxusb_vbus2_status=0; ++ } ++ #endif ++ } ++ #endif //defined(IFX_GPIO_USB_VBUS) || defined(IFX_GPIO_USB_VBUS1) || defined(IFX_GPIO_USB_VBUS2) ++ #else ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ ifxusb_vbus_status=0; ++ //usb_set_vbus_on(); ++ #endif //defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++ #if defined(__IS_AMAZON_SE__) ++ clear_bit (4, (volatile unsigned long *)AMAZON_SE_GPIO_P0_OUT); ++ ifxusb_vbus_status=0; ++ #endif //defined(__IS_AMAZON_SE__) ++ #if defined(__IS_AR9__) ++ if(_core_if->core_no==0) ++ { ++ if (bsp_port_reserve_pin(1, 13, PORT_MODULE_USB) != 0) { ++ IFX_PRINT("Can't Disable USB1 5.5V power!!\n"); ++ return; ++ } ++ bsp_port_clear_altsel0(1, 13, PORT_MODULE_USB); ++ bsp_port_clear_altsel1(1, 13, PORT_MODULE_USB); ++ bsp_port_set_dir_out(1, 13, PORT_MODULE_USB); ++ bsp_port_set_pudsel(1, 13, PORT_MODULE_USB); ++ bsp_port_set_puden(1, 13, PORT_MODULE_USB); ++ bsp_port_clear_output(1, 13, PORT_MODULE_USB); ++ IFX_DEBUGP("Disable USB1 power!!\n"); ++ ifxusb_vbus1_status=0; ++ } ++ else ++ { ++ if (bsp_port_reserve_pin(3, 4, PORT_MODULE_USB) != 0) { ++ IFX_PRINT("Can't Disable USB2 5.5V power!!\n"); ++ return; ++ } ++ bsp_port_clear_altsel0(3, 4, PORT_MODULE_USB); ++ bsp_port_clear_altsel1(3, 4, PORT_MODULE_USB); ++ bsp_port_set_dir_out(3, 4, PORT_MODULE_USB); ++ bsp_port_set_pudsel(3, 4, PORT_MODULE_USB); ++ bsp_port_set_puden(3, 4, PORT_MODULE_USB); ++ bsp_port_clear_output(3, 4, PORT_MODULE_USB); ++ IFX_DEBUGP("Disable USB2 power!!\n"); ++ ++ ifxusb_vbus2_status=0; ++ } ++ #endif //defined(__IS_AR9__) ++ #if defined(__IS_VR9__) ++ if(_core_if->core_no==0) ++ { ++ ifxusb_vbus1_status=0; ++ } ++ else ++ { ++ ifxusb_vbus2_status=0; ++ } ++ #endif //defined(__IS_VR9__) ++ #endif //defined(__UEIP__) ++} ++ ++ ++ ++/*! ++ \brief Read Current VBus status ++ \param _core_if Pointer of core_if structure ++ */ ++int ifxusb_vbus(ifxusb_core_if_t *_core_if) ++{ ++#if defined(__UEIP__) ++ #if defined(IFX_GPIO_USB_VBUS) || defined(IFX_LEDGPIO_USB_VBUS) || defined(IFX_LEDLED_USB_VBUS) ++ return (ifxusb_vbus_status); ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS1) || defined(IFX_LEDGPIO_USB_VBUS1) || defined(IFX_LEDLED_USB_VBUS1) ++ if(_core_if->core_no==0) ++ return (ifxusb_vbus1_status); ++ #endif ++ ++ #if defined(IFX_GPIO_USB_VBUS2) || defined(IFX_LEDGPIO_USB_VBUS2) || defined(IFX_LEDLED_USB_VBUS2) ++ if(_core_if->core_no==1) ++ return (ifxusb_vbus2_status); ++ #endif ++#else //defined(__UEIP__) ++#endif ++ return -1; ++} ++ ++#if defined(__UEIP__) ++#else ++ #if defined(__IS_TWINPASS__) ++ #define ADSL_BASE 0x20000 ++ #define CRI_BASE 0x31F00 ++ #define CRI_CCR0 CRI_BASE + 0x00 ++ #define CRI_CCR1 CRI_BASE + 0x01*4 ++ #define CRI_CDC0 CRI_BASE + 0x02*4 ++ #define CRI_CDC1 CRI_BASE + 0x03*4 ++ #define CRI_RST CRI_BASE + 0x04*4 ++ #define CRI_MASK0 CRI_BASE + 0x05*4 ++ #define CRI_MASK1 CRI_BASE + 0x06*4 ++ #define CRI_MASK2 CRI_BASE + 0x07*4 ++ #define CRI_STATUS0 CRI_BASE + 0x08*4 ++ #define CRI_STATUS1 CRI_BASE + 0x09*4 ++ #define CRI_STATUS2 CRI_BASE + 0x0A*4 ++ #define CRI_AMASK0 CRI_BASE + 0x0B*4 ++ #define CRI_AMASK1 CRI_BASE + 0x0C*4 ++ #define CRI_UPDCTL CRI_BASE + 0x0D*4 ++ #define CRI_MADST CRI_BASE + 0x0E*4 ++ // 0x0f is missing ++ #define CRI_EVENT0 CRI_BASE + 0x10*4 ++ #define CRI_EVENT1 CRI_BASE + 0x11*4 ++ #define CRI_EVENT2 CRI_BASE + 0x12*4 ++ ++ #define IRI_I_ENABLE 0x32000 ++ #define STY_SMODE 0x3c004 ++ #define AFE_TCR_0 0x3c0dc ++ #define AFE_ADDR_ADDR 0x3c0e8 ++ #define AFE_RDATA_ADDR 0x3c0ec ++ #define AFE_WDATA_ADDR 0x3c0f0 ++ #define AFE_CONFIG 0x3c0f4 ++ #define AFE_SERIAL_CFG 0x3c0fc ++ ++ #define DFE_BASE_ADDR 0xBE116000 ++ //#define DFE_BASE_ADDR 0x9E116000 ++ ++ #define MEI_FR_ARCINT_C (DFE_BASE_ADDR + 0x0000001C) ++ #define MEI_DBG_WADDR_C (DFE_BASE_ADDR + 0x00000024) ++ #define MEI_DBG_RADDR_C (DFE_BASE_ADDR + 0x00000028) ++ #define MEI_DBG_DATA_C (DFE_BASE_ADDR + 0x0000002C) ++ #define MEI_DBG_DECO_C (DFE_BASE_ADDR + 0x00000030) ++ #define MEI_DBG_MASTER_C (DFE_BASE_ADDR + 0x0000003C) ++ ++ static void WriteARCmem(uint32_t addr, uint32_t data) ++ { ++ writel(1 ,(volatile uint32_t *)MEI_DBG_MASTER_C); ++ writel(1 ,(volatile uint32_t *)MEI_DBG_DECO_C ); ++ writel(addr ,(volatile uint32_t *)MEI_DBG_WADDR_C ); ++ writel(data ,(volatile uint32_t *)MEI_DBG_DATA_C ); ++ while( (ifxusb_rreg((volatile uint32_t *)MEI_FR_ARCINT_C) & 0x20) != 0x20 ){}; ++ writel(0 ,(volatile uint32_t *)MEI_DBG_MASTER_C); ++ IFX_DEBUGP("WriteARCmem %08x %08x\n",addr,data); ++ }; ++ ++ static uint32_t ReadARCmem(uint32_t addr) ++ { ++ u32 data; ++ writel(1 ,(volatile uint32_t *)MEI_DBG_MASTER_C); ++ writel(1 ,(volatile uint32_t *)MEI_DBG_DECO_C ); ++ writel(addr ,(volatile uint32_t *)MEI_DBG_RADDR_C ); ++ while( (ifxusb_rreg((volatile uint32_t *)MEI_FR_ARCINT_C) & 0x20) != 0x20 ){}; ++ data = ifxusb_rreg((volatile uint32_t *)MEI_DBG_DATA_C ); ++ writel(0 ,(volatile uint32_t *)MEI_DBG_MASTER_C); ++ IFX_DEBUGP("ReadARCmem %08x %08x\n",addr,data); ++ return data; ++ }; ++ ++ void ifxusb_enable_afe_oc(void) ++ { ++ /* Start the clock */ ++ WriteARCmem(CRI_UPDCTL ,0x00000008); ++ WriteARCmem(CRI_CCR0 ,0x00000014); ++ WriteARCmem(CRI_CCR1 ,0x00000500); ++ WriteARCmem(AFE_CONFIG ,0x000001c8); ++ WriteARCmem(AFE_SERIAL_CFG,0x00000016); // (DANUBE_PCI_CFG_BASE+(1< ++#include "ifxusb_version.h" ++ ++ ++#include ++#include ++#include ++#include ++ ++#include "ifxusb_plat.h" ++#include "ifxusb_regs.h" ++#include "ifxusb_cif.h" ++ ++#ifdef __IS_DEVICE__ ++ #include "ifxpcd.h" ++#endif ++ ++#ifdef __IS_HOST__ ++ #include "ifxhcd.h" ++#endif ++ ++#include ++#include ++#include ++ ++ ++#ifdef __IS_HOST__ ++ extern char ifxusb_driver_name[]; ++ ++ #ifdef __IS_DUAL__ ++ extern ifxhcd_hcd_t ifxusb_hcd_1; ++ extern ifxhcd_hcd_t ifxusb_hcd_2; ++ extern char ifxusb_hcd_name_1[]; ++ extern char ifxusb_hcd_name_2[]; ++ #else ++ extern ifxhcd_hcd_t ifxusb_hcd; ++ extern char ifxusb_hcd_name[]; ++ #endif ++ ++#endif ++ ++#ifdef __IS_DEVICE__ ++ extern char ifxusb_driver_name[]; ++ ++ extern ifxpcd_pcd_t ifxusb_pcd; ++ extern char ifxusb_pcd_name[]; ++#endif ++ ++ ++//Attributes for sysfs (for 2.6 only) ++ ++extern struct device_attribute dev_attr_dbglevel; ++ ++#ifdef __IS_DUAL__ ++ extern struct device_attribute dev_attr_dump_params_1; ++ extern struct device_attribute dev_attr_dump_params_2; ++#else ++ extern struct device_attribute dev_attr_dump_params; ++#endif ++ ++#ifdef __IS_DUAL__ ++ extern struct device_attribute dev_attr_mode_1; ++ extern struct device_attribute dev_attr_mode_2; ++#else ++ extern struct device_attribute dev_attr_mode; ++#endif ++ ++#ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ extern struct device_attribute dev_attr_buspower_1; ++ extern struct device_attribute dev_attr_buspower_2; ++ extern struct device_attribute dev_attr_bussuspend_1; ++ extern struct device_attribute dev_attr_bussuspend_2; ++ extern struct device_attribute dev_attr_busconnected_1; ++ extern struct device_attribute dev_attr_busconnected_2; ++ extern struct device_attribute dev_attr_connectspeed_1; ++ extern struct device_attribute dev_attr_connectspeed_1; ++ #else ++ extern struct device_attribute dev_attr_buspower; ++ extern struct device_attribute dev_attr_bussuspend; ++ extern struct device_attribute dev_attr_busconnected; ++ extern struct device_attribute dev_attr_connectspeed; ++ #endif ++#endif //__IS_HOST__ ++ ++#ifdef __IS_DEVICE__ ++ extern struct device_attribute dev_attr_devspeed; ++ extern struct device_attribute dev_attr_enumspeed; ++#endif //__IS_DEVICE__ ++ ++#ifdef __ENABLE_DUMP__ ++ #ifdef __IS_DUAL__ ++ extern struct device_attribute dev_attr_dump_reg_1; ++ extern struct device_attribute dev_attr_dump_reg_2; ++ extern struct device_attribute dev_attr_dump_spram_1; ++ extern struct device_attribute dev_attr_dump_spram_2; ++ #ifdef __IS_HOST__ ++ extern struct device_attribute dev_attr_dump_host_state_1; ++ extern struct device_attribute dev_attr_dump_host_state_2; ++ #else ++ #endif ++ #else ++ extern struct device_attribute dev_attr_dump_reg; ++ extern struct device_attribute dev_attr_dump_spram; ++ #ifdef __IS_HOST__ ++ extern struct device_attribute dev_attr_dump_host_state; ++ #else ++ #endif ++ #endif ++#endif //__ENABLE_DUMP__ ++ ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++static ssize_t procfs_dbglevel_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++{ ++ #ifdef __IS_HOST__ ++ return sprintf( buf, "%08X\n",h_dbg_lvl ); ++ #else ++ return sprintf( buf, "%08X\n",d_dbg_lvl ); ++ #endif ++} ++ ++static ssize_t procfs_dbglevel_store(struct file *file, const char *buffer, unsigned long count, void *data) ++{ ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ #ifdef __IS_HOST__ ++ h_dbg_lvl =value; ++ #else ++ d_dbg_lvl =value; ++ #endif ++ //turn on and off power ++ return count; ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dbglevel_show( struct device *_dev, struct device_attribute *attr,char *buf) ++#else ++ static ssize_t sysfs_dbglevel_show( struct device *_dev, char *buf) ++#endif ++{ ++ #ifdef __IS_HOST__ ++ return sprintf( buf, "%08X\n",h_dbg_lvl ); ++ #else ++ return sprintf( buf, "%08X\n",d_dbg_lvl ); ++ #endif ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dbglevel_store( struct device *_dev, struct device_attribute *attr,const char *buffer, size_t count ) ++#else ++ static ssize_t sysfs_dbglevel_store( struct device *_dev, const char *buffer, size_t count ) ++#endif ++{ ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ #ifdef __IS_HOST__ ++ h_dbg_lvl =value; ++ #else ++ d_dbg_lvl =value; ++ #endif ++ //turn on and off power ++ return count; ++} ++ ++DEVICE_ATTR(dbglevel, S_IRUGO|S_IWUSR, sysfs_dbglevel_show, sysfs_dbglevel_store); ++ ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++static void ifxusb_dump_params(ifxusb_core_if_t *_core_if); ++ ++#ifdef __IS_DUAL__ ++ static void dump_params_1(void) ++ { ++ ifxusb_dump_params(&ifxusb_hcd_1.core_if); ++ } ++ static void dump_params_2(void) ++ { ++ ifxusb_dump_params(&ifxusb_hcd_2.core_if); ++ } ++ ++ static ssize_t procfs_dump_params_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_params_1(); ++ return 0; ++ } ++ static ssize_t procfs_dump_params_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_params_2(); ++ return 0; ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_params_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_params_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_params_1(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_params_1, S_IRUGO|S_IWUSR, sysfs_dump_params_show_1, NULL); ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_params_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_params_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_params_2(); ++ return 0; ++ } ++ ++ DEVICE_ATTR(dump_params_2, S_IRUGO|S_IWUSR, sysfs_dump_params_show_2, NULL); ++#else ++ static void dump_params(void) ++ { ++ #ifdef __IS_HOST__ ++ ifxusb_dump_params(&ifxusb_hcd.core_if); ++ #else ++ ifxusb_dump_params(&ifxusb_pcd.core_if); ++ #endif ++ } ++ ++ static ssize_t procfs_dump_params_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_params(); ++ return 0; ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_params_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_params_show( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_params(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_params, S_IRUGO|S_IWUSR, sysfs_dump_params_show, NULL); ++#endif ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#ifdef __IS_DUAL__ ++ static ssize_t mode_show_1(char *buf) ++ { ++ if((ifxusb_rreg(&ifxusb_hcd_1.core_if.core_global_regs->gintsts ) & 0x1) == 1) ++ return sprintf( buf, "HOST\n" ); ++ else ++ return sprintf( buf, "DEVICE(INCORRECT!)\n" ); ++ } ++ ++ static ssize_t mode_show_2(char *buf) ++ { ++ if((ifxusb_rreg(&ifxusb_hcd_2.core_if.core_global_regs->gintsts ) & 0x1) == 1) ++ return sprintf( buf, "HOST\n" ); ++ else ++ return sprintf( buf, "DEVICE(INCORRECT!)\n" ); ++ } ++ ++ static ssize_t procfs_mode_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return mode_show_1(buf); ++ } ++ static ssize_t procfs_mode_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return mode_show_2(buf); ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_mode_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_mode_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ return mode_show_1(buf); ++ } ++ ++ DEVICE_ATTR(mode_1, S_IRUGO|S_IWUSR, sysfs_mode_show_1, 0); ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_mode_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_mode_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ return mode_show_2(buf); ++ } ++ DEVICE_ATTR(mode_2, S_IRUGO|S_IWUSR, sysfs_mode_show_2, NULL); ++#else ++ static ssize_t mode_show(char *buf) ++ { ++ #ifdef __IS_HOST__ ++ if((ifxusb_rreg(&ifxusb_hcd.core_if.core_global_regs->gintsts ) & 0x1) == 1) ++ return sprintf( buf, "HOST\n" ); ++ else ++ return sprintf( buf, "DEVICE(INCORRECT!)\n" ); ++ #else ++ if((ifxusb_rreg(&ifxusb_pcd.core_if.core_global_regs->gintsts ) & 0x1) != 1) ++ return sprintf( buf, "DEVICE\n" ); ++ else ++ return sprintf( buf, "HOST(INCORRECT!)\n" ); ++ #endif ++ } ++ static ssize_t procfs_mode_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return mode_show(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_mode_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_mode_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return mode_show(buf); ++ } ++ DEVICE_ATTR(mode, S_IRUGO|S_IWUSR, sysfs_mode_show, NULL); ++#endif ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++#ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ static ssize_t buspower_show_1(char *buf) ++ { ++ if(ifxusb_vbus (&ifxusb_hcd_1.core_if)==1) return sprintf( buf, "1\n" ); ++ if(ifxusb_vbus (&ifxusb_hcd_1.core_if)==0) return sprintf( buf, "0\n" ); ++ return sprintf( buf, "UNKNOWN\n" ); ++ } ++ static void buspower_store_1(uint32_t value) ++ { ++ if (value==1) ifxusb_vbus_on (&ifxusb_hcd_1.core_if); ++ else if(value==0) ifxusb_vbus_off(&ifxusb_hcd_1.core_if); ++ } ++ static ssize_t buspower_show_2(char *buf) ++ { ++ if(ifxusb_vbus (&ifxusb_hcd_2.core_if)==1) return sprintf( buf, "1\n" ); ++ if(ifxusb_vbus (&ifxusb_hcd_2.core_if)==0) return sprintf( buf, "0\n" ); ++ return sprintf( buf, "UNKNOWN\n" ); ++ } ++ static void buspower_store_2(uint32_t value) ++ { ++ if (value==1) ifxusb_vbus_on (&ifxusb_hcd_2.core_if); ++ else if(value==0) ifxusb_vbus_off(&ifxusb_hcd_2.core_if); ++ } ++ static ssize_t procfs_buspower_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return buspower_show_1(buf); ++ } ++ static ssize_t procfs_buspower_store_1(struct file *file, const char *buffer, unsigned long count, void *data) ++ { ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ buspower_store_1(value); ++ return count; ++ } ++ static ssize_t procfs_buspower_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return buspower_show_2(buf); ++ } ++ static ssize_t procfs_buspower_store_2(struct file *file, const char *buffer, unsigned long count, void *data) ++ { ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ buspower_store_2(value); ++ return count; ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_buspower_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_buspower_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ return buspower_show_1(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_buspower_store_1( struct device *_dev, struct device_attribute *attr,const char *buffer, size_t count ) ++ #else ++ static ssize_t sysfs_buspower_store_1( struct device *_dev, const char *buffer, size_t count ) ++ #endif ++ { ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ buspower_store_1(value); ++ return count; ++ } ++ DEVICE_ATTR(buspower_1, S_IRUGO|S_IWUSR, sysfs_buspower_show_1, sysfs_buspower_store_1); ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_buspower_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_buspower_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ return buspower_show_2(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_buspower_store_2( struct device *_dev, struct device_attribute *attr,const char *buffer, size_t count ) ++ #else ++ static ssize_t sysfs_buspower_store_2( struct device *_dev, const char *buffer, size_t count ) ++ #endif ++ { ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ buspower_store_2(value); ++ return count; ++ } ++ DEVICE_ATTR(buspower_2, S_IRUGO|S_IWUSR, sysfs_buspower_show_2, sysfs_buspower_store_2); ++ #else ++ static ssize_t buspower_show(char *buf) ++ { ++ if(ifxusb_vbus (&ifxusb_hcd.core_if)==1) return sprintf( buf, "1\n" ); ++ if(ifxusb_vbus (&ifxusb_hcd.core_if)==0) return sprintf( buf, "0\n" ); ++ return sprintf( buf, "UNKNOWN\n" ); ++ } ++ static void buspower_store(uint32_t value) ++ { ++ if (value==1) ifxusb_vbus_on (&ifxusb_hcd.core_if); ++ else if(value==0) ifxusb_vbus_off(&ifxusb_hcd.core_if); ++ } ++ static ssize_t procfs_buspower_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return buspower_show(buf); ++ } ++ static ssize_t procfs_buspower_store(struct file *file, const char *buffer, unsigned long count, void *data) ++ { ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ buspower_store(value); ++ return count; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_buspower_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_buspower_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return buspower_show(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_buspower_store( struct device *_dev, struct device_attribute *attr,const char *buffer, size_t count ) ++ #else ++ static ssize_t sysfs_buspower_store( struct device *_dev, const char *buffer, size_t count ) ++ #endif ++ { ++ char buf[10]; ++ int i = 0; ++ uint32_t value; ++ if (copy_from_user(buf, &buffer[i], sizeof("0xFFFFFFFF\n")+1)) ++ return -EFAULT; ++ value = simple_strtoul(buf, NULL, 16); ++ buspower_store(value); ++ return count; ++ } ++ DEVICE_ATTR(buspower, S_IRUGO|S_IWUSR, sysfs_buspower_show, sysfs_buspower_store); ++ #endif ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ ++ #ifdef __IS_DUAL__ ++ static ssize_t bussuspend_show_1(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd_1.core_if.hprt0); ++ return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp); ++ } ++ static ssize_t bussuspend_show_2(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd_2.core_if.hprt0); ++ return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp); ++ } ++ ++ static ssize_t procfs_bussuspend_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return bussuspend_show_1(buf); ++ } ++ static ssize_t procfs_bussuspend_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return bussuspend_show_2(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_bussuspend_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_bussuspend_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ return bussuspend_show_1(buf); ++ } ++ DEVICE_ATTR(bussuspend_1, S_IRUGO|S_IWUSR, sysfs_bussuspend_show_1, 0); ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_bussuspend_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_bussuspend_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ return bussuspend_show_2(buf); ++ } ++ DEVICE_ATTR(bussuspend_2, S_IRUGO|S_IWUSR, sysfs_bussuspend_show_2, 0); ++ #else ++ static ssize_t bussuspend_show(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd.core_if.hprt0); ++ return sprintf (buf, "Bus Suspend = 0x%x\n", val.b.prtsusp); ++ } ++ static ssize_t procfs_bussuspend_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return bussuspend_show(buf); ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_bussuspend_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_bussuspend_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return bussuspend_show(buf); ++ } ++ DEVICE_ATTR(bussuspend, S_IRUGO|S_IWUSR, sysfs_bussuspend_show, 0); ++ #endif ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ #ifdef __IS_DUAL__ ++ static ssize_t busconnected_show_1(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd_1.core_if.hprt0); ++ return sprintf (buf, "Bus Connected = 0x%x\n", val.b.prtconnsts); ++ } ++ static ssize_t busconnected_show_2(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd_2.core_if.hprt0); ++ return sprintf (buf, "Bus Connected = 0x%x\n", val.b.prtconnsts); ++ } ++ ++ static ssize_t procfs_busconnected_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return busconnected_show_1(buf); ++ } ++ static ssize_t procfs_busconnected_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return busconnected_show_2(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_busconnected_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_busconnected_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ return busconnected_show_1(buf); ++ } ++ DEVICE_ATTR(busconnected_1, S_IRUGO|S_IWUSR, sysfs_busconnected_show_1, 0); ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_busconnected_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_busconnected_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ return busconnected_show_2(buf); ++ } ++ DEVICE_ATTR(busconnected_2, S_IRUGO|S_IWUSR, sysfs_busconnected_show_2, 0); ++ #else ++ static ssize_t busconnected_show(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd.core_if.hprt0); ++ return sprintf (buf, "Bus Connected = 0x%x\n", val.b.prtconnsts); ++ } ++ static ssize_t procfs_busconnected_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return busconnected_show(buf); ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_busconnected_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_busconnected_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return busconnected_show(buf); ++ } ++ DEVICE_ATTR(busconnected, S_IRUGO|S_IWUSR, sysfs_busconnected_show, 0); ++ #endif ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ #ifdef __IS_DUAL__ ++ static ssize_t connectspeed_show_1(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd_1.core_if.hprt0); ++ if( val.b.prtspd ==0) return sprintf (buf, "Bus Speed = High (%d)\n", val.b.prtspd); ++ if( val.b.prtspd ==1) return sprintf (buf, "Bus Speed = Full (%d)\n", val.b.prtspd); ++ if( val.b.prtspd ==2) return sprintf (buf, "Bus Speed = Low (%d)\n", val.b.prtspd); ++ return sprintf (buf, "Bus Speed = Unknown (%d)\n", val.b.prtspd); ++ } ++ static ssize_t connectspeed_show_2(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd_2.core_if.hprt0); ++ if( val.b.prtspd ==0) return sprintf (buf, "Bus Speed = High (%d)\n", val.b.prtspd); ++ if( val.b.prtspd ==1) return sprintf (buf, "Bus Speed = Full (%d)\n", val.b.prtspd); ++ if( val.b.prtspd ==2) return sprintf (buf, "Bus Speed = Low (%d)\n", val.b.prtspd); ++ return sprintf (buf, "Bus Speed = Unknown (%d)\n", val.b.prtspd); ++ } ++ ++ static ssize_t procfs_connectspeed_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return connectspeed_show_1(buf); ++ } ++ static ssize_t procfs_connectspeed_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return connectspeed_show_2(buf); ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_connectspeed_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_connectspeed_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ return connectspeed_show_1(buf); ++ } ++ DEVICE_ATTR(connectspeed_1, S_IRUGO|S_IWUSR, sysfs_connectspeed_show_1, 0); ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_connectspeed_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_connectspeed_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ return connectspeed_show_2(buf); ++ } ++ DEVICE_ATTR(connectspeed_2, S_IRUGO|S_IWUSR, sysfs_connectspeed_show_2, 0); ++ #else ++ static ssize_t connectspeed_show(char *buf) ++ { ++ hprt0_data_t val; ++ val.d32 = ifxusb_rreg(ifxusb_hcd.core_if.hprt0); ++ if( val.b.prtspd ==0) return sprintf (buf, "Bus Speed = High (%d)\n", val.b.prtspd); ++ if( val.b.prtspd ==1) return sprintf (buf, "Bus Speed = Full (%d)\n", val.b.prtspd); ++ if( val.b.prtspd ==2) return sprintf (buf, "Bus Speed = Low (%d)\n", val.b.prtspd); ++ return sprintf (buf, "Bus Speed = Unknown (%d)\n", val.b.prtspd); ++ } ++ ++ static ssize_t procfs_connectspeed_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return connectspeed_show(buf); ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_connectspeed_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_connectspeed_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return connectspeed_show(buf); ++ } ++ DEVICE_ATTR(connectspeed, S_IRUGO|S_IWUSR, sysfs_connectspeed_show, 0); ++ #endif ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++#endif ++ ++ ++#ifdef __IS_DEVICE__ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ static ssize_t devspeed_show(char *buf) ++ { ++ dcfg_data_t val; ++ val.d32 = ifxusb_rreg(&ifxusb_pcd.core_if.dev_global_regs->dcfg); ++ if( val.b.devspd ==0) return sprintf (buf, "Dev Speed = High (%d)\n", val.b.devspd); ++ if( val.b.devspd ==1) return sprintf (buf, "Dev Speed = Full (%d)\n", val.b.devspd); ++ if( val.b.devspd ==3) return sprintf (buf, "Dev Speed = Full (%d)\n", val.b.devspd); ++ return sprintf (buf, "Dev Speed = Unknown (%d)\n", val.b.devspd); ++ } ++ ++ static ssize_t procfs_devspeed_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return devspeed_show(buf); ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_devspeed_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_devspeed_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return devspeed_show(buf); ++ } ++ DEVICE_ATTR(devspeed, S_IRUGO|S_IWUSR, sysfs_devspeed_show, 0); ++ ++ static ssize_t enumspeed_show(char *buf) ++ { ++ dsts_data_t val; ++ val.d32 = ifxusb_rreg(&ifxusb_pcd.core_if.dev_global_regs->dsts); ++ if( val.b.enumspd ==0) return sprintf (buf, "Enum Speed = High (%d)\n", val.b.enumspd); ++ if( val.b.enumspd ==1) return sprintf (buf, "Enum Speed = Full (%d)\n", val.b.enumspd); ++ if( val.b.enumspd ==2) return sprintf (buf, "Enum Speed = Low (%d)\n", val.b.enumspd); ++ return sprintf (buf, "Enum Speed = invalid(%d)\n", val.b.enumspd); ++ } ++ ++ static ssize_t procfs_enumspeed_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ return enumspeed_show(buf); ++ } ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_enumspeed_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_enumspeed_show( struct device *_dev, char *buf) ++ #endif ++ { ++ return enumspeed_show(buf); ++ } ++ DEVICE_ATTR(enumspeed, S_IRUGO|S_IWUSR, sysfs_enumspeed_show, 0); ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++#endif ++ ++ ++////////////////////////////////////////////////////////////////////////////////// ++#ifdef __ENABLE_DUMP__ ++ ++ #ifdef __IS_DUAL__ ++ static void dump_reg_1(void) ++ { ++ ifxusb_dump_registers(&ifxusb_hcd_1.core_if); ++ } ++ static void dump_reg_2(void) ++ { ++ ifxusb_dump_registers(&ifxusb_hcd_2.core_if); ++ } ++ ++ static ssize_t procfs_dump_reg_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_reg_1(); ++ return 0; ++ } ++ static ssize_t procfs_dump_reg_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_reg_2(); ++ return 0; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_reg_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_reg_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_reg_1(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_reg_1, S_IRUGO|S_IWUSR, sysfs_dump_reg_show_1, 0); ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_reg_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_reg_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_reg_2(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_reg_2, S_IRUGO|S_IWUSR, sysfs_dump_reg_show_2, 0); ++ #else ++ static void dump_reg(void) ++ { ++ #ifdef __IS_HOST__ ++ ifxusb_dump_registers(&ifxusb_hcd.core_if); ++ #endif ++ #ifdef __IS_DEVICE__ ++ ifxusb_dump_registers(&ifxusb_pcd.core_if); ++ #endif ++ } ++ static ssize_t procfs_dump_reg_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_reg(); ++ return 0; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_reg_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_reg_show( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_reg(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_reg, S_IRUGO|S_IWUSR, sysfs_dump_reg_show, 0); ++ #endif ++ ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ #ifdef __IS_DUAL__ ++ static void dump_spram_1(void) ++ { ++ ifxusb_dump_spram(&ifxusb_hcd_1.core_if); ++ } ++ static void dump_spram_2(void) ++ { ++ ifxusb_dump_spram(&ifxusb_hcd_2.core_if); ++ } ++ ++ static ssize_t procfs_dump_spram_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_spram_1(); ++ return 0; ++ } ++ static ssize_t procfs_dump_spram_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_spram_2(); ++ return 0; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_spram_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_spram_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_spram_1(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_spram_1, S_IRUGO|S_IWUSR, sysfs_dump_spram_show_1, 0); ++ ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_spram_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_spram_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_spram_2(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_spram_2, S_IRUGO|S_IWUSR, sysfs_dump_spram_show_2, 0); ++ #else ++ static void dump_spram(void) ++ { ++ #ifdef __IS_HOST__ ++ ifxusb_dump_spram(&ifxusb_hcd.core_if); ++ #endif ++ #ifdef __IS_DEVICE__ ++ ifxusb_dump_spram(&ifxusb_pcd.core_if); ++ #endif ++ } ++ static ssize_t procfs_dump_spram_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ dump_spram(); ++ return 0; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_spram_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_spram_show( struct device *_dev,char *buf) ++ #endif ++ { ++ dump_spram(); ++ return 0; ++ } ++ DEVICE_ATTR(dump_spram, S_IRUGO|S_IWUSR, sysfs_dump_spram_show, 0); ++ #endif ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ #ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ static ssize_t procfs_dump_host_state_show_1(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ ifxhcd_dump_state(&ifxusb_hcd_1); ++ return 0; ++ } ++ static ssize_t procfs_dump_host_state_show_2(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ ifxhcd_dump_state(&ifxusb_hcd_2); ++ return 0; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_host_state_show_1( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_host_state_show_1( struct device *_dev,char *buf) ++ #endif ++ { ++ ifxhcd_dump_state(&ifxusb_hcd_1); ++ return 0; ++ } ++ DEVICE_ATTR(dump_host_state_1, S_IRUGO|S_IWUSR, sysfs_dump_host_state_show_1, 0); ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_host_state_show_2( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_host_state_show_2( struct device *_dev,char *buf) ++ #endif ++ { ++ ifxhcd_dump_state(&ifxusb_hcd_2); ++ return 0; ++ } ++ DEVICE_ATTR(dump_host_state_2, S_IRUGO|S_IWUSR, sysfs_dump_host_state_show_2, 0); ++ #else ++ static ssize_t procfs_dump_host_state_show(char *buf, char **start, off_t offset, int count, int *eof, void *data) ++ { ++ ifxhcd_dump_state(&ifxusb_hcd); ++ return 0; ++ } ++ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ static ssize_t sysfs_dump_host_state_show( struct device *_dev, struct device_attribute *attr,char *buf) ++ #else ++ static ssize_t sysfs_dump_host_state_show( struct device *_dev,char *buf) ++ #endif ++ { ++ ifxhcd_dump_state(&ifxusb_hcd); ++ return 0; ++ } ++ DEVICE_ATTR(dump_host_state, S_IRUGO|S_IWUSR, sysfs_dump_host_state_show, 0); ++ #endif ++ ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++///////////////////////////////////////////////////////////////////////////////////////////////////// ++ ++ #endif //IS_HOST_ ++ ++#endif //__ENABLE_DUMP__ ++ ++////////////////////////////////////////////////////////////////////////////////// ++ ++static int ifx_proc_addproc(char *funcname, read_proc_t *hookfuncr, write_proc_t *hookfuncw); ++static void ifx_proc_delproc(char *funcname); ++ ++////////////////////////////////////////////////////////////////////////////////// ++ ++/*! ++ \brief This function create the sysfs and procfs entries ++ \param[in] _dev Pointer of device structure, if applied ++ */ ++void ifxusb_attr_create (void *_dev) ++{ ++ int error; ++ ++ struct device *dev = (struct device *) _dev; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ error = ifx_proc_addproc("dbglevel", procfs_dbglevel_show, procfs_dbglevel_store); ++ error = device_create_file(dev, &dev_attr_dbglevel); ++ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("dump_params_1", procfs_dump_params_show_1, NULL); ++ error = ifx_proc_addproc("dump_params_2", procfs_dump_params_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_dump_params_1); ++ error = device_create_file(dev, &dev_attr_dump_params_2); ++ #else ++ error = ifx_proc_addproc("dump_params", procfs_dump_params_show, NULL); ++ error = device_create_file(dev, &dev_attr_dump_params); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("mode_1", procfs_mode_show_1, NULL); ++ error = ifx_proc_addproc("mode_2", procfs_mode_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_mode_1); ++ error = device_create_file(dev, &dev_attr_mode_2); ++ #else ++ error = ifx_proc_addproc("mode", procfs_mode_show, NULL); ++ error = device_create_file(dev, &dev_attr_mode); ++ #endif ++ ++ #ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("buspower_1", procfs_buspower_show_1, procfs_buspower_store_1); ++ error = ifx_proc_addproc("buspower_2", procfs_buspower_show_2, procfs_buspower_store_2); ++ error = device_create_file(dev, &dev_attr_buspower_1); ++ error = device_create_file(dev, &dev_attr_buspower_2); ++ #else ++ error = ifx_proc_addproc("buspower", procfs_buspower_show, procfs_buspower_store); ++ error = device_create_file(dev, &dev_attr_buspower); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("bussuspend_1", procfs_bussuspend_show_1, NULL); ++ error = ifx_proc_addproc("bussuspend_2", procfs_bussuspend_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_bussuspend_1); ++ error = device_create_file(dev, &dev_attr_bussuspend_2); ++ #else ++ error = ifx_proc_addproc("bussuspend", procfs_bussuspend_show, NULL); ++ error = device_create_file(dev, &dev_attr_bussuspend); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("busconnected_1", procfs_busconnected_show_1, NULL); ++ error = ifx_proc_addproc("busconnected_2", procfs_busconnected_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_busconnected_1); ++ error = device_create_file(dev, &dev_attr_busconnected_2); ++ #else ++ error = ifx_proc_addproc("busconnected", procfs_busconnected_show, NULL); ++ error = device_create_file(dev, &dev_attr_busconnected); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("connectspeed_1", procfs_connectspeed_show_1, NULL); ++ error = ifx_proc_addproc("connectspeed_2", procfs_connectspeed_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_connectspeed_1); ++ error = device_create_file(dev, &dev_attr_connectspeed_2); ++ #else ++ error = ifx_proc_addproc("connectspeed", procfs_connectspeed_show, NULL); ++ error = device_create_file(dev, &dev_attr_connectspeed); ++ #endif ++ #endif ++ ++ #ifdef __IS_DEVICE__ ++ error = ifx_proc_addproc("devspeed", procfs_devspeed_show, NULL); ++ error = device_create_file(dev, &dev_attr_devspeed); ++ error = ifx_proc_addproc("enumspeed", procfs_enumspeed_show, NULL); ++ error = device_create_file(dev, &dev_attr_enumspeed); ++ #endif ++ ++ ////////////////////////////////////////////////////// ++ #ifdef __ENABLE_DUMP__ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("dump_reg_1", procfs_dump_reg_show_1, NULL); ++ error = ifx_proc_addproc("dump_reg_2", procfs_dump_reg_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_dump_reg_1); ++ error = device_create_file(dev, &dev_attr_dump_reg_2); ++ #else ++ error = ifx_proc_addproc("dump_reg", procfs_dump_reg_show, NULL); ++ error = device_create_file(dev, &dev_attr_dump_reg); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("dump_spram_1", procfs_dump_spram_show_1, NULL); ++ error = ifx_proc_addproc("dump_spram_2", procfs_dump_spram_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_dump_spram_1); ++ error = device_create_file(dev, &dev_attr_dump_spram_2); ++ #else ++ error = ifx_proc_addproc("dump_spram", procfs_dump_spram_show, NULL); ++ error = device_create_file(dev, &dev_attr_dump_spram); ++ #endif ++ ++ #ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ error = ifx_proc_addproc("dump_host_state_1", procfs_dump_host_state_show_1, NULL); ++ error = ifx_proc_addproc("dump_host_state_2", procfs_dump_host_state_show_2, NULL); ++ error = device_create_file(dev, &dev_attr_dump_host_state_1); ++ error = device_create_file(dev, &dev_attr_dump_host_state_2); ++ #else ++ error = ifx_proc_addproc("dump_host_state", procfs_dump_host_state_show, NULL); ++ error = device_create_file(dev, &dev_attr_dump_host_state); ++ #endif ++ #endif ++ #endif //__ENABLE_DUMP__ ++ ////////////////////////////////////////////////////// ++} ++ ++ ++/*! ++ \brief This function remove the sysfs and procfs entries ++ \param[in] _dev Pointer of device structure, if applied ++ */ ++void ifxusb_attr_remove (void *_dev) ++{ ++ struct device *dev = (struct device *) _dev; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ifx_proc_delproc("dbglevel"); ++ device_remove_file(dev, &dev_attr_dbglevel); ++ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("dump_params_1"); ++ ifx_proc_delproc("dump_params_2"); ++ device_remove_file(dev, &dev_attr_dump_params_1); ++ device_remove_file(dev, &dev_attr_dump_params_2); ++ #else ++ ifx_proc_delproc("dump_params"); ++ device_remove_file(dev, &dev_attr_dump_params); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("mode_1"); ++ ifx_proc_delproc("mode_2"); ++ device_remove_file(dev, &dev_attr_mode_1); ++ device_remove_file(dev, &dev_attr_mode_2); ++ #else ++ ifx_proc_delproc("mode"); ++ device_remove_file(dev, &dev_attr_mode); ++ #endif ++ ++ #ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("buspower_1"); ++ ifx_proc_delproc("buspower_2"); ++ device_remove_file(dev, &dev_attr_buspower_1); ++ device_remove_file(dev, &dev_attr_buspower_2); ++ #else ++ ifx_proc_delproc("buspower"); ++ device_remove_file(dev, &dev_attr_buspower); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("bussuspend_1"); ++ ifx_proc_delproc("bussuspend_2"); ++ device_remove_file(dev, &dev_attr_bussuspend_1); ++ device_remove_file(dev, &dev_attr_bussuspend_2); ++ #else ++ ifx_proc_delproc("bussuspend"); ++ device_remove_file(dev, &dev_attr_bussuspend); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("busconnected_1"); ++ ifx_proc_delproc("busconnected_2"); ++ device_remove_file(dev, &dev_attr_busconnected_1); ++ device_remove_file(dev, &dev_attr_busconnected_2); ++ #else ++ ifx_proc_delproc("busconnected"); ++ device_remove_file(dev, &dev_attr_busconnected); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("connectspeed_1"); ++ ifx_proc_delproc("connectspeed_2"); ++ device_remove_file(dev, &dev_attr_connectspeed_1); ++ device_remove_file(dev, &dev_attr_connectspeed_2); ++ #else ++ ifx_proc_delproc("connectspeed"); ++ device_remove_file(dev, &dev_attr_connectspeed); ++ #endif ++ #endif ++ ++ #ifdef __IS_DEVICE__ ++ ifx_proc_delproc("devspeed"); ++ device_remove_file(dev, &dev_attr_devspeed); ++ ifx_proc_delproc("enumspeed"); ++ device_remove_file(dev, &dev_attr_enumspeed); ++ #endif ++ ++ #ifdef __ENABLE_DUMP__ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("dump_reg_1"); ++ ifx_proc_delproc("dump_reg_2"); ++ device_remove_file(dev, &dev_attr_dump_reg_1); ++ device_remove_file(dev, &dev_attr_dump_reg_2); ++ #else ++ ifx_proc_delproc("dump_reg"); ++ device_remove_file(dev, &dev_attr_dump_reg); ++ #endif ++ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("dump_spram_1"); ++ ifx_proc_delproc("dump_spram_2"); ++ device_remove_file(dev, &dev_attr_dump_spram_1); ++ device_remove_file(dev, &dev_attr_dump_spram_2); ++ #else ++ ifx_proc_delproc("dump_spram"); ++ device_remove_file(dev, &dev_attr_dump_spram); ++ #endif ++ ++ #ifdef __IS_HOST__ ++ #ifdef __IS_DUAL__ ++ ifx_proc_delproc("dump_host_state_1"); ++ ifx_proc_delproc("dump_host_state_2"); ++ device_remove_file(dev, &dev_attr_dump_host_state_1); ++ device_remove_file(dev, &dev_attr_dump_host_state_2); ++ #else ++ ifx_proc_delproc("dump_host_state"); ++ device_remove_file(dev, &dev_attr_dump_host_state); ++ #endif ++ #endif ++ #endif //__ENABLE_DUMP__ ++ /* AVM/WK fix: del IFXUSB root dir*/ ++ ifx_proc_delproc(NULL); ++} ++ ++static struct proc_dir_entry * proc_ifx_root = NULL; ++ ++/* initialize the proc file system and make a dir named /proc/[name] */ ++static void ifx_proc_init(void) ++{ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ proc_ifx_root = proc_mkdir(ifxusb_driver_name, (void *)0); ++ if (!proc_ifx_root){ ++ IFX_PRINT("%s proc initialization failed! \n", ifxusb_driver_name); ++ return; ++ } ++} ++ ++/* proc file system add function for debugging. */ ++static int ifx_proc_addproc(char *funcname, read_proc_t *hookfuncr, write_proc_t *hookfuncw) ++{ ++ struct proc_dir_entry *pe; ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ if (!proc_ifx_root) ++ ifx_proc_init(); ++ ++ if (hookfuncw == NULL) ++ { ++ pe = create_proc_read_entry(funcname, S_IRUGO, proc_ifx_root, hookfuncr, NULL); ++ if (!pe) ++ { ++ IFX_PRINT("ERROR in creating read proc entry (%s)! \n", funcname); ++ return -1; ++ } ++ } ++ else ++ { ++ pe = create_proc_entry(funcname, S_IRUGO | S_IWUGO, proc_ifx_root); ++ if (pe) ++ { ++ pe->read_proc = hookfuncr; ++ pe->write_proc = hookfuncw; ++ } ++ else ++ { ++ IFX_PRINT("ERROR in creating proc entry (%s)! \n", funcname); ++ return -1; ++ } ++ } ++ return 0; ++} ++ ++ ++/* proc file system del function for removing module. */ ++static void ifx_proc_delproc(char *funcname) ++{ ++/* AVM/WK Fix*/ ++ if (funcname != NULL) { ++ remove_proc_entry(funcname, proc_ifx_root); ++ } else { ++ remove_proc_entry(ifxusb_driver_name, NULL); ++ proc_ifx_root = NULL; ++ } ++} ++ ++static void ifxusb_dump_params(ifxusb_core_if_t *_core_if) ++{ ++ ifxusb_params_t *params=&_core_if->params; ++ ++ #ifdef __IS_HOST__ ++ IFX_PRINT("IFXUSB Dump Parameters ( Host Mode) \n"); ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++ IFX_PRINT("IFXUSB Dump Parameters ( Device Mode) \n"); ++ #endif //__IS_DEVICE__ ++ ++ #ifdef __DESC_DMA__ ++ IFX_PRINT("DMA: Hermes DMA\n"); ++ #else ++ IFX_PRINT("DMA: Non-Desc DMA\n"); ++ #endif ++ IFX_PRINT(" Burst size: %d\n",params->dma_burst_size); ++ ++ if (params->speed==1) ++ IFX_PRINT("Full Speed only\n"); ++ else if(params->speed==0) ++ IFX_PRINT("Full/Hign Speed\n"); ++ else ++ IFX_PRINT("Unkonwn setting (%d) for Speed\n",params->speed); ++ ++ IFX_PRINT("Total Data FIFO size: %d(0x%06X) DWord, %d(0x%06X) Bytes\n", ++ params->data_fifo_size,params->data_fifo_size, ++ params->data_fifo_size*4, params->data_fifo_size*4 ++ ); ++ ++ #ifdef __IS_DEVICE__ ++ IFX_PRINT("Rx FIFO size: %d(0x%06X) DWord, %d(0x%06X) Bytes\n", ++ params->rx_fifo_size,params->rx_fifo_size, ++ params->rx_fifo_size*4, params->rx_fifo_size*4 ++ ); ++ { ++ int i; ++ for(i=0;itx_fifo_size[i],params->tx_fifo_size[i], ++ params->tx_fifo_size[i]*4, params->tx_fifo_size[i]*4 ++ ); ++ } ++ } ++ #ifdef __DED_FIFO__ ++ IFX_PRINT("Treshold : %s Rx:%d Tx:%d \n", ++ (params->thr_ctl)?"On":"Off",params->tx_thr_length,params->rx_thr_length); ++ #endif ++ #else //__IS_HOST__ ++ IFX_PRINT("Host Channels: %d\n",params->host_channels); ++ ++ IFX_PRINT("Rx FIFO size: %d(0x%06X) DWord, %d(0x%06X) Bytes\n", ++ params->data_fifo_size,params->data_fifo_size, ++ params->data_fifo_size*4, params->data_fifo_size*4 ++ ); ++ ++ IFX_PRINT("NP Tx FIFO size: %d(0x%06X) DWord, %d(0x%06X) Bytes\n", ++ params->nperio_tx_fifo_size,params->nperio_tx_fifo_size, ++ params->nperio_tx_fifo_size*4, params->nperio_tx_fifo_size*4 ++ ); ++ ++ IFX_PRINT(" P Tx FIFO size: %d(0x%06X) DWord, %d(0x%06X) Bytes\n", ++ params->perio_tx_fifo_size,params->perio_tx_fifo_size, ++ params->perio_tx_fifo_size*4, params->perio_tx_fifo_size*4 ++ ); ++ #endif //__IS_HOST__ ++ ++ IFX_PRINT("Max Transfer size: %d(0x%06X) Bytes\n", ++ params->max_transfer_size,params->max_transfer_size ++ ); ++ IFX_PRINT("Max Packet Count: %d(0x%06X)\n", ++ params->max_packet_count,params->max_packet_count ++ ); ++ ++ IFX_PRINT("PHY UTMI Width: %d\n",params->phy_utmi_width); ++ ++ IFX_PRINT("Turn Around Time: HS:%d FS:%d\n",params->turn_around_time_hs,params->turn_around_time_fs); ++ IFX_PRINT("Timeout Calibration: HS:%d FS:%d\n",params->timeout_cal_hs,params->timeout_cal_fs); ++ ++ ++ IFX_PRINT("==================================================\n"); ++ IFX_PRINT("End of Parameters Dump\n"); ++ IFX_PRINT("==================================================\n"); ++} ++ ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_driver.c b/drivers/usb/ifxhcd/ifxusb_driver.c +new file mode 100644 +index 0000000..2334905 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_driver.c +@@ -0,0 +1,970 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_driver.c ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : The provides the initialization and cleanup entry ++ ** points for the IFX USB driver. This module can be ++ ** dynamically loaded with insmod command or built-in ++ ** with kernel. When loaded or executed the ifxusb_driver_init ++ ** function is called. When the module is removed (using rmmod), ++ ** the ifxusb_driver_cleanup function is called. ++ *****************************************************************************/ ++ ++/*! ++ \file ifxusb_driver.c ++ \brief This file contains the loading/unloading interface to the Linux driver. ++*/ ++ ++#include ++#include "ifxusb_version.h" ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include /* permission constants */ ++#include ++#include ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20) ++ #include ++#endif ++ ++#include ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) ++ #include ++#endif ++ ++#include "ifxusb_plat.h" ++ ++#include "ifxusb_cif.h" ++ ++#ifdef __IS_HOST__ ++ #include "ifxhcd.h" ++ ++ #define USB_DRIVER_DESC "IFX USB HCD driver" ++ const char ifxusb_driver_name[] = "ifxusb_hcd"; ++ ++ #ifdef __IS_DUAL__ ++ ifxhcd_hcd_t ifxusb_hcd_1; ++ ifxhcd_hcd_t ifxusb_hcd_2; ++ const char ifxusb_hcd_name_1[] = "ifxusb_hcd_1"; ++ const char ifxusb_hcd_name_2[] = "ifxusb_hcd_2"; ++ #else ++ ifxhcd_hcd_t ifxusb_hcd; ++ const char ifxusb_hcd_name[] = "ifxusb_hcd"; ++ #endif ++ ++ #if defined(__DO_OC_INT__) ++ static unsigned int oc_int_installed=0; ++ static ifxhcd_hcd_t *oc_int_id=NULL; ++ #endif ++#endif ++ ++#ifdef __IS_DEVICE__ ++ #include "ifxpcd.h" ++ ++ #define USB_DRIVER_DESC "IFX USB PCD driver" ++ const char ifxusb_driver_name[] = "ifxusb_pcd"; ++ ++ ifxpcd_pcd_t ifxusb_pcd; ++ const char ifxusb_pcd_name[] = "ifxusb_pcd"; ++#endif ++ ++/* Global Debug Level Mask. */ ++#ifdef __IS_HOST__ ++ uint32_t h_dbg_lvl = 0x00; ++#endif ++ ++#ifdef __IS_DEVICE__ ++ uint32_t d_dbg_lvl = 0x00; ++#endif ++ ++ifxusb_params_t ifxusb_module_params; ++ ++static void parse_parms(void); ++ ++ ++#include ++#define IFX_USB0_IR (INT_NUM_IM1_IRL0 + 22) ++#define IFX_USB1_IR (INT_NUM_IM2_IRL0 + 19) ++ ++/*! ++ \brief This function is called when a driver is unregistered. This happens when ++ the rmmod command is executed. The device may or may not be electrically ++ present. If it is present, the driver stops device processing. Any resources ++ used on behalf of this device are freed. ++*/ ++static int ifxusb_driver_remove(struct platform_device *_dev) ++{ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ #ifdef __IS_HOST__ ++ #if defined(__DO_OC_INT__) ++ #if defined(__DO_OC_INT_ENABLE__) ++ ifxusb_oc_int_off(); ++ #endif ++ ++ if(oc_int_installed && oc_int_id) ++ free_irq((unsigned int)IFXUSB_OC_IRQ, oc_int_id ); ++ oc_int_installed=0; ++ oc_int_id=NULL; ++ #endif ++ ++ #if defined(__IS_DUAL__) ++ ifxhcd_remove(&ifxusb_hcd_1); ++ ifxusb_core_if_remove(&ifxusb_hcd_1.core_if ); ++ ifxhcd_remove(&ifxusb_hcd_2); ++ ifxusb_core_if_remove(&ifxusb_hcd_2.core_if ); ++ #else ++ ifxhcd_remove(&ifxusb_hcd); ++ ifxusb_core_if_remove(&ifxusb_hcd.core_if ); ++ #endif ++ #endif ++ ++ #ifdef __IS_DEVICE__ ++ ifxpcd_remove(); ++ ifxusb_core_if_remove(&ifxusb_pcd.core_if ); ++ #endif ++ ++ /* Remove the device attributes */ ++ ++ ifxusb_attr_remove(&_dev->dev); ++ ++ return 0; ++} ++ ++ ++/* Function to setup the structures to control one usb core running as host*/ ++#ifdef __IS_HOST__ ++/*! ++ \brief inlined by ifxusb_driver_probe(), handling host mode probing. Run at each host core. ++*/ ++ static inline int ifxusb_driver_probe_h(ifxhcd_hcd_t *_hcd, ++ int _irq, ++ uint32_t _iobase, ++ uint32_t _fifomem, ++ uint32_t _fifodbg ++ ) ++ { ++ int retval = 0; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ++#ifdef __DEV_NEW__ ++ ifxusb_power_off (&_hcd->core_if); ++ ifxusb_phy_power_off (&_hcd->core_if); // Test ++ mdelay(500); ++#endif //__DEV_NEW__ ++ ifxusb_power_on (&_hcd->core_if); ++ mdelay(50); ++ ifxusb_phy_power_on (&_hcd->core_if); // Test ++ mdelay(50); ++ ifxusb_hard_reset(&_hcd->core_if); ++ retval =ifxusb_core_if_init(&_hcd->core_if, ++ _irq, ++ _iobase, ++ _fifomem, ++ _fifodbg); ++ if(retval) ++ return retval; ++ ++ ifxusb_host_core_init(&_hcd->core_if,&ifxusb_module_params); ++ ++ ifxusb_disable_global_interrupts( &_hcd->core_if); ++ ++ /* The driver is now initialized and need to be registered into Linux USB sub-system */ ++ ++ retval = ifxhcd_init(_hcd); // hook the hcd into usb ss ++ ++ if (retval != 0) ++ { ++ IFX_ERROR("_hcd_init failed\n"); ++ return retval; ++ } ++ ++ //ifxusb_enable_global_interrupts( _hcd->core_if ); // this should be done at hcd_start , including hcd_interrupt ++ return 0; ++ } ++#endif //__IS_HOST__ ++ ++#ifdef __IS_DEVICE__ ++/*! ++ \brief inlined by ifxusb_driver_probe(), handling device mode probing. ++*/ ++ static inline int ifxusb_driver_probe_d(ifxpcd_pcd_t *_pcd, ++ int _irq, ++ uint32_t _iobase, ++ uint32_t _fifomem, ++ uint32_t _fifodbg ++ ) ++ { ++ int retval = 0; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++#ifdef __DEV_NEW__ ++ ifxusb_power_off (&_pcd->core_if); ++ ifxusb_phy_power_off (&_pcd->core_if); // Test ++ mdelay(500); ++#endif // __DEV_NEW__ ++ ifxusb_power_on (&_pcd->core_if); ++ mdelay(50); ++ ifxusb_phy_power_on (&_pcd->core_if); // Test ++ mdelay(50); ++ ifxusb_hard_reset(&_pcd->core_if); ++ retval =ifxusb_core_if_init(&_pcd->core_if, ++ _irq, ++ _iobase, ++ _fifomem, ++ _fifodbg); ++ if(retval) ++ return retval; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ifxusb_dev_core_init(&_pcd->core_if,&ifxusb_module_params); ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ifxusb_disable_global_interrupts( &_pcd->core_if); ++ ++ /* The driver is now initialized and need to be registered into ++ Linux USB Gadget sub-system ++ */ ++ retval = ifxpcd_init(); ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ++ if (retval != 0) ++ { ++ IFX_ERROR("_pcd_init failed\n"); ++ return retval; ++ } ++ //ifxusb_enable_global_interrupts( _pcd->core_if ); // this should be done at gadget bind or start ++ return 0; ++ } ++#endif //__IS_DEVICE__ ++ ++ ++ ++/*! ++ \brief This function is called by module management in 2.6 kernel or by ifxusb_driver_init with 2.4 kernel ++ It is to probe and setup IFXUSB core(s). ++*/ ++static int ifxusb_driver_probe(struct platform_device *_dev) ++{ ++ int retval = 0; ++ int *pins = _dev->dev.platform_data; ++ if (ltq_is_vr9()) { ++ gpio_request(6, "id1"); ++ gpio_request(9, "id2"); ++ gpio_direction_input(6); ++ gpio_direction_input(9); ++ } ++ if (pins) { ++ if (pins[0]) { ++ gpio_request(pins[0], "vbus1"); ++ gpio_direction_output(pins[0], 1); ++ } ++ if (pins[1] && ltq_is_vr9()) { ++ gpio_request(pins[1], "vbus2"); ++ gpio_direction_output(pins[1], 1); ++ } ++ } ++ // Parsing and store the parameters ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ parse_parms(); ++ ++ #ifdef __IS_HOST__ ++ #if defined(__IS_DUAL__) ++ memset(&ifxusb_hcd_1, 0, sizeof(ifxhcd_hcd_t)); ++ memset(&ifxusb_hcd_2, 0, sizeof(ifxhcd_hcd_t)); ++ ++ ifxusb_hcd_1.core_if.core_no=0; ++ ifxusb_hcd_2.core_if.core_no=1; ++ ifxusb_hcd_1.core_if.core_name=(char *)ifxusb_hcd_name_1; ++ ifxusb_hcd_2.core_if.core_name=(char *)ifxusb_hcd_name_2; ++ ++ ifxusb_hcd_1.dev=&_dev->dev; ++ ifxusb_hcd_2.dev=&_dev->dev; ++ ++ retval = ifxusb_driver_probe_h(&ifxusb_hcd_1, ++ IFX_USB0_IR, ++ IFXUSB1_IOMEM_BASE, ++ IFXUSB1_FIFOMEM_BASE, ++ IFXUSB1_FIFODBG_BASE ++ ); ++ if(retval) ++ goto ifxusb_driver_probe_fail; ++ ++ retval = ifxusb_driver_probe_h(&ifxusb_hcd_2, ++ IFX_USB1_IR, ++ IFXUSB2_IOMEM_BASE, ++ IFXUSB2_FIFOMEM_BASE, ++ IFXUSB2_FIFODBG_BASE ++ ); ++ if(retval) ++ goto ifxusb_driver_probe_fail; ++ ++ #elif defined(__IS_FIRST__) ++ memset(&ifxusb_hcd, 0, sizeof(ifxhcd_hcd_t)); ++ ++ ifxusb_hcd.core_if.core_no=0; ++ ifxusb_hcd.core_if.core_name=(char *)ifxusb_hcd_name; ++ ++ ifxusb_hcd.dev=&_dev->dev; ++ ++ retval = ifxusb_driver_probe_h(&ifxusb_hcd, ++ IFX_USB0_IR, ++ IFXUSB1_IOMEM_BASE, ++ IFXUSB1_FIFOMEM_BASE, ++ IFXUSB1_FIFODBG_BASE ++ ); ++ if(retval) ++ goto ifxusb_driver_probe_fail; ++ ++ #elif defined(__IS_SECOND__) ++ memset(&ifxusb_hcd, 0, sizeof(ifxhcd_hcd_t)); ++ ++ ifxusb_hcd.core_if.core_no=1; ++ ifxusb_hcd.core_if.core_name=(char *)ifxusb_hcd_name; ++ ++ ifxusb_hcd.dev=&_dev->dev; ++ ++ retval = ifxusb_driver_probe_h(&ifxusb_hcd, ++ IFX_USB1_IR, ++ IFXUSB2_IOMEM_BASE, ++ IFXUSB2_FIFOMEM_BASE, ++ IFXUSB2_FIFODBG_BASE ++ ); ++ if(retval) ++ goto ifxusb_driver_probe_fail; ++ ++ #else ++ memset(&ifxusb_hcd, 0, sizeof(ifxhcd_hcd_t)); ++ ++ ifxusb_hcd.core_if.core_no=0; ++ ifxusb_hcd.core_if.core_name=(char *)ifxusb_hcd_name; ++ ++ ifxusb_hcd.dev=&_dev->dev; ++ ++ retval = ifxusb_driver_probe_h(&ifxusb_hcd, ++ IFXUSB_IRQ, ++ IFXUSB_IOMEM_BASE, ++ IFXUSB_FIFOMEM_BASE, ++ IFXUSB_FIFODBG_BASE ++ ); ++ if(retval) ++ goto ifxusb_driver_probe_fail; ++ #endif ++ ++ #if defined(__DO_OC_INT__) ++ IFXUSB_DEBUGPL( DBG_CIL, "registering (overcurrent) handler for irq%d\n", IFXUSB_OC_IRQ); ++ #if defined(__IS_DUAL__) ++ request_irq((unsigned int)IFXUSB_OC_IRQ, &ifx_hcd_oc_irq, ++// SA_INTERRUPT|SA_SHIRQ, "ifxusb_oc", (void *)&ifxusb_hcd_1); ++ IRQF_DISABLED | IRQF_SHARED, "ifxusb_oc", (void *)&ifxusb_hcd_1); ++ oc_int_id=&ifxusb_hcd_1; ++ #else ++ request_irq((unsigned int)IFXUSB_OC_IRQ, &ifx_hcd_oc_irq, ++// SA_INTERRUPT|SA_SHIRQ, "ifxusb_oc", (void *)&ifxusb_hcd); ++ IRQF_DISABLED | IRQF_SHARED, "ifxusb_oc", (void *)&ifxusb_hcd); ++ oc_int_id=&ifxusb_hcd; ++ #endif ++ oc_int_installed=1; ++ ++ #if defined(__DO_OC_INT_ENABLE__) ++ ifxusb_oc_int_on(); ++ #endif ++ #endif ++ ++ #endif ++ ++ #ifdef __IS_DEVICE__ ++ memset(&ifxusb_pcd, 0, sizeof(ifxpcd_pcd_t)); ++ ifxusb_pcd.core_if.core_name=(char *)&ifxusb_pcd_name[0]; ++ ++ ifxusb_pcd.dev=&_dev->dev; ++ ++ #if defined(__IS_FIRST__) ++ ifxusb_pcd.core_if.core_no=0; ++ retval = ifxusb_driver_probe_d(&ifxusb_pcd, ++ IFXUSB1_IRQ, ++ IFXUSB1_IOMEM_BASE, ++ IFXUSB1_FIFOMEM_BASE, ++ IFXUSB1_FIFODBG_BASE ++ ); ++ #elif defined(__IS_SECOND__) ++ ifxusb_pcd.core_if.core_no=1; ++ retval = ifxusb_driver_probe_d(&ifxusb_pcd, ++ IFXUSB2_IRQ, ++ IFXUSB2_IOMEM_BASE, ++ IFXUSB2_FIFOMEM_BASE, ++ IFXUSB2_FIFODBG_BASE ++ ); ++ #else ++ ifxusb_pcd.core_if.core_no=0; ++ retval = ifxusb_driver_probe_d(&ifxusb_pcd, ++ IFXUSB_IRQ, ++ IFXUSB_IOMEM_BASE, ++ IFXUSB_FIFOMEM_BASE, ++ IFXUSB_FIFODBG_BASE ++ ); ++ #endif ++ if(retval) ++ goto ifxusb_driver_probe_fail; ++ #endif ++ ++ ifxusb_attr_create(&_dev->dev); ++ ++ return 0; ++ ++ifxusb_driver_probe_fail: ++ ifxusb_driver_remove(_dev); ++ return retval; ++} ++ ++ ++ ++/*! ++ \brief This function is called when the ifxusb_driver is installed with the insmod command. ++*/ ++ ++ ++static struct platform_driver ifxusb_driver = { ++ .driver = { ++ .name = ifxusb_driver_name, ++ .owner = THIS_MODULE, ++ }, ++ .probe = ifxusb_driver_probe, ++ .remove = ifxusb_driver_remove, ++}; ++ ++int __init ifxusb_driver_init(void) ++{ ++ int retval = 0; ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ IFX_PRINT("%s: version %s\n", ifxusb_driver_name, IFXUSB_VERSION); ++ ++ retval = platform_driver_register(&ifxusb_driver); ++ ++ if (retval < 0) { ++ IFX_ERROR("%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ return retval; ++} ++ ++#if 0 // 2.4 ++ int __init ifxusb_driver_init(void) ++ { ++ int retval = 0; ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ IFX_PRINT("%s: version %s\n", ifxusb_driver_name, IFXUSB_VERSION); ++ retval = ifxusb_driver_probe(); ++ ++ if (retval < 0) { ++ IFX_ERROR("%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++ ++ return retval; ++ } ++#endif ++ ++module_init(ifxusb_driver_init); ++ ++ ++/*! ++ \brief This function is called when the driver is removed from the kernel ++ with the rmmod command. The driver unregisters itself with its bus ++ driver. ++*/ ++ ++void __exit ifxusb_driver_cleanup(void) ++{ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ++ platform_driver_unregister(&ifxusb_driver); ++ ++ IFX_PRINT("%s module removed\n", ifxusb_driver_name); ++} ++#if 0 ++ void __exit ifxusb_driver_cleanup(void) ++ { ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ ifxusb_driver_remove(); ++ IFX_PRINT("%s module removed\n", ifxusb_driver_name); ++ } ++#endif ++module_exit(ifxusb_driver_cleanup); ++ ++ ++ ++MODULE_DESCRIPTION(USB_DRIVER_DESC); ++MODULE_AUTHOR("Infineon"); ++MODULE_LICENSE("GPL"); ++ ++ ++ ++// Parameters set when loaded ++//static long dbg_lvl =0xFFFFFFFF; ++static long dbg_lvl =0; ++static short dma_burst_size =-1; ++static short speed =-1; ++static long data_fifo_size =-1; ++#ifdef __IS_DEVICE__ ++ static long rx_fifo_size =-1; ++ #ifdef __DED_FIFO__ ++ static long tx_fifo_size_00 =-1; ++ static long tx_fifo_size_01 =-1; ++ static long tx_fifo_size_02 =-1; ++ static long tx_fifo_size_03 =-1; ++ static long tx_fifo_size_04 =-1; ++ static long tx_fifo_size_05 =-1; ++ static long tx_fifo_size_06 =-1; ++ static long tx_fifo_size_07 =-1; ++ static long tx_fifo_size_08 =-1; ++ static long tx_fifo_size_09 =-1; ++ static long tx_fifo_size_10 =-1; ++ static long tx_fifo_size_11 =-1; ++ static long tx_fifo_size_12 =-1; ++ static long tx_fifo_size_13 =-1; ++ static long tx_fifo_size_14 =-1; ++ static long tx_fifo_size_15 =-1; ++ static short thr_ctl=-1; ++ static long tx_thr_length =-1; ++ static long rx_thr_length =-1; ++ #else ++ static long nperio_tx_fifo_size =-1; ++ static long perio_tx_fifo_size_01 =-1; ++ static long perio_tx_fifo_size_02 =-1; ++ static long perio_tx_fifo_size_03 =-1; ++ static long perio_tx_fifo_size_04 =-1; ++ static long perio_tx_fifo_size_05 =-1; ++ static long perio_tx_fifo_size_06 =-1; ++ static long perio_tx_fifo_size_07 =-1; ++ static long perio_tx_fifo_size_08 =-1; ++ static long perio_tx_fifo_size_09 =-1; ++ static long perio_tx_fifo_size_10 =-1; ++ static long perio_tx_fifo_size_11 =-1; ++ static long perio_tx_fifo_size_12 =-1; ++ static long perio_tx_fifo_size_13 =-1; ++ static long perio_tx_fifo_size_14 =-1; ++ static long perio_tx_fifo_size_15 =-1; ++ #endif ++ static short dev_endpoints =-1; ++#endif ++ ++#ifdef __IS_HOST__ ++ static long rx_fifo_size =-1; ++ static long nperio_tx_fifo_size =-1; ++ static long perio_tx_fifo_size =-1; ++ static short host_channels =-1; ++#endif ++ ++static long max_transfer_size =-1; ++static long max_packet_count =-1; ++static long phy_utmi_width =-1; ++static long turn_around_time_hs =-1; ++static long turn_around_time_fs =-1; ++static long timeout_cal_hs =-1; ++static long timeout_cal_fs =-1; ++ ++/*! ++ \brief Parsing the parameters taken when module load ++*/ ++static void parse_parms(void) ++{ ++ ++ IFX_DEBUGPL(DBG_ENTRY, "%s() %d\n", __func__, __LINE__ ); ++ #ifdef __IS_HOST__ ++ h_dbg_lvl=dbg_lvl; ++ #endif ++ #ifdef __IS_DEVICE__ ++ d_dbg_lvl=dbg_lvl; ++ #endif ++ ++ switch(dma_burst_size) ++ { ++ case 0: ++ case 1: ++ case 4: ++ case 8: ++ case 16: ++ ifxusb_module_params.dma_burst_size=dma_burst_size; ++ break; ++ default: ++ ifxusb_module_params.dma_burst_size=default_param_dma_burst_size; ++ } ++ ++ if(speed==0 || speed==1) ++ ifxusb_module_params.speed=speed; ++ else ++ ifxusb_module_params.speed=default_param_speed; ++ ++ if(max_transfer_size>=2048 && max_transfer_size<=65535) ++ ifxusb_module_params.max_transfer_size=max_transfer_size; ++ else ++ ifxusb_module_params.max_transfer_size=default_param_max_transfer_size; ++ ++ if(max_packet_count>=15 && max_packet_count<=511) ++ ifxusb_module_params.max_packet_count=max_packet_count; ++ else ++ ifxusb_module_params.max_packet_count=default_param_max_packet_count; ++ ++ switch(phy_utmi_width) ++ { ++ case 8: ++ case 16: ++ ifxusb_module_params.phy_utmi_width=phy_utmi_width; ++ break; ++ default: ++ ifxusb_module_params.phy_utmi_width=default_param_phy_utmi_width; ++ } ++ ++ if(turn_around_time_hs>=0 && turn_around_time_hs<=7) ++ ifxusb_module_params.turn_around_time_hs=turn_around_time_hs; ++ else ++ ifxusb_module_params.turn_around_time_hs=default_param_turn_around_time_hs; ++ ++ if(turn_around_time_fs>=0 && turn_around_time_fs<=7) ++ ifxusb_module_params.turn_around_time_fs=turn_around_time_fs; ++ else ++ ifxusb_module_params.turn_around_time_fs=default_param_turn_around_time_fs; ++ ++ if(timeout_cal_hs>=0 && timeout_cal_hs<=7) ++ ifxusb_module_params.timeout_cal_hs=timeout_cal_hs; ++ else ++ ifxusb_module_params.timeout_cal_hs=default_param_timeout_cal_hs; ++ ++ if(timeout_cal_fs>=0 && timeout_cal_fs<=7) ++ ifxusb_module_params.timeout_cal_fs=timeout_cal_fs; ++ else ++ ifxusb_module_params.timeout_cal_fs=default_param_timeout_cal_fs; ++ ++ if(data_fifo_size>=32 && data_fifo_size<=32768) ++ ifxusb_module_params.data_fifo_size=data_fifo_size; ++ else ++ ifxusb_module_params.data_fifo_size=default_param_data_fifo_size; ++ ++ #ifdef __IS_HOST__ ++ if(host_channels>=1 && host_channels<=16) ++ ifxusb_module_params.host_channels=host_channels; ++ else ++ ifxusb_module_params.host_channels=default_param_host_channels; ++ ++ if(rx_fifo_size>=16 && rx_fifo_size<=32768) ++ ifxusb_module_params.rx_fifo_size=rx_fifo_size; ++ else ++ ifxusb_module_params.rx_fifo_size=default_param_rx_fifo_size; ++ ++ if(nperio_tx_fifo_size>=16 && nperio_tx_fifo_size<=32768) ++ ifxusb_module_params.nperio_tx_fifo_size=nperio_tx_fifo_size; ++ else ++ ifxusb_module_params.nperio_tx_fifo_size=default_param_nperio_tx_fifo_size; ++ ++ if(perio_tx_fifo_size>=16 && perio_tx_fifo_size<=32768) ++ ifxusb_module_params.perio_tx_fifo_size=perio_tx_fifo_size; ++ else ++ ifxusb_module_params.perio_tx_fifo_size=default_param_perio_tx_fifo_size; ++ #endif //__IS_HOST__ ++ ++ #ifdef __IS_DEVICE__ ++ if(rx_fifo_size>=16 && rx_fifo_size<=32768) ++ ifxusb_module_params.rx_fifo_size=rx_fifo_size; ++ else ++ ifxusb_module_params.rx_fifo_size=default_param_rx_fifo_size; ++ #ifdef __DED_FIFO__ ++ if(tx_fifo_size_00>=16 && tx_fifo_size_00<=32768) ++ ifxusb_module_params.tx_fifo_size[ 0]=tx_fifo_size_00; ++ else ++ ifxusb_module_params.tx_fifo_size[ 0]=default_param_tx_fifo_size_00; ++ if(tx_fifo_size_01>=0 && tx_fifo_size_01<=32768) ++ ifxusb_module_params.tx_fifo_size[ 1]=tx_fifo_size_01; ++ else ++ ifxusb_module_params.tx_fifo_size[ 1]=default_param_tx_fifo_size_01; ++ if(tx_fifo_size_02>=0 && tx_fifo_size_02<=32768) ++ ifxusb_module_params.tx_fifo_size[ 2]=tx_fifo_size_02; ++ else ++ ifxusb_module_params.tx_fifo_size[ 2]=default_param_tx_fifo_size_02; ++ if(tx_fifo_size_03>=0 && tx_fifo_size_03<=32768) ++ ifxusb_module_params.tx_fifo_size[ 3]=tx_fifo_size_03; ++ else ++ ifxusb_module_params.tx_fifo_size[ 3]=default_param_tx_fifo_size_03; ++ if(tx_fifo_size_04>=0 && tx_fifo_size_04<=32768) ++ ifxusb_module_params.tx_fifo_size[ 4]=tx_fifo_size_04; ++ else ++ ifxusb_module_params.tx_fifo_size[ 4]=default_param_tx_fifo_size_04; ++ if(tx_fifo_size_05>=0 && tx_fifo_size_05<=32768) ++ ifxusb_module_params.tx_fifo_size[ 5]=tx_fifo_size_05; ++ else ++ ifxusb_module_params.tx_fifo_size[ 5]=default_param_tx_fifo_size_05; ++ if(tx_fifo_size_06>=0 && tx_fifo_size_06<=32768) ++ ifxusb_module_params.tx_fifo_size[ 6]=tx_fifo_size_06; ++ else ++ ifxusb_module_params.tx_fifo_size[ 6]=default_param_tx_fifo_size_06; ++ if(tx_fifo_size_07>=0 && tx_fifo_size_07<=32768) ++ ifxusb_module_params.tx_fifo_size[ 7]=tx_fifo_size_07; ++ else ++ ifxusb_module_params.tx_fifo_size[ 7]=default_param_tx_fifo_size_07; ++ if(tx_fifo_size_08>=0 && tx_fifo_size_08<=32768) ++ ifxusb_module_params.tx_fifo_size[ 8]=tx_fifo_size_08; ++ else ++ ifxusb_module_params.tx_fifo_size[ 8]=default_param_tx_fifo_size_08; ++ if(tx_fifo_size_09>=0 && tx_fifo_size_09<=32768) ++ ifxusb_module_params.tx_fifo_size[ 9]=tx_fifo_size_09; ++ else ++ ifxusb_module_params.tx_fifo_size[ 9]=default_param_tx_fifo_size_09; ++ if(tx_fifo_size_10>=0 && tx_fifo_size_10<=32768) ++ ifxusb_module_params.tx_fifo_size[10]=tx_fifo_size_10; ++ else ++ ifxusb_module_params.tx_fifo_size[10]=default_param_tx_fifo_size_10; ++ if(tx_fifo_size_11>=0 && tx_fifo_size_11<=32768) ++ ifxusb_module_params.tx_fifo_size[11]=tx_fifo_size_11; ++ else ++ ifxusb_module_params.tx_fifo_size[11]=default_param_tx_fifo_size_11; ++ if(tx_fifo_size_12>=0 && tx_fifo_size_12<=32768) ++ ifxusb_module_params.tx_fifo_size[12]=tx_fifo_size_12; ++ else ++ ifxusb_module_params.tx_fifo_size[12]=default_param_tx_fifo_size_12; ++ if(tx_fifo_size_13>=0 && tx_fifo_size_13<=32768) ++ ifxusb_module_params.tx_fifo_size[13]=tx_fifo_size_13; ++ else ++ ifxusb_module_params.tx_fifo_size[13]=default_param_tx_fifo_size_13; ++ if(tx_fifo_size_14>=0 && tx_fifo_size_14<=32768) ++ ifxusb_module_params.tx_fifo_size[14]=tx_fifo_size_14; ++ else ++ ifxusb_module_params.tx_fifo_size[14]=default_param_tx_fifo_size_14; ++ if(tx_fifo_size_15>=0 && tx_fifo_size_15<=32768) ++ ifxusb_module_params.tx_fifo_size[15]=tx_fifo_size_15; ++ else ++ ifxusb_module_params.tx_fifo_size[15]=default_param_tx_fifo_size_15; ++ if(thr_ctl==0 || thr_ctl==1) ++ ifxusb_module_params.thr_ctl=thr_ctl; ++ else ++ ifxusb_module_params.thr_ctl=default_param_thr_ctl; ++ if(tx_thr_length>=16 && tx_thr_length<=511) ++ ifxusb_module_params.tx_thr_length=tx_thr_length; ++ else ++ ifxusb_module_params.tx_thr_length=default_param_tx_thr_length; ++ if(rx_thr_length>=16 && rx_thr_length<=511) ++ ifxusb_module_params.rx_thr_length=rx_thr_length; ++ else ++ ifxusb_module_params.rx_thr_length=default_param_rx_thr_length; ++ #else //__DED_FIFO__ ++ if(nperio_tx_fifo_size>=16 && nperio_tx_fifo_size<=32768) ++ ifxusb_module_params.tx_fifo_size[ 0]=nperio_tx_fifo_size; ++ else ++ ifxusb_module_params.tx_fifo_size[ 0]=default_param_nperio_tx_fifo_size; ++ if(perio_tx_fifo_size_01>=0 && perio_tx_fifo_size_01<=32768) ++ ifxusb_module_params.tx_fifo_size[ 1]=perio_tx_fifo_size_01; ++ else ++ ifxusb_module_params.tx_fifo_size[ 1]=default_param_perio_tx_fifo_size_01; ++ if(perio_tx_fifo_size_02>=0 && perio_tx_fifo_size_02<=32768) ++ ifxusb_module_params.tx_fifo_size[ 2]=perio_tx_fifo_size_02; ++ else ++ ifxusb_module_params.tx_fifo_size[ 2]=default_param_perio_tx_fifo_size_02; ++ if(perio_tx_fifo_size_03>=0 && perio_tx_fifo_size_03<=32768) ++ ifxusb_module_params.tx_fifo_size[ 3]=perio_tx_fifo_size_03; ++ else ++ ifxusb_module_params.tx_fifo_size[ 3]=default_param_perio_tx_fifo_size_03; ++ if(perio_tx_fifo_size_04>=0 && perio_tx_fifo_size_04<=32768) ++ ifxusb_module_params.tx_fifo_size[ 4]=perio_tx_fifo_size_04; ++ else ++ ifxusb_module_params.tx_fifo_size[ 4]=default_param_perio_tx_fifo_size_04; ++ if(perio_tx_fifo_size_05>=0 && perio_tx_fifo_size_05<=32768) ++ ifxusb_module_params.tx_fifo_size[ 5]=perio_tx_fifo_size_05; ++ else ++ ifxusb_module_params.tx_fifo_size[ 5]=default_param_perio_tx_fifo_size_05; ++ if(perio_tx_fifo_size_06>=0 && perio_tx_fifo_size_06<=32768) ++ ifxusb_module_params.tx_fifo_size[ 6]=perio_tx_fifo_size_06; ++ else ++ ifxusb_module_params.tx_fifo_size[ 6]=default_param_perio_tx_fifo_size_06; ++ if(perio_tx_fifo_size_07>=0 && perio_tx_fifo_size_07<=32768) ++ ifxusb_module_params.tx_fifo_size[ 7]=perio_tx_fifo_size_07; ++ else ++ ifxusb_module_params.tx_fifo_size[ 7]=default_param_perio_tx_fifo_size_07; ++ if(perio_tx_fifo_size_08>=0 && perio_tx_fifo_size_08<=32768) ++ ifxusb_module_params.tx_fifo_size[ 8]=perio_tx_fifo_size_08; ++ else ++ ifxusb_module_params.tx_fifo_size[ 8]=default_param_perio_tx_fifo_size_08; ++ if(perio_tx_fifo_size_09>=0 && perio_tx_fifo_size_09<=32768) ++ ifxusb_module_params.tx_fifo_size[ 9]=perio_tx_fifo_size_09; ++ else ++ ifxusb_module_params.tx_fifo_size[ 9]=default_param_perio_tx_fifo_size_09; ++ if(perio_tx_fifo_size_10>=0 && perio_tx_fifo_size_10<=32768) ++ ifxusb_module_params.tx_fifo_size[10]=perio_tx_fifo_size_10; ++ else ++ ifxusb_module_params.tx_fifo_size[10]=default_param_perio_tx_fifo_size_10; ++ if(perio_tx_fifo_size_11>=0 && perio_tx_fifo_size_11<=32768) ++ ifxusb_module_params.tx_fifo_size[11]=perio_tx_fifo_size_11; ++ else ++ ifxusb_module_params.tx_fifo_size[11]=default_param_perio_tx_fifo_size_11; ++ if(perio_tx_fifo_size_12>=0 && perio_tx_fifo_size_12<=32768) ++ ifxusb_module_params.tx_fifo_size[12]=perio_tx_fifo_size_12; ++ else ++ ifxusb_module_params.tx_fifo_size[12]=default_param_perio_tx_fifo_size_12; ++ if(perio_tx_fifo_size_13>=0 && perio_tx_fifo_size_13<=32768) ++ ifxusb_module_params.tx_fifo_size[13]=perio_tx_fifo_size_13; ++ else ++ ifxusb_module_params.tx_fifo_size[13]=default_param_perio_tx_fifo_size_13; ++ if(perio_tx_fifo_size_14>=0 && perio_tx_fifo_size_14<=32768) ++ ifxusb_module_params.tx_fifo_size[14]=perio_tx_fifo_size_14; ++ else ++ ifxusb_module_params.tx_fifo_size[14]=default_param_perio_tx_fifo_size_14; ++ if(perio_tx_fifo_size_15>=0 && perio_tx_fifo_size_15<=32768) ++ ifxusb_module_params.tx_fifo_size[15]=perio_tx_fifo_size_15; ++ else ++ ifxusb_module_params.tx_fifo_size[15]=default_param_perio_tx_fifo_size_15; ++ #endif //__DED_FIFO__ ++ #endif //__IS_DEVICE__ ++} ++ ++ ++ ++ ++ ++ ++ ++module_param(dbg_lvl, long, 0444); ++MODULE_PARM_DESC(dbg_lvl, "Debug level."); ++ ++module_param(dma_burst_size, short, 0444); ++MODULE_PARM_DESC(dma_burst_size, "DMA Burst Size 0, 1, 4, 8, 16"); ++ ++module_param(speed, short, 0444); ++MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed"); ++ ++module_param(data_fifo_size, long, 0444); ++MODULE_PARM_DESC(data_fifo_size, "Total number of words in the data FIFO memory 32-32768"); ++ ++#ifdef __IS_DEVICE__ ++ module_param(rx_fifo_size, long, 0444); ++ MODULE_PARM_DESC(rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++ ++ #ifdef __DED_FIFO__ ++ module_param(tx_fifo_size_00, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_00, "Number of words in the Tx FIFO #00 16-32768"); ++ module_param(tx_fifo_size_01, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_01, "Number of words in the Tx FIFO #01 0-32768"); ++ module_param(tx_fifo_size_02, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_02, "Number of words in the Tx FIFO #02 0-32768"); ++ module_param(tx_fifo_size_03, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_03, "Number of words in the Tx FIFO #03 0-32768"); ++ module_param(tx_fifo_size_04, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_04, "Number of words in the Tx FIFO #04 0-32768"); ++ module_param(tx_fifo_size_05, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_05, "Number of words in the Tx FIFO #05 0-32768"); ++ module_param(tx_fifo_size_06, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_06, "Number of words in the Tx FIFO #06 0-32768"); ++ module_param(tx_fifo_size_07, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_07, "Number of words in the Tx FIFO #07 0-32768"); ++ module_param(tx_fifo_size_08, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_08, "Number of words in the Tx FIFO #08 0-32768"); ++ module_param(tx_fifo_size_09, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_09, "Number of words in the Tx FIFO #09 0-32768"); ++ module_param(tx_fifo_size_10, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_10, "Number of words in the Tx FIFO #10 0-32768"); ++ module_param(tx_fifo_size_11, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_11, "Number of words in the Tx FIFO #11 0-32768"); ++ module_param(tx_fifo_size_12, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_12, "Number of words in the Tx FIFO #12 0-32768"); ++ module_param(tx_fifo_size_13, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_13, "Number of words in the Tx FIFO #13 0-32768"); ++ module_param(tx_fifo_size_14, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_14, "Number of words in the Tx FIFO #14 0-32768"); ++ module_param(tx_fifo_size_15, long, 0444); ++ MODULE_PARM_DESC(tx_fifo_size_15, "Number of words in the Tx FIFO #15 0-32768"); ++ ++ module_param(thr_ctl, short, 0444); ++ MODULE_PARM_DESC(thr_ctl, "0=Without 1=With Theshold Ctrl"); ++ ++ module_param(tx_thr_length, long, 0444); ++ MODULE_PARM_DESC(tx_thr_length, "TX Threshold length"); ++ ++ module_param(rx_thr_length, long, 0444); ++ MODULE_PARM_DESC(rx_thr_length, "RX Threshold length"); ++ ++ #else ++ module_param(nperio_tx_fifo_size, long, 0444); ++ MODULE_PARM_DESC(nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768"); ++ ++ module_param(perio_tx_fifo_size_01, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_01, "Number of words in the periodic Tx FIFO #01 0-32768"); ++ module_param(perio_tx_fifo_size_02, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_02, "Number of words in the periodic Tx FIFO #02 0-32768"); ++ module_param(perio_tx_fifo_size_03, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_03, "Number of words in the periodic Tx FIFO #03 0-32768"); ++ module_param(perio_tx_fifo_size_04, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_04, "Number of words in the periodic Tx FIFO #04 0-32768"); ++ module_param(perio_tx_fifo_size_05, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_05, "Number of words in the periodic Tx FIFO #05 0-32768"); ++ module_param(perio_tx_fifo_size_06, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_06, "Number of words in the periodic Tx FIFO #06 0-32768"); ++ module_param(perio_tx_fifo_size_07, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_07, "Number of words in the periodic Tx FIFO #07 0-32768"); ++ module_param(perio_tx_fifo_size_08, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_08, "Number of words in the periodic Tx FIFO #08 0-32768"); ++ module_param(perio_tx_fifo_size_09, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_09, "Number of words in the periodic Tx FIFO #09 0-32768"); ++ module_param(perio_tx_fifo_size_10, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_10, "Number of words in the periodic Tx FIFO #10 0-32768"); ++ module_param(perio_tx_fifo_size_11, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_11, "Number of words in the periodic Tx FIFO #11 0-32768"); ++ module_param(perio_tx_fifo_size_12, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_12, "Number of words in the periodic Tx FIFO #12 0-32768"); ++ module_param(perio_tx_fifo_size_13, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_13, "Number of words in the periodic Tx FIFO #13 0-32768"); ++ module_param(perio_tx_fifo_size_14, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_14, "Number of words in the periodic Tx FIFO #14 0-32768"); ++ module_param(perio_tx_fifo_size_15, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size_15, "Number of words in the periodic Tx FIFO #15 0-32768"); ++ #endif//__DED_FIFO__ ++ module_param(dev_endpoints, short, 0444); ++ MODULE_PARM_DESC(dev_endpoints, "The number of endpoints in addition to EP0 available for device mode 1-15"); ++#endif ++ ++#ifdef __IS_HOST__ ++ module_param(rx_fifo_size, long, 0444); ++ MODULE_PARM_DESC(rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++ ++ module_param(nperio_tx_fifo_size, long, 0444); ++ MODULE_PARM_DESC(nperio_tx_fifo_size, "Number of words in the non-periodic Tx FIFO 16-32768"); ++ ++ module_param(perio_tx_fifo_size, long, 0444); ++ MODULE_PARM_DESC(perio_tx_fifo_size, "Number of words in the host periodic Tx FIFO 16-32768"); ++ ++ module_param(host_channels, short, 0444); ++ MODULE_PARM_DESC(host_channels, "The number of host channel registers to use 1-16"); ++#endif ++ ++module_param(max_transfer_size, long, 0444); ++MODULE_PARM_DESC(max_transfer_size, "The maximum transfer size supported in bytes 2047-65535"); ++ ++module_param(max_packet_count, long, 0444); ++MODULE_PARM_DESC(max_packet_count, "The maximum number of packets in a transfer 15-511"); ++ ++module_param(phy_utmi_width, long, 0444); ++MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits"); ++ ++module_param(turn_around_time_hs, long, 0444); ++MODULE_PARM_DESC(turn_around_time_hs, "Turn-Around time for HS"); ++ ++module_param(turn_around_time_fs, long, 0444); ++MODULE_PARM_DESC(turn_around_time_fs, "Turn-Around time for FS"); ++ ++module_param(timeout_cal_hs, long, 0444); ++MODULE_PARM_DESC(timeout_cal_hs, "Timeout Cal for HS"); ++ ++module_param(timeout_cal_fs, long, 0444); ++MODULE_PARM_DESC(timeout_cal_fs, "Timeout Cal for FS"); ++ ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_plat.h b/drivers/usb/ifxhcd/ifxusb_plat.h +new file mode 100644 +index 0000000..a50294f +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_plat.h +@@ -0,0 +1,1018 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_plat.h ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : This file contains the Platform Specific constants, interfaces ++ ** (functions and macros). ++ ** FUNCTIONS : ++ ** COMPILER : gcc ++ ** REFERENCE : IFX hardware ref handbook for each plateforms ++ ** COPYRIGHT : ++ ** Version Control Section ** ++ ** $Author$ ++ ** $Date$ ++ ** $Revisions$ ++ ** $Log$ Revision history ++ *****************************************************************************/ ++ ++ ++/*! ++ \defgroup IFXUSB_PLATEFORM_DEFINITION Platform Specific constants, interfaces (functions and macros). ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief Maintain plateform specific definitions and macros in this file. ++ Each plateform has its own definition zone. ++ */ ++ ++/*! ++ \defgroup IFXUSB_PLATEFORM_MEM_ADDR Definition of memory address and size and default parameters ++ \ingroup IFXUSB_PLATEFORM_DEFINITION ++ */ ++ ++/*! ++ \defgroup IFXUSB_DBG_ROUTINE Routines for debug message ++ \ingroup IFXUSB_PLATEFORM_DEFINITION ++ */ ++ ++ ++/*! \file ifxusb_plat.h ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the Platform Specific constants, interfaces (functions and macros). ++*/ ++ ++#if !defined(__IFXUSB_PLAT_H__) ++#define __IFXUSB_PLAT_H__ ++ ++ ++#include ++#include ++#include ++#include ++#include ++ ++ ++#define IFXUSB_IOMEM_SIZE 0x00001000 ++#define IFXUSB_FIFOMEM_SIZE 0x00010000 ++#define IFXUSB_FIFODBG_SIZE 0x00020000 ++ ++ ++ ++/*! ++ \addtogroup IFXUSB_PLATEFORM_MEM_ADDR ++ */ ++/*@{*/ ++#if defined(__UEIP__) ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++// #define IFXUSB_IRQ 54 ++ #define IFXUSB_IOMEM_BASE 0x1e101000 ++ #define IFXUSB_FIFOMEM_BASE 0x1e120000 ++ #define IFXUSB_FIFODBG_BASE 0x1e140000 ++// #define IFXUSB_OC_IRQ 151 ++ ++ #ifndef DANUBE_RCU_BASE_ADDR ++ #define DANUBE_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ ++ #ifndef DANUBE_CGU ++ #define DANUBE_CGU (0xBF103000) ++ #endif ++ #ifndef DANUBE_CGU_IFCCR ++ #define DANUBE_CGU_IFCCR ((volatile unsigned long *)(DANUBE_CGU+ 0x0018)) ++ #endif ++ #ifndef DANUBE_PMU ++ #define DANUBE_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef DANUBE_PMU_PWDCR ++ #define DANUBE_PMU_PWDCR ((volatile unsigned long *)(DANUBE_PMU+0x001C)) ++ #endif ++ ++ #ifndef DANUBE_GPIO_P0_OUT ++ #define DANUBE_GPIO_P0_OUT (0xBF103000+0x10) ++ #define DANUBE_GPIO_P0_DIR (0xBF103000+0x18) ++ #define DANUBE_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define DANUBE_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define DANUBE_GPIO_P0_OD (0xBF103000+0x24) ++ #define DANUBE_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define DANUBE_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define DANUBE_GPIO_P1_OUT (0xBF103000+0x40) ++ #define DANUBE_GPIO_P1_DIR (0xBF103000+0x48) ++ #define DANUBE_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define DANUBE_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define DANUBE_GPIO_P1_OD (0xBF103000+0x54) ++ #define DANUBE_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define DANUBE_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ #define DANUBE_RCU_USBCFG ((volatile unsigned long *)(DANUBE_RCU_BASE_ADDR + 0x18)) ++ #define DANUBE_RCU_RESET ((volatile unsigned long *)(DANUBE_RCU_BASE_ADDR + 0x10)) ++ #define DANUBE_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define DANUBE_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define DANUBE_USBCFG_SLV_END_BIT 9 // 0:little_end, 1:big_end ++ ++ #define default_param_dma_burst_size 4 ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 4 ++ #define default_param_turn_around_time_fs 4 ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 640 ++ #define default_param_nperio_tx_fifo_size 640 ++ #define default_param_perio_tx_fifo_size 768 ++ #endif //__IS_HOST__ ++ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_INTR__ ++ #define default_param_rx_fifo_size 1024 ++ #define default_param_nperio_tx_fifo_size 1016 ++ #define default_param_perio_tx_fifo_size_01 8 ++ #else ++ #define default_param_rx_fifo_size 1024 ++ #define default_param_nperio_tx_fifo_size 1024 ++ #define default_param_perio_tx_fifo_size_01 0 ++ #endif ++ #define default_param_perio_tx_fifo_size_02 0 ++ #define default_param_perio_tx_fifo_size_03 0 ++ #define default_param_perio_tx_fifo_size_04 0 ++ #define default_param_perio_tx_fifo_size_05 0 ++ #define default_param_perio_tx_fifo_size_06 0 ++ #define default_param_perio_tx_fifo_size_07 0 ++ #define default_param_perio_tx_fifo_size_08 0 ++ #define default_param_perio_tx_fifo_size_09 0 ++ #define default_param_perio_tx_fifo_size_10 0 ++ #define default_param_perio_tx_fifo_size_11 0 ++ #define default_param_perio_tx_fifo_size_12 0 ++ #define default_param_perio_tx_fifo_size_13 0 ++ #define default_param_perio_tx_fifo_size_14 0 ++ #define default_param_perio_tx_fifo_size_15 0 ++ #endif //__IS_DEVICE__ ++ ++ #elif defined(__IS_AMAZON_SE__) ++ //#include ++ //#include ++ ++// #define IFXUSB_IRQ 31 ++ #define IFXUSB_IOMEM_BASE 0x1e101000 ++ #define IFXUSB_FIFOMEM_BASE 0x1e120000 ++ #define IFXUSB_FIFODBG_BASE 0x1e140000 ++// #define IFXUSB_OC_IRQ 20 ++ ++ #ifndef AMAZON_SE_RCU_BASE_ADDR ++ #define AMAZON_SE_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ #define AMAZON_SE_RCU_USBCFG ((volatile unsigned long *)(AMAZON_SE_RCU_BASE_ADDR + 0x18)) ++ #define AMAZON_SE_RCU_RESET ((volatile unsigned long *)(AMAZON_SE_RCU_BASE_ADDR + 0x10)) ++ #define AMAZON_SE_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define AMAZON_SE_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define AMAZON_SE_USBCFG_SLV_END_BIT 9 // 0:little_end, 1:big_end ++ ++ #ifndef AMAZON_SE_GPIO_P0_OUT ++ #define AMAZON_SE_GPIO_P0_OUT (0xBF103000+0x10) ++ #define AMAZON_SE_GPIO_P0_DIR (0xBF103000+0x18) ++ #define AMAZON_SE_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define AMAZON_SE_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define AMAZON_SE_GPIO_P0_OD (0xBF103000+0x24) ++ #define AMAZON_SE_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define AMAZON_SE_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define AMAZON_SE_GPIO_P1_OUT (0xBF103000+0x40) ++ #define AMAZON_SE_GPIO_P1_DIR (0xBF103000+0x48) ++ #define AMAZON_SE_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define AMAZON_SE_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define AMAZON_SE_GPIO_P1_OD (0xBF103000+0x54) ++ #define AMAZON_SE_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define AMAZON_SE_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ #ifndef AMAZON_SE_CGU ++ #define AMAZON_SE_CGU (0xBF103000) ++ #endif ++ #ifndef AMAZON_SE_CGU_IFCCR ++ #define AMAZON_SE_CGU_IFCCR ((volatile unsigned long *)(AMAZON_SE_CGU+ 0x0018)) ++ #endif ++ #ifndef AMAZON_SE_PMU ++ #define AMAZON_SE_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef AMAZON_SE_PMU_PWDCR ++ #define AMAZON_SE_PMU_PWDCR ((volatile unsigned long *)(AMAZON_SE_PMU+0x001C)) ++ #endif ++ ++ #define default_param_dma_burst_size 4 ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 4 //(NoChange) ++ #define default_param_turn_around_time_fs 4 //(NoChange) ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 240 ++ #define default_param_nperio_tx_fifo_size 240 ++ #define default_param_perio_tx_fifo_size 32 ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_INTR__ ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 248 ++ #define default_param_perio_tx_fifo_size_01 8 ++ #else ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 256 ++ #define default_param_perio_tx_fifo_size_01 0 ++ #endif ++ #define default_param_perio_tx_fifo_size_02 0 ++ #define default_param_perio_tx_fifo_size_03 0 ++ #define default_param_perio_tx_fifo_size_04 0 ++ #define default_param_perio_tx_fifo_size_05 0 ++ #define default_param_perio_tx_fifo_size_06 0 ++ #define default_param_perio_tx_fifo_size_07 0 ++ #define default_param_perio_tx_fifo_size_08 0 ++ #define default_param_perio_tx_fifo_size_09 0 ++ #define default_param_perio_tx_fifo_size_10 0 ++ #define default_param_perio_tx_fifo_size_11 0 ++ #define default_param_perio_tx_fifo_size_12 0 ++ #define default_param_perio_tx_fifo_size_13 0 ++ #define default_param_perio_tx_fifo_size_14 0 ++ #define default_param_perio_tx_fifo_size_15 0 ++ #endif //__IS_DEVICE__ ++ ++ #elif defined(__IS_AR9__) ++// #define IFXUSB1_IRQ 54 ++ #define IFXUSB1_IOMEM_BASE 0x1E101000 ++ #define IFXUSB1_FIFOMEM_BASE 0x1E120000 ++ #define IFXUSB1_FIFODBG_BASE 0x1E140000 ++ ++// #define IFXUSB2_IRQ 83 ++ #define IFXUSB2_IOMEM_BASE 0x1E106000 ++ #define IFXUSB2_FIFOMEM_BASE 0x1E1E0000 ++ #define IFXUSB2_FIFODBG_BASE 0x1E1C0000 ++ ++// #define IFXUSB_OC_IRQ 60 ++ ++ #ifndef AR9_RCU_BASE_ADDR ++ #define AR9_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ ++ #ifndef AR9_CGU ++ #define AR9_CGU (0xBF103000) ++ #endif ++ #ifndef AR9_CGU_IFCCR ++ #define AR9_CGU_IFCCR ((volatile unsigned long *)(AR9_CGU+ 0x0018)) ++ #endif ++ ++ #ifndef AR9_PMU ++ #define AR9_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef AR9_PMU_PWDCR ++ #define AR9_PMU_PWDCR ((volatile unsigned long *)(AR9_PMU+0x001C)) ++ #endif ++ ++ #ifndef AR9_GPIO_P0_OUT ++ #define AR9_GPIO_P0_OUT (0xBF103000+0x10) ++ #define AR9_GPIO_P0_DIR (0xBF103000+0x18) ++ #define AR9_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define AR9_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define AR9_GPIO_P0_OD (0xBF103000+0x24) ++ #define AR9_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define AR9_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define AR9_GPIO_P1_OUT (0xBF103000+0x40) ++ #define AR9_GPIO_P1_DIR (0xBF103000+0x48) ++ #define AR9_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define AR9_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define AR9_GPIO_P1_OD (0xBF103000+0x54) ++ #define AR9_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define AR9_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ #define AR9_RCU_USB1CFG ((volatile unsigned long *)(AR9_RCU_BASE_ADDR + 0x18)) ++ #define AR9_RCU_USB2CFG ((volatile unsigned long *)(AR9_RCU_BASE_ADDR + 0x34)) ++ #define AR9_RCU_USBRESET ((volatile unsigned long *)(AR9_RCU_BASE_ADDR + 0x10)) ++ #define AR9_USBCFG_ARB 7 // ++ #define AR9_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define AR9_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define AR9_USBCFG_SLV_END_BIT 17 // 0:little_end, 1:big_end ++ ++ #define default_param_dma_burst_size 4 ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 4 //(NoChange) ++ #define default_param_turn_around_time_fs 4 //(NoChange) ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 240 ++ #define default_param_nperio_tx_fifo_size 240 ++ #define default_param_perio_tx_fifo_size 32 ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_INTR__ ++ #define default_param_rx_fifo_size 256 ++// #define default_param_nperio_tx_fifo_size 248 ++// #define default_param_perio_tx_fifo_size_01 8 ++ #define default_param_nperio_tx_fifo_size 252 ++ #define default_param_perio_tx_fifo_size_01 4 ++ #else ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 256 ++ #define default_param_perio_tx_fifo_size_01 0 ++ #endif ++ #define default_param_perio_tx_fifo_size_02 0 ++ #define default_param_perio_tx_fifo_size_03 0 ++ #define default_param_perio_tx_fifo_size_04 0 ++ #define default_param_perio_tx_fifo_size_05 0 ++ #define default_param_perio_tx_fifo_size_06 0 ++ #define default_param_perio_tx_fifo_size_07 0 ++ #define default_param_perio_tx_fifo_size_08 0 ++ #define default_param_perio_tx_fifo_size_09 0 ++ #define default_param_perio_tx_fifo_size_10 0 ++ #define default_param_perio_tx_fifo_size_11 0 ++ #define default_param_perio_tx_fifo_size_12 0 ++ #define default_param_perio_tx_fifo_size_13 0 ++ #define default_param_perio_tx_fifo_size_14 0 ++ #define default_param_perio_tx_fifo_size_15 0 ++ #endif //__IS_DEVICE__ ++ ++ #elif defined(__IS_VR9__) ++// #define IFXUSB1_IRQ 54 ++ #define IFXUSB1_IOMEM_BASE 0x1E101000 ++ #define IFXUSB1_FIFOMEM_BASE 0x1E120000 ++ #define IFXUSB1_FIFODBG_BASE 0x1E140000 ++ ++// #define IFXUSB2_IRQ 83 ++ #define IFXUSB2_IOMEM_BASE 0x1E106000 ++ #define IFXUSB2_FIFOMEM_BASE 0x1E1E0000 ++ #define IFXUSB2_FIFODBG_BASE 0x1E1C0000 ++// #define IFXUSB_OC_IRQ 60 ++ ++ #ifndef VR9_RCU_BASE_ADDR ++ #define VR9_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ ++ #ifndef VR9_CGU ++ #define VR9_CGU (0xBF103000) ++ #endif ++ #ifndef VR9_CGU_IFCCR ++ #define VR9_CGU_IFCCR ((volatile unsigned long *)(VR9_CGU+ 0x0018)) ++ #endif ++ ++ #ifndef VR9_PMU ++ #define VR9_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef VR9_PMU_PWDCR ++ #define VR9_PMU_PWDCR ((volatile unsigned long *)(VR9_PMU+0x001C)) ++ #endif ++ ++ #ifndef VR9_GPIO_P0_OUT ++ #define VR9_GPIO_P0_OUT (0xBF103000+0x10) ++ #define VR9_GPIO_P0_DIR (0xBF103000+0x18) ++ #define VR9_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define VR9_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define VR9_GPIO_P0_OD (0xBF103000+0x24) ++ #define VR9_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define VR9_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define VR9_GPIO_P1_OUT (0xBF103000+0x40) ++ #define VR9_GPIO_P1_DIR (0xBF103000+0x48) ++ #define VR9_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define VR9_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define VR9_GPIO_P1_OD (0xBF103000+0x54) ++ #define VR9_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define VR9_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ #define VR9_RCU_USB1CFG ((volatile unsigned long *)(VR9_RCU_BASE_ADDR + 0x18)) ++ #define VR9_RCU_USB2CFG ((volatile unsigned long *)(VR9_RCU_BASE_ADDR + 0x34)) ++ #define VR9_RCU_USB_ANA_CFG1A ((volatile unsigned long *)(AR9_RCU_BASE_ADDR + 0x38)) ++ #define VR9_RCU_USB_ANA_CFG1B ((volatile unsigned long *)(AR9_RCU_BASE_ADDR + 0x3C)) ++ #define VR9_RCU_USBRESET ((volatile unsigned long *)(VR9_RCU_BASE_ADDR + 0x10)) ++ #define VR9_RCU_USBRESET2 ((volatile unsigned long *)(VR9_RCU_BASE_ADDR + 0x48)) ++ #define VR9_USBCFG_ARB 7 // ++ #define VR9_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define VR9_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define VR9_USBCFG_SLV_END_BIT 9 // 0:little_end, 1:big_end ++ ++ /*== AVM/BC 20101220 Workaround VR9 DMA burst size == ++ * Using 2 Devices in diferent ports cause a general USB Host Error. ++ * Workaround found in UGW4.3 ++ */ ++// #define default_param_dma_burst_size 4 //(ALL) ++ //WA for AHB ++ #define default_param_dma_burst_size 0 //(ALL) ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 6 //(NoChange) snpsid >= 0x4f54260a ++ #define default_param_turn_around_time_fs 6 //(NoChange) snpsid >= 0x4f54260a ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 240 ++ #define default_param_nperio_tx_fifo_size 240 ++ #define default_param_perio_tx_fifo_size 32 ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++#if 0 ++ #define default_param_rx_fifo_size 256 ++ #define default_param_tx_fifo_size_00 -1 ++ #define default_param_tx_fifo_size_01 -1 ++ #define default_param_tx_fifo_size_02 -1 ++#else ++ #define default_param_rx_fifo_size 256 ++ #define default_param_tx_fifo_size_00 32 ++ #define default_param_tx_fifo_size_01 200 ++ #define default_param_tx_fifo_size_02 8 ++#endif ++ #define default_param_tx_fifo_size_03 -1 ++ #define default_param_tx_fifo_size_04 -1 ++ #define default_param_tx_fifo_size_05 -1 ++ #define default_param_tx_fifo_size_06 -1 ++ #define default_param_tx_fifo_size_07 -1 ++ #define default_param_tx_fifo_size_08 -1 ++ #define default_param_tx_fifo_size_09 -1 ++ #define default_param_tx_fifo_size_10 -1 ++ #define default_param_tx_fifo_size_11 -1 ++ #define default_param_tx_fifo_size_12 -1 ++ #define default_param_tx_fifo_size_13 -1 ++ #define default_param_tx_fifo_size_14 -1 ++ #define default_param_tx_fifo_size_15 -1 ++ #define default_param_dma_unalgned_tx -1 ++ #define default_param_dma_unalgned_rx -1 ++ #define default_param_thr_ctl -1 ++ #define default_param_tx_thr_length -1 ++ #define default_param_rx_thr_length -1 ++ #endif //__IS_DEVICE__ ++ #else // __IS_VR9__ ++ #error "Please choose one platform!!" ++ #endif // __IS_VR9__ ++ ++#else //UEIP ++ #if defined(__IS_TWINPASS__) || defined(__IS_DANUBE__) ++// #define IFXUSB_IRQ 54 ++ #define IFXUSB_IOMEM_BASE 0x1e101000 ++ #define IFXUSB_FIFOMEM_BASE 0x1e120000 ++ #define IFXUSB_FIFODBG_BASE 0x1e140000 ++// #define IFXUSB_OC_IRQ 151 ++ ++ ++ #ifndef DANUBE_RCU_BASE_ADDR ++ #define DANUBE_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ ++ #ifndef DANUBE_CGU ++ #define DANUBE_CGU (0xBF103000) ++ #endif ++ #ifndef DANUBE_CGU_IFCCR ++ #define DANUBE_CGU_IFCCR ((volatile unsigned long *)(DANUBE_CGU+ 0x0018)) ++ #endif ++ #ifndef DANUBE_PMU ++ #define DANUBE_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef DANUBE_PMU_PWDCR ++ #define DANUBE_PMU_PWDCR ((volatile unsigned long *)(DANUBE_PMU+0x001C)) ++ #endif ++ ++ #ifndef DANUBE_GPIO_P0_OUT ++ #define DANUBE_GPIO_P0_OUT (0xBF103000+0x10) ++ #define DANUBE_GPIO_P0_DIR (0xBF103000+0x18) ++ #define DANUBE_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define DANUBE_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define DANUBE_GPIO_P0_OD (0xBF103000+0x24) ++ #define DANUBE_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define DANUBE_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define DANUBE_GPIO_P1_OUT (0xBF103000+0x40) ++ #define DANUBE_GPIO_P1_DIR (0xBF103000+0x48) ++ #define DANUBE_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define DANUBE_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define DANUBE_GPIO_P1_OD (0xBF103000+0x54) ++ #define DANUBE_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define DANUBE_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ ++ #define DANUBE_RCU_USBCFG ((volatile unsigned long *)(DANUBE_RCU_BASE_ADDR + 0x18)) ++ #define DANUBE_RCU_RESET ((volatile unsigned long *)(DANUBE_RCU_BASE_ADDR + 0x10)) ++ #define DANUBE_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define DANUBE_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define DANUBE_USBCFG_SLV_END_BIT 9 // 0:little_end, 1:big_end ++ ++ #define default_param_dma_burst_size 4 ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 4 //(NoChange) ++ #define default_param_turn_around_time_fs 4 //(NoChange) ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 640 ++ #define default_param_nperio_tx_fifo_size 640 ++ #define default_param_perio_tx_fifo_size 768 ++ #endif //__IS_HOST__ ++ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_INTR__ ++ #define default_param_rx_fifo_size 1024 ++ #define default_param_nperio_tx_fifo_size 1016 ++ #define default_param_perio_tx_fifo_size_01 8 ++ #else ++ #define default_param_rx_fifo_size 1024 ++ #define default_param_nperio_tx_fifo_size 1024 ++ #define default_param_perio_tx_fifo_size_01 0 ++ #endif ++ #define default_param_perio_tx_fifo_size_02 0 ++ #define default_param_perio_tx_fifo_size_03 0 ++ #define default_param_perio_tx_fifo_size_04 0 ++ #define default_param_perio_tx_fifo_size_05 0 ++ #define default_param_perio_tx_fifo_size_06 0 ++ #define default_param_perio_tx_fifo_size_07 0 ++ #define default_param_perio_tx_fifo_size_08 0 ++ #define default_param_perio_tx_fifo_size_09 0 ++ #define default_param_perio_tx_fifo_size_10 0 ++ #define default_param_perio_tx_fifo_size_11 0 ++ #define default_param_perio_tx_fifo_size_12 0 ++ #define default_param_perio_tx_fifo_size_13 0 ++ #define default_param_perio_tx_fifo_size_14 0 ++ #define default_param_perio_tx_fifo_size_15 0 ++ #endif //__IS_DEVICE__ ++ ++ #elif defined(__IS_AMAZON_SE__) ++ #include ++ //#include ++ ++// #define IFXUSB_IRQ 31 ++ #define IFXUSB_IOMEM_BASE 0x1e101000 ++ #define IFXUSB_FIFOMEM_BASE 0x1e120000 ++ #define IFXUSB_FIFODBG_BASE 0x1e140000 ++// #define IFXUSB_OC_IRQ 20 ++ ++ #define AMAZON_SE_RCU_USBCFG ((volatile unsigned long *)(AMAZON_SE_RCU_BASE_ADDR + 0x18)) ++ #define AMAZON_SE_RCU_RESET ((volatile unsigned long *)(AMAZON_SE_RCU_BASE_ADDR + 0x10)) ++ #define AMAZON_SE_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define AMAZON_SE_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define AMAZON_SE_USBCFG_SLV_END_BIT 9 // 0:little_end, 1:big_end ++ ++ #ifndef AMAZON_SE_GPIO_P0_OUT ++ #define AMAZON_SE_GPIO_P0_OUT (0xBF103000+0x10) ++ #define AMAZON_SE_GPIO_P0_DIR (0xBF103000+0x18) ++ #define AMAZON_SE_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define AMAZON_SE_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define AMAZON_SE_GPIO_P0_OD (0xBF103000+0x24) ++ #define AMAZON_SE_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define AMAZON_SE_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define AMAZON_SE_GPIO_P1_OUT (0xBF103000+0x40) ++ #define AMAZON_SE_GPIO_P1_DIR (0xBF103000+0x48) ++ #define AMAZON_SE_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define AMAZON_SE_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define AMAZON_SE_GPIO_P1_OD (0xBF103000+0x54) ++ #define AMAZON_SE_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define AMAZON_SE_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ ++ #ifndef AMAZON_SE_CGU ++ #define AMAZON_SE_CGU (0xBF103000) ++ #endif ++ #ifndef AMAZON_SE_CGU_IFCCR ++ #define AMAZON_SE_CGU_IFCCR ((volatile unsigned long *)(AMAZON_SE_CGU+ 0x0018)) ++ #endif ++ #ifndef AMAZON_SE_PMU ++ #define AMAZON_SE_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef AMAZON_SE_PMU_PWDCR ++ #define AMAZON_SE_PMU_PWDCR ((volatile unsigned long *)(AMAZON_SE_PMU+0x001C)) ++ #endif ++ ++ #define default_param_dma_burst_size 4 ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 4 //(NoChange) ++ #define default_param_turn_around_time_fs 4 //(NoChange) ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 240 ++ #define default_param_nperio_tx_fifo_size 240 ++ #define default_param_perio_tx_fifo_size 32 ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_INTR__ ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 248 ++ #define default_param_perio_tx_fifo_size_01 8 ++ #else ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 256 ++ #define default_param_perio_tx_fifo_size_01 0 ++ #endif ++ #define default_param_perio_tx_fifo_size_02 0 ++ #define default_param_perio_tx_fifo_size_03 0 ++ #define default_param_perio_tx_fifo_size_04 0 ++ #define default_param_perio_tx_fifo_size_05 0 ++ #define default_param_perio_tx_fifo_size_06 0 ++ #define default_param_perio_tx_fifo_size_07 0 ++ #define default_param_perio_tx_fifo_size_08 0 ++ #define default_param_perio_tx_fifo_size_09 0 ++ #define default_param_perio_tx_fifo_size_10 0 ++ #define default_param_perio_tx_fifo_size_11 0 ++ #define default_param_perio_tx_fifo_size_12 0 ++ #define default_param_perio_tx_fifo_size_13 0 ++ #define default_param_perio_tx_fifo_size_14 0 ++ #define default_param_perio_tx_fifo_size_15 0 ++ #endif //__IS_DEVICE__ ++ ++ #elif defined(__IS_AR9__) ++// #define IFXUSB1_IRQ 54 ++ #define IFXUSB1_IOMEM_BASE 0x1E101000 ++ #define IFXUSB1_FIFOMEM_BASE 0x1E120000 ++ #define IFXUSB1_FIFODBG_BASE 0x1E140000 ++ ++// #define IFXUSB2_IRQ 83 ++ #define IFXUSB2_IOMEM_BASE 0x1E106000 ++ #define IFXUSB2_FIFOMEM_BASE 0x1E1E0000 ++ #define IFXUSB2_FIFODBG_BASE 0x1E1C0000 ++ ++// #define IFXUSB_OC_IRQ 60 ++ ++ #ifndef AMAZON_S_RCU_BASE_ADDR ++ #define AMAZON_S_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ ++ #ifndef AMAZON_S_CGU ++ #define AMAZON_S_CGU (0xBF103000) ++ #endif ++ #ifndef AMAZON_S_CGU_IFCCR ++ #define AMAZON_S_CGU_IFCCR ((volatile unsigned long *)(AMAZON_S_CGU+ 0x0018)) ++ #endif ++ ++ #ifndef AMAZON_S_PMU ++ #define AMAZON_S_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef AMAZON_S_PMU_PWDCR ++ #define AMAZON_S_PMU_PWDCR ((volatile unsigned long *)(AMAZON_S_PMU+0x001C)) ++ #endif ++ ++ #ifndef AMAZON_S_GPIO_P0_OUT ++ #define AMAZON_S_GPIO_P0_OUT (0xBF103000+0x10) ++ #define AMAZON_S_GPIO_P0_DIR (0xBF103000+0x18) ++ #define AMAZON_S_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define AMAZON_S_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define AMAZON_S_GPIO_P0_OD (0xBF103000+0x24) ++ #define AMAZON_S_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define AMAZON_S_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define AMAZON_S_GPIO_P1_OUT (0xBF103000+0x40) ++ #define AMAZON_S_GPIO_P1_DIR (0xBF103000+0x48) ++ #define AMAZON_S_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define AMAZON_S_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define AMAZON_S_GPIO_P1_OD (0xBF103000+0x54) ++ #define AMAZON_S_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define AMAZON_S_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ #define AMAZON_S_RCU_USB1CFG ((volatile unsigned long *)(AMAZON_S_RCU_BASE_ADDR + 0x18)) ++ #define AMAZON_S_RCU_USB2CFG ((volatile unsigned long *)(AMAZON_S_RCU_BASE_ADDR + 0x34)) ++ #define AMAZON_S_RCU_USBRESET ((volatile unsigned long *)(AMAZON_S_RCU_BASE_ADDR + 0x10)) ++ #define AMAZON_S_USBCFG_ARB 7 // ++ #define AMAZON_S_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define AMAZON_S_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define AMAZON_S_USBCFG_SLV_END_BIT 17 // 0:little_end, 1:big_end ++ ++ #define default_param_dma_burst_size 4 ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 4 //(NoChange) ++ #define default_param_turn_around_time_fs 4 //(NoChange) ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 240 ++ #define default_param_nperio_tx_fifo_size 240 ++ #define default_param_perio_tx_fifo_size 32 ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++ #ifdef __DED_INTR__ ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 248 ++ #define default_param_perio_tx_fifo_size_01 8 ++ #else ++ #define default_param_rx_fifo_size 256 ++ #define default_param_nperio_tx_fifo_size 256 ++ #define default_param_perio_tx_fifo_size_01 0 ++ #endif ++ #define default_param_perio_tx_fifo_size_02 0 ++ #define default_param_perio_tx_fifo_size_03 0 ++ #define default_param_perio_tx_fifo_size_04 0 ++ #define default_param_perio_tx_fifo_size_05 0 ++ #define default_param_perio_tx_fifo_size_06 0 ++ #define default_param_perio_tx_fifo_size_07 0 ++ #define default_param_perio_tx_fifo_size_08 0 ++ #define default_param_perio_tx_fifo_size_09 0 ++ #define default_param_perio_tx_fifo_size_10 0 ++ #define default_param_perio_tx_fifo_size_11 0 ++ #define default_param_perio_tx_fifo_size_12 0 ++ #define default_param_perio_tx_fifo_size_13 0 ++ #define default_param_perio_tx_fifo_size_14 0 ++ #define default_param_perio_tx_fifo_size_15 0 ++ #endif //__IS_DEVICE__ ++ ++ #elif defined(__IS_VR9__) ++// #define IFXUSB1_IRQ 54 ++ #define IFXUSB1_IOMEM_BASE 0x1E101000 ++ #define IFXUSB1_FIFOMEM_BASE 0x1E120000 ++ #define IFXUSB1_FIFODBG_BASE 0x1E140000 ++ ++// #define IFXUSB2_IRQ 83 ++ #define IFXUSB2_IOMEM_BASE 0x1E106000 ++ #define IFXUSB2_FIFOMEM_BASE 0x1E1E0000 ++ #define IFXUSB2_FIFODBG_BASE 0x1E1C0000 ++// #define IFXUSB_OC_IRQ 60 ++ ++ #ifndef AMAZON_S_RCU_BASE_ADDR ++ #define AMAZON_S_RCU_BASE_ADDR (0xBF203000) ++ #endif ++ ++ #ifndef AMAZON_S_CGU ++ #define AMAZON_S_CGU (0xBF103000) ++ #endif ++ #ifndef AMAZON_S_CGU_IFCCR ++ #define AMAZON_S_CGU_IFCCR ((volatile unsigned long *)(AMAZON_S_CGU+ 0x0018)) ++ #endif ++ ++ #ifndef AMAZON_S_PMU ++ #define AMAZON_S_PMU (KSEG1+0x1F102000) ++ #endif ++ #ifndef AMAZON_S_PMU_PWDCR ++ #define AMAZON_S_PMU_PWDCR ((volatile unsigned long *)(AMAZON_S_PMU+0x001C)) ++ #endif ++ ++ #ifndef AMAZON_S_GPIO_P0_OUT ++ #define AMAZON_S_GPIO_P0_OUT (0xBF103000+0x10) ++ #define AMAZON_S_GPIO_P0_DIR (0xBF103000+0x18) ++ #define AMAZON_S_GPIO_P0_ALTSEL0 (0xBF103000+0x1C) ++ #define AMAZON_S_GPIO_P0_ALTSEL1 (0xBF103000+0x20) ++ #define AMAZON_S_GPIO_P0_OD (0xBF103000+0x24) ++ #define AMAZON_S_GPIO_P0_PUDSEL (0xBF103000+0x2C) ++ #define AMAZON_S_GPIO_P0_PUDEN (0xBF103000+0x30) ++ #define AMAZON_S_GPIO_P1_OUT (0xBF103000+0x40) ++ #define AMAZON_S_GPIO_P1_DIR (0xBF103000+0x48) ++ #define AMAZON_S_GPIO_P1_ALTSEL0 (0xBF103000+0x4C) ++ #define AMAZON_S_GPIO_P1_ALTSEL1 (0xBF103000+0x50) ++ #define AMAZON_S_GPIO_P1_OD (0xBF103000+0x54) ++ #define AMAZON_S_GPIO_P1_PUDSEL (0xBF103000+0x5C) ++ #define AMAZON_S_GPIO_P1_PUDEN (0xBF103000+0x60) ++ #endif ++ ++ #define AMAZON_S_RCU_USB1CFG ((volatile unsigned long *)(AMAZON_S_RCU_BASE_ADDR + 0x18)) ++ #define AMAZON_S_RCU_USB2CFG ((volatile unsigned long *)(AMAZON_S_RCU_BASE_ADDR + 0x34)) ++ #define AMAZON_S_RCU_USBRESET ((volatile unsigned long *)(AMAZON_S_RCU_BASE_ADDR + 0x10)) ++ #define AMAZON_S_USBCFG_ARB 7 // ++ #define AMAZON_S_USBCFG_HDSEL_BIT 11 // 0:host, 1:device ++ #define AMAZON_S_USBCFG_HOST_END_BIT 10 // 0:little_end, 1:big_end ++ #define AMAZON_S_USBCFG_SLV_END_BIT 17 // 0:little_end, 1:big_end ++ ++ #define default_param_dma_burst_size 4 //(ALL) ++ ++ #define default_param_speed IFXUSB_PARAM_SPEED_HIGH ++ ++ #define default_param_max_transfer_size -1 //(Max, hwcfg) ++ #define default_param_max_packet_count -1 //(Max, hwcfg) ++ #define default_param_phy_utmi_width 16 ++ ++ #define default_param_turn_around_time_hs 6 //(NoChange) snpsid >= 0x4f54260a ++ #define default_param_turn_around_time_fs 6 //(NoChange) snpsid >= 0x4f54260a ++ #define default_param_timeout_cal_hs -1 //(NoChange) ++ #define default_param_timeout_cal_fs -1 //(NoChange) ++ ++ #define default_param_data_fifo_size -1 //(Max, hwcfg) ++ ++ #ifdef __IS_HOST__ ++ #define default_param_host_channels -1 //(Max, hwcfg) ++ #define default_param_rx_fifo_size 240 ++ #define default_param_nperio_tx_fifo_size 240 ++ #define default_param_perio_tx_fifo_size 32 ++ #endif //__IS_HOST__ ++ #ifdef __IS_DEVICE__ ++ #define default_param_rx_fifo_size 256 ++ #define default_param_tx_fifo_size_00 -1 ++ #define default_param_tx_fifo_size_01 -1 ++ #define default_param_tx_fifo_size_02 -1 ++ #define default_param_tx_fifo_size_03 -1 ++ #define default_param_tx_fifo_size_04 -1 ++ #define default_param_tx_fifo_size_05 -1 ++ #define default_param_tx_fifo_size_06 -1 ++ #define default_param_tx_fifo_size_07 -1 ++ #define default_param_tx_fifo_size_08 -1 ++ #define default_param_tx_fifo_size_09 -1 ++ #define default_param_tx_fifo_size_10 -1 ++ #define default_param_tx_fifo_size_11 -1 ++ #define default_param_tx_fifo_size_12 -1 ++ #define default_param_tx_fifo_size_13 -1 ++ #define default_param_tx_fifo_size_14 -1 ++ #define default_param_tx_fifo_size_15 -1 ++ #define default_param_dma_unalgned_tx -1 ++ #define default_param_dma_unalgned_rx -1 ++ #define default_param_thr_ctl -1 ++ #define default_param_tx_thr_length -1 ++ #define default_param_rx_thr_length -1 ++ #endif //__IS_DEVICE__ ++ #else // __IS_VR9__ ++ #error "Please choose one platform!!" ++ #endif // __IS_VR9__ ++#endif //UEIP ++ ++/*@}*//*IFXUSB_PLATEFORM_MEM_ADDR*/ ++ ++///////////////////////////////////////////////////////////////////////// ++ ++#ifdef __IS_HOST__ ++ #ifdef CONFIG_USB_HOST_IFX_FORCE_USB11 ++ #undef default_param_speed ++ #define default_param_speed IFXUSB_PARAM_SPEED_FULL ++ #endif ++#endif ++#ifdef __IS_DEVICE__ ++ #ifndef CONFIG_USB_GADGET_DUALSPEED ++ #undef default_param_speed ++ #define default_param_speed IFXUSB_PARAM_SPEED_FULL ++ #endif ++#endif ++ ++///////////////////////////////////////////////////////////////////////// ++ ++static __inline__ void UDELAY( const uint32_t _usecs ) ++{ ++ udelay( _usecs ); ++} ++ ++static __inline__ void MDELAY( const uint32_t _msecs ) ++{ ++ mdelay( _msecs ); ++} ++ ++static __inline__ void SPIN_LOCK( spinlock_t *_lock ) ++{ ++ spin_lock(_lock); ++} ++ ++static __inline__ void SPIN_UNLOCK( spinlock_t *_lock ) ++{ ++ spin_unlock(_lock); ++} ++ ++#define SPIN_LOCK_IRQSAVE( _l, _f ) \ ++ { \ ++ spin_lock_irqsave(_l,_f); \ ++ } ++ ++#define SPIN_UNLOCK_IRQRESTORE( _l,_f ) \ ++ { \ ++ spin_unlock_irqrestore(_l,_f); \ ++ } ++ ++///////////////////////////////////////////////////////////////////////// ++/*! ++ \addtogroup IFXUSB_DBG_ROUTINE ++ */ ++/*@{*/ ++#ifdef __IS_HOST__ ++ extern uint32_t h_dbg_lvl; ++#endif ++ ++#ifdef __IS_DEVICE__ ++ extern uint32_t d_dbg_lvl; ++#endif ++ ++/*! \brief When debug level has the DBG_CIL bit set, display CIL Debug messages. */ ++#define DBG_CIL (0x2) ++/*! \brief When debug level has the DBG_CILV bit set, display CIL Verbose debug messages */ ++#define DBG_CILV (0x20) ++/*! \brief When debug level has the DBG_PCD bit set, display PCD (Device) debug messages */ ++#define DBG_PCD (0x4) ++/*! \brief When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug messages */ ++#define DBG_PCDV (0x40) ++/*! \brief When debug level has the DBG_HCD bit set, display Host debug messages */ ++#define DBG_HCD (0x8) ++/*! \brief When debug level has the DBG_HCDV bit set, display Verbose Host debug messages */ ++#define DBG_HCDV (0x80) ++/*! \brief When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host mode. */ ++#define DBG_HCD_URB (0x800) ++/*! \brief When debug level has any bit set, display debug messages */ ++#define DBG_ANY (0xFF) ++/*! \brief All debug messages off */ ++#define DBG_OFF 0 ++ ++#define DBG_ENTRY (0x8000) ++ ++#define IFXUSB "IFXUSB: " ++ ++/*! ++ \fn inline uint32_t SET_DEBUG_LEVEL( const uint32_t _new ) ++ \brief Set the Debug Level variable. ++ \param _new 32-bit mask of debug level. ++ \return previous debug level ++ */ ++static inline uint32_t SET_DEBUG_LEVEL( const uint32_t _new ) ++{ ++ #ifdef __IS_HOST__ ++ uint32_t old = h_dbg_lvl; ++ h_dbg_lvl = _new; ++ #endif ++ ++ #ifdef __IS_DEVICE__ ++ uint32_t old = d_dbg_lvl; ++ d_dbg_lvl = _new; ++ #endif ++ return old; ++} ++ ++#ifdef __DEBUG__ ++ #ifdef __IS_HOST__ ++ # define IFX_DEBUGPL(lvl, x...) do{ if ((lvl)&h_dbg_lvl)printk( KERN_DEBUG IFXUSB x ); }while(0) ++ # define CHK_DEBUG_LEVEL(level) ((level) & h_dbg_lvl) ++ #endif ++ ++ #ifdef __IS_DEVICE__ ++ # define IFX_DEBUGPL(lvl, x...) do{ if ((lvl)&d_dbg_lvl)printk( KERN_DEBUG IFXUSB x ); }while(0) ++ # define CHK_DEBUG_LEVEL(level) ((level) & d_dbg_lvl) ++ #endif ++ ++ # define IFX_DEBUGP(x...) IFX_DEBUGPL(DBG_ANY, x ) ++#else ++ # define IFX_DEBUGPL(lvl, x...) do{}while(0) ++ # define IFX_DEBUGP(x...) ++ # define CHK_DEBUG_LEVEL(level) (0) ++#endif //__DEBUG__ ++ ++/* Print an Error message. */ ++#define IFX_ERROR(x...) printk( KERN_ERR IFXUSB x ) ++/* Print a Warning message. */ ++#define IFX_WARN(x...) printk( KERN_WARNING IFXUSB x ) ++/* Print a notice (normal but significant message). */ ++#define IFX_NOTICE(x...) printk( KERN_NOTICE IFXUSB x ) ++/* Basic message printing. */ ++#define IFX_PRINT(x...) printk( KERN_INFO IFXUSB x ) ++ ++/*@}*//*IFXUSB_DBG_ROUTINE*/ ++ ++ ++#endif //__IFXUSB_PLAT_H__ ++ +diff --git a/drivers/usb/ifxhcd/ifxusb_regs.h b/drivers/usb/ifxhcd/ifxusb_regs.h +new file mode 100644 +index 0000000..014c6db +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_regs.h +@@ -0,0 +1,1420 @@ ++/***************************************************************************** ++ ** FILE NAME : ifxusb_regs.h ++ ** PROJECT : IFX USB sub-system V3 ++ ** MODULES : IFX USB sub-system Host and Device driver ++ ** SRC VERSION : 1.0 ++ ** DATE : 1/Jan/2009 ++ ** AUTHOR : Chen, Howard ++ ** DESCRIPTION : This file contains the data structures for accessing the IFXUSB core ++ ** registers. ++ ** The application interfaces with the USB core by reading from and ++ ** writing to the Control and Status Register (CSR) space through the ++ ** AHB Slave interface. These registers are 32 bits wide, and the ++ ** addresses are 32-bit-block aligned. ++ ** CSRs are classified as follows: ++ ** - Core Global Registers ++ ** - Device Mode Registers ++ ** - Device Global Registers ++ ** - Device Endpoint Specific Registers ++ ** - Host Mode Registers ++ ** - Host Global Registers ++ ** - Host Port CSRs ++ ** - Host Channel Specific Registers ++ ** ++ ** Only the Core Global registers can be accessed in both Device and ++ ** Host modes. When the USB core is operating in one mode, either ++ ** Device or Host, the application must not access registers from the ++ ** other mode. When the core switches from one mode to another, the ++ ** registers in the new mode of operation must be reprogrammed as they ++ ** would be after a power-on reset. ++ ** FUNCTIONS : ++ ** COMPILER : gcc ++ ** REFERENCE : Synopsys DWC-OTG Driver 2.7 ++ ** COPYRIGHT : ++ ** Version Control Section ** ++ ** $Author$ ++ ** $Date$ ++ ** $Revisions$ ++ ** $Log$ Revision history ++*****************************************************************************/ ++ ++ ++ ++/*! ++ \defgroup IFXUSB_CSR_DEFINITION Control and Status Register bit-map definition ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief Data structures for accessing the IFXUSB core registers. ++ The application interfaces with the USB core by reading from and ++ writing to the Control and Status Register (CSR) space through the ++ AHB Slave interface. These registers are 32 bits wide, and the ++ addresses are 32-bit-block aligned. ++ CSRs are classified as follows: ++ - Core Global Registers ++ - Device Mode Registers ++ - Device Global Registers ++ - Device Endpoint Specific Registers ++ - Host Mode Registers ++ - Host Global Registers ++ - Host Port CSRs ++ - Host Channel Specific Registers ++ ++ Only the Core Global registers can be accessed in both Device andHost modes. ++ When the USB core is operating in one mode, either Device or Host, the ++ application must not access registers from the other mode. When the core ++ switches from one mode to another, the registers in the new mode of operation ++ must be reprogrammed as they would be after a power-on reset. ++ */ ++ ++/*! ++ \defgroup IFXUSB_CSR_DEVICE_GLOBAL_REG Device Mode Registers ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to access Device Mode Global Registers ++ */ ++ ++/*! ++ \defgroup IFXUSB_CSR_DEVICE_EP_REG Device Mode EP Registers ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to access Device Mode EP Registers ++ There will be one set of endpoint registers per logical endpoint ++ implemented. ++ These registers are visible only in Device mode and must not be ++ accessed in Host mode, as the results are unknown. ++ */ ++ ++/*! ++ \defgroup IFXUSB_CSR_DEVICE_DMA_DESC Device mode scatter dma descriptor strusture ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to DMA descriptor ++ */ ++ ++ ++/*! ++ \defgroup IFXUSB_CSR_HOST_GLOBAL_REG Host Mode Registers ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to access Host Mode Global Registers ++ */ ++ ++/*! ++ \defgroup IFXUSB_CSR_HOST_HC_REG Host Mode HC Registers ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to access Host Mode Host Channel Registers ++ There will be one set of endpoint registers per host channel ++ implemented. ++ These registers are visible only in Host mode and must not be ++ accessed in Device mode, as the results are unknown. ++ */ ++ ++/*! ++ \defgroup IFXUSB_CSR_PWR_CLK_GATING_REG Power and Clock Gating Control Register ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to Power and Clock Gating Control Register ++ */ ++ ++ ++ ++ ++ ++ ++ ++ ++/*! ++ \defgroup IFXUSB_CSR_CORE_GLOBAL_REG Core Global Registers ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to access Core Global Registers ++ */ ++/*! ++ \defgroup IFXUSB_CSR_CORE_GLOBAL_REG Core Global Registers ++ \ingroup IFXUSB_CSR_DEFINITION ++ \brief Bit-mapped structure to access Core Global Registers ++ */ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++/*! ++ \file ifxusb_regs.h ++ \ingroup IFXUSB_DRIVER_V3 ++ \brief This file contains the data structures for accessing the IFXUSB core registers. ++ */ ++ ++ ++#ifndef __IFXUSB_REGS_H__ ++#define __IFXUSB_REGS_H__ ++ ++/****************************************************************************/ ++ ++#define MAX_PERIO_FIFOS 15 /** Maximum number of Periodic FIFOs */ ++#define MAX_TX_FIFOS 15 /** Maximum number of Periodic FIFOs */ ++#define MAX_EPS_CHANNELS 16 /** Maximum number of Endpoints/HostChannels */ ++ ++/****************************************************************************/ ++ ++/*! ++ \addtogroup IFXUSB_CSR_ACCESS_MACROS ++ */ ++/*@{*/ ++ ++//#define RecordRegRW ++ ++/*! ++ \fn static __inline__ uint32_t ifxusb_rreg( volatile uint32_t *_reg) ++ \brief Reads the content of a register. ++ \param _reg address of register to read. ++ \return contents of the register. ++ */ ++static __inline__ uint32_t ifxusb_rreg( volatile uint32_t *_reg) ++{ ++ #ifdef RecordRegRW ++ uint32_t r; ++ r=*(_reg); ++ return (r); ++ #else ++ return (*(_reg)); ++ #endif ++}; ++ ++ ++/*! ++ \fn static __inline__ void ifxusb_wreg( volatile uint32_t *_reg, const uint32_t _value) ++ \brief Writes a register with a 32 bit value. ++ \param _reg address of register to write. ++ \param _value value to write to _reg. ++ */ ++static __inline__ void ifxusb_wreg( volatile uint32_t *_reg, const uint32_t _value) ++{ ++ #ifdef RecordRegRW ++ printk(KERN_INFO "[W %p<-%08X]\n",_reg,_value); ++ #else ++ *(_reg)=_value; ++ #endif ++}; ++ ++/*! ++ \fn static __inline__ void ifxusb_mreg( volatile uint32_t *_reg, const uint32_t _clear_mask, const uint32_t _set_mask) ++ \brief Modifies bit values in a register. Using the ++ algorithm: (reg_contents & ~clear_mask) | set_mask. ++ \param _reg address of register to modify. ++ \param _clear_mask bit mask to be cleared. ++ \param _set_mask bit mask to be set. ++ */ ++static __inline__ void ifxusb_mreg( volatile uint32_t *_reg, const uint32_t _clear_mask, const uint32_t _set_mask) ++{ ++ uint32_t v; ++ #ifdef RecordRegRW ++ uint32_t r; ++ v= *(_reg); ++ r=v; ++ r&=(~_clear_mask); ++ r|= _set_mask; ++ *(_reg)=r ; ++ printk(KERN_INFO "[M %p->%08X+%08X/%08X<-%08X]\n",_reg,r,_clear_mask,_set_mask,r); ++ #else ++ v= *(_reg); ++ v&=(~_clear_mask); ++ v|= _set_mask; ++ *(_reg)=v ; ++ #endif ++}; ++ ++/*@}*//*IFXUSB_CSR_ACCESS_MACROS*/ ++/****************************************************************************/ ++ ++/*! ++ \addtogroup IFXUSB_CSR_CORE_GLOBAL_REG ++ */ ++/*@{*/ ++ ++/*! ++ \struct ifxusb_core_global_regs ++ \brief IFXUSB Core registers . ++ The ifxusb_core_global_regs structure defines the size ++ and relative field offsets for the Core Global registers. ++ */ ++typedef struct ifxusb_core_global_regs ++{ ++ volatile uint32_t gotgctl; /*!< 000h OTG Control and Status Register. */ ++ volatile uint32_t gotgint; /*!< 004h OTG Interrupt Register. */ ++ volatile uint32_t gahbcfg; /*!< 008h Core AHB Configuration Register. */ ++ volatile uint32_t gusbcfg; /*!< 00Ch Core USB Configuration Register. */ ++ volatile uint32_t grstctl; /*!< 010h Core Reset Register. */ ++ volatile uint32_t gintsts; /*!< 014h Core Interrupt Register. */ ++ volatile uint32_t gintmsk; /*!< 018h Core Interrupt Mask Register. */ ++ volatile uint32_t grxstsr; /*!< 01Ch Receive Status Queue Read Register (Read Only). */ ++ volatile uint32_t grxstsp; /*!< 020h Receive Status Queue Read & POP Register (Read Only). */ ++ volatile uint32_t grxfsiz; /*!< 024h Receive FIFO Size Register. */ ++ volatile uint32_t gnptxfsiz; /*!< 028h Non Periodic Transmit FIFO Size Register. */ ++ volatile uint32_t gnptxsts; /*!< 02Ch Non Periodic Transmit FIFO/Queue Status Register (Read Only). */ ++ volatile uint32_t gi2cctl; /*!< 030h I2C Access Register. */ ++ volatile uint32_t gpvndctl; /*!< 034h PHY Vendor Control Register. */ ++ volatile uint32_t ggpio; /*!< 038h General Purpose Input/Output Register. */ ++ volatile uint32_t guid; /*!< 03Ch User ID Register. */ ++ volatile uint32_t gsnpsid; /*!< 040h Synopsys ID Register (Read Only). */ ++ volatile uint32_t ghwcfg1; /*!< 044h User HW Config1 Register (Read Only). */ ++ volatile uint32_t ghwcfg2; /*!< 048h User HW Config2 Register (Read Only). */ ++ volatile uint32_t ghwcfg3; /*!< 04Ch User HW Config3 Register (Read Only). */ ++ volatile uint32_t ghwcfg4; /*!< 050h User HW Config4 Register (Read Only). */ ++ volatile uint32_t reserved[43]; /*!< 054h Reserved 054h-0FFh */ ++ volatile uint32_t hptxfsiz; /*!< 100h Host Periodic Transmit FIFO Size Register. */ ++ volatile uint32_t dptxfsiz_dieptxf[15];/*!< 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15. ++ Device Periodic Transmit FIFO#n Register if dedicated ++ fifos are disabled, otherwise Device Transmit FIFO#n ++ Register. ++ */ ++} ifxusb_core_global_regs_t; ++ ++/*! ++ \brief Bits of the Core OTG Control and Status Register (GOTGCTL). ++ */ ++typedef union gotgctl_data ++{ ++ uint32_t d32; ++ struct{ ++ unsigned reserved21_31 : 11; ++ unsigned currmod : 1 ; /*!< 20 */ ++ unsigned bsesvld : 1 ; /*!< 19 */ ++ unsigned asesvld : 1 ; /*!< 18 */ ++ unsigned reserved17 : 1 ; ++ unsigned conidsts : 1 ; /*!< 16 */ ++ unsigned reserved12_15 : 4 ; ++ unsigned devhnpen : 1 ; /*!< 11 */ ++ unsigned hstsethnpen : 1 ; /*!< 10 */ ++ unsigned hnpreq : 1 ; /*!< 09 */ ++ unsigned hstnegscs : 1 ; /*!< 08 */ ++ unsigned reserved2_7 : 6 ; ++ unsigned sesreq : 1 ; /*!< 01 */ ++ unsigned sesreqscs : 1 ; /*!< 00 */ ++ } b; ++} gotgctl_data_t; ++ ++/*! ++ \brief Bit fields of the Core OTG Interrupt Register (GOTGINT). ++ */ ++typedef union gotgint_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved31_20 : 12; ++ unsigned debdone : 1 ; /*!< 19 Debounce Done */ ++ unsigned adevtoutchng : 1 ; /*!< 18 A-Device Timeout Change */ ++ unsigned hstnegdet : 1 ; /*!< 17 Host Negotiation Detected */ ++ unsigned reserver10_16 : 7 ; ++ unsigned hstnegsucstschng : 1 ; /*!< 09 Host Negotiation Success Status Change */ ++ unsigned sesreqsucstschng : 1 ; /*!< 08 Session Request Success Status Change */ ++ unsigned reserved3_7 : 5 ; ++ unsigned sesenddet : 1 ; /*!< 02 Session End Detected */ ++ unsigned reserved0_1 : 2 ; ++ } b; ++} gotgint_data_t; ++ ++/*! ++ \brief Bit fields of the Core AHB Configuration Register (GAHBCFG). ++ */ ++typedef union gahbcfg_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved9_31 : 23; ++ unsigned ptxfemplvl : 1 ; /*!< 08 Periodic FIFO empty level trigger condition*/ ++ unsigned nptxfemplvl : 1 ; /*!< 07 Non-Periodic FIFO empty level trigger condition*/ ++ #define IFXUSB_GAHBCFG_TXFEMPTYLVL_EMPTY 1 ++ #define IFXUSB_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0 ++ unsigned reserved : 1 ; ++ unsigned dmaenable : 1 ; /*!< 05 DMA enable*/ ++ #define IFXUSB_GAHBCFG_DMAENABLE 1 ++ unsigned hburstlen : 4 ; /*!< 01-04 DMA Burst-length*/ ++ #define IFXUSB_GAHBCFG_INT_DMA_BURST_SINGLE 0 ++ #define IFXUSB_GAHBCFG_INT_DMA_BURST_INCR 1 ++ #define IFXUSB_GAHBCFG_INT_DMA_BURST_INCR4 3 ++ #define IFXUSB_GAHBCFG_INT_DMA_BURST_INCR8 5 ++ #define IFXUSB_GAHBCFG_INT_DMA_BURST_INCR16 7 ++ unsigned glblintrmsk : 1 ; /*!< 00 USB Global Interrupt Enable */ ++ #define IFXUSB_GAHBCFG_GLBINT_ENABLE 1 ++ } b; ++} gahbcfg_data_t; ++ ++/*! ++ \brief Bit fields of the Core USB Configuration Register (GUSBCFG). ++*/ ++typedef union gusbcfg_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved31 : 1; ++ unsigned ForceDevMode : 1; /*!< 30 Force Device Mode */ ++ unsigned ForceHstMode : 1; /*!< 29 Force Host Mode */ ++ unsigned TxEndDelay : 1; /*!< 28 Tx End Delay */ ++ unsigned reserved2723 : 5; ++ unsigned term_sel_dl_pulse : 1; /*!< 22 TermSel DLine Pulsing Selection */ ++ unsigned reserved2117 : 5; ++ unsigned otgutmifssel : 1; /*!< 16 UTMIFS Select */ ++ unsigned phylpwrclksel : 1; /*!< 15 PHY Low-Power Clock Select */ ++ unsigned reserved14 : 1; ++ unsigned usbtrdtim : 4; /*!< 13-10 USB Turnaround Time */ ++ unsigned hnpcap : 1; /*!< 09 HNP-Capable */ ++ unsigned srpcap : 1; /*!< 08 SRP-Capable */ ++ unsigned reserved07 : 1; ++ unsigned physel : 1; /*!< 06 USB 2.0 High-Speed PHY or ++ USB 1.1 Full-Speed Serial ++ Transceiver Select */ ++ unsigned fsintf : 1; /*!< 05 Full-Speed Serial Interface Select */ ++ unsigned ulpi_utmi_sel : 1; /*!< 04 ULPI or UTMI+ Select */ ++ unsigned phyif : 1; /*!< 03 PHY Interface */ ++ unsigned toutcal : 3; /*!< 00-02 HS/FS Timeout Calibration */ ++ }b; ++} gusbcfg_data_t; ++ ++/*! ++ \brief Bit fields of the Core Reset Register (GRSTCTL). ++ */ ++typedef union grstctl_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned ahbidle : 1; /*!< 31 AHB Master Idle. Indicates the AHB Master State ++ Machine is in IDLE condition. */ ++ unsigned dmareq : 1; /*!< 30 DMA Request Signal. Indicated DMA request is in ++ probress. Used for debug purpose. */ ++ unsigned reserved11_29 :19; ++ unsigned txfnum : 5; /*!< 10-06 TxFIFO Number (TxFNum) to be flushed. ++ 0x00: Non Periodic TxFIFO Flush or TxFIFO 0 ++ 0x01-0x0F: Periodic TxFIFO Flush or TxFIFO n ++ 0x10: Flush all TxFIFO ++ */ ++ unsigned txfflsh : 1; /*!< 05 TxFIFO Flush */ ++ unsigned rxfflsh : 1; /*!< 04 RxFIFO Flush */ ++ unsigned intknqflsh : 1; /*!< 03 In Token Sequence Learning Queue Flush (Device Only) */ ++ unsigned hstfrm : 1; /*!< 02 Host Frame Counter Reset (Host Only) */ ++ unsigned hsftrst : 1; /*!< 01 Hclk Soft Reset */ ++ ++ unsigned csftrst : 1; /*!< 00 Core Soft Reset ++ The application can flush the control logic in the ++ entire core using this bit. This bit resets the ++ pipelines in the AHB Clock domain as well as the ++ PHY Clock domain. ++ The state machines are reset to an IDLE state, the ++ control bits in the CSRs are cleared, all the ++ transmit FIFOs and the receive FIFO are flushed. ++ The status mask bits that control the generation of ++ the interrupt, are cleared, to clear the ++ interrupt. The interrupt status bits are not ++ cleared, so the application can get the status of ++ any events that occurred in the core after it has ++ set this bit. ++ Any transactions on the AHB are terminated as soon ++ as possible following the protocol. Any ++ transactions on the USB are terminated immediately. ++ The configuration settings in the CSRs are ++ unchanged, so the software doesn't have to ++ reprogram these registers (Device ++ Configuration/Host Configuration/Core System ++ Configuration/Core PHY Configuration). ++ The application can write to this bit, any time it ++ wants to reset the core. This is a self clearing ++ bit and the core clears this bit after all the ++ necessary logic is reset in the core, which may ++ take several clocks, depending on the current state ++ of the core. ++ */ ++ }b; ++} grstctl_t; ++ ++/*! ++ \brief Bit fields of the Core Interrupt Mask Register (GINTMSK) and ++ Core Interrupt Register (GINTSTS). ++ */ ++typedef union gint_data ++{ ++ uint32_t d32; ++ #define IFXUSB_SOF_INTR_MASK 0x0008 ++ struct ++ { ++ unsigned wkupintr : 1; /*!< 31 Resume/Remote Wakeup Detected Interrupt */ ++ unsigned sessreqintr : 1; /*!< 30 Session Request/New Session Detected Interrupt */ ++ unsigned disconnect : 1; /*!< 29 Disconnect Detected Interrupt */ ++ unsigned conidstschng : 1; /*!< 28 Connector ID Status Change */ ++ unsigned reserved27 : 1; ++ unsigned ptxfempty : 1; /*!< 26 Periodic TxFIFO Empty */ ++ unsigned hcintr : 1; /*!< 25 Host Channels Interrupt */ ++ unsigned portintr : 1; /*!< 24 Host Port Interrupt */ ++ unsigned reserved23 : 1; ++ unsigned fetsuspmsk : 1; /*!< 22 Data Fetch Suspended */ ++ unsigned incomplisoout : 1; /*!< 21 Incomplete IsochronousOUT/Period Transfer */ ++ unsigned incomplisoin : 1; /*!< 20 Incomplete Isochronous IN Transfer */ ++ unsigned outepintr : 1; /*!< 19 OUT Endpoints Interrupt */ ++ unsigned inepintr : 1; /*!< 18 IN Endpoints Interrupt */ ++ unsigned epmismatch : 1; /*!< 17 Endpoint Mismatch Interrupt */ ++ unsigned reserved16 : 1; ++ unsigned eopframe : 1; /*!< 15 End of Periodic Frame Interrupt */ ++ unsigned isooutdrop : 1; /*!< 14 Isochronous OUT Packet Dropped Interrupt */ ++ unsigned enumdone : 1; /*!< 13 Enumeration Done */ ++ unsigned usbreset : 1; /*!< 12 USB Reset */ ++ unsigned usbsuspend : 1; /*!< 11 USB Suspend */ ++ unsigned erlysuspend : 1; /*!< 10 Early Suspend */ ++ unsigned i2cintr : 1; /*!< 09 I2C Interrupt */ ++ unsigned reserved8 : 1; ++ unsigned goutnakeff : 1; /*!< 07 Global OUT NAK Effective */ ++ unsigned ginnakeff : 1; /*!< 06 Global Non-periodic IN NAK Effective */ ++ unsigned nptxfempty : 1; /*!< 05 Non-periodic TxFIFO Empty */ ++ unsigned rxstsqlvl : 1; /*!< 04 Receive FIFO Non-Empty */ ++ unsigned sofintr : 1; /*!< 03 Start of (u)Frame */ ++ unsigned otgintr : 1; /*!< 02 OTG Interrupt */ ++ unsigned modemismatch : 1; /*!< 01 Mode Mismatch Interrupt */ ++ unsigned reserved0 : 1; ++ } b; ++} gint_data_t; ++ ++/*! ++ \brief Bit fields in the Receive Status Read and Pop Registers (GRXSTSR, GRXSTSP) ++ */ ++typedef union grxsts_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved : 7; ++ unsigned fn : 4; /*!< 24-21 Frame Number */ ++ unsigned pktsts : 4; /*!< 20-17 Packet Status */ ++ #define IFXUSB_DSTS_DATA_UPDT 0x2 // OUT Data Packet ++ #define IFXUSB_DSTS_XFER_COMP 0x3 // OUT Data Transfer Complete ++ #define IFXUSB_DSTS_GOUT_NAK 0x1 // Global OUT NAK ++ #define IFXUSB_DSTS_SETUP_COMP 0x4 // Setup Phase Complete ++ #define IFXUSB_DSTS_SETUP_UPDT 0x6 // SETUP Packet ++ unsigned dpid : 2; /*!< 16-15 Data PID */ ++ unsigned bcnt :11; /*!< 14-04 Byte Count */ ++ unsigned epnum : 4; /*!< 03-00 Endpoint Number */ ++ } db; ++ struct ++ { ++ unsigned reserved :11; ++ unsigned pktsts : 4; /*!< 20-17 Packet Status */ ++ #define IFXUSB_HSTS_DATA_UPDT 0x2 // OUT Data Packet ++ #define IFXUSB_HSTS_XFER_COMP 0x3 // OUT Data Transfer Complete ++ #define IFXUSB_HSTS_DATA_TOGGLE_ERR 0x5 // DATA TOGGLE Error ++ #define IFXUSB_HSTS_CH_HALTED 0x7 // Channel Halted ++ unsigned dpid : 2; /*!< 16-15 Data PID */ ++ unsigned bcnt :11; /*!< 14-04 Byte Count */ ++ unsigned chnum : 4; /*!< 03-00 Channel Number */ ++ } hb; ++} grxsts_data_t; ++ ++/*! ++ \brief Bit fields in the FIFO Size Registers (HPTXFSIZ, GNPTXFSIZ, DPTXFSIZn). ++ */ ++typedef union fifosize_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned depth : 16; /*!< 31-16 TxFIFO Depth (in DWord)*/ ++ unsigned startaddr : 16; /*!< 15-00 RAM Starting address */ ++ } b; ++} fifosize_data_t; ++ ++/*! ++ \brief Bit fields in the Non-Periodic Transmit FIFO/Queue Status Register (GNPTXSTS). ++ */ ++ ++typedef union gnptxsts_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved : 1; ++ unsigned nptxqtop_chnep : 4; /*!< 30-27 Channel/EP Number of top of the Non-Periodic ++ Transmit Request Queue ++ */ ++ unsigned nptxqtop_token : 2; /*!< 26-25 Token Type top of the Non-Periodic ++ Transmit Request Queue ++ 0 - IN/OUT ++ 1 - Zero Length OUT ++ 2 - PING/Complete Split ++ 3 - Channel Halt ++ */ ++ unsigned nptxqtop_terminate : 1; /*!< 24 Terminate (Last entry for the selected ++ channel/EP)*/ ++ unsigned nptxqspcavail : 8; /*!< 23-16 Transmit Request Queue Space Available */ ++ unsigned nptxfspcavail :16; /*!< 15-00 TxFIFO Space Avail (in DWord)*/ ++ }b; ++} gnptxsts_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Transmit FIFO Status Register (DTXFSTS). ++ */ ++typedef union dtxfsts_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved : 16; ++ unsigned txfspcavail : 16; /*!< 15-00 TxFIFO Space Avail (in DWord)*/ ++ }b; ++} dtxfsts_data_t; ++ ++ ++/*! ++ \brief Bit fields in the I2C Control Register (I2CCTL). ++ */ ++typedef union gi2cctl_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned bsydne : 1; /*!< 31 I2C Busy/Done*/ ++ unsigned rw : 1; /*!< 30 Read/Write Indicator */ ++ unsigned reserved : 2; ++ unsigned i2cdevaddr : 2; /*!< 27-26 I2C Device Address */ ++ unsigned i2csuspctl : 1; /*!< 25 I2C Suspend Control */ ++ unsigned ack : 1; /*!< 24 I2C ACK */ ++ unsigned i2cen : 1; /*!< 23 I2C Enable */ ++ unsigned addr : 7; /*!< 22-16 I2C Address */ ++ unsigned regaddr : 8; /*!< 15-08 I2C Register Addr */ ++ unsigned rwdata : 8; /*!< I2C Read/Write Data */ ++ } b; ++} gi2cctl_data_t; ++ ++ ++/*! ++ \brief Bit fields in the User HW Config1 Register. ++ */ ++typedef union hwcfg1_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned ep_dir15 : 2; /*!< Direction of each EP ++ 0: BIDIR (IN and OUT) endpoint ++ 1: IN endpoint ++ 2: OUT endpoint ++ 3: Reserved ++ */ ++ unsigned ep_dir14 : 2; ++ unsigned ep_dir13 : 2; ++ unsigned ep_dir12 : 2; ++ unsigned ep_dir11 : 2; ++ unsigned ep_dir10 : 2; ++ unsigned ep_dir09 : 2; ++ unsigned ep_dir08 : 2; ++ unsigned ep_dir07 : 2; ++ unsigned ep_dir06 : 2; ++ unsigned ep_dir05 : 2; ++ unsigned ep_dir04 : 2; ++ unsigned ep_dir03 : 2; ++ unsigned ep_dir02 : 2; ++ unsigned ep_dir01 : 2; ++ unsigned ep_dir00 : 2; ++ }b; ++} hwcfg1_data_t; ++ ++/*! ++ \brief Bit fields in the User HW Config2 Register. ++ */ ++typedef union hwcfg2_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved31 : 1; ++ unsigned dev_token_q_depth : 5; /*!< 30-26 Device Mode IN Token Sequence Learning Queue Depth */ ++ unsigned host_perio_tx_q_depth : 2; /*!< 25-24 Host Mode Periodic Request Queue Depth */ ++ unsigned nonperio_tx_q_depth : 2; /*!< 23-22 Non-periodic Request Queue Depth */ ++ unsigned rx_status_q_depth : 2; /*!< 21-20 Multi Processor Interrupt Enabled */ ++ unsigned dynamic_fifo : 1; /*!< 19 Dynamic FIFO Sizing Enabled */ ++ unsigned perio_ep_supported : 1; /*!< 18 Periodic OUT Channels Supported in Host Mode */ ++ unsigned num_host_chan : 4; /*!< 17-14 Number of Host Channels */ ++ unsigned num_dev_ep : 4; /*!< 13-10 Number of Device Endpoints */ ++ unsigned fs_phy_type : 2; /*!< 09-08 Full-Speed PHY Interface Type */ ++ #define IFXUSB_HWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 ++ #define IFXUSB_HWCFG2_FS_PHY_TYPE_DEDICATE 1 ++ #define IFXUSB_HWCFG2_FS_PHY_TYPE_UTMI 2 ++ #define IFXUSB_HWCFG2_FS_PHY_TYPE_ULPI 3 ++ unsigned hs_phy_type : 2; /*!< 07-06 High-Speed PHY Interface Type */ ++ #define IFXUSB_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 ++ #define IFXUSB_HWCFG2_HS_PHY_TYPE_UTMI 1 ++ #define IFXUSB_HWCFG2_HS_PHY_TYPE_ULPI 2 ++ #define IFXUSB_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 ++ unsigned point2point : 1; /*!< 05 Point-to-Point */ ++ unsigned architecture : 2; /*!< 04-03 Architecture */ ++ #define IFXUSB_HWCFG2_ARCH_SLAVE_ONLY 0 ++ #define IFXUSB_HWCFG2_ARCH_EXT_DMA 1 ++ #define IFXUSB_HWCFG2_ARCH_INT_DMA 2 ++ unsigned op_mode : 3; /*!< 02-00 Mode of Operation */ ++ #define IFXUSB_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0 ++ #define IFXUSB_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1 ++ #define IFXUSB_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2 ++ #define IFXUSB_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 ++ #define IFXUSB_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 ++ #define IFXUSB_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 ++ #define IFXUSB_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 ++ } b; ++} hwcfg2_data_t; ++ ++/*! ++ \brief Bit fields in the User HW Config3 Register. ++ */ ++typedef union hwcfg3_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned dfifo_depth :16; /*!< 31-16 DFIFO Depth */ ++ unsigned reserved15_12 : 4; ++ unsigned synch_reset_type : 1; /*!< 11 Reset Style for Clocked always Blocks in RTL */ ++ unsigned optional_features : 1; /*!< 10 Optional Features Removed */ ++ unsigned vendor_ctrl_if : 1; /*!< 09 Vendor Control Interface Support */ ++ unsigned i2c : 1; /*!< 08 I2C Selection */ ++ unsigned otg_func : 1; /*!< 07 OTG Function Enabled */ ++ unsigned packet_size_cntr_width : 3; /*!< 06-04 Width of Packet Size Counters */ ++ unsigned xfer_size_cntr_width : 4; /*!< 03-00 Width of Transfer Size Counters */ ++ } b; ++} hwcfg3_data_t; ++ ++/*! ++ \brief Bit fields in the User HW Config4 ++ * Register. Read the register into the d32 element then read ++ * out the bits using the bit elements. ++ */ ++typedef union hwcfg4_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned desc_dma_dyn : 1; /*!< 31 Scatter/Gather DMA */ ++ unsigned desc_dma : 1; /*!< 30 Scatter/Gather DMA configuration */ ++ unsigned num_in_eps : 4; /*!< 29-26 Number of Device Mode IN Endpoints Including Control Endpoints */ ++ unsigned ded_fifo_en : 1; /*!< 25 Enable Dedicated Transmit FIFO for device IN Endpoints */ ++ unsigned session_end_filt_en : 1; /*!< 24 session_end Filter Enabled */ ++ unsigned b_valid_filt_en : 1; /*!< 23 b_valid Filter Enabled */ ++ unsigned a_valid_filt_en : 1; /*!< 22 a_valid Filter Enabled */ ++ unsigned vbus_valid_filt_en : 1; /*!< 21 vbus_valid Filter Enabled */ ++ unsigned iddig_filt_en : 1; /*!< 20 iddig Filter Enable */ ++ unsigned num_dev_mode_ctrl_ep : 4; /*!< 19-16 Number of Device Mode Control Endpoints in Addition to Endpoint 0 */ ++ unsigned utmi_phy_data_width : 2; /*!< 15-14 UTMI+ PHY/ULPI-to-Internal UTMI+ Wrapper Data Width */ ++ unsigned reserved13_06 : 8; ++ unsigned min_ahb_freq : 1; /*!< 05 Minimum AHB Frequency Less Than 60 MHz */ ++ unsigned power_optimiz : 1; /*!< 04 Enable Power Optimization? */ ++ unsigned num_dev_perio_in_ep : 4; /*!< 03-00 Number of Device Mode Periodic IN Endpoints */ ++ } b; ++} hwcfg4_data_t; ++ ++/*@}*//*IFXUSB_CSR_CORE_GLOBAL_REG*/ ++ ++/****************************************************************************/ ++/*! ++ \addtogroup IFXUSB_CSR_DEVICE_GLOBAL_REG ++ */ ++/*@{*/ ++ ++/*! ++ \struct ifxusb_dev_global_regs ++ \brief IFXUSB Device Mode Global registers. Offsets 800h-BFFh ++ The ifxusb_dev_global_regs structure defines the size ++ and relative field offsets for the Device Global registers. ++ These registers are visible only in Device mode and must not be ++ accessed in Host mode, as the results are unknown. ++ */ ++typedef struct ifxusb_dev_global_regs ++{ ++ volatile uint32_t dcfg; /*!< 800h Device Configuration Register. */ ++ volatile uint32_t dctl; /*!< 804h Device Control Register. */ ++ volatile uint32_t dsts; /*!< 808h Device Status Register (Read Only). */ ++ uint32_t unused; ++ volatile uint32_t diepmsk; /*!< 810h Device IN Endpoint Common Interrupt Mask Register. */ ++ volatile uint32_t doepmsk; /*!< 814h Device OUT Endpoint Common Interrupt Mask Register. */ ++ volatile uint32_t daint; /*!< 818h Device All Endpoints Interrupt Register. */ ++ volatile uint32_t daintmsk; /*!< 81Ch Device All Endpoints Interrupt Mask Register. */ ++ volatile uint32_t dtknqr1; /*!< 820h Device IN Token Queue Read Register-1 (Read Only). */ ++ volatile uint32_t dtknqr2; /*!< 824h Device IN Token Queue Read Register-2 (Read Only). */ ++ volatile uint32_t dvbusdis; /*!< 828h Device VBUS discharge Register.*/ ++ volatile uint32_t dvbuspulse; /*!< 82Ch Device VBUS Pulse Register. */ ++ volatile uint32_t dtknqr3_dthrctl; /*!< 830h Device IN Token Queue Read Register-3 (Read Only). ++ Device Thresholding control register (Read/Write) ++ */ ++ volatile uint32_t dtknqr4_fifoemptymsk; /*!< 834h Device IN Token Queue Read Register-4 (Read Only). ++ Device IN EPs empty Inr. Mask Register (Read/Write) ++ */ ++} ifxusb_device_global_regs_t; ++ ++/*! ++ \brief Bit fields in the Device Configuration Register. ++ */ ++ ++typedef union dcfg_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved31_26 : 6; ++ unsigned perschintvl : 2; /*!< 25-24 Periodic Scheduling Interval */ ++ unsigned descdma : 1; /*!< 23 Enable Descriptor DMA in Device mode */ ++ unsigned epmscnt : 5; /*!< 22-18 In Endpoint Mis-match count */ ++ unsigned reserved13_17 : 5; ++ unsigned perfrint : 2; /*!< 12-11 Periodic Frame Interval */ ++ #define IFXUSB_DCFG_FRAME_INTERVAL_80 0 ++ #define IFXUSB_DCFG_FRAME_INTERVAL_85 1 ++ #define IFXUSB_DCFG_FRAME_INTERVAL_90 2 ++ #define IFXUSB_DCFG_FRAME_INTERVAL_95 3 ++ unsigned devaddr : 7; /*!< 10-04 Device Addresses */ ++ unsigned reserved3 : 1; ++ unsigned nzstsouthshk : 1; /*!< 02 Non Zero Length Status OUT Handshake */ ++ #define IFXUSB_DCFG_SEND_STALL 1 ++ unsigned devspd : 2; /*!< 01-00 Device Speed */ ++ } b; ++} dcfg_data_t; ++ ++/*! ++ \brief Bit fields in the Device Control Register. ++ */ ++typedef union dctl_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved16_31 :16; ++ unsigned ifrmnum : 1; /*!< 15 Ignore Frame Number for ISOC EPs */ ++ unsigned gmc : 2; /*!< 14-13 Global Multi Count */ ++ unsigned gcontbna : 1; /*!< 12 Global Continue on BNA */ ++ unsigned pwronprgdone : 1; /*!< 11 Power-On Programming Done */ ++ unsigned cgoutnak : 1; /*!< 10 Clear Global OUT NAK */ ++ unsigned sgoutnak : 1; /*!< 09 Set Global OUT NAK */ ++ unsigned cgnpinnak : 1; /*!< 08 Clear Global Non-Periodic IN NAK */ ++ unsigned sgnpinnak : 1; /*!< 07 Set Global Non-Periodic IN NAK */ ++ unsigned tstctl : 3; /*!< 06-04 Test Control */ ++ unsigned goutnaksts : 1; /*!< 03 Global OUT NAK Status */ ++ unsigned gnpinnaksts : 1; /*!< 02 Global Non-Periodic IN NAK Status */ ++ unsigned sftdiscon : 1; /*!< 01 Soft Disconnect */ ++ unsigned rmtwkupsig : 1; /*!< 00 Remote Wakeup */ ++ } b; ++} dctl_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Device Status Register. ++ */ ++typedef union dsts_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved22_31 :10; ++ unsigned soffn :14; /*!< 21-08 Frame or Microframe Number of the received SOF */ ++ unsigned reserved4_7 : 4; ++ unsigned errticerr : 1; /*!< 03 Erratic Error */ ++ unsigned enumspd : 2; /*!< 02-01 Enumerated Speed */ ++ #define IFXUSB_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 ++ #define IFXUSB_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 ++ #define IFXUSB_DSTS_ENUMSPD_LS_PHY_6MHZ 2 ++ #define IFXUSB_DSTS_ENUMSPD_FS_PHY_48MHZ 3 ++ unsigned suspsts : 1; /*!< 00 Suspend Status */ ++ } b; ++} dsts_data_t; ++ ++/*! ++ \brief Bit fields in the Device IN EP Interrupt Register ++ and the Device IN EP Common Mask Register. ++ */ ++typedef union diepint_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved14_31 :18; ++ unsigned nakmsk : 1; /*!< 13 NAK interrupt Mask */ ++ unsigned reserved10_12 : 3; ++ unsigned bna : 1; /*!< 09 BNA Interrupt mask */ ++ unsigned txfifoundrn : 1; /*!< 08 Fifo Underrun Mask */ ++ unsigned emptyintr : 1; /*!< 07 IN Endpoint HAK Effective mask */ ++ unsigned inepnakeff : 1; /*!< 06 IN Endpoint HAK Effective mask */ ++ unsigned intknepmis : 1; /*!< 05 IN Token Received with EP mismatch mask */ ++ unsigned intktxfemp : 1; /*!< 04 IN Token received with TxF Empty mask */ ++ unsigned timeout : 1; /*!< 03 TimeOUT Handshake mask (non-ISOC EPs) */ ++ unsigned ahberr : 1; /*!< 02 AHB Error mask */ ++ unsigned epdisabled : 1; /*!< 01 Endpoint disable mask */ ++ unsigned xfercompl : 1; /*!< 00 Transfer complete mask */ ++ } b; ++} diepint_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Device OUT EP Interrupt Register and ++ Device OUT EP Common Interrupt Mask Register. ++ */ ++typedef union doepint_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved15_31 :17; ++ unsigned nyetmsk : 1; /*!< 14 NYET Interrupt */ ++ unsigned nakmsk : 1; /*!< 13 NAK Interrupt */ ++ unsigned bbleerrmsk : 1; /*!< 12 Babble Interrupt */ ++ unsigned reserved10_11 : 2; ++ unsigned bna : 1; /*!< 09 BNA Interrupt */ ++ unsigned outpkterr : 1; /*!< 08 OUT packet Error */ ++ unsigned reserved07 : 1; ++ unsigned back2backsetup : 1; /*!< 06 Back-to-Back SETUP Packets Received */ ++ unsigned stsphsercvd : 1; /*!< 05 */ ++ unsigned outtknepdis : 1; /*!< 04 OUT Token Received when Endpoint Disabled */ ++ unsigned setup : 1; /*!< 03 Setup Phase Done (contorl EPs) */ ++ unsigned ahberr : 1; /*!< 02 AHB Error */ ++ unsigned epdisabled : 1; /*!< 01 Endpoint disable */ ++ unsigned xfercompl : 1; /*!< 00 Transfer complete */ ++ } b; ++} doepint_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Device All EP Interrupt Registers. ++ */ ++typedef union daint_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned out : 16; /*!< 31-16 OUT Endpoint bits */ ++ unsigned in : 16; /*!< 15-00 IN Endpoint bits */ ++ } eps; ++ struct ++ { ++ /** OUT Endpoint bits */ ++ unsigned outep15 : 1; ++ unsigned outep14 : 1; ++ unsigned outep13 : 1; ++ unsigned outep12 : 1; ++ unsigned outep11 : 1; ++ unsigned outep10 : 1; ++ unsigned outep09 : 1; ++ unsigned outep08 : 1; ++ unsigned outep07 : 1; ++ unsigned outep06 : 1; ++ unsigned outep05 : 1; ++ unsigned outep04 : 1; ++ unsigned outep03 : 1; ++ unsigned outep02 : 1; ++ unsigned outep01 : 1; ++ unsigned outep00 : 1; ++ /** IN Endpoint bits */ ++ unsigned inep15 : 1; ++ unsigned inep14 : 1; ++ unsigned inep13 : 1; ++ unsigned inep12 : 1; ++ unsigned inep11 : 1; ++ unsigned inep10 : 1; ++ unsigned inep09 : 1; ++ unsigned inep08 : 1; ++ unsigned inep07 : 1; ++ unsigned inep06 : 1; ++ unsigned inep05 : 1; ++ unsigned inep04 : 1; ++ unsigned inep03 : 1; ++ unsigned inep02 : 1; ++ unsigned inep01 : 1; ++ unsigned inep00 : 1; ++ } ep; ++} daint_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Device IN Token Queue Read Registers. ++ */ ++typedef union dtknq1_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned epnums0_5 :24; /*!< 31-08 EP Numbers of IN Tokens 0 ... 4 */ ++ unsigned wrap_bit : 1; /*!< 07 write pointer has wrapped */ ++ unsigned reserved05_06 : 2; ++ unsigned intknwptr : 5; /*!< 04-00 In Token Queue Write Pointer */ ++ }b; ++} dtknq1_data_t; ++ ++ ++/*! ++ \brief Bit fields in Threshold control Register ++ */ ++typedef union dthrctl_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved26_31 : 6; ++ unsigned rx_thr_len : 9; /*!< 25-17 Rx Thr. Length */ ++ unsigned rx_thr_en : 1; /*!< 16 Rx Thr. Enable */ ++ unsigned reserved11_15 : 5; ++ unsigned tx_thr_len : 9; /*!< 10-02 Tx Thr. Length */ ++ unsigned iso_thr_en : 1; /*!< 01 ISO Tx Thr. Enable */ ++ unsigned non_iso_thr_en : 1; /*!< 00 non ISO Tx Thr. Enable */ ++ } b; ++} dthrctl_data_t; ++ ++/*@}*//*IFXUSB_CSR_DEVICE_GLOBAL_REG*/ ++ ++/****************************************************************************/ ++ ++/*! ++ \addtogroup IFXUSB_CSR_DEVICE_EP_REG ++ */ ++/*@{*/ ++ ++/*! ++ \struct ifxusb_dev_in_ep_regs ++ \brief Device Logical IN Endpoint-Specific Registers. ++ There will be one set of endpoint registers per logical endpoint ++ implemented. ++ each EP's IN EP Register are offset at : ++ 900h + * (ep_num * 20h) ++ */ ++ ++typedef struct ifxusb_dev_in_ep_regs ++{ ++ volatile uint32_t diepctl; /*!< 00h: Endpoint Control Register */ ++ uint32_t reserved04; /*!< 04h: */ ++ volatile uint32_t diepint; /*!< 08h: Endpoint Interrupt Register */ ++ uint32_t reserved0C; /*!< 0Ch: */ ++ volatile uint32_t dieptsiz; /*!< 10h: Endpoint Transfer Size Register.*/ ++ volatile uint32_t diepdma; /*!< 14h: Endpoint DMA Address Register. */ ++ volatile uint32_t dtxfsts; /*!< 18h: Endpoint Transmit FIFO Status Register. */ ++ volatile uint32_t diepdmab; /*!< 1Ch: Endpoint DMA Buffer Register. */ ++} ifxusb_dev_in_ep_regs_t; ++ ++/*! ++ \brief Device Logical OUT Endpoint-Specific Registers. ++ There will be one set of endpoint registers per logical endpoint ++ implemented. ++ each EP's OUT EP Register are offset at : ++ B00h + * (ep_num * 20h) + 00h ++ */ ++typedef struct ifxusb_dev_out_ep_regs ++{ ++ volatile uint32_t doepctl; /*!< 00h: Endpoint Control Register */ ++ volatile uint32_t doepfn; /*!< 04h: Endpoint Frame number Register */ ++ volatile uint32_t doepint; /*!< 08h: Endpoint Interrupt Register */ ++ uint32_t reserved0C; /*!< 0Ch: */ ++ volatile uint32_t doeptsiz; /*!< 10h: Endpoint Transfer Size Register.*/ ++ volatile uint32_t doepdma; /*!< 14h: Endpoint DMA Address Register. */ ++ uint32_t reserved18; /*!< 18h: */ ++ volatile uint32_t doepdmab; /*!< 1Ch: Endpoint DMA Buffer Register. */ ++} ifxusb_dev_out_ep_regs_t; ++ ++ ++/*! ++ \brief Bit fields in the Device EP Control ++ Register. ++ */ ++typedef union depctl_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned epena : 1; /*!< 31 Endpoint Enable */ ++ unsigned epdis : 1; /*!< 30 Endpoint Disable */ ++ unsigned setd1pid : 1; /*!< 29 Set DATA1 PID (INTR/Bulk IN and OUT endpoints) */ ++ unsigned setd0pid : 1; /*!< 28 Set DATA0 PID (INTR/Bulk IN and OUT endpoints) */ ++ unsigned snak : 1; /*!< 27 Set NAK */ ++ unsigned cnak : 1; /*!< 26 Clear NAK */ ++ unsigned txfnum : 4; /*!< 25-22 Tx Fifo Number */ ++ unsigned stall : 1; /*!< 21 Stall Handshake */ ++ unsigned snp : 1; /*!< 20 Snoop Mode */ ++ unsigned eptype : 2; /*!< 19-18 Endpoint Type ++ 0: Control ++ 1: Isochronous ++ 2: Bulk ++ 3: Interrupt ++ */ ++ unsigned naksts : 1; /*!< 17 NAK Status */ ++ unsigned dpid : 1; /*!< 16 Endpoint DPID (INTR/Bulk IN and OUT endpoints) */ ++ unsigned usbactep : 1; /*!< 15 USB Active Endpoint */ ++ unsigned nextep : 4; /*!< 14-11 Next Endpoint */ ++ unsigned mps :11; /*!< 10-00 Maximum Packet Size */ ++ #define IFXUSB_DEP0CTL_MPS_64 0 ++ #define IFXUSB_DEP0CTL_MPS_32 1 ++ #define IFXUSB_DEP0CTL_MPS_16 2 ++ #define IFXUSB_DEP0CTL_MPS_8 3 ++ } b; ++} depctl_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Device EP Transfer Size Register. (EP0 and EPn) ++ */ ++typedef union deptsiz_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved31 : 1; ++ unsigned supcnt : 2; /*!< 30-29 Setup Packet Count */ ++ unsigned reserved20_28 : 9; ++ unsigned pktcnt : 1; /*!< 19 Packet Count */ ++ unsigned reserved7_18 :12; ++ unsigned xfersize : 7; /*!< 06-00 Transfer size */ ++ }b0; ++ struct ++ { ++ unsigned reserved : 1; ++ unsigned mc : 2; /*!< 30-29 Multi Count */ ++ unsigned pktcnt :10; /*!< 28-19 Packet Count */ ++ unsigned xfersize :19; /*!< 18-00 Transfer size */ ++ } b; ++} deptsiz_data_t; ++ ++/*@}*//*IFXUSB_CSR_DEVICE_EP_REG*/ ++/****************************************************************************/ ++ ++/*! ++ \addtogroup IFXUSB_CSR_DEVICE_DMA_DESC ++ */ ++/*@{*/ ++/*! ++ \struct desc_sts_data ++ \brief Bit fields in the DMA Descriptor status quadlet. ++ */ ++typedef union desc_sts_data ++{ ++ struct ++ { ++ unsigned bs : 2; /*!< 31-30 Buffer Status */ ++ #define BS_HOST_READY 0x0 ++ #define BS_DMA_BUSY 0x1 ++ #define BS_DMA_DONE 0x2 ++ #define BS_HOST_BUSY 0x3 ++ unsigned sts : 2; /*!< 29-28 Receive/Trasmit Status */ ++ #define RTS_SUCCESS 0x0 ++ #define RTS_BUFFLUSH 0x1 ++ #define RTS_RESERVED 0x2 ++ #define RTS_BUFERR 0x3 ++ unsigned l : 1; /*!< 27 Last */ ++ unsigned sp : 1; /*!< 26 Short Packet */ ++ unsigned ioc : 1; /*!< 25 Interrupt On Complete */ ++ unsigned sr : 1; /*!< 24 Setup Packet received */ ++ unsigned mtrf : 1; /*!< 23 Multiple Transfer */ ++ unsigned reserved16_22 : 7; ++ unsigned bytes :16; /*!< 15-00 Transfer size in bytes */ ++ } b; ++ uint32_t d32; /*!< DMA Descriptor data buffer pointer */ ++} desc_sts_data_t; ++ ++/*@}*//*IFXUSB_CSR_DEVICE_DMA_DESC*/ ++/****************************************************************************/ ++ ++/*! ++ \addtogroup IFXUSB_CSR_HOST_GLOBAL_REG ++ */ ++/*@{*/ ++/*! ++ \struct ifxusb_host_global_regs ++ \brief IFXUSB Host Mode Global registers. Offsets 400h-7FFh ++ The ifxusb_host_global_regs structure defines the size ++ and relative field offsets for the Host Global registers. ++ These registers are visible only in Host mode and must not be ++ accessed in Device mode, as the results are unknown. ++ */ ++typedef struct ifxusb_host_global_regs ++{ ++ volatile uint32_t hcfg; /*!< 400h Host Configuration Register. */ ++ volatile uint32_t hfir; /*!< 404h Host Frame Interval Register. */ ++ volatile uint32_t hfnum; /*!< 408h Host Frame Number / Frame Remaining Register. */ ++ uint32_t reserved40C; ++ volatile uint32_t hptxsts; /*!< 410h Host Periodic Transmit FIFO/ Queue Status Register. */ ++ volatile uint32_t haint; /*!< 414h Host All Channels Interrupt Register. */ ++ volatile uint32_t haintmsk; /*!< 418h Host All Channels Interrupt Mask Register. */ ++} ifxusb_host_global_regs_t; ++ ++/*! ++ \brief Bit fields in the Host Configuration Register. ++ */ ++typedef union hcfg_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved31_03 :29; ++ unsigned fslssupp : 1; /*!< 02 FS/LS Only Support */ ++ unsigned fslspclksel : 2; /*!< 01-00 FS/LS Phy Clock Select */ ++ #define IFXUSB_HCFG_30_60_MHZ 0 ++ #define IFXUSB_HCFG_48_MHZ 1 ++ #define IFXUSB_HCFG_6_MHZ 2 ++ } b; ++} hcfg_data_t; ++ ++/*! ++ \brief Bit fields in the Host Frame Interval Register. ++ */ ++typedef union hfir_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved : 16; ++ unsigned frint : 16; /*!< 15-00 Frame Interval */ ++ } b; ++} hfir_data_t; ++ ++/*! ++ \brief Bit fields in the Host Frame Time Remaing/Number Register. ++ */ ++typedef union hfnum_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned frrem : 16; /*!< 31-16 Frame Time Remaining */ ++ unsigned frnum : 16; /*!< 15-00 Frame Number*/ ++ #define IFXUSB_HFNUM_MAX_FRNUM 0x3FFF ++ } b; ++} hfnum_data_t; ++ ++/*! ++ \brief Bit fields in the Host Periodic Transmit FIFO/Queue Status Register ++ */ ++typedef union hptxsts_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ struct ++ { ++ /** Top of the Periodic Transmit Request Queue ++ * - bit 24 - Terminate (last entry for the selected channel) ++ */ ++ unsigned ptxqtop_odd : 1; /*!< 31 Top of the Periodic Transmit Request ++ Queue Odd/even microframe*/ ++ unsigned ptxqtop_chnum : 4; /*!< 30-27 Top of the Periodic Transmit Request ++ Channel Number */ ++ unsigned ptxqtop_token : 2; /*!< 26-25 Top of the Periodic Transmit Request ++ Token Type ++ 0 - Zero length ++ 1 - Ping ++ 2 - Disable ++ */ ++ unsigned ptxqtop_terminate : 1; /*!< 24 Top of the Periodic Transmit Request ++ Terminate (last entry for the selected channel)*/ ++ unsigned ptxqspcavail : 8; /*!< 23-16 Periodic Transmit Request Queue Space Available */ ++ unsigned ptxfspcavail :16; /*!< 15-00 Periodic Transmit Data FIFO Space Available */ ++ } b; ++} hptxsts_data_t; ++ ++/*! ++ \brief Bit fields in the Host Port Control and Status Register. ++ */ ++typedef union hprt0_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved19_31 :13; ++ unsigned prtspd : 2; /*!< 18-17 Port Speed */ ++ #define IFXUSB_HPRT0_PRTSPD_HIGH_SPEED 0 ++ #define IFXUSB_HPRT0_PRTSPD_FULL_SPEED 1 ++ #define IFXUSB_HPRT0_PRTSPD_LOW_SPEED 2 ++ unsigned prttstctl : 4; /*!< 16-13 Port Test Control */ ++ unsigned prtpwr : 1; /*!< 12 Port Power */ ++ unsigned prtlnsts : 2; /*!< 11-10 Port Line Status */ ++ unsigned reserved9 : 1; ++ unsigned prtrst : 1; /*!< 08 Port Reset */ ++ unsigned prtsusp : 1; /*!< 07 Port Suspend */ ++ unsigned prtres : 1; /*!< 06 Port Resume */ ++ unsigned prtovrcurrchng : 1; /*!< 05 Port Overcurrent Change */ ++ unsigned prtovrcurract : 1; /*!< 04 Port Overcurrent Active */ ++ unsigned prtenchng : 1; /*!< 03 Port Enable/Disable Change */ ++ unsigned prtena : 1; /*!< 02 Port Enable */ ++ unsigned prtconndet : 1; /*!< 01 Port Connect Detected */ ++ unsigned prtconnsts : 1; /*!< 00 Port Connect Status */ ++ }b; ++} hprt0_data_t; ++ ++/*! ++ \brief Bit fields in the Host All Interrupt Register. ++ */ ++typedef union haint_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved : 16; ++ unsigned ch15 : 1; ++ unsigned ch14 : 1; ++ unsigned ch13 : 1; ++ unsigned ch12 : 1; ++ unsigned ch11 : 1; ++ unsigned ch10 : 1; ++ unsigned ch09 : 1; ++ unsigned ch08 : 1; ++ unsigned ch07 : 1; ++ unsigned ch06 : 1; ++ unsigned ch05 : 1; ++ unsigned ch04 : 1; ++ unsigned ch03 : 1; ++ unsigned ch02 : 1; ++ unsigned ch01 : 1; ++ unsigned ch00 : 1; ++ } b; ++ struct ++ { ++ unsigned reserved : 16; ++ unsigned chint : 16; ++ } b2; ++} haint_data_t; ++/*@}*//*IFXUSB_CSR_HOST_GLOBAL_REG*/ ++/****************************************************************************/ ++/*! ++ \addtogroup IFXUSB_CSR_HOST_HC_REG ++ */ ++/*@{*/ ++/*! ++ \brief Host Channel Specific Registers ++ There will be one set of hc registers per host channelimplemented. ++ each HC's Register are offset at : ++ 500h + * (hc_num * 20h) ++ */ ++typedef struct ifxusb_hc_regs ++{ ++ volatile uint32_t hcchar; /*!< 00h Host Channel Characteristic Register.*/ ++ volatile uint32_t hcsplt; /*!< 04h Host Channel Split Control Register.*/ ++ volatile uint32_t hcint; /*!< 08h Host Channel Interrupt Register. */ ++ volatile uint32_t hcintmsk; /*!< 0Ch Host Channel Interrupt Mask Register. */ ++ volatile uint32_t hctsiz; /*!< 10h Host Channel Transfer Size Register. */ ++ volatile uint32_t hcdma; /*!< 14h Host Channel DMA Address Register. */ ++ uint32_t reserved[2]; /*!< 18h Reserved. */ ++} ifxusb_hc_regs_t; ++ ++ ++/*! ++ \brief Bit fields in the Host Channel Characteristics Register. ++ */ ++typedef union hcchar_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned chen : 1; /*!< 31 Channel enable */ ++ unsigned chdis : 1; /*!< 30 Channel disable */ ++ unsigned oddfrm : 1; /*!< 29 Frame to transmit periodic transaction */ ++ unsigned devaddr : 7; /*!< 28-22 Device address */ ++ unsigned multicnt : 2; /*!< 21-20 Packets per frame for periodic transfers */ ++ unsigned eptype : 2; /*!< 19-18 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ ++ unsigned lspddev : 1; /*!< 17 0: Full/high speed device, 1: Low speed device */ ++ unsigned reserved : 1; ++ unsigned epdir : 1; /*!< 15 0: OUT, 1: IN */ ++ unsigned epnum : 4; /*!< 14-11 Endpoint number */ ++ unsigned mps :11; /*!< 10-00 Maximum packet size in bytes */ ++ } b; ++} hcchar_data_t; ++ ++/*! ++ \brief Bit fields in the Host Channel Split Control Register ++ */ ++typedef union hcsplt_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned spltena : 1; /*!< 31 Split Enble */ ++ unsigned reserved :14; ++ unsigned compsplt : 1; /*!< 16 Do Complete Split */ ++ unsigned xactpos : 2; /*!< 15-14 Transaction Position */ ++ #define IFXUSB_HCSPLIT_XACTPOS_MID 0 ++ #define IFXUSB_HCSPLIT_XACTPOS_END 1 ++ #define IFXUSB_HCSPLIT_XACTPOS_BEGIN 2 ++ #define IFXUSB_HCSPLIT_XACTPOS_ALL 3 ++ unsigned hubaddr : 7; /*!< 13-07 Hub Address */ ++ unsigned prtaddr : 7; /*!< 06-00 Port Address */ ++ } b; ++} hcsplt_data_t; ++ ++/*! ++ \brief Bit fields in the Host Interrupt Register. ++ */ ++typedef union hcint_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved :21; ++ unsigned datatglerr : 1; /*!< 10 Data Toggle Error */ ++ unsigned frmovrun : 1; /*!< 09 Frame Overrun */ ++ unsigned bblerr : 1; /*!< 08 Babble Error */ ++ unsigned xacterr : 1; /*!< 07 Transaction Err */ ++ unsigned nyet : 1; /*!< 06 NYET Response Received */ ++ unsigned ack : 1; /*!< 05 ACK Response Received */ ++ unsigned nak : 1; /*!< 04 NAK Response Received */ ++ unsigned stall : 1; /*!< 03 STALL Response Received */ ++ unsigned ahberr : 1; /*!< 02 AHB Error */ ++ unsigned chhltd : 1; /*!< 01 Channel Halted */ ++ unsigned xfercomp : 1; /*!< 00 Channel Halted */ ++ }b; ++} hcint_data_t; ++ ++ ++/*! ++ \brief Bit fields in the Host Channel Transfer Size ++ Register. ++ */ ++typedef union hctsiz_data ++{ ++ uint32_t d32; ++ struct ++ { ++ /** */ ++ unsigned dopng : 1; /*!< 31 Do PING protocol when 1 */ ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control), SETUP (Control) ++ */ ++ unsigned pid : 2; /*!< 30-29 Packet ID for next data packet ++ 0: DATA0 ++ 1: DATA2 ++ 2: DATA1 ++ 3: MDATA (non-Control), SETUP (Control) ++ */ ++ #define IFXUSB_HCTSIZ_DATA0 0 ++ #define IFXUSB_HCTSIZ_DATA1 2 ++ #define IFXUSB_HCTSIZ_DATA2 1 ++ #define IFXUSB_HCTSIZ_MDATA 3 ++ #define IFXUSB_HCTSIZ_SETUP 3 ++ unsigned pktcnt :10; /*!< 28-19 Data packets to transfer */ ++ unsigned xfersize :19; /*!< 18-00 Total transfer size in bytes */ ++ }b; ++} hctsiz_data_t; ++ ++/*@}*//*IFXUSB_CSR_HOST_HC_REG*/ ++ ++/****************************************************************************/ ++ ++/*! ++ \addtogroup IFXUSB_CSR_PWR_CLK_GATING_REG ++ */ ++/*@{*/ ++/*! ++ \brief Bit fields in the Power and Clock Gating Control Register ++ */ ++typedef union pcgcctl_data ++{ ++ uint32_t d32; ++ struct ++ { ++ unsigned reserved : 27; ++ unsigned physuspended : 1; /*!< 04 PHY Suspended */ ++ unsigned rstpdwnmodule : 1; /*!< 03 Reset Power Down Modules */ ++ unsigned pwrclmp : 1; /*!< 02 Power Clamp */ ++ unsigned gatehclk : 1; /*!< 01 Gate Hclk */ ++ unsigned stoppclk : 1; /*!< 00 Stop Pclk */ ++ } b; ++} pcgcctl_data_t; ++/*@}*//*IFXUSB_CSR_PWR_CLK_GATING_REG*/ ++ ++/****************************************************************************/ ++ ++#endif //__IFXUSB_REGS_H__ +diff --git a/drivers/usb/ifxhcd/ifxusb_version.h b/drivers/usb/ifxhcd/ifxusb_version.h +new file mode 100644 +index 0000000..2dff735 +--- /dev/null ++++ b/drivers/usb/ifxhcd/ifxusb_version.h +@@ -0,0 +1,5 @@ ++ ++#ifndef IFXUSB_VERSION ++#define IFXUSB_VERSION "3.0alpha B100312" ++#endif ++ +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0047-MIPS-adds-gptu-driver.patch b/target/linux/lantiq/patches-3.3/0047-MIPS-adds-gptu-driver.patch new file mode 100644 index 0000000000..3258456db8 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0047-MIPS-adds-gptu-driver.patch @@ -0,0 +1,195 @@ +From 4dd444aa83346f37a2efcb10a2e3c83db2bf4ca5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 14 Mar 2012 15:37:19 +0100 +Subject: [PATCH 47/70] MIPS: adds gptu driver + +--- + arch/mips/lantiq/xway/gptu.c | 176 ++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 176 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/lantiq/xway/gptu.c + +diff --git a/arch/mips/lantiq/xway/gptu.c b/arch/mips/lantiq/xway/gptu.c +new file mode 100644 +index 0000000..ac82c37 +--- /dev/null ++++ b/arch/mips/lantiq/xway/gptu.c +@@ -0,0 +1,176 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2012 John Crispin ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include "../clk.h" ++ ++#include "../devices.h" ++ ++#define ltq_gptu_w32(x, y) ltq_w32((x), ltq_gptu_membase + (y)) ++#define ltq_gptu_r32(x) ltq_r32(ltq_gptu_membase + (x)) ++ ++ ++/* the magic ID byte of the core */ ++#define GPTU_MAGIC 0x59 ++/* clock control register */ ++#define GPTU_CLC 0x00 ++/* id register */ ++#define GPTU_ID 0x08 ++/* interrupt node enable */ ++#define GPTU_IRNEN 0xf4 ++/* interrupt control register */ ++#define GPTU_IRCR 0xf8 ++/* interrupt capture register */ ++#define GPTU_IRNCR 0xfc ++/* there are 3 identical blocks of 2 timers. calculate register offsets */ ++#define GPTU_SHIFT(x) (x % 2 ? 4 : 0) ++#define GPTU_BASE(x) (((x >> 1) * 0x20) + 0x10) ++/* timer control register */ ++#define GPTU_CON(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x00) ++/* timer auto reload register */ ++#define GPTU_RUN(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x08) ++/* timer manual reload register */ ++#define GPTU_RLD(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x10) ++/* timer count register */ ++#define GPTU_CNT(x) (GPTU_BASE(x) + GPTU_SHIFT(x) + 0x18) ++ ++/* GPTU_CON(x) */ ++#define CON_CNT BIT(2) ++#define CON_EDGE_FALL BIT(7) ++#define CON_SYNC BIT(8) ++#define CON_CLK_INT BIT(10) ++ ++/* GPTU_RUN(x) */ ++#define RUN_SEN BIT(0) ++#define RUN_RL BIT(2) ++ ++/* set clock to runmode */ ++#define CLC_RMC BIT(8) ++/* bring core out of suspend */ ++#define CLC_SUSPEND BIT(4) ++/* the disable bit */ ++#define CLC_DISABLE BIT(0) ++ ++#define TIMER_INTERRUPT (INT_NUM_IM3_IRL0 + 22) ++ ++enum gptu_timer { ++ TIMER1A = 0, ++ TIMER1B, ++ TIMER2A, ++ TIMER2B, ++ TIMER3A, ++ TIMER3B ++}; ++ ++static struct resource ltq_gptu_resource = ++ MEM_RES("GPTU", LTQ_GPTU_BASE_ADDR, LTQ_GPTU_SIZE); ++ ++static void __iomem *ltq_gptu_membase; ++ ++static irqreturn_t timer_irq_handler(int irq, void *priv) ++{ ++ int timer = irq - TIMER_INTERRUPT; ++ ltq_gptu_w32(1 << timer, GPTU_IRNCR); ++ return IRQ_HANDLED; ++} ++ ++static void gptu_hwinit(void) ++{ ++ struct clk *clk = clk_get_sys("ltq_gptu", NULL); ++ clk_enable(clk); ++ ltq_gptu_w32(0x00, GPTU_IRNEN); ++ ltq_gptu_w32(0xff, GPTU_IRNCR); ++ ltq_gptu_w32(CLC_RMC | CLC_SUSPEND, GPTU_CLC); ++} ++ ++static void gptu_hwexit(void) ++{ ++ ltq_gptu_w32(0x00, GPTU_IRNEN); ++ ltq_gptu_w32(0xff, GPTU_IRNCR); ++ ltq_gptu_w32(CLC_DISABLE, GPTU_CLC); ++} ++ ++static int ltq_gptu_enable(struct clk *clk) ++{ ++ int ret = request_irq(TIMER_INTERRUPT + clk->bits, timer_irq_handler, ++ IRQF_TIMER, "timer", NULL); ++ if (ret) { ++ pr_err("gptu: failed to request irq\n"); ++ return ret; ++ } ++ ++ ltq_gptu_w32(CON_CNT | CON_EDGE_FALL | CON_SYNC | CON_CLK_INT, ++ GPTU_CON(clk->bits)); ++ ltq_gptu_w32(1, GPTU_RLD(clk->bits)); ++ ltq_gptu_w32(ltq_gptu_r32(GPTU_IRNEN) | clk->bits, GPTU_IRNEN); ++ ltq_gptu_w32(RUN_SEN | RUN_RL, GPTU_RUN(clk->bits)); ++ return 0; ++} ++ ++static void ltq_gptu_disable(struct clk *clk) ++{ ++ ltq_gptu_w32(0, GPTU_RUN(clk->bits)); ++ ltq_gptu_w32(0, GPTU_CON(clk->bits)); ++ ltq_gptu_w32(0, GPTU_RLD(clk->bits)); ++ ltq_gptu_w32(ltq_gptu_r32(GPTU_IRNEN) & ~clk->bits, GPTU_IRNEN); ++ free_irq(TIMER_INTERRUPT + clk->bits, NULL); ++} ++ ++static inline void clkdev_add_gptu(const char *con, unsigned int timer) ++{ ++ struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL); ++ ++ clk->cl.dev_id = "ltq_gptu"; ++ clk->cl.con_id = con; ++ clk->cl.clk = clk; ++ clk->enable = ltq_gptu_enable; ++ clk->disable = ltq_gptu_disable; ++ clk->bits = timer; ++ clkdev_add(&clk->cl); ++} ++ ++static int __init gptu_setup(void) ++{ ++ /* remap gptu register range */ ++ ltq_gptu_membase = ltq_remap_resource(<q_gptu_resource); ++ if (!ltq_gptu_membase) ++ panic("Failed to remap gptu memory"); ++ ++ /* power up the core */ ++ gptu_hwinit(); ++ ++ /* the gptu has a ID register */ ++ if (((ltq_gptu_r32(GPTU_ID) >> 8) & 0xff) != GPTU_MAGIC) { ++ pr_err("gptu: failed to find magic\n"); ++ gptu_hwexit(); ++ return -ENAVAIL; ++ } ++ ++ /* register the clocks */ ++ clkdev_add_gptu("timer1a", TIMER1A); ++ clkdev_add_gptu("timer1b", TIMER1B); ++ clkdev_add_gptu("timer2a", TIMER2A); ++ clkdev_add_gptu("timer2b", TIMER2B); ++ clkdev_add_gptu("timer3a", TIMER3A); ++ clkdev_add_gptu("timer3b", TIMER3B); ++ ++ pr_info("gptu: 6 timers loaded\n"); ++ ++ return 0; ++} ++ ++arch_initcall(gptu_setup); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0048-MIPS-lantiq-pci-rename-variable-inside.patch b/target/linux/lantiq/patches-3.3/0048-MIPS-lantiq-pci-rename-variable-inside.patch new file mode 100644 index 0000000000..b3db0bc5f6 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0048-MIPS-lantiq-pci-rename-variable-inside.patch @@ -0,0 +1,79 @@ +From 202f1bad2707e843dccc0fb08233692f8c845f90 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 12:00:17 +0100 +Subject: [PATCH 48/70] MIPS: lantiq: pci: rename variable inside + +* rename a global var inside the pci code +--- + arch/mips/pci/ops-lantiq.c | 6 +++--- + arch/mips/pci/pci-lantiq.c | 6 +++--- + arch/mips/pci/pci-lantiq.h | 2 +- + 3 files changed, 7 insertions(+), 7 deletions(-) + +diff --git a/arch/mips/pci/ops-lantiq.c b/arch/mips/pci/ops-lantiq.c +index 1f2afb5..5cbb0cf 100644 +--- a/arch/mips/pci/ops-lantiq.c ++++ b/arch/mips/pci/ops-lantiq.c +@@ -41,7 +41,7 @@ static int ltq_pci_config_access(unsigned char access_type, struct pci_bus *bus, + + spin_lock_irqsave(&ebu_lock, flags); + +- cfg_base = (unsigned long) ltq_pci_mapped_cfg; ++ cfg_base = (unsigned long) ltq_pci_cfgbase; + cfg_base |= (bus->number << LTQ_PCI_CFG_BUSNUM_SHF) | (devfn << + LTQ_PCI_CFG_FUNNUM_SHF) | (where & ~0x3); + +@@ -55,11 +55,11 @@ static int ltq_pci_config_access(unsigned char access_type, struct pci_bus *bus, + wmb(); + + /* clean possible Master abort */ +- cfg_base = (unsigned long) ltq_pci_mapped_cfg; ++ cfg_base = (unsigned long) ltq_pci_cfgbase; + cfg_base |= (0x0 << LTQ_PCI_CFG_FUNNUM_SHF) + 4; + temp = ltq_r32(((u32 *)(cfg_base))); + temp = swab32(temp); +- cfg_base = (unsigned long) ltq_pci_mapped_cfg; ++ cfg_base = (unsigned long) ltq_pci_cfgbase; + cfg_base |= (0x68 << LTQ_PCI_CFG_FUNNUM_SHF) + 4; + ltq_w32(temp, ((u32 *)cfg_base)); + +diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c +index 47b551c..efcdd45 100644 +--- a/arch/mips/pci/pci-lantiq.c ++++ b/arch/mips/pci/pci-lantiq.c +@@ -65,8 +65,8 @@ + #define ltq_pci_w32(x, y) ltq_w32((x), ltq_pci_membase + (y)) + #define ltq_pci_r32(x) ltq_r32(ltq_pci_membase + (x)) + +-#define ltq_pci_cfg_w32(x, y) ltq_w32((x), ltq_pci_mapped_cfg + (y)) +-#define ltq_pci_cfg_r32(x) ltq_r32(ltq_pci_mapped_cfg + (x)) ++#define ltq_pci_cfg_w32(x, y) ltq_w32((x), ltq_pci_cfgbase + (y)) ++#define ltq_pci_cfg_r32(x) ltq_r32(ltq_pci_cfgbase + (x)) + + struct ltq_pci_gpio_map { + int pin; +@@ -273,7 +273,7 @@ static int __devinit ltq_pci_probe(struct platform_device *pdev) + pci_probe_only = 0; + ltq_pci_irq_map = ltq_pci_data->irq; + ltq_pci_membase = ioremap_nocache(PCI_CR_BASE_ADDR, PCI_CR_SIZE); +- ltq_pci_mapped_cfg = ++ ltq_pci_cfgbase = + ioremap_nocache(LTQ_PCI_CFG_BASE, LTQ_PCI_CFG_BASE); + ltq_pci_controller.io_map_base = + (unsigned long)ioremap(LTQ_PCI_IO_BASE, LTQ_PCI_IO_SIZE - 1); +diff --git a/arch/mips/pci/pci-lantiq.h b/arch/mips/pci/pci-lantiq.h +index 66bf6cd..c4721b4 100644 +--- a/arch/mips/pci/pci-lantiq.h ++++ b/arch/mips/pci/pci-lantiq.h +@@ -9,7 +9,7 @@ + #ifndef _LTQ_PCI_H__ + #define _LTQ_PCI_H__ + +-extern __iomem void *ltq_pci_mapped_cfg; ++extern __iomem void *ltq_pci_cfgbase; + extern int ltq_pci_read_config_dword(struct pci_bus *bus, + unsigned int devfn, int where, int size, u32 *val); + extern int ltq_pci_write_config_dword(struct pci_bus *bus, +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0049-MIPS-lantiq-pci-give-xway-pci-support-its-own-kbuild.patch b/target/linux/lantiq/patches-3.3/0049-MIPS-lantiq-pci-give-xway-pci-support-its-own-kbuild.patch new file mode 100644 index 0000000000..5401436b02 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0049-MIPS-lantiq-pci-give-xway-pci-support-its-own-kbuild.patch @@ -0,0 +1,45 @@ +From e5ae2eed68a7ccd406bde12ecfc12c88a52aeb06 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 13:13:31 +0100 +Subject: [PATCH 49/70] MIPS: lantiq: pci: give xway pci support its own + kbuild symbol + +--- + arch/mips/lantiq/Kconfig | 5 +++++ + arch/mips/pci/Makefile | 2 +- + 2 files changed, 6 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig +index cb6b39f..dde9fc6 100644 +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -19,8 +19,13 @@ config SOC_XWAY + + config SOC_FALCON + bool "FALCON" ++ + endchoice + ++config PCI_LANTIQ ++ bool "PCI Support" ++ depends on SOC_XWAY && PCI ++ + source "arch/mips/lantiq/xway/Kconfig" + source "arch/mips/lantiq/falcon/Kconfig" + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index c3ac4b0..1f07a58 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -41,7 +41,7 @@ obj-$(CONFIG_SIBYTE_SB1250) += fixup-sb1250.o pci-sb1250.o + obj-$(CONFIG_SIBYTE_BCM112X) += fixup-sb1250.o pci-sb1250.o + obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o + obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o +-obj-$(CONFIG_SOC_XWAY) += pci-lantiq.o ops-lantiq.o ++obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0050-MIPS-lantiq-pci-move-pcibios-code-into-fixup-lantiq..patch b/target/linux/lantiq/patches-3.3/0050-MIPS-lantiq-pci-move-pcibios-code-into-fixup-lantiq..patch new file mode 100644 index 0000000000..08be0c57da --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0050-MIPS-lantiq-pci-move-pcibios-code-into-fixup-lantiq..patch @@ -0,0 +1,131 @@ +From a4f83d3f635603b982f11e7f1cb22057958ff52b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 15:53:10 +0100 +Subject: [PATCH 50/70] MIPS: lantiq: pci: move pcibios code into + fixup-lantiq.c + +--- + arch/mips/pci/Makefile | 1 + + arch/mips/pci/fixup-lantiq.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + arch/mips/pci/pci-lantiq.c | 24 ++---------------------- + 3 files changed, 45 insertions(+), 22 deletions(-) + create mode 100644 arch/mips/pci/fixup-lantiq.c + +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index 1f07a58..499a019 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -41,6 +41,7 @@ obj-$(CONFIG_SIBYTE_SB1250) += fixup-sb1250.o pci-sb1250.o + obj-$(CONFIG_SIBYTE_BCM112X) += fixup-sb1250.o pci-sb1250.o + obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o + obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o ++obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o +diff --git a/arch/mips/pci/fixup-lantiq.c b/arch/mips/pci/fixup-lantiq.c +new file mode 100644 +index 0000000..daf5ae9 +--- /dev/null ++++ b/arch/mips/pci/fixup-lantiq.c +@@ -0,0 +1,42 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License version 2 as published ++ * by the Free Software Foundation. ++ * ++ * Copyright (C) 2012 John Crispin ++ */ ++ ++#include ++#include ++ ++int (*ltqpci_map_irq)(const struct pci_dev *dev, u8 slot, u8 pin) = NULL; ++int (*ltqpci_plat_arch_init)(struct pci_dev *dev) = NULL; ++int (*ltqpci_plat_dev_init)(struct pci_dev *dev) = NULL; ++int *ltq_pci_irq_map; ++ ++int pcibios_plat_dev_init(struct pci_dev *dev) ++{ ++ if (ltqpci_plat_arch_init) ++ return ltqpci_plat_arch_init(dev); ++ ++ if (ltqpci_plat_dev_init) ++ return ltqpci_plat_dev_init(dev); ++ ++ return 0; ++} ++ ++int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ if (ltqpci_map_irq) ++ return ltqpci_map_irq(dev, slot, pin); ++ if (ltq_pci_irq_map[slot]) { ++ dev_info(&dev->dev, "SLOT:%d PIN:%d IRQ:%d\n", slot, pin, ltq_pci_irq_map[slot]); ++ return ltq_pci_irq_map[slot]; ++ } ++ printk(KERN_ERR "lq_pci: trying to map irq for unknown slot %d\n", ++ slot); ++ ++ return 0; ++} ++ ++ +diff --git a/arch/mips/pci/pci-lantiq.c b/arch/mips/pci/pci-lantiq.c +index efcdd45..7a29738 100644 +--- a/arch/mips/pci/pci-lantiq.c ++++ b/arch/mips/pci/pci-lantiq.c +@@ -93,16 +93,14 @@ static struct ltq_pci_gpio_map ltq_pci_gpio_map[] = { + { 37, 2, 0, "pci-req4" }, + }; + +-__iomem void *ltq_pci_mapped_cfg; ++__iomem void *ltq_pci_cfgbase; + static __iomem void *ltq_pci_membase; + +-int (*ltqpci_plat_dev_init)(struct pci_dev *dev) = NULL; +- + /* Since the PCI REQ pins can be reused for other functionality, make it + possible to exclude those from interpretation by the PCI controller */ + static int ltq_pci_req_mask = 0xf; + +-static int *ltq_pci_irq_map; ++extern int *ltq_pci_irq_map; + + struct pci_ops ltq_pci_ops = { + .read = ltq_pci_read_config_dword, +@@ -131,14 +129,6 @@ static struct pci_controller ltq_pci_controller = { + .io_offset = 0x00000000UL, + }; + +-int pcibios_plat_dev_init(struct pci_dev *dev) +-{ +- if (ltqpci_plat_dev_init) +- return ltqpci_plat_dev_init(dev); +- +- return 0; +-} +- + static u32 ltq_calc_bar11mask(void) + { + u32 mem, bar11mask; +@@ -256,16 +246,6 @@ static int __devinit ltq_pci_startup(struct device *dev) + return 0; + } + +-int __init pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) +-{ +- if (ltq_pci_irq_map[slot]) +- return ltq_pci_irq_map[slot]; +- printk(KERN_ERR "lq_pci: trying to map irq for unknown slot %d\n", +- slot); +- +- return 0; +-} +- + static int __devinit ltq_pci_probe(struct platform_device *pdev) + { + struct ltq_pci_data *ltq_pci_data = +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0051-MIPS-lantiq-pcie-add-pcie-driver.patch b/target/linux/lantiq/patches-3.3/0051-MIPS-lantiq-pcie-add-pcie-driver.patch new file mode 100644 index 0000000000..0d5e503a7b --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0051-MIPS-lantiq-pcie-add-pcie-driver.patch @@ -0,0 +1,3503 @@ +From 5497be6394beeac27c136b254dcdfd0d741289de Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 8 Mar 2012 15:57:33 +0100 +Subject: [PATCH 51/70] MIPS: lantiq: pcie: add pcie driver + +--- + arch/mips/Kconfig | 1 + + arch/mips/lantiq/Kconfig | 4 - + arch/mips/lantiq/xway/Kconfig | 21 + + arch/mips/pci/Makefile | 2 + + arch/mips/pci/fixup-lantiq-pcie.c | 81 +++ + arch/mips/pci/pci.c | 25 + + arch/mips/pci/pcie-lantiq-msi.c | 399 +++++++++++ + arch/mips/pci/pcie-lantiq-phy.c | 408 ++++++++++++ + arch/mips/pci/pcie-lantiq.c | 1146 ++++++++++++++++++++++++++++++++ + arch/mips/pci/pcie-lantiq.h | 1305 +++++++++++++++++++++++++++++++++++++ + 10 files changed, 3388 insertions(+), 4 deletions(-) + create mode 100644 arch/mips/pci/fixup-lantiq-pcie.c + create mode 100644 arch/mips/pci/pcie-lantiq-msi.c + create mode 100644 arch/mips/pci/pcie-lantiq-phy.c + create mode 100644 arch/mips/pci/pcie-lantiq.c + create mode 100644 arch/mips/pci/pcie-lantiq.h + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index df4e125..0dd8ae3 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -2369,6 +2369,7 @@ config PCI_DOMAINS + bool + + source "drivers/pci/Kconfig" ++source "drivers/pci/pcie/Kconfig" + + # + # ISA support is now enabled via select. Too many systems still have the one +diff --git a/arch/mips/lantiq/Kconfig b/arch/mips/lantiq/Kconfig +index dde9fc6..d21d9d4 100644 +--- a/arch/mips/lantiq/Kconfig ++++ b/arch/mips/lantiq/Kconfig +@@ -22,10 +22,6 @@ config SOC_FALCON + + endchoice + +-config PCI_LANTIQ +- bool "PCI Support" +- depends on SOC_XWAY && PCI +- + source "arch/mips/lantiq/xway/Kconfig" + source "arch/mips/lantiq/falcon/Kconfig" + +diff --git a/arch/mips/lantiq/xway/Kconfig b/arch/mips/lantiq/xway/Kconfig +index 2b857de..54a51ff 100644 +--- a/arch/mips/lantiq/xway/Kconfig ++++ b/arch/mips/lantiq/xway/Kconfig +@@ -8,6 +8,27 @@ config LANTIQ_MACH_EASY50712 + + endmenu + ++choice ++ prompt "PCI" ++ default PCI_LANTIQ_NONE ++ ++config PCI_LANTIQ_NONE ++ bool "None" ++ ++config PCI_LANTIQ ++ bool "PCI Support" ++ depends on PCI ++ ++config PCIE_LANTIQ ++ bool "PCIE Support" ++ select ARCH_SUPPORTS_MSI ++ ++endchoice ++ ++config PCIE_LANTIQ_MSI ++ bool ++ depends on PCIE_LANTIQ && PCI_MSI ++ default y + endif + + if SOC_AMAZON_SE +diff --git a/arch/mips/pci/Makefile b/arch/mips/pci/Makefile +index 499a019..31e70c5 100644 +--- a/arch/mips/pci/Makefile ++++ b/arch/mips/pci/Makefile +@@ -43,6 +43,8 @@ obj-$(CONFIG_SIBYTE_BCM1x80) += pci-bcm1480.o pci-bcm1480ht.o + obj-$(CONFIG_SNI_RM) += fixup-sni.o ops-sni.o + obj-$(CONFIG_LANTIQ) += fixup-lantiq.o + obj-$(CONFIG_PCI_LANTIQ) += pci-lantiq.o ops-lantiq.o ++obj-$(CONFIG_PCIE_LANTIQ) += pcie-lantiq-phy.o pcie-lantiq.o fixup-lantiq-pcie.o ++obj-$(CONFIG_PCIE_LANTIQ_MSI) += pcie-lantiq-msi.o + obj-$(CONFIG_TANBAC_TB0219) += fixup-tb0219.o + obj-$(CONFIG_TANBAC_TB0226) += fixup-tb0226.o + obj-$(CONFIG_TANBAC_TB0287) += fixup-tb0287.o +diff --git a/arch/mips/pci/fixup-lantiq-pcie.c b/arch/mips/pci/fixup-lantiq-pcie.c +new file mode 100644 +index 0000000..84517df +--- /dev/null ++++ b/arch/mips/pci/fixup-lantiq-pcie.c +@@ -0,0 +1,81 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_fixup_pcie.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \file ifxmips_fixup_pcie.c ++ \ingroup IFX_PCIE ++ \brief PCIe Fixup functions source file ++*/ ++#include ++#include ++#include ++ ++#include ++ ++#include "pcie-lantiq.h" ++ ++#define PCI_VENDOR_ID_INFINEON 0x15D1 ++#define PCI_DEVICE_ID_INFINEON_DANUBE 0x000F ++#define PCI_DEVICE_ID_INFINEON_PCIE 0x0011 ++#define PCI_VENDOR_ID_LANTIQ 0x1BEF ++#define PCI_DEVICE_ID_LANTIQ_PCIE 0x0011 ++ ++ ++ ++static void __devinit ++ifx_pcie_fixup_resource(struct pci_dev *dev) ++{ ++ u32 reg; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev)); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s: fixup host controller %s (%04x:%04x)\n", ++ __func__, pci_name(dev), dev->vendor, dev->device); ++ ++ /* Setup COMMAND register */ ++ reg = PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER /* | ++ PCI_COMMAND_INTX_DISABLE */| PCI_COMMAND_SERR; ++ pci_write_config_word(dev, PCI_COMMAND, reg); ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev)); ++} ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE, ifx_pcie_fixup_resource); ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_VENDOR_ID_LANTIQ, ifx_pcie_fixup_resource); ++ ++static void __devinit ++ifx_pcie_rc_class_early_fixup(struct pci_dev *dev) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: enter\n", __func__, pci_name(dev)); ++ ++ if (dev->devfn == PCI_DEVFN(0, 0) && ++ (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { ++ ++ dev->class = (PCI_CLASS_BRIDGE_PCI << 8) | (dev->class & 0xff); ++ ++ printk(KERN_INFO "%s: fixed pcie host bridge to pci-pci bridge\n", __func__); ++ } ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s: exit\n", __func__, pci_name(dev)); ++} ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INFINEON, PCI_DEVICE_ID_INFINEON_PCIE, ++ ifx_pcie_rc_class_early_fixup); ++ ++DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LANTIQ, PCI_DEVICE_ID_LANTIQ_PCIE, ++ ifx_pcie_rc_class_early_fixup); +diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c +index 1552150..cd034a9 100644 +--- a/arch/mips/pci/pci.c ++++ b/arch/mips/pci/pci.c +@@ -201,6 +201,31 @@ static int __init pcibios_init(void) + + subsys_initcall(pcibios_init); + ++int pcibios_host_nr(void) ++{ ++ int count; ++ struct pci_controller *hose; ++ for (count = 0, hose = hose_head; hose; hose = hose->next, count++) { ++ ; ++ } ++ return count; ++} ++EXPORT_SYMBOL(pcibios_host_nr); ++ ++int pcibios_1st_host_bus_nr(void) ++{ ++ int bus_nr = 0; ++ struct pci_controller *hose = hose_head; ++ ++ if (hose != NULL) { ++ if (hose->bus != NULL) { ++ bus_nr = hose->bus->subordinate + 1; ++ } ++ } ++ return bus_nr; ++} ++EXPORT_SYMBOL(pcibios_1st_host_bus_nr); ++ + static int pcibios_enable_resources(struct pci_dev *dev, int mask) + { + u16 cmd, old_cmd; +diff --git a/arch/mips/pci/pcie-lantiq-msi.c b/arch/mips/pci/pcie-lantiq-msi.c +new file mode 100644 +index 0000000..9cbf639 +--- /dev/null ++++ b/arch/mips/pci/pcie-lantiq-msi.c +@@ -0,0 +1,399 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_msi.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCI MSI sub module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe MSI Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Date $Author $Comment ++** 02 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \defgroup IFX_PCIE_MSI MSI OS APIs ++ \ingroup IFX_PCIE ++ \brief PCIe bus driver OS interface functions ++*/ ++ ++/*! ++ \file ifxmips_pcie_msi.c ++ \ingroup IFX_PCIE ++ \brief PCIe MSI OS interface file ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "pcie-lantiq.h" ++ ++#define IFX_MSI_IRQ_NUM 16 ++#define SM(_v, _f) (((_v) << _f##_S) & (_f)) ++ ++#define IFX_MSI_PIC_REG_BASE (KSEG1 | 0x1F700000) ++#define IFX_PCIE_MSI_IR0 (INT_NUM_IM4_IRL0 + 27) ++#define IFX_PCIE_MSI_IR1 (INT_NUM_IM4_IRL0 + 28) ++#define IFX_PCIE_MSI_IR2 (INT_NUM_IM4_IRL0 + 29) ++#define IFX_PCIE_MSI_IR3 (INT_NUM_IM0_IRL0 + 30) ++ ++#define IFX_MSI_PCI_INT_DISABLE 0x80000000 ++#define IFX_MSI_PIC_INT_LINE 0x30000000 ++#define IFX_MSI_PIC_MSG_ADDR 0x0FFF0000 ++#define IFX_MSI_PIC_MSG_DATA 0x0000FFFF ++#define IFX_MSI_PIC_BIG_ENDIAN 1 ++#define IFX_MSI_PIC_INT_LINE_S 28 ++#define IFX_MSI_PIC_MSG_ADDR_S 16 ++#define IFX_MSI_PIC_MSG_DATA_S 0x0 ++ ++enum { ++ IFX_PCIE_MSI_IDX0 = 0, ++ IFX_PCIE_MSI_IDX1, ++ IFX_PCIE_MSI_IDX2, ++ IFX_PCIE_MSI_IDX3, ++}; ++ ++typedef struct ifx_msi_irq_idx { ++ const int irq; ++ const int idx; ++}ifx_msi_irq_idx_t; ++ ++struct ifx_msi_pic { ++ volatile u32 pic_table[IFX_MSI_IRQ_NUM]; ++ volatile u32 pic_endian; /* 0x40 */ ++}; ++typedef struct ifx_msi_pic *ifx_msi_pic_t; ++ ++typedef struct ifx_msi_irq { ++ const volatile ifx_msi_pic_t msi_pic_p; ++ const u32 msi_phy_base; ++ const ifx_msi_irq_idx_t msi_irq_idx[IFX_MSI_IRQ_NUM]; ++ /* ++ * Each bit in msi_free_irq_bitmask represents a MSI interrupt that is ++ * in use. ++ */ ++ u16 msi_free_irq_bitmask; ++ ++ /* ++ * Each bit in msi_multiple_irq_bitmask tells that the device using ++ * this bit in msi_free_irq_bitmask is also using the next bit. This ++ * is used so we can disable all of the MSI interrupts when a device ++ * uses multiple. ++ */ ++ u16 msi_multiple_irq_bitmask; ++}ifx_msi_irq_t; ++ ++static ifx_msi_irq_t msi_irqs[IFX_PCIE_CORE_NR] = { ++ { ++ .msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI_PIC_REG_BASE, ++ .msi_phy_base = PCIE_MSI_PHY_BASE, ++ .msi_irq_idx = { ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ }, ++ .msi_free_irq_bitmask = 0, ++ .msi_multiple_irq_bitmask= 0, ++ }, ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ { ++ .msi_pic_p = (const volatile ifx_msi_pic_t)IFX_MSI1_PIC_REG_BASE, ++ .msi_phy_base = PCIE1_MSI_PHY_BASE, ++ .msi_irq_idx = { ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ {IFX_PCIE1_MSI_IR0, IFX_PCIE_MSI_IDX0}, {IFX_PCIE1_MSI_IR1, IFX_PCIE_MSI_IDX1}, ++ {IFX_PCIE1_MSI_IR2, IFX_PCIE_MSI_IDX2}, {IFX_PCIE1_MSI_IR3, IFX_PCIE_MSI_IDX3}, ++ }, ++ .msi_free_irq_bitmask = 0, ++ .msi_multiple_irq_bitmask= 0, ++ ++ }, ++#endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++}; ++ ++/* ++ * This lock controls updates to msi_free_irq_bitmask, ++ * msi_multiple_irq_bitmask and pic register settting ++ */ ++static DEFINE_SPINLOCK(ifx_pcie_msi_lock); ++ ++void pcie_msi_pic_init(int pcie_port) ++{ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_pic_p->pic_endian = IFX_MSI_PIC_BIG_ENDIAN; ++ spin_unlock(&ifx_pcie_msi_lock); ++} ++ ++/** ++ * \fn int arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) ++ * \brief Called when a driver request MSI interrupts instead of the ++ * legacy INT A-D. This routine will allocate multiple interrupts ++ * for MSI devices that support them. A device can override this by ++ * programming the MSI control bits [6:4] before calling ++ * pci_enable_msi(). ++ * ++ * \param[in] pdev Device requesting MSI interrupts ++ * \param[in] desc MSI descriptor ++ * ++ * \return -EINVAL Invalid pcie root port or invalid msi bit ++ * \return 0 OK ++ * \ingroup IFX_PCIE_MSI ++ */ ++int ++arch_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *desc) ++{ ++ int irq, pos; ++ u16 control; ++ int irq_idx; ++ int irq_step; ++ int configured_private_bits; ++ int request_private_bits; ++ struct msi_msg msg; ++ u16 search_mask; ++ struct ifx_pci_controller *ctrl = pdev->bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s %s enter\n", __func__, pci_name(pdev)); ++ ++ /* XXX, skip RC MSI itself */ ++ if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) { ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s RC itself doesn't use MSI interrupt\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* ++ * Read the MSI config to figure out how many IRQs this device ++ * wants. Most devices only want 1, which will give ++ * configured_private_bits and request_private_bits equal 0. ++ */ ++ pci_read_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, &control); ++ ++ /* ++ * If the number of private bits has been configured then use ++ * that value instead of the requested number. This gives the ++ * driver the chance to override the number of interrupts ++ * before calling pci_enable_msi(). ++ */ ++ configured_private_bits = (control & PCI_MSI_FLAGS_QSIZE) >> 4; ++ if (configured_private_bits == 0) { ++ /* Nothing is configured, so use the hardware requested size */ ++ request_private_bits = (control & PCI_MSI_FLAGS_QMASK) >> 1; ++ } ++ else { ++ /* ++ * Use the number of configured bits, assuming the ++ * driver wanted to override the hardware request ++ * value. ++ */ ++ request_private_bits = configured_private_bits; ++ } ++ ++ /* ++ * The PCI 2.3 spec mandates that there are at most 32 ++ * interrupts. If this device asks for more, only give it one. ++ */ ++ if (request_private_bits > 5) { ++ request_private_bits = 0; ++ } ++again: ++ /* ++ * The IRQs have to be aligned on a power of two based on the ++ * number being requested. ++ */ ++ irq_step = (1 << request_private_bits); ++ ++ /* Mask with one bit for each IRQ */ ++ search_mask = (1 << irq_step) - 1; ++ ++ /* ++ * We're going to search msi_free_irq_bitmask_lock for zero ++ * bits. This represents an MSI interrupt number that isn't in ++ * use. ++ */ ++ spin_lock(&ifx_pcie_msi_lock); ++ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos += irq_step) { ++ if ((msi_irqs[pcie_port].msi_free_irq_bitmask & (search_mask << pos)) == 0) { ++ msi_irqs[pcie_port].msi_free_irq_bitmask |= search_mask << pos; ++ msi_irqs[pcie_port].msi_multiple_irq_bitmask |= (search_mask >> 1) << pos; ++ break; ++ } ++ } ++ spin_unlock(&ifx_pcie_msi_lock); ++ ++ /* Make sure the search for available interrupts didn't fail */ ++ if (pos >= IFX_MSI_IRQ_NUM) { ++ if (request_private_bits) { ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s: Unable to find %d free " ++ "interrupts, trying just one", __func__, 1 << request_private_bits); ++ request_private_bits = 0; ++ goto again; ++ } ++ else { ++ printk(KERN_ERR "%s: Unable to find a free MSI interrupt\n", __func__); ++ return -EINVAL; ++ } ++ } ++ irq = msi_irqs[pcie_port].msi_irq_idx[pos].irq; ++ irq_idx = msi_irqs[pcie_port].msi_irq_idx[pos].idx; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "pos %d, irq %d irq_idx %d\n", pos, irq, irq_idx); ++ ++ /* ++ * Initialize MSI. This has to match the memory-write endianess from the device ++ * Address bits [23:12] ++ */ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] = SM(irq_idx, IFX_MSI_PIC_INT_LINE) | ++ SM((msi_irqs[pcie_port].msi_phy_base >> 12), IFX_MSI_PIC_MSG_ADDR) | ++ SM((1 << pos), IFX_MSI_PIC_MSG_DATA); ++ ++ /* Enable this entry */ ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~IFX_MSI_PCI_INT_DISABLE; ++ spin_unlock(&ifx_pcie_msi_lock); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "pic_table[%d]: 0x%08x\n", ++ pos, msi_irqs[pcie_port].msi_pic_p->pic_table[pos]); ++ ++ /* Update the number of IRQs the device has available to it */ ++ control &= ~PCI_MSI_FLAGS_QSIZE; ++ control |= (request_private_bits << 4); ++ pci_write_config_word(pdev, desc->msi_attrib.pos + PCI_MSI_FLAGS, control); ++ ++ irq_set_msi_desc(irq, desc); ++ msg.address_hi = 0x0; ++ msg.address_lo = msi_irqs[pcie_port].msi_phy_base; ++ msg.data = SM((1 << pos), IFX_MSI_PIC_MSG_DATA); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "msi_data: pos %d 0x%08x\n", pos, msg.data); ++ ++ write_msi_msg(irq, &msg); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__); ++ return 0; ++} ++ ++static int ++pcie_msi_irq_to_port(unsigned int irq, int *port) ++{ ++ int ret = 0; ++ ++ if (irq == IFX_PCIE_MSI_IR0 || irq == IFX_PCIE_MSI_IR1 || ++ irq == IFX_PCIE_MSI_IR2 || irq == IFX_PCIE_MSI_IR3) { ++ *port = IFX_PCIE_PORT0; ++ } ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++ else if (irq == IFX_PCIE1_MSI_IR0 || irq == IFX_PCIE1_MSI_IR1 || ++ irq == IFX_PCIE1_MSI_IR2 || irq == IFX_PCIE1_MSI_IR3) { ++ *port = IFX_PCIE_PORT1; ++ } ++#endif /* CONFIG_IFX_PCIE_2ND_CORE */ ++ else { ++ printk(KERN_ERR "%s: Attempted to teardown illegal " ++ "MSI interrupt (%d)\n", __func__, irq); ++ ret = -EINVAL; ++ } ++ return ret; ++} ++ ++/** ++ * \fn void arch_teardown_msi_irq(unsigned int irq) ++ * \brief Called when a device no longer needs its MSI interrupts. All ++ * MSI interrupts for the device are freed. ++ * ++ * \param irq The devices first irq number. There may be multple in sequence. ++ * \return none ++ * \ingroup IFX_PCIE_MSI ++ */ ++void ++arch_teardown_msi_irq(unsigned int irq) ++{ ++ int pos; ++ int number_irqs; ++ u16 bitmask; ++ int pcie_port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s enter\n", __func__); ++ ++ BUG_ON(irq > (INT_NUM_IM4_IRL0 + 31)); ++ ++ if (pcie_msi_irq_to_port(irq, &pcie_port) != 0) { ++ return; ++ } ++ ++ /* Shift the mask to the correct bit location, not always correct ++ * Probally, the first match will be chosen. ++ */ ++ for (pos = 0; pos < IFX_MSI_IRQ_NUM; pos++) { ++ if ((msi_irqs[pcie_port].msi_irq_idx[pos].irq == irq) ++ && (msi_irqs[pcie_port].msi_free_irq_bitmask & ( 1 << pos))) { ++ break; ++ } ++ } ++ if (pos >= IFX_MSI_IRQ_NUM) { ++ printk(KERN_ERR "%s: Unable to find a matched MSI interrupt\n", __func__); ++ return; ++ } ++ spin_lock(&ifx_pcie_msi_lock); ++ /* Disable this entry */ ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] |= IFX_MSI_PCI_INT_DISABLE; ++ msi_irqs[pcie_port].msi_pic_p->pic_table[pos] &= ~(IFX_MSI_PIC_INT_LINE | IFX_MSI_PIC_MSG_ADDR | IFX_MSI_PIC_MSG_DATA); ++ spin_unlock(&ifx_pcie_msi_lock); ++ /* ++ * Count the number of IRQs we need to free by looking at the ++ * msi_multiple_irq_bitmask. Each bit set means that the next ++ * IRQ is also owned by this device. ++ */ ++ number_irqs = 0; ++ while (((pos + number_irqs) < IFX_MSI_IRQ_NUM) && ++ (msi_irqs[pcie_port].msi_multiple_irq_bitmask & (1 << (pos + number_irqs)))) { ++ number_irqs++; ++ } ++ number_irqs++; ++ ++ /* Mask with one bit for each IRQ */ ++ bitmask = (1 << number_irqs) - 1; ++ ++ bitmask <<= pos; ++ if ((msi_irqs[pcie_port].msi_free_irq_bitmask & bitmask) != bitmask) { ++ printk(KERN_ERR "%s: Attempted to teardown MSI " ++ "interrupt (%d) not in use\n", __func__, irq); ++ return; ++ } ++ /* Checks are done, update the in use bitmask */ ++ spin_lock(&ifx_pcie_msi_lock); ++ msi_irqs[pcie_port].msi_free_irq_bitmask &= ~bitmask; ++ msi_irqs[pcie_port].msi_multiple_irq_bitmask &= ~(bitmask >> 1); ++ spin_unlock(&ifx_pcie_msi_lock); ++ IFX_PCIE_PRINT(PCIE_MSG_MSI, "%s exit\n", __func__); ++} ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Chuanhua.Lei@infineon.com"); ++MODULE_SUPPORTED_DEVICE("Infineon PCIe IP builtin MSI PIC module"); ++MODULE_DESCRIPTION("Infineon PCIe IP builtin MSI PIC driver"); ++ +diff --git a/arch/mips/pci/pcie-lantiq-phy.c b/arch/mips/pci/pcie-lantiq-phy.c +new file mode 100644 +index 0000000..9f5027d +--- /dev/null ++++ b/arch/mips/pci/pcie-lantiq-phy.c +@@ -0,0 +1,408 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_phy.c ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe PHY sub module ++** ++** DATE : 14 May 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 14 May,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++/*! ++ \file ifxmips_pcie_phy.c ++ \ingroup IFX_PCIE ++ \brief PCIe PHY PLL register programming source file ++*/ ++#include ++#include ++#include ++#include ++ ++#include "pcie-lantiq.h" ++ ++/* PCIe PDI only supports 16 bit operation */ ++ ++#define IFX_PCIE_PHY_REG_WRITE16(__addr, __data) \ ++ ((*(volatile u16 *) (__addr)) = (__data)) ++ ++#define IFX_PCIE_PHY_REG_READ16(__addr) \ ++ (*(volatile u16 *) (__addr)) ++ ++#define IFX_PCIE_PHY_REG16(__addr) \ ++ (*(volatile u16 *) (__addr)) ++ ++#define IFX_PCIE_PHY_REG(__reg, __value, __mask) do { \ ++ u16 read_data; \ ++ u16 write_data; \ ++ read_data = IFX_PCIE_PHY_REG_READ16((__reg)); \ ++ write_data = (read_data & ((u16)~(__mask))) | (((u16)(__value)) & ((u16)(__mask)));\ ++ IFX_PCIE_PHY_REG_WRITE16((__reg), write_data); \ ++} while (0) ++ ++#define IFX_PCIE_PLL_TIMEOUT 1000 /* Tunnable */ ++ ++static void ++pcie_phy_comm_setup(int pcie_port) ++{ ++ /* PLL Setting */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF); ++ ++ /* increase the bias reference voltage */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF); ++ ++ /* Endcnt */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF); ++ ++ /* force */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF); ++ ++ /* ctrl_lim */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF); ++ ++ /* ctrl */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00); ++ ++ /* RTERM*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF); ++ ++ /* Improved 100MHz clock output */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF); ++ ++ /* Reduced CDR BW to avoid glitches */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); ++} ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++static void ++pcie_phy_36mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++ ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE ++static void ++pcie_phy_36mhz_ssc_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++ ++ /* PLL Setting */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL1(pcie_port), 0x120e, 0xFFFF); ++ ++ /* Increase the bias reference voltage */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x39D7, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x0900, 0xFFFF); ++ ++ /* Endcnt */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_EI(pcie_port), 0x0004, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_A_CTRL(pcie_port), 0x6803, 0xFFFF); ++ ++ /* Force */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0008, 0x0008); ++ ++ /* Predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL2(pcie_port), 0x0706, 0xFFFF); ++ ++ /* ctrl_lim */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL3(pcie_port), 0x1FFF, 0xFFFF); ++ ++ /* ctrl */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_A_CTRL1(pcie_port), 0x0800, 0xFF00); ++ ++ /* predrv_ser_en */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4702, 0x7F00); ++ ++ /* RTERM*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL2(pcie_port), 0x2e00, 0xFFFF); ++ ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0400, 0x0400); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0100); ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1c72, 0xFFFF); ++ ++ /* improved 100MHz clock output */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL2(pcie_port), 0x3096, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_A_CTRL2(pcie_port), 0x4707, 0xFFFF); ++ ++ /* reduced CDR BW to avoid glitches */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CDR(pcie_port), 0x0235, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_25MHZ_MODE ++static void ++pcie_phy_25mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0000, 0x0200); ++ ++ /* en_ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0002, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0040, 0x0070); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x6000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x4000, 0x4000); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_25MHZ_MODE */ ++ ++#ifdef CONFIG_IFX_PCIE_PHY_100MHZ_MODE ++static void ++pcie_phy_100mhz_mode_setup(int pcie_port) ++{ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d enter\n", __func__, pcie_port); ++ /* en_ext_mmd_div_ratio */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0002); ++ ++ /* ext_mmd_div_ratio*/ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL3(pcie_port), 0x0000, 0x0070); ++ ++ /* pll_ensdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0200, 0x0200); ++ ++ /* en_const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x0100, 0x0100); ++ ++ /* mmd */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL3(pcie_port), 0x2000, 0xe000); ++ ++ /* lf_mode */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_A_CTRL2(pcie_port), 0x0000, 0x4000); ++ ++ /* const_sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL1(pcie_port), 0x38e4, 0xFFFF); ++ ++ /* const sdm */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL2(pcie_port), 0x00ee, 0x00FF); ++ ++ /* pllmod */ ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL7(pcie_port), 0x0002, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL6(pcie_port), 0x3a04, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL5(pcie_port), 0xfae3, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_PLL_CTRL4(pcie_port), 0x1b72, 0xFFFF); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "%s pcie_port %d exit\n", __func__, pcie_port); ++} ++#endif /* CONFIG_IFX_PCIE_PHY_100MHZ_MODE */ ++ ++static int ++pcie_phy_wait_startup_ready(int pcie_port) ++{ ++ int i; ++ ++ for (i = 0; i < IFX_PCIE_PLL_TIMEOUT; i++) { ++ if ((IFX_PCIE_PHY_REG16(PCIE_PHY_PLL_STATUS(pcie_port)) & 0x0040) != 0) { ++ break; ++ } ++ udelay(10); ++ } ++ if (i >= IFX_PCIE_PLL_TIMEOUT) { ++ printk(KERN_ERR "%s PLL Link timeout\n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static void ++pcie_phy_load_enable(int pcie_port, int slice) ++{ ++ /* Set the load_en of tx/rx slice to '1' */ ++ switch (slice) { ++ case 1: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0010, 0x0010); ++ break; ++ case 2: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0010, 0x0010); ++ break; ++ case 3: ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0002, 0x0002); ++ break; ++ } ++} ++ ++static void ++pcie_phy_load_disable(int pcie_port, int slice) ++{ ++ /* set the load_en of tx/rx slice to '0' */ ++ switch (slice) { ++ case 1: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_CTRL1(pcie_port), 0x0000, 0x0010); ++ break; ++ case 2: ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_CTRL1(pcie_port), 0x0000, 0x0010); ++ break; ++ case 3: ++ IFX_PCIE_PHY_REG(PCIE_PHY_RX1_CTRL1(pcie_port), 0x0000, 0x0002); ++ break; ++ } ++} ++ ++static void pcie_phy_load_war(int pcie_port) ++{ ++ int slice; ++ ++ for (slice = 1; slice < 4; slice++) { ++ pcie_phy_load_enable(pcie_port, slice); ++ udelay(1); ++ pcie_phy_load_disable(pcie_port, slice); ++ } ++} ++ ++static void pcie_phy_tx2_modulation(int pcie_port) ++{ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD1(pcie_port), 0x1FFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD2(pcie_port), 0xFFFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0601, 0xFFFF); ++ mdelay(1); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX2_MOD3(pcie_port), 0x0001, 0xFFFF); ++} ++ ++static void pcie_phy_tx1_modulation(int pcie_port) ++{ ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD1(pcie_port), 0x1FFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD2(pcie_port), 0xFFFE, 0xFFFF); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0601, 0xFFFF); ++ mdelay(1); ++ IFX_PCIE_PHY_REG(PCIE_PHY_TX1_MOD3(pcie_port), 0x0001, 0xFFFF); ++} ++ ++static void pcie_phy_tx_modulation_war(int pcie_port) ++{ ++ int i; ++#define PCIE_PHY_MODULATION_NUM 5 ++ for (i = 0; i < PCIE_PHY_MODULATION_NUM; i++) { ++ pcie_phy_tx2_modulation(pcie_port); ++ pcie_phy_tx1_modulation(pcie_port); ++ } ++#undef PCIE_PHY_MODULATION_NUM ++} ++ ++void pcie_phy_clock_mode_setup(int pcie_port) ++{ ++ pcie_pdi_big_endian(pcie_port); ++ ++ /* Enable PDI to access PCIe PHY register */ ++ pcie_pdi_pmu_enable(pcie_port); ++ ++ /* Configure PLL and PHY clock */ ++ pcie_phy_comm_setup(pcie_port); ++ ++#ifdef CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ pcie_phy_36mhz_mode_setup(pcie_port); ++#elif defined(CONFIG_IFX_PCIE_PHY_36MHZ_SSC_MODE) ++ pcie_phy_36mhz_ssc_mode_setup(pcie_port); ++#elif defined(CONFIG_IFX_PCIE_PHY_25MHZ_MODE) ++ pcie_phy_25mhz_mode_setup(pcie_port); ++#elif defined (CONFIG_IFX_PCIE_PHY_100MHZ_MODE) ++ pcie_phy_100mhz_mode_setup(pcie_port); ++#else ++ #error "PCIE PHY Clock Mode must be chosen first!!!!" ++#endif /* CONFIG_IFX_PCIE_PHY_36MHZ_MODE */ ++ ++ /* Enable PCIe PHY and make PLL setting take effect */ ++ pcie_phy_pmu_enable(pcie_port); ++ ++ /* Check if we are in startup_ready status */ ++ pcie_phy_wait_startup_ready(pcie_port); ++ ++ pcie_phy_load_war(pcie_port); ++ ++ /* Apply TX modulation workarounds */ ++ pcie_phy_tx_modulation_war(pcie_port); ++ ++#ifdef IFX_PCI_PHY_REG_DUMP ++ IFX_PCIE_PRINT(PCIE_MSG_PHY, "Modified PHY register dump\n"); ++ pcie_phy_reg_dump(pcie_port); ++#endif ++} ++ +diff --git a/arch/mips/pci/pcie-lantiq.c b/arch/mips/pci/pcie-lantiq.c +new file mode 100644 +index 0000000..1df55b5 +--- /dev/null ++++ b/arch/mips/pci/pcie-lantiq.c +@@ -0,0 +1,1146 @@ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define CONFIG_IFX_PCIE_1ST_CORE ++ ++#include "pcie-lantiq.h" ++ ++#define IFX_PCIE_IR (INT_NUM_IM4_IRL0 + 25) ++#define IFX_PCIE_INTA (INT_NUM_IM4_IRL0 + 8) ++#define IFX_PCIE_INTB (INT_NUM_IM4_IRL0 + 9) ++#define IFX_PCIE_INTC (INT_NUM_IM4_IRL0 + 10) ++#define IFX_PCIE_INTD (INT_NUM_IM4_IRL0 + 11) ++#define MS(_v, _f) (((_v) & (_f)) >> _f##_S) ++#define SM(_v, _f) (((_v) << _f##_S) & (_f)) ++#define IFX_REG_SET_BIT(_f, _r) \ ++ IFX_REG_W32((IFX_REG_R32((_r)) &~ (_f)) | (_f), (_r)) ++#define IFX_PCIE_LTSSM_ENABLE_TIMEOUT 10 ++#define IFX_PCIE_PHY_LINK_UP_TIMEOUT 1000 ++#define IFX_PCIE_PHY_LOOP_CNT 5 ++ ++static DEFINE_SPINLOCK(ifx_pcie_lock); ++ ++int pcibios_1st_host_bus_nr(void); ++ ++unsigned int g_pcie_debug_flag = PCIE_MSG_ANY & (~PCIE_MSG_CFG); ++ ++static ifx_pcie_irq_t pcie_irqs[IFX_PCIE_CORE_NR] = { ++ { ++ .ir_irq = { ++ .irq = IFX_PCIE_IR, ++ .name = "ifx_pcie_rc0", ++ }, ++ ++ .legacy_irq = { ++ { ++ .irq_bit = PCIE_IRN_INTA, ++ .irq = IFX_PCIE_INTA, ++ }, ++ { ++ .irq_bit = PCIE_IRN_INTB, ++ .irq = IFX_PCIE_INTB, ++ }, ++ { ++ .irq_bit = PCIE_IRN_INTC, ++ .irq = IFX_PCIE_INTC, ++ }, ++ { ++ .irq_bit = PCIE_IRN_INTD, ++ .irq = IFX_PCIE_INTD, ++ }, ++ }, ++ }, ++}; ++ ++static inline int pcie_ltssm_enable(int pcie_port) ++{ ++ int i; ++ ++ IFX_REG_W32(PCIE_RC_CCR_LTSSM_ENABLE, PCIE_RC_CCR(pcie_port)); /* Enable LTSSM */ ++ ++ /* Wait for the link to come up */ ++ for (i = 0; i < IFX_PCIE_LTSSM_ENABLE_TIMEOUT; i++) { ++ if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_RETRAIN_PENDING)) { ++ break; ++ } ++ udelay(10); ++ } ++ if (i >= IFX_PCIE_LTSSM_ENABLE_TIMEOUT) { ++ IFX_PCIE_PRINT(PCIE_MSG_INIT, "%s link timeout!!!!!\n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static inline void pcie_status_register_clear(int pcie_port) ++{ ++ IFX_REG_W32(0, PCIE_RC_DR(pcie_port)); ++ IFX_REG_W32(0, PCIE_PCICMDSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_DCTLSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_LCTLSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_SLCTLSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_RSTS(pcie_port)); ++ IFX_REG_W32(0, PCIE_UES_R(pcie_port)); ++ IFX_REG_W32(0, PCIE_UEMR(pcie_port)); ++ IFX_REG_W32(0, PCIE_UESR(pcie_port)); ++ IFX_REG_W32(0, PCIE_CESR(pcie_port)); ++ IFX_REG_W32(0, PCIE_CEMR(pcie_port)); ++ IFX_REG_W32(0, PCIE_RESR(pcie_port)); ++ IFX_REG_W32(0, PCIE_PVCCRSR(pcie_port)); ++ IFX_REG_W32(0, PCIE_VC0_RSR0(pcie_port)); ++ IFX_REG_W32(0, PCIE_TPFCS(pcie_port)); ++ IFX_REG_W32(0, PCIE_TNPFCS(pcie_port)); ++ IFX_REG_W32(0, PCIE_TCFCS(pcie_port)); ++ IFX_REG_W32(0, PCIE_QSR(pcie_port)); ++ IFX_REG_W32(0, PCIE_IOBLSECS(pcie_port)); ++} ++ ++static inline int ifx_pcie_link_up(int pcie_port) ++{ ++ return (IFX_REG_R32(PCIE_PHY_SR(pcie_port)) & PCIE_PHY_SR_PHY_LINK_UP) ? 1 : 0; ++} ++ ++static inline void pcie_mem_io_setup(int pcie_port) ++{ ++ unsigned int reg; ++ /* ++ * BAR[0:1] readonly register ++ * RC contains only minimal BARs for packets mapped to this device ++ * Mem/IO filters defines a range of memory occupied by memory mapped IO devices that ++ * reside on the downstream side fo the bridge. ++ */ ++ reg = SM((PCIE_MEM_PHY_PORT_TO_END(pcie_port) >> 20), PCIE_MBML_MEM_LIMIT_ADDR) ++ | SM((PCIE_MEM_PHY_PORT_TO_BASE(pcie_port) >> 20), PCIE_MBML_MEM_BASE_ADDR); ++ IFX_REG_W32(reg, PCIE_MBML(pcie_port)); ++ ++ /* PCIe_PBML, same as MBML */ ++ IFX_REG_W32(IFX_REG_R32(PCIE_MBML(pcie_port)), PCIE_PMBL(pcie_port)); ++ ++ /* IO Address Range */ ++ reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 12), PCIE_IOBLSECS_IO_LIMIT_ADDR) ++ | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 12), PCIE_IOBLSECS_IO_BASE_ADDR); ++ reg |= PCIE_IOBLSECS_32BIT_IO_ADDR; ++ IFX_REG_W32(reg, PCIE_IOBLSECS(pcie_port)); ++ ++ reg = SM((PCIE_IO_PHY_PORT_TO_END(pcie_port) >> 16), PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT) ++ | SM((PCIE_IO_PHY_PORT_TO_BASE(pcie_port) >> 16), PCIE_IO_BANDL_UPPER_16BIT_IO_BASE); ++ IFX_REG_W32(reg, PCIE_IO_BANDL(pcie_port)); ++} ++ ++static inline void pcie_msi_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* XXX, MSI stuff should only apply to EP */ ++ /* MSI Capability: Only enable 32-bit addresses */ ++ reg = IFX_REG_R32(PCIE_MCAPR(pcie_port)); ++ reg &= ~PCIE_MCAPR_ADDR64_CAP; ++ reg |= PCIE_MCAPR_MSI_ENABLE; ++ ++ /* Disable multiple message */ ++ reg &= ~(PCIE_MCAPR_MULTI_MSG_CAP | PCIE_MCAPR_MULTI_MSG_ENABLE); ++ IFX_REG_W32(reg, PCIE_MCAPR(pcie_port)); ++} ++ ++static inline void pcie_pm_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* Enable PME, Soft reset enabled */ ++ reg = IFX_REG_R32(PCIE_PM_CSR(pcie_port)); ++ reg |= PCIE_PM_CSR_PME_ENABLE | PCIE_PM_CSR_SW_RST; ++ IFX_REG_W32(reg, PCIE_PM_CSR(pcie_port)); ++} ++ ++static inline void pcie_bus_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ reg = SM(0, PCIE_BNR_PRIMARY_BUS_NUM) | SM(1, PCIE_PNR_SECONDARY_BUS_NUM) | SM(0xFF, PCIE_PNR_SUB_BUS_NUM); ++ IFX_REG_W32(reg, PCIE_BNR(pcie_port)); ++} ++ ++static inline void pcie_device_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* Device capability register, set up Maximum payload size */ ++ reg = IFX_REG_R32(PCIE_DCAP(pcie_port)); ++ reg |= PCIE_DCAP_ROLE_BASE_ERR_REPORT; ++ reg |= SM(PCIE_MAX_PAYLOAD_128, PCIE_DCAP_MAX_PAYLOAD_SIZE); ++ ++ /* Only available for EP */ ++ reg &= ~(PCIE_DCAP_EP_L0S_LATENCY | PCIE_DCAP_EP_L1_LATENCY); ++ IFX_REG_W32(reg, PCIE_DCAP(pcie_port)); ++ ++ /* Device control and status register */ ++ /* Set Maximum Read Request size for the device as a Requestor */ ++ reg = IFX_REG_R32(PCIE_DCTLSTS(pcie_port)); ++ ++ /* ++ * Request size can be larger than the MPS used, but the completions returned ++ * for the read will be bounded by the MPS size. ++ * In our system, Max request size depends on AHB burst size. It is 64 bytes. ++ * but we set it as 128 as minimum one. ++ */ ++ reg |= SM(PCIE_MAX_PAYLOAD_128, PCIE_DCTLSTS_MAX_READ_SIZE) ++ | SM(PCIE_MAX_PAYLOAD_128, PCIE_DCTLSTS_MAX_PAYLOAD_SIZE); ++ ++ /* Enable relaxed ordering, no snoop, and all kinds of errors */ ++ reg |= PCIE_DCTLSTS_RELAXED_ORDERING_EN | PCIE_DCTLSTS_ERR_EN | PCIE_DCTLSTS_NO_SNOOP_EN; ++ ++ IFX_REG_W32(reg, PCIE_DCTLSTS(pcie_port)); ++} ++ ++static inline void pcie_link_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* ++ * XXX, Link capability register, bit 18 for EP CLKREQ# dynamic clock management for L1, L2/3 CPM ++ * L0s is reported during link training via TS1 order set by N_FTS ++ */ ++ reg = IFX_REG_R32(PCIE_LCAP(pcie_port)); ++ reg &= ~PCIE_LCAP_L0S_EIXT_LATENCY; ++ reg |= SM(3, PCIE_LCAP_L0S_EIXT_LATENCY); ++ IFX_REG_W32(reg, PCIE_LCAP(pcie_port)); ++ ++ /* Link control and status register */ ++ reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port)); ++ ++ /* Link Enable, ASPM enabled */ ++ reg &= ~PCIE_LCTLSTS_LINK_DISABLE; ++ ++#ifdef CONFIG_PCIEASPM ++ /* ++ * We use the same physical reference clock that the platform provides on the connector ++ * It paved the way for ASPM to calculate the new exit Latency ++ */ ++ reg |= PCIE_LCTLSTS_SLOT_CLK_CFG; ++ reg |= PCIE_LCTLSTS_COM_CLK_CFG; ++ /* ++ * We should disable ASPM by default except that we have dedicated power management support ++ * Enable ASPM will cause the system hangup/instability, performance degration ++ */ ++ reg |= PCIE_LCTLSTS_ASPM_ENABLE; ++#else ++ reg &= ~PCIE_LCTLSTS_ASPM_ENABLE; ++#endif /* CONFIG_PCIEASPM */ ++ ++ /* ++ * The maximum size of any completion with data packet is bounded by the MPS setting ++ * in device control register ++ */ ++ /* RCB may cause multiple split transactions, two options available, we use 64 byte RCB */ ++ reg &= ~ PCIE_LCTLSTS_RCB128; ++ IFX_REG_W32(reg, PCIE_LCTLSTS(pcie_port)); ++} ++ ++static inline void pcie_error_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* ++ * Forward ERR_COR, ERR_NONFATAL, ERR_FATAL to the backbone ++ * Poisoned write TLPs and completions indicating poisoned TLPs will set the PCIe_PCICMDSTS.MDPE ++ */ ++ reg = IFX_REG_R32(PCIE_INTRBCTRL(pcie_port)); ++ reg |= PCIE_INTRBCTRL_SERR_ENABLE | PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE; ++ ++ IFX_REG_W32(reg, PCIE_INTRBCTRL(pcie_port)); ++ ++ /* Uncorrectable Error Mask Register, Unmask all bits in PCIE_UESR */ ++ reg = IFX_REG_R32(PCIE_UEMR(pcie_port)); ++ reg &= ~PCIE_ALL_UNCORRECTABLE_ERR; ++ IFX_REG_W32(reg, PCIE_UEMR(pcie_port)); ++ ++ /* Uncorrectable Error Severity Register, ALL errors are FATAL */ ++ IFX_REG_W32(PCIE_ALL_UNCORRECTABLE_ERR, PCIE_UESR(pcie_port)); ++ ++ /* Correctable Error Mask Register, unmask all bits */ ++ reg = IFX_REG_R32(PCIE_CEMR(pcie_port)); ++ reg &= ~PCIE_CORRECTABLE_ERR; ++ IFX_REG_W32(reg, PCIE_CEMR(pcie_port)); ++ ++ /* Advanced Error Capabilities and Control Registr */ ++ reg = IFX_REG_R32(PCIE_AECCR(pcie_port)); ++ reg |= PCIE_AECCR_ECRC_CHECK_EN | PCIE_AECCR_ECRC_GEN_EN; ++ IFX_REG_W32(reg, PCIE_AECCR(pcie_port)); ++ ++ /* Root Error Command Register, Report all types of errors */ ++ reg = IFX_REG_R32(PCIE_RECR(pcie_port)); ++ reg |= PCIE_RECR_ERR_REPORT_EN; ++ IFX_REG_W32(reg, PCIE_RECR(pcie_port)); ++ ++ /* Clear the Root status register */ ++ reg = IFX_REG_R32(PCIE_RESR(pcie_port)); ++ IFX_REG_W32(reg, PCIE_RESR(pcie_port)); ++} ++ ++static inline void pcie_root_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* Root control and capabilities register */ ++ reg = IFX_REG_R32(PCIE_RCTLCAP(pcie_port)); ++ reg |= PCIE_RCTLCAP_SERR_ENABLE | PCIE_RCTLCAP_PME_INT_EN; ++ IFX_REG_W32(reg, PCIE_RCTLCAP(pcie_port)); ++} ++ ++static inline void pcie_vc_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* Port VC Capability Register 2 */ ++ reg = IFX_REG_R32(PCIE_PVC2(pcie_port)); ++ reg &= ~PCIE_PVC2_VC_ARB_WRR; ++ reg |= PCIE_PVC2_VC_ARB_16P_FIXED_WRR; ++ IFX_REG_W32(reg, PCIE_PVC2(pcie_port)); ++ ++ /* VC0 Resource Capability Register */ ++ reg = IFX_REG_R32(PCIE_VC0_RC(pcie_port)); ++ reg &= ~PCIE_VC0_RC_REJECT_SNOOP; ++ IFX_REG_W32(reg, PCIE_VC0_RC(pcie_port)); ++} ++ ++static inline void pcie_port_logic_setup(int pcie_port) ++{ ++ unsigned int reg; ++ ++ /* FTS number, default 12, increase to 63, may increase time from/to L0s to L0 */ ++ reg = IFX_REG_R32(PCIE_AFR(pcie_port)); ++ reg &= ~(PCIE_AFR_FTS_NUM | PCIE_AFR_COM_FTS_NUM); ++ reg |= SM(PCIE_AFR_FTS_NUM_DEFAULT, PCIE_AFR_FTS_NUM) ++ | SM(PCIE_AFR_FTS_NUM_DEFAULT, PCIE_AFR_COM_FTS_NUM); ++ /* L0s and L1 entry latency */ ++ reg &= ~(PCIE_AFR_L0S_ENTRY_LATENCY | PCIE_AFR_L1_ENTRY_LATENCY); ++ reg |= SM(PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT, PCIE_AFR_L0S_ENTRY_LATENCY) ++ | SM(PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT, PCIE_AFR_L1_ENTRY_LATENCY); ++ IFX_REG_W32(reg, PCIE_AFR(pcie_port)); ++ ++ /* Port Link Control Register */ ++ reg = IFX_REG_R32(PCIE_PLCR(pcie_port)); ++ reg |= PCIE_PLCR_DLL_LINK_EN; /* Enable the DLL link */ ++ IFX_REG_W32(reg, PCIE_PLCR(pcie_port)); ++ ++ /* Lane Skew Register */ ++ reg = IFX_REG_R32(PCIE_LSR(pcie_port)); ++ /* Enable ACK/NACK and FC */ ++ reg &= ~(PCIE_LSR_ACKNAK_DISABLE | PCIE_LSR_FC_DISABLE); ++ IFX_REG_W32(reg, PCIE_LSR(pcie_port)); ++ ++ /* Symbol Timer Register and Filter Mask Register 1 */ ++ reg = IFX_REG_R32(PCIE_STRFMR(pcie_port)); ++ ++ /* Default SKP interval is very accurate already, 5us */ ++ /* Enable IO/CFG transaction */ ++ reg |= PCIE_STRFMR_RX_CFG_TRANS_ENABLE | PCIE_STRFMR_RX_IO_TRANS_ENABLE; ++ /* Disable FC WDT */ ++ reg &= ~PCIE_STRFMR_FC_WDT_DISABLE; ++ IFX_REG_W32(reg, PCIE_STRFMR(pcie_port)); ++ ++ /* Filter Masker Register 2 */ ++ reg = IFX_REG_R32(PCIE_FMR2(pcie_port)); ++ reg |= PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 | PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1; ++ IFX_REG_W32(reg, PCIE_FMR2(pcie_port)); ++ ++ /* VC0 Completion Receive Queue Control Register */ ++ reg = IFX_REG_R32(PCIE_VC0_CRQCR(pcie_port)); ++ reg &= ~PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE; ++ reg |= SM(PCIE_VC0_TLP_QUEUE_MODE_BYPASS, PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE); ++ IFX_REG_W32(reg, PCIE_VC0_CRQCR(pcie_port)); ++} ++ ++static inline void pcie_rc_cfg_reg_setup(int pcie_port) ++{ ++ /* diable ltssm */ ++ IFX_REG_W32(0, PCIE_RC_CCR(pcie_port)); ++ ++ pcie_mem_io_setup(pcie_port); ++ pcie_msi_setup(pcie_port); ++ pcie_pm_setup(pcie_port); ++ pcie_bus_setup(pcie_port); ++ pcie_device_setup(pcie_port); ++ pcie_link_setup(pcie_port); ++ pcie_error_setup(pcie_port); ++ pcie_root_setup(pcie_port); ++ pcie_vc_setup(pcie_port); ++ pcie_port_logic_setup(pcie_port); ++} ++ ++static int ifx_pcie_wait_phy_link_up(int pcie_port) ++{ ++ int i; ++ ++ /* Wait for PHY link is up */ ++ for (i = 0; i < IFX_PCIE_PHY_LINK_UP_TIMEOUT; i++) { ++ if (ifx_pcie_link_up(pcie_port)) { ++ break; ++ } ++ udelay(100); ++ } ++ if (i >= IFX_PCIE_PHY_LINK_UP_TIMEOUT) { ++ printk(KERN_ERR "%s timeout\n", __func__); ++ return -1; ++ } ++ ++ /* Check data link up or not */ ++ if (!(IFX_REG_R32(PCIE_RC_DR(pcie_port)) & PCIE_RC_DR_DLL_UP)) { ++ printk(KERN_ERR "%s DLL link is still down\n", __func__); ++ return -1; ++ } ++ ++ /* Check Data link active or not */ ++ if (!(IFX_REG_R32(PCIE_LCTLSTS(pcie_port)) & PCIE_LCTLSTS_DLL_ACTIVE)) { ++ printk(KERN_ERR "%s DLL is not active\n", __func__); ++ return -1; ++ } ++ return 0; ++} ++ ++static inline int pcie_app_loigc_setup(int pcie_port) ++{ ++ IFX_REG_W32(PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS, PCIE_AHB_CTRL(pcie_port)); ++ ++ /* Pull PCIe EP out of reset */ ++ pcie_device_rst_deassert(pcie_port); ++ ++ /* Start LTSSM training between RC and EP */ ++ pcie_ltssm_enable(pcie_port); ++ ++ /* Check PHY status after enabling LTSSM */ ++ if (ifx_pcie_wait_phy_link_up(pcie_port) != 0) { ++ return -1; ++ } ++ return 0; ++} ++ ++/* ++ * Must be done after ltssm due to based on negotiated link ++ * width and payload size ++ * Update the Replay Time Limit. Empirically, some PCIe ++ * devices take a little longer to respond than expected under ++ * load. As a workaround for this we configure the Replay Time ++ * Limit to the value expected for a 512 byte MPS instead of ++ * our actual 128 byte MPS. The numbers below are directly ++ * from the PCIe spec table 3-4/5. ++ */ ++static inline void pcie_replay_time_update(int pcie_port) ++{ ++ unsigned int reg; ++ int nlw; ++ int rtl; ++ ++ reg = IFX_REG_R32(PCIE_LCTLSTS(pcie_port)); ++ ++ nlw = MS(reg, PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH); ++ switch (nlw) { ++ case PCIE_MAX_LENGTH_WIDTH_X1: ++ rtl = 1677; ++ break; ++ case PCIE_MAX_LENGTH_WIDTH_X2: ++ rtl = 867; ++ break; ++ case PCIE_MAX_LENGTH_WIDTH_X4: ++ rtl = 462; ++ break; ++ case PCIE_MAX_LENGTH_WIDTH_X8: ++ rtl = 258; ++ break; ++ default: ++ rtl = 1677; ++ break; ++ } ++ reg = IFX_REG_R32(PCIE_ALTRT(pcie_port)); ++ reg &= ~PCIE_ALTRT_REPLAY_TIME_LIMIT; ++ reg |= SM(rtl, PCIE_ALTRT_REPLAY_TIME_LIMIT); ++ IFX_REG_W32(reg, PCIE_ALTRT(pcie_port)); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_REG, "%s PCIE_ALTRT 0x%08x\n", ++ __func__, IFX_REG_R32(PCIE_ALTRT(pcie_port))); ++} ++ ++/* ++ * Table 359 Enhanced Configuration Address Mapping1) ++ * 1) This table is defined in Table 7-1, page 341, PCI Express Base Specification v1.1 ++ * Memory Address PCI Express Configuration Space ++ * A[(20+n-1):20] Bus Number 1 < n < 8 ++ * A[19:15] Device Number ++ * A[14:12] Function Number ++ * A[11:8] Extended Register Number ++ * A[7:2] Register Number ++ * A[1:0] Along with size of the access, used to generate Byte Enables ++ * For VR9, only the address bits [22:0] are mapped to the configuration space: ++ * . Address bits [22:20] select the target bus (1-of-8)1) ++ * . Address bits [19:15] select the target device (1-of-32) on the bus ++ * . Address bits [14:12] select the target function (1-of-8) within the device. ++ * . Address bits [11:2] selects the target dword (1-of-1024) within the selected function.s configuration space ++ * . Address bits [1:0] define the start byte location within the selected dword. ++ */ ++static inline unsigned int pcie_bus_addr(u8 bus_num, u16 devfn, int where) ++{ ++ unsigned int addr; ++ u8 bus; ++ ++ if (!bus_num) { ++ /* type 0 */ ++ addr = ((PCI_SLOT(devfn) & 0x1F) << 15) | ((PCI_FUNC(devfn) & 0x7) << 12) | ((where & 0xFFF)& ~3); ++ } else { ++ bus = bus_num; ++ /* type 1, only support 8 buses */ ++ addr = ((bus & 0x7) << 20) | ((PCI_SLOT(devfn) & 0x1F) << 15) | ++ ((PCI_FUNC(devfn) & 0x7) << 12) | ((where & 0xFFF) & ~3); ++ } ++ IFX_PCIE_PRINT(PCIE_MSG_CFG, "%s: bus addr : %02x:%02x.%01x/%02x, addr=%08x\n", ++ __func__, bus_num, PCI_SLOT(devfn), PCI_FUNC(devfn), where, addr); ++ return addr; ++} ++ ++static int pcie_valid_config(int pcie_port, int bus, int dev) ++{ ++ /* RC itself */ ++ if ((bus == 0) && (dev == 0)) ++ return 1; ++ ++ /* No physical link */ ++ if (!ifx_pcie_link_up(pcie_port)) ++ return 0; ++ ++ /* Bus zero only has RC itself ++ * XXX, check if EP will be integrated ++ */ ++ if ((bus == 0) && (dev != 0)) ++ return 0; ++ ++ /* Maximum 8 buses supported for VRX */ ++ if (bus > 9) ++ return 0; ++ ++ /* ++ * PCIe is PtP link, one bus only supports only one device ++ * except bus zero and PCIe switch which is virtual bus device ++ * The following two conditions really depends on the system design ++ * and attached the device. ++ * XXX, how about more new switch ++ */ ++ if ((bus == 1) && (dev != 0)) ++ return 0; ++ ++ if ((bus >= 3) && (dev != 0)) ++ return 0; ++ return 1; ++} ++ ++static inline unsigned int ifx_pcie_cfg_rd(int pcie_port, unsigned int reg) ++{ ++ return IFX_REG_R32((volatile unsigned int *)(PCIE_CFG_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++static inline void ifx_pcie_cfg_wr(int pcie_port, unsigned int reg, unsigned int val) ++{ ++ IFX_REG_W32( val, (volatile unsigned int *)(PCIE_CFG_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++static inline unsigned int ifx_pcie_rc_cfg_rd(int pcie_port, unsigned int reg) ++{ ++ return IFX_REG_R32((volatile unsigned int *)(PCIE_RC_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++static inline void ifx_pcie_rc_cfg_wr(int pcie_port, unsigned int reg, unsigned int val) ++{ ++ IFX_REG_W32(val, (volatile unsigned int *)(PCIE_RC_PORT_TO_BASE(pcie_port) + reg)); ++} ++ ++unsigned int ifx_pcie_bus_enum_read_hack(int where, unsigned int value) ++{ ++ unsigned int tvalue = value; ++ ++ if (where == PCI_PRIMARY_BUS) { ++ u8 primary, secondary, subordinate; ++ ++ primary = tvalue & 0xFF; ++ secondary = (tvalue >> 8) & 0xFF; ++ subordinate = (tvalue >> 16) & 0xFF; ++ primary += pcibios_1st_host_bus_nr(); ++ secondary += pcibios_1st_host_bus_nr(); ++ subordinate += pcibios_1st_host_bus_nr(); ++ tvalue = (tvalue & 0xFF000000) | (unsigned int)primary | (unsigned int)(secondary << 8) | (unsigned int)(subordinate << 16); ++ } ++ return tvalue; ++} ++ ++unsigned int ifx_pcie_bus_enum_write_hack(int where, unsigned int value) ++{ ++ unsigned int tvalue = value; ++ ++ if (where == PCI_PRIMARY_BUS) { ++ u8 primary, secondary, subordinate; ++ ++ primary = tvalue & 0xFF; ++ secondary = (tvalue >> 8) & 0xFF; ++ subordinate = (tvalue >> 16) & 0xFF; ++ if (primary > 0 && primary != 0xFF) ++ primary -= pcibios_1st_host_bus_nr(); ++ if (secondary > 0 && secondary != 0xFF) ++ secondary -= pcibios_1st_host_bus_nr(); ++ if (subordinate > 0 && subordinate != 0xFF) ++ subordinate -= pcibios_1st_host_bus_nr(); ++ tvalue = (tvalue & 0xFF000000) | (unsigned int)primary | (unsigned int)(secondary << 8) | (unsigned int)(subordinate << 16); ++ } else if (where == PCI_SUBORDINATE_BUS) { ++ u8 subordinate = tvalue & 0xFF; ++ subordinate = subordinate > 0 ? subordinate - pcibios_1st_host_bus_nr() : 0; ++ tvalue = subordinate; ++ } ++ return tvalue; ++} ++ ++/** ++ * \fn static int ifx_pcie_read_config(struct pci_bus *bus, unsigned int devfn, ++ * int where, int size, unsigned int *value) ++ * \brief Read a value from configuration space ++ * ++ * \param[in] bus Pointer to pci bus ++ * \param[in] devfn PCI device function number ++ * \param[in] where PCI register number ++ * \param[in] size Register read size ++ * \param[out] value Pointer to return value ++ * \return PCIBIOS_BAD_REGISTER_NUMBER Invalid register number ++ * \return PCIBIOS_FUNC_NOT_SUPPORTED PCI function not supported ++ * \return PCIBIOS_DEVICE_NOT_FOUND PCI device not found ++ * \return PCIBIOS_SUCCESSFUL OK ++ * \ingroup IFX_PCIE_OS ++ */ ++static int ifx_pcie_read_config(struct pci_bus *bus, unsigned int devfn, int where, int size, unsigned int *value) ++{ ++ unsigned int data = 0; ++ int bus_number = bus->number; ++ static const unsigned int mask[8] = {0, 0xff, 0xffff, 0, 0xffffffff, 0, 0, 0}; ++ int ret = PCIBIOS_SUCCESSFUL; ++ struct ifx_pci_controller *ctrl = bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ if (unlikely(size != 1 && size != 2 && size != 4)){ ++ ret = PCIBIOS_BAD_REGISTER_NUMBER; ++ goto out; ++ } ++ ++ /* Make sure the address is aligned to natural boundary */ ++ if (unlikely(((size - 1) & where))) { ++ ret = PCIBIOS_BAD_REGISTER_NUMBER; ++ goto out; ++ } ++ ++ /* ++ * If we are second controller, we have to cheat OS so that it assume ++ * its bus number starts from 0 in host controller ++ */ ++ bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port); ++ ++ /* ++ * We need to force the bus number to be zero on the root ++ * bus. Linux numbers the 2nd root bus to start after all ++ * busses on root 0. ++ */ ++ if (bus->parent == NULL) ++ bus_number = 0; ++ ++ /* ++ * PCIe only has a single device connected to it. It is ++ * always device ID 0. Don't bother doing reads for other ++ * device IDs on the first segment. ++ */ ++ if ((bus_number == 0) && (PCI_SLOT(devfn) != 0)) { ++ ret = PCIBIOS_FUNC_NOT_SUPPORTED; ++ goto out; ++ } ++ ++ if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) { ++ *value = 0xffffffff; ++ ret = PCIBIOS_DEVICE_NOT_FOUND; ++ goto out; ++ } ++ ++ IFX_PCIE_PRINT(PCIE_MSG_READ_CFG, "%s: %02x:%02x.%01x/%02x:%01d\n", __func__, bus_number, ++ PCI_SLOT(devfn), PCI_FUNC(devfn), where, size); ++ ++ PCIE_IRQ_LOCK(ifx_pcie_lock); ++ if (bus_number == 0) { /* RC itself */ ++ unsigned int t; ++ ++ t = (where & ~3); ++ data = ifx_pcie_rc_cfg_rd(pcie_port, t); ++ IFX_PCIE_PRINT(PCIE_MSG_READ_CFG, "%s: rd local cfg, offset:%08x, data:%08x\n", ++ __func__, t, data); ++ } else { ++ unsigned int addr = pcie_bus_addr(bus_number, devfn, where); ++ ++ data = ifx_pcie_cfg_rd(pcie_port, addr); ++ if (pcie_port == IFX_PCIE_PORT0) { ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ data = le32_to_cpu(data); ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } else { ++#ifdef CONFIG_IFX_PCIE1_HW_SWAP ++ data = le32_to_cpu(data); ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } ++ } ++ /* To get a correct PCI topology, we have to restore the bus number to OS */ ++ data = ifx_pcie_bus_enum_hack(bus, devfn, where, data, pcie_port, 1); ++ ++ PCIE_IRQ_UNLOCK(ifx_pcie_lock); ++ IFX_PCIE_PRINT(PCIE_MSG_READ_CFG, "%s: read config: data=%08x raw=%08x\n", ++ __func__, (data >> (8 * (where & 3))) & mask[size & 7], data); ++ ++ *value = (data >> (8 * (where & 3))) & mask[size & 7]; ++out: ++ return ret; ++} ++ ++static unsigned int ifx_pcie_size_to_value(int where, int size, unsigned int data, unsigned int value) ++{ ++ unsigned int shift; ++ unsigned int tdata = data; ++ ++ switch (size) { ++ case 1: ++ shift = (where & 0x3) << 3; ++ tdata &= ~(0xffU << shift); ++ tdata |= ((value & 0xffU) << shift); ++ break; ++ case 2: ++ shift = (where & 3) << 3; ++ tdata &= ~(0xffffU << shift); ++ tdata |= ((value & 0xffffU) << shift); ++ break; ++ case 4: ++ tdata = value; ++ break; ++ } ++ return tdata; ++} ++ ++/** ++ * \fn static static int ifx_pcie_write_config(struct pci_bus *bus, unsigned int devfn, ++ * int where, int size, unsigned int value) ++ * \brief Write a value to PCI configuration space ++ * ++ * \param[in] bus Pointer to pci bus ++ * \param[in] devfn PCI device function number ++ * \param[in] where PCI register number ++ * \param[in] size The register size to be written ++ * \param[in] value The valule to be written ++ * \return PCIBIOS_BAD_REGISTER_NUMBER Invalid register number ++ * \return PCIBIOS_DEVICE_NOT_FOUND PCI device not found ++ * \return PCIBIOS_SUCCESSFUL OK ++ * \ingroup IFX_PCIE_OS ++ */ ++static int ifx_pcie_write_config(struct pci_bus *bus, unsigned int devfn, int where, int size, unsigned int value) ++{ ++ int bus_number = bus->number; ++ int ret = PCIBIOS_SUCCESSFUL; ++ struct ifx_pci_controller *ctrl = bus->sysdata; ++ int pcie_port = ctrl->port; ++ unsigned int tvalue = value; ++ unsigned int data; ++ ++ /* Make sure the address is aligned to natural boundary */ ++ if (unlikely(((size - 1) & where))) { ++ ret = PCIBIOS_BAD_REGISTER_NUMBER; ++ goto out; ++ } ++ /* ++ * If we are second controller, we have to cheat OS so that it assume ++ * its bus number starts from 0 in host controller ++ */ ++ bus_number = ifx_pcie_bus_nr_deduct(bus_number, pcie_port); ++ ++ /* ++ * We need to force the bus number to be zero on the root ++ * bus. Linux numbers the 2nd root bus to start after all ++ * busses on root 0. ++ */ ++ if (bus->parent == NULL) ++ bus_number = 0; ++ ++ if (pcie_valid_config(pcie_port, bus_number, PCI_SLOT(devfn)) == 0) { ++ ret = PCIBIOS_DEVICE_NOT_FOUND; ++ goto out; ++ } ++ ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG, "%s: %02x:%02x.%01x/%02x:%01d value=%08x\n", __func__, ++ bus_number, PCI_SLOT(devfn), PCI_FUNC(devfn), where, size, value); ++ ++ /* XXX, some PCIe device may need some delay */ ++ PCIE_IRQ_LOCK(ifx_pcie_lock); ++ ++ /* ++ * To configure the correct bus topology using native way, we have to cheat Os so that ++ * it can configure the PCIe hardware correctly. ++ */ ++ tvalue = ifx_pcie_bus_enum_hack(bus, devfn, where, value, pcie_port, 0); ++ ++ if (bus_number == 0) { /* RC itself */ ++ unsigned int t; ++ ++ t = (where & ~3); ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: wr local cfg, offset:%08x, fill:%08x\n", __func__, t, value); ++ data = ifx_pcie_rc_cfg_rd(pcie_port, t); ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: rd local cfg, offset:%08x, data:%08x\n", __func__, t, data); ++ ++ data = ifx_pcie_size_to_value(where, size, data, tvalue); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: wr local cfg, offset:%08x, value:%08x\n", __func__, t, data); ++ ifx_pcie_rc_cfg_wr(pcie_port, t, data); ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: rd local cfg, offset:%08x, value:%08x\n", ++ __func__, t, ifx_pcie_rc_cfg_rd(pcie_port, t)); ++ } else { ++ unsigned int addr = pcie_bus_addr(bus_number, devfn, where); ++ ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: wr cfg, offset:%08x, fill:%08x\n", __func__, addr, value); ++ data = ifx_pcie_cfg_rd(pcie_port, addr); ++ if (pcie_port == IFX_PCIE_PORT0) { ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ data = le32_to_cpu(data); ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } else { ++#ifdef CONFIG_IFX_PCIE1_HW_SWAP ++ data = le32_to_cpu(data); ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG,"%s: rd cfg, offset:%08x, data:%08x\n", __func__, addr, data); ++ ++ data = ifx_pcie_size_to_value(where, size, data, tvalue); ++ if (pcie_port == IFX_PCIE_PORT0) { ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ data = cpu_to_le32(data); ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } else { ++#ifdef CONFIG_IFX_PCIE1_HW_SWAP ++ data = cpu_to_le32(data); ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ } ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG, "%s: wr cfg, offset:%08x, value:%08x\n", __func__, addr, data); ++ ifx_pcie_cfg_wr(pcie_port, addr, data); ++ IFX_PCIE_PRINT(PCIE_MSG_WRITE_CFG, "%s: rd cfg, offset:%08x, value:%08x\n", ++ __func__, addr, ifx_pcie_cfg_rd(pcie_port, addr)); ++ } ++ PCIE_IRQ_UNLOCK(ifx_pcie_lock); ++out: ++ return ret; ++} ++ ++static struct resource ifx_pcie_io_resource = { ++ .name = "PCIe0 I/O space", ++ .start = PCIE_IO_PHY_BASE, ++ .end = PCIE_IO_PHY_END, ++ .flags = IORESOURCE_IO, ++}; ++ ++static struct resource ifx_pcie_mem_resource = { ++ .name = "PCIe0 Memory space", ++ .start = PCIE_MEM_PHY_BASE, ++ .end = PCIE_MEM_PHY_END, ++ .flags = IORESOURCE_MEM, ++}; ++ ++static struct pci_ops ifx_pcie_ops = { ++ .read = ifx_pcie_read_config, ++ .write = ifx_pcie_write_config, ++}; ++ ++static struct ifx_pci_controller ifx_pcie_controller[IFX_PCIE_CORE_NR] = { ++ { ++ .pcic = { ++ .pci_ops = &ifx_pcie_ops, ++ .mem_resource = &ifx_pcie_mem_resource, ++ .io_resource = &ifx_pcie_io_resource, ++ }, ++ .port = IFX_PCIE_PORT0, ++ }, ++}; ++ ++static inline void pcie_core_int_clear_all(int pcie_port) ++{ ++ unsigned int reg; ++ reg = IFX_REG_R32(PCIE_IRNCR(pcie_port)); ++ reg &= PCIE_RC_CORE_COMBINED_INT; ++ IFX_REG_W32(reg, PCIE_IRNCR(pcie_port)); ++} ++ ++static irqreturn_t pcie_rc_core_isr(int irq, void *dev_id) ++{ ++ struct ifx_pci_controller *ctrl = (struct ifx_pci_controller *)dev_id; ++ int pcie_port = ctrl->port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_ISR, "PCIe RC error intr %d\n", irq); ++ pcie_core_int_clear_all(pcie_port); ++ return IRQ_HANDLED; ++} ++ ++static int pcie_rc_core_int_init(int pcie_port) ++{ ++ int ret; ++ ++ /* Enable core interrupt */ ++ IFX_REG_SET_BIT(PCIE_RC_CORE_COMBINED_INT, PCIE_IRNEN(pcie_port)); ++ ++ /* Clear it first */ ++ IFX_REG_SET_BIT(PCIE_RC_CORE_COMBINED_INT, PCIE_IRNCR(pcie_port)); ++ ret = request_irq(pcie_irqs[pcie_port].ir_irq.irq, pcie_rc_core_isr, IRQF_DISABLED, ++ pcie_irqs[pcie_port].ir_irq.name, &ifx_pcie_controller[pcie_port]); ++ if (ret) ++ printk(KERN_ERR "%s request irq %d failed\n", __func__, IFX_PCIE_IR); ++ ++ return ret; ++} ++ ++int ifx_pcie_bios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) ++{ ++ unsigned int irq_bit = 0; ++ int irq = 0; ++ struct ifx_pci_controller *ctrl = dev->bus->sysdata; ++ int pcie_port = ctrl->port; ++ ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s port %d dev %s slot %d pin %d \n", __func__, pcie_port, pci_name(dev), slot, pin); ++ ++ if ((pin == PCIE_LEGACY_DISABLE) || (pin > PCIE_LEGACY_INT_MAX)) { ++ printk(KERN_WARNING "WARNING: dev %s: invalid interrupt pin %d\n", pci_name(dev), pin); ++ return -1; ++ } ++ /* Pin index so minus one */ ++ irq_bit = pcie_irqs[pcie_port].legacy_irq[pin - 1].irq_bit; ++ irq = pcie_irqs[pcie_port].legacy_irq[pin - 1].irq; ++ IFX_REG_SET_BIT(irq_bit, PCIE_IRNEN(pcie_port)); ++ IFX_REG_SET_BIT(irq_bit, PCIE_IRNCR(pcie_port)); ++ IFX_PCIE_PRINT(PCIE_MSG_FIXUP, "%s dev %s irq %d assigned\n", __func__, pci_name(dev), irq); ++ return irq; ++} ++ ++/** ++ * \fn int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) ++ * \brief Called to perform platform specific PCI setup ++ * ++ * \param[in] dev The Linux PCI device structure for the device to map ++ * \return OK ++ * \ingroup IFX_PCIE_OS ++ */ ++int ifx_pcie_bios_plat_dev_init(struct pci_dev *dev) ++{ ++ u16 config; ++ unsigned int dconfig; ++ int pos; ++ /* Enable reporting System errors and parity errors on all devices */ ++ /* Enable parity checking and error reporting */ ++ pci_read_config_word(dev, PCI_COMMAND, &config); ++ config |= PCI_COMMAND_PARITY | PCI_COMMAND_SERR /*| PCI_COMMAND_INVALIDATE | ++ PCI_COMMAND_FAST_BACK*/; ++ pci_write_config_word(dev, PCI_COMMAND, config); ++ ++ if (dev->subordinate) { ++ /* Set latency timers on sub bridges */ ++ pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x40); /* XXX, */ ++ /* More bridge error detection */ ++ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &config); ++ config |= PCI_BRIDGE_CTL_PARITY | PCI_BRIDGE_CTL_SERR; ++ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, config); ++ } ++ /* Enable the PCIe normal error reporting */ ++ pos = pci_find_capability(dev, PCI_CAP_ID_EXP); ++ if (pos) { ++ /* Disable system error generation in response to error messages */ ++ pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &config); ++ config &= ~(PCI_EXP_RTCTL_SECEE | PCI_EXP_RTCTL_SENFEE | PCI_EXP_RTCTL_SEFEE); ++ pci_write_config_word(dev, pos + PCI_EXP_RTCTL, config); ++ ++ /* Clear PCIE Capability's Device Status */ ++ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &config); ++ pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, config); ++ ++ /* Update Device Control */ ++ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &config); ++ /* Correctable Error Reporting */ ++ config |= PCI_EXP_DEVCTL_CERE; ++ /* Non-Fatal Error Reporting */ ++ config |= PCI_EXP_DEVCTL_NFERE; ++ /* Fatal Error Reporting */ ++ config |= PCI_EXP_DEVCTL_FERE; ++ /* Unsupported Request */ ++ config |= PCI_EXP_DEVCTL_URRE; ++ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, config); ++ } ++ ++ /* Find the Advanced Error Reporting capability */ ++ pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); ++ if (pos) { ++ /* Clear Uncorrectable Error Status */ ++ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &dconfig); ++ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, dconfig); ++ /* Enable reporting of all uncorrectable errors */ ++ /* Uncorrectable Error Mask - turned on bits disable errors */ ++ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, 0); ++ /* ++ * Leave severity at HW default. This only controls if ++ * errors are reported as uncorrectable or ++ * correctable, not if the error is reported. ++ */ ++ /* PCI_ERR_UNCOR_SEVER - Uncorrectable Error Severity */ ++ /* Clear Correctable Error Status */ ++ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &dconfig); ++ pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, dconfig); ++ /* Enable reporting of all correctable errors */ ++ /* Correctable Error Mask - turned on bits disable errors */ ++ pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, 0); ++ /* Advanced Error Capabilities */ ++ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &dconfig); ++ /* ECRC Generation Enable */ ++ if (dconfig & PCI_ERR_CAP_ECRC_GENC) ++ dconfig |= PCI_ERR_CAP_ECRC_GENE; ++ /* ECRC Check Enable */ ++ if (dconfig & PCI_ERR_CAP_ECRC_CHKC) ++ dconfig |= PCI_ERR_CAP_ECRC_CHKE; ++ pci_write_config_dword(dev, pos + PCI_ERR_CAP, dconfig); ++ ++ /* PCI_ERR_HEADER_LOG - Header Log Register (16 bytes) */ ++ /* Enable Root Port's interrupt in response to error messages */ ++ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, ++ PCI_ERR_ROOT_CMD_COR_EN | ++ PCI_ERR_ROOT_CMD_NONFATAL_EN | ++ PCI_ERR_ROOT_CMD_FATAL_EN); ++ /* Clear the Root status register */ ++ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &dconfig); ++ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, dconfig); ++ } ++ /* WAR, only 128 MRRS is supported, force all EPs to support this value */ ++ pcie_set_readrq(dev, 128); ++ return 0; ++} ++ ++static void pcie_phy_rst(int pcie_port) ++{ ++ pcie_phy_rst_assert(pcie_port); ++ pcie_phy_rst_deassert(pcie_port); ++ /* Make sure PHY PLL is stable */ ++ udelay(20); ++} ++ ++static int pcie_rc_initialize(int pcie_port) ++{ ++ int i; ++ ++ pcie_rcu_endian_setup(pcie_port); ++ ++ pcie_ep_gpio_rst_init(pcie_port); ++ ++ /* ++ * XXX, PCIe elastic buffer bug will cause not to be detected. One more ++ * reset PCIe PHY will solve this issue ++ */ ++ for (i = 0; i < IFX_PCIE_PHY_LOOP_CNT; i++) { ++ /* Disable PCIe PHY Analog part for sanity check */ ++ pcie_phy_pmu_disable(pcie_port); ++ pcie_phy_rst(pcie_port); ++ /* PCIe Core reset enabled, low active, sw programmed */ ++ pcie_core_rst_assert(pcie_port); ++ /* Put PCIe EP in reset status */ ++ pcie_device_rst_assert(pcie_port); ++ /* PCI PHY & Core reset disabled, high active, sw programmed */ ++ pcie_core_rst_deassert(pcie_port); ++ /* Already in a quiet state, program PLL, enable PHY, check ready bit */ ++ pcie_phy_clock_mode_setup(pcie_port); ++ /* Enable PCIe PHY and Clock */ ++ pcie_core_pmu_setup(pcie_port); ++ /* Clear status registers */ ++ pcie_status_register_clear(pcie_port); ++#ifdef CONFIG_PCI_MSI ++ pcie_msi_init(pcie_port); ++#endif /* CONFIG_PCI_MSI */ ++ pcie_rc_cfg_reg_setup(pcie_port); ++ ++ /* Once link is up, break out */ ++ if (pcie_app_loigc_setup(pcie_port) == 0) ++ break; ++ } ++ if (i >= IFX_PCIE_PHY_LOOP_CNT) { ++ printk(KERN_ERR "%s link up failed!!!!!\n", __func__); ++ return -EIO; ++ } ++ /* NB, don't increase ACK/NACK timer timeout value, which will cause a lot of COR errors */ ++ pcie_replay_time_update(pcie_port); ++ return 0; ++} ++ ++static int inline ifx_pcie_startup_port_nr(void) ++{ ++ int pcie_port = IFX_PCIE_PORT0; ++ ++ pcie_port = IFX_PCIE_PORT0; ++ return pcie_port; ++} ++ ++/** ++ * \fn static int __init ifx_pcie_bios_init(void) ++ * \brief Initialize the IFX PCIe controllers ++ * ++ * \return -EIO PCIe PHY link is not up ++ * \return -ENOMEM Configuration/IO space failed to map ++ * \return 0 OK ++ * \ingroup IFX_PCIE_OS ++ */ ++extern int (*ltqpci_plat_arch_init)(struct pci_dev *dev); ++extern int (*ltqpci_map_irq)(const struct pci_dev *dev, u8 slot, u8 pin); ++static int __devinit ltq_pcie_probe(struct platform_device *pdev) ++{ ++ char ver_str[128] = {0}; ++ void __iomem *io_map_base; ++ int pcie_port; ++ int startup_port; ++ ltqpci_map_irq = ifx_pcie_bios_map_irq; ++ ltqpci_plat_arch_init = ifx_pcie_bios_plat_dev_init; ++ /* Enable AHB Master/ Slave */ ++ pcie_ahb_pmu_setup(); ++ ++ startup_port = ifx_pcie_startup_port_nr(); ++ ++ ltq_gpio_request(&pdev->dev, IFX_PCIE_GPIO_RESET, 0, 1, "pcie-reset"); ++ ++ for (pcie_port = startup_port; pcie_port < IFX_PCIE_CORE_NR; pcie_port++){ ++ if (pcie_rc_initialize(pcie_port) == 0) { ++ /* Otherwise, warning will pop up */ ++ io_map_base = ioremap(PCIE_IO_PHY_PORT_TO_BASE(pcie_port), PCIE_IO_SIZE); ++ if (io_map_base == NULL) ++ return -ENOMEM; ++ ifx_pcie_controller[pcie_port].pcic.io_map_base = (unsigned long)io_map_base; ++ register_pci_controller(&ifx_pcie_controller[pcie_port].pcic); ++ /* XXX, clear error status */ ++ pcie_rc_core_int_init(pcie_port); ++ } ++ } ++ ++ printk(KERN_INFO "%s", ver_str); ++return 0; ++} ++ ++static struct platform_driver ltq_pcie_driver = { ++ .probe = ltq_pcie_probe, ++ .driver = { ++ .name = "pcie-xway", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++int __init pciebios_init(void) ++{ ++ return platform_driver_register(<q_pcie_driver); ++} ++ ++arch_initcall(pciebios_init); +diff --git a/arch/mips/pci/pcie-lantiq.h b/arch/mips/pci/pcie-lantiq.h +new file mode 100644 +index 0000000..d877c23 +--- /dev/null ++++ b/arch/mips/pci/pcie-lantiq.h +@@ -0,0 +1,1305 @@ ++/****************************************************************************** ++** ++** FILE NAME : ifxmips_pcie_reg.h ++** PROJECT : IFX UEIP for VRX200 ++** MODULES : PCIe module ++** ++** DATE : 02 Mar 2009 ++** AUTHOR : Lei Chuanhua ++** DESCRIPTION : PCIe Root Complex Driver ++** COPYRIGHT : Copyright (c) 2009 ++** Infineon Technologies AG ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** ++** This program is free software; you can redistribute it and/or modify ++** it under the terms of the GNU General Public License as published by ++** the Free Software Foundation; either version 2 of the License, or ++** (at your option) any later version. ++** HISTORY ++** $Version $Date $Author $Comment ++** 0.0.1 17 Mar,2009 Lei Chuanhua Initial version ++*******************************************************************************/ ++#ifndef IFXMIPS_PCIE_REG_H ++#define IFXMIPS_PCIE_REG_H ++#include ++#include ++#include ++#include ++/*! ++ \file ifxmips_pcie_reg.h ++ \ingroup IFX_PCIE ++ \brief header file for PCIe module register definition ++*/ ++/* PCIe Address Mapping Base */ ++#define PCIE_CFG_PHY_BASE 0x1D000000UL ++#define PCIE_CFG_BASE (KSEG1 + PCIE_CFG_PHY_BASE) ++#define PCIE_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE_MEM_PHY_BASE 0x1C000000UL ++#define PCIE_MEM_BASE (KSEG1 + PCIE_MEM_PHY_BASE) ++#define PCIE_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE_MEM_PHY_END (PCIE_MEM_PHY_BASE + PCIE_MEM_SIZE - 1) ++ ++#define PCIE_IO_PHY_BASE 0x1D800000UL ++#define PCIE_IO_BASE (KSEG1 + PCIE_IO_PHY_BASE) ++#define PCIE_IO_SIZE (1 * 1024 * 1024) ++#define PCIE_IO_PHY_END (PCIE_IO_PHY_BASE + PCIE_IO_SIZE - 1) ++ ++#define PCIE_RC_CFG_BASE (KSEG1 + 0x1D900000) ++#define PCIE_APP_LOGIC_REG (KSEG1 + 0x1E100900) ++#define PCIE_MSI_PHY_BASE 0x1F600000UL ++ ++#define PCIE_PDI_PHY_BASE 0x1F106800UL ++#define PCIE_PDI_BASE (KSEG1 + PCIE_PDI_PHY_BASE) ++#define PCIE_PDI_SIZE 0x400 ++ ++#define PCIE1_CFG_PHY_BASE 0x19000000UL ++#define PCIE1_CFG_BASE (KSEG1 + PCIE1_CFG_PHY_BASE) ++#define PCIE1_CFG_SIZE (8 * 1024 * 1024) ++ ++#define PCIE1_MEM_PHY_BASE 0x18000000UL ++#define PCIE1_MEM_BASE (KSEG1 + PCIE1_MEM_PHY_BASE) ++#define PCIE1_MEM_SIZE (16 * 1024 * 1024) ++#define PCIE1_MEM_PHY_END (PCIE1_MEM_PHY_BASE + PCIE1_MEM_SIZE - 1) ++ ++#define PCIE1_IO_PHY_BASE 0x19800000UL ++#define PCIE1_IO_BASE (KSEG1 + PCIE1_IO_PHY_BASE) ++#define PCIE1_IO_SIZE (1 * 1024 * 1024) ++#define PCIE1_IO_PHY_END (PCIE1_IO_PHY_BASE + PCIE1_IO_SIZE - 1) ++ ++#define PCIE1_RC_CFG_BASE (KSEG1 + 0x19900000) ++#define PCIE1_APP_LOGIC_REG (KSEG1 + 0x1E100700) ++#define PCIE1_MSI_PHY_BASE 0x1F400000UL ++ ++#define PCIE1_PDI_PHY_BASE 0x1F700400UL ++#define PCIE1_PDI_BASE (KSEG1 + PCIE1_PDI_PHY_BASE) ++#define PCIE1_PDI_SIZE 0x400 ++ ++#define PCIE_CFG_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_CFG_BASE) : (PCIE_CFG_BASE)) ++#define PCIE_MEM_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_BASE) : (PCIE_MEM_BASE)) ++#define PCIE_IO_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_BASE) : (PCIE_IO_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_MEM_PHY_BASE) : (PCIE_MEM_PHY_BASE)) ++#define PCIE_MEM_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_MEM_PHY_END) : (PCIE_MEM_PHY_END)) ++#define PCIE_IO_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_IO_PHY_BASE) : (PCIE_IO_PHY_BASE)) ++#define PCIE_IO_PHY_PORT_TO_END(X) ((X) > 0 ? (PCIE1_IO_PHY_END) : (PCIE_IO_PHY_END)) ++#define PCIE_APP_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_APP_LOGIC_REG) : (PCIE_APP_LOGIC_REG)) ++#define PCIE_RC_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_RC_CFG_BASE) : (PCIE_RC_CFG_BASE)) ++#define PCIE_PHY_PORT_TO_BASE(X) ((X) > 0 ? (PCIE1_PDI_BASE) : (PCIE_PDI_BASE)) ++ ++/* PCIe Application Logic Register */ ++/* RC Core Control Register */ ++#define PCIE_RC_CCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x10) ++/* This should be enabled after initializing configuratin registers ++ * Also should check link status retraining bit ++ */ ++#define PCIE_RC_CCR_LTSSM_ENABLE 0x00000001 /* Enable LTSSM to continue link establishment */ ++ ++/* RC Core Debug Register */ ++#define PCIE_RC_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x14) ++#define PCIE_RC_DR_DLL_UP 0x00000001 /* Data Link Layer Up */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE 0x0000000E /* Current Power State */ ++#define PCIE_RC_DR_CURRENT_POWER_STATE_S 1 ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE 0x000001F0 /* Current LTSSM State */ ++#define PCIE_RC_DR_CURRENT_LTSSM_STATE_S 4 ++ ++#define PCIE_RC_DR_PM_DEV_STATE 0x00000E00 /* Power Management D-State */ ++#define PCIE_RC_DR_PM_DEV_STATE_S 9 ++ ++#define PCIE_RC_DR_PM_ENABLED 0x00001000 /* Power Management State from PMU */ ++#define PCIE_RC_DR_PME_EVENT_ENABLED 0x00002000 /* Power Management Event Enable State */ ++#define PCIE_RC_DR_AUX_POWER_ENABLED 0x00004000 /* Auxiliary Power Enable */ ++ ++/* Current Power State Definition */ ++enum { ++ PCIE_RC_DR_D0 = 0, ++ PCIE_RC_DR_D1, /* Not supported */ ++ PCIE_RC_DR_D2, /* Not supported */ ++ PCIE_RC_DR_D3, ++ PCIE_RC_DR_UN, ++}; ++ ++/* PHY Link Status Register */ ++#define PCIE_PHY_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x18) ++#define PCIE_PHY_SR_PHY_LINK_UP 0x00000001 /* PHY Link Up/Down Indicator */ ++ ++/* Electromechanical Control Register */ ++#define PCIE_EM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x1C) ++#define PCIE_EM_CR_CARD_IS_PRESENT 0x00000001 /* Card Presence Detect State */ ++#define PCIE_EM_CR_MRL_OPEN 0x00000002 /* MRL Sensor State */ ++#define PCIE_EM_CR_POWER_FAULT_SET 0x00000004 /* Power Fault Detected */ ++#define PCIE_EM_CR_MRL_SENSOR_SET 0x00000008 /* MRL Sensor Changed */ ++#define PCIE_EM_CR_PRESENT_DETECT_SET 0x00000010 /* Card Presense Detect Changed */ ++#define PCIE_EM_CR_CMD_CPL_INT_SET 0x00000020 /* Command Complete Interrupt */ ++#define PCIE_EM_CR_SYS_INTERLOCK_SET 0x00000040 /* System Electromechanical IterLock Engaged */ ++#define PCIE_EM_CR_ATTENTION_BUTTON_SET 0x00000080 /* Attention Button Pressed */ ++ ++/* Interrupt Status Register */ ++#define PCIE_IR_SR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x20) ++#define PCIE_IR_SR_PME_CAUSE_MSI 0x00000002 /* MSI caused by PME */ ++#define PCIE_IR_SR_HP_PME_WAKE_GEN 0x00000004 /* Hotplug PME Wake Generation */ ++#define PCIE_IR_SR_HP_MSI 0x00000008 /* Hotplug MSI */ ++#define PCIE_IR_SR_AHB_LU_ERR 0x00000030 /* AHB Bridge Lookup Error Signals */ ++#define PCIE_IR_SR_AHB_LU_ERR_S 4 ++#define PCIE_IR_SR_INT_MSG_NUM 0x00003E00 /* Interrupt Message Number */ ++#define PCIE_IR_SR_INT_MSG_NUM_S 9 ++#define PCIE_IR_SR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_IR_SR_AER_INT_MSG_NUM_S 27 ++ ++/* Message Control Register */ ++#define PCIE_MSG_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x30) ++#define PCIE_MSG_CR_GEN_PME_TURN_OFF_MSG 0x00000001 /* Generate PME Turn Off Message */ ++#define PCIE_MSG_CR_GEN_UNLOCK_MSG 0x00000002 /* Generate Unlock Message */ ++ ++#define PCIE_VDM_DR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x34) ++ ++/* Vendor-Defined Message Requester ID Register */ ++#define PCIE_VDM_RID(X) (PCIE_APP_PORT_TO_BASE (X) + 0x38) ++#define PCIE_VDM_RID_VENROR_MSG_REQ_ID 0x0000FFFF ++#define PCIE_VDM_RID_VDMRID_S 0 ++ ++/* ASPM Control Register */ ++#define PCIE_ASPM_CR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x40) ++#define PCIE_ASPM_CR_HOT_RST 0x00000001 /* Hot Reset Request to the downstream device */ ++#define PCIE_ASPM_CR_REQ_EXIT_L1 0x00000002 /* Request to Exit L1 */ ++#define PCIE_ASPM_CR_REQ_ENTER_L1 0x00000004 /* Request to Enter L1 */ ++ ++/* Vendor Message DW0 Register */ ++#define PCIE_VM_MSG_DW0(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x50) ++#define PCIE_VM_MSG_DW0_TYPE 0x0000001F /* Message type */ ++#define PCIE_VM_MSG_DW0_TYPE_S 0 ++#define PCIE_VM_MSG_DW0_FORMAT 0x00000060 /* Format */ ++#define PCIE_VM_MSG_DW0_FORMAT_S 5 ++#define PCIE_VM_MSG_DW0_TC 0x00007000 /* Traffic Class */ ++#define PCIE_VM_MSG_DW0_TC_S 12 ++#define PCIE_VM_MSG_DW0_ATTR 0x000C0000 /* Atrributes */ ++#define PCIE_VM_MSG_DW0_ATTR_S 18 ++#define PCIE_VM_MSG_DW0_EP_TLP 0x00100000 /* Poisoned TLP */ ++#define PCIE_VM_MSG_DW0_TD 0x00200000 /* TLP Digest */ ++#define PCIE_VM_MSG_DW0_LEN 0xFFC00000 /* Length */ ++#define PCIE_VM_MSG_DW0_LEN_S 22 ++ ++/* Format Definition */ ++enum { ++ PCIE_VM_MSG_FORMAT_00 = 0, /* 3DW Hdr, no data*/ ++ PCIE_VM_MSG_FORMAT_01, /* 4DW Hdr, no data */ ++ PCIE_VM_MSG_FORMAT_10, /* 3DW Hdr, with data */ ++ PCIE_VM_MSG_FORMAT_11, /* 4DW Hdr, with data */ ++}; ++ ++/* Traffic Class Definition */ ++enum { ++ PCIE_VM_MSG_TC0 = 0, ++ PCIE_VM_MSG_TC1, ++ PCIE_VM_MSG_TC2, ++ PCIE_VM_MSG_TC3, ++ PCIE_VM_MSG_TC4, ++ PCIE_VM_MSG_TC5, ++ PCIE_VM_MSG_TC6, ++ PCIE_VM_MSG_TC7, ++}; ++ ++/* Attributes Definition */ ++enum { ++ PCIE_VM_MSG_ATTR_00 = 0, /* RO and No Snoop cleared */ ++ PCIE_VM_MSG_ATTR_01, /* RO cleared , No Snoop set */ ++ PCIE_VM_MSG_ATTR_10, /* RO set, No Snoop cleared*/ ++ PCIE_VM_MSG_ATTR_11, /* RO and No Snoop set */ ++}; ++ ++/* Payload Size Definition */ ++#define PCIE_VM_MSG_LEN_MIN 0 ++#define PCIE_VM_MSG_LEN_MAX 1024 ++ ++/* Vendor Message DW1 Register */ ++#define PCIE_VM_MSG_DW1(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x54) ++#define PCIE_VM_MSG_DW1_FUNC_NUM 0x00000070 /* Function Number */ ++#define PCIE_VM_MSG_DW1_FUNC_NUM_S 8 ++#define PCIE_VM_MSG_DW1_CODE 0x00FF0000 /* Message Code */ ++#define PCIE_VM_MSG_DW1_CODE_S 16 ++#define PCIE_VM_MSG_DW1_TAG 0xFF000000 /* Tag */ ++#define PCIE_VM_MSG_DW1_TAG_S 24 ++ ++#define PCIE_VM_MSG_DW2(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x58) ++#define PCIE_VM_MSG_DW3(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x5C) ++ ++/* Vendor Message Request Register */ ++#define PCIE_VM_MSG_REQR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x60) ++#define PCIE_VM_MSG_REQR_REQ 0x00000001 /* Vendor Message Request */ ++ ++ ++/* AHB Slave Side Band Control Register */ ++#define PCIE_AHB_SSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x70) ++#define PCIE_AHB_SSB_REQ_BCM 0x00000001 /* Slave Reques BCM filed */ ++#define PCIE_AHB_SSB_REQ_EP 0x00000002 /* Slave Reques EP filed */ ++#define PCIE_AHB_SSB_REQ_TD 0x00000004 /* Slave Reques TD filed */ ++#define PCIE_AHB_SSB_REQ_ATTR 0x00000018 /* Slave Reques Attribute number */ ++#define PCIE_AHB_SSB_REQ_ATTR_S 3 ++#define PCIE_AHB_SSB_REQ_TC 0x000000E0 /* Slave Request TC Field */ ++#define PCIE_AHB_SSB_REQ_TC_S 5 ++ ++/* AHB Master SideBand Ctrl Register */ ++#define PCIE_AHB_MSB(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x74) ++#define PCIE_AHB_MSB_RESP_ATTR 0x00000003 /* Master Response Attribute number */ ++#define PCIE_AHB_MSB_RESP_ATTR_S 0 ++#define PCIE_AHB_MSB_RESP_BAD_EOT 0x00000004 /* Master Response Badeot filed */ ++#define PCIE_AHB_MSB_RESP_BCM 0x00000008 /* Master Response BCM filed */ ++#define PCIE_AHB_MSB_RESP_EP 0x00000010 /* Master Response EP filed */ ++#define PCIE_AHB_MSB_RESP_TD 0x00000020 /* Master Response TD filed */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM 0x000003C0 /* Master Response Function number */ ++#define PCIE_AHB_MSB_RESP_FUN_NUM_S 6 ++ ++/* AHB Control Register, fixed bus enumeration exception */ ++#define PCIE_AHB_CTRL(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0x78) ++#define PCIE_AHB_CTRL_BUS_ERROR_SUPPRESS 0x00000001 ++ ++/* Interrupt Enalbe Register */ ++#define PCIE_IRNEN(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF4) ++#define PCIE_IRNCR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xF8) ++#define PCIE_IRNICR(X) (volatile u32*)(PCIE_APP_PORT_TO_BASE(X) + 0xFC) ++ ++/* PCIe interrupt enable/control/capture register definition */ ++#define PCIE_IRN_AER_REPORT 0x00000001 /* AER Interrupt */ ++#define PCIE_IRN_AER_MSIX 0x00000002 /* Advanced Error MSI-X Interrupt */ ++#define PCIE_IRN_PME 0x00000004 /* PME Interrupt */ ++#define PCIE_IRN_HOTPLUG 0x00000008 /* Hotplug Interrupt */ ++#define PCIE_IRN_RX_VDM_MSG 0x00000010 /* Vendor-Defined Message Interrupt */ ++#define PCIE_IRN_RX_CORRECTABLE_ERR_MSG 0x00000020 /* Correctable Error Message Interrupt */ ++#define PCIE_IRN_RX_NON_FATAL_ERR_MSG 0x00000040 /* Non-fatal Error Message */ ++#define PCIE_IRN_RX_FATAL_ERR_MSG 0x00000080 /* Fatal Error Message */ ++#define PCIE_IRN_RX_PME_MSG 0x00000100 /* PME Message Interrupt */ ++#define PCIE_IRN_RX_PME_TURNOFF_ACK 0x00000200 /* PME Turnoff Ack Message Interrupt */ ++#define PCIE_IRN_AHB_BR_FATAL_ERR 0x00000400 /* AHB Fatal Error Interrupt */ ++#define PCIE_IRN_LINK_AUTO_BW_STATUS 0x00000800 /* Link Auto Bandwidth Status Interrupt */ ++#define PCIE_IRN_BW_MGT 0x00001000 /* Bandwidth Managment Interrupt */ ++#define PCIE_IRN_INTA 0x00002000 /* INTA */ ++#define PCIE_IRN_INTB 0x00004000 /* INTB */ ++#define PCIE_IRN_INTC 0x00008000 /* INTC */ ++#define PCIE_IRN_INTD 0x00010000 /* INTD */ ++#define PCIE_IRN_WAKEUP 0x00020000 /* Wake up Interrupt */ ++ ++#define PCIE_RC_CORE_COMBINED_INT (PCIE_IRN_AER_REPORT | PCIE_IRN_AER_MSIX | PCIE_IRN_PME | \ ++ PCIE_IRN_HOTPLUG | PCIE_IRN_RX_VDM_MSG | PCIE_IRN_RX_CORRECTABLE_ERR_MSG |\ ++ PCIE_IRN_RX_NON_FATAL_ERR_MSG | PCIE_IRN_RX_FATAL_ERR_MSG | \ ++ PCIE_IRN_RX_PME_MSG | PCIE_IRN_RX_PME_TURNOFF_ACK | PCIE_IRN_AHB_BR_FATAL_ERR | \ ++ PCIE_IRN_LINK_AUTO_BW_STATUS | PCIE_IRN_BW_MGT) ++/* PCIe RC Configuration Register */ ++#define PCIE_VDID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x00) ++ ++/* Bit definition from pci_reg.h */ ++#define PCIE_PCICMDSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x04) ++#define PCIE_CCRID(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x08) ++#define PCIE_CLSLTHTBR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x0C) /* EP only */ ++/* BAR0, BAR1,Only necessary if the bridges implements a device-specific register set or memory buffer */ ++#define PCIE_BAR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10) /* Not used*/ ++#define PCIE_BAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14) /* Not used */ ++ ++#define PCIE_BNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x18) /* Mandatory */ ++/* Bus Number Register bits */ ++#define PCIE_BNR_PRIMARY_BUS_NUM 0x000000FF ++#define PCIE_BNR_PRIMARY_BUS_NUM_S 0 ++#define PCIE_PNR_SECONDARY_BUS_NUM 0x0000FF00 ++#define PCIE_PNR_SECONDARY_BUS_NUM_S 8 ++#define PCIE_PNR_SUB_BUS_NUM 0x00FF0000 ++#define PCIE_PNR_SUB_BUS_NUM_S 16 ++ ++/* IO Base/Limit Register bits */ ++#define PCIE_IOBLSECS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x1C) /* RC only */ ++#define PCIE_IOBLSECS_32BIT_IO_ADDR 0x00000001 ++#define PCIE_IOBLSECS_IO_BASE_ADDR 0x000000F0 ++#define PCIE_IOBLSECS_IO_BASE_ADDR_S 4 ++#define PCIE_IOBLSECS_32BIT_IOLIMT 0x00000100 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR 0x0000F000 ++#define PCIE_IOBLSECS_IO_LIMIT_ADDR_S 12 ++ ++/* Non-prefetchable Memory Base/Limit Register bit */ ++#define PCIE_MBML(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x20) /* RC only */ ++#define PCIE_MBML_MEM_BASE_ADDR 0x0000FFF0 ++#define PCIE_MBML_MEM_BASE_ADDR_S 4 ++#define PCIE_MBML_MEM_LIMIT_ADDR 0xFFF00000 ++#define PCIE_MBML_MEM_LIMIT_ADDR_S 20 ++ ++/* Prefetchable Memory Base/Limit Register bit */ ++#define PCIE_PMBL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x24) /* RC only */ ++#define PCIE_PMBL_64BIT_ADDR 0x00000001 ++#define PCIE_PMBL_UPPER_12BIT 0x0000FFF0 ++#define PCIE_PMBL_UPPER_12BIT_S 4 ++#define PCIE_PMBL_E64MA 0x00010000 ++#define PCIE_PMBL_END_ADDR 0xFFF00000 ++#define PCIE_PMBL_END_ADDR_S 20 ++#define PCIE_PMBU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x28) /* RC only */ ++#define PCIE_PMLU32(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x2C) /* RC only */ ++ ++/* I/O Base/Limit Upper 16 bits register */ ++#define PCIE_IO_BANDL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x30) /* RC only */ ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE 0x0000FFFF ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_BASE_S 0 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT 0xFFFF0000 ++#define PCIE_IO_BANDL_UPPER_16BIT_IO_LIMIT_S 16 ++ ++#define PCIE_CPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x34) ++#define PCIE_EBBAR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x38) ++ ++/* Interrupt and Secondary Bridge Control Register */ ++#define PCIE_INTRBCTRL(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x3C) ++ ++#define PCIE_INTRBCTRL_INT_LINE 0x000000FF ++#define PCIE_INTRBCTRL_INT_LINE_S 0 ++#define PCIE_INTRBCTRL_INT_PIN 0x0000FF00 ++#define PCIE_INTRBCTRL_INT_PIN_S 8 ++#define PCIE_INTRBCTRL_PARITY_ERR_RESP_ENABLE 0x00010000 /* #PERR */ ++#define PCIE_INTRBCTRL_SERR_ENABLE 0x00020000 /* #SERR */ ++#define PCIE_INTRBCTRL_ISA_ENABLE 0x00040000 /* ISA enable, IO 64KB only */ ++#define PCIE_INTRBCTRL_VGA_ENABLE 0x00080000 /* VGA enable */ ++#define PCIE_INTRBCTRL_VGA_16BIT_DECODE 0x00100000 /* VGA 16bit decode */ ++#define PCIE_INTRBCTRL_RST_SECONDARY_BUS 0x00400000 /* Secondary bus rest, hot rest, 1ms */ ++/* Others are read only */ ++enum { ++ PCIE_INTRBCTRL_INT_NON = 0, ++ PCIE_INTRBCTRL_INTA, ++ PCIE_INTRBCTRL_INTB, ++ PCIE_INTRBCTRL_INTC, ++ PCIE_INTRBCTRL_INTD, ++}; ++ ++#define PCIE_PM_CAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x40) ++ ++/* Power Management Control and Status Register */ ++#define PCIE_PM_CSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x44) ++ ++#define PCIE_PM_CSR_POWER_STATE 0x00000003 /* Power State */ ++#define PCIE_PM_CSR_POWER_STATE_S 0 ++#define PCIE_PM_CSR_SW_RST 0x00000008 /* Soft Reset Enabled */ ++#define PCIE_PM_CSR_PME_ENABLE 0x00000100 /* PME Enable */ ++#define PCIE_PM_CSR_PME_STATUS 0x00008000 /* PME status */ ++ ++/* MSI Capability Register for EP */ ++#define PCIE_MCAPR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x50) ++ ++#define PCIE_MCAPR_MSI_CAP_ID 0x000000FF /* MSI Capability ID */ ++#define PCIE_MCAPR_MSI_CAP_ID_S 0 ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_MCAPR_MSI_NEXT_CAP_PTR_S 8 ++#define PCIE_MCAPR_MSI_ENABLE 0x00010000 /* MSI Enable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP 0x000E0000 /* Multiple Message Capable */ ++#define PCIE_MCAPR_MULTI_MSG_CAP_S 17 ++#define PCIE_MCAPR_MULTI_MSG_ENABLE 0x00700000 /* Multiple Message Enable */ ++#define PCIE_MCAPR_MULTI_MSG_ENABLE_S 20 ++#define PCIE_MCAPR_ADDR64_CAP 0X00800000 /* 64-bit Address Capable */ ++ ++/* MSI Message Address Register */ ++#define PCIE_MA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x54) ++ ++#define PCIE_MA_ADDR_MASK 0xFFFFFFFC /* Message Address */ ++ ++/* MSI Message Upper Address Register */ ++#define PCIE_MUA(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x58) ++ ++/* MSI Message Data Register */ ++#define PCIE_MD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x5C) ++ ++#define PCIE_MD_DATA 0x0000FFFF /* Message Data */ ++#define PCIE_MD_DATA_S 0 ++ ++/* PCI Express Capability Register */ ++#define PCIE_XCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70) ++ ++#define PCIE_XCAP_ID 0x000000FF /* PCI Express Capability ID */ ++#define PCIE_XCAP_ID_S 0 ++#define PCIE_XCAP_NEXT_CAP 0x0000FF00 /* Next Capability Pointer */ ++#define PCIE_XCAP_NEXT_CAP_S 8 ++#define PCIE_XCAP_VER 0x000F0000 /* PCI Express Capability Version */ ++#define PCIE_XCAP_VER_S 16 ++#define PCIE_XCAP_DEV_PORT_TYPE 0x00F00000 /* Device Port Type */ ++#define PCIE_XCAP_DEV_PORT_TYPE_S 20 ++#define PCIE_XCAP_SLOT_IMPLEMENTED 0x01000000 /* Slot Implemented */ ++#define PCIE_XCAP_MSG_INT_NUM 0x3E000000 /* Interrupt Message Number */ ++#define PCIE_XCAP_MSG_INT_NUM_S 25 ++ ++/* Device Capability Register */ ++#define PCIE_DCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74) ++ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE 0x00000007 /* Max Payload size */ ++#define PCIE_DCAP_MAX_PAYLOAD_SIZE_S 0 ++#define PCIE_DCAP_PHANTOM_FUNC 0x00000018 /* Phanton Function, not supported */ ++#define PCIE_DCAP_PHANTOM_FUNC_S 3 ++#define PCIE_DCAP_EXT_TAG 0x00000020 /* Extended Tag Field */ ++#define PCIE_DCAP_EP_L0S_LATENCY 0x000001C0 /* EP L0s latency only */ ++#define PCIE_DCAP_EP_L0S_LATENCY_S 6 ++#define PCIE_DCAP_EP_L1_LATENCY 0x00000E00 /* EP L1 latency only */ ++#define PCIE_DCAP_EP_L1_LATENCY_S 9 ++#define PCIE_DCAP_ROLE_BASE_ERR_REPORT 0x00008000 /* Role Based ERR */ ++ ++/* Maximum payload size supported */ ++enum { ++ PCIE_MAX_PAYLOAD_128 = 0, ++ PCIE_MAX_PAYLOAD_256, ++ PCIE_MAX_PAYLOAD_512, ++ PCIE_MAX_PAYLOAD_1024, ++ PCIE_MAX_PAYLOAD_2048, ++ PCIE_MAX_PAYLOAD_4096, ++}; ++ ++/* Device Control and Status Register */ ++#define PCIE_DCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x78) ++ ++#define PCIE_DCTLSTS_CORRECTABLE_ERR_EN 0x00000001 /* COR-ERR */ ++#define PCIE_DCTLSTS_NONFATAL_ERR_EN 0x00000002 /* Non-fatal ERR */ ++#define PCIE_DCTLSTS_FATAL_ERR_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_DCTLSYS_UR_REQ_EN 0x00000008 /* UR ERR */ ++#define PCIE_DCTLSTS_RELAXED_ORDERING_EN 0x00000010 /* Enable relaxing ordering */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE 0x000000E0 /* Max payload mask */ ++#define PCIE_DCTLSTS_MAX_PAYLOAD_SIZE_S 5 ++#define PCIE_DCTLSTS_EXT_TAG_EN 0x00000100 /* Extended tag field */ ++#define PCIE_DCTLSTS_PHANTOM_FUNC_EN 0x00000200 /* Phantom Function Enable */ ++#define PCIE_DCTLSTS_AUX_PM_EN 0x00000400 /* AUX Power PM Enable */ ++#define PCIE_DCTLSTS_NO_SNOOP_EN 0x00000800 /* Enable no snoop, except root port*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE 0x00007000 /* Max Read Request size*/ ++#define PCIE_DCTLSTS_MAX_READ_SIZE_S 12 ++#define PCIE_DCTLSTS_CORRECTABLE_ERR 0x00010000 /* COR-ERR Detected */ ++#define PCIE_DCTLSTS_NONFATAL_ERR 0x00020000 /* Non-Fatal ERR Detected */ ++#define PCIE_DCTLSTS_FATAL_ER 0x00040000 /* Fatal ERR Detected */ ++#define PCIE_DCTLSTS_UNSUPPORTED_REQ 0x00080000 /* UR Detected */ ++#define PCIE_DCTLSTS_AUX_POWER 0x00100000 /* Aux Power Detected */ ++#define PCIE_DCTLSTS_TRANSACT_PENDING 0x00200000 /* Transaction pending */ ++ ++#define PCIE_DCTLSTS_ERR_EN (PCIE_DCTLSTS_CORRECTABLE_ERR_EN | \ ++ PCIE_DCTLSTS_NONFATAL_ERR_EN | PCIE_DCTLSTS_FATAL_ERR_EN | \ ++ PCIE_DCTLSYS_UR_REQ_EN) ++ ++/* Link Capability Register */ ++#define PCIE_LCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7C) ++#define PCIE_LCAP_MAX_LINK_SPEED 0x0000000F /* Max link speed, 0x1 by default */ ++#define PCIE_LCAP_MAX_LINK_SPEED_S 0 ++#define PCIE_LCAP_MAX_LENGTH_WIDTH 0x000003F0 /* Maxium Length Width */ ++#define PCIE_LCAP_MAX_LENGTH_WIDTH_S 4 ++#define PCIE_LCAP_ASPM_LEVEL 0x00000C00 /* Active State Link PM Support */ ++#define PCIE_LCAP_ASPM_LEVEL_S 10 ++#define PCIE_LCAP_L0S_EIXT_LATENCY 0x00007000 /* L0s Exit Latency */ ++#define PCIE_LCAP_L0S_EIXT_LATENCY_S 12 ++#define PCIE_LCAP_L1_EXIT_LATENCY 0x00038000 /* L1 Exit Latency */ ++#define PCIE_LCAP_L1_EXIT_LATENCY_S 15 ++#define PCIE_LCAP_CLK_PM 0x00040000 /* Clock Power Management */ ++#define PCIE_LCAP_SDER 0x00080000 /* Surprise Down Error Reporting */ ++#define PCIE_LCAP_DLL_ACTIVE_REPROT 0x00100000 /* Data Link Layer Active Reporting Capable */ ++#define PCIE_LCAP_PORT_NUM 0xFF0000000 /* Port number */ ++#define PCIE_LCAP_PORT_NUM_S 24 ++ ++/* Maximum Length width definition */ ++#define PCIE_MAX_LENGTH_WIDTH_RES 0x00 ++#define PCIE_MAX_LENGTH_WIDTH_X1 0x01 /* Default */ ++#define PCIE_MAX_LENGTH_WIDTH_X2 0x02 ++#define PCIE_MAX_LENGTH_WIDTH_X4 0x04 ++#define PCIE_MAX_LENGTH_WIDTH_X8 0x08 ++#define PCIE_MAX_LENGTH_WIDTH_X12 0x0C ++#define PCIE_MAX_LENGTH_WIDTH_X16 0x10 ++#define PCIE_MAX_LENGTH_WIDTH_X32 0x20 ++ ++/* Active State Link PM definition */ ++enum { ++ PCIE_ASPM_RES0 = 0, ++ PCIE_ASPM_L0S_ENTRY_SUPPORT, /* L0s */ ++ PCIE_ASPM_RES1, ++ PCIE_ASPM_L0S_L1_ENTRY_SUPPORT, /* L0s and L1, default */ ++}; ++ ++/* L0s Exit Latency definition */ ++enum { ++ PCIE_L0S_EIXT_LATENCY_L64NS = 0, /* < 64 ns */ ++ PCIE_L0S_EIXT_LATENCY_B64A128, /* > 64 ns < 128 ns */ ++ PCIE_L0S_EIXT_LATENCY_B128A256, /* > 128 ns < 256 ns */ ++ PCIE_L0S_EIXT_LATENCY_B256A512, /* > 256 ns < 512 ns */ ++ PCIE_L0S_EIXT_LATENCY_B512TO1U, /* > 512 ns < 1 us */ ++ PCIE_L0S_EIXT_LATENCY_B1A2U, /* > 1 us < 2 us */ ++ PCIE_L0S_EIXT_LATENCY_B2A4U, /* > 2 us < 4 us */ ++ PCIE_L0S_EIXT_LATENCY_M4US, /* > 4 us */ ++}; ++ ++/* L1 Exit Latency definition */ ++enum { ++ PCIE_L1_EXIT_LATENCY_L1US = 0, /* < 1 us */ ++ PCIE_L1_EXIT_LATENCY_B1A2, /* > 1 us < 2 us */ ++ PCIE_L1_EXIT_LATENCY_B2A4, /* > 2 us < 4 us */ ++ PCIE_L1_EXIT_LATENCY_B4A8, /* > 4 us < 8 us */ ++ PCIE_L1_EXIT_LATENCY_B8A16, /* > 8 us < 16 us */ ++ PCIE_L1_EXIT_LATENCY_B16A32, /* > 16 us < 32 us */ ++ PCIE_L1_EXIT_LATENCY_B32A64, /* > 32 us < 64 us */ ++ PCIE_L1_EXIT_LATENCY_M64US, /* > 64 us */ ++}; ++ ++/* Link Control and Status Register */ ++#define PCIE_LCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x80) ++#define PCIE_LCTLSTS_ASPM_ENABLE 0x00000003 /* Active State Link PM Control */ ++#define PCIE_LCTLSTS_ASPM_ENABLE_S 0 ++#define PCIE_LCTLSTS_RCB128 0x00000008 /* Read Completion Boundary 128*/ ++#define PCIE_LCTLSTS_LINK_DISABLE 0x00000010 /* Link Disable */ ++#define PCIE_LCTLSTS_RETRIAN_LINK 0x00000020 /* Retrain Link */ ++#define PCIE_LCTLSTS_COM_CLK_CFG 0x00000040 /* Common Clock Configuration */ ++#define PCIE_LCTLSTS_EXT_SYNC 0x00000080 /* Extended Synch */ ++#define PCIE_LCTLSTS_CLK_PM_EN 0x00000100 /* Enable Clock Powerm Management */ ++#define PCIE_LCTLSTS_LINK_SPEED 0x000F0000 /* Link Speed */ ++#define PCIE_LCTLSTS_LINK_SPEED_S 16 ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH 0x03F00000 /* Negotiated Link Width */ ++#define PCIE_LCTLSTS_NEGOTIATED_LINK_WIDTH_S 20 ++#define PCIE_LCTLSTS_RETRAIN_PENDING 0x08000000 /* Link training is ongoing */ ++#define PCIE_LCTLSTS_SLOT_CLK_CFG 0x10000000 /* Slot Clock Configuration */ ++#define PCIE_LCTLSTS_DLL_ACTIVE 0x20000000 /* Data Link Layer Active */ ++ ++/* Slot Capabilities Register */ ++#define PCIE_SLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x84) ++ ++/* Slot Capabilities */ ++#define PCIE_SLCTLSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x88) ++ ++/* Root Control and Capability Register */ ++#define PCIE_RCTLCAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x8C) ++#define PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR 0x00000001 /* #SERR on COR-ERR */ ++#define PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR 0x00000002 /* #SERR on Non-Fatal ERR */ ++#define PCIE_RCTLCAP_SERR_ON_FATAL_ERR 0x00000004 /* #SERR on Fatal ERR */ ++#define PCIE_RCTLCAP_PME_INT_EN 0x00000008 /* PME Interrupt Enable */ ++#define PCIE_RCTLCAP_SERR_ENABLE (PCIE_RCTLCAP_SERR_ON_CORRECTABLE_ERR | \ ++ PCIE_RCTLCAP_SERR_ON_NONFATAL_ERR | PCIE_RCTLCAP_SERR_ON_FATAL_ERR) ++/* Root Status Register */ ++#define PCIE_RSTS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x90) ++#define PCIE_RSTS_PME_REQ_ID 0x0000FFFF /* PME Request ID */ ++#define PCIE_RSTS_PME_REQ_ID_S 0 ++#define PCIE_RSTS_PME_STATUS 0x00010000 /* PME Status */ ++#define PCIE_RSTS_PME_PENDING 0x00020000 /* PME Pending */ ++ ++/* PCI Express Enhanced Capability Header */ ++#define PCIE_ENHANCED_CAP(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x100) ++#define PCIE_ENHANCED_CAP_ID 0x0000FFFF /* PCI Express Extended Capability ID */ ++#define PCIE_ENHANCED_CAP_ID_S 0 ++#define PCIE_ENHANCED_CAP_VER 0x000F0000 /* Capability Version */ ++#define PCIE_ENHANCED_CAP_VER_S 16 ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET 0xFFF00000 /* Next Capability Offset */ ++#define PCIE_ENHANCED_CAP_NEXT_OFFSET_S 20 ++ ++/* Uncorrectable Error Status Register */ ++#define PCIE_UES_R(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x104) ++#define PCIE_DATA_LINK_PROTOCOL_ERR 0x00000010 /* Data Link Protocol Error Status */ ++#define PCIE_SURPRISE_DOWN_ERROR 0x00000020 /* Surprise Down Error Status */ ++#define PCIE_POISONED_TLP 0x00001000 /* Poisoned TLP Status */ ++#define PCIE_FC_PROTOCOL_ERR 0x00002000 /* Flow Control Protocol Error Status */ ++#define PCIE_COMPLETION_TIMEOUT 0x00004000 /* Completion Timeout Status */ ++#define PCIE_COMPLETOR_ABORT 0x00008000 /* Completer Abort Error */ ++#define PCIE_UNEXPECTED_COMPLETION 0x00010000 /* Unexpected Completion Status */ ++#define PCIE_RECEIVER_OVERFLOW 0x00020000 /* Receive Overflow Status */ ++#define PCIE_MALFORNED_TLP 0x00040000 /* Malformed TLP Stauts */ ++#define PCIE_ECRC_ERR 0x00080000 /* ECRC Error Stauts */ ++#define PCIE_UR_REQ 0x00100000 /* Unsupported Request Error Status */ ++#define PCIE_ALL_UNCORRECTABLE_ERR (PCIE_DATA_LINK_PROTOCOL_ERR | PCIE_SURPRISE_DOWN_ERROR | \ ++ PCIE_POISONED_TLP | PCIE_FC_PROTOCOL_ERR | PCIE_COMPLETION_TIMEOUT | \ ++ PCIE_COMPLETOR_ABORT | PCIE_UNEXPECTED_COMPLETION | PCIE_RECEIVER_OVERFLOW |\ ++ PCIE_MALFORNED_TLP | PCIE_ECRC_ERR | PCIE_UR_REQ) ++ ++/* Uncorrectable Error Mask Register, Mask means no report */ ++#define PCIE_UEMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x108) ++ ++/* Uncorrectable Error Severity Register */ ++#define PCIE_UESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x10C) ++ ++/* Correctable Error Status Register */ ++#define PCIE_CESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x110) ++#define PCIE_RX_ERR 0x00000001 /* Receive Error Status */ ++#define PCIE_BAD_TLP 0x00000040 /* Bad TLP Status */ ++#define PCIE_BAD_DLLP 0x00000080 /* Bad DLLP Status */ ++#define PCIE_REPLAY_NUM_ROLLOVER 0x00000100 /* Replay Number Rollover Status */ ++#define PCIE_REPLAY_TIMER_TIMEOUT_ERR 0x00001000 /* Reply Timer Timeout Status */ ++#define PCIE_ADVISORY_NONFTAL_ERR 0x00002000 /* Advisory Non-Fatal Error Status */ ++#define PCIE_CORRECTABLE_ERR (PCIE_RX_ERR | PCIE_BAD_TLP | PCIE_BAD_DLLP | PCIE_REPLAY_NUM_ROLLOVER |\ ++ PCIE_REPLAY_TIMER_TIMEOUT_ERR | PCIE_ADVISORY_NONFTAL_ERR) ++ ++/* Correctable Error Mask Register */ ++#define PCIE_CEMR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x114) ++ ++/* Advanced Error Capabilities and Control Register */ ++#define PCIE_AECCR(X) (volatile u32*)(PCIE_RC_CFG_BASE + 0x118) ++#define PCIE_AECCR_FIRST_ERR_PTR 0x0000001F /* First Error Pointer */ ++#define PCIE_AECCR_FIRST_ERR_PTR_S 0 ++#define PCIE_AECCR_ECRC_GEN_CAP 0x00000020 /* ECRC Generation Capable */ ++#define PCIE_AECCR_ECRC_GEN_EN 0x00000040 /* ECRC Generation Enable */ ++#define PCIE_AECCR_ECRC_CHECK_CAP 0x00000080 /* ECRC Check Capable */ ++#define PCIE_AECCR_ECRC_CHECK_EN 0x00000100 /* ECRC Check Enable */ ++ ++/* Header Log Register 1 */ ++#define PCIE_HLR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x11C) ++ ++/* Header Log Register 2 */ ++#define PCIE_HLR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x120) ++ ++/* Header Log Register 3 */ ++#define PCIE_HLR3(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x124) ++ ++/* Header Log Register 4 */ ++#define PCIE_HLR4(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x128) ++ ++/* Root Error Command Register */ ++#define PCIE_RECR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x12C) ++#define PCIE_RECR_CORRECTABLE_ERR_REPORT_EN 0x00000001 /* COR-ERR */ ++#define PCIE_RECR_NONFATAL_ERR_REPORT_EN 0x00000002 /* Non-Fatal ERR */ ++#define PCIE_RECR_FATAL_ERR_REPORT_EN 0x00000004 /* Fatal ERR */ ++#define PCIE_RECR_ERR_REPORT_EN (PCIE_RECR_CORRECTABLE_ERR_REPORT_EN | \ ++ PCIE_RECR_NONFATAL_ERR_REPORT_EN | PCIE_RECR_FATAL_ERR_REPORT_EN) ++ ++/* Root Error Status Register */ ++#define PCIE_RESR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x130) ++#define PCIE_RESR_CORRECTABLE_ERR 0x00000001 /* COR-ERR Receveid */ ++#define PCIE_RESR_MULTI_CORRECTABLE_ERR 0x00000002 /* Multiple COR-ERR Received */ ++#define PCIE_RESR_FATAL_NOFATAL_ERR 0x00000004 /* ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_MULTI_FATAL_NOFATAL_ERR 0x00000008 /* Multiple ERR Fatal/Non-Fatal Received */ ++#define PCIE_RESR_FIRST_UNCORRECTABLE_FATAL_ERR 0x00000010 /* First UN-COR Fatal */ ++#define PCIR_RESR_NON_FATAL_ERR 0x00000020 /* Non-Fatal Error Message Received */ ++#define PCIE_RESR_FATAL_ERR 0x00000040 /* Fatal Message Received */ ++#define PCIE_RESR_AER_INT_MSG_NUM 0xF8000000 /* Advanced Error Interrupt Message Number */ ++#define PCIE_RESR_AER_INT_MSG_NUM_S 27 ++ ++/* Error Source Indentification Register */ ++#define PCIE_ESIR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x134) ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID 0x0000FFFF ++#define PCIE_ESIR_CORRECTABLE_ERR_SRC_ID_S 0 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID 0xFFFF0000 ++#define PCIE_ESIR_FATAL_NON_FATAL_SRC_ID_S 16 ++ ++/* VC Enhanced Capability Header */ ++#define PCIE_VC_ECH(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x140) ++ ++/* Port VC Capability Register */ ++#define PCIE_PVC1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x144) ++#define PCIE_PVC1_EXT_VC_CNT 0x00000007 /* Extended VC Count */ ++#define PCIE_PVC1_EXT_VC_CNT_S 0 ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT 0x00000070 /* Low Priority Extended VC Count */ ++#define PCIE_PVC1_LOW_PRI_EXT_VC_CNT_S 4 ++#define PCIE_PVC1_REF_CLK 0x00000300 /* Reference Clock */ ++#define PCIE_PVC1_REF_CLK_S 8 ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE 0x00000C00 /* Port Arbitration Table Entry Size */ ++#define PCIE_PVC1_PORT_ARB_TAB_ENTRY_SIZE_S 10 ++ ++/* Extended Virtual Channel Count Defintion */ ++#define PCIE_EXT_VC_CNT_MIN 0 ++#define PCIE_EXT_VC_CNT_MAX 7 ++ ++/* Port Arbitration Table Entry Size Definition */ ++enum { ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S1BIT = 0, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S2BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S4BIT, ++ PCIE_PORT_ARB_TAB_ENTRY_SIZE_S8BIT, ++}; ++ ++/* Port VC Capability Register 2 */ ++#define PCIE_PVC2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x148) ++#define PCIE_PVC2_VC_ARB_16P_FIXED_WRR 0x00000001 /* HW Fixed arbitration, 16 phase WRR */ ++#define PCIE_PVC2_VC_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_PVC2_VC_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_PVC2_VC_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_PVC2_VC_ARB_WRR 0x0000000F ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET 0xFF000000 /* VC arbitration table offset, not support */ ++#define PCIE_PVC2_VC_ARB_TAB_OFFSET_S 24 ++ ++/* Port VC Control and Status Register */ ++#define PCIE_PVCCRSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x14C) ++#define PCIE_PVCCRSR_LOAD_VC_ARB_TAB 0x00000001 /* Load VC Arbitration Table */ ++#define PCIE_PVCCRSR_VC_ARB_SEL 0x0000000E /* VC Arbitration Select */ ++#define PCIE_PVCCRSR_VC_ARB_SEL_S 1 ++#define PCIE_PVCCRSR_VC_ARB_TAB_STATUS 0x00010000 /* Arbitration Status */ ++ ++/* VC0 Resource Capability Register */ ++#define PCIE_VC0_RC(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x150) ++#define PCIE_VC0_RC_PORT_ARB_HW_FIXED 0x00000001 /* HW Fixed arbitration */ ++#define PCIE_VC0_RC_PORT_ARB_32P_WRR 0x00000002 /* 32 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_64P_WRR 0x00000004 /* 64 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_128P_WRR 0x00000008 /* 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_128P_WRR 0x00000010 /* Time-based 128 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB_TM_256P_WRR 0x00000020 /* Time-based 256 phase WRR */ ++#define PCIE_VC0_RC_PORT_ARB (PCIE_VC0_RC_PORT_ARB_HW_FIXED | PCIE_VC0_RC_PORT_ARB_32P_WRR |\ ++ PCIE_VC0_RC_PORT_ARB_64P_WRR | PCIE_VC0_RC_PORT_ARB_128P_WRR | \ ++ PCIE_VC0_RC_PORT_ARB_TM_128P_WRR | PCIE_VC0_RC_PORT_ARB_TM_256P_WRR) ++ ++#define PCIE_VC0_RC_REJECT_SNOOP 0x00008000 /* Reject Snoop Transactioin */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS 0x007F0000 /* Maximum time Slots */ ++#define PCIE_VC0_RC_MAX_TIMESLOTS_S 16 ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET 0xFF000000 /* Port Arbitration Table Offset */ ++#define PCIE_VC0_RC_PORT_ARB_TAB_OFFSET_S 24 ++ ++/* VC0 Resource Control Register */ ++#define PCIE_VC0_RC0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x154) ++#define PCIE_VC0_RC0_TVM0 0x00000001 /* TC0 and VC0 */ ++#define PCIE_VC0_RC0_TVM1 0x00000002 /* TC1 and VC1 */ ++#define PCIE_VC0_RC0_TVM2 0x00000004 /* TC2 and VC2 */ ++#define PCIE_VC0_RC0_TVM3 0x00000008 /* TC3 and VC3 */ ++#define PCIE_VC0_RC0_TVM4 0x00000010 /* TC4 and VC4 */ ++#define PCIE_VC0_RC0_TVM5 0x00000020 /* TC5 and VC5 */ ++#define PCIE_VC0_RC0_TVM6 0x00000040 /* TC6 and VC6 */ ++#define PCIE_VC0_RC0_TVM7 0x00000080 /* TC7 and VC7 */ ++#define PCIE_VC0_RC0_TC_VC 0x000000FF /* TC/VC mask */ ++ ++#define PCIE_VC0_RC0_LOAD_PORT_ARB_TAB 0x00010000 /* Load Port Arbitration Table */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL 0x000E0000 /* Port Arbitration Select */ ++#define PCIE_VC0_RC0_PORT_ARB_SEL_S 17 ++#define PCIE_VC0_RC0_VC_ID 0x07000000 /* VC ID */ ++#define PCIE_VC0_RC0_VC_ID_S 24 ++#define PCIE_VC0_RC0_VC_EN 0x80000000 /* VC Enable */ ++ ++/* VC0 Resource Status Register */ ++#define PCIE_VC0_RSR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x158) ++#define PCIE_VC0_RSR0_PORT_ARB_TAB_STATUS 0x00010000 /* Port Arbitration Table Status,not used */ ++#define PCIE_VC0_RSR0_VC_NEG_PENDING 0x00020000 /* VC Negotiation Pending */ ++ ++/* Ack Latency Timer and Replay Timer Register */ ++#define PCIE_ALTRT(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x700) ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT 0x0000FFFF /* Round Trip Latency Time Limit */ ++#define PCIE_ALTRT_ROUND_TRIP_LATENCY_LIMIT_S 0 ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT 0xFFFF0000 /* Replay Time Limit */ ++#define PCIE_ALTRT_REPLAY_TIME_LIMIT_S 16 ++ ++/* Other Message Register */ ++#define PCIE_OMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x704) ++ ++/* Port Force Link Register */ ++#define PCIE_PFLR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x708) ++#define PCIE_PFLR_LINK_NUM 0x000000FF /* Link Number */ ++#define PCIE_PFLR_LINK_NUM_S 0 ++#define PCIE_PFLR_FORCE_LINK 0x00008000 /* Force link */ ++#define PCIE_PFLR_LINK_STATE 0x003F0000 /* Link State */ ++#define PCIE_PFLR_LINK_STATE_S 16 ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT 0xFF000000 /* Low Power Entrance Count, only for EP */ ++#define PCIE_PFLR_LOW_POWER_ENTRY_CNT_S 24 ++ ++/* Ack Frequency Register */ ++#define PCIE_AFR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x70C) ++#define PCIE_AFR_AF 0x000000FF /* Ack Frequency */ ++#define PCIE_AFR_AF_S 0 ++#define PCIE_AFR_FTS_NUM 0x0000FF00 /* The number of Fast Training Sequence from L0S to L0 */ ++#define PCIE_AFR_FTS_NUM_S 8 ++#define PCIE_AFR_COM_FTS_NUM 0x00FF0000 /* N_FTS; when common clock is used*/ ++#define PCIE_AFR_COM_FTS_NUM_S 16 ++#define PCIE_AFR_L0S_ENTRY_LATENCY 0x07000000 /* L0s Entrance Latency */ ++#define PCIE_AFR_L0S_ENTRY_LATENCY_S 24 ++#define PCIE_AFR_L1_ENTRY_LATENCY 0x38000000 /* L1 Entrance Latency */ ++#define PCIE_AFR_L1_ENTRY_LATENCY_S 27 ++#define PCIE_AFR_FTS_NUM_DEFAULT 32 ++#define PCIE_AFR_L0S_ENTRY_LATENCY_DEFAULT 7 ++#define PCIE_AFR_L1_ENTRY_LATENCY_DEFAULT 5 ++ ++/* Port Link Control Register */ ++#define PCIE_PLCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x710) ++#define PCIE_PLCR_OTHER_MSG_REQ 0x00000001 /* Other Message Request */ ++#define PCIE_PLCR_SCRAMBLE_DISABLE 0x00000002 /* Scramble Disable */ ++#define PCIE_PLCR_LOOPBACK_EN 0x00000004 /* Loopback Enable */ ++#define PCIE_PLCR_LTSSM_HOT_RST 0x00000008 /* Force LTSSM to the hot reset */ ++#define PCIE_PLCR_DLL_LINK_EN 0x00000020 /* Enable Link initialization */ ++#define PCIE_PLCR_FAST_LINK_SIM_EN 0x00000080 /* Sets all internal timers to fast mode for simulation purposes */ ++#define PCIE_PLCR_LINK_MODE 0x003F0000 /* Link Mode Enable Mask */ ++#define PCIE_PLCR_LINK_MODE_S 16 ++#define PCIE_PLCR_CORRUPTED_CRC_EN 0x02000000 /* Enabled Corrupt CRC */ ++ ++/* Lane Skew Register */ ++#define PCIE_LSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x714) ++#define PCIE_LSR_LANE_SKEW_NUM 0x00FFFFFF /* Insert Lane Skew for Transmit, not applicable */ ++#define PCIE_LSR_LANE_SKEW_NUM_S 0 ++#define PCIE_LSR_FC_DISABLE 0x01000000 /* Disable of Flow Control */ ++#define PCIE_LSR_ACKNAK_DISABLE 0x02000000 /* Disable of Ack/Nak */ ++#define PCIE_LSR_LANE_DESKEW_DISABLE 0x80000000 /* Disable of Lane-to-Lane Skew */ ++ ++/* Symbol Number Register */ ++#define PCIE_SNR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x718) ++#define PCIE_SNR_TS 0x0000000F /* Number of TS Symbol */ ++#define PCIE_SNR_TS_S 0 ++#define PCIE_SNR_SKP 0x00000700 /* Number of SKP Symbol */ ++#define PCIE_SNR_SKP_S 8 ++#define PCIE_SNR_REPLAY_TIMER 0x0007C000 /* Timer Modifier for Replay Timer */ ++#define PCIE_SNR_REPLAY_TIMER_S 14 ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER 0x00F80000 /* Timer Modifier for Ack/Nak Latency Timer */ ++#define PCIE_SNR_ACKNAK_LATENCY_TIMER_S 19 ++#define PCIE_SNR_FC_TIMER 0x1F000000 /* Timer Modifier for Flow Control Watchdog Timer */ ++#define PCIE_SNR_FC_TIMER_S 28 ++ ++/* Symbol Timer Register and Filter Mask Register 1 */ ++#define PCIE_STRFMR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x71C) ++#define PCIE_STRFMR_SKP_INTERVAL 0x000007FF /* SKP lnterval Value */ ++#define PCIE_STRFMR_SKP_INTERVAL_S 0 ++#define PCIE_STRFMR_FC_WDT_DISABLE 0x00008000 /* Disable of FC Watchdog Timer */ ++#define PCIE_STRFMR_TLP_FUNC_MISMATCH_OK 0x00010000 /* Mask Function Mismatch Filtering for Incoming Requests */ ++#define PCIE_STRFMR_POISONED_TLP_OK 0x00020000 /* Mask Poisoned TLP Filtering */ ++#define PCIE_STRFMR_BAR_MATCH_OK 0x00040000 /* Mask BAR Match Filtering */ ++#define PCIE_STRFMR_TYPE1_CFG_REQ_OK 0x00080000 /* Mask Type 1 Configuration Request Filtering */ ++#define PCIE_STRFMR_LOCKED_REQ_OK 0x00100000 /* Mask Locked Request Filtering */ ++#define PCIE_STRFMR_CPL_TAG_ERR_RULES_OK 0x00200000 /* Mask Tag Error Rules for Received Completions */ ++#define PCIE_STRFMR_CPL_REQUESTOR_ID_MISMATCH_OK 0x00400000 /* Mask Requester ID Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_FUNC_MISMATCH_OK 0x00800000 /* Mask Function Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_TC_MISMATCH_OK 0x01000000 /* Mask Traffic Class Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_ATTR_MISMATCH_OK 0x02000000 /* Mask Attribute Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_CPL_LENGTH_MISMATCH_OK 0x04000000 /* Mask Length Mismatch Error for Received Completions */ ++#define PCIE_STRFMR_TLP_ECRC_ERR_OK 0x08000000 /* Mask ECRC Error Filtering */ ++#define PCIE_STRFMR_CPL_TLP_ECRC_OK 0x10000000 /* Mask ECRC Error Filtering for Completions */ ++#define PCIE_STRFMR_RX_TLP_MSG_NO_DROP 0x20000000 /* Send Message TLPs */ ++#define PCIE_STRFMR_RX_IO_TRANS_ENABLE 0x40000000 /* Mask Filtering of received I/O Requests */ ++#define PCIE_STRFMR_RX_CFG_TRANS_ENABLE 0x80000000 /* Mask Filtering of Received Configuration Requests */ ++ ++#define PCIE_DEF_SKP_INTERVAL 700 /* 1180 ~1538 , 125MHz * 2, 250MHz * 1 */ ++ ++/* Filter Masker Register 2 */ ++#define PCIE_FMR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x720) ++#define PCIE_FMR2_VENDOR_MSG0_PASSED_TO_TRGT1 0x00000001 /* Mask RADM Filtering and Error Handling Rules */ ++#define PCIE_FMR2_VENDOR_MSG1_PASSED_TO_TRGT1 0x00000002 /* Mask RADM Filtering and Error Handling Rules */ ++ ++/* Debug Register 0 */ ++#define PCIE_DBR0(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x728) ++ ++/* Debug Register 1 */ ++#define PCIE_DBR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x72C) ++ ++/* Transmit Posted FC Credit Status Register */ ++#define PCIE_TPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x730) ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS 0x00000FFF /* Transmit Posted Data FC Credits */ ++#define PCIE_TPFCS_TX_P_DATA_FC_CREDITS_S 0 ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS 0x000FF000 /* Transmit Posted Header FC Credits */ ++#define PCIE_TPFCS_TX_P_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Non-Posted FC Credit Status */ ++#define PCIE_TNPFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x734) ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS 0x00000FFF /* Transmit Non-Posted Data FC Credits */ ++#define PCIE_TNPFCS_TX_NP_DATA_FC_CREDITS_S 0 ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS 0x000FF000 /* Transmit Non-Posted Header FC Credits */ ++#define PCIE_TNPFCS_TX_NP_HDR_FC_CREDITS_S 12 ++ ++/* Transmit Complete FC Credit Status Register */ ++#define PCIE_TCFCS(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x738) ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS 0x00000FFF /* Transmit Completion Data FC Credits */ ++#define PCIE_TCFCS_TX_CPL_DATA_FC_CREDITS_S 0 ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS 0x000FF000 /* Transmit Completion Header FC Credits */ ++#define PCIE_TCFCS_TX_CPL_HDR_FC_CREDITS_S 12 ++ ++/* Queue Status Register */ ++#define PCIE_QSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x73C) ++#define PCIE_QSR_WAIT_UPDATE_FC_DLL 0x00000001 /* Received TLP FC Credits Not Returned */ ++#define PCIE_QSR_TX_RETRY_BUF_NOT_EMPTY 0x00000002 /* Transmit Retry Buffer Not Empty */ ++#define PCIE_QSR_RX_QUEUE_NOT_EMPTY 0x00000004 /* Received Queue Not Empty */ ++ ++/* VC Transmit Arbitration Register 1 */ ++#define PCIE_VCTAR1(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x740) ++#define PCIE_VCTAR1_WRR_WEIGHT_VC0 0x000000FF /* WRR Weight for VC0 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC1 0x0000FF00 /* WRR Weight for VC1 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC2 0x00FF0000 /* WRR Weight for VC2 */ ++#define PCIE_VCTAR1_WRR_WEIGHT_VC3 0xFF000000 /* WRR Weight for VC3 */ ++ ++/* VC Transmit Arbitration Register 2 */ ++#define PCIE_VCTAR2(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x744) ++#define PCIE_VCTAR2_WRR_WEIGHT_VC4 0x000000FF /* WRR Weight for VC4 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC5 0x0000FF00 /* WRR Weight for VC5 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC6 0x00FF0000 /* WRR Weight for VC6 */ ++#define PCIE_VCTAR2_WRR_WEIGHT_VC7 0xFF000000 /* WRR Weight for VC7 */ ++ ++/* VC0 Posted Receive Queue Control Register */ ++#define PCIE_VC0_PRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x748) ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS 0x00000FFF /* VC0 Posted Data Credits */ ++#define PCIE_VC0_PRQCR_P_DATA_CREDITS_S 0 ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS 0x000FF000 /* VC0 Posted Header Credits */ ++#define PCIE_VC0_PRQCR_P_HDR_CREDITS_S 12 ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE 0x00E00000 /* VC0 Posted TLP Queue Mode */ ++#define PCIE_VC0_PRQCR_P_TLP_QUEUE_MODE_S 20 ++#define PCIE_VC0_PRQCR_TLP_RELAX_ORDER 0x40000000 /* TLP Type Ordering for VC0 */ ++#define PCIE_VC0_PRQCR_VC_STRICT_ORDER 0x80000000 /* VC0 Ordering for Receive Queues */ ++ ++/* VC0 Non-Posted Receive Queue Control */ ++#define PCIE_VC0_NPRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x74C) ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS 0x00000FFF /* VC0 Non-Posted Data Credits */ ++#define PCIE_VC0_NPRQCR_NP_DATA_CREDITS_S 0 ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS 0x000FF000 /* VC0 Non-Posted Header Credits */ ++#define PCIE_VC0_NPRQCR_NP_HDR_CREDITS_S 12 ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE 0x00E00000 /* VC0 Non-Posted TLP Queue Mode */ ++#define PCIE_VC0_NPRQCR_NP_TLP_QUEUE_MODE_S 20 ++ ++/* VC0 Completion Receive Queue Control */ ++#define PCIE_VC0_CRQCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x750) ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS 0x00000FFF /* VC0 Completion TLP Queue Mode */ ++#define PCIE_VC0_CRQCR_CPL_DATA_CREDITS_S 0 ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS 0x000FF000 /* VC0 Completion Header Credits */ ++#define PCIE_VC0_CRQCR_CPL_HDR_CREDITS_S 12 ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE 0x00E00000 /* VC0 Completion Data Credits */ ++#define PCIE_VC0_CRQCR_CPL_TLP_QUEUE_MODE_S 21 ++ ++/* Applicable to the above three registers */ ++enum { ++ PCIE_VC0_TLP_QUEUE_MODE_STORE_FORWARD = 1, ++ PCIE_VC0_TLP_QUEUE_MODE_CUT_THROUGH = 2, ++ PCIE_VC0_TLP_QUEUE_MODE_BYPASS = 4, ++}; ++ ++/* VC0 Posted Buffer Depth Register */ ++#define PCIE_VC0_PBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7A8) ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Posted Data Queue Depth */ ++#define PCIE_VC0_PBD_P_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Posted Header Queue Depth */ ++#define PCIE_VC0_PBD_P_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Non-Posted Buffer Depth Register */ ++#define PCIE_VC0_NPBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7AC) ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES 0x00003FFF /* VC0 Non-Posted Data Queue Depth */ ++#define PCIE_VC0_NPBD_NP_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Non-Posted Header Queue Depth */ ++#define PCIE_VC0_NPBD_NP_HDR_QUEUE_ENTRIES_S 16 ++ ++/* VC0 Completion Buffer Depth Register */ ++#define PCIE_VC0_CBD(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x7B0) ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES 0x00003FFF /* C0 Completion Data Queue Depth */ ++#define PCIE_VC0_CBD_CPL_DATA_QUEUE_ENTRIES_S 0 ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES 0x03FF0000 /* VC0 Completion Header Queue Depth */ ++#define PCIE_VC0_CBD_CPL_HDR_QUEUE_ENTRIES_S 16 ++ ++/* PHY Status Register, all zeros in VR9 */ ++#define PCIE_PHYSR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x810) ++ ++/* PHY Control Register, all zeros in VR9 */ ++#define PCIE_PHYCR(X) (volatile u32*)(PCIE_RC_PORT_TO_BASE(X) + 0x814) ++ ++/* ++ * PCIe PDI PHY register definition, suppose all the following ++ * stuff is confidential. ++ * XXX, detailed bit definition ++ */ ++#define PCIE_PHY_PLL_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x22 << 1)) ++#define PCIE_PHY_PLL_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x23 << 1)) ++#define PCIE_PHY_PLL_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x24 << 1)) ++#define PCIE_PHY_PLL_CTRL4(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x25 << 1)) ++#define PCIE_PHY_PLL_CTRL5(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x26 << 1)) ++#define PCIE_PHY_PLL_CTRL6(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x27 << 1)) ++#define PCIE_PHY_PLL_CTRL7(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x28 << 1)) ++#define PCIE_PHY_PLL_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x29 << 1)) ++#define PCIE_PHY_PLL_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2A << 1)) ++#define PCIE_PHY_PLL_A_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2B << 1)) ++#define PCIE_PHY_PLL_STATUS(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x2C << 1)) ++ ++#define PCIE_PHY_TX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x30 << 1)) ++#define PCIE_PHY_TX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x31 << 1)) ++#define PCIE_PHY_TX1_CTRL3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x32 << 1)) ++#define PCIE_PHY_TX1_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x33 << 1)) ++#define PCIE_PHY_TX1_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x34 << 1)) ++#define PCIE_PHY_TX1_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x35 << 1)) ++#define PCIE_PHY_TX1_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x36 << 1)) ++#define PCIE_PHY_TX1_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x37 << 1)) ++ ++#define PCIE_PHY_TX2_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x38 << 1)) ++#define PCIE_PHY_TX2_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x39 << 1)) ++#define PCIE_PHY_TX2_A_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3B << 1)) ++#define PCIE_PHY_TX2_A_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3C << 1)) ++#define PCIE_PHY_TX2_MOD1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3D << 1)) ++#define PCIE_PHY_TX2_MOD2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3E << 1)) ++#define PCIE_PHY_TX2_MOD3(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x3F << 1)) ++ ++#define PCIE_PHY_RX1_CTRL1(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x50 << 1)) ++#define PCIE_PHY_RX1_CTRL2(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x51 << 1)) ++#define PCIE_PHY_RX1_CDR(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x52 << 1)) ++#define PCIE_PHY_RX1_EI(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x53 << 1)) ++#define PCIE_PHY_RX1_A_CTRL(X) (PCIE_PHY_PORT_TO_BASE(X) + (0x55 << 1)) ++ ++/* Interrupt related stuff */ ++#define PCIE_LEGACY_DISABLE 0 ++#define PCIE_LEGACY_INTA 1 ++#define PCIE_LEGACY_INTB 2 ++#define PCIE_LEGACY_INTC 3 ++#define PCIE_LEGACY_INTD 4 ++#define PCIE_LEGACY_INT_MAX PCIE_LEGACY_INTD ++ ++#define PCIE_IRQ_LOCK(lock) do { \ ++ unsigned long flags; \ ++ spin_lock_irqsave(&(lock), flags); ++#define PCIE_IRQ_UNLOCK(lock) \ ++ spin_unlock_irqrestore(&(lock), flags); \ ++} while (0) ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,18) ++#define IRQF_SHARED SA_SHIRQ ++#endif ++ ++#define PCIE_MSG_MSI 0x00000001 ++#define PCIE_MSG_ISR 0x00000002 ++#define PCIE_MSG_FIXUP 0x00000004 ++#define PCIE_MSG_READ_CFG 0x00000008 ++#define PCIE_MSG_WRITE_CFG 0x00000010 ++#define PCIE_MSG_CFG (PCIE_MSG_READ_CFG | PCIE_MSG_WRITE_CFG) ++#define PCIE_MSG_REG 0x00000020 ++#define PCIE_MSG_INIT 0x00000040 ++#define PCIE_MSG_ERR 0x00000080 ++#define PCIE_MSG_PHY 0x00000100 ++#define PCIE_MSG_ANY 0x000001ff ++ ++#define IFX_PCIE_PORT0 0 ++#define IFX_PCIE_PORT1 1 ++ ++#ifdef CONFIG_IFX_PCIE_2ND_CORE ++#define IFX_PCIE_CORE_NR 2 ++#else ++#define IFX_PCIE_CORE_NR 1 ++#endif ++ ++//#define IFX_PCIE_ERROR_INT ++ ++//#define IFX_PCIE_DBG ++ ++#if defined(IFX_PCIE_DBG) ++#define IFX_PCIE_PRINT(_m, _fmt, args...) do { \ ++ if (g_pcie_debug_flag & (_m)) { \ ++ ifx_pcie_debug((_fmt), ##args); \ ++ } \ ++} while (0) ++ ++#define INLINE ++#else ++#define IFX_PCIE_PRINT(_m, _fmt, args...) \ ++ do {} while(0) ++#define INLINE inline ++#endif ++ ++struct ifx_pci_controller { ++ struct pci_controller pcic; ++ ++ /* RC specific, per host bus information */ ++ u32 port; /* Port index, 0 -- 1st core, 1 -- 2nd core */ ++}; ++ ++typedef struct ifx_pcie_ir_irq { ++ const unsigned int irq; ++ const char name[16]; ++}ifx_pcie_ir_irq_t; ++ ++typedef struct ifx_pcie_legacy_irq{ ++ const u32 irq_bit; ++ const int irq; ++}ifx_pcie_legacy_irq_t; ++ ++typedef struct ifx_pcie_irq { ++ ifx_pcie_ir_irq_t ir_irq; ++ ifx_pcie_legacy_irq_t legacy_irq[PCIE_LEGACY_INT_MAX]; ++}ifx_pcie_irq_t; ++ ++extern u32 g_pcie_debug_flag; ++extern void ifx_pcie_debug(const char *fmt, ...); ++extern void pcie_phy_clock_mode_setup(int pcie_port); ++extern void pcie_msi_pic_init(int pcie_port); ++extern u32 ifx_pcie_bus_enum_read_hack(int where, u32 value); ++extern u32 ifx_pcie_bus_enum_write_hack(int where, u32 value); ++ ++ ++#include ++#include ++#include ++#include ++ ++#include ++ ++#define IFX_PCIE_GPIO_RESET 38 ++#define IFX_REG_R32 ltq_r32 ++#define IFX_REG_W32 ltq_w32 ++#define CONFIG_IFX_PCIE_HW_SWAP ++#define IFX_RCU_AHB_ENDIAN ((volatile u32*)(IFX_RCU + 0x004C)) ++#define IFX_RCU_RST_REQ ((volatile u32*)(IFX_RCU + 0x0010)) ++#define IFX_RCU_AHB_BE_PCIE_PDI 0x00000080 /* Configure PCIE PDI module in big endian*/ ++ ++#define IFX_RCU (KSEG1 | 0x1F203000) ++#define IFX_RCU_AHB_BE_PCIE_M 0x00000001 /* Configure AHB master port that connects to PCIe RC in big endian */ ++#define IFX_RCU_AHB_BE_PCIE_S 0x00000010 /* Configure AHB slave port that connects to PCIe RC in little endian */ ++#define IFX_RCU_AHB_BE_XBAR_M 0x00000002 /* Configure AHB master port that connects to XBAR in big endian */ ++#define CONFIG_IFX_PCIE_PHY_36MHZ_MODE ++ ++#define IFX_PMU1_MODULE_PCIE_PHY (0) ++#define IFX_PMU1_MODULE_PCIE_CTRL (1) ++#define IFX_PMU1_MODULE_PDI (4) ++#define IFX_PMU1_MODULE_MSI (5) ++ ++#define IFX_PMU_MODULE_PCIE_L0_CLK (31) ++ ++ ++static inline void pcie_ep_gpio_rst_init(int pcie_port) ++{ ++} ++ ++static inline void pcie_ahb_pmu_setup(void) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "ahb"); ++ clk_enable(clk); ++ //ltq_pmu_enable(PMU_AHBM | PMU_AHBS); ++} ++ ++static inline void pcie_rcu_endian_setup(int pcie_port) ++{ ++ u32 reg; ++ ++ reg = IFX_REG_R32(IFX_RCU_AHB_ENDIAN); ++#ifdef CONFIG_IFX_PCIE_HW_SWAP ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg |= IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#else ++ reg |= IFX_RCU_AHB_BE_PCIE_M; ++ reg &= ~IFX_RCU_AHB_BE_PCIE_S; ++ reg &= ~IFX_RCU_AHB_BE_XBAR_M; ++#endif /* CONFIG_IFX_PCIE_HW_SWAP */ ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++ IFX_PCIE_PRINT(PCIE_MSG_REG, "%s IFX_RCU_AHB_ENDIAN: 0x%08x\n", __func__, IFX_REG_R32(IFX_RCU_AHB_ENDIAN)); ++} ++ ++static inline void pcie_phy_pmu_enable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "phy"); ++ clk_enable(clk); ++ //ltq_pmu1_enable(1<PCIe and PDI endianness */ ++ reg |= IFX_RCU_AHB_BE_PCIE_PDI; ++ IFX_REG_W32(reg, IFX_RCU_AHB_ENDIAN); ++} ++ ++static inline void pcie_pdi_pmu_enable(int pcie_port) ++{ ++ struct clk *clk; ++ clk = clk_get_sys("ltq_pcie", "pdi"); ++ clk_enable(clk); ++ //ltq_pmu1_enable(1< 1) { ++ tbus_number -= pcibios_1st_host_bus_nr(); ++ } ++#endif /* CONFIG_IFX_PCI */ ++ return tbus_number; ++} ++ ++static inline u32 ++ifx_pcie_bus_enum_hack(struct pci_bus *bus, u32 devfn, int where, u32 value, int pcie_port, int read) ++{ ++ struct pci_dev *pdev; ++ u32 tvalue = value; ++ ++ /* Sanity check */ ++ pdev = pci_get_slot(bus, devfn); ++ if (pdev == NULL) { ++ return tvalue; ++ } ++ ++ /* Only care about PCI bridge */ ++ if (pdev->hdr_type != PCI_HEADER_TYPE_BRIDGE) { ++ return tvalue; ++ } ++ ++ if (read) { /* Read hack */ ++ #ifdef CONFIG_IFX_PCI ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_read_hack(where, tvalue); ++ } ++ #endif /* CONFIG_IFX_PCI */ ++ } ++ else { /* Write hack */ ++ #ifdef CONFIG_IFX_PCI ++ if (pcibios_host_nr() > 1) { ++ tvalue = ifx_pcie_bus_enum_write_hack(where, tvalue); ++ } ++ #endif ++ } ++ return tvalue; ++} ++ ++#endif /* IFXMIPS_PCIE_VR9_H */ ++ +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0052-MIPS-lantiq-make-GPIO3-work-on-AR9.patch b/target/linux/lantiq/patches-3.3/0052-MIPS-lantiq-make-GPIO3-work-on-AR9.patch new file mode 100644 index 0000000000..b38cbeb727 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0052-MIPS-lantiq-make-GPIO3-work-on-AR9.patch @@ -0,0 +1,236 @@ +From 85859883ce603bf0db782c03294873dad39176e5 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 13 Aug 2011 13:59:50 +0200 +Subject: [PATCH 52/70] MIPS: lantiq: make GPIO3 work on AR9 + +There are 3 16bit and 1 8bit gpio ports on AR9. The gpio driver needs a hack +at 2 places to make the different register layout of the GPIO3 work properly +with the driver. Before only GPIO0-2 were supported. As the GPIO number scheme +clashes with the new size, we also move the other gpio chips to new offsets. + +Signed-off-by: John Crispin +Signed-off-by: Thomas Langer +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 2 + + arch/mips/lantiq/xway/devices.c | 3 + + arch/mips/lantiq/xway/gpio.c | 84 ++++++++++++++++---- + arch/mips/lantiq/xway/gpio_ebu.c | 3 +- + arch/mips/lantiq/xway/gpio_stp.c | 3 +- + 5 files changed, 75 insertions(+), 20 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index d1b8cc8..bfdeb16 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -126,7 +126,9 @@ + #define LTQ_GPIO0_BASE_ADDR 0x1E100B10 + #define LTQ_GPIO1_BASE_ADDR 0x1E100B40 + #define LTQ_GPIO2_BASE_ADDR 0x1E100B70 ++#define LTQ_GPIO3_BASE_ADDR 0x1E100BA0 + #define LTQ_GPIO_SIZE 0x30 ++#define LTQ_GPIO3_SIZE 0x10 + + /* SSC */ + #define LTQ_SSC_BASE_ADDR 0x1e100800 +diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c +index 5efa4f3..e6d45bc 100644 +--- a/arch/mips/lantiq/xway/devices.c ++++ b/arch/mips/lantiq/xway/devices.c +@@ -34,6 +34,7 @@ static struct resource ltq_gpio_resource[] = { + MEM_RES("gpio0", LTQ_GPIO0_BASE_ADDR, LTQ_GPIO_SIZE), + MEM_RES("gpio1", LTQ_GPIO1_BASE_ADDR, LTQ_GPIO_SIZE), + MEM_RES("gpio2", LTQ_GPIO2_BASE_ADDR, LTQ_GPIO_SIZE), ++ MEM_RES("gpio3", LTQ_GPIO3_BASE_ADDR, LTQ_GPIO3_SIZE), + }; + + void __init ltq_register_gpio(void) +@@ -47,6 +48,8 @@ void __init ltq_register_gpio(void) + if (ltq_is_ar9() || ltq_is_vr9()) { + platform_device_register_simple("ltq_gpio", 2, + <q_gpio_resource[2], 1); ++ platform_device_register_simple("ltq_gpio", 3, ++ <q_gpio_resource[3], 1); + } + } + +diff --git a/arch/mips/lantiq/xway/gpio.c b/arch/mips/lantiq/xway/gpio.c +index 54ec6c9..375329b 100644 +--- a/arch/mips/lantiq/xway/gpio.c ++++ b/arch/mips/lantiq/xway/gpio.c +@@ -23,9 +23,17 @@ + #define LTQ_GPIO_OD 0x14 + #define LTQ_GPIO_PUDSEL 0x1C + #define LTQ_GPIO_PUDEN 0x20 ++#define LTQ_GPIO3_OD 0x24 ++#define LTQ_GPIO3_ALTSEL1 0x24 ++#define LTQ_GPIO3_PUDSEL 0x28 ++#define LTQ_GPIO3_PUDEN 0x2C + ++/* PORT3 only has 8 pins and its register layout ++ is slightly different */ + #define PINS_PER_PORT 16 +-#define MAX_PORTS 3 ++#define PINS_PORT3 8 ++#define MAX_PORTS 4 ++#define MAX_PIN 56 + + #define ltq_gpio_getbit(m, r, p) (!!(ltq_r32(m + r) & (1 << p))) + #define ltq_gpio_setbit(m, r, p) ltq_w32_mask(0, (1 << p), m + r) +@@ -55,7 +63,7 @@ int ltq_gpio_request(struct device *dev, unsigned int pin, unsigned int mux, + { + int id = 0; + +- if (pin >= (MAX_PORTS * PINS_PER_PORT)) ++ if (pin >= MAX_PIN) + return -EINVAL; + if (devm_gpio_request(dev, pin, name)) { + pr_err("failed to setup lantiq gpio: %s\n", name); +@@ -75,12 +83,21 @@ int ltq_gpio_request(struct device *dev, unsigned int pin, unsigned int mux, + else + ltq_gpio_clearbit(ltq_gpio_port[id].membase, + LTQ_GPIO_ALTSEL0, pin); +- if (mux & 0x1) +- ltq_gpio_setbit(ltq_gpio_port[id].membase, +- LTQ_GPIO_ALTSEL1, pin); +- else +- ltq_gpio_clearbit(ltq_gpio_port[id].membase, +- LTQ_GPIO_ALTSEL1, pin); ++ if (id == 3) { ++ if (mux & 0x1) ++ ltq_gpio_setbit(ltq_gpio_port[1].membase, ++ LTQ_GPIO3_ALTSEL1, pin); ++ else ++ ltq_gpio_clearbit(ltq_gpio_port[1].membase, ++ LTQ_GPIO3_ALTSEL1, pin); ++ } else { ++ if (mux & 0x1) ++ ltq_gpio_setbit(ltq_gpio_port[id].membase, ++ LTQ_GPIO_ALTSEL1, pin); ++ else ++ ltq_gpio_clearbit(ltq_gpio_port[id].membase, ++ LTQ_GPIO_ALTSEL1, pin); ++ } + return 0; + } + EXPORT_SYMBOL(ltq_gpio_request); +@@ -106,10 +123,19 @@ static int ltq_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) + { + struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); + +- ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); ++ if (chip->ngpio == PINS_PORT3) { ++ ltq_gpio_clearbit(ltq_gpio_port[0].membase, ++ LTQ_GPIO3_OD, offset); ++ ltq_gpio_setbit(ltq_gpio_port[0].membase, ++ LTQ_GPIO3_PUDSEL, offset); ++ ltq_gpio_setbit(ltq_gpio_port[0].membase, ++ LTQ_GPIO3_PUDEN, offset); ++ } else { ++ ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); ++ ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_PUDSEL, offset); ++ ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_PUDEN, offset); ++ } + ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_DIR, offset); +- ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_PUDSEL, offset); +- ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_PUDEN, offset); + + return 0; + } +@@ -119,10 +145,19 @@ static int ltq_gpio_direction_output(struct gpio_chip *chip, + { + struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); + +- ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); ++ if (chip->ngpio == PINS_PORT3) { ++ ltq_gpio_setbit(ltq_gpio_port[0].membase, ++ LTQ_GPIO3_OD, offset); ++ ltq_gpio_clearbit(ltq_gpio_port[0].membase, ++ LTQ_GPIO3_PUDSEL, offset); ++ ltq_gpio_clearbit(ltq_gpio_port[0].membase, ++ LTQ_GPIO3_PUDEN, offset); ++ } else { ++ ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_OD, offset); ++ ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_PUDSEL, offset); ++ ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_PUDEN, offset); ++ } + ltq_gpio_setbit(ltq_gpio->membase, LTQ_GPIO_DIR, offset); +- ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_PUDSEL, offset); +- ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_PUDEN, offset); + ltq_gpio_set(chip, offset, value); + + return 0; +@@ -133,7 +168,11 @@ static int ltq_gpio_req(struct gpio_chip *chip, unsigned offset) + struct ltq_gpio *ltq_gpio = container_of(chip, struct ltq_gpio, chip); + + ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_ALTSEL0, offset); +- ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_ALTSEL1, offset); ++ if (chip->ngpio == PINS_PORT3) ++ ltq_gpio_clearbit(ltq_gpio_port[1].membase, ++ LTQ_GPIO3_ALTSEL1, offset); ++ else ++ ltq_gpio_clearbit(ltq_gpio->membase, LTQ_GPIO_ALTSEL1, offset); + return 0; + } + +@@ -146,6 +185,16 @@ static int ltq_gpio_probe(struct platform_device *pdev) + pdev->id); + return -EINVAL; + } ++ ++ /* dirty hack - The registers of port3 are not mapped linearly. ++ Port 3 may only load if Port 1/2 are mapped */ ++ if ((pdev->id == 3) && (!ltq_gpio_port[1].membase ++ || !ltq_gpio_port[2].membase)) { ++ dev_err(&pdev->dev, ++ "ports 1/2 need to be loaded before port 3 works\n"); ++ return -ENOMEM; ++ } ++ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get memory for gpio port %d\n", +@@ -175,7 +224,10 @@ static int ltq_gpio_probe(struct platform_device *pdev) + ltq_gpio_port[pdev->id].chip.set = ltq_gpio_set; + ltq_gpio_port[pdev->id].chip.request = ltq_gpio_req; + ltq_gpio_port[pdev->id].chip.base = PINS_PER_PORT * pdev->id; +- ltq_gpio_port[pdev->id].chip.ngpio = PINS_PER_PORT; ++ if (pdev->id == 3) ++ ltq_gpio_port[pdev->id].chip.ngpio = PINS_PORT3; ++ else ++ ltq_gpio_port[pdev->id].chip.ngpio = PINS_PER_PORT; + platform_set_drvdata(pdev, <q_gpio_port[pdev->id]); + return gpiochip_add(<q_gpio_port[pdev->id].chip); + } +diff --git a/arch/mips/lantiq/xway/gpio_ebu.c b/arch/mips/lantiq/xway/gpio_ebu.c +index b91c7f1..bc5696b 100644 +--- a/arch/mips/lantiq/xway/gpio_ebu.c ++++ b/arch/mips/lantiq/xway/gpio_ebu.c +@@ -61,9 +61,8 @@ static struct gpio_chip ltq_ebu_chip = { + .label = "ltq_ebu", + .direction_output = ltq_ebu_direction_output, + .set = ltq_ebu_set, +- .base = 72, ++ .base = 100, + .ngpio = 16, +- .can_sleep = 1, + .owner = THIS_MODULE, + }; + +diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c +index da91c5e..9610c10 100644 +--- a/arch/mips/lantiq/xway/gpio_stp.c ++++ b/arch/mips/lantiq/xway/gpio_stp.c +@@ -74,9 +74,8 @@ static struct gpio_chip ltq_stp_chip = { + .label = "ltq_stp", + .direction_output = ltq_stp_direction_output, + .set = ltq_stp_set, +- .base = 48, ++ .base = 200, + .ngpio = 24, +- .can_sleep = 1, + .owner = THIS_MODULE, + }; + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0053-MIPS-lantiq-VPE-extensions.patch b/target/linux/lantiq/patches-3.3/0053-MIPS-lantiq-VPE-extensions.patch new file mode 100644 index 0000000000..34e3ac9483 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0053-MIPS-lantiq-VPE-extensions.patch @@ -0,0 +1,1221 @@ +From 948bb4dd94209332253d2b69c28f44e2bb11f518 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 29 Sep 2011 20:30:40 +0200 +Subject: [PATCH 53/70] MIPS: lantiq: VPE extensions + +--- + arch/mips/Kconfig | 22 +++ + arch/mips/include/asm/mipsmtregs.h | 54 +++++++ + arch/mips/kernel/Makefile | 3 +- + arch/mips/kernel/mips-mt.c | 97 +++++++++++-- + arch/mips/kernel/mtsched_proc.c | 279 ++++++++++++++++++++++++++++++++++++ + arch/mips/kernel/perf_proc.c | 191 ++++++++++++++++++++++++ + arch/mips/kernel/proc.c | 17 +++ + arch/mips/kernel/smtc.c | 7 + + arch/mips/kernel/vpe.c | 250 ++++++++++++++++++++++++++++++++- + 9 files changed, 905 insertions(+), 15 deletions(-) + create mode 100644 arch/mips/kernel/mtsched_proc.c + create mode 100644 arch/mips/kernel/perf_proc.c + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 0dd8ae3..4a36923 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1935,6 +1935,28 @@ config MIPS_VPE_LOADER + Includes a loader for loading an elf relocatable object + onto another VPE and running it. + ++config IFX_VPE_EXT ++ bool "IFX APRP Extensions" ++ depends on MIPS_VPE_LOADER ++ default y ++ help ++ IFX included extensions in APRP ++ ++config PERFCTRS ++ bool "34K Performance counters" ++ depends on MIPS_MT && PROC_FS ++ default n ++ help ++ 34K Performance counter through /proc ++ ++config MTSCHED ++ bool "Support mtsched priority configuration for TCs" ++ depends on MIPS_MT && PROC_FS ++ default y ++ help ++ Support for mtsched priority configuration for TCs through ++ /proc/mips/mtsched ++ + config MIPS_MT_SMTC_IM_BACKSTOP + bool "Use per-TC register bits as backstop for inhibited IM bits" + depends on MIPS_MT_SMTC +diff --git a/arch/mips/include/asm/mipsmtregs.h b/arch/mips/include/asm/mipsmtregs.h +index c9420aa..04bfb4b 100644 +--- a/arch/mips/include/asm/mipsmtregs.h ++++ b/arch/mips/include/asm/mipsmtregs.h +@@ -28,14 +28,34 @@ + #define read_c0_vpeconf0() __read_32bit_c0_register($1, 2) + #define write_c0_vpeconf0(val) __write_32bit_c0_register($1, 2, val) + ++#define read_c0_vpeconf1() __read_32bit_c0_register($1, 3) ++#define write_c0_vpeconf1(val) __write_32bit_c0_register($1, 3, val) ++ ++#define read_c0_vpeschedule() __read_32bit_c0_register($1, 5) ++#define write_c0_vpeschedule(val) __write_32bit_c0_register($1, 5, val) ++ ++#define read_c0_vpeschefback() __read_32bit_c0_register($1, 6) ++#define write_c0_vpeschefback(val) __write_32bit_c0_register($1, 6, val) ++ ++#define read_c0_vpeopt() __read_32bit_c0_register($1, 7) ++#define write_c0_vpeopt(val) __write_32bit_c0_register($1, 7, val) ++ + #define read_c0_tcstatus() __read_32bit_c0_register($2, 1) + #define write_c0_tcstatus(val) __write_32bit_c0_register($2, 1, val) + + #define read_c0_tcbind() __read_32bit_c0_register($2, 2) ++#define write_c0_tcbind(val) __write_32bit_c0_register($2, 2, val) + + #define read_c0_tccontext() __read_32bit_c0_register($2, 5) + #define write_c0_tccontext(val) __write_32bit_c0_register($2, 5, val) + ++#define read_c0_tcschedule() __read_32bit_c0_register($2, 6) ++#define write_c0_tcschedule(val) __write_32bit_c0_register($2, 6, val) ++ ++#define read_c0_tcschefback() __read_32bit_c0_register($2, 7) ++#define write_c0_tcschefback(val) __write_32bit_c0_register($2, 7, val) ++ ++ + #else /* Assembly */ + /* + * Macros for use in assembly language code +@@ -74,6 +94,8 @@ + #define MVPCONTROL_STLB_SHIFT 2 + #define MVPCONTROL_STLB (_ULCAST_(1) << MVPCONTROL_STLB_SHIFT) + ++#define MVPCONTROL_CPA_SHIFT 3 ++#define MVPCONTROL_CPA (_ULCAST_(1) << MVPCONTROL_CPA_SHIFT) + + /* MVPConf0 fields */ + #define MVPCONF0_PTC_SHIFT 0 +@@ -84,6 +106,8 @@ + #define MVPCONF0_TCA ( _ULCAST_(1) << MVPCONF0_TCA_SHIFT) + #define MVPCONF0_PTLBE_SHIFT 16 + #define MVPCONF0_PTLBE (_ULCAST_(0x3ff) << MVPCONF0_PTLBE_SHIFT) ++#define MVPCONF0_PCP_SHIFT 27 ++#define MVPCONF0_PCP (_ULCAST_(1) << MVPCONF0_PCP_SHIFT) + #define MVPCONF0_TLBS_SHIFT 29 + #define MVPCONF0_TLBS (_ULCAST_(1) << MVPCONF0_TLBS_SHIFT) + #define MVPCONF0_M_SHIFT 31 +@@ -121,9 +145,25 @@ + #define VPECONF0_VPA (_ULCAST_(1) << VPECONF0_VPA_SHIFT) + #define VPECONF0_MVP_SHIFT 1 + #define VPECONF0_MVP (_ULCAST_(1) << VPECONF0_MVP_SHIFT) ++#define VPECONF0_ICS_SHIFT 16 ++#define VPECONF0_ICS (_ULCAST_(1) << VPECONF0_ICS_SHIFT) ++#define VPECONF0_DCS_SHIFT 17 ++#define VPECONF0_DCS (_ULCAST_(1) << VPECONF0_DCS_SHIFT) + #define VPECONF0_XTC_SHIFT 21 + #define VPECONF0_XTC (_ULCAST_(0xff) << VPECONF0_XTC_SHIFT) + ++/* VPEOpt fields */ ++#define VPEOPT_DWX_SHIFT 0 ++#define VPEOPT_IWX_SHIFT 8 ++#define VPEOPT_IWX0 ( _ULCAST_(0x1) << VPEOPT_IWX_SHIFT) ++#define VPEOPT_IWX1 ( _ULCAST_(0x2) << VPEOPT_IWX_SHIFT) ++#define VPEOPT_IWX2 ( _ULCAST_(0x4) << VPEOPT_IWX_SHIFT) ++#define VPEOPT_IWX3 ( _ULCAST_(0x8) << VPEOPT_IWX_SHIFT) ++#define VPEOPT_DWX0 ( _ULCAST_(0x1) << VPEOPT_DWX_SHIFT) ++#define VPEOPT_DWX1 ( _ULCAST_(0x2) << VPEOPT_DWX_SHIFT) ++#define VPEOPT_DWX2 ( _ULCAST_(0x4) << VPEOPT_DWX_SHIFT) ++#define VPEOPT_DWX3 ( _ULCAST_(0x8) << VPEOPT_DWX_SHIFT) ++ + /* TCStatus fields (per TC) */ + #define TCSTATUS_TASID (_ULCAST_(0xff)) + #define TCSTATUS_IXMT_SHIFT 10 +@@ -350,6 +390,14 @@ do { \ + #define write_vpe_c0_vpecontrol(val) mttc0(1, 1, val) + #define read_vpe_c0_vpeconf0() mftc0(1, 2) + #define write_vpe_c0_vpeconf0(val) mttc0(1, 2, val) ++#define read_vpe_c0_vpeschedule() mftc0(1, 5) ++#define write_vpe_c0_vpeschedule(val) mttc0(1, 5, val) ++#define read_vpe_c0_vpeschefback() mftc0(1, 6) ++#define write_vpe_c0_vpeschefback(val) mttc0(1, 6, val) ++#define read_vpe_c0_vpeopt() mftc0(1, 7) ++#define write_vpe_c0_vpeopt(val) mttc0(1, 7, val) ++#define read_vpe_c0_wired() mftc0(6, 0) ++#define write_vpe_c0_wired(val) mttc0(6, 0, val) + #define read_vpe_c0_count() mftc0(9, 0) + #define write_vpe_c0_count(val) mttc0(9, 0, val) + #define read_vpe_c0_status() mftc0(12, 0) +@@ -381,6 +429,12 @@ do { \ + #define write_tc_c0_tchalt(val) mttc0(2, 4, val) + #define read_tc_c0_tccontext() mftc0(2, 5) + #define write_tc_c0_tccontext(val) mttc0(2, 5, val) ++#define read_tc_c0_tcschedule() mftc0(2, 6) ++#define write_tc_c0_tcschedule(val) mttc0(2, 6, val) ++#define read_tc_c0_tcschefback() mftc0(2, 7) ++#define write_tc_c0_tcschefback(val) mttc0(2, 7, val) ++#define read_tc_c0_entryhi() mftc0(10, 0) ++#define write_tc_c0_entryhi(val) mttc0(10, 0, val) + + /* GPR */ + #define read_tc_gpr_sp() mftgpr(29) +diff --git a/arch/mips/kernel/Makefile b/arch/mips/kernel/Makefile +index 0c6877e..4d75dd7 100644 +--- a/arch/mips/kernel/Makefile ++++ b/arch/mips/kernel/Makefile +@@ -90,7 +90,8 @@ obj-$(CONFIG_MIPS32_O32) += binfmt_elfo32.o scall64-o32.o + + obj-$(CONFIG_KGDB) += kgdb.o + obj-$(CONFIG_PROC_FS) += proc.o +- ++obj-$(CONFIG_MTSCHED) += mtsched_proc.o ++obj-$(CONFIG_PERFCTRS) += perf_proc.o + obj-$(CONFIG_64BIT) += cpu-bugs64.o + + obj-$(CONFIG_I8253) += i8253.o +diff --git a/arch/mips/kernel/mips-mt.c b/arch/mips/kernel/mips-mt.c +index c23d11f..11d6489 100644 +--- a/arch/mips/kernel/mips-mt.c ++++ b/arch/mips/kernel/mips-mt.c +@@ -21,26 +21,96 @@ + #include + + int vpelimit; +- + static int __init maxvpes(char *str) + { + get_option(&str, &vpelimit); +- + return 1; + } +- + __setup("maxvpes=", maxvpes); + + int tclimit; +- + static int __init maxtcs(char *str) + { + get_option(&str, &tclimit); ++ return 1; ++} ++__setup("maxtcs=", maxtcs); + ++#ifdef CONFIG_IFX_VPE_EXT ++int stlb; ++static int __init istlbshared(char *str) ++{ ++ get_option(&str, &stlb); + return 1; + } ++__setup("vpe_tlb_shared=", istlbshared); + +-__setup("maxtcs=", maxtcs); ++int vpe0_wired; ++static int __init vpe0wired(char *str) ++{ ++ get_option(&str, &vpe0_wired); ++ return 1; ++} ++__setup("vpe0_wired_tlb_entries=", vpe0wired); ++ ++int vpe1_wired; ++static int __init vpe1wired(char *str) ++{ ++ get_option(&str, &vpe1_wired); ++ return 1; ++} ++__setup("vpe1_wired_tlb_entries=", vpe1wired); ++ ++#ifdef CONFIG_MIPS_MT_SMTC ++extern int nostlb; ++#endif ++void configure_tlb(void) ++{ ++ int vpeflags, tcflags, tlbsiz; ++ unsigned int config1val; ++ vpeflags = dvpe(); ++ tcflags = dmt(); ++ write_c0_vpeconf0((read_c0_vpeconf0() | VPECONF0_MVP)); ++ write_c0_mvpcontrol((read_c0_mvpcontrol() | MVPCONTROL_VPC)); ++ mips_ihb(); ++ //printk("stlb = %d, vpe0_wired = %d vpe1_wired=%d\n", stlb,vpe0_wired, vpe1_wired); ++ if (stlb) { ++ if (!(read_c0_mvpconf0() & MVPCONF0_TLBS)) { ++ emt(tcflags); ++ evpe(vpeflags); ++ return; ++ } ++ ++ write_c0_mvpcontrol(read_c0_mvpcontrol() | MVPCONTROL_STLB); ++ write_c0_wired(vpe0_wired + vpe1_wired); ++ if (((read_vpe_c0_config() & MIPS_CONF_MT) >> 7) == 1) { ++ config1val = read_vpe_c0_config1(); ++ tlbsiz = (((config1val >> 25) & 0x3f) + 1); ++ if (tlbsiz > 64) ++ tlbsiz = 64; ++ cpu_data[0].tlbsize = tlbsiz; ++ current_cpu_data.tlbsize = tlbsiz; ++ } ++ ++ } ++ else { ++ write_c0_mvpcontrol(read_c0_mvpcontrol() & ~MVPCONTROL_STLB); ++ write_c0_wired(vpe0_wired); ++ } ++ ++ ehb(); ++ write_c0_mvpcontrol((read_c0_mvpcontrol() & ~MVPCONTROL_VPC)); ++ ehb(); ++ local_flush_tlb_all(); ++ ++ printk("Wired TLB entries for Linux read_c0_wired() = %d\n", read_c0_wired()); ++#ifdef CONFIG_MIPS_MT_SMTC ++ nostlb = !stlb; ++#endif ++ emt(tcflags); ++ evpe(vpeflags); ++} ++#endif + + /* + * Dump new MIPS MT state for the core. Does not leave TCs halted. +@@ -78,18 +148,18 @@ void mips_mt_regdump(unsigned long mvpctl) + if ((read_tc_c0_tcbind() & TCBIND_CURVPE) == i) { + printk(" VPE %d\n", i); + printk(" VPEControl : %08lx\n", +- read_vpe_c0_vpecontrol()); ++ read_vpe_c0_vpecontrol()); + printk(" VPEConf0 : %08lx\n", +- read_vpe_c0_vpeconf0()); ++ read_vpe_c0_vpeconf0()); + printk(" VPE%d.Status : %08lx\n", +- i, read_vpe_c0_status()); ++ i, read_vpe_c0_status()); + printk(" VPE%d.EPC : %08lx %pS\n", +- i, read_vpe_c0_epc(), +- (void *) read_vpe_c0_epc()); ++ i, read_vpe_c0_epc(), ++ (void *) read_vpe_c0_epc()); + printk(" VPE%d.Cause : %08lx\n", +- i, read_vpe_c0_cause()); ++ i, read_vpe_c0_cause()); + printk(" VPE%d.Config7 : %08lx\n", +- i, read_vpe_c0_config7()); ++ i, read_vpe_c0_config7()); + break; /* Next VPE */ + } + } +@@ -287,6 +357,9 @@ void mips_mt_set_cpuoptions(void) + printk("Mapped %ld ITC cells starting at 0x%08x\n", + ((itcblkgrn & 0x7fe00000) >> 20), itc_base); + } ++#ifdef CONFIG_IFX_VPE_EXT ++ configure_tlb(); ++#endif + } + + /* +diff --git a/arch/mips/kernel/mtsched_proc.c b/arch/mips/kernel/mtsched_proc.c +new file mode 100644 +index 0000000..4dafded +--- /dev/null ++++ b/arch/mips/kernel/mtsched_proc.c +@@ -0,0 +1,279 @@ ++/* ++ * /proc hooks for MIPS MT scheduling policy management for 34K cores ++ * ++ * This program is free software; you can distribute it and/or modify it ++ * under the terms of the GNU General Public License (Version 2) as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope it will be useful, but WITHOUT ++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or ++ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ++ * for more details. ++ * ++ * You should have received a copy of the GNU General Public License along ++ * with this program; if not, write to the Free Software Foundation, Inc., ++ * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. ++ * ++ * Copyright (C) 2006 Mips Technologies, Inc ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static struct proc_dir_entry *mtsched_proc; ++ ++#ifndef CONFIG_MIPS_MT_SMTC ++#define NTCS 2 ++#else ++#define NTCS NR_CPUS ++#endif ++#define NVPES 2 ++ ++int lastvpe = 1; ++int lasttc = 8; ++ ++static int proc_read_mtsched(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int totalen = 0; ++ int len; ++ ++ int i; ++ int vpe; ++ int mytc; ++ unsigned long flags; ++ unsigned int mtflags; ++ unsigned int haltstate; ++ unsigned int vpes_checked[NVPES]; ++ unsigned int vpeschedule[NVPES]; ++ unsigned int vpeschefback[NVPES]; ++ unsigned int tcschedule[NTCS]; ++ unsigned int tcschefback[NTCS]; ++ ++ /* Dump the state of the MIPS MT scheduling policy manager */ ++ /* Inititalize control state */ ++ for(i = 0; i < NVPES; i++) { ++ vpes_checked[i] = 0; ++ vpeschedule[i] = 0; ++ vpeschefback[i] = 0; ++ } ++ for(i = 0; i < NTCS; i++) { ++ tcschedule[i] = 0; ++ tcschefback[i] = 0; ++ } ++ ++ /* Disable interrupts and multithreaded issue */ ++ local_irq_save(flags); ++ mtflags = dvpe(); ++ ++ /* Then go through the TCs, halt 'em, and extract the values */ ++ mytc = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT; ++ for(i = 0; i < NTCS; i++) { ++ if(i == mytc) { ++ /* No need to halt ourselves! */ ++ tcschedule[i] = read_c0_tcschedule(); ++ tcschefback[i] = read_c0_tcschefback(); ++ /* If VPE bound to TC hasn't been checked, do it */ ++ vpe = read_c0_tcbind() & TCBIND_CURVPE; ++ if(!vpes_checked[vpe]) { ++ vpeschedule[vpe] = read_c0_vpeschedule(); ++ vpeschefback[vpe] = read_c0_vpeschefback(); ++ vpes_checked[vpe] = 1; ++ } ++ } else { ++ settc(i); ++ haltstate = read_tc_c0_tchalt(); ++ write_tc_c0_tchalt(TCHALT_H); ++ mips_ihb(); ++ tcschedule[i] = read_tc_c0_tcschedule(); ++ tcschefback[i] = read_tc_c0_tcschefback(); ++ /* If VPE bound to TC hasn't been checked, do it */ ++ vpe = read_tc_c0_tcbind() & TCBIND_CURVPE; ++ if(!vpes_checked[vpe]) { ++ vpeschedule[vpe] = read_vpe_c0_vpeschedule(); ++ vpeschefback[vpe] = read_vpe_c0_vpeschefback(); ++ vpes_checked[vpe] = 1; ++ } ++ if(!haltstate) write_tc_c0_tchalt(0); ++ } ++ } ++ /* Re-enable MT and interrupts */ ++ evpe(mtflags); ++ local_irq_restore(flags); ++ ++ for(vpe=0; vpe < NVPES; vpe++) { ++ len = sprintf(page, "VPE[%d].VPEschedule = 0x%08x\n", ++ vpe, vpeschedule[vpe]); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "VPE[%d].VPEschefback = 0x%08x\n", ++ vpe, vpeschefback[vpe]); ++ totalen += len; ++ page += len; ++ } ++ for(i=0; i < NTCS; i++) { ++ len = sprintf(page, "TC[%d].TCschedule = 0x%08x\n", ++ i, tcschedule[i]); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "TC[%d].TCschefback = 0x%08x\n", ++ i, tcschefback[i]); ++ totalen += len; ++ page += len; ++ } ++ return totalen; ++} ++ ++/* ++ * Write to perf counter registers based on text input ++ */ ++ ++#define TXTBUFSZ 100 ++ ++static int proc_write_mtsched(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ int len = 0; ++ char mybuf[TXTBUFSZ]; ++ /* At most, we will set up 9 TCs and 2 VPEs, 11 entries in all */ ++ char entity[1]; //, entity1[1]; ++ int number[1]; ++ unsigned long value[1]; ++ int nparsed = 0 , index = 0; ++ unsigned long flags; ++ unsigned int mtflags; ++ unsigned int haltstate; ++ unsigned int tcbindval; ++ ++ if(count >= TXTBUFSZ) len = TXTBUFSZ-1; ++ else len = count; ++ memset(mybuf,0,TXTBUFSZ); ++ if(copy_from_user(mybuf, buffer, len)) return -EFAULT; ++ ++ nparsed = sscanf(mybuf, "%c%d %lx", ++ &entity[0] ,&number[0], &value[0]); ++ ++ /* ++ * Having acquired the inputs, which might have ++ * generated exceptions and preemptions, ++ * program the registers. ++ */ ++ /* Disable interrupts and multithreaded issue */ ++ local_irq_save(flags); ++ mtflags = dvpe(); ++ ++ if(entity[index] == 't' ) { ++ /* Set TCSchedule or TCScheFBack of specified TC */ ++ if(number[index] > NTCS) goto skip; ++ /* If it's our own TC, do it direct */ ++ if(number[index] == ++ ((read_c0_tcbind() & TCBIND_CURTC) ++ >> TCBIND_CURTC_SHIFT)) { ++ if(entity[index] == 't') ++ write_c0_tcschedule(value[index]); ++ else ++ write_c0_tcschefback(value[index]); ++ } else { ++ /* Otherwise, we do it via MTTR */ ++ settc(number[index]); ++ haltstate = read_tc_c0_tchalt(); ++ write_tc_c0_tchalt(TCHALT_H); ++ mips_ihb(); ++ if(entity[index] == 't') ++ write_tc_c0_tcschedule(value[index]); ++ else ++ write_tc_c0_tcschefback(value[index]); ++ mips_ihb(); ++ if(!haltstate) write_tc_c0_tchalt(0); ++ } ++ } else if(entity[index] == 'v') { ++ /* Set VPESchedule of specified VPE */ ++ if(number[index] > NVPES) goto skip; ++ tcbindval = read_c0_tcbind(); ++ /* Are we doing this to our current VPE? */ ++ if((tcbindval & TCBIND_CURVPE) == number[index]) { ++ /* Then life is simple */ ++ write_c0_vpeschedule(value[index]); ++ } else { ++ /* ++ * Bind ourselves to the other VPE long enough ++ * to program the bind value. ++ */ ++ write_c0_tcbind((tcbindval & ~TCBIND_CURVPE) ++ | number[index]); ++ mips_ihb(); ++ write_c0_vpeschedule(value[index]); ++ mips_ihb(); ++ /* Restore previous binding */ ++ write_c0_tcbind(tcbindval); ++ mips_ihb(); ++ } ++ } ++ ++ else if(entity[index] == 'r') { ++ unsigned int vpes_checked[2], vpe ,i , mytc; ++ vpes_checked[0] = vpes_checked[1] = 0; ++ ++ /* Then go through the TCs, halt 'em, and extract the values */ ++ mytc = (read_c0_tcbind() & TCBIND_CURTC) >> TCBIND_CURTC_SHIFT; ++ ++ for(i = 0; i < NTCS; i++) { ++ if(i == mytc) { ++ /* No need to halt ourselves! */ ++ write_c0_vpeschefback(0); ++ write_c0_tcschefback(0); ++ } else { ++ settc(i); ++ haltstate = read_tc_c0_tchalt(); ++ write_tc_c0_tchalt(TCHALT_H); ++ mips_ihb(); ++ write_tc_c0_tcschefback(0); ++ /* If VPE bound to TC hasn't been checked, do it */ ++ vpe = read_tc_c0_tcbind() & TCBIND_CURVPE; ++ if(!vpes_checked[vpe]) { ++ write_vpe_c0_vpeschefback(0); ++ vpes_checked[vpe] = 1; ++ } ++ if(!haltstate) write_tc_c0_tchalt(0); ++ } ++ } ++ } ++ else { ++ printk ("\n Usage : <0/1> \n Example : t0 0x01\n"); ++ } ++ ++skip: ++ /* Re-enable MT and interrupts */ ++ evpe(mtflags); ++ local_irq_restore(flags); ++ return (len); ++} ++ ++static int __init init_mtsched_proc(void) ++{ ++ extern struct proc_dir_entry *get_mips_proc_dir(void); ++ struct proc_dir_entry *mips_proc_dir; ++ ++ if (!cpu_has_mipsmt) { ++ printk("mtsched: not a MIPS MT capable processor\n"); ++ return -ENODEV; ++ } ++ ++ mips_proc_dir = get_mips_proc_dir(); ++ ++ mtsched_proc = create_proc_entry("mtsched", 0644, mips_proc_dir); ++ mtsched_proc->read_proc = proc_read_mtsched; ++ mtsched_proc->write_proc = proc_write_mtsched; ++ ++ return 0; ++} ++ ++/* Automagically create the entry */ ++module_init(init_mtsched_proc); +diff --git a/arch/mips/kernel/perf_proc.c b/arch/mips/kernel/perf_proc.c +new file mode 100644 +index 0000000..7eec015 +--- /dev/null ++++ b/arch/mips/kernel/perf_proc.c +@@ -0,0 +1,191 @@ ++/* ++ * /proc hooks for CPU performance counter support for SMTC kernel ++ * (and ultimately others) ++ * Copyright (C) 2006 Mips Technologies, Inc ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* ++ * /proc diagnostic and statistics hooks ++ */ ++ ++ ++/* Internal software-extended event counters */ ++ ++static unsigned long long extencount[4] = {0,0,0,0}; ++ ++static struct proc_dir_entry *perf_proc; ++ ++static int proc_read_perf(char *page, char **start, off_t off, ++ int count, int *eof, void *data) ++{ ++ int totalen = 0; ++ int len; ++ ++ len = sprintf(page, "PerfCnt[0].Ctl : 0x%08x\n", read_c0_perfctrl0()); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[0].Cnt : %Lu\n", ++ extencount[0] + (unsigned long long)((unsigned)read_c0_perfcntr0())); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[1].Ctl : 0x%08x\n", read_c0_perfctrl1()); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[1].Cnt : %Lu\n", ++ extencount[1] + (unsigned long long)((unsigned)read_c0_perfcntr1())); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[2].Ctl : 0x%08x\n", read_c0_perfctrl2()); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[2].Cnt : %Lu\n", ++ extencount[2] + (unsigned long long)((unsigned)read_c0_perfcntr2())); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[3].Ctl : 0x%08x\n", read_c0_perfctrl3()); ++ totalen += len; ++ page += len; ++ len = sprintf(page, "PerfCnt[3].Cnt : %Lu\n", ++ extencount[3] + (unsigned long long)((unsigned)read_c0_perfcntr3())); ++ totalen += len; ++ page += len; ++ ++ return totalen; ++} ++ ++/* ++ * Write to perf counter registers based on text input ++ */ ++ ++#define TXTBUFSZ 100 ++ ++static int proc_write_perf(struct file *file, const char *buffer, ++ unsigned long count, void *data) ++{ ++ int len; ++ int nparsed; ++ int index; ++ char mybuf[TXTBUFSZ]; ++ ++ int which[4]; ++ unsigned long control[4]; ++ long long ctrdata[4]; ++ ++ if(count >= TXTBUFSZ) len = TXTBUFSZ-1; ++ else len = count; ++ memset(mybuf,0,TXTBUFSZ); ++ if(copy_from_user(mybuf, buffer, len)) return -EFAULT; ++ ++ nparsed = sscanf(mybuf, ++ "%d %lx %Ld %d %lx %Ld %d %lx %Ld %d %lx %Ld", ++ &which[0], &control[0], &ctrdata[0], ++ &which[1], &control[1], &ctrdata[1], ++ &which[2], &control[2], &ctrdata[2], ++ &which[3], &control[3], &ctrdata[3]); ++ ++ for(index = 0; nparsed >= 3; index++) { ++ switch (which[index]) { ++ case 0: ++ write_c0_perfctrl0(control[index]); ++ if(ctrdata[index] != -1) { ++ extencount[0] = (unsigned long long)ctrdata[index]; ++ write_c0_perfcntr0((unsigned long)0); ++ } ++ break; ++ case 1: ++ write_c0_perfctrl1(control[index]); ++ if(ctrdata[index] != -1) { ++ extencount[1] = (unsigned long long)ctrdata[index]; ++ write_c0_perfcntr1((unsigned long)0); ++ } ++ break; ++ case 2: ++ write_c0_perfctrl2(control[index]); ++ if(ctrdata[index] != -1) { ++ extencount[2] = (unsigned long long)ctrdata[index]; ++ write_c0_perfcntr2((unsigned long)0); ++ } ++ break; ++ case 3: ++ write_c0_perfctrl3(control[index]); ++ if(ctrdata[index] != -1) { ++ extencount[3] = (unsigned long long)ctrdata[index]; ++ write_c0_perfcntr3((unsigned long)0); ++ } ++ break; ++ } ++ nparsed -= 3; ++ } ++ return (len); ++} ++ ++extern int (*perf_irq)(void); ++ ++/* ++ * Invoked when timer interrupt vector picks up a perf counter overflow ++ */ ++ ++static int perf_proc_irq(void) ++{ ++ unsigned long snapshot; ++ ++ /* ++ * It would be nice to do this as a loop, but we don't have ++ * indirect access to CP0 registers. ++ */ ++ snapshot = read_c0_perfcntr0(); ++ if ((long)snapshot < 0) { ++ extencount[0] += ++ (unsigned long long)((unsigned)read_c0_perfcntr0()); ++ write_c0_perfcntr0(0); ++ } ++ snapshot = read_c0_perfcntr1(); ++ if ((long)snapshot < 0) { ++ extencount[1] += ++ (unsigned long long)((unsigned)read_c0_perfcntr1()); ++ write_c0_perfcntr1(0); ++ } ++ snapshot = read_c0_perfcntr2(); ++ if ((long)snapshot < 0) { ++ extencount[2] += ++ (unsigned long long)((unsigned)read_c0_perfcntr2()); ++ write_c0_perfcntr2(0); ++ } ++ snapshot = read_c0_perfcntr3(); ++ if ((long)snapshot < 0) { ++ extencount[3] += ++ (unsigned long long)((unsigned)read_c0_perfcntr3()); ++ write_c0_perfcntr3(0); ++ } ++ return 0; ++} ++ ++static int __init init_perf_proc(void) ++{ ++ extern struct proc_dir_entry *get_mips_proc_dir(void); ++ ++ struct proc_dir_entry *mips_proc_dir = get_mips_proc_dir(); ++ ++ write_c0_perfcntr0(0); ++ write_c0_perfcntr1(0); ++ write_c0_perfcntr2(0); ++ write_c0_perfcntr3(0); ++ perf_proc = create_proc_entry("perf", 0644, mips_proc_dir); ++ perf_proc->read_proc = proc_read_perf; ++ perf_proc->write_proc = proc_write_perf; ++ perf_irq = perf_proc_irq; ++ ++ return 0; ++} ++ ++/* Automagically create the entry */ ++module_init(init_perf_proc); +diff --git a/arch/mips/kernel/proc.c b/arch/mips/kernel/proc.c +index e309665..2de204f 100644 +--- a/arch/mips/kernel/proc.c ++++ b/arch/mips/kernel/proc.c +@@ -7,6 +7,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -110,3 +111,19 @@ const struct seq_operations cpuinfo_op = { + .stop = c_stop, + .show = show_cpuinfo, + }; ++ ++/* ++ * Support for MIPS/local /proc hooks in /proc/mips/ ++ */ ++ ++static struct proc_dir_entry *mips_proc = NULL; ++ ++struct proc_dir_entry *get_mips_proc_dir(void) ++{ ++ /* ++ * This ought not to be preemptable. ++ */ ++ if(mips_proc == NULL) ++ mips_proc = proc_mkdir("mips", NULL); ++ return(mips_proc); ++} +diff --git a/arch/mips/kernel/smtc.c b/arch/mips/kernel/smtc.c +index 0a42ff3..41f5258 100644 +--- a/arch/mips/kernel/smtc.c ++++ b/arch/mips/kernel/smtc.c +@@ -1334,6 +1334,13 @@ void smtc_get_new_mmu_context(struct mm_struct *mm, unsigned long cpu) + asid = asid_cache(cpu); + + do { ++#ifdef CONFIG_IFX_VPE_EXT ++ /* If TLB is shared between AP and RP (AP is running SMTC), ++ leave out max ASID i.e., ASID_MASK for RP ++ */ ++ if (!nostlb && ((asid & ASID_MASK) == (ASID_MASK - 1))) ++ asid++; ++#endif + if (!((asid += ASID_INC) & ASID_MASK) ) { + if (cpu_has_vtag_icache) + flush_icache_all(); +diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c +index bfa12a4..e338ba5 100644 +--- a/arch/mips/kernel/vpe.c ++++ b/arch/mips/kernel/vpe.c +@@ -75,6 +75,58 @@ static struct kspd_notifications kspd_events; + static int kspd_events_reqd; + #endif + ++#ifdef CONFIG_IFX_VPE_EXT ++static int is_sdepgm; ++extern int stlb; ++extern int vpe0_wired; ++extern int vpe1_wired; ++unsigned int vpe1_load_addr; ++ ++static int __init load_address(char *str) ++{ ++ get_option(&str, &vpe1_load_addr); ++ return 1; ++} ++__setup("vpe1_load_addr=", load_address); ++ ++#include ++#define write_vpe_c0_wired(val) mttc0(6, 0, val) ++ ++#ifndef COMMAND_LINE_SIZE ++# define COMMAND_LINE_SIZE 512 ++#endif ++ ++char command_line[COMMAND_LINE_SIZE * 2]; ++ ++static unsigned int vpe1_mem; ++static int __init vpe1mem(char *str) ++{ ++ vpe1_mem = memparse(str, &str); ++ return 1; ++} ++__setup("vpe1_mem=", vpe1mem); ++ ++uint32_t vpe1_wdog_ctr; ++static int __init wdog_ctr(char *str) ++{ ++ get_option(&str, &vpe1_wdog_ctr); ++ return 1; ++} ++ ++__setup("vpe1_wdog_ctr_addr=", wdog_ctr); ++EXPORT_SYMBOL(vpe1_wdog_ctr); ++ ++uint32_t vpe1_wdog_timeout; ++static int __init wdog_timeout(char *str) ++{ ++ get_option(&str, &vpe1_wdog_timeout); ++ return 1; ++} ++ ++__setup("vpe1_wdog_timeout=", wdog_timeout); ++EXPORT_SYMBOL(vpe1_wdog_timeout); ++ ++#endif + /* grab the likely amount of memory we will need. */ + #ifdef CONFIG_MIPS_VPE_LOADER_TOM + #define P_SIZE (2 * 1024 * 1024) +@@ -267,6 +319,13 @@ static void *alloc_progmem(unsigned long len) + void *addr; + + #ifdef CONFIG_MIPS_VPE_LOADER_TOM ++#ifdef CONFIG_IFX_VPE_EXT ++ if (vpe1_load_addr) { ++ memset((void *)vpe1_load_addr, 0, len); ++ return (void *)vpe1_load_addr; ++ } ++#endif ++ + /* + * This means you must tell Linux to use less memory than you + * physically have, for example by passing a mem= boot argument. +@@ -745,6 +804,12 @@ static int vpe_run(struct vpe * v) + } + + /* Write the address we want it to start running from in the TCPC register. */ ++#if defined(CONFIG_IFX_VPE_EXT) && 0 ++ if (stlb) ++ write_vpe_c0_wired(vpe0_wired + vpe1_wired); ++ else ++ write_vpe_c0_wired(vpe1_wired); ++#endif + write_tc_c0_tcrestart((unsigned long)v->__start); + write_tc_c0_tccontext((unsigned long)0); + +@@ -758,6 +823,20 @@ static int vpe_run(struct vpe * v) + + write_tc_c0_tchalt(read_tc_c0_tchalt() & ~TCHALT_H); + ++#if defined(CONFIG_IFX_VPE_EXT) && 0 ++ /* ++ * $a2 & $a3 are used to pass command line parameters to VPE1. $a2 ++ * points to the start of the command line string and $a3 points to ++ * the end of the string. This convention is identical to the Linux ++ * kernel boot parameter passing mechanism. Please note that $a3 is ++ * used to pass physical memory size or 0 in SDE tool kit. So, if you ++ * are passing comand line parameters through $a2 & $a3 SDE programs ++ * don't work as desired. ++ */ ++ mttgpr(6, command_line); ++ mttgpr(7, (command_line + strlen(command_line))); ++ if (is_sdepgm) ++#endif + /* + * The sde-kit passes 'memsize' to __start in $a3, so set something + * here... Or set $a3 to zero and define DFLT_STACK_SIZE and +@@ -832,6 +911,9 @@ static int find_vpe_symbols(struct vpe * v, Elf_Shdr * sechdrs, + if ( (v->__start == 0) || (v->shared_ptr == NULL)) + return -1; + ++#ifdef CONFIG_IFX_VPE_EXT ++ is_sdepgm = 1; ++#endif + return 0; + } + +@@ -993,6 +1075,15 @@ static int vpe_elfload(struct vpe * v) + (unsigned long)v->load_addr + v->len); + + if ((find_vpe_symbols(v, sechdrs, symindex, strtab, &mod)) < 0) { ++#ifdef CONFIG_IFX_VPE_EXT ++ if (vpe1_load_addr) { ++ /* Conversion to KSEG1 is required ??? */ ++ v->__start = KSEG1ADDR(vpe1_load_addr); ++ is_sdepgm = 0; ++ return 0; ++ } ++#endif ++ + if (v->__start == 0) { + printk(KERN_WARNING "VPE loader: program does not contain " + "a __start symbol\n"); +@@ -1063,6 +1154,9 @@ static int vpe_open(struct inode *inode, struct file *filp) + struct vpe_notifications *not; + struct vpe *v; + int ret; ++#ifdef CONFIG_IFX_VPE_EXT ++ int progsize; ++#endif + + if (minor != iminor(inode)) { + /* assume only 1 device at the moment. */ +@@ -1088,7 +1182,12 @@ static int vpe_open(struct inode *inode, struct file *filp) + release_progmem(v->load_addr); + cleanup_tc(get_tc(tclimit)); + } +- ++#ifdef CONFIG_IFX_VPE_EXT ++ progsize = (vpe1_mem != 0) ? vpe1_mem : P_SIZE; ++ //printk("progsize = %x\n", progsize); ++ v->pbuffer = vmalloc(progsize); ++ v->plen = progsize; ++#else + /* this of-course trashes what was there before... */ + v->pbuffer = vmalloc(P_SIZE); + if (!v->pbuffer) { +@@ -1096,11 +1195,14 @@ static int vpe_open(struct inode *inode, struct file *filp) + return -ENOMEM; + } + v->plen = P_SIZE; ++#endif + v->load_addr = NULL; + v->len = 0; + ++#if 0 + v->uid = filp->f_cred->fsuid; + v->gid = filp->f_cred->fsgid; ++#endif + + #ifdef CONFIG_MIPS_APSP_KSPD + /* get kspd to tell us when a syscall_exit happens */ +@@ -1348,6 +1450,133 @@ static void kspd_sp_exit( int sp_id) + cleanup_tc(get_tc(sp_id)); + } + #endif ++#ifdef CONFIG_IFX_VPE_EXT ++int32_t vpe1_sw_start(void* sw_start_addr, uint32_t tcmask, uint32_t flags) ++{ ++ enum vpe_state state; ++ struct vpe *v = get_vpe(tclimit); ++ struct vpe_notifications *not; ++ ++ if (tcmask || flags) { ++ printk(KERN_WARNING "Currently tcmask and flags should be 0.\ ++ other values not supported\n"); ++ return -1; ++ } ++ ++ state = xchg(&v->state, VPE_STATE_INUSE); ++ if (state != VPE_STATE_UNUSED) { ++ vpe_stop(v); ++ ++ list_for_each_entry(not, &v->notify, list) { ++ not->stop(tclimit); ++ } ++ } ++ ++ v->__start = (unsigned long)sw_start_addr; ++ is_sdepgm = 0; ++ ++ if (!vpe_run(v)) { ++ printk(KERN_DEBUG "VPE loader: VPE1 running successfully\n"); ++ return 0; ++ } ++ return -1; ++} ++ ++EXPORT_SYMBOL(vpe1_sw_start); ++ ++int32_t vpe1_sw_stop(uint32_t flags) ++{ ++ struct vpe *v = get_vpe(tclimit); ++ ++ if (!vpe_free(v)) { ++ printk(KERN_DEBUG "RP Stopped\n"); ++ return 0; ++ } ++ else ++ return -1; ++} ++ ++EXPORT_SYMBOL(vpe1_sw_stop); ++ ++uint32_t vpe1_get_load_addr (uint32_t flags) ++{ ++ return vpe1_load_addr; ++} ++ ++EXPORT_SYMBOL(vpe1_get_load_addr); ++ ++uint32_t vpe1_get_max_mem (uint32_t flags) ++{ ++ if (!vpe1_mem) ++ return P_SIZE; ++ else ++ return vpe1_mem; ++} ++ ++EXPORT_SYMBOL(vpe1_get_max_mem); ++ ++void* vpe1_get_cmdline_argument(void) ++{ ++ return saved_command_line; ++} ++ ++EXPORT_SYMBOL(vpe1_get_cmdline_argument); ++ ++int32_t vpe1_set_boot_param(char *field, char *value, char flags) ++{ ++ char *ptr, string[64]; ++ int start_off, end_off; ++ if (!field) ++ return -1; ++ strcpy(string, field); ++ if (value) { ++ strcat(string, "="); ++ strcat(string, value); ++ strcat(command_line, " "); ++ strcat(command_line, string); ++ } ++ else { ++ ptr = strstr(command_line, string); ++ if (ptr) { ++ start_off = ptr - command_line; ++ ptr += strlen(string); ++ while ((*ptr != ' ') && (*ptr != '\0')) ++ ptr++; ++ end_off = ptr - command_line; ++ command_line[start_off] = '\0'; ++ strcat (command_line, command_line+end_off); ++ } ++ } ++ return 0; ++} ++ ++EXPORT_SYMBOL(vpe1_set_boot_param); ++ ++int32_t vpe1_get_boot_param(char *field, char **value, char flags) ++{ ++ char *ptr, string[64]; ++ int i = 0; ++ if (!field) ++ return -1; ++ if ((ptr = strstr(command_line, field))) { ++ ptr += strlen(field) + 1; /* including = */ ++ while ((*ptr != ' ') && (*ptr != '\0')) ++ string[i++] = *ptr++; ++ string[i] = '\0'; ++ *value = kmalloc((strlen(string) + 1), GFP_KERNEL); ++ if (*value != NULL) ++ strcpy(*value, string); ++ } ++ else ++ *value = NULL; ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL(vpe1_get_boot_param); ++ ++extern void configure_tlb(void); ++#endif + + static ssize_t store_kill(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) +@@ -1429,6 +1658,18 @@ static int __init vpe_module_init(void) + printk("VPE loader: not a MIPS MT capable processor\n"); + return -ENODEV; + } ++#ifdef CONFIG_IFX_VPE_EXT ++#ifndef CONFIG_MIPS_MT_SMTC ++ configure_tlb(); ++#endif ++#endif ++ ++#ifndef CONFIG_MIPS_MT_SMTC ++ if (!vpelimit) ++ vpelimit = 1; ++ if (!tclimit) ++ tclimit = 1; ++#endif + + if (vpelimit == 0) { + printk(KERN_WARNING "No VPEs reserved for AP/SP, not " +@@ -1473,10 +1714,12 @@ static int __init vpe_module_init(void) + mtflags = dmt(); + vpflags = dvpe(); + ++ back_to_back_c0_hazard(); ++ + /* Put MVPE's into 'configuration state' */ + set_c0_mvpcontrol(MVPCONTROL_VPC); + +- /* dump_mtregs(); */ ++ dump_mtregs(); + + val = read_c0_mvpconf0(); + hw_tcs = (val & MVPCONF0_PTC) + 1; +@@ -1488,6 +1731,7 @@ static int __init vpe_module_init(void) + * reschedule send IPIs or similar we might hang. + */ + clear_c0_mvpcontrol(MVPCONTROL_VPC); ++ back_to_back_c0_hazard(); + evpe(vpflags); + emt(mtflags); + local_irq_restore(flags); +@@ -1513,6 +1757,7 @@ static int __init vpe_module_init(void) + } + + v->ntcs = hw_tcs - tclimit; ++ write_tc_c0_tcbind((read_tc_c0_tcbind() & ~TCBIND_CURVPE) | 1); + + /* add the tc to the list of this vpe's tc's. */ + list_add(&t->tc, &v->tc); +@@ -1581,6 +1826,7 @@ static int __init vpe_module_init(void) + out_reenable: + /* release config state */ + clear_c0_mvpcontrol(MVPCONTROL_VPC); ++ back_to_back_c0_hazard(); + + evpe(vpflags); + emt(mtflags); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0054-MIPS-lantiq-falcon-VPE-softdog.patch b/target/linux/lantiq/patches-3.3/0054-MIPS-lantiq-falcon-VPE-softdog.patch new file mode 100644 index 0000000000..18dced7da4 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0054-MIPS-lantiq-falcon-VPE-softdog.patch @@ -0,0 +1,180 @@ +From b7a07d349a9a05ac39f5d359a86519d93395b729 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 29 Sep 2011 21:29:14 +0200 +Subject: [PATCH 54/70] MIPS: lantiq: falcon VPE softdog + +--- + arch/mips/include/asm/mach-lantiq/falcon/vpe.h | 44 ++++++++++ + arch/mips/lantiq/falcon/softdog_vpe.c | 109 ++++++++++++++++++++++++ + 2 files changed, 153 insertions(+), 0 deletions(-) + create mode 100644 arch/mips/include/asm/mach-lantiq/falcon/vpe.h + create mode 100644 arch/mips/lantiq/falcon/softdog_vpe.c + +diff --git a/arch/mips/include/asm/mach-lantiq/falcon/vpe.h b/arch/mips/include/asm/mach-lantiq/falcon/vpe.h +new file mode 100644 +index 0000000..22a701b +--- /dev/null ++++ b/arch/mips/include/asm/mach-lantiq/falcon/vpe.h +@@ -0,0 +1,44 @@ ++/* ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Copyright (C) 2005 infineon ++ * Copyright (C) 2007 John Crispin ++ * ++ */ ++#ifndef _IFXMIPS_VPE_H__ ++#define _IFXMIPS_VPE_H__ ++ ++/* For the explanation of the APIs please refer the section "MT APRP Kernel ++ * Programming" in AR9 SW Architecture Specification ++ */ ++int32_t vpe1_sw_start(void* sw_start_addr, uint32_t tcmask, uint32_t flags); ++int32_t vpe1_sw_stop(uint32_t flags); ++uint32_t vpe1_get_load_addr (uint32_t flags); ++uint32_t vpe1_get_max_mem (uint32_t flags); ++ ++int32_t vpe1_set_boot_param(char *field, char *value, char flags); ++int32_t vpe1_get_boot_param(char *field, char **value, char flags); ++ ++/* Watchdog APIs */ ++extern unsigned long vpe1_wdog_ctr; ++extern unsigned long vpe1_wdog_timeout; ++ ++unsigned long vpe1_sw_wdog_start(unsigned long); ++unsigned long vpe1_sw_wdog_stop(unsigned long); ++ ++typedef int (*VPE_SW_WDOG_RESET)(unsigned long wdog_cleared_ok_count); ++int32_t vpe1_sw_wdog_register_reset_handler(VPE_SW_WDOG_RESET reset_fn); ++ ++#endif +diff --git a/arch/mips/lantiq/falcon/softdog_vpe.c b/arch/mips/lantiq/falcon/softdog_vpe.c +new file mode 100644 +index 0000000..85d22a2 +--- /dev/null ++++ b/arch/mips/lantiq/falcon/softdog_vpe.c +@@ -0,0 +1,109 @@ ++/* ++** ============================================================================= ++** FILE NAME : softdog_vpe.c ++** MODULES : LXDB ++** DATE : 24-03-2008 ++** AUTHOR : LXDB Team ++** DESCRIPTION : This header file contains the code for the watchdog ++** implentation on vpe1 side. ++** REFERENCES : ++** COPYRIGHT : Copyright (c) 2008 ++** Am Campeon 1-12, 85579 Neubiberg, Germany ++** Any use of this software is subject to the conclusion of a respective ++** License agreement. Without such a License agreement no rights to the ++** software are granted ++** ++** HISTORY : ++** $Date $Author $Comment ++** 24-03-2008 LXDB Initial version ++** ============================================================================ ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++ ++static unsigned long last_wdog_value; ++static unsigned long vpe1_wdog_cleared; ++ ++static unsigned long vpe1_wdog_dead; ++static void watchdog_vpe0_fire(unsigned long); /* Called when vpe0 timer expires */ ++static void keep_alive_vpe0(unsigned long); ++VPE_SW_WDOG_RESET reset_local_fn; ++ ++ ++static struct timer_list watchdog_vpe0_ticktock = ++ TIMER_INITIALIZER(watchdog_vpe0_fire, 0, 0); ++ ++static void watchdog_vpe0_fire (unsigned long flags) ++{ ++ volatile unsigned long *wdog_ctr_value; ++ wdog_ctr_value = (void*)vpe1_wdog_ctr; ++ if (*wdog_ctr_value == last_wdog_value) { /* VPE1 watchdog expiry handling */ ++ vpe1_sw_wdog_stop(flags); ++ vpe1_wdog_dead++; ++ printk(KERN_DEBUG "VPE1 watchdog reset handler called\n"); ++ /* Call the reset handler function */ ++ reset_local_fn(flags); ++ } else { /* Everything is OK on vpe1 side. Continue. */ ++ last_wdog_value = *wdog_ctr_value; ++ vpe1_wdog_cleared++; ++ keep_alive_vpe0(flags); ++ } ++} ++ ++int32_t vpe1_sw_wdog_register_reset_handler (VPE_SW_WDOG_RESET reset_fn) ++{ ++ reset_local_fn = (VPE_SW_WDOG_RESET)reset_fn; ++ return 0; ++} ++ ++static void keep_alive_vpe0(unsigned long flags) ++{ ++ mod_timer(&watchdog_vpe0_ticktock, jiffies+ vpe1_wdog_timeout ); ++} ++ ++unsigned long vpe1_sw_wdog_start(unsigned long flags) ++{ ++ volatile unsigned long *wdog_ctr_value; ++ wdog_ctr_value = (void*)vpe1_wdog_ctr; ++ *wdog_ctr_value = 0; ++ last_wdog_value = 0; ++ keep_alive_vpe0(flags); ++ return 0; ++} ++ ++unsigned long vpe1_sw_wdog_stop(unsigned long flags) ++{ ++ del_timer(&watchdog_vpe0_ticktock); ++ return 0; ++} ++ ++static int __init watchdog_vpe1_init(void) ++{ ++ /* Nothing to be done here */ ++ return 0; ++} ++ ++static void __exit watchdog_vpe1_exit(void) ++{ ++ unsigned long flags=0; ++ vpe1_sw_wdog_stop(flags); ++} ++ ++module_init(watchdog_vpe1_init); ++module_exit(watchdog_vpe1_exit); ++ ++EXPORT_SYMBOL(vpe1_sw_wdog_register_reset_handler); ++EXPORT_SYMBOL(vpe1_sw_wdog_start); ++EXPORT_SYMBOL(vpe1_sw_wdog_stop); ++ ++MODULE_AUTHOR("LXDB"); ++MODULE_DESCRIPTION("Software Watchdog For VPE1"); ++MODULE_LICENSE("GPL"); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0055-MIPS-lantiq-udp-in-kernel-redirect.patch b/target/linux/lantiq/patches-3.3/0055-MIPS-lantiq-udp-in-kernel-redirect.patch new file mode 100644 index 0000000000..8b69bbd3e2 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0055-MIPS-lantiq-udp-in-kernel-redirect.patch @@ -0,0 +1,378 @@ +From 186a2683b8fc1730dff28b2201d7508501a1dee4 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 29 Sep 2011 20:29:54 +0200 +Subject: [PATCH 55/70] MIPS: lantiq: udp in-kernel redirect + +--- + include/linux/udp_redirect.h | 57 +++++++++++++ + net/Kconfig | 6 ++ + net/ipv4/Makefile | 3 + + net/ipv4/udp.c | 28 ++++++- + net/ipv4/udp_redirect_symb.c | 186 ++++++++++++++++++++++++++++++++++++++++++ + 5 files changed, 276 insertions(+), 4 deletions(-) + create mode 100644 include/linux/udp_redirect.h + create mode 100644 net/ipv4/udp_redirect_symb.c + +diff --git a/include/linux/udp_redirect.h b/include/linux/udp_redirect.h +new file mode 100644 +index 0000000..de1e64f +--- /dev/null ++++ b/include/linux/udp_redirect.h +@@ -0,0 +1,57 @@ ++#ifndef _UDP_REDIRECT_H ++#define _UDP_REDIRECT_H ++ ++/****************************************************************************** ++ ++ Copyright (c) 2006 ++ Infineon Technologies AG ++ Am Campeon 1-12; 81726 Munich, Germany ++ ++ THE DELIVERY OF THIS SOFTWARE AS WELL AS THE HEREBY GRANTED NON-EXCLUSIVE, ++ WORLDWIDE LICENSE TO USE, COPY, MODIFY, DISTRIBUTE AND SUBLICENSE THIS ++ SOFTWARE IS FREE OF CHARGE. ++ ++ THE LICENSED SOFTWARE IS PROVIDED "AS IS" AND INFINEON EXPRESSLY DISCLAIMS ++ ALL REPRESENTATIONS AND WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING ++ WITHOUT LIMITATION, WARRANTIES OR REPRESENTATIONS OF WORKMANSHIP, ++ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, THAT THE ++ OPERATING OF THE LICENSED SOFTWARE WILL BE ERROR FREE OR FREE OF ANY THIRD ++ PARTY CLAIMS, INCLUDING WITHOUT LIMITATION CLAIMS OF THIRD PARTY INTELLECTUAL ++ PROPERTY INFRINGEMENT. ++ ++ EXCEPT FOR ANY LIABILITY DUE TO WILFUL ACTS OR GROSS NEGLIGENCE AND EXCEPT ++ FOR ANY PERSONAL INJURY INFINEON SHALL IN NO EVENT BE LIABLE FOR ANY CLAIM ++ OR DAMAGES OF ANY KIND, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ DEALINGS IN THE SOFTWARE. ++ ++******************************************************************************/ ++ ++/* ============================= */ ++/* Includes */ ++/* ============================= */ ++#ifndef _LINUX_TYPES_H ++#include ++#endif ++ ++ ++/* ============================= */ ++/* Definitions */ ++/* ============================= */ ++#define UDP_REDIRECT_MAGIC (void*)0x55445052L ++ ++ ++/* ============================= */ ++/* Global variable declaration */ ++/* ============================= */ ++extern int (*udp_do_redirect_fn)(struct sock *sk, struct sk_buff *skb); ++extern int (*udpredirect_getfrag_fn)(void *p, char * to, ++ int offset, int fraglen, int odd, ++ struct sk_buff *skb); ++/* ============================= */ ++/* Global function declaration */ ++/* ============================= */ ++ ++extern int udpredirect_getfrag(void *p, char * to, int offset, ++ int fraglen, int odd, struct sk_buff *skb); ++#endif +diff --git a/net/Kconfig b/net/Kconfig +index e07272d..2118693 100644 +--- a/net/Kconfig ++++ b/net/Kconfig +@@ -72,6 +72,12 @@ config INET + + Short answer: say Y. + ++config IFX_UDP_REDIRECT ++ bool "IFX Kernel Packet Interface for UDP redirection" ++ help ++ You can say Y here if you want to use hooks from kernel for ++ UDP redirection. ++ + if INET + source "net/ipv4/Kconfig" + source "net/ipv6/Kconfig" +diff --git a/net/ipv4/Makefile b/net/ipv4/Makefile +index ff75d3b..127edb9 100644 +--- a/net/ipv4/Makefile ++++ b/net/ipv4/Makefile +@@ -14,6 +14,9 @@ obj-y := route.o inetpeer.o protocol.o \ + inet_fragment.o ping.o + + obj-$(CONFIG_SYSCTL) += sysctl_net_ipv4.o ++ifneq ($(CONFIG_IFX_UDP_REDIRECT),) ++obj-$(CONFIG_IFX_UDP_REDIRECT) += udp_redirect_symb.o ++endif + obj-$(CONFIG_PROC_FS) += proc.o + obj-$(CONFIG_IP_MULTIPLE_TABLES) += fib_rules.o + obj-$(CONFIG_IP_MROUTE) += ipmr.o +diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c +index 5d075b5..a2ee1bf 100644 +--- a/net/ipv4/udp.c ++++ b/net/ipv4/udp.c +@@ -108,6 +108,10 @@ + #include + #include "udp_impl.h" + ++#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE) ++#include ++#endif ++ + struct udp_table udp_table __read_mostly; + EXPORT_SYMBOL(udp_table); + +@@ -804,7 +808,7 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + u8 tos; + int err, is_udplite = IS_UDPLITE(sk); + int corkreq = up->corkflag || msg->msg_flags&MSG_MORE; +- int (*getfrag)(void *, char *, int, int, int, struct sk_buff *); ++ int (*getfrag)(void *, char *, int, int, int, struct sk_buff *) = NULL; + struct sk_buff *skb; + struct ip_options_data opt_copy; + +@@ -821,7 +825,13 @@ int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, + ipc.opt = NULL; + ipc.tx_flags = 0; + +- getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; ++/* UDPREDIRECT */ ++#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE) ++ if(udpredirect_getfrag_fn && sk->sk_user_data == UDP_REDIRECT_MAGIC) ++ getfrag = udpredirect_getfrag_fn; ++ else ++#endif /* IFX_UDP_REDIRECT */ ++ getfrag = is_udplite ? udplite_getfrag : ip_generic_getfrag; + + fl4 = &inet->cork.fl.u.ip4; + if (up->pending) { +@@ -1625,6 +1635,7 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, + struct rtable *rt = skb_rtable(skb); + __be32 saddr, daddr; + struct net *net = dev_net(skb->dev); ++ int ret = 0; + + /* + * Validate the packet. +@@ -1657,7 +1668,16 @@ int __udp4_lib_rcv(struct sk_buff *skb, struct udp_table *udptable, + sk = __udp4_lib_lookup_skb(skb, uh->source, uh->dest, udptable); + + if (sk != NULL) { +- int ret = udp_queue_rcv_skb(sk, skb); ++ /* UDPREDIRECT */ ++#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE) ++ if(udp_do_redirect_fn && sk->sk_user_data == UDP_REDIRECT_MAGIC) ++ { ++ udp_do_redirect_fn(sk,skb); ++ kfree_skb(skb); ++ return(0); ++ } ++#endif ++ ret = udp_queue_rcv_skb(sk, skb); + sock_put(sk); + + /* a return value > 0 means to resubmit the input, but +@@ -1954,7 +1974,7 @@ struct proto udp_prot = { + .clear_sk = sk_prot_clear_portaddr_nulls, + }; + EXPORT_SYMBOL(udp_prot); +- ++EXPORT_SYMBOL(udp_rcv); + /* ------------------------------------------------------------------------ */ + #ifdef CONFIG_PROC_FS + +diff --git a/net/ipv4/udp_redirect_symb.c b/net/ipv4/udp_redirect_symb.c +new file mode 100644 +index 0000000..5617e86 +--- /dev/null ++++ b/net/ipv4/udp_redirect_symb.c +@@ -0,0 +1,186 @@ ++/****************************************************************************** ++ ++ Copyright (c) 2006 ++ Infineon Technologies AG ++ Am Campeon 1-12; 81726 Munich, Germany ++ ++ THE DELIVERY OF THIS SOFTWARE AS WELL AS THE HEREBY GRANTED NON-EXCLUSIVE, ++ WORLDWIDE LICENSE TO USE, COPY, MODIFY, DISTRIBUTE AND SUBLICENSE THIS ++ SOFTWARE IS FREE OF CHARGE. ++ ++ THE LICENSED SOFTWARE IS PROVIDED "AS IS" AND INFINEON EXPRESSLY DISCLAIMS ++ ALL REPRESENTATIONS AND WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING ++ WITHOUT LIMITATION, WARRANTIES OR REPRESENTATIONS OF WORKMANSHIP, ++ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, DURABILITY, THAT THE ++ OPERATING OF THE LICENSED SOFTWARE WILL BE ERROR FREE OR FREE OF ANY THIRD ++ PARTY CLAIMS, INCLUDING WITHOUT LIMITATION CLAIMS OF THIRD PARTY INTELLECTUAL ++ PROPERTY INFRINGEMENT. ++ ++ EXCEPT FOR ANY LIABILITY DUE TO WILFUL ACTS OR GROSS NEGLIGENCE AND EXCEPT ++ FOR ANY PERSONAL INJURY INFINEON SHALL IN NO EVENT BE LIABLE FOR ANY CLAIM ++ OR DAMAGES OF ANY KIND, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ++ ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++ DEALINGS IN THE SOFTWARE. ++ ++******************************************************************************/ ++#if defined(CONFIG_IFX_UDP_REDIRECT) || defined(CONFIG_IFX_UDP_REDIRECT_MODULE) ++/* ============================= */ ++/* Includes */ ++/* ============================= */ ++#include ++#include ++#include ++#include ++#include ++ ++/* ============================= */ ++/* Global variable definition */ ++/* ============================= */ ++int (*udpredirect_getfrag_fn) (void *p, char * to, int offset, ++ int fraglen, int odd, struct sk_buff *skb) = NULL; ++int (*udp_do_redirect_fn)(struct sock *sk, struct sk_buff *skb) = NULL; ++ ++/* ============================= */ ++/* Local type definitions */ ++/* ============================= */ ++struct udpfakehdr ++{ ++ struct udphdr uh; ++ u32 saddr; ++ u32 daddr; ++ struct iovec *iov; ++ u32 wcheck; ++}; ++ ++/* ============================= */ ++/* Local function declaration */ ++/* ============================= */ ++static int udpredirect_csum_partial_copy_fromiovecend(unsigned char *kdata, ++ struct iovec *iov, int offset, unsigned int len, __wsum *csump); ++ ++static int udpredirect_memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, ++ int len); ++ ++/* ============================= */ ++/* Global function definition */ ++/* ============================= */ ++ ++/* ++ Copy of udp_getfrag() from udp.c ++ This function exists because no copy_from_user() is needed for udpredirect. ++*/ ++ ++int ++udpredirect_getfrag(void *from, char *to, int offset, int len, int odd, struct sk_buff *skb) ++{ ++ struct iovec *iov = from; ++ ++ if (skb->ip_summed == CHECKSUM_PARTIAL) { ++ if (udpredirect_memcpy_fromiovecend(to, iov, offset, len) < 0) ++ return -EFAULT; ++ } else { ++ __wsum csum = 0; ++ if (udpredirect_csum_partial_copy_fromiovecend(to, iov, offset, len, &csum) < 0) ++ return -EFAULT; ++ skb->csum = csum_block_add(skb->csum, csum, odd); ++ } ++ return 0; ++} ++ ++static int udpredirect_memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, ++ int len) ++{ ++ /* Skip over the finished iovecs */ ++ while (offset >= iov->iov_len) { ++ offset -= iov->iov_len; ++ iov++; ++ } ++ ++ while (len > 0) { ++ u8 __user *base = iov->iov_base + offset; ++ int copy = min_t(unsigned int, len, iov->iov_len - offset); ++ ++ offset = 0; ++ memcpy(kdata, base, copy); ++ len -= copy; ++ kdata += copy; ++ iov++; ++ } ++ ++ return 0; ++} ++ ++/* ++ Copy of csum_partial_copy_fromiovecend() from iovec.c ++ This function exists because no copy_from_user() is needed for udpredirect. ++*/ ++ ++int udpredirect_csum_partial_copy_fromiovecend(unsigned char *kdata, struct iovec *iov, ++ int offset, unsigned int len, __wsum *csump) ++{ ++ __wsum csum = *csump; ++ int partial_cnt = 0, err = 0; ++ ++ /* Skip over the finished iovecs */ ++ while (offset >= iov->iov_len) { ++ offset -= iov->iov_len; ++ iov++; ++ } ++ ++ while (len > 0) { ++ u8 __user *base = iov->iov_base + offset; ++ int copy = min_t(unsigned int, len, iov->iov_len - offset); ++ ++ offset = 0; ++ ++ /* There is a remnant from previous iov. */ ++ if (partial_cnt) { ++ int par_len = 4 - partial_cnt; ++ ++ /* iov component is too short ... */ ++ if (par_len > copy) { ++ memcpy(kdata, base, copy); ++ kdata += copy; ++ base += copy; ++ partial_cnt += copy; ++ len -= copy; ++ iov++; ++ if (len) ++ continue; ++ *csump = csum_partial(kdata - partial_cnt, ++ partial_cnt, csum); ++ goto out; ++ } ++ memcpy(kdata, base, par_len); ++ csum = csum_partial(kdata - partial_cnt, 4, csum); ++ kdata += par_len; ++ base += par_len; ++ copy -= par_len; ++ len -= par_len; ++ partial_cnt = 0; ++ } ++ ++ if (len > copy) { ++ partial_cnt = copy % 4; ++ if (partial_cnt) { ++ copy -= partial_cnt; ++ memcpy(kdata + copy, base + copy, partial_cnt); ++ } ++ } ++ ++ if (copy) { ++ csum = csum_partial_copy_nocheck(base, kdata, copy, csum); ++ } ++ len -= copy + partial_cnt; ++ kdata += copy + partial_cnt; ++ iov++; ++ } ++ *csump = csum; ++out: ++ return err; ++} ++ ++EXPORT_SYMBOL(udpredirect_getfrag); ++EXPORT_SYMBOL(udp_do_redirect_fn); ++EXPORT_SYMBOL(udpredirect_getfrag_fn); ++#endif /* CONFIG_IFX_UDP_REDIRECT* */ +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0056-MIPS-lantiq-cache-split.patch b/target/linux/lantiq/patches-3.3/0056-MIPS-lantiq-cache-split.patch new file mode 100644 index 0000000000..22cc4b00b7 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0056-MIPS-lantiq-cache-split.patch @@ -0,0 +1,321 @@ +From d137c7d096436ed5673d1555687423d627a07086 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 29 Sep 2011 20:31:54 +0200 +Subject: [PATCH 56/70] MIPS: lantiq: cache split + +--- + arch/mips/Kconfig | 22 ++++++ + arch/mips/kernel/vpe.c | 66 ++++++++++++++++++ + arch/mips/mm/c-r4k.c | 172 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 260 insertions(+), 0 deletions(-) + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index 4a36923..df21d0d 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -1942,6 +1942,28 @@ config IFX_VPE_EXT + help + IFX included extensions in APRP + ++config IFX_VPE_CACHE_SPLIT ++ bool "IFX Cache Split Ways" ++ depends on IFX_VPE_EXT ++ help ++ IFX extension for reserving (splitting) cache ways among VPEs. You must ++ give kernel command line arguments vpe_icache_shared=0 or ++ vpe_dcache_shared=0 to enable splitting of icache or dcache ++ respectively. Then you can specify which cache ways should be ++ assigned to which VPE. There are total 8 cache ways, 4 each ++ for dcache and icache: dcache_way0, dcache_way1,dcache_way2, ++ dcache_way3 and icache_way0,icache_way1, icache_way2,icache_way3. ++ ++ For example, if you specify vpe_icache_shared=0 and icache_way2=1, ++ then the 3rd icache way will be assigned to VPE0 and denied in VPE1. ++ ++ For icache, software is required to make at least one cache way available ++ for a VPE at all times i.e., one can't assign all the icache ways to one ++ VPE. ++ ++ By default, vpe_dcache_shared and vpe_icache_shared are set to 1 ++ (i.e., both icache and dcache are shared among VPEs) ++ + config PERFCTRS + bool "34K Performance counters" + depends on MIPS_MT && PROC_FS +diff --git a/arch/mips/kernel/vpe.c b/arch/mips/kernel/vpe.c +index e338ba5..0511d11 100644 +--- a/arch/mips/kernel/vpe.c ++++ b/arch/mips/kernel/vpe.c +@@ -127,6 +127,13 @@ __setup("vpe1_wdog_timeout=", wdog_timeout); + EXPORT_SYMBOL(vpe1_wdog_timeout); + + #endif ++ ++#ifdef CONFIG_IFX_VPE_CACHE_SPLIT /* Code for splitting the cache ways among VPEs. */ ++extern int vpe_icache_shared,vpe_dcache_shared; ++extern int icache_way0,icache_way1,icache_way2,icache_way3; ++extern int dcache_way0,dcache_way1,dcache_way2,dcache_way3; ++#endif ++ + /* grab the likely amount of memory we will need. */ + #ifdef CONFIG_MIPS_VPE_LOADER_TOM + #define P_SIZE (2 * 1024 * 1024) +@@ -865,6 +872,65 @@ static int vpe_run(struct vpe * v) + /* enable this VPE */ + write_vpe_c0_vpeconf0(read_vpe_c0_vpeconf0() | VPECONF0_VPA); + ++#ifdef CONFIG_IFX_VPE_CACHE_SPLIT ++ if ( (!vpe_icache_shared) || (!vpe_dcache_shared) ) { ++ ++ /* PCP bit must be 1 to split the cache */ ++ if(read_c0_mvpconf0() & MVPCONF0_PCP) { ++ ++ if ( !vpe_icache_shared ){ ++ write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0()) & ~VPECONF0_ICS); ++ ++ /* ++ * If any cache way is 1, then that way is denied ++ * in VPE1. Otherwise assign that way to VPE1. ++ */ ++ if (!icache_way0) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX0 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX0 ); ++ if (!icache_way1) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX1 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX1 ); ++ if (!icache_way2) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX2 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX2 ); ++ if (!icache_way3) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_IWX3 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_IWX3 ); ++ } ++ ++ if ( !vpe_dcache_shared ) { ++ write_vpe_c0_vpeconf0((read_vpe_c0_vpeconf0()) & ~VPECONF0_DCS); ++ ++ /* ++ * If any cache way is 1, then that way is denied ++ * in VPE1. Otherwise assign that way to VPE1. ++ */ ++ if (!dcache_way0) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX0 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX0 ); ++ if (!dcache_way1) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX1 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX1 ); ++ if (!dcache_way2) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX2 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX2 ); ++ if (!dcache_way3) ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() | VPEOPT_DWX3 ); ++ else ++ write_vpe_c0_vpeopt(read_vpe_c0_vpeopt() & ~VPEOPT_DWX3 ); ++ } ++ } ++ } ++#endif /* endif CONFIG_IFX_VPE_CACHE_SPLIT */ ++ + /* clear out any left overs from a previous program */ + write_vpe_c0_status(0); + write_vpe_c0_cause(0); +diff --git a/arch/mips/mm/c-r4k.c b/arch/mips/mm/c-r4k.c +index 4f9eb0b..9e9f925 100644 +--- a/arch/mips/mm/c-r4k.c ++++ b/arch/mips/mm/c-r4k.c +@@ -1386,6 +1386,106 @@ static int __init setcoherentio(char *str) + __setup("coherentio", setcoherentio); + #endif + ++#ifdef CONFIG_IFX_VPE_CACHE_SPLIT /* Code for splitting the cache ways among VPEs. */ ++ ++#include ++ ++/* ++ * By default, vpe_icache_shared and vpe_dcache_shared ++ * values are 1 i.e., both icache and dcache are shared ++ * among the VPEs. ++ */ ++ ++int vpe_icache_shared = 1; ++static int __init vpe_icache_shared_val(char *str) ++{ ++ get_option(&str, &vpe_icache_shared); ++ return 1; ++} ++__setup("vpe_icache_shared=", vpe_icache_shared_val); ++EXPORT_SYMBOL(vpe_icache_shared); ++ ++int vpe_dcache_shared = 1; ++static int __init vpe_dcache_shared_val(char *str) ++{ ++ get_option(&str, &vpe_dcache_shared); ++ return 1; ++} ++__setup("vpe_dcache_shared=", vpe_dcache_shared_val); ++EXPORT_SYMBOL(vpe_dcache_shared); ++ ++/* ++ * Software is required to make atleast one icache ++ * way available for a VPE at all times i.e., one ++ * can't assign all the icache ways to one VPE. ++ */ ++ ++int icache_way0 = 0; ++static int __init icache_way0_val(char *str) ++{ ++ get_option(&str, &icache_way0); ++ return 1; ++} ++__setup("icache_way0=", icache_way0_val); ++ ++int icache_way1 = 0; ++static int __init icache_way1_val(char *str) ++{ ++ get_option(&str, &icache_way1); ++ return 1; ++} ++__setup("icache_way1=", icache_way1_val); ++ ++int icache_way2 = 0; ++static int __init icache_way2_val(char *str) ++{ ++ get_option(&str, &icache_way2); ++ return 1; ++} ++__setup("icache_way2=", icache_way2_val); ++ ++int icache_way3 = 0; ++static int __init icache_way3_val(char *str) ++{ ++ get_option(&str, &icache_way3); ++ return 1; ++} ++__setup("icache_way3=", icache_way3_val); ++ ++int dcache_way0 = 0; ++static int __init dcache_way0_val(char *str) ++{ ++ get_option(&str, &dcache_way0); ++ return 1; ++} ++__setup("dcache_way0=", dcache_way0_val); ++ ++int dcache_way1 = 0; ++static int __init dcache_way1_val(char *str) ++{ ++ get_option(&str, &dcache_way1); ++ return 1; ++} ++__setup("dcache_way1=", dcache_way1_val); ++ ++int dcache_way2 = 0; ++static int __init dcache_way2_val(char *str) ++{ ++ get_option(&str, &dcache_way2); ++ return 1; ++} ++__setup("dcache_way2=", dcache_way2_val); ++ ++int dcache_way3 = 0; ++static int __init dcache_way3_val(char *str) ++{ ++ get_option(&str, &dcache_way3); ++ return 1; ++} ++__setup("dcache_way3=", dcache_way3_val); ++ ++#endif /* endif CONFIG_IFX_VPE_CACHE_SPLIT */ ++ + void __cpuinit r4k_cache_init(void) + { + extern void build_clear_page(void); +@@ -1405,6 +1505,78 @@ void __cpuinit r4k_cache_init(void) + break; + } + ++#ifdef CONFIG_IFX_VPE_CACHE_SPLIT ++ /* ++ * We split the cache ways appropriately among the VPEs ++ * based on cache ways values we received as command line ++ * arguments ++ */ ++ if ( (!vpe_icache_shared) || (!vpe_dcache_shared) ){ ++ ++ /* PCP bit must be 1 to split the cache */ ++ if(read_c0_mvpconf0() & MVPCONF0_PCP) { ++ ++ /* Set CPA bit which enables us to modify VPEOpt register */ ++ write_c0_mvpcontrol((read_c0_mvpcontrol()) | MVPCONTROL_CPA); ++ ++ if ( !vpe_icache_shared ){ ++ write_c0_vpeconf0((read_c0_vpeconf0()) & ~VPECONF0_ICS); ++ /* ++ * If any cache way is 1, then that way is denied ++ * in VPE0. Otherwise assign that way to VPE0. ++ */ ++ printk(KERN_DEBUG "icache is split\n"); ++ printk(KERN_DEBUG "icache_way0=%d icache_way1=%d icache_way2=%d icache_way3=%d\n", ++ icache_way0, icache_way1,icache_way2, icache_way3); ++ if (icache_way0) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX0 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX0 ); ++ if (icache_way1) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX1 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX1 ); ++ if (icache_way2) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX2 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX2 ); ++ if (icache_way3) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_IWX3 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_IWX3 ); ++ } ++ ++ if ( !vpe_dcache_shared ) { ++ /* ++ * If any cache way is 1, then that way is denied ++ * in VPE0. Otherwise assign that way to VPE0. ++ */ ++ printk(KERN_DEBUG "dcache is split\n"); ++ printk(KERN_DEBUG "dcache_way0=%d dcache_way1=%d dcache_way2=%d dcache_way3=%d\n", ++ dcache_way0, dcache_way1, dcache_way2, dcache_way3); ++ write_c0_vpeconf0((read_c0_vpeconf0()) & ~VPECONF0_DCS); ++ if (dcache_way0) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX0 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX0 ); ++ if (dcache_way1) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX1 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX1 ); ++ if (dcache_way2) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX2 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX2 ); ++ if (dcache_way3) ++ write_c0_vpeopt(read_c0_vpeopt() | VPEOPT_DWX3 ); ++ else ++ write_c0_vpeopt(read_c0_vpeopt() & ~VPEOPT_DWX3 ); ++ } ++ } ++ } ++ ++#endif /* endif CONFIG_IFX_VPE_CACHE_SPLIT */ ++ + probe_pcache(); + setup_scache(); + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0057-MIPS-clean-up-clock-code.patch b/target/linux/lantiq/patches-3.3/0057-MIPS-clean-up-clock-code.patch new file mode 100644 index 0000000000..134fddb067 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0057-MIPS-clean-up-clock-code.patch @@ -0,0 +1,319 @@ +From 580d6aef484b22b5da88588d9a2d944b4f06dc98 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 20 Mar 2012 08:26:04 +0100 +Subject: [PATCH 57/70] MIPS: clean up clock code + +--- + arch/mips/lantiq/clk.c | 11 +++ + arch/mips/lantiq/clk.h | 3 +- + arch/mips/lantiq/xway/devices.c | 2 +- + arch/mips/lantiq/xway/sysctrl.c | 166 ++++++++++++++++++++++++++++++--------- + 4 files changed, 143 insertions(+), 39 deletions(-) + +diff --git a/arch/mips/lantiq/clk.c b/arch/mips/lantiq/clk.c +index 84a201e..5494b6e 100644 +--- a/arch/mips/lantiq/clk.c ++++ b/arch/mips/lantiq/clk.c +@@ -44,6 +44,7 @@ struct clk *clk_get_fpi(void) + { + return &cpu_clk_generic[1]; + } ++EXPORT_SYMBOL_GPL(clk_get_fpi); + + struct clk *clk_get_io(void) + { +@@ -70,6 +71,16 @@ unsigned long clk_get_rate(struct clk *clk) + } + EXPORT_SYMBOL(clk_get_rate); + ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ if (unlikely(!clk_good(clk))) ++ return 0; ++ ++ clk->rate = rate; ++ return 0; ++} ++EXPORT_SYMBOL(clk_set_rate); ++ + int clk_enable(struct clk *clk) + { + if (unlikely(!clk_good(clk))) +diff --git a/arch/mips/lantiq/clk.h b/arch/mips/lantiq/clk.h +index d047768..b34e675 100644 +--- a/arch/mips/lantiq/clk.h ++++ b/arch/mips/lantiq/clk.h +@@ -12,6 +12,7 @@ + #include + + /* clock speeds */ ++#define CLOCK_33M 33333333 + #define CLOCK_60M 60000000 + #define CLOCK_62_5M 62500000 + #define CLOCK_83M 83333333 +@@ -38,9 +39,9 @@ + struct clk { + struct clk_lookup cl; + unsigned long rate; +- unsigned long (*get_rate) (void); + unsigned int module; + unsigned int bits; ++ unsigned long (*get_rate) (void); + int (*enable) (struct clk *clk); + void (*disable) (struct clk *clk); + int (*activate) (struct clk *clk); +diff --git a/arch/mips/lantiq/xway/devices.c b/arch/mips/lantiq/xway/devices.c +index e6d45bc..5d4650d 100644 +--- a/arch/mips/lantiq/xway/devices.c ++++ b/arch/mips/lantiq/xway/devices.c +@@ -59,7 +59,7 @@ static struct resource ltq_stp_resource = + + void __init ltq_register_gpio_stp(void) + { +- platform_device_register_simple("ltq_stp", 0, <q_stp_resource, 1); ++ platform_device_register_simple("ltq_stp", -1, <q_stp_resource, 1); + } + + /* asc ports - amazon se has its own serial mapping */ +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index ac7383f..9df048c 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -16,40 +16,57 @@ + #include "../devices.h" + + /* clock control register */ +-#define LTQ_CGU_IFCCR 0x0018 ++#define CGU_IFCCR 0x0018 + /* system clock register */ +-#define LTQ_CGU_SYS 0x0010 +- +-/* the enable / disable registers */ +-#define LTQ_PMU_PWDCR 0x1C +-#define LTQ_PMU_PWDSR 0x20 +-#define LTQ_PMU_PWDCR1 0x24 +-#define LTQ_PMU_PWDSR1 0x28 +- +-#define PWDCR(x) ((x) ? (LTQ_PMU_PWDCR1) : (LTQ_PMU_PWDCR)) +-#define PWDSR(x) ((x) ? (LTQ_PMU_PWDSR1) : (LTQ_PMU_PWDSR)) +- +-/* CGU - clock generation unit */ +-#define CGU_EPHY 0x10 ++#define CGU_SYS 0x0010 ++/* pci control register */ ++#define CGU_PCICR 0x0034 ++/* ephy configuration register */ ++#define CGU_EPHY 0x10 ++/* power control register */ ++#define PMU_PWDCR 0x1C ++/* power status register */ ++#define PMU_PWDSR 0x20 ++/* power control register */ ++#define PMU_PWDCR1 0x24 ++/* power status register */ ++#define PMU_PWDSR1 0x28 ++/* power control register */ ++#define PWDCR(x) ((x) ? (PMU_PWDCR1) : (PMU_PWDCR)) ++/* power status register */ ++#define PWDSR(x) ((x) ? (PMU_PWDSR1) : (PMU_PWDSR)) + + /* PMU - power management unit */ +-#define PMU_DMA 0x0020 +-#define PMU_SPI 0x0100 +-#define PMU_EPHY 0x0080 +-#define PMU_USB 0x8041 +-#define PMU_STP 0x0800 +-#define PMU_GPT 0x1000 +-#define PMU_PPE 0x2000 +-#define PMU_FPI 0x4000 +-#define PMU_SWITCH 0x10000000 +-#define PMU_AHBS 0x2000 +-#define PMU_AHBM 0x8000 +-#define PMU_PCIE_CLK 0x80000000 +- +-#define PMU1_PCIE_PHY 0x0001 +-#define PMU1_PCIE_CTL 0x0002 +-#define PMU1_PCIE_MSI 0x0020 +-#define PMU1_PCIE_PDI 0x0010 ++#define PMU_USB0_P BIT(0) ++#define PMU_PCI BIT(4) ++#define PMU_DMA BIT(5) ++#define PMU_USB0 BIT(5) ++#define PMU_SPI BIT(8) ++#define PMU_EPHY BIT(7) ++#define PMU_EBU BIT(10) ++#define PMU_STP BIT(11) ++#define PMU_GPT BIT(12) ++#define PMU_PPE BIT(13) ++#define PMU_AHBS BIT(13) /* vr9 */ ++#define PMU_FPI BIT(14) ++#define PMU_AHBM BIT(15) ++#define PMU_PPE_QSB BIT(18) ++#define PMU_PPE_SLL01 BIT(19) ++#define PMU_PPE_TC BIT(21) ++#define PMU_PPE_EMA BIT(22) ++#define PMU_PPE_DPLUM BIT(23) ++#define PMU_PPE_DPLUS BIT(24) ++#define PMU_USB1_P BIT(26) ++#define PMU_USB1 BIT(27) ++#define PMU_SWITCH BIT(28) ++#define PMU_PPE_TOP BIT(29) ++#define PMU_GPHY BIT(30) ++#define PMU_PCIE_CLK BIT(31) ++ ++#define PMU1_PCIE_PHY BIT(0) ++#define PMU1_PCIE_CTL BIT(1) ++#define PMU1_PCIE_PDI BIT(4) ++#define PMU1_PCIE_MSI BIT(5) + + #define ltq_pmu_w32(x, y) ltq_w32((x), ltq_pmu_membase + (y)) + #define ltq_pmu_r32(x) ltq_r32(ltq_pmu_membase + (x)) +@@ -69,13 +86,13 @@ static void __iomem *ltq_pmu_membase; + + static int ltq_cgu_enable(struct clk *clk) + { +- ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) | clk->bits, LTQ_CGU_IFCCR); ++ ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | clk->bits, CGU_IFCCR); + return 0; + } + + static void ltq_cgu_disable(struct clk *clk) + { +- ltq_cgu_w32(ltq_cgu_r32(LTQ_CGU_IFCCR) & ~clk->bits, LTQ_CGU_IFCCR); ++ ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~clk->bits, CGU_IFCCR); + } + + static int ltq_pmu_enable(struct clk *clk) +@@ -94,9 +111,49 @@ static int ltq_pmu_enable(struct clk *clk) + + static void ltq_pmu_disable(struct clk *clk) + { +- ltq_pmu_w32(ltq_pmu_r32(LTQ_PMU_PWDCR) | clk->bits, LTQ_PMU_PWDCR); ++ ltq_pmu_w32(ltq_pmu_r32(PWDCR(clk->module)) | clk->bits, ++ PWDCR(clk->module)); ++} ++ ++static int ltq_pci_enable(struct clk *clk) ++{ ++ unsigned int ifccr = ltq_cgu_r32(CGU_IFCCR); ++ /* set clock bus speed */ ++ if (ltq_is_ar9()) { ++ ifccr &= ~0x1f00000; ++ if (clk->rate == CLOCK_33M) ++ ifccr |= 0xe00000; ++ else ++ ifccr |= 0x700000; /* 62.5M */ ++ } else { ++ ifccr &= ~0xf00000; ++ if (clk->rate == CLOCK_33M) ++ ifccr |= 0x800000; ++ else ++ ifccr |= 0x400000; /* 62.5M */ ++ } ++ ltq_cgu_w32(ifccr, CGU_IFCCR); ++ return 0; ++} ++ ++static int ltq_pci_ext_enable(struct clk *clk) ++{ ++ /* enable external pci clock */ ++ ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) & ~(1 << 16), ++ CGU_IFCCR); ++ ltq_cgu_w32((1 << 30), CGU_PCICR); ++ return 0; ++} ++ ++static void ltq_pci_ext_disable(struct clk *clk) ++{ ++ /* enable external pci clock */ ++ ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16), ++ CGU_IFCCR); ++ ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR); + } + ++/* manage the clock gates via PMU */ + static inline void clkdev_add_pmu(const char *dev, const char *con, + unsigned int module, unsigned int bits) + { +@@ -112,6 +169,7 @@ static inline void clkdev_add_pmu(const char *dev, const char *con, + clkdev_add(&clk->cl); + } + ++/* manage the clock generator */ + static inline void clkdev_add_cgu(const char *dev, const char *con, + unsigned int bits) + { +@@ -126,6 +184,33 @@ static inline void clkdev_add_cgu(const char *dev, const char *con, + clkdev_add(&clk->cl); + } + ++/* pci needs its own enable function */ ++static inline void clkdev_add_pci(void) ++{ ++ struct clk *clk = kzalloc(sizeof(struct clk), GFP_KERNEL); ++ struct clk *clk_ext = kzalloc(sizeof(struct clk), GFP_KERNEL); ++ ++ /* main pci clock */ ++ clk->cl.dev_id = "ltq_pci"; ++ clk->cl.con_id = NULL; ++ clk->cl.clk = clk; ++ clk->rate = CLOCK_33M; ++ clk->enable = ltq_pci_enable; ++ clk->disable = ltq_pmu_disable; ++ clk->module = 0; ++ clk->bits = PMU_PCI; ++ clkdev_add(&clk->cl); ++ ++ /* use internal/external bus clock */ ++ clk_ext->cl.dev_id = "ltq_pci"; ++ clk_ext->cl.con_id = "external"; ++ clk_ext->cl.clk = clk_ext; ++ clk_ext->enable = ltq_pci_ext_enable; ++ clk_ext->disable = ltq_pci_ext_disable; ++ clkdev_add(&clk_ext->cl); ++ ++} ++ + void __init ltq_soc_init(void) + { + ltq_pmu_membase = ltq_remap_resource(<q_pmu_resource); +@@ -144,14 +229,16 @@ void __init ltq_soc_init(void) + ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_BUSCON0) & ~EBU_WRDIS, LTQ_EBU_BUSCON0); + + /* add our clocks */ ++ clkdev_add_pmu("ltq_fpi", NULL, 0, PMU_FPI); + clkdev_add_pmu("ltq_dma", NULL, 0, PMU_DMA); + clkdev_add_pmu("ltq_stp", NULL, 0, PMU_STP); + clkdev_add_pmu("ltq_spi", NULL, 0, PMU_SPI); + clkdev_add_pmu("ltq_gptu", NULL, 0, PMU_GPT); ++ clkdev_add_pmu("ltq_ebu", NULL, 0, PMU_EBU); + if (!ltq_is_vr9()) + clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE); + if (ltq_is_ase()) { +- if (ltq_cgu_r32(LTQ_CGU_SYS) & (1 << 5)) ++ if (ltq_cgu_r32(CGU_SYS) & (1 << 5)) + clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M); + else + clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M); +@@ -166,11 +253,16 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_pcie", "pdi", 1, PMU1_PCIE_PDI); + clkdev_add_pmu("ltq_pcie", "ctl", 1, PMU1_PCIE_CTL); + clkdev_add_pmu("ltq_pcie", "ahb", 0, PMU_AHBM | PMU_AHBS); +- clkdev_add_pmu("usb0", NULL, 0, (1<<6) | 1); +- clkdev_add_pmu("usb1", NULL, 0, (1<<26) | (1<<27)); ++ clkdev_add_pmu("usb0", NULL, 0, PMU_USB0 | PMU_USB0_P); ++ clkdev_add_pmu("usb1", NULL, 0, PMU_USB1 | PMU_USB1_P); ++ clkdev_add_pmu("ltq_vrx200", NULL, 0, ++ PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM | ++ PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | ++ PMU_PPE_QSB); + } else { + clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), + ltq_danube_io_region_clock()); ++ clkdev_add_pci(); + if (ltq_is_ar9()) + clkdev_add_pmu("ltq_etop", "switch", 0, PMU_SWITCH); + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0058-MIPS-cleanup-reset-code.patch b/target/linux/lantiq/patches-3.3/0058-MIPS-cleanup-reset-code.patch new file mode 100644 index 0000000000..b91a01d0c1 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0058-MIPS-cleanup-reset-code.patch @@ -0,0 +1,103 @@ +From f2e6a9e24af6d3e3ca14e66269c9621cecb9836b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 16 Mar 2012 15:49:32 +0100 +Subject: [PATCH 58/70] MIPS: cleanup reset code + +--- + arch/mips/lantiq/xway/reset.c | 59 ++++++++++++++++++++++++++++++++++------ + 1 files changed, 50 insertions(+), 9 deletions(-) + +diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c +index c705bbf..abdf9b1 100644 +--- a/arch/mips/lantiq/xway/reset.c ++++ b/arch/mips/lantiq/xway/reset.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + + #include +@@ -20,12 +21,45 @@ + #define ltq_rcu_w32(x, y) ltq_w32((x), ltq_rcu_membase + (y)) + #define ltq_rcu_r32(x) ltq_r32(ltq_rcu_membase + (x)) + +-/* register definitions */ +-#define LTQ_RCU_RST 0x0010 +-#define LTQ_RCU_RST_ALL 0x40000000 +- +-#define LTQ_RCU_RST_STAT 0x0014 +-#define LTQ_RCU_STAT_SHIFT 26 ++/* reset request register */ ++#define RCU_RST_REQ 0x0010 ++/* reset status register */ ++#define RCU_RST_STAT 0x0014 ++ ++/* reset cause */ ++#define RCU_STAT_SHIFT 26 ++/* Global SW Reset */ ++#define RCU_RD_SRST BIT(30) ++/* Memory Controller */ ++#define RCU_RD_MC BIT(14) ++/* PCI core */ ++#define RCU_RD_PCI BIT(13) ++/* Voice DFE/AFE */ ++#define RCU_RD_DFE_AFE BIT(12) ++/* DSL AFE */ ++#define RCU_RD_DSL_AFE BIT(11) ++/* SDIO core */ ++#define RCU_RD_SDIO BIT(10) ++/* DMA core */ ++#define RCU_RD_DMA BIT(9) ++/* PPE core */ ++#define RCU_RD_PPE BIT(8) ++/* ARC/DFE core */ ++#define RCU_RD_ARC_DFE BIT(7) ++/* AHB bus */ ++#define RCU_RD_AHB BIT(6) ++/* Ethernet MAC1 */ ++#define RCU_RD_ENET_MAC1 BIT(5) ++/* USB and Phy core */ ++#define RCU_RD_USB BIT(4) ++/* CPU1 subsystem */ ++#define RCU_RD_CPU1 BIT(3) ++/* FPI bus */ ++#define RCU_RD_FPI BIT(2) ++/* CPU0 subsystem */ ++#define RCU_RD_CPU0 BIT(1) ++/* HW reset via HRST pin */ ++#define RCU_RD_HRST BIT(0) + + static struct resource ltq_rcu_resource = + MEM_RES("rcu", LTQ_RCU_BASE_ADDR, LTQ_RCU_SIZE); +@@ -36,16 +70,23 @@ static void __iomem *ltq_rcu_membase; + /* This function is used by the watchdog driver */ + int ltq_reset_cause(void) + { +- u32 val = ltq_rcu_r32(LTQ_RCU_RST_STAT); +- return val >> LTQ_RCU_STAT_SHIFT; ++ u32 val = ltq_rcu_r32(RCU_RST_STAT); ++ return val >> RCU_STAT_SHIFT; + } + EXPORT_SYMBOL_GPL(ltq_reset_cause); + ++void ltq_reset_once(unsigned int module, ulong usec) ++{ ++ ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) | module, RCU_RST_REQ); ++ udelay(usec); ++ ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) & ~module, RCU_RST_REQ); ++} ++ + static void ltq_machine_restart(char *command) + { + pr_notice("System restart\n"); + local_irq_disable(); +- ltq_rcu_w32(ltq_rcu_r32(LTQ_RCU_RST) | LTQ_RCU_RST_ALL, LTQ_RCU_RST); ++ ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) | RCU_RD_SRST, RCU_RST_REQ); + unreachable(); + } + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0059-MIPS-lantiq-fixes-ar9-vr9-clock.patch b/target/linux/lantiq/patches-3.3/0059-MIPS-lantiq-fixes-ar9-vr9-clock.patch new file mode 100644 index 0000000000..7197a12504 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0059-MIPS-lantiq-fixes-ar9-vr9-clock.patch @@ -0,0 +1,116 @@ +From bbe68381c786d73ff4dc8b8b10deaa9db7700a9d Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Sat, 17 Mar 2012 09:58:07 +0100 +Subject: [PATCH 59/70] MIPS: lantiq: fixes ar9/vr9 clock + +--- + arch/mips/lantiq/clk.h | 4 +++- + arch/mips/lantiq/xway/clk.c | 29 ++++++++++++++++++++++++----- + arch/mips/lantiq/xway/sysctrl.c | 13 ++++++++----- + 3 files changed, 35 insertions(+), 11 deletions(-) + +diff --git a/arch/mips/lantiq/clk.h b/arch/mips/lantiq/clk.h +index b34e675..010dfa7 100644 +--- a/arch/mips/lantiq/clk.h ++++ b/arch/mips/lantiq/clk.h +@@ -56,8 +56,10 @@ extern unsigned long ltq_danube_cpu_hz(void); + extern unsigned long ltq_danube_fpi_hz(void); + extern unsigned long ltq_danube_io_region_clock(void); + ++extern unsigned long ltq_ar9_cpu_hz(void); ++extern unsigned long ltq_ar9_fpi_hz(void); ++ + extern unsigned long ltq_vr9_cpu_hz(void); + extern unsigned long ltq_vr9_fpi_hz(void); +-extern unsigned long ltq_vr9_io_region_clock(void); + + #endif +diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c +index 3635c9f..2bafc04 100644 +--- a/arch/mips/lantiq/xway/clk.c ++++ b/arch/mips/lantiq/xway/clk.c +@@ -217,6 +217,30 @@ unsigned long ltq_danube_cpu_hz(void) + } + } + ++unsigned long ltq_ar9_sys_hz(void) ++{ ++ if (((ltq_cgu_r32(LTQ_CGU_SYS) >> 3) & 0x3) == 0x2) ++ return CLOCK_393M; ++ return CLOCK_333M; ++} ++ ++unsigned long ltq_ar9_fpi_hz(void) ++{ ++ unsigned long sys = ltq_ar9_sys_hz(); ++ ++ if (ltq_cgu_r32(LTQ_CGU_SYS) & BIT(0)) ++ return sys; ++ return sys >> 1; ++} ++ ++unsigned long ltq_ar9_cpu_hz(void) ++{ ++ if (ltq_cgu_r32(LTQ_CGU_SYS) & BIT(2)) ++ return ltq_ar9_fpi_hz(); ++ else ++ return ltq_ar9_sys_hz(); ++} ++ + unsigned long ltq_danube_fpi_hz(void) + { + unsigned long ddr_clock = DDR_HZ; +@@ -299,11 +323,6 @@ unsigned long ltq_vr9_fpi_hz(void) + return clk; + } + +-unsigned long ltq_vr9_io_region_clock(void) +-{ +- return ltq_vr9_fpi_hz(); +-} +- + unsigned long ltq_vr9_fpi_bus_clock(int fpi) + { + return ltq_vr9_fpi_hz(); +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 9df048c..6771a7e 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -237,6 +237,8 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_ebu", NULL, 0, PMU_EBU); + if (!ltq_is_vr9()) + clkdev_add_pmu("ltq_etop", NULL, 0, PMU_PPE); ++ if (!ltq_is_ase()) ++ clkdev_add_pci(); + if (ltq_is_ase()) { + if (ltq_cgu_r32(CGU_SYS) & (1 << 5)) + clkdev_add_static(CLOCK_266M, CLOCK_133M, CLOCK_133M); +@@ -246,7 +248,7 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_etop", "ephy", 0, PMU_EPHY); + } else if (ltq_is_vr9()) { + clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(), +- ltq_vr9_io_region_clock()); ++ ltq_vr9_fpi_hz()); + clkdev_add_pmu("ltq_pcie", "phy", 1, PMU1_PCIE_PHY); + clkdev_add_pmu("ltq_pcie", "bus", 0, PMU_PCIE_CLK); + clkdev_add_pmu("ltq_pcie", "msi", 1, PMU1_PCIE_MSI); +@@ -259,11 +261,12 @@ void __init ltq_soc_init(void) + PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM | + PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | + PMU_PPE_QSB); ++ } else if (ltq_is_ar9()) { ++ clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(), ++ ltq_ar9_fpi_hz()); ++ clkdev_add_pmu("ltq_etop", "switch", 0, PMU_SWITCH); + } else { + clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), +- ltq_danube_io_region_clock()); +- clkdev_add_pci(); +- if (ltq_is_ar9()) +- clkdev_add_pmu("ltq_etop", "switch", 0, PMU_SWITCH); ++ ltq_danube_io_region_clock()); + } + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0060-MIPS-lantiq-fixes-danube-clock.patch b/target/linux/lantiq/patches-3.3/0060-MIPS-lantiq-fixes-danube-clock.patch new file mode 100644 index 0000000000..549038ba02 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0060-MIPS-lantiq-fixes-danube-clock.patch @@ -0,0 +1,57 @@ +From e8f13d2824871027e8b6d374a2db3672db043915 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Mon, 19 Mar 2012 15:53:37 +0100 +Subject: [PATCH 60/70] MIPS: lantiq: fixes danube clock + +--- + arch/mips/lantiq/xway/clk.c | 20 ++++++++++---------- + 1 files changed, 10 insertions(+), 10 deletions(-) + +diff --git a/arch/mips/lantiq/xway/clk.c b/arch/mips/lantiq/xway/clk.c +index 2bafc04..5d850dc 100644 +--- a/arch/mips/lantiq/xway/clk.c ++++ b/arch/mips/lantiq/xway/clk.c +@@ -181,7 +181,7 @@ unsigned long ltq_danube_io_region_clock(void) + { + unsigned int ret = ltq_get_pll0_fosc(); + +- switch (ltq_cgu_r32(LTQ_CGU_PLL2_CFG) & CGU_SYS_DDR_SEL) { ++ switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0x3) { + default: + case 0: + return (ret + 1) / 2; +@@ -203,6 +203,15 @@ unsigned long ltq_danube_fpi_bus_clock(int fpi) + return ret; + } + ++unsigned long ltq_danube_fpi_hz(void) ++{ ++ unsigned long ddr_clock = DDR_HZ; ++ ++ if (ltq_cgu_r32(LTQ_CGU_SYS) & 0x40) ++ return ddr_clock >> 1; ++ return ddr_clock; ++} ++ + unsigned long ltq_danube_cpu_hz(void) + { + switch (ltq_cgu_r32(LTQ_CGU_SYS) & 0xc) { +@@ -241,15 +250,6 @@ unsigned long ltq_ar9_cpu_hz(void) + return ltq_ar9_sys_hz(); + } + +-unsigned long ltq_danube_fpi_hz(void) +-{ +- unsigned long ddr_clock = DDR_HZ; +- +- if (ltq_cgu_r32(LTQ_CGU_SYS) & 0x40) +- return ddr_clock >> 1; +- return ddr_clock; +-} +- + unsigned long ltq_vr9_cpu_hz(void) + { + unsigned int cpu_sel; +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0061-MIPS-adds-dsl-clocks.patch b/target/linux/lantiq/patches-3.3/0061-MIPS-adds-dsl-clocks.patch new file mode 100644 index 0000000000..a0a85ec24e --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0061-MIPS-adds-dsl-clocks.patch @@ -0,0 +1,66 @@ +From 77da4ad0d8dfe7c5f46a06296a04a992a961c1a3 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 20 Mar 2012 13:05:11 +0100 +Subject: [PATCH 61/70] MIPS: adds dsl clocks + +--- + arch/mips/lantiq/xway/sysctrl.c | 15 +++++++++++++-- + 1 files changed, 13 insertions(+), 2 deletions(-) + +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 6771a7e..3672fc6 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -41,8 +41,9 @@ + #define PMU_PCI BIT(4) + #define PMU_DMA BIT(5) + #define PMU_USB0 BIT(5) ++#define PMU_EPHY BIT(7) /* ase */ + #define PMU_SPI BIT(8) +-#define PMU_EPHY BIT(7) ++#define PMU_DFE BIT(9) + #define PMU_EBU BIT(10) + #define PMU_STP BIT(11) + #define PMU_GPT BIT(12) +@@ -147,7 +148,7 @@ static int ltq_pci_ext_enable(struct clk *clk) + + static void ltq_pci_ext_disable(struct clk *clk) + { +- /* enable external pci clock */ ++ /* disable external pci clock (internal) */ + ltq_cgu_w32(ltq_cgu_r32(CGU_IFCCR) | (1 << 16), + CGU_IFCCR); + ltq_cgu_w32((1 << 31) | (1 << 30), CGU_PCICR); +@@ -246,6 +247,9 @@ void __init ltq_soc_init(void) + clkdev_add_static(CLOCK_133M, CLOCK_133M, CLOCK_133M); + clkdev_add_cgu("ltq_etop", "ephycgu", CGU_EPHY), + clkdev_add_pmu("ltq_etop", "ephy", 0, PMU_EPHY); ++ clkdev_add_pmu("ltq_dsl", NULL, 0, ++ PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | ++ PMU_AHBS | PMU_DFE); + } else if (ltq_is_vr9()) { + clkdev_add_static(ltq_vr9_cpu_hz(), ltq_vr9_fpi_hz(), + ltq_vr9_fpi_hz()); +@@ -261,12 +265,19 @@ void __init ltq_soc_init(void) + PMU_SWITCH | PMU_PPE_DPLUS | PMU_PPE_DPLUM | + PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | + PMU_PPE_QSB); ++ clkdev_add_pmu("ltq_dsl", NULL, 0, PMU_DFE | PMU_AHBS); + } else if (ltq_is_ar9()) { + clkdev_add_static(ltq_ar9_cpu_hz(), ltq_ar9_fpi_hz(), + ltq_ar9_fpi_hz()); + clkdev_add_pmu("ltq_etop", "switch", 0, PMU_SWITCH); ++ clkdev_add_pmu("ltq_dsl", NULL, 0, ++ PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | ++ PMU_PPE_QSB | PMU_AHBS | PMU_DFE); + } else { + clkdev_add_static(ltq_danube_cpu_hz(), ltq_danube_fpi_hz(), + ltq_danube_io_region_clock()); ++ clkdev_add_pmu("ltq_dsl", NULL, 0, ++ PMU_PPE_EMA | PMU_PPE_TC | PMU_PPE_SLL01 | ++ PMU_PPE_QSB | PMU_AHBS | PMU_DFE); + } + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0062-MIPS-lantiq-dont-always-register-asc0.patch b/target/linux/lantiq/patches-3.3/0062-MIPS-lantiq-dont-always-register-asc0.patch new file mode 100644 index 0000000000..185210f220 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0062-MIPS-lantiq-dont-always-register-asc0.patch @@ -0,0 +1,31 @@ +From d9acb5a4a0b3781a63f805695319fc8d2d4ec427 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 20 Mar 2012 08:22:11 +0100 +Subject: [PATCH 62/70] MIPS: lantiq: dont always register asc0 + +--- + arch/mips/lantiq/xway/prom.c | 6 ++---- + 1 files changed, 2 insertions(+), 4 deletions(-) + +diff --git a/arch/mips/lantiq/xway/prom.c b/arch/mips/lantiq/xway/prom.c +index e3dcbbd..f776d5a 100644 +--- a/arch/mips/lantiq/xway/prom.c ++++ b/arch/mips/lantiq/xway/prom.c +@@ -101,12 +101,10 @@ void __init ltq_soc_detect(struct ltq_soc_info *i) + + void __init ltq_soc_setup(void) + { +- if (ltq_is_ase()) { ++ if (ltq_is_ase()) + ltq_register_ase_asc(); +- } else { +- ltq_register_asc(0); ++ else + ltq_register_asc(1); +- } + ltq_register_gpio(); + ltq_register_wdt(); + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0063-MIPS-lantiq-irqs-were-not-cleared-properly-on-boot.patch b/target/linux/lantiq/patches-3.3/0063-MIPS-lantiq-irqs-were-not-cleared-properly-on-boot.patch new file mode 100644 index 0000000000..85573284be --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0063-MIPS-lantiq-irqs-were-not-cleared-properly-on-boot.patch @@ -0,0 +1,34 @@ +From 875a7139535da541aa2e9de308b3f6f791a131d0 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Tue, 20 Mar 2012 09:44:27 +0100 +Subject: [PATCH 63/70] MIPS: lantiq: irqs were not cleared properly on boot + +--- + arch/mips/lantiq/irq.c | 10 +++++----- + 1 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/arch/mips/lantiq/irq.c b/arch/mips/lantiq/irq.c +index f17caec..7224231 100644 +--- a/arch/mips/lantiq/irq.c ++++ b/arch/mips/lantiq/irq.c +@@ -326,12 +326,12 @@ void __init arch_init_irq(void) + panic("Failed to remap eiu memory\n"); + } + +- /* make sure all irqs are turned off by default */ +- for (i = 0; i < 5; i++) ++ for (i = 0; i < 5; i++) { ++ /* make sure all irqs are turned off by default */ + ltq_icu_w32(0, LTQ_ICU_IM0_IER + (i * LTQ_ICU_OFFSET)); +- +- /* clear all possibly pending interrupts */ +- ltq_icu_w32(~0, LTQ_ICU_IM0_ISR + (i * LTQ_ICU_OFFSET)); ++ /* clear all possibly pending interrupts */ ++ ltq_icu_w32(~0, LTQ_ICU_IM0_ISR + (i * LTQ_ICU_OFFSET)); ++ } + + mips_cpu_irq_init(); + +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0064-MIPS-lantiq-adds-bootsel-helper.patch b/target/linux/lantiq/patches-3.3/0064-MIPS-lantiq-adds-bootsel-helper.patch new file mode 100644 index 0000000000..d89b3a170f --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0064-MIPS-lantiq-adds-bootsel-helper.patch @@ -0,0 +1,66 @@ +From 7746053f44b55a7cd914e1b7753cde7ac39e6fd6 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 21 Mar 2012 14:17:37 +0100 +Subject: [PATCH 64/70] MIPS: lantiq: adds bootsel helper + +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_soc.h | 12 ++++++++++++ + arch/mips/lantiq/xway/reset.c | 12 +++++++++++- + 2 files changed, 23 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +index bfdeb16..1ec8f2a 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_soc.h +@@ -144,6 +144,18 @@ + #define LTQ_MPS_BASE_ADDR (KSEG1 + 0x1F107000) + #define LTQ_MPS_CHIPID ((u32 *)(LTQ_MPS_BASE_ADDR + 0x0344)) + ++/* BOOT_SEL - find what boot media we have */ ++#define BS_EXT_ROM 0x0 ++#define BS_FLASH 0x1 ++#define BS_MII0 0x2 ++#define BS_PCI 0x3 ++#define BS_UART1 0x4 ++#define BS_SPI 0x5 ++#define BS_NAND 0x6 ++#define BS_RMII0 0x7 ++ ++extern unsigned char ltq_boot_select(void); ++ + /* register access macros for EBU and CGU */ + #define ltq_ebu_w32(x, y) ltq_w32((x), ltq_ebu_membase + (y)) + #define ltq_ebu_r32(x) ltq_r32(ltq_ebu_membase + (x)) +diff --git a/arch/mips/lantiq/xway/reset.c b/arch/mips/lantiq/xway/reset.c +index abdf9b1..970ca17 100644 +--- a/arch/mips/lantiq/xway/reset.c ++++ b/arch/mips/lantiq/xway/reset.c +@@ -27,7 +27,11 @@ + #define RCU_RST_STAT 0x0014 + + /* reset cause */ +-#define RCU_STAT_SHIFT 26 ++#define RCU_STAT_SHIFT 26 ++/* boot selection */ ++#define RCU_BOOT_SEL_SHIFT 26 ++#define RCU_BOOT_SEL_MASK 0x7 ++ + /* Global SW Reset */ + #define RCU_RD_SRST BIT(30) + /* Memory Controller */ +@@ -75,6 +79,12 @@ int ltq_reset_cause(void) + } + EXPORT_SYMBOL_GPL(ltq_reset_cause); + ++unsigned char ltq_boot_select(void) ++{ ++ u32 val = ltq_rcu_r32(RCU_RST_STAT); ++ return (val >> RCU_BOOT_SEL_SHIFT) & RCU_BOOT_SEL_MASK; ++} ++ + void ltq_reset_once(unsigned int module, ulong usec) + { + ltq_rcu_w32(ltq_rcu_r32(RCU_RST_REQ) | module, RCU_RST_REQ); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0065-MIPS-lantiq-adds-USB_ARCH_HAS_HCD-to-CONFIG_LANTIQ.patch b/target/linux/lantiq/patches-3.3/0065-MIPS-lantiq-adds-USB_ARCH_HAS_HCD-to-CONFIG_LANTIQ.patch new file mode 100644 index 0000000000..c53d009d7e --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0065-MIPS-lantiq-adds-USB_ARCH_HAS_HCD-to-CONFIG_LANTIQ.patch @@ -0,0 +1,24 @@ +From 495ab2cb0a347dd8270634e42993418eb8122c9e Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Fri, 23 Mar 2012 11:28:22 +0100 +Subject: [PATCH 65/70] MIPS: lantiq: adds USB_ARCH_HAS_HCD to CONFIG_LANTIQ + +--- + arch/mips/Kconfig | 1 + + 1 files changed, 1 insertions(+), 0 deletions(-) + +diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig +index df21d0d..8d3b920 100644 +--- a/arch/mips/Kconfig ++++ b/arch/mips/Kconfig +@@ -232,6 +232,7 @@ config LANTIQ + select CLKDEV_LOOKUP + select HAVE_OPROFILE + select MIPS_MACHINE ++ select USB_ARCH_HAS_HCD + + config LASAT + bool "LASAT Networks platforms" +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0066-MIPS-lantiq-fixes-bad-PMU_USB0-0-define.patch b/target/linux/lantiq/patches-3.3/0066-MIPS-lantiq-fixes-bad-PMU_USB0-0-define.patch new file mode 100644 index 0000000000..e711bdd0ef --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0066-MIPS-lantiq-fixes-bad-PMU_USB0-0-define.patch @@ -0,0 +1,25 @@ +From 307d2969597246720e7fdfad2a28a2561fb0b951 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 5 Apr 2012 21:51:05 +0200 +Subject: [PATCH 66/70] MIPS: lantiq: fixes bad PMU_USB0(0) define + +--- + arch/mips/lantiq/xway/sysctrl.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 3672fc6..5807456 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -40,7 +40,7 @@ + #define PMU_USB0_P BIT(0) + #define PMU_PCI BIT(4) + #define PMU_DMA BIT(5) +-#define PMU_USB0 BIT(5) ++#define PMU_USB0 BIT(6) + #define PMU_EPHY BIT(7) /* ase */ + #define PMU_SPI BIT(8) + #define PMU_DFE BIT(9) +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0067-MIPS-lantiq-fix-dwc_otg-usb-for-ase.patch b/target/linux/lantiq/patches-3.3/0067-MIPS-lantiq-fix-dwc_otg-usb-for-ase.patch new file mode 100644 index 0000000000..65f3cb05f8 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0067-MIPS-lantiq-fix-dwc_otg-usb-for-ase.patch @@ -0,0 +1,60 @@ +From 6f421cda8e6e43f74b8f8a7f63db67c6d3ac9ba3 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 11 Apr 2012 18:43:50 +0200 +Subject: [PATCH 67/70] MIPS: lantiq: fix dwc_otg usb for ase + +changed irq number and pmu settings. little bit of fiddling to get the now variable +irq into resources. + +Signed-off-by: Conor O'Gorman +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_irq.h | 1 + + drivers/usb/dwc_otg/dwc_otg_driver.c | 3 +++ + drivers/usb/dwc_otg/dwc_otg_ifx.c | 5 ++++- + 3 files changed, 8 insertions(+), 1 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +index b7f10e6..d9c892b 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +@@ -36,6 +36,7 @@ + + #define LTQ_TIMER6_INT (INT_NUM_IM1_IRL0 + 23) + #define LTQ_USB_INT (INT_NUM_IM1_IRL0 + 22) ++#define LTQ_USB_ASE_INT (INT_NUM_IM0_IRL0 + 31) + #define LTQ_USB_OC_INT (INT_NUM_IM4_IRL0 + 23) + + #define MIPS_CPU_TIMER_IRQ 7 +diff --git a/drivers/usb/dwc_otg/dwc_otg_driver.c b/drivers/usb/dwc_otg/dwc_otg_driver.c +index 1b0daab..5c64ebb 100644 +--- a/drivers/usb/dwc_otg/dwc_otg_driver.c ++++ b/drivers/usb/dwc_otg/dwc_otg_driver.c +@@ -860,6 +860,9 @@ static int __init dwc_otg_init(void) + + printk(KERN_INFO "%s: version %s\n", dwc_driver_name, DWC_DRIVER_VERSION); + ++ if (ltq_is_ase()) ++ dwc_irq = LTQ_USB_ASE_INT; ++ + // ifxmips setup + retval = ifx_usb_hc_init(dwc_iomem_base, dwc_irq); + if (retval < 0) +diff --git a/drivers/usb/dwc_otg/dwc_otg_ifx.c b/drivers/usb/dwc_otg/dwc_otg_ifx.c +index 0a4c209..e45da85 100644 +--- a/drivers/usb/dwc_otg/dwc_otg_ifx.c ++++ b/drivers/usb/dwc_otg/dwc_otg_ifx.c +@@ -61,7 +61,10 @@ void dwc_otg_power_on (void) + // clear power + writel(readl(DANUBE_PMU_PWDCR) | 0x41, DANUBE_PMU_PWDCR); + // set clock gating +- writel(readl(DANUBE_CGU_IFCCR) | 0x30, DANUBE_CGU_IFCCR); ++ if (ltq_is_ase()) ++ writel(readl(DANUBE_CGU_IFCCR) & ~0x20, DANUBE_CGU_IFCCR); ++ else ++ writel(readl(DANUBE_CGU_IFCCR) | 0x30, DANUBE_CGU_IFCCR); + // set power + writel(readl(DANUBE_PMU_PWDCR) & ~0x1, DANUBE_PMU_PWDCR); + writel(readl(DANUBE_PMU_PWDCR) & ~0x40, DANUBE_PMU_PWDCR); +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0068-MIPS-lantiq-stp-fix-for-ase-add-get-clock-disabled.patch b/target/linux/lantiq/patches-3.3/0068-MIPS-lantiq-stp-fix-for-ase-add-get-clock-disabled.patch new file mode 100644 index 0000000000..ffa96cd50f --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0068-MIPS-lantiq-stp-fix-for-ase-add-get-clock-disabled.patch @@ -0,0 +1,88 @@ +From 0ea046d777bf567cfd89c7d968c0b962e3e7589b Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Wed, 11 Apr 2012 18:47:53 +0200 +Subject: [PATCH 68/70] MIPS: lantiq: stp, fix for ase, add get, clock + disabled + +Lantiq serial-to-parallel hardware gpio module +Added gpio pins as used for amazon se (ase) +Added get to enable reporting of gpio status +Changed to use software update, as hw clock was not running on ase. Clock +really only needed if hw flashing was implemented. + +Signed-off-by: Conor O'Gorman +--- + arch/mips/lantiq/xway/gpio_stp.c | 22 +++++++++++++--------- + 1 files changed, 13 insertions(+), 9 deletions(-) + +diff --git a/arch/mips/lantiq/xway/gpio_stp.c b/arch/mips/lantiq/xway/gpio_stp.c +index 9610c10..791beeb 100644 +--- a/arch/mips/lantiq/xway/gpio_stp.c ++++ b/arch/mips/lantiq/xway/gpio_stp.c +@@ -27,6 +27,7 @@ + #define LTQ_STP_AR 0x10 + + #define LTQ_STP_CON_SWU (1 << 31) ++#define LTQ_STP_SWU_MASK (1 << 31) + #define LTQ_STP_2HZ 0 + #define LTQ_STP_4HZ (1 << 23) + #define LTQ_STP_8HZ (2 << 23) +@@ -60,6 +61,12 @@ static void ltq_stp_set(struct gpio_chip *chip, unsigned offset, int value) + else + ltq_stp_shadow &= ~(1 << offset); + ltq_stp_w32(ltq_stp_shadow, LTQ_STP_CPU0); ++ ltq_stp_w32_mask(LTQ_STP_SWU_MASK, LTQ_STP_CON_SWU, LTQ_STP_CON0); ++} ++ ++static int ltq_stp_get(struct gpio_chip *chip, unsigned offset) ++{ ++ return !!(ltq_stp_r32(LTQ_STP_CPU0) & (1<dev, 4, 2, 1, "stp-st") || +- ltq_gpio_request(&pdev->dev, 5, 2, 1, "stp-d") || +- ltq_gpio_request(&pdev->dev, 6, 2, 1, "stp-sh")) { ++ pin = ltq_is_ase() ? 1 : 4; ++ if (ltq_gpio_request(&pdev->dev, pin, 2, 1, "stp-st") || ++ ltq_gpio_request(&pdev->dev, pin+1, 2, 1, "stp-d") || ++ ltq_gpio_request(&pdev->dev, pin+2, 2, 1, "stp-sh")) { + dev_err(&pdev->dev, "failed to request needed gpios\n"); + return -EBUSY; + } +-- +1.7.9.1 + diff --git a/target/linux/lantiq/patches-3.3/0069-MIPS-lantiq-fix-spi-for-ase-update-for-clkdev-and-pl.patch b/target/linux/lantiq/patches-3.3/0069-MIPS-lantiq-fix-spi-for-ase-update-for-clkdev-and-pl.patch new file mode 100644 index 0000000000..2ea1547ca3 --- /dev/null +++ b/target/linux/lantiq/patches-3.3/0069-MIPS-lantiq-fix-spi-for-ase-update-for-clkdev-and-pl.patch @@ -0,0 +1,197 @@ +From e765119d933dd84de2c095d422f1609486775f79 Mon Sep 17 00:00:00 2001 +From: John Crispin +Date: Thu, 12 Apr 2012 13:25:42 +0200 +Subject: [PATCH 69/70] MIPS: lantiq: fix spi for ase, update for clkdev and + platform driver + +irqs, gpios, chipselects +updated to use module_platform_driver() +clkdev is a bit hacky, using ltq_spi.0, as specifying no device numbering led to +the mtd driver not hooking up to an spi flash. + +Signed-off-by: Conor O'Gorman +--- + .../mips/include/asm/mach-lantiq/xway/lantiq_irq.h | 4 ++ + arch/mips/lantiq/xway/sysctrl.c | 2 +- + drivers/spi/spi-xway.c | 58 ++++++++++---------- + 3 files changed, 35 insertions(+), 29 deletions(-) + +diff --git a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +index d9c892b..d86acdd 100644 +--- a/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h ++++ b/arch/mips/include/asm/mach-lantiq/xway/lantiq_irq.h +@@ -30,6 +30,10 @@ + #define LTQ_SSC_TIR_AR9 (INT_NUM_IM0_IRL0 + 14) + #define LTQ_SSC_RIR_AR9 (INT_NUM_IM0_IRL0 + 15) + #define LTQ_SSC_EIR (INT_NUM_IM0_IRL0 + 16) ++#define LTQ_SSC_RIR_ASE (INT_NUM_IM0_IRL0 + 16) ++#define LTQ_SSC_TIR_ASE (INT_NUM_IM0_IRL0 + 17) ++#define LTQ_SSC_EIR_ASE (INT_NUM_IM0_IRL0 + 18) ++#define LTQ_SSC_FIR_ASE (INT_NUM_IM0_IRL0 + 19) + + #define LTQ_MEI_DYING_GASP_INT (INT_NUM_IM1_IRL0 + 21) + #define LTQ_MEI_INT (INT_NUM_IM1_IRL0 + 23) +diff --git a/arch/mips/lantiq/xway/sysctrl.c b/arch/mips/lantiq/xway/sysctrl.c +index 5807456..de4ce8f 100644 +--- a/arch/mips/lantiq/xway/sysctrl.c ++++ b/arch/mips/lantiq/xway/sysctrl.c +@@ -233,7 +233,7 @@ void __init ltq_soc_init(void) + clkdev_add_pmu("ltq_fpi", NULL, 0, PMU_FPI); + clkdev_add_pmu("ltq_dma", NULL, 0, PMU_DMA); + clkdev_add_pmu("ltq_stp", NULL, 0, PMU_STP); +- clkdev_add_pmu("ltq_spi", NULL, 0, PMU_SPI); ++ clkdev_add_pmu("ltq_spi.0", NULL, 0, PMU_SPI); + clkdev_add_pmu("ltq_gptu", NULL, 0, PMU_GPT); + clkdev_add_pmu("ltq_ebu", NULL, 0, PMU_EBU); + if (!ltq_is_vr9()) +diff --git a/drivers/spi/spi-xway.c b/drivers/spi/spi-xway.c +index 016a6d0..be5c25b 100644 +--- a/drivers/spi/spi-xway.c ++++ b/drivers/spi/spi-xway.c +@@ -143,9 +143,9 @@ + #define LTQ_SPI_IRNEN_ALL 0xF + + /* Hard-wired GPIOs used by SPI controller */ +-#define LTQ_SPI_GPIO_DI 16 +-#define LTQ_SPI_GPIO_DO 17 +-#define LTQ_SPI_GPIO_CLK 18 ++#define LTQ_SPI_GPIO_DI (ltq_is_ase()? 8 : 16) ++#define LTQ_SPI_GPIO_DO (ltq_is_ase()? 9 : 17) ++#define LTQ_SPI_GPIO_CLK (ltq_is_ase()? 10 : 18) + + struct ltq_spi { + struct spi_bitbang bitbang; +@@ -229,7 +229,7 @@ static void ltq_spi_hw_enable(struct ltq_spi *hw) + u32 clc; + + /* Power-up mdule */ +- clk_enable(hw->spiclk); ++ clk_enable(hw->spiclk); + + /* + * Set clock divider for run mode to 1 to +@@ -245,7 +245,7 @@ static void ltq_spi_hw_disable(struct ltq_spi *hw) + ltq_spi_reg_write(hw, LTQ_SPI_CLC_DISS, LTQ_SPI_CLC); + + /* Power-down mdule */ +- clk_disable(hw->spiclk); ++ clk_disable(hw->spiclk); + } + + static void ltq_spi_reset_fifos(struct ltq_spi *hw) +@@ -284,7 +284,7 @@ static inline int ltq_spi_wait_ready(struct ltq_spi *hw) + cond_resched(); + } while (!time_after_eq(jiffies, timeout)); + +- dev_err(hw->dev, "SPI wait ready timed out\n"); ++ dev_err(hw->dev, "SPI wait ready timed out stat: %x\n", stat); + + return -ETIMEDOUT; + } +@@ -556,6 +556,12 @@ static const struct ltq_spi_cs_gpio_map ltq_spi_cs[] = { + { 11, 3 }, + }; + ++static const struct ltq_spi_cs_gpio_map ltq_spi_cs_ase[] = { ++ { 7, 2 }, ++ { 15, 1 }, ++ { 14, 1 }, ++}; ++ + static int ltq_spi_setup(struct spi_device *spi) + { + struct ltq_spi *hw = ltq_spi_to_hw(spi); +@@ -600,8 +606,10 @@ static int ltq_spi_setup(struct spi_device *spi) + cstate->cs_activate = ltq_spi_gpio_cs_activate; + cstate->cs_deactivate = ltq_spi_gpio_cs_deactivate; + } else { +- ret = ltq_gpio_request(&spi->dev, ltq_spi_cs[spi->chip_select].gpio, +- ltq_spi_cs[spi->chip_select].mux, ++ struct ltq_spi_cs_gpio_map *cs_map = ++ ltq_is_ase() ? ltq_spi_cs_ase : ltq_spi_cs; ++ ret = ltq_gpio_request(&spi->dev, cs_map[spi->chip_select].gpio, ++ cs_map[spi->chip_select].mux, + 1, "spi-cs"); + if (ret) + return -EBUSY; +@@ -633,7 +641,8 @@ static void ltq_spi_cleanup(struct spi_device *spi) + if (cdata && cdata->gpio) + gpio = cdata->gpio; + else +- gpio = ltq_spi_cs[spi->chip_select].gpio; ++ gpio = ltq_is_ase() ? ltq_spi_cs_ase[spi->chip_select].gpio : ++ ltq_spi_cs[spi->chip_select].gpio; + + gpio_free(gpio); + kfree(cstate); +@@ -868,7 +877,8 @@ static const struct ltq_spi_irq_map ltq_spi_irqs[] = { + { "spi_err", ltq_spi_err_irq }, + }; + +-static int __init ltq_spi_probe(struct platform_device *pdev) ++static int __devinit ++ltq_spi_probe(struct platform_device *pdev) + { + struct spi_master *master; + struct resource *r; +@@ -910,14 +920,14 @@ static int __init ltq_spi_probe(struct platform_device *pdev) + + hw->fpiclk = clk_get_fpi(); + if (IS_ERR(hw->fpiclk)) { +- dev_err(&pdev->dev, "clk_get\n"); ++ dev_err(&pdev->dev, "fpi clk\n"); + ret = PTR_ERR(hw->fpiclk); + goto err_master; + } + + hw->spiclk = clk_get(&pdev->dev, NULL); + if (IS_ERR(hw->spiclk)) { +- dev_err(&pdev->dev, "clk_get\n"); ++ dev_err(&pdev->dev, "spi clk\n"); + ret = PTR_ERR(hw->spiclk); + goto err_master; + } +@@ -1014,7 +1024,8 @@ err: + return ret; + } + +-static int __exit ltq_spi_remove(struct platform_device *pdev) ++static int __devexit ++ltq_spi_remove(struct platform_device *pdev) + { + struct ltq_spi *hw = platform_get_drvdata(pdev); + int ret, i; +@@ -1043,24 +1054,15 @@ static int __exit ltq_spi_remove(struct platform_device *pdev) + } + + static struct platform_driver ltq_spi_driver = { ++ .probe = ltq_spi_probe, ++ .remove = __devexit_p(ltq_spi_remove), + .driver = { +- .name = "ltq_spi", +- .owner = THIS_MODULE, +- }, +- .remove = __exit_p(ltq_spi_remove), ++ .name = "ltq_spi", ++ .owner = THIS_MODULE, ++ }, + }; + +-static int __init ltq_spi_init(void) +-{ +- return platform_driver_probe(<q_spi_driver, ltq_spi_probe); +-} +-module_init(ltq_spi_init); +- +-static void __exit ltq_spi_exit(void) +-{ +- platform_driver_unregister(<q_spi_driver); +-} +-module_exit(ltq_spi_exit); ++module_platform_driver(ltq_spi_driver); + + MODULE_DESCRIPTION("Lantiq SoC SPI controller driver"); + MODULE_AUTHOR("Daniel Schwierzeck "); +-- +1.7.9.1 +

Parameter NameMeaning
otg_capSpecifies the OTG capabilities. The driver will automatically detect the ++ value for this parameter if none is specified. ++ - 0: HNP and SRP capable (default, if available) ++ - 1: SRP Only capable ++ - 2: No HNP/SRP capable ++
dma_enableSpecifies whether to use slave or DMA mode for accessing the data FIFOs. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Slave ++ - 1: DMA (default, if available) ++
dma_burst_sizeThe DMA Burst size (applicable only for External DMA Mode). ++ - Values: 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++
speedSpecifies the maximum speed of operation in host and device mode. The ++ actual speed depends on the speed of the attached device and the value of ++ phy_type. ++ - 0: High Speed (default) ++ - 1: Full Speed ++
host_support_fs_ls_low_powerSpecifies whether low power mode is supported when attached to a Full ++ Speed or Low Speed device in host mode. ++ - 0: Don't support low power mode (default) ++ - 1: Support low power mode ++
host_ls_low_power_phy_clkSpecifies the PHY clock rate in low power mode when connected to a Low ++ Speed device in host mode. This parameter is applicable only if ++ HOST_SUPPORT_FS_LS_LOW_POWER is enabled. ++ - 0: 48 MHz (default) ++ - 1: 6 MHz ++
enable_dynamic_fifo Specifies whether FIFOs may be resized by the driver software. ++ - 0: Use cC FIFO size parameters ++ - 1: Allow dynamic FIFO sizing (default) ++
data_fifo_sizeTotal number of 4-byte words in the data FIFO memory. This memory ++ includes the Rx FIFO, non-periodic Tx FIFO, and periodic Tx FIFOs. ++ - Values: 32 to 32768 (default 8192) ++ ++ Note: The total FIFO memory depth in the FPGA configuration is 8192. ++
dev_rx_fifo_sizeNumber of 4-byte words in the Rx FIFO in device mode when dynamic ++ FIFO sizing is enabled. ++ - Values: 16 to 32768 (default 1064) ++
dev_nperio_tx_fifo_sizeNumber of 4-byte words in the non-periodic Tx FIFO in device mode when ++ dynamic FIFO sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++
dev_perio_tx_fifo_size_n (n = 1 to 15)Number of 4-byte words in each of the periodic Tx FIFOs in device mode ++ when dynamic FIFO sizing is enabled. ++ - Values: 4 to 768 (default 256) ++
host_rx_fifo_sizeNumber of 4-byte words in the Rx FIFO in host mode when dynamic FIFO ++ sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++
host_nperio_tx_fifo_sizeNumber of 4-byte words in the non-periodic Tx FIFO in host mode when ++ dynamic FIFO sizing is enabled in the core. ++ - Values: 16 to 32768 (default 1024) ++
host_perio_tx_fifo_sizeNumber of 4-byte words in the host periodic Tx FIFO when dynamic FIFO ++ sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++
max_transfer_sizeThe maximum transfer size supported in bytes. ++ - Values: 2047 to 65,535 (default 65,535) ++
max_packet_countThe maximum number of packets in a transfer. ++ - Values: 15 to 511 (default 511) ++
host_channelsThe number of host channel registers to use. ++ - Values: 1 to 16 (default 12) ++ ++ Note: The FPGA configuration supports a maximum of 12 host channels. ++
dev_endpointsThe number of endpoints in addition to EP0 available for device mode ++ operations. ++ - Values: 1 to 15 (default 6 IN and OUT) ++ ++ Note: The FPGA configuration supports a maximum of 6 IN and OUT endpoints in ++ addition to EP0. ++
phy_typeSpecifies the type of PHY interface to use. By default, the driver will ++ automatically detect the phy_type. ++ - 0: Full Speed ++ - 1: UTMI+ (default, if available) ++ - 2: ULPI ++
phy_utmi_widthSpecifies the UTMI+ Data Width. This parameter is applicable for a ++ phy_type of UTMI+. Also, this parameter is applicable only if the ++ OTG_HSPHY_WIDTH cC parameter was set to "8 and 16 bits", meaning that the ++ core has been configured to work at either data path width. ++ - Values: 8 or 16 bits (default 16) ++
phy_ulpi_ddrSpecifies whether the ULPI operates at double or single data rate. This ++ parameter is only applicable if phy_type is ULPI. ++ - 0: single data rate ULPI interface with 8 bit wide data bus (default) ++ - 1: double data rate ULPI interface with 4 bit wide data bus ++
i2c_enableSpecifies whether to use the I2C interface for full speed PHY. This ++ parameter is only applicable if PHY_TYPE is FS. ++ - 0: Disabled (default) ++ - 1: Enabled ++
otg_en_multiple_tx_fifoSpecifies whether dedicatedto tx fifos are enabled for non periodic IN EPs. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Disabled ++ - 1: Enabled (default, if available) ++
dev_tx_fifo_size_n (n = 1 to 15)Number of 4-byte words in each of the Tx FIFOs in device mode ++ when dynamic FIFO sizing is enabled. ++ - Values: 4 to 768 (default 256) ++