linux/arch/arm/mach-orion5x/ts78xx-setup.c
<<
>>
Prefs
   1/*
   2 * arch/arm/mach-orion5x/ts78xx-setup.c
   3 *
   4 * Maintainer: Alexander Clouter <alex@digriz.org.uk>
   5 *
   6 * This file is licensed under the terms of the GNU General Public
   7 * License version 2.  This program is licensed "as is" without any
   8 * warranty of any kind, whether express or implied.
   9 */
  10
  11#include <linux/kernel.h>
  12#include <linux/init.h>
  13#include <linux/sysfs.h>
  14#include <linux/platform_device.h>
  15#include <linux/mv643xx_eth.h>
  16#include <linux/ata_platform.h>
  17#include <linux/m48t86.h>
  18#include <linux/mtd/nand.h>
  19#include <linux/mtd/partitions.h>
  20#include <linux/timeriomem-rng.h>
  21#include <asm/mach-types.h>
  22#include <asm/mach/arch.h>
  23#include <asm/mach/map.h>
  24#include <mach/orion5x.h>
  25#include "common.h"
  26#include "mpp.h"
  27#include "ts78xx-fpga.h"
  28
  29/*****************************************************************************
  30 * TS-78xx Info
  31 ****************************************************************************/
  32
  33/*
  34 * FPGA - lives where the PCI bus would be at ORION5X_PCI_MEM_PHYS_BASE
  35 */
  36#define TS78XX_FPGA_REGS_PHYS_BASE      0xe8000000
  37#define TS78XX_FPGA_REGS_VIRT_BASE      0xff900000
  38#define TS78XX_FPGA_REGS_SIZE           SZ_1M
  39
  40static struct ts78xx_fpga_data ts78xx_fpga = {
  41        .id             = 0,
  42        .state          = 1,
  43/*      .supports       = ... - populated by ts78xx_fpga_supports() */
  44};
  45
  46/*****************************************************************************
  47 * I/O Address Mapping
  48 ****************************************************************************/
  49static struct map_desc ts78xx_io_desc[] __initdata = {
  50        {
  51                .virtual        = TS78XX_FPGA_REGS_VIRT_BASE,
  52                .pfn            = __phys_to_pfn(TS78XX_FPGA_REGS_PHYS_BASE),
  53                .length         = TS78XX_FPGA_REGS_SIZE,
  54                .type           = MT_DEVICE,
  55        },
  56};
  57
  58void __init ts78xx_map_io(void)
  59{
  60        orion5x_map_io();
  61        iotable_init(ts78xx_io_desc, ARRAY_SIZE(ts78xx_io_desc));
  62}
  63
  64/*****************************************************************************
  65 * Ethernet
  66 ****************************************************************************/
  67static struct mv643xx_eth_platform_data ts78xx_eth_data = {
  68        .phy_addr       = MV643XX_ETH_PHY_ADDR(0),
  69};
  70
  71/*****************************************************************************
  72 * SATA
  73 ****************************************************************************/
  74static struct mv_sata_platform_data ts78xx_sata_data = {
  75        .n_ports        = 2,
  76};
  77
  78/*****************************************************************************
  79 * RTC M48T86 - nicked^Wborrowed from arch/arm/mach-ep93xx/ts72xx.c
  80 ****************************************************************************/
  81#define TS_RTC_CTRL     (TS78XX_FPGA_REGS_VIRT_BASE | 0x808)
  82#define TS_RTC_DATA     (TS78XX_FPGA_REGS_VIRT_BASE | 0x80c)
  83
  84static unsigned char ts78xx_ts_rtc_readbyte(unsigned long addr)
  85{
  86        writeb(addr, TS_RTC_CTRL);
  87        return readb(TS_RTC_DATA);
  88}
  89
  90static void ts78xx_ts_rtc_writebyte(unsigned char value, unsigned long addr)
  91{
  92        writeb(addr, TS_RTC_CTRL);
  93        writeb(value, TS_RTC_DATA);
  94}
  95
  96static struct m48t86_ops ts78xx_ts_rtc_ops = {
  97        .readbyte       = ts78xx_ts_rtc_readbyte,
  98        .writebyte      = ts78xx_ts_rtc_writebyte,
  99};
 100
 101static struct platform_device ts78xx_ts_rtc_device = {
 102        .name           = "rtc-m48t86",
 103        .id             = -1,
 104        .dev            = {
 105                .platform_data  = &ts78xx_ts_rtc_ops,
 106        },
 107        .num_resources  = 0,
 108};
 109
 110/*
 111 * TS uses some of the user storage space on the RTC chip so see if it is
 112 * present; as it's an optional feature at purchase time and not all boards
 113 * will have it present
 114 *
 115 * I've used the method TS use in their rtc7800.c example for the detection
 116 *
 117 * TODO: track down a guinea pig without an RTC to see if we can work out a
 118 *              better RTC detection routine
 119 */
 120static int ts78xx_ts_rtc_load(void)
 121{
 122        int rc;
 123        unsigned char tmp_rtc0, tmp_rtc1;
 124
 125        tmp_rtc0 = ts78xx_ts_rtc_readbyte(126);
 126        tmp_rtc1 = ts78xx_ts_rtc_readbyte(127);
 127
 128        ts78xx_ts_rtc_writebyte(0x00, 126);
 129        ts78xx_ts_rtc_writebyte(0x55, 127);
 130        if (ts78xx_ts_rtc_readbyte(127) == 0x55) {
 131                ts78xx_ts_rtc_writebyte(0xaa, 127);
 132                if (ts78xx_ts_rtc_readbyte(127) == 0xaa
 133                                && ts78xx_ts_rtc_readbyte(126) == 0x00) {
 134                        ts78xx_ts_rtc_writebyte(tmp_rtc0, 126);
 135                        ts78xx_ts_rtc_writebyte(tmp_rtc1, 127);
 136
 137                        if (ts78xx_fpga.supports.ts_rtc.init == 0) {
 138                                rc = platform_device_register(&ts78xx_ts_rtc_device);
 139                                if (!rc)
 140                                        ts78xx_fpga.supports.ts_rtc.init = 1;
 141                        } else
 142                                rc = platform_device_add(&ts78xx_ts_rtc_device);
 143
 144                        return rc;
 145                }
 146        }
 147
 148        return -ENODEV;
 149};
 150
 151static void ts78xx_ts_rtc_unload(void)
 152{
 153        platform_device_del(&ts78xx_ts_rtc_device);
 154}
 155
 156/*****************************************************************************
 157 * NAND Flash
 158 ****************************************************************************/
 159#define TS_NAND_CTRL    (TS78XX_FPGA_REGS_VIRT_BASE | 0x800)    /* VIRT */
 160#define TS_NAND_DATA    (TS78XX_FPGA_REGS_PHYS_BASE | 0x804)    /* PHYS */
 161
 162/*
 163 * hardware specific access to control-lines
 164 *
 165 * ctrl:
 166 * NAND_NCE: bit 0 -> bit 2
 167 * NAND_CLE: bit 1 -> bit 1
 168 * NAND_ALE: bit 2 -> bit 0
 169 */
 170static void ts78xx_ts_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
 171                        unsigned int ctrl)
 172{
 173        struct nand_chip *this = mtd->priv;
 174
 175        if (ctrl & NAND_CTRL_CHANGE) {
 176                unsigned char bits;
 177
 178                bits = (ctrl & NAND_NCE) << 2;
 179                bits |= ctrl & NAND_CLE;
 180                bits |= (ctrl & NAND_ALE) >> 2;
 181
 182                writeb((readb(TS_NAND_CTRL) & ~0x7) | bits, TS_NAND_CTRL);
 183        }
 184
 185        if (cmd != NAND_CMD_NONE)
 186                writeb(cmd, this->IO_ADDR_W);
 187}
 188
 189static int ts78xx_ts_nand_dev_ready(struct mtd_info *mtd)
 190{
 191        return readb(TS_NAND_CTRL) & 0x20;
 192}
 193
 194const char *ts_nand_part_probes[] = { "cmdlinepart", NULL };
 195
 196static struct mtd_partition ts78xx_ts_nand_parts[] = {
 197        {
 198                .name           = "mbr",
 199                .offset         = 0,
 200                .size           = SZ_128K,
 201                .mask_flags     = MTD_WRITEABLE,
 202        }, {
 203                .name           = "kernel",
 204                .offset         = MTDPART_OFS_APPEND,
 205                .size           = SZ_4M,
 206        }, {
 207                .name           = "initrd",
 208                .offset         = MTDPART_OFS_APPEND,
 209                .size           = SZ_4M,
 210        }, {
 211                .name           = "rootfs",
 212                .offset         = MTDPART_OFS_APPEND,
 213                .size           = MTDPART_SIZ_FULL,
 214        }
 215};
 216
 217static struct platform_nand_data ts78xx_ts_nand_data = {
 218        .chip   = {
 219                .part_probe_types       = ts_nand_part_probes,
 220                .partitions             = ts78xx_ts_nand_parts,
 221                .nr_partitions          = ARRAY_SIZE(ts78xx_ts_nand_parts),
 222                .chip_delay             = 15,
 223                .options                = NAND_USE_FLASH_BBT,
 224        },
 225        .ctrl   = {
 226                /*
 227                 * The HW ECC offloading functions, used to give about a 9%
 228                 * performance increase for 'dd if=/dev/mtdblockX' and 5% for
 229                 * nanddump.  This all however was changed by git commit
 230                 * e6cf5df1838c28bb060ac45b5585e48e71bbc740 so now there is
 231                 * no performance advantage to be had so we no longer bother
 232                 */
 233                .cmd_ctrl               = ts78xx_ts_nand_cmd_ctrl,
 234                .dev_ready              = ts78xx_ts_nand_dev_ready,
 235        },
 236};
 237
 238static struct resource ts78xx_ts_nand_resources = {
 239        .start          = TS_NAND_DATA,
 240        .end            = TS_NAND_DATA + 4,
 241        .flags          = IORESOURCE_IO,
 242};
 243
 244static struct platform_device ts78xx_ts_nand_device = {
 245        .name           = "gen_nand",
 246        .id             = -1,
 247        .dev            = {
 248                .platform_data  = &ts78xx_ts_nand_data,
 249        },
 250        .resource       = &ts78xx_ts_nand_resources,
 251        .num_resources  = 1,
 252};
 253
 254static int ts78xx_ts_nand_load(void)
 255{
 256        int rc;
 257
 258        if (ts78xx_fpga.supports.ts_nand.init == 0) {
 259                rc = platform_device_register(&ts78xx_ts_nand_device);
 260                if (!rc)
 261                        ts78xx_fpga.supports.ts_nand.init = 1;
 262        } else
 263                rc = platform_device_add(&ts78xx_ts_nand_device);
 264
 265        return rc;
 266};
 267
 268static void ts78xx_ts_nand_unload(void)
 269{
 270        platform_device_del(&ts78xx_ts_nand_device);
 271}
 272
 273/*****************************************************************************
 274 * HW RNG
 275 ****************************************************************************/
 276#define TS_RNG_DATA     (TS78XX_FPGA_REGS_PHYS_BASE | 0x044)
 277
 278static struct resource ts78xx_ts_rng_resource = {
 279        .flags          = IORESOURCE_MEM,
 280        .start          = TS_RNG_DATA,
 281        .end            = TS_RNG_DATA + 4 - 1,
 282};
 283
 284static struct timeriomem_rng_data ts78xx_ts_rng_data = {
 285        .period         = 1000000, /* one second */
 286};
 287
 288static struct platform_device ts78xx_ts_rng_device = {
 289        .name           = "timeriomem_rng",
 290        .id             = -1,
 291        .dev            = {
 292                .platform_data  = &ts78xx_ts_rng_data,
 293        },
 294        .resource       = &ts78xx_ts_rng_resource,
 295        .num_resources  = 1,
 296};
 297
 298static int ts78xx_ts_rng_load(void)
 299{
 300        int rc;
 301
 302        if (ts78xx_fpga.supports.ts_rng.init == 0) {
 303                rc = platform_device_register(&ts78xx_ts_rng_device);
 304                if (!rc)
 305                        ts78xx_fpga.supports.ts_rng.init = 1;
 306        } else
 307                rc = platform_device_add(&ts78xx_ts_rng_device);
 308
 309        return rc;
 310};
 311
 312static void ts78xx_ts_rng_unload(void)
 313{
 314        platform_device_del(&ts78xx_ts_rng_device);
 315}
 316
 317/*****************************************************************************
 318 * FPGA 'hotplug' support code
 319 ****************************************************************************/
 320static void ts78xx_fpga_devices_zero_init(void)
 321{
 322        ts78xx_fpga.supports.ts_rtc.init = 0;
 323        ts78xx_fpga.supports.ts_nand.init = 0;
 324        ts78xx_fpga.supports.ts_rng.init = 0;
 325}
 326
 327static void ts78xx_fpga_supports(void)
 328{
 329        /* TODO: put this 'table' into ts78xx-fpga.h */
 330        switch (ts78xx_fpga.id) {
 331        case TS7800_REV_1:
 332        case TS7800_REV_2:
 333        case TS7800_REV_3:
 334        case TS7800_REV_4:
 335        case TS7800_REV_5:
 336                ts78xx_fpga.supports.ts_rtc.present = 1;
 337                ts78xx_fpga.supports.ts_nand.present = 1;
 338                ts78xx_fpga.supports.ts_rng.present = 1;
 339                break;
 340        default:
 341                ts78xx_fpga.supports.ts_rtc.present = 0;
 342                ts78xx_fpga.supports.ts_nand.present = 0;
 343                ts78xx_fpga.supports.ts_rng.present = 0;
 344        }
 345}
 346
 347static int ts78xx_fpga_load_devices(void)
 348{
 349        int tmp, ret = 0;
 350
 351        if (ts78xx_fpga.supports.ts_rtc.present == 1) {
 352                tmp = ts78xx_ts_rtc_load();
 353                if (tmp) {
 354                        printk(KERN_INFO "TS-78xx: RTC not registered\n");
 355                        ts78xx_fpga.supports.ts_rtc.present = 0;
 356                }
 357                ret |= tmp;
 358        }
 359        if (ts78xx_fpga.supports.ts_nand.present == 1) {
 360                tmp = ts78xx_ts_nand_load();
 361                if (tmp) {
 362                        printk(KERN_INFO "TS-78xx: NAND not registered\n");
 363                        ts78xx_fpga.supports.ts_nand.present = 0;
 364                }
 365                ret |= tmp;
 366        }
 367        if (ts78xx_fpga.supports.ts_rng.present == 1) {
 368                tmp = ts78xx_ts_rng_load();
 369                if (tmp) {
 370                        printk(KERN_INFO "TS-78xx: RNG not registered\n");
 371                        ts78xx_fpga.supports.ts_rng.present = 0;
 372                }
 373                ret |= tmp;
 374        }
 375
 376        return ret;
 377}
 378
 379static int ts78xx_fpga_unload_devices(void)
 380{
 381        int ret = 0;
 382
 383        if (ts78xx_fpga.supports.ts_rtc.present == 1)
 384                ts78xx_ts_rtc_unload();
 385        if (ts78xx_fpga.supports.ts_nand.present == 1)
 386                ts78xx_ts_nand_unload();
 387        if (ts78xx_fpga.supports.ts_rng.present == 1)
 388                ts78xx_ts_rng_unload();
 389
 390        return ret;
 391}
 392
 393static int ts78xx_fpga_load(void)
 394{
 395        ts78xx_fpga.id = readl(TS78XX_FPGA_REGS_VIRT_BASE);
 396
 397        printk(KERN_INFO "TS-78xx FPGA: magic=0x%.6x, rev=0x%.2x\n",
 398                        (ts78xx_fpga.id >> 8) & 0xffffff,
 399                        ts78xx_fpga.id & 0xff);
 400
 401        ts78xx_fpga_supports();
 402
 403        if (ts78xx_fpga_load_devices()) {
 404                ts78xx_fpga.state = -1;
 405                return -EBUSY;
 406        }
 407
 408        return 0;
 409};
 410
 411static int ts78xx_fpga_unload(void)
 412{
 413        unsigned int fpga_id;
 414
 415        fpga_id = readl(TS78XX_FPGA_REGS_VIRT_BASE);
 416
 417        /*
 418         * There does not seem to be a feasible way to block access to the GPIO
 419         * pins from userspace (/dev/mem).  This if clause should hopefully warn
 420         * those foolish enough not to follow 'policy' :)
 421         *
 422         * UrJTAG SVN since r1381 can be used to reprogram the FPGA
 423         */
 424        if (ts78xx_fpga.id != fpga_id) {
 425                printk(KERN_ERR "TS-78xx FPGA: magic/rev mismatch\n"
 426                        "TS-78xx FPGA: was 0x%.6x/%.2x but now 0x%.6x/%.2x\n",
 427                        (ts78xx_fpga.id >> 8) & 0xffffff, ts78xx_fpga.id & 0xff,
 428                        (fpga_id >> 8) & 0xffffff, fpga_id & 0xff);
 429                ts78xx_fpga.state = -1;
 430                return -EBUSY;
 431        }
 432
 433        if (ts78xx_fpga_unload_devices()) {
 434                ts78xx_fpga.state = -1;
 435                return -EBUSY;
 436        }
 437
 438        return 0;
 439};
 440
 441static ssize_t ts78xx_fpga_show(struct kobject *kobj,
 442                        struct kobj_attribute *attr, char *buf)
 443{
 444        if (ts78xx_fpga.state < 0)
 445                return sprintf(buf, "borked\n");
 446
 447        return sprintf(buf, "%s\n", (ts78xx_fpga.state) ? "online" : "offline");
 448}
 449
 450static ssize_t ts78xx_fpga_store(struct kobject *kobj,
 451                        struct kobj_attribute *attr, const char *buf, size_t n)
 452{
 453        int value, ret;
 454
 455        if (ts78xx_fpga.state < 0) {
 456                printk(KERN_ERR "TS-78xx FPGA: borked, you must powercycle asap\n");
 457                return -EBUSY;
 458        }
 459
 460        if (strncmp(buf, "online", sizeof("online") - 1) == 0)
 461                value = 1;
 462        else if (strncmp(buf, "offline", sizeof("offline") - 1) == 0)
 463                value = 0;
 464        else {
 465                printk(KERN_ERR "ts78xx_fpga_store: Invalid value\n");
 466                return -EINVAL;
 467        }
 468
 469        if (ts78xx_fpga.state == value)
 470                return n;
 471
 472        ret = (ts78xx_fpga.state == 0)
 473                ? ts78xx_fpga_load()
 474                : ts78xx_fpga_unload();
 475
 476        if (!(ret < 0))
 477                ts78xx_fpga.state = value;
 478
 479        return n;
 480}
 481
 482static struct kobj_attribute ts78xx_fpga_attr =
 483        __ATTR(ts78xx_fpga, 0644, ts78xx_fpga_show, ts78xx_fpga_store);
 484
 485/*****************************************************************************
 486 * General Setup
 487 ****************************************************************************/
 488static struct orion5x_mpp_mode ts78xx_mpp_modes[] __initdata = {
 489        {  0, MPP_UNUSED },
 490        {  1, MPP_GPIO },               /* JTAG Clock */
 491        {  2, MPP_GPIO },               /* JTAG Data In */
 492        {  3, MPP_GPIO },               /* Lat ECP2 256 FPGA - PB2B */
 493        {  4, MPP_GPIO },               /* JTAG Data Out */
 494        {  5, MPP_GPIO },               /* JTAG TMS */
 495        {  6, MPP_GPIO },               /* Lat ECP2 256 FPGA - PB31A_CLK4+ */
 496        {  7, MPP_GPIO },               /* Lat ECP2 256 FPGA - PB22B */
 497        {  8, MPP_UNUSED },
 498        {  9, MPP_UNUSED },
 499        { 10, MPP_UNUSED },
 500        { 11, MPP_UNUSED },
 501        { 12, MPP_UNUSED },
 502        { 13, MPP_UNUSED },
 503        { 14, MPP_UNUSED },
 504        { 15, MPP_UNUSED },
 505        { 16, MPP_UART },
 506        { 17, MPP_UART },
 507        { 18, MPP_UART },
 508        { 19, MPP_UART },
 509        /*
 510         * MPP[20] PCI Clock Out 1
 511         * MPP[21] PCI Clock Out 0
 512         * MPP[22] Unused
 513         * MPP[23] Unused
 514         * MPP[24] Unused
 515         * MPP[25] Unused
 516         */
 517        { -1 },
 518};
 519
 520static void __init ts78xx_init(void)
 521{
 522        int ret;
 523
 524        /*
 525         * Setup basic Orion functions. Need to be called early.
 526         */
 527        orion5x_init();
 528
 529        orion5x_mpp_conf(ts78xx_mpp_modes);
 530
 531        /*
 532         * Configure peripherals.
 533         */
 534        orion5x_ehci0_init();
 535        orion5x_ehci1_init();
 536        orion5x_eth_init(&ts78xx_eth_data);
 537        orion5x_sata_init(&ts78xx_sata_data);
 538        orion5x_uart0_init();
 539        orion5x_uart1_init();
 540        orion5x_xor_init();
 541
 542        /* FPGA init */
 543        ts78xx_fpga_devices_zero_init();
 544        ret = ts78xx_fpga_load();
 545        ret = sysfs_create_file(power_kobj, &ts78xx_fpga_attr.attr);
 546        if (ret)
 547                printk(KERN_ERR "sysfs_create_file failed: %d\n", ret);
 548}
 549
 550MACHINE_START(TS78XX, "Technologic Systems TS-78xx SBC")
 551        /* Maintainer: Alexander Clouter <alex@digriz.org.uk> */
 552        .phys_io        = ORION5X_REGS_PHYS_BASE,
 553        .io_pg_offst    = ((ORION5X_REGS_VIRT_BASE) >> 18) & 0xFFFC,
 554        .boot_params    = 0x00000100,
 555        .init_machine   = ts78xx_init,
 556        .map_io         = ts78xx_map_io,
 557        .init_irq       = orion5x_init_irq,
 558        .timer          = &orion5x_timer,
 559MACHINE_END
 560