1/* 2 * Copyright (C) 2006,2007 Felix Fietkau <nbd@openwrt.org> 3 * Copyright (C) 2006,2007 Eugene Konev <ejka@openwrt.org> 4 * 5 * This program is free software; you can redistribute it and/or modify 6 * it under the terms of the GNU General Public License as published by 7 * the Free Software Foundation; either version 2 of the License, or 8 * (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 20#include <linux/init.h> 21#include <linux/types.h> 22#include <linux/module.h> 23#include <linux/delay.h> 24#include <linux/dma-mapping.h> 25#include <linux/platform_device.h> 26#include <linux/mtd/physmap.h> 27#include <linux/serial.h> 28#include <linux/serial_8250.h> 29#include <linux/ioport.h> 30#include <linux/io.h> 31#include <linux/vlynq.h> 32#include <linux/leds.h> 33#include <linux/string.h> 34#include <linux/etherdevice.h> 35#include <linux/phy.h> 36#include <linux/phy_fixed.h> 37#include <linux/gpio.h> 38#include <linux/clk.h> 39 40#include <asm/addrspace.h> 41#include <asm/mach-ar7/ar7.h> 42#include <asm/mach-ar7/prom.h> 43 44/***************************************************************************** 45 * VLYNQ Bus 46 ****************************************************************************/ 47struct plat_vlynq_data { 48 struct plat_vlynq_ops ops; 49 int gpio_bit; 50 int reset_bit; 51}; 52 53static int vlynq_on(struct vlynq_device *dev) 54{ 55 int ret; 56 struct plat_vlynq_data *pdata = dev->dev.platform_data; 57 58 ret = gpio_request(pdata->gpio_bit, "vlynq"); 59 if (ret) 60 goto out; 61 62 ar7_device_reset(pdata->reset_bit); 63 64 ret = ar7_gpio_disable(pdata->gpio_bit); 65 if (ret) 66 goto out_enabled; 67 68 ret = ar7_gpio_enable(pdata->gpio_bit); 69 if (ret) 70 goto out_enabled; 71 72 ret = gpio_direction_output(pdata->gpio_bit, 0); 73 if (ret) 74 goto out_gpio_enabled; 75 76 msleep(50); 77 78 gpio_set_value(pdata->gpio_bit, 1); 79 80 msleep(50); 81 82 return 0; 83 84out_gpio_enabled: 85 ar7_gpio_disable(pdata->gpio_bit); 86out_enabled: 87 ar7_device_disable(pdata->reset_bit); 88 gpio_free(pdata->gpio_bit); 89out: 90 return ret; 91} 92 93static void vlynq_off(struct vlynq_device *dev) 94{ 95 struct plat_vlynq_data *pdata = dev->dev.platform_data; 96 97 ar7_gpio_disable(pdata->gpio_bit); 98 gpio_free(pdata->gpio_bit); 99 ar7_device_disable(pdata->reset_bit); 100} 101 102static struct resource vlynq_low_res[] = { 103 { 104 .name = "regs", 105 .flags = IORESOURCE_MEM, 106 .start = AR7_REGS_VLYNQ0, 107 .end = AR7_REGS_VLYNQ0 + 0xff, 108 }, 109 { 110 .name = "irq", 111 .flags = IORESOURCE_IRQ, 112 .start = 29, 113 .end = 29, 114 }, 115 { 116 .name = "mem", 117 .flags = IORESOURCE_MEM, 118 .start = 0x04000000, 119 .end = 0x04ffffff, 120 }, 121 { 122 .name = "devirq", 123 .flags = IORESOURCE_IRQ, 124 .start = 80, 125 .end = 111, 126 }, 127}; 128 129static struct resource vlynq_high_res[] = { 130 { 131 .name = "regs", 132 .flags = IORESOURCE_MEM, 133 .start = AR7_REGS_VLYNQ1, 134 .end = AR7_REGS_VLYNQ1 + 0xff, 135 }, 136 { 137 .name = "irq", 138 .flags = IORESOURCE_IRQ, 139 .start = 33, 140 .end = 33, 141 }, 142 { 143 .name = "mem", 144 .flags = IORESOURCE_MEM, 145 .start = 0x0c000000, 146 .end = 0x0cffffff, 147 }, 148 { 149 .name = "devirq", 150 .flags = IORESOURCE_IRQ, 151 .start = 112, 152 .end = 143, 153 }, 154}; 155 156static struct plat_vlynq_data vlynq_low_data = { 157 .ops = { 158 .on = vlynq_on, 159 .off = vlynq_off, 160 }, 161 .reset_bit = 20, 162 .gpio_bit = 18, 163}; 164 165static struct plat_vlynq_data vlynq_high_data = { 166 .ops = { 167 .on = vlynq_on, 168 .off = vlynq_off, 169 }, 170 .reset_bit = 16, 171 .gpio_bit = 19, 172}; 173 174static struct platform_device vlynq_low = { 175 .id = 0, 176 .name = "vlynq", 177 .dev = { 178 .platform_data = &vlynq_low_data, 179 }, 180 .resource = vlynq_low_res, 181 .num_resources = ARRAY_SIZE(vlynq_low_res), 182}; 183 184static struct platform_device vlynq_high = { 185 .id = 1, 186 .name = "vlynq", 187 .dev = { 188 .platform_data = &vlynq_high_data, 189 }, 190 .resource = vlynq_high_res, 191 .num_resources = ARRAY_SIZE(vlynq_high_res), 192}; 193 194/***************************************************************************** 195 * Flash 196 ****************************************************************************/ 197static struct resource physmap_flash_resource = { 198 .name = "mem", 199 .flags = IORESOURCE_MEM, 200 .start = 0x10000000, 201 .end = 0x107fffff, 202}; 203 204static const char *ar7_probe_types[] = { "ar7part", NULL }; 205 206static struct physmap_flash_data physmap_flash_data = { 207 .width = 2, 208 .part_probe_types = ar7_probe_types, 209}; 210 211static struct platform_device physmap_flash = { 212 .name = "physmap-flash", 213 .dev = { 214 .platform_data = &physmap_flash_data, 215 }, 216 .resource = &physmap_flash_resource, 217 .num_resources = 1, 218}; 219 220/***************************************************************************** 221 * Ethernet 222 ****************************************************************************/ 223static struct resource cpmac_low_res[] = { 224 { 225 .name = "regs", 226 .flags = IORESOURCE_MEM, 227 .start = AR7_REGS_MAC0, 228 .end = AR7_REGS_MAC0 + 0x7ff, 229 }, 230 { 231 .name = "irq", 232 .flags = IORESOURCE_IRQ, 233 .start = 27, 234 .end = 27, 235 }, 236}; 237 238static struct resource cpmac_high_res[] = { 239 { 240 .name = "regs", 241 .flags = IORESOURCE_MEM, 242 .start = AR7_REGS_MAC1, 243 .end = AR7_REGS_MAC1 + 0x7ff, 244 }, 245 { 246 .name = "irq", 247 .flags = IORESOURCE_IRQ, 248 .start = 41, 249 .end = 41, 250 }, 251}; 252 253static struct fixed_phy_status fixed_phy_status __initdata = { 254 .link = 1, 255 .speed = 100, 256 .duplex = 1, 257}; 258 259static struct plat_cpmac_data cpmac_low_data = { 260 .reset_bit = 17, 261 .power_bit = 20, 262 .phy_mask = 0x80000000, 263}; 264 265static struct plat_cpmac_data cpmac_high_data = { 266 .reset_bit = 21, 267 .power_bit = 22, 268 .phy_mask = 0x7fffffff, 269}; 270 271static u64 cpmac_dma_mask = DMA_BIT_MASK(32); 272 273static struct platform_device cpmac_low = { 274 .id = 0, 275 .name = "cpmac", 276 .dev = { 277 .dma_mask = &cpmac_dma_mask, 278 .coherent_dma_mask = DMA_BIT_MASK(32), 279 .platform_data = &cpmac_low_data, 280 }, 281 .resource = cpmac_low_res, 282 .num_resources = ARRAY_SIZE(cpmac_low_res), 283}; 284 285static struct platform_device cpmac_high = { 286 .id = 1, 287 .name = "cpmac", 288 .dev = { 289 .dma_mask = &cpmac_dma_mask, 290 .coherent_dma_mask = DMA_BIT_MASK(32), 291 .platform_data = &cpmac_high_data, 292 }, 293 .resource = cpmac_high_res, 294 .num_resources = ARRAY_SIZE(cpmac_high_res), 295}; 296 297static void __init cpmac_get_mac(int instance, unsigned char *dev_addr) 298{ 299 char name[5], *mac; 300 301 sprintf(name, "mac%c", 'a' + instance); 302 mac = prom_getenv(name); 303 if (!mac && instance) { 304 sprintf(name, "mac%c", 'a'); 305 mac = prom_getenv(name); 306 } 307 308 if (mac) { 309 if (!mac_pton(mac, dev_addr)) { 310 pr_warn("cannot parse mac address, using random address\n"); 311 eth_random_addr(dev_addr); 312 } 313 } else 314 eth_random_addr(dev_addr); 315} 316 317/***************************************************************************** 318 * USB 319 ****************************************************************************/ 320static struct resource usb_res[] = { 321 { 322 .name = "regs", 323 .flags = IORESOURCE_MEM, 324 .start = AR7_REGS_USB, 325 .end = AR7_REGS_USB + 0xff, 326 }, 327 { 328 .name = "irq", 329 .flags = IORESOURCE_IRQ, 330 .start = 32, 331 .end = 32, 332 }, 333 { 334 .name = "mem", 335 .flags = IORESOURCE_MEM, 336 .start = 0x03400000, 337 .end = 0x03401fff, 338 }, 339}; 340 341static struct platform_device ar7_udc = { 342 .name = "ar7_udc", 343 .resource = usb_res, 344 .num_resources = ARRAY_SIZE(usb_res), 345}; 346 347/***************************************************************************** 348 * LEDs 349 ****************************************************************************/ 350static struct gpio_led default_leds[] = { 351 { 352 .name = "status", 353 .gpio = 8, 354 .active_low = 1, 355 }, 356}; 357 358static struct gpio_led titan_leds[] = { 359 { .name = "status", .gpio = 8, .active_low = 1, }, 360 { .name = "wifi", .gpio = 13, .active_low = 1, }, 361}; 362 363static struct gpio_led dsl502t_leds[] = { 364 { 365 .name = "status", 366 .gpio = 9, 367 .active_low = 1, 368 }, 369 { 370 .name = "ethernet", 371 .gpio = 7, 372 .active_low = 1, 373 }, 374 { 375 .name = "usb", 376 .gpio = 12, 377 .active_low = 1, 378 }, 379}; 380 381static struct gpio_led dg834g_leds[] = { 382 { 383 .name = "ppp", 384 .gpio = 6, 385 .active_low = 1, 386 }, 387 { 388 .name = "status", 389 .gpio = 7, 390 .active_low = 1, 391 }, 392 { 393 .name = "adsl", 394 .gpio = 8, 395 .active_low = 1, 396 }, 397 { 398 .name = "wifi", 399 .gpio = 12, 400 .active_low = 1, 401 }, 402 { 403 .name = "power", 404 .gpio = 14, 405 .active_low = 1, 406 .default_trigger = "default-on", 407 }, 408}; 409 410static struct gpio_led fb_sl_leds[] = { 411 { 412 .name = "1", 413 .gpio = 7, 414 }, 415 { 416 .name = "2", 417 .gpio = 13, 418 .active_low = 1, 419 }, 420 { 421 .name = "3", 422 .gpio = 10, 423 .active_low = 1, 424 }, 425 { 426 .name = "4", 427 .gpio = 12, 428 .active_low = 1, 429 }, 430 { 431 .name = "5", 432 .gpio = 9, 433 .active_low = 1, 434 }, 435}; 436 437static struct gpio_led fb_fon_leds[] = { 438 { 439 .name = "1", 440 .gpio = 8, 441 }, 442 { 443 .name = "2", 444 .gpio = 3, 445 .active_low = 1, 446 }, 447 { 448 .name = "3", 449 .gpio = 5, 450 }, 451 { 452 .name = "4", 453 .gpio = 4, 454 .active_low = 1, 455 }, 456 { 457 .name = "5", 458 .gpio = 11, 459 .active_low = 1, 460 }, 461}; 462 463static struct gpio_led gt701_leds[] = { 464 { 465 .name = "inet:green", 466 .gpio = 13, 467 .active_low = 1, 468 }, 469 { 470 .name = "usb", 471 .gpio = 12, 472 .active_low = 1, 473 }, 474 { 475 .name = "inet:red", 476 .gpio = 9, 477 .active_low = 1, 478 }, 479 { 480 .name = "power:red", 481 .gpio = 7, 482 .active_low = 1, 483 }, 484 { 485 .name = "power:green", 486 .gpio = 8, 487 .active_low = 1, 488 .default_trigger = "default-on", 489 }, 490 { 491 .name = "ethernet", 492 .gpio = 10, 493 .active_low = 1, 494 }, 495}; 496 497static struct gpio_led_platform_data ar7_led_data; 498 499static struct platform_device ar7_gpio_leds = { 500 .name = "leds-gpio", 501 .dev = { 502 .platform_data = &ar7_led_data, 503 } 504}; 505 506static void __init detect_leds(void) 507{ 508 char *prid, *usb_prod; 509 510 /* Default LEDs */ 511 ar7_led_data.num_leds = ARRAY_SIZE(default_leds); 512 ar7_led_data.leds = default_leds; 513 514 /* FIXME: the whole thing is unreliable */ 515 prid = prom_getenv("ProductID"); 516 usb_prod = prom_getenv("usb_prod"); 517 518 /* If we can't get the product id from PROM, use the default LEDs */ 519 if (!prid) 520 return; 521 522 if (strstr(prid, "Fritz_Box_FON")) { 523 ar7_led_data.num_leds = ARRAY_SIZE(fb_fon_leds); 524 ar7_led_data.leds = fb_fon_leds; 525 } else if (strstr(prid, "Fritz_Box_")) { 526 ar7_led_data.num_leds = ARRAY_SIZE(fb_sl_leds); 527 ar7_led_data.leds = fb_sl_leds; 528 } else if ((!strcmp(prid, "AR7RD") || !strcmp(prid, "AR7DB")) 529 && usb_prod != NULL && strstr(usb_prod, "DSL-502T")) { 530 ar7_led_data.num_leds = ARRAY_SIZE(dsl502t_leds); 531 ar7_led_data.leds = dsl502t_leds; 532 } else if (strstr(prid, "DG834")) { 533 ar7_led_data.num_leds = ARRAY_SIZE(dg834g_leds); 534 ar7_led_data.leds = dg834g_leds; 535 } else if (strstr(prid, "CYWM") || strstr(prid, "CYWL")) { 536 ar7_led_data.num_leds = ARRAY_SIZE(titan_leds); 537 ar7_led_data.leds = titan_leds; 538 } else if (strstr(prid, "GT701")) { 539 ar7_led_data.num_leds = ARRAY_SIZE(gt701_leds); 540 ar7_led_data.leds = gt701_leds; 541 } 542} 543 544/***************************************************************************** 545 * Watchdog 546 ****************************************************************************/ 547static struct resource ar7_wdt_res = { 548 .name = "regs", 549 .flags = IORESOURCE_MEM, 550 .start = -1, /* Filled at runtime */ 551 .end = -1, /* Filled at runtime */ 552}; 553 554static struct platform_device ar7_wdt = { 555 .name = "ar7_wdt", 556 .resource = &ar7_wdt_res, 557 .num_resources = 1, 558}; 559 560/***************************************************************************** 561 * Init 562 ****************************************************************************/ 563static int __init ar7_register_uarts(void) 564{ 565#ifdef CONFIG_SERIAL_8250 566 static struct uart_port uart_port __initdata; 567 struct clk *bus_clk; 568 int res; 569 570 memset(&uart_port, 0, sizeof(struct uart_port)); 571 572 bus_clk = clk_get(NULL, "bus"); 573 if (IS_ERR(bus_clk)) 574 panic("unable to get bus clk"); 575 576 uart_port.type = PORT_AR7; 577 uart_port.uartclk = clk_get_rate(bus_clk) / 2; 578 uart_port.iotype = UPIO_MEM32; 579 uart_port.regshift = 2; 580 581 uart_port.line = 0; 582 uart_port.irq = AR7_IRQ_UART0; 583 uart_port.mapbase = AR7_REGS_UART0; 584 uart_port.membase = ioremap(uart_port.mapbase, 256); 585 586 res = early_serial_setup(&uart_port); 587 if (res) 588 return res; 589 590 /* Only TNETD73xx have a second serial port */ 591 if (ar7_has_second_uart()) { 592 uart_port.line = 1; 593 uart_port.irq = AR7_IRQ_UART1; 594 uart_port.mapbase = UR8_REGS_UART1; 595 uart_port.membase = ioremap(uart_port.mapbase, 256); 596 597 res = early_serial_setup(&uart_port); 598 if (res) 599 return res; 600 } 601#endif 602 603 return 0; 604} 605 606static void __init titan_fixup_devices(void) 607{ 608 /* Set vlynq0 data */ 609 vlynq_low_data.reset_bit = 15; 610 vlynq_low_data.gpio_bit = 14; 611 612 /* Set vlynq1 data */ 613 vlynq_high_data.reset_bit = 16; 614 vlynq_high_data.gpio_bit = 7; 615 616 /* Set vlynq0 resources */ 617 vlynq_low_res[0].start = TITAN_REGS_VLYNQ0; 618 vlynq_low_res[0].end = TITAN_REGS_VLYNQ0 + 0xff; 619 vlynq_low_res[1].start = 33; 620 vlynq_low_res[1].end = 33; 621 vlynq_low_res[2].start = 0x0c000000; 622 vlynq_low_res[2].end = 0x0fffffff; 623 vlynq_low_res[3].start = 80; 624 vlynq_low_res[3].end = 111; 625 626 /* Set vlynq1 resources */ 627 vlynq_high_res[0].start = TITAN_REGS_VLYNQ1; 628 vlynq_high_res[0].end = TITAN_REGS_VLYNQ1 + 0xff; 629 vlynq_high_res[1].start = 34; 630 vlynq_high_res[1].end = 34; 631 vlynq_high_res[2].start = 0x40000000; 632 vlynq_high_res[2].end = 0x43ffffff; 633 vlynq_high_res[3].start = 112; 634 vlynq_high_res[3].end = 143; 635 636 /* Set cpmac0 data */ 637 cpmac_low_data.phy_mask = 0x40000000; 638 639 /* Set cpmac1 data */ 640 cpmac_high_data.phy_mask = 0x80000000; 641 642 /* Set cpmac0 resources */ 643 cpmac_low_res[0].start = TITAN_REGS_MAC0; 644 cpmac_low_res[0].end = TITAN_REGS_MAC0 + 0x7ff; 645 646 /* Set cpmac1 resources */ 647 cpmac_high_res[0].start = TITAN_REGS_MAC1; 648 cpmac_high_res[0].end = TITAN_REGS_MAC1 + 0x7ff; 649} 650 651static int __init ar7_register_devices(void) 652{ 653 void __iomem *bootcr; 654 u32 val; 655 int res; 656 657 res = ar7_register_uarts(); 658 if (res) 659 pr_err("unable to setup uart(s): %d\n", res); 660 661 res = platform_device_register(&physmap_flash); 662 if (res) 663 pr_warn("unable to register physmap-flash: %d\n", res); 664 665 if (ar7_is_titan()) 666 titan_fixup_devices(); 667 668 ar7_device_disable(vlynq_low_data.reset_bit); 669 res = platform_device_register(&vlynq_low); 670 if (res) 671 pr_warn("unable to register vlynq-low: %d\n", res); 672 673 if (ar7_has_high_vlynq()) { 674 ar7_device_disable(vlynq_high_data.reset_bit); 675 res = platform_device_register(&vlynq_high); 676 if (res) 677 pr_warn("unable to register vlynq-high: %d\n", res); 678 } 679 680 if (ar7_has_high_cpmac()) { 681 res = fixed_phy_add(PHY_POLL, cpmac_high.id, 682 &fixed_phy_status, -1); 683 if (!res) { 684 cpmac_get_mac(1, cpmac_high_data.dev_addr); 685 686 res = platform_device_register(&cpmac_high); 687 if (res) 688 pr_warn("unable to register cpmac-high: %d\n", 689 res); 690 } else 691 pr_warn("unable to add cpmac-high phy: %d\n", res); 692 } else 693 cpmac_low_data.phy_mask = 0xffffffff; 694 695 res = fixed_phy_add(PHY_POLL, cpmac_low.id, &fixed_phy_status, -1); 696 if (!res) { 697 cpmac_get_mac(0, cpmac_low_data.dev_addr); 698 res = platform_device_register(&cpmac_low); 699 if (res) 700 pr_warn("unable to register cpmac-low: %d\n", res); 701 } else 702 pr_warn("unable to add cpmac-low phy: %d\n", res); 703 704 detect_leds(); 705 res = platform_device_register(&ar7_gpio_leds); 706 if (res) 707 pr_warn("unable to register leds: %d\n", res); 708 709 res = platform_device_register(&ar7_udc); 710 if (res) 711 pr_warn("unable to register usb slave: %d\n", res); 712 713 /* Register watchdog only if enabled in hardware */ 714 bootcr = ioremap_nocache(AR7_REGS_DCL, 4); 715 val = readl(bootcr); 716 iounmap(bootcr); 717 if (val & AR7_WDT_HW_ENA) { 718 if (ar7_has_high_vlynq()) 719 ar7_wdt_res.start = UR8_REGS_WDT; 720 else 721 ar7_wdt_res.start = AR7_REGS_WDT; 722 723 ar7_wdt_res.end = ar7_wdt_res.start + 0x20; 724 res = platform_device_register(&ar7_wdt); 725 if (res) 726 pr_warn("unable to register watchdog: %d\n", res); 727 } 728 729 return 0; 730} 731device_initcall(ar7_register_devices); 732