--- /dev/null +++ b/arch/mips/lantiq/xway/Kconfig @@ -0,0 +1,19 @@ +if SOC_LANTIQ_XWAY + +menu "Mips Machine" + +config LANTIQ_MACH_EASY50812 + bool "Easy50812" + default y + +config LANTIQ_MACH_EASY50712 + bool "Easy50712" + default y + +config LANTIQ_MACH_EASY4010 + bool "Easy4010" + default y + +endmenu + +endif --- /dev/null +++ b/arch/mips/lantiq/xway/gpio_ebu.c @@ -0,0 +1,116 @@ +/* + * 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 + +#define LQ_EBU_BUSCON 0x1e7ff +#define LQ_EBU_WP 0x80000000 + +static int shadow = 0x0000; +static void __iomem *virt; + +static int +lq_ebu_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + return 0; +} + +static void +lq_ebu_apply(void) +{ + unsigned long flags; + spin_lock_irqsave(&ebu_lock, flags); + lq_w32(LQ_EBU_BUSCON, LQ_EBU_BUSCON1); + *((__u16*)virt) = shadow; + lq_w32(LQ_EBU_BUSCON | LQ_EBU_WP, LQ_EBU_BUSCON1); + spin_unlock_irqrestore(&ebu_lock, flags); +} + +static void +lq_ebu_set(struct gpio_chip *chip, unsigned offset, int value) +{ + if(value) + shadow |= (1 << offset); + else + shadow &= ~(1 << offset); + lq_ebu_apply(); +} + +static struct gpio_chip +lq_ebu_chip = +{ + .label = "lq_ebu", + .direction_output = lq_ebu_direction_output, + .set = lq_ebu_set, + .base = 32, + .ngpio = 16, + .can_sleep = 0, + .owner = THIS_MODULE, +}; + +static int __devinit +lq_ebu_probe(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int ret = 0; + if (!res) + return -ENOENT; + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) + return -EBUSY; + + /* tell the ebu controller which mem addr we will be using */ + lq_w32(pdev->resource->start | 0x1, LQ_EBU_ADDRSEL1); + lq_w32(LQ_EBU_BUSCON | LQ_EBU_WP, LQ_EBU_BUSCON1); + + virt = ioremap_nocache(res->start, resource_size(res)); + if (!virt) + { + dev_err(&pdev->dev, "Failed to ioremap mem region\n"); + ret = -ENOMEM; + goto err_release_mem_region; + } + /* grab the default settings passed form the platform code */ + shadow = (unsigned int) pdev->dev.platform_data; + + ret = gpiochip_add(&lq_ebu_chip); + if (!ret) + { + lq_ebu_apply(); + return 0; + } + +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); + return ret; +} + +static struct platform_driver +lq_ebu_driver = { + .probe = lq_ebu_probe, + .driver = { + .name = "lq_ebu", + .owner = THIS_MODULE, + }, +}; + +static int __init +init_lq_ebu(void) +{ + return platform_driver_register(&lq_ebu_driver); +} + +postcore_initcall(init_lq_ebu); --- /dev/null +++ b/arch/mips/lantiq/xway/gpio_leds.c @@ -0,0 +1,161 @@ +/* + * 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) 2007 John Crispin + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define LQ_STP_BASE 0x1E100BB0 +#define LQ_STP_SIZE 0x40 + +#define LQ_STP_CON0 0x00 +#define LQ_STP_CON1 0x04 +#define LQ_STP_CPU0 0x08 +#define LQ_STP_CPU1 0x0C +#define LQ_STP_AR 0x10 + +#define STP_CON0_SWU (1 << 31) + +#define LQ_STP_2HZ (0) +#define LQ_STP_4HZ (1 << 23) +#define LQ_STP_8HZ (2 << 23) +#define LQ_STP_10HZ (3 << 23) +#define LQ_STP_MASK (0xf << 23) + +#define LQ_STP_UPD_SRC_FPI (1 << 31) +#define LQ_STP_UPD_MASK (3 << 30) +#define LQ_STP_ADSL_SRC (3 << 24) + +#define LQ_STP_GROUP0 (1 << 0) + +#define LQ_STP_RISING 0 +#define LQ_STP_FALLING (1 << 26) +#define LQ_STP_EDGE_MASK (1 << 26) + +#define lq_stp_r32(reg) __raw_readl(virt + reg) +#define lq_stp_w32(val, reg) __raw_writel(val, virt + reg) +#define lq_stp_w32_mask(clear, set, reg) \ + lq_w32((lq_r32(virt + reg) & ~clear) | set, virt + reg) + +static int shadow = 0xffff; +static void __iomem *virt; + +static int +lq_stp_direction_output(struct gpio_chip *chip, unsigned offset, int value) +{ + return 0; +} + +static void +lq_stp_set(struct gpio_chip *chip, unsigned offset, int value) +{ + if(value) + shadow |= (1 << offset); + else + shadow &= ~(1 << offset); + lq_stp_w32(shadow, LQ_STP_CPU0); +} + +static struct gpio_chip lq_stp_chip = +{ + .label = "lq_stp", + .direction_output = lq_stp_direction_output, + .set = lq_stp_set, + .base = 48, + .ngpio = 24, + .can_sleep = 0, + .owner = THIS_MODULE, +}; + +static int +lq_stp_hw_init(void) +{ + /* the 3 pins used to control the external stp */ + lq_gpio_request(4, 1, 0, 1, "stp-st"); + lq_gpio_request(5, 1, 0, 1, "stp-d"); + lq_gpio_request(6, 1, 0, 1, "stp-sh"); + + /* sane defaults */ + lq_stp_w32(0, LQ_STP_AR); + lq_stp_w32(0, LQ_STP_CPU0); + lq_stp_w32(0, LQ_STP_CPU1); + lq_stp_w32(STP_CON0_SWU, LQ_STP_CON0); + lq_stp_w32(0, LQ_STP_CON1); + + /* rising or falling edge */ + lq_stp_w32_mask(LQ_STP_EDGE_MASK, LQ_STP_FALLING, LQ_STP_CON0); + + /* per default stp 15-0 are set */ + lq_stp_w32_mask(0, LQ_STP_GROUP0, LQ_STP_CON1); + + /* stp are update periodically by the FPID */ + lq_stp_w32_mask(LQ_STP_UPD_MASK, LQ_STP_UPD_SRC_FPI, LQ_STP_CON1); + + /* set stp update speed */ + lq_stp_w32_mask(LQ_STP_MASK, LQ_STP_8HZ, LQ_STP_CON1); + + /* adsl 0 and 1 stp are updated by the arc */ + lq_stp_w32_mask(0, LQ_STP_ADSL_SRC, LQ_STP_CON0); + + lq_pmu_enable(PMU_LED); + return 0; +} + +static int +lq_stp_probe(struct platform_device *pdev) +{ + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int ret = 0; + if (!res) + return -ENOENT; + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if (!res) + return -EBUSY; + virt = ioremap_nocache(res->start, resource_size(res)); + if(!virt) + { + ret = -ENOMEM; + goto err_release_mem_region; + } + ret = gpiochip_add(&lq_stp_chip); + if(!ret) + return lq_stp_hw_init(); + + iounmap(virt); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); + return ret; +} + +static struct platform_driver lq_stp_driver = { + .probe = lq_stp_probe, + .driver = { + .name = "lq_stp", + .owner = THIS_MODULE, + }, +}; + +int __init +init_lq_stp(void) +{ + int ret = platform_driver_register(&lq_stp_driver); + if (ret) + printk(KERN_INFO + "lq_stp: error registering platfom driver"); + return ret; +} + +postcore_initcall(init_lq_stp); --- /dev/null +++ b/arch/mips/lantiq/xway/mach-easy4010.c @@ -0,0 +1,82 @@ +/* + * 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 "devices.h" + +#ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition easy4010_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "uboot_env", + .offset = 0x20000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x30000, + .size = 0x3D0000, + }, +}; +#endif + +static struct physmap_flash_data easy4010_flash_data = { +#ifdef CONFIG_MTD_PARTITIONS + .nr_parts = ARRAY_SIZE(easy4010_partitions), + .parts = easy4010_partitions, +#endif +}; + +static struct lq_pci_data lq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct lq_eth_data lq_eth_data = { + .mii_mode = REV_MII_MODE, +}; + +static void __init +easy4010_init(void) +{ + lq_register_gpio(); + lq_register_gpio_stp(); + lq_register_asc(0); + lq_register_asc(1); + lq_register_nor(&easy4010_flash_data); + lq_register_wdt(); + lq_register_pci(&lq_pci_data); + lq_register_ethernet(&lq_eth_data); +} + +MIPS_MACHINE(LANTIQ_MACH_EASY4010, + "EASY4010", + "EASY4010 Eval Board", + easy4010_init); --- /dev/null +++ b/arch/mips/lantiq/xway/mach-easy50712.c @@ -0,0 +1,82 @@ +/* + * 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 "devices.h" + +#ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition easy50712_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x20000, + }, + { + .name = "uboot_env", + .offset = 0x20000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x30000, + .size = 0x3D0000, + }, +}; +#endif + +static struct physmap_flash_data easy50712_flash_data = { +#ifdef CONFIG_MTD_PARTITIONS + .nr_parts = ARRAY_SIZE(easy50712_partitions), + .parts = easy50712_partitions, +#endif +}; + +static struct lq_pci_data lq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct lq_eth_data lq_eth_data = { + .mii_mode = REV_MII_MODE, +}; + +static void __init +easy50712_init(void) +{ + lq_register_asc(0); + lq_register_asc(1); + lq_register_gpio(); + lq_register_gpio_stp(); + lq_register_nor(&easy50712_flash_data); + lq_register_wdt(); + lq_register_pci(&lq_pci_data); + lq_register_ethernet(&lq_eth_data); +} + +MIPS_MACHINE(LANTIQ_MACH_EASY50712, + "EASY50712", + "EASY50712 Eval Board", + easy50712_init); --- /dev/null +++ b/arch/mips/lantiq/xway/mach-easy50812.c @@ -0,0 +1,81 @@ +/* + * 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 "devices.h" + +#ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition easy50812_partitions[] = +{ + { + .name = "uboot", + .offset = 0x0, + .size = 0x40000, + }, + { + .name = "uboot_env", + .offset = 0x40000, + .size = 0x10000, + }, + { + .name = "linux", + .offset = 0x50000, + .size = 0x3B0000, + }, +}; +#endif + +static struct physmap_flash_data easy50812_flash_data = { +#ifdef CONFIG_MTD_PARTITIONS + .nr_parts = ARRAY_SIZE(easy50812_partitions), + .parts = easy50812_partitions, +#endif +}; + +static struct lq_pci_data lq_pci_data = { + .clock = PCI_CLOCK_INT, + .gpio = PCI_GNT1 | PCI_REQ1, + .irq = { + [14] = INT_NUM_IM0_IRL0 + 22, + }, +}; + +static struct lq_eth_data lq_eth_data = { + .mii_mode = REV_MII_MODE, +}; + +static void __init +easy50812_init(void) +{ + lq_register_gpio(); + lq_register_asc(0); + lq_register_asc(1); + lq_register_nor(&easy50812_flash_data); + lq_register_wdt(); + lq_register_pci(&lq_pci_data); + lq_register_ethernet(&lq_eth_data); +} + +MIPS_MACHINE(LANTIQ_MACH_EASY50812, + "EASY50812", + "EASY50812 Eval Board", + easy50812_init); --- /dev/null +++ b/arch/mips/lantiq/xway/prom.c @@ -0,0 +1,52 @@ +/* + * 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" + +#define SOC_DANUBE "Danube" +#define SOC_TWINPASS "Twinpass" +#define SOC_AR9 "AR9" + +void __init +lq_soc_detect(struct lq_soc_info *i) +{ + i->partnum = (lq_r32(LQ_MPS_CHIPID) & 0x0FFFFFFF) >> 12; + i->rev = (lq_r32(LQ_MPS_CHIPID) & 0xF0000000) >> 28; + 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: + printk(KERN_ERR "unknown chiprev : 0x%08X\n", i->partnum); + while(1) { }; + break; + } +} --- /dev/null +++ b/arch/mips/lantiq/xway/devices.c @@ -0,0 +1,336 @@ +/* + * 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 + +#include +#include +#include + +#define IRQ_RES(resname,irq) {.name=#resname,.start=(irq),.flags=IORESOURCE_IRQ} + +/* gpio leds */ +static struct gpio_led_platform_data lq_gpio_led_data; + +static struct platform_device lq_gpio_leds = +{ + .name = "leds-gpio", + .dev = { + .platform_data = (void *) &lq_gpio_led_data, + } +}; + +void __init +lq_register_gpio_leds(struct gpio_led *leds, int cnt) +{ + lq_gpio_led_data.leds = leds; + lq_gpio_led_data.num_leds = cnt; + platform_device_register(&lq_gpio_leds); +} + +/* gpio buttons */ +static struct gpio_buttons_platform_data lq_gpio_buttons_platform_data; + +static struct platform_device lq_gpio_buttons_platform_device = +{ + .name = "gpio-buttons", + .id = 0, + .dev = { + .platform_data = (void *) &lq_gpio_buttons_platform_data, + }, +}; + +void __init +lq_register_gpio_buttons(struct gpio_button *buttons, int cnt) +{ + lq_gpio_buttons_platform_data.buttons = buttons; + lq_gpio_buttons_platform_data.nbuttons = cnt; + platform_device_register(&lq_gpio_buttons_platform_device); +} + +/* serial to parallel conversion */ +static struct resource lq_stp_resource = +{ + .name = "stp", + .start = LQ_STP_BASE, + .end = LQ_STP_BASE + LQ_STP_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +void __init +lq_register_gpio_stp(void) +{ + platform_device_register_simple("lq_stp", 0, &lq_stp_resource, 1); +} + +/* nor flash */ +static struct resource lq_nor_resource = +{ + .name = "nor", + .start = LQ_FLASH_START, + .end = LQ_FLASH_START + LQ_FLASH_MAX - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device lq_nor = +{ + .name = "lq_nor", + .resource = &lq_nor_resource, + .num_resources = 1, +}; + +void __init +lq_register_nor(struct physmap_flash_data *data) +{ + lq_nor.dev.platform_data = data; + platform_device_register(&lq_nor); +} + +/* watchdog */ +static struct resource lq_wdt_resource = +{ + .name = "watchdog", + .start = LQ_WDT_BASE, + .end = LQ_WDT_BASE + LQ_WDT_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +void __init +lq_register_wdt(void) +{ + platform_device_register_simple("lq_wdt", 0, &lq_wdt_resource, 1); +} + +/* gpio */ +static struct resource lq_gpio_resource[] = { + { + .name = "gpio0", + .start = LQ_GPIO0_BASE_ADDR, + .end = LQ_GPIO0_BASE_ADDR + LQ_GPIO_SIZE - 1, + .flags = IORESOURCE_MEM, + }, { + .name = "gpio1", + .start = LQ_GPIO1_BASE_ADDR, + .end = LQ_GPIO1_BASE_ADDR + LQ_GPIO_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +void __init +lq_register_gpio(void) +{ + platform_device_register_simple("lq_gpio", 0, &lq_gpio_resource[0], 1); + platform_device_register_simple("lq_gpio", 1, &lq_gpio_resource[1], 1); +} + +/* pci */ +static struct platform_device lq_pci = +{ + .name = "lq_pci", + .num_resources = 0, +}; + +void __init +lq_register_pci(struct lq_pci_data *data) +{ + lq_pci.dev.platform_data = data; + platform_device_register(&lq_pci); +} + +/* ebu */ +static struct resource lq_ebu_resource = +{ + .name = "gpio_ebu", + .start = LQ_EBU_GPIO_START, + .end = LQ_EBU_GPIO_START + LQ_EBU_GPIO_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device lq_ebu = +{ + .name = "lq_ebu", + .resource = &lq_ebu_resource, + .num_resources = 1, +}; + +void __init +lq_register_gpio_ebu(unsigned int value) +{ + lq_ebu.dev.platform_data = (void*) value; + platform_device_register(&lq_ebu); +} + +/* ethernet */ +unsigned char lq_ethaddr[6] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + +static int __init +lq_set_ethaddr(char *str) +{ + sscanf(&str[8], "0%02hhx:0%02hhx:0%02hhx:0%02hhx:0%02hhx:0%02hhx", + &lq_ethaddr[0], &lq_ethaddr[1], &lq_ethaddr[2], + &lq_ethaddr[3], &lq_ethaddr[4], &lq_ethaddr[5]); + return 0; +} +__setup("ethaddr=", lq_set_ethaddr); + +static struct resource lq_ethernet_resources = +{ + .name = "etop", + .start = LQ_PPE32_BASE_ADDR, + .end = LQ_PPE32_BASE_ADDR + LQ_PPE32_SIZE - 1, + .flags = IORESOURCE_MEM, +}; + +static struct platform_device lq_ethernet = +{ + .name = "lq_etop", + .resource = &lq_ethernet_resources, + .num_resources = 1, +}; + +void __init +lq_register_ethernet(struct lq_eth_data *eth) +{ + if(!eth) + return; + if(!eth->mac) + eth->mac = lq_ethaddr; + if(!is_valid_ether_addr(eth->mac)) + random_ether_addr(eth->mac); + lq_ethernet.dev.platform_data = eth; + platform_device_register(&lq_ethernet); +} + +/* tapi */ +static struct resource mps_resources[] = { + { + .name = "voice-mem", + .flags = IORESOURCE_MEM, + .start = 0x1f107000, + .end = 0x1f1073ff, + }, + { + .name = "voice-mailbox", + .flags = IORESOURCE_MEM, + .start = 0x1f200000, + .end = 0x1f2007ff, + }, +}; + +static struct platform_device mps_device = { + .name = "mps", + .resource = mps_resources, + .num_resources = ARRAY_SIZE(mps_resources), +}; + +static struct platform_device vmmc_device = { + .name = "vmmc", + .dev = { + .parent = &mps_device.dev, + }, +}; + +static unsigned int *cp1_base; +unsigned int* +lq_get_cp1_base(void) +{ + return cp1_base; +} +EXPORT_SYMBOL(lq_get_cp1_base); + +void __init +lq_register_tapi(void) +{ +#define CP1_SIZE (1 << 20) + dma_addr_t dma; + mps_device.dev.platform_data = + cp1_base = + (void*)CPHYSADDR(dma_alloc_coherent(NULL, CP1_SIZE, &dma, GFP_ATOMIC)); + mps_device.dev.platform_data = cp1_base; + platform_device_register(&mps_device); + platform_device_register(&vmmc_device); +} + +/* asc ports */ +static struct resource lq_asc0_resources[] = +{ + { + .start = LQ_ASC0_BASE, + .end = LQ_ASC0_BASE + LQ_ASC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + IRQ_RES(tx, INT_NUM_IM3_IRL0), + IRQ_RES(rx, INT_NUM_IM3_IRL0 + 1), + IRQ_RES(err, INT_NUM_IM3_IRL0 + 2), +}; + +static struct resource lq_asc1_resources[] = +{ + { + .start = LQ_ASC1_BASE, + .end = LQ_ASC1_BASE + LQ_ASC_SIZE - 1, + .flags = IORESOURCE_MEM, + }, + IRQ_RES(tx, INT_NUM_IM3_IRL0 + 8), + IRQ_RES(rx, INT_NUM_IM3_IRL0 + 9), + IRQ_RES(err, INT_NUM_IM3_IRL0 + 10), +}; + +void __init +lq_register_asc(int port) +{ + switch (port) { + case 0: + platform_device_register_simple("lq_asc", 0, + lq_asc0_resources, ARRAY_SIZE(lq_asc0_resources)); + break; + case 1: + platform_device_register_simple("lq_asc", 1, + lq_asc1_resources, ARRAY_SIZE(lq_asc1_resources)); + break; + default: + break; + } +} + +void __init +lq_register_crypto(const char *name) +{ + platform_device_register_simple(name, 0, 0, 0); +} + +/* madwifi */ +int lantiq_emulate_madwifi_eep = 0; +EXPORT_SYMBOL(lantiq_emulate_madwifi_eep); + +void __init +lq_register_madwifi_eep(void) +{ + lantiq_emulate_madwifi_eep = 1; +} + + + --- /dev/null +++ b/arch/mips/lantiq/xway/devices.h @@ -0,0 +1,28 @@ +/* + * 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 _LQ_DEVICES_H__ +#define _LQ_DEVICES_H__ + +#include +#include + +extern void __init lq_register_gpio(void); +extern void __init lq_register_gpio_stp(void); +extern void __init lq_register_gpio_ebu(unsigned int value); +extern void __init lq_register_gpio_leds(struct gpio_led *leds, int cnt); +extern void __init lq_register_pci(struct lq_pci_data *data); +extern void __init lq_register_nor(struct physmap_flash_data *data); +extern void __init lq_register_tapi(void); +extern void __init lq_register_madwifi_eep(void); +extern void __init lq_register_wdt(void); +extern void __init lq_register_ethernet(struct lq_eth_data *eth); +extern void __init lq_register_asc(int port); +extern void __init lq_register_gpio_buttons(struct gpio_button *buttons, int cnt); + +#endif --- /dev/null +++ b/arch/mips/lantiq/xway/dma.c @@ -0,0 +1,701 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define LQ_DMA_CS ((u32 *)(LQ_DMA_BASE_ADDR + 0x18)) +#define LQ_DMA_CIE ((u32 *)(LQ_DMA_BASE_ADDR + 0x2C)) +#define LQ_DMA_IRNEN ((u32 *)(LQ_DMA_BASE_ADDR + 0xf4)) +#define LQ_DMA_CCTRL ((u32 *)(LQ_DMA_BASE_ADDR + 0x1C)) +#define LQ_DMA_CIS ((u32 *)(LQ_DMA_BASE_ADDR + 0x28)) +#define LQ_DMA_CDLEN ((u32 *)(LQ_DMA_BASE_ADDR + 0x24)) +#define LQ_DMA_PS ((u32 *)(LQ_DMA_BASE_ADDR + 0x40)) +#define LQ_DMA_PCTRL ((u32 *)(LQ_DMA_BASE_ADDR + 0x44)) +#define LQ_DMA_CTRL ((u32 *)(LQ_DMA_BASE_ADDR + 0x10)) +#define LQ_DMA_CPOLL ((u32 *)(LQ_DMA_BASE_ADDR + 0x14)) +#define LQ_DMA_CDBA ((u32 *)(LQ_DMA_BASE_ADDR + 0x20)) + +/*25 descriptors for each dma channel,4096/8/20=25.xx*/ +#define LQ_DMA_DESCRIPTOR_OFFSET 25 + +#define MAX_DMA_DEVICE_NUM 6 /*max ports connecting to dma */ +#define MAX_DMA_CHANNEL_NUM 20 /*max dma channels */ +#define DMA_INT_BUDGET 100 /*budget for interrupt handling */ +#define DMA_POLL_COUNTER 4 /*fix me, set the correct counter value here! */ + +extern void lq_mask_and_ack_irq(unsigned int irq_nr); +extern void lq_enable_irq(unsigned int irq_nr); +extern void lq_disable_irq(unsigned int irq_nr); + +u64 *g_desc_list; +struct dma_device_info dma_devs[MAX_DMA_DEVICE_NUM]; +struct dma_channel_info dma_chan[MAX_DMA_CHANNEL_NUM]; + +static const char *global_device_name[MAX_DMA_DEVICE_NUM] = + { "PPE", "DEU", "SPI", "SDIO", "MCTRL0", "MCTRL1" }; + +struct dma_chan_map default_dma_map[MAX_DMA_CHANNEL_NUM] = { + {"PPE", LQ_DMA_RX, 0, LQ_DMA_CH0_INT, 0}, + {"PPE", LQ_DMA_TX, 0, LQ_DMA_CH1_INT, 0}, + {"PPE", LQ_DMA_RX, 1, LQ_DMA_CH2_INT, 1}, + {"PPE", LQ_DMA_TX, 1, LQ_DMA_CH3_INT, 1}, + {"PPE", LQ_DMA_RX, 2, LQ_DMA_CH4_INT, 2}, + {"PPE", LQ_DMA_TX, 2, LQ_DMA_CH5_INT, 2}, + {"PPE", LQ_DMA_RX, 3, LQ_DMA_CH6_INT, 3}, + {"PPE", LQ_DMA_TX, 3, LQ_DMA_CH7_INT, 3}, + {"DEU", LQ_DMA_RX, 0, LQ_DMA_CH8_INT, 0}, + {"DEU", LQ_DMA_TX, 0, LQ_DMA_CH9_INT, 0}, + {"DEU", LQ_DMA_RX, 1, LQ_DMA_CH10_INT, 1}, + {"DEU", LQ_DMA_TX, 1, LQ_DMA_CH11_INT, 1}, + {"SPI", LQ_DMA_RX, 0, LQ_DMA_CH12_INT, 0}, + {"SPI", LQ_DMA_TX, 0, LQ_DMA_CH13_INT, 0}, + {"SDIO", LQ_DMA_RX, 0, LQ_DMA_CH14_INT, 0}, + {"SDIO", LQ_DMA_TX, 0, LQ_DMA_CH15_INT, 0}, + {"MCTRL0", LQ_DMA_RX, 0, LQ_DMA_CH16_INT, 0}, + {"MCTRL0", LQ_DMA_TX, 0, LQ_DMA_CH17_INT, 0}, + {"MCTRL1", LQ_DMA_RX, 1, LQ_DMA_CH18_INT, 1}, + {"MCTRL1", LQ_DMA_TX, 1, LQ_DMA_CH19_INT, 1} +}; + +struct dma_chan_map *chan_map = default_dma_map; +volatile u32 g_lq_dma_int_status; +volatile int g_lq_dma_in_process; /* 0=not in process, 1=in process */ + +void do_dma_tasklet(unsigned long); +DECLARE_TASKLET(dma_tasklet, do_dma_tasklet, 0); + +u8 *common_buffer_alloc(int len, int *byte_offset, void **opt) +{ + u8 *buffer = kmalloc(len * sizeof(u8), GFP_KERNEL); + + *byte_offset = 0; + + return buffer; +} + +void common_buffer_free(u8 *dataptr, void *opt) +{ + kfree(dataptr); +} + +void enable_ch_irq(struct dma_channel_info *pCh) +{ + int chan_no = (int)(pCh - dma_chan); + unsigned long flag; + + local_irq_save(flag); + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(0x4a, LQ_DMA_CIE); + lq_w32(lq_r32(LQ_DMA_IRNEN) | (1 << chan_no), LQ_DMA_IRNEN); + local_irq_restore(flag); + lq_enable_irq(pCh->irq); +} + +void disable_ch_irq(struct dma_channel_info *pCh) +{ + unsigned long flag; + int chan_no = (int) (pCh - dma_chan); + + local_irq_save(flag); + g_lq_dma_int_status &= ~(1 << chan_no); + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(0, LQ_DMA_CIE); + lq_w32(lq_r32(LQ_DMA_IRNEN) & ~(1 << chan_no), LQ_DMA_IRNEN); + local_irq_restore(flag); + lq_mask_and_ack_irq(pCh->irq); +} + +void open_chan(struct dma_channel_info *pCh) +{ + unsigned long flag; + int chan_no = (int)(pCh - dma_chan); + + local_irq_save(flag); + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(lq_r32(LQ_DMA_CCTRL) | 1, LQ_DMA_CCTRL); + if (pCh->dir == LQ_DMA_RX) + enable_ch_irq(pCh); + local_irq_restore(flag); +} + +void close_chan(struct dma_channel_info *pCh) +{ + unsigned long flag; + int chan_no = (int) (pCh - dma_chan); + + local_irq_save(flag); + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(lq_r32(LQ_DMA_CCTRL) & ~1, LQ_DMA_CCTRL); + disable_ch_irq(pCh); + local_irq_restore(flag); +} + +void reset_chan(struct dma_channel_info *pCh) +{ + int chan_no = (int) (pCh - dma_chan); + + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(lq_r32(LQ_DMA_CCTRL) | 2, LQ_DMA_CCTRL); +} + +void rx_chan_intr_handler(int chan_no) +{ + struct dma_device_info *pDev = (struct dma_device_info *)dma_chan[chan_no].dma_dev; + struct dma_channel_info *pCh = &dma_chan[chan_no]; + struct rx_desc *rx_desc_p; + int tmp; + unsigned long flag; + + /*handle command complete interrupt */ + rx_desc_p = (struct rx_desc *)pCh->desc_base + pCh->curr_desc; + if (rx_desc_p->status.field.OWN == CPU_OWN + && rx_desc_p->status.field.C + && rx_desc_p->status.field.data_length < 1536){ + /* Every thing is correct, then we inform the upper layer */ + pDev->current_rx_chan = pCh->rel_chan_no; + if (pDev->intr_handler) + pDev->intr_handler(pDev, RCV_INT); + pCh->weight--; + } else { + local_irq_save(flag); + tmp = lq_r32(LQ_DMA_CS); + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(lq_r32(LQ_DMA_CIS) | 0x7e, LQ_DMA_CIS); + lq_w32(tmp, LQ_DMA_CS); + g_lq_dma_int_status &= ~(1 << chan_no); + local_irq_restore(flag); + lq_enable_irq(dma_chan[chan_no].irq); + } +} + +inline void tx_chan_intr_handler(int chan_no) +{ + struct dma_device_info *pDev = (struct dma_device_info *)dma_chan[chan_no].dma_dev; + struct dma_channel_info *pCh = &dma_chan[chan_no]; + int tmp; + unsigned long flag; + + local_irq_save(flag); + tmp = lq_r32(LQ_DMA_CS); + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(lq_r32(LQ_DMA_CIS) | 0x7e, LQ_DMA_CIS); + lq_w32(tmp, LQ_DMA_CS); + g_lq_dma_int_status &= ~(1 << chan_no); + local_irq_restore(flag); + pDev->current_tx_chan = pCh->rel_chan_no; + if (pDev->intr_handler) + pDev->intr_handler(pDev, TRANSMIT_CPT_INT); +} + +void do_dma_tasklet(unsigned long unused) +{ + int i; + int chan_no = 0; + int budget = DMA_INT_BUDGET; + int weight = 0; + unsigned long flag; + + while (g_lq_dma_int_status) { + if (budget-- < 0) { + tasklet_schedule(&dma_tasklet); + return; + } + chan_no = -1; + weight = 0; + for (i = 0; i < MAX_DMA_CHANNEL_NUM; i++) { + if ((g_lq_dma_int_status & (1 << i)) && dma_chan[i].weight > 0) { + if (dma_chan[i].weight > weight) { + chan_no = i; + weight = dma_chan[chan_no].weight; + } + } + } + + if (chan_no >= 0) { + if (chan_map[chan_no].dir == LQ_DMA_RX) + rx_chan_intr_handler(chan_no); + else + tx_chan_intr_handler(chan_no); + } else { + for (i = 0; i < MAX_DMA_CHANNEL_NUM; i++) + dma_chan[i].weight = dma_chan[i].default_weight; + } + } + + local_irq_save(flag); + g_lq_dma_in_process = 0; + if (g_lq_dma_int_status) { + g_lq_dma_in_process = 1; + tasklet_schedule(&dma_tasklet); + } + local_irq_restore(flag); +} + +irqreturn_t dma_interrupt(int irq, void *dev_id) +{ + struct dma_channel_info *pCh; + int chan_no = 0; + int tmp; + + pCh = (struct dma_channel_info *)dev_id; + chan_no = (int)(pCh - dma_chan); + if (chan_no < 0 || chan_no > 19) + BUG(); + + tmp = lq_r32(LQ_DMA_IRNEN); + lq_w32(0, LQ_DMA_IRNEN); + g_lq_dma_int_status |= 1 << chan_no; + lq_w32(tmp, LQ_DMA_IRNEN); + lq_mask_and_ack_irq(irq); + + if (!g_lq_dma_in_process) { + g_lq_dma_in_process = 1; + tasklet_schedule(&dma_tasklet); + } + + return IRQ_HANDLED; +} + +struct dma_device_info *dma_device_reserve(char *dev_name) +{ + int i; + + for (i = 0; i < MAX_DMA_DEVICE_NUM; i++) { + if (strcmp(dev_name, dma_devs[i].device_name) == 0) { + if (dma_devs[i].reserved) + return NULL; + dma_devs[i].reserved = 1; + break; + } + } + + return &dma_devs[i]; +} +EXPORT_SYMBOL(dma_device_reserve); + +void dma_device_release(struct dma_device_info *dev) +{ + dev->reserved = 0; +} +EXPORT_SYMBOL(dma_device_release); + +void dma_device_register(struct dma_device_info *dev) +{ + int i, j; + int chan_no = 0; + u8 *buffer; + int byte_offset; + unsigned long flag; + struct dma_device_info *pDev; + struct dma_channel_info *pCh; + struct rx_desc *rx_desc_p; + struct tx_desc *tx_desc_p; + + for (i = 0; i < dev->max_tx_chan_num; i++) { + pCh = dev->tx_chan[i]; + if (pCh->control == LQ_DMA_CH_ON) { + chan_no = (int)(pCh - dma_chan); + for (j = 0; j < pCh->desc_len; j++) { + tx_desc_p = (struct tx_desc *)pCh->desc_base + j; + memset(tx_desc_p, 0, sizeof(struct tx_desc)); + } + local_irq_save(flag); + lq_w32(chan_no, LQ_DMA_CS); + /* check if the descriptor length is changed */ + if (lq_r32(LQ_DMA_CDLEN) != pCh->desc_len) + lq_w32(pCh->desc_len, LQ_DMA_CDLEN); + + lq_w32(lq_r32(LQ_DMA_CCTRL) & ~1, LQ_DMA_CCTRL); + lq_w32(lq_r32(LQ_DMA_CCTRL) | 2, LQ_DMA_CCTRL); + while (lq_r32(LQ_DMA_CCTRL) & 2) + ; + lq_w32(lq_r32(LQ_DMA_IRNEN) | (1 << chan_no), LQ_DMA_IRNEN); + lq_w32(0x30100, LQ_DMA_CCTRL); /* reset and enable channel,enable channel later */ + local_irq_restore(flag); + } + } + + for (i = 0; i < dev->max_rx_chan_num; i++) { + pCh = dev->rx_chan[i]; + if (pCh->control == LQ_DMA_CH_ON) { + chan_no = (int)(pCh - dma_chan); + + for (j = 0; j < pCh->desc_len; j++) { + rx_desc_p = (struct rx_desc *)pCh->desc_base + j; + pDev = (struct dma_device_info *)(pCh->dma_dev); + buffer = pDev->buffer_alloc(pCh->packet_size, &byte_offset, (void *)&(pCh->opt[j])); + if (!buffer) + break; + + dma_cache_inv((unsigned long) buffer, pCh->packet_size); + + rx_desc_p->Data_Pointer = (u32)CPHYSADDR((u32)buffer); + rx_desc_p->status.word = 0; + rx_desc_p->status.field.byte_offset = byte_offset; + rx_desc_p->status.field.OWN = DMA_OWN; + rx_desc_p->status.field.data_length = pCh->packet_size; + } + + local_irq_save(flag); + lq_w32(chan_no, LQ_DMA_CS); + /* check if the descriptor length is changed */ + if (lq_r32(LQ_DMA_CDLEN) != pCh->desc_len) + lq_w32(pCh->desc_len, LQ_DMA_CDLEN); + lq_w32(lq_r32(LQ_DMA_CCTRL) & ~1, LQ_DMA_CCTRL); + lq_w32(lq_r32(LQ_DMA_CCTRL) | 2, LQ_DMA_CCTRL); + while (lq_r32(LQ_DMA_CCTRL) & 2) + ; + lq_w32(0x0a, LQ_DMA_CIE); /* fix me, should enable all the interrupts here? */ + lq_w32(lq_r32(LQ_DMA_IRNEN) | (1 << chan_no), LQ_DMA_IRNEN); + lq_w32(0x30000, LQ_DMA_CCTRL); + local_irq_restore(flag); + lq_enable_irq(dma_chan[chan_no].irq); + } + } +} +EXPORT_SYMBOL(dma_device_register); + +void dma_device_unregister(struct dma_device_info *dev) +{ + int i, j; + int chan_no; + struct dma_channel_info *pCh; + struct rx_desc *rx_desc_p; + struct tx_desc *tx_desc_p; + unsigned long flag; + + for (i = 0; i < dev->max_tx_chan_num; i++) { + pCh = dev->tx_chan[i]; + if (pCh->control == LQ_DMA_CH_ON) { + chan_no = (int)(dev->tx_chan[i] - dma_chan); + local_irq_save(flag); + lq_w32(chan_no, LQ_DMA_CS); + pCh->curr_desc = 0; + pCh->prev_desc = 0; + pCh->control = LQ_DMA_CH_OFF; + lq_w32(0, LQ_DMA_CIE); /* fix me, should disable all the interrupts here? */ + lq_w32(lq_r32(LQ_DMA_IRNEN) & ~(1 << chan_no), LQ_DMA_IRNEN); /* disable interrupts */ + lq_w32(lq_r32(LQ_DMA_CCTRL) & ~1, LQ_DMA_CCTRL); + while (lq_r32(LQ_DMA_CCTRL) & 1) + ; + local_irq_restore(flag); + + for (j = 0; j < pCh->desc_len; j++) { + tx_desc_p = (struct tx_desc *)pCh->desc_base + j; + if ((tx_desc_p->status.field.OWN == CPU_OWN && tx_desc_p->status.field.C) + || (tx_desc_p->status.field.OWN == DMA_OWN && tx_desc_p->status.field.data_length > 0)) { + dev->buffer_free((u8 *) __va(tx_desc_p->Data_Pointer), (void *)pCh->opt[j]); + } + tx_desc_p->status.field.OWN = CPU_OWN; + memset(tx_desc_p, 0, sizeof(struct tx_desc)); + } + /* TODO should free buffer that is not transferred by dma */ + } + } + + for (i = 0; i < dev->max_rx_chan_num; i++) { + pCh = dev->rx_chan[i]; + chan_no = (int)(dev->rx_chan[i] - dma_chan); + lq_disable_irq(pCh->irq); + + local_irq_save(flag); + g_lq_dma_int_status &= ~(1 << chan_no); + pCh->curr_desc = 0; + pCh->prev_desc = 0; + pCh->control = LQ_DMA_CH_OFF; + + lq_w32(chan_no, LQ_DMA_CS); + lq_w32(0, LQ_DMA_CIE); /* fix me, should disable all the interrupts here? */ + lq_w32(lq_r32(LQ_DMA_IRNEN) & ~(1 << chan_no), LQ_DMA_IRNEN); /* disable interrupts */ + lq_w32(lq_r32(LQ_DMA_CCTRL) & ~1, LQ_DMA_CCTRL); + while (lq_r32(LQ_DMA_CCTRL) & 1) + ; + + local_irq_restore(flag); + for (j = 0; j < pCh->desc_len; j++) { + rx_desc_p = (struct rx_desc *) pCh->desc_base + j; + if ((rx_desc_p->status.field.OWN == CPU_OWN + && rx_desc_p->status.field.C) + || (rx_desc_p->status.field.OWN == DMA_OWN + && rx_desc_p->status.field.data_length > 0)) { + dev->buffer_free((u8 *) + __va(rx_desc_p->Data_Pointer), + (void *) pCh->opt[j]); + } + } + } +} +EXPORT_SYMBOL(dma_device_unregister); + +int dma_device_read(struct dma_device_info *dma_dev, u8 **dataptr, void **opt) +{ + u8 *buf; + int len; + int byte_offset = 0; + void *p = NULL; + struct dma_channel_info *pCh = dma_dev->rx_chan[dma_dev->current_rx_chan]; + struct rx_desc *rx_desc_p; + + /* get the rx data first */ + rx_desc_p = (struct rx_desc *) pCh->desc_base + pCh->curr_desc; + if (!(rx_desc_p->status.field.OWN == CPU_OWN && rx_desc_p->status.field.C)) + return 0; + + buf = (u8 *) __va(rx_desc_p->Data_Pointer); + *(u32 *)dataptr = (u32)buf; + len = rx_desc_p->status.field.data_length; + + if (opt) + *(int *)opt = (int)pCh->opt[pCh->curr_desc]; + + /* replace with a new allocated buffer */ + buf = dma_dev->buffer_alloc(pCh->packet_size, &byte_offset, &p); + + if (buf) { + dma_cache_inv((unsigned long) buf, pCh->packet_size); + pCh->opt[pCh->curr_desc] = p; + wmb(); + + rx_desc_p->Data_Pointer = (u32) CPHYSADDR((u32) buf); + rx_desc_p->status.word = (DMA_OWN << 31) | ((byte_offset) << 23) | pCh->packet_size; + wmb(); + } else { + *(u32 *) dataptr = 0; + if (opt) + *(int *) opt = 0; + len = 0; + } + + /* increase the curr_desc pointer */ + pCh->curr_desc++; + if (pCh->curr_desc == pCh->desc_len) + pCh->curr_desc = 0; + + return len; +} +EXPORT_SYMBOL(dma_device_read); + +int dma_device_write(struct dma_device_info *dma_dev, u8 *dataptr, int len, void *opt) +{ + unsigned long flag; + u32 tmp, byte_offset; + struct dma_channel_info *pCh; + int chan_no; + struct tx_desc *tx_desc_p; + local_irq_save(flag); + + pCh = dma_dev->tx_chan[dma_dev->current_tx_chan]; + chan_no = (int)(pCh - (struct dma_channel_info *) dma_chan); + + tx_desc_p = (struct tx_desc *)pCh->desc_base + pCh->prev_desc; + while (tx_desc_p->status.field.OWN == CPU_OWN && tx_desc_p->status.field.C) { + dma_dev->buffer_free((u8 *) __va(tx_desc_p->Data_Pointer), pCh->opt[pCh->prev_desc]); + memset(tx_desc_p, 0, sizeof(struct tx_desc)); + pCh->prev_desc = (pCh->prev_desc + 1) % (pCh->desc_len); + tx_desc_p = (struct tx_desc *)pCh->desc_base + pCh->prev_desc; + } + tx_desc_p = (struct tx_desc *)pCh->desc_base + pCh->curr_desc; + /* Check whether this descriptor is available */ + if (tx_desc_p->status.field.OWN == DMA_OWN || tx_desc_p->status.field.C) { + /* if not, the tell the upper layer device */ + dma_dev->intr_handler (dma_dev, TX_BUF_FULL_INT); + local_irq_restore(flag); + printk(KERN_INFO "%s %d: failed to write!\n", __func__, __LINE__); + + return 0; + } + pCh->opt[pCh->curr_desc] = opt; + /* byte offset----to adjust the starting address of the data buffer, should be multiple of the burst length. */ + byte_offset = ((u32) CPHYSADDR((u32) dataptr)) % ((dma_dev->tx_burst_len) * 4); + dma_cache_wback((unsigned long) dataptr, len); + wmb(); + tx_desc_p->Data_Pointer = (u32) CPHYSADDR((u32) dataptr) - byte_offset; + wmb(); + tx_desc_p->status.word = (DMA_OWN << 31) | DMA_DESC_SOP_SET | DMA_DESC_EOP_SET | ((byte_offset) << 23) | len; + wmb(); + + pCh->curr_desc++; + if (pCh->curr_desc == pCh->desc_len) + pCh->curr_desc = 0; + + /*Check whether this descriptor is available */ + tx_desc_p = (struct tx_desc *) pCh->desc_base + pCh->curr_desc; + if (tx_desc_p->status.field.OWN == DMA_OWN) { + /*if not , the tell the upper layer device */ + dma_dev->intr_handler (dma_dev, TX_BUF_FULL_INT); + } + + lq_w32(chan_no, LQ_DMA_CS); + tmp = lq_r32(LQ_DMA_CCTRL); + + if (!(tmp & 1)) + pCh->open(pCh); + + local_irq_restore(flag); + + return len; +} +EXPORT_SYMBOL(dma_device_write); + +int map_dma_chan(struct dma_chan_map *map) +{ + int i, j; + int result; + + for (i = 0; i < MAX_DMA_DEVICE_NUM; i++) + strcpy(dma_devs[i].device_name, global_device_name[i]); + + for (i = 0; i < MAX_DMA_CHANNEL_NUM; i++) { + dma_chan[i].irq = map[i].irq; + result = request_irq(dma_chan[i].irq, dma_interrupt, IRQF_DISABLED, map[i].dev_name, (void *)&dma_chan[i]); + if (result) { + printk(KERN_WARNING "error, cannot get dma_irq!\n"); + free_irq(dma_chan[i].irq, (void *) &dma_interrupt); + + return -EFAULT; + } + } + + for (i = 0; i < MAX_DMA_DEVICE_NUM; i++) { + dma_devs[i].num_tx_chan = 0; /*set default tx channel number to be one */ + dma_devs[i].num_rx_chan = 0; /*set default rx channel number to be one */ + dma_devs[i].max_rx_chan_num = 0; + dma_devs[i].max_tx_chan_num = 0; + dma_devs[i].buffer_alloc = &common_buffer_alloc; + dma_devs[i].buffer_free = &common_buffer_free; + dma_devs[i].intr_handler = NULL; + dma_devs[i].tx_burst_len = 4; + dma_devs[i].rx_burst_len = 4; + if (i == 0) { + lq_w32(0, LQ_DMA_PS); + lq_w32(lq_r32(LQ_DMA_PCTRL) | ((0xf << 8) | (1 << 6)), LQ_DMA_PCTRL); /*enable dma drop */ + } + + if (i == 1) { + lq_w32(1, LQ_DMA_PS); + lq_w32(0x14, LQ_DMA_PCTRL); /*deu port setting */ + } + + for (j = 0; j < MAX_DMA_CHANNEL_NUM; j++) { + dma_chan[j].byte_offset = 0; + dma_chan[j].open = &open_chan; + dma_chan[j].close = &close_chan; + dma_chan[j].reset = &reset_chan; + dma_chan[j].enable_irq = &enable_ch_irq; + dma_chan[j].disable_irq = &disable_ch_irq; + dma_chan[j].rel_chan_no = map[j].rel_chan_no; + dma_chan[j].control = LQ_DMA_CH_OFF; + dma_chan[j].default_weight = LQ_DMA_CH_DEFAULT_WEIGHT; + dma_chan[j].weight = dma_chan[j].default_weight; + dma_chan[j].curr_desc = 0; + dma_chan[j].prev_desc = 0; + } + + for (j = 0; j < MAX_DMA_CHANNEL_NUM; j++) { + if (strcmp(dma_devs[i].device_name, map[j].dev_name) == 0) { + if (map[j].dir == LQ_DMA_RX) { + dma_chan[j].dir = LQ_DMA_RX; + dma_devs[i].max_rx_chan_num++; + dma_devs[i].rx_chan[dma_devs[i].max_rx_chan_num - 1] = &dma_chan[j]; + dma_devs[i].rx_chan[dma_devs[i].max_rx_chan_num - 1]->pri = map[j].pri; + dma_chan[j].dma_dev = (void *)&dma_devs[i]; + } else if (map[j].dir == LQ_DMA_TX) { + /*TX direction */ + dma_chan[j].dir = LQ_DMA_TX; + dma_devs[i].max_tx_chan_num++; + dma_devs[i].tx_chan[dma_devs[i].max_tx_chan_num - 1] = &dma_chan[j]; + dma_devs[i].tx_chan[dma_devs[i].max_tx_chan_num - 1]->pri = map[j].pri; + dma_chan[j].dma_dev = (void *)&dma_devs[i]; + } else { + printk(KERN_WARNING "WRONG DMA MAP!\n"); + } + } + } + } + + return 0; +} + +void dma_chip_init(void) +{ + int i; + + /* enable DMA from PMU */ + lq_pmu_enable(PMU_DMA); + + /* reset DMA */ + lq_w32(lq_r32(LQ_DMA_CTRL) | 1, LQ_DMA_CTRL); + + /* disable all interrupts */ + lq_w32(0, LQ_DMA_IRNEN); + + for (i = 0; i < MAX_DMA_CHANNEL_NUM; i++) { + lq_w32(i, LQ_DMA_CS); + lq_w32(0x2, LQ_DMA_CCTRL); + lq_w32(0x80000040, LQ_DMA_CPOLL); + lq_w32(lq_r32(LQ_DMA_CCTRL) & ~0x1, LQ_DMA_CCTRL); + } +} + +int lq_dma_init(void) +{ + int i; + + dma_chip_init(); + + if (map_dma_chan(default_dma_map)) + BUG(); + + g_desc_list = (u64 *)KSEG1ADDR(__get_free_page(GFP_DMA)); + + if (g_desc_list == NULL) { + printk(KERN_WARNING "no memory for desriptor\n"); + return -ENOMEM; + } + + memset(g_desc_list, 0, PAGE_SIZE); + + for (i = 0; i < MAX_DMA_CHANNEL_NUM; i++) { + dma_chan[i].desc_base = (u32)g_desc_list + i * LQ_DMA_DESCRIPTOR_OFFSET * 8; + dma_chan[i].curr_desc = 0; + dma_chan[i].desc_len = LQ_DMA_DESCRIPTOR_OFFSET; + + lq_w32(i, LQ_DMA_CS); + lq_w32((u32)CPHYSADDR(dma_chan[i].desc_base), LQ_DMA_CDBA); + lq_w32(dma_chan[i].desc_len, LQ_DMA_CDLEN); + } + return 0; +} + +arch_initcall(lq_dma_init); + +void dma_cleanup(void) +{ + int i; + + free_page(KSEG0ADDR((unsigned long) g_desc_list)); + for (i = 0; i < MAX_DMA_CHANNEL_NUM; i++) + free_irq(dma_chan[i].irq, (void *)&dma_interrupt); +} + +MODULE_LICENSE("GPL"); --- /dev/null +++ b/arch/mips/lantiq/xway/pmu.c @@ -0,0 +1,36 @@ +/* + * 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 + +#define LQ_PMU_PWDCR ((u32 *)(LQ_PMU_BASE_ADDR + 0x001C)) +#define LQ_PMU_PWDSR ((u32 *)(LQ_PMU_BASE_ADDR + 0x0020)) + +void +lq_pmu_enable(unsigned int module) +{ + int err = 1000000; + + lq_w32(lq_r32(LQ_PMU_PWDCR) & ~module, LQ_PMU_PWDCR); + while (--err && (lq_r32(LQ_PMU_PWDSR) & module)); + + if (!err) + panic("activating PMU module failed!"); +} +EXPORT_SYMBOL(lq_pmu_enable); + +void +lq_pmu_disable(unsigned int module) +{ + lq_w32(lq_r32(LQ_PMU_PWDCR) | module, LQ_PMU_PWDCR); +} +EXPORT_SYMBOL(lq_pmu_disable); --- /dev/null +++ b/arch/mips/lantiq/xway/timer.c @@ -0,0 +1,828 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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]; +}; + +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. */ + lq_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) +{ + lq_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) +{ + lq_w32(0x00, LQ_GPTU_IRNEN); + lq_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); + + lq_pmu_disable(PMU_GPT); +} + +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 = lq_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; + + lq_w32(0, LQ_GPTU_IRNEN); + lq_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); --- /dev/null +++ b/arch/mips/lantiq/xway/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 ifxmips_request_timer(unsigned int, unsigned int, unsigned long, unsigned long, unsigned long); +extern int ifxmips_free_timer(unsigned int); +extern int ifxmips_start_timer(unsigned int, int); +extern int ifxmips_stop_timer(unsigned int); +extern int ifxmips_reset_counter_flags(u32 timer, u32 flags); +extern int ifxmips_get_count_value(unsigned int, unsigned long *); +extern u32 ifxmips_cal_divider(unsigned long); +extern int ifxmips_set_timer(unsigned int, unsigned int, int, int, unsigned int, unsigned long, unsigned long); +extern int ifxmips_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__ */ --- /dev/null +++ b/arch/mips/lantiq/xway/Makefile @@ -0,0 +1,5 @@ +obj-y := pmu.o prom.o dma.o timer.o reset.o clk-xway.o +obj-y += gpio.o gpio_ebu.o gpio_leds.o devices.o +obj-$(CONFIG_LANTIQ_MACH_EASY50812) += mach-easy50812.o +obj-$(CONFIG_LANTIQ_MACH_EASY50712) += mach-easy50712.o +obj-$(CONFIG_LANTIQ_MACH_EASY4010) += mach-easy4010.o --- /dev/null +++ b/arch/mips/lantiq/xway/clk-xway.c @@ -0,0 +1,219 @@ +/* + * 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) 2007 Xu Liang, infineon + * Copyright (C) 2010 John Crispin + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include + +static unsigned int lq_ram_clocks[] = {CLOCK_167M, CLOCK_133M, CLOCK_111M, CLOCK_83M }; +#define DDR_HZ lq_ram_clocks[lq_r32(LQ_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 CGU_PLL0_PHASE_DIVIDER_ENABLE (lq_r32(LQ_CGU_PLL0_CFG) & (1 << 31)) +#define CGU_PLL0_BYPASS (lq_r32(LQ_CGU_PLL0_CFG) & (1 << 30)) +#define CGU_PLL0_CFG_DSMSEL (lq_r32(LQ_CGU_PLL0_CFG) & (1 << 28)) +#define CGU_PLL0_CFG_FRAC_EN (lq_r32(LQ_CGU_PLL0_CFG) & (1 << 27)) +#define CGU_PLL1_SRC (lq_r32(LQ_CGU_PLL1_CFG) & (1 << 31)) +#define CGU_PLL2_PHASE_DIVIDER_ENABLE (lq_r32(LQ_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(*LQ_CGU_PLL0_CFG, 26, 17) +#define CGU_PLL0_CFG_PLLN GET_BITS(*LQ_CGU_PLL0_CFG, 12, 6) +#define CGU_PLL0_CFG_PLLM GET_BITS(*LQ_CGU_PLL0_CFG, 5, 2) +#define CGU_PLL2_SRC GET_BITS(*LQ_CGU_PLL2_CFG, 18, 17) +#define CGU_PLL2_CFG_INPUT_DIV GET_BITS(*LQ_CGU_PLL2_CFG, 16, 13) + +#define LQ_GPTU_GPT_CLC ((u32 *)(LQ_GPTU_BASE_ADDR + 0x0000)) +#define LQ_CGU_PLL0_CFG ((u32 *)(LQ_CGU_BASE_ADDR + 0x0004)) +#define LQ_CGU_PLL1_CFG ((u32 *)(LQ_CGU_BASE_ADDR + 0x0008)) +#define LQ_CGU_PLL2_CFG ((u32 *)(LQ_CGU_BASE_ADDR + 0x000C)) +#define LQ_CGU_SYS ((u32 *)(LQ_CGU_BASE_ADDR + 0x0010)) +#define LQ_CGU_UPDATE ((u32 *)(LQ_CGU_BASE_ADDR + 0x0014)) +#define LQ_CGU_IF_CLK ((u32 *)(LQ_CGU_BASE_ADDR + 0x0018)) +#define LQ_CGU_OSC_CON ((u32 *)(LQ_CGU_BASE_ADDR + 0x001C)) +#define LQ_CGU_SMD ((u32 *)(LQ_CGU_BASE_ADDR + 0x0020)) +#define LQ_CGU_CT1SR ((u32 *)(LQ_CGU_BASE_ADDR + 0x0028)) +#define LQ_CGU_CT2SR ((u32 *)(LQ_CGU_BASE_ADDR + 0x002C)) +#define LQ_CGU_PCMCR ((u32 *)(LQ_CGU_BASE_ADDR + 0x0030)) +#define LQ_CGU_PCI_CR ((u32 *)(LQ_CGU_BASE_ADDR + 0x0034)) +#define LQ_CGU_PD_PC ((u32 *)(LQ_CGU_BASE_ADDR + 0x0038)) +#define LQ_CGU_FMR ((u32 *)(LQ_CGU_BASE_ADDR + 0x003C)) + +static unsigned int lq_get_pll0_fdiv(void); + +static inline unsigned int +get_input_clock(int pll) +{ + switch (pll) { + case 0: + if (lq_r32(LQ_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 lq_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 +lq_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 +lq_get_pll0_fdiv(void) +{ + unsigned int div = CGU_PLL2_CFG_INPUT_DIV + 1; + return (lq_get_pll0_fosc() + (div >> 1)) / div; +} + +unsigned int +lq_get_io_region_clock(void) +{ + unsigned int ret = lq_get_pll0_fosc(); + switch (lq_r32(LQ_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(lq_get_io_region_clock); + +unsigned int +lq_get_fpi_bus_clock(int fpi) +{ + unsigned int ret = lq_get_io_region_clock(); + if ((fpi == 2) && (lq_r32(LQ_CGU_SYS) & CGU_SYS_FPI_SEL)) + ret >>= 1; + return ret; +} +EXPORT_SYMBOL(lq_get_fpi_bus_clock); + +unsigned int +lq_get_cpu_hz(void) +{ + switch (lq_r32(LQ_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(lq_get_cpu_hz); + +unsigned int +lq_get_fpi_hz(void) +{ + unsigned int ddr_clock = DDR_HZ; + if (lq_r32(LQ_CGU_SYS) & 0x40) + return ddr_clock >> 1; + return ddr_clock; +} +EXPORT_SYMBOL(lq_get_fpi_hz); + + --- /dev/null +++ b/arch/mips/lantiq/xway/gpio.c @@ -0,0 +1,206 @@ +/* + * 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 + +#define LQ_GPIO0_BASE_ADDR 0x1E100B10 +#define LQ_GPIO1_BASE_ADDR 0x1E100B40 +#define LQ_GPIO_SIZE 0x30 + +#define LQ_GPIO_OUT 0x00 +#define LQ_GPIO_IN 0x04 +#define LQ_GPIO_DIR 0x08 +#define LQ_GPIO_ALTSEL0 0x0C +#define LQ_GPIO_ALTSEL1 0x10 +#define LQ_GPIO_OD 0x14 + +#define PINS_PER_PORT 16 + +#define lq_gpio_getbit(m, r, p) !!(lq_r32(m + r) & (1 << p)) +#define lq_gpio_setbit(m, r, p) lq_w32_mask(0, (1 << p), m + r) +#define lq_gpio_clearbit(m, r, p) lq_w32_mask((1 << p), 0, m + r) + +struct lq_gpio +{ + void __iomem *membase; + struct gpio_chip chip; +}; + +int +gpio_to_irq(unsigned int gpio) +{ + return -EINVAL; +} +EXPORT_SYMBOL(gpio_to_irq); + +int +lq_gpio_setconfig(unsigned int pin, unsigned int reg, unsigned int val) +{ + void __iomem *membase = (void*)KSEG1ADDR(LQ_GPIO0_BASE_ADDR); + if(pin >= (2 * PINS_PER_PORT)) + return -EINVAL; + if(pin >= PINS_PER_PORT) + { + pin -= PINS_PER_PORT; + membase += LQ_GPIO_SIZE; + } + if(val) + lq_w32_mask(0, (1 << pin), membase + reg); + else + lq_w32_mask((1 << pin), 0, membase + reg); + return 0; +} +EXPORT_SYMBOL(lq_gpio_setconfig); + +int +lq_gpio_request(unsigned int pin, unsigned int alt0, + unsigned int alt1, unsigned int dir, const char *name) +{ + void __iomem *membase = (void*)KSEG1ADDR(LQ_GPIO0_BASE_ADDR); + if(pin >= (2 * PINS_PER_PORT)) + return -EINVAL; + if(gpio_request(pin, name)) + { + printk("failed to register %s gpio\n", name); + return -EBUSY; + } + if(dir) + gpio_direction_output(pin, 1); + else + gpio_direction_input(pin); + if(pin >= PINS_PER_PORT) + { + pin -= PINS_PER_PORT; + membase += LQ_GPIO_SIZE; + } + if(alt0) + lq_gpio_setbit(membase, LQ_GPIO_ALTSEL0, pin); + else + lq_gpio_clearbit(membase, LQ_GPIO_ALTSEL0, pin); + if(alt1) + lq_gpio_setbit(membase, LQ_GPIO_ALTSEL1, pin); + else + lq_gpio_clearbit(membase, LQ_GPIO_ALTSEL1, pin); + return 0; +} +EXPORT_SYMBOL(lq_gpio_request); + +static void +lq_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct lq_gpio *lq_gpio = container_of(chip, struct lq_gpio, chip); + if(value) + lq_gpio_setbit(lq_gpio->membase, LQ_GPIO_OUT, offset); + else + lq_gpio_clearbit(lq_gpio->membase, LQ_GPIO_OUT, offset); +} + +static int +lq_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct lq_gpio *lq_gpio = container_of(chip, struct lq_gpio, chip); + return lq_gpio_getbit(lq_gpio->membase, LQ_GPIO_IN, offset); +} + +static int +lq_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct lq_gpio *lq_gpio = container_of(chip, struct lq_gpio, chip); + lq_gpio_clearbit(lq_gpio->membase, LQ_GPIO_OD, offset); + lq_gpio_clearbit(lq_gpio->membase, LQ_GPIO_DIR, offset); + return 0; +} + +static int +lq_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct lq_gpio *lq_gpio = container_of(chip, struct lq_gpio, chip); + lq_gpio_setbit(lq_gpio->membase, LQ_GPIO_OD, offset); + lq_gpio_setbit(lq_gpio->membase, LQ_GPIO_DIR, offset); + lq_gpio_set(chip, offset, value); + return 0; +} + +static int +lq_gpio_req(struct gpio_chip *chip, unsigned offset) +{ + struct lq_gpio *lq_gpio = container_of(chip, struct lq_gpio, chip); + lq_gpio_clearbit(lq_gpio->membase, LQ_GPIO_ALTSEL0, offset); + lq_gpio_clearbit(lq_gpio->membase, LQ_GPIO_ALTSEL1, offset); + return 0; +} + +static int +lq_gpio_probe(struct platform_device *pdev) +{ + struct lq_gpio *lq_gpio = kzalloc(sizeof(struct lq_gpio), GFP_KERNEL); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + int ret = 0; + if(!res) + { + ret = -ENOENT; + goto err_free; + } + res = request_mem_region(res->start, resource_size(res), + dev_name(&pdev->dev)); + if(!res) + { + ret = -EBUSY; + goto err_free; + } + lq_gpio->membase = ioremap_nocache(res->start, resource_size(res)); + if(!lq_gpio->membase) + { + ret = -ENOMEM; + goto err_release_mem_region; + } + lq_gpio->chip.label = "lq_gpio"; + lq_gpio->chip.direction_input = lq_gpio_direction_input; + lq_gpio->chip.direction_output = lq_gpio_direction_output; + lq_gpio->chip.get = lq_gpio_get; + lq_gpio->chip.set = lq_gpio_set; + lq_gpio->chip.request = lq_gpio_req; + lq_gpio->chip.base = PINS_PER_PORT * pdev->id; + lq_gpio->chip.ngpio = PINS_PER_PORT; + platform_set_drvdata(pdev, lq_gpio); + ret = gpiochip_add(&lq_gpio->chip); + if(!ret) + return 0; + + iounmap(lq_gpio->membase); +err_release_mem_region: + release_mem_region(res->start, resource_size(res)); +err_free: + kfree(lq_gpio); + return ret; +} + +static struct platform_driver +lq_gpio_driver = { + .probe = lq_gpio_probe, + .driver = { + .name = "lq_gpio", + .owner = THIS_MODULE, + }, +}; + +int __init +lq_gpio_init(void) +{ + int ret = platform_driver_register(&lq_gpio_driver); + if(ret) + printk(KERN_INFO "lq_gpio : Error registering platfom driver!"); + return ret; +} + +postcore_initcall(lq_gpio_init); --- /dev/null +++ b/arch/mips/lantiq/xway/reset.c @@ -0,0 +1,53 @@ +/* + * 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 + +#define LQ_RCU_RST ((u32 *)(LQ_RCU_BASE_ADDR + 0x0010)) +#define LQ_RCU_RST_ALL 0x40000000 + +static void +lq_machine_restart(char *command) +{ + printk(KERN_NOTICE "System restart\n"); + local_irq_disable(); + lq_w32(lq_r32(LQ_RCU_RST) | LQ_RCU_RST_ALL, LQ_RCU_RST); + for(;;); +} + +static void +lq_machine_halt(void) +{ + printk(KERN_NOTICE "System halted.\n"); + local_irq_disable(); + for(;;); +} + +static void +lq_machine_power_off(void) +{ + printk(KERN_NOTICE "Please turn off the power now.\n"); + local_irq_disable(); + for(;;); +} + +static int __init +mips_reboot_setup(void) +{ + _machine_restart = lq_machine_restart; + _machine_halt = lq_machine_halt; + pm_power_off = lq_machine_power_off; + return 0; +} + +arch_initcall(mips_reboot_setup);