linux/drivers/net/ixp2000/pm3386.c
<<
>>
Prefs
   1/*
   2 * Helper functions for the PM3386s on the Radisys ENP2611
   3 * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org>
   4 * Dedicated to Marija Kulikova.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/delay.h>
  14#include <linux/netdevice.h>
  15#include <asm/io.h>
  16#include "pm3386.h"
  17
  18/*
  19 * Read from register 'reg' of PM3386 device 'pm'.
  20 */
  21static u16 pm3386_reg_read(int pm, int reg)
  22{
  23        void *_reg;
  24        u16 value;
  25
  26        _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
  27        if (pm == 1)
  28                _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
  29
  30        value = *((volatile u16 *)(_reg + (reg << 1)));
  31
  32//      printk(KERN_INFO "pm3386_reg_read(%d, %.3x) = %.8x\n", pm, reg, value);
  33
  34        return value;
  35}
  36
  37/*
  38 * Write to register 'reg' of PM3386 device 'pm', and perform
  39 * a readback from the identification register.
  40 */
  41static void pm3386_reg_write(int pm, int reg, u16 value)
  42{
  43        void *_reg;
  44        u16 dummy;
  45
  46//      printk(KERN_INFO "pm3386_reg_write(%d, %.3x, %.8x)\n", pm, reg, value);
  47
  48        _reg = (void *)ENP2611_PM3386_0_VIRT_BASE;
  49        if (pm == 1)
  50                _reg = (void *)ENP2611_PM3386_1_VIRT_BASE;
  51
  52        *((volatile u16 *)(_reg + (reg << 1))) = value;
  53
  54        dummy = *((volatile u16 *)_reg);
  55        __asm__ __volatile__("mov %0, %0" : "+r" (dummy));
  56}
  57
  58/*
  59 * Read from port 'port' register 'reg', where the registers
  60 * for the different ports are 'spacing' registers apart.
  61 */
  62static u16 pm3386_port_reg_read(int port, int _reg, int spacing)
  63{
  64        int reg;
  65
  66        reg = _reg;
  67        if (port & 1)
  68                reg += spacing;
  69
  70        return pm3386_reg_read(port >> 1, reg);
  71}
  72
  73/*
  74 * Write to port 'port' register 'reg', where the registers
  75 * for the different ports are 'spacing' registers apart.
  76 */
  77static void pm3386_port_reg_write(int port, int _reg, int spacing, u16 value)
  78{
  79        int reg;
  80
  81        reg = _reg;
  82        if (port & 1)
  83                reg += spacing;
  84
  85        pm3386_reg_write(port >> 1, reg, value);
  86}
  87
  88int pm3386_secondary_present(void)
  89{
  90        return pm3386_reg_read(1, 0) == 0x3386;
  91}
  92
  93void pm3386_reset(void)
  94{
  95        u8 mac[3][6];
  96        int secondary;
  97
  98        secondary = pm3386_secondary_present();
  99
 100        /* Save programmed MAC addresses.  */
 101        pm3386_get_mac(0, mac[0]);
 102        pm3386_get_mac(1, mac[1]);
 103        if (secondary)
 104                pm3386_get_mac(2, mac[2]);
 105
 106        /* Assert analog and digital reset.  */
 107        pm3386_reg_write(0, 0x002, 0x0060);
 108        if (secondary)
 109                pm3386_reg_write(1, 0x002, 0x0060);
 110        mdelay(1);
 111
 112        /* Deassert analog reset.  */
 113        pm3386_reg_write(0, 0x002, 0x0062);
 114        if (secondary)
 115                pm3386_reg_write(1, 0x002, 0x0062);
 116        mdelay(10);
 117
 118        /* Deassert digital reset.  */
 119        pm3386_reg_write(0, 0x002, 0x0063);
 120        if (secondary)
 121                pm3386_reg_write(1, 0x002, 0x0063);
 122        mdelay(10);
 123
 124        /* Restore programmed MAC addresses.  */
 125        pm3386_set_mac(0, mac[0]);
 126        pm3386_set_mac(1, mac[1]);
 127        if (secondary)
 128                pm3386_set_mac(2, mac[2]);
 129
 130        /* Disable carrier on all ports.  */
 131        pm3386_set_carrier(0, 0);
 132        pm3386_set_carrier(1, 0);
 133        if (secondary)
 134                pm3386_set_carrier(2, 0);
 135}
 136
 137static u16 swaph(u16 x)
 138{
 139        return ((x << 8) | (x >> 8)) & 0xffff;
 140}
 141
 142int pm3386_port_count(void)
 143{
 144        return 2 + pm3386_secondary_present();
 145}
 146
 147void pm3386_init_port(int port)
 148{
 149        int pm = port >> 1;
 150
 151        /*
 152         * Work around ENP2611 bootloader programming MAC address
 153         * in reverse.
 154         */
 155        if (pm3386_port_reg_read(port, 0x30a, 0x100) == 0x0000 &&
 156            (pm3386_port_reg_read(port, 0x309, 0x100) & 0xff00) == 0x5000) {
 157                u16 temp[3];
 158
 159                temp[0] = pm3386_port_reg_read(port, 0x308, 0x100);
 160                temp[1] = pm3386_port_reg_read(port, 0x309, 0x100);
 161                temp[2] = pm3386_port_reg_read(port, 0x30a, 0x100);
 162                pm3386_port_reg_write(port, 0x308, 0x100, swaph(temp[2]));
 163                pm3386_port_reg_write(port, 0x309, 0x100, swaph(temp[1]));
 164                pm3386_port_reg_write(port, 0x30a, 0x100, swaph(temp[0]));
 165        }
 166
 167        /*
 168         * Initialise narrowbanding mode.  See application note 2010486
 169         * for more information.  (@@@ We also need to issue a reset
 170         * when ROOL or DOOL are detected.)
 171         */
 172        pm3386_port_reg_write(port, 0x708, 0x10, 0xd055);
 173        udelay(500);
 174        pm3386_port_reg_write(port, 0x708, 0x10, 0x5055);
 175
 176        /*
 177         * SPI-3 ingress block.  Set 64 bytes SPI-3 burst size
 178         * towards SPI-3 bridge.
 179         */
 180        pm3386_port_reg_write(port, 0x122, 0x20, 0x0002);
 181
 182        /*
 183         * Enable ingress protocol checking, and soft reset the
 184         * SPI-3 ingress block.
 185         */
 186        pm3386_reg_write(pm, 0x103, 0x0003);
 187        while (!(pm3386_reg_read(pm, 0x103) & 0x80))
 188                ;
 189
 190        /*
 191         * SPI-3 egress block.  Gather 12288 bytes of the current
 192         * packet in the TX fifo before initiating transmit on the
 193         * SERDES interface.  (Prevents TX underflows.)
 194         */
 195        pm3386_port_reg_write(port, 0x221, 0x20, 0x0007);
 196
 197        /*
 198         * Enforce odd parity from the SPI-3 bridge, and soft reset
 199         * the SPI-3 egress block.
 200         */
 201        pm3386_reg_write(pm, 0x203, 0x000d & ~(4 << (port & 1)));
 202        while ((pm3386_reg_read(pm, 0x203) & 0x000c) != 0x000c)
 203                ;
 204
 205        /*
 206         * EGMAC block.  Set this channels to reject long preambles,
 207         * not send or transmit PAUSE frames, enable preamble checking,
 208         * disable frame length checking, enable FCS appending, enable
 209         * TX frame padding.
 210         */
 211        pm3386_port_reg_write(port, 0x302, 0x100, 0x0113);
 212
 213        /*
 214         * Soft reset the EGMAC block.
 215         */
 216        pm3386_port_reg_write(port, 0x301, 0x100, 0x8000);
 217        pm3386_port_reg_write(port, 0x301, 0x100, 0x0000);
 218
 219        /*
 220         * Auto-sense autonegotiation status.
 221         */
 222        pm3386_port_reg_write(port, 0x306, 0x100, 0x0100);
 223
 224        /*
 225         * Allow reception of jumbo frames.
 226         */
 227        pm3386_port_reg_write(port, 0x310, 0x100, 9018);
 228
 229        /*
 230         * Allow transmission of jumbo frames.
 231         */
 232        pm3386_port_reg_write(port, 0x336, 0x100, 9018);
 233
 234        /* @@@ Should set 0x337/0x437 (RX forwarding threshold.)  */
 235
 236        /*
 237         * Set autonegotiation parameters to 'no PAUSE, full duplex.'
 238         */
 239        pm3386_port_reg_write(port, 0x31c, 0x100, 0x0020);
 240
 241        /*
 242         * Enable and restart autonegotiation.
 243         */
 244        pm3386_port_reg_write(port, 0x318, 0x100, 0x0003);
 245        pm3386_port_reg_write(port, 0x318, 0x100, 0x0002);
 246}
 247
 248void pm3386_get_mac(int port, u8 *mac)
 249{
 250        u16 temp;
 251
 252        temp = pm3386_port_reg_read(port, 0x308, 0x100);
 253        mac[0] = temp & 0xff;
 254        mac[1] = (temp >> 8) & 0xff;
 255
 256        temp = pm3386_port_reg_read(port, 0x309, 0x100);
 257        mac[2] = temp & 0xff;
 258        mac[3] = (temp >> 8) & 0xff;
 259
 260        temp = pm3386_port_reg_read(port, 0x30a, 0x100);
 261        mac[4] = temp & 0xff;
 262        mac[5] = (temp >> 8) & 0xff;
 263}
 264
 265void pm3386_set_mac(int port, u8 *mac)
 266{
 267        pm3386_port_reg_write(port, 0x308, 0x100, (mac[1] << 8) | mac[0]);
 268        pm3386_port_reg_write(port, 0x309, 0x100, (mac[3] << 8) | mac[2]);
 269        pm3386_port_reg_write(port, 0x30a, 0x100, (mac[5] << 8) | mac[4]);
 270}
 271
 272static u32 pm3386_get_stat(int port, u16 base)
 273{
 274        u32 value;
 275
 276        value = pm3386_port_reg_read(port, base, 0x100);
 277        value |= pm3386_port_reg_read(port, base + 1, 0x100) << 16;
 278
 279        return value;
 280}
 281
 282void pm3386_get_stats(int port, struct net_device_stats *stats)
 283{
 284        /*
 285         * Snapshot statistics counters.
 286         */
 287        pm3386_port_reg_write(port, 0x500, 0x100, 0x0001);
 288        while (pm3386_port_reg_read(port, 0x500, 0x100) & 0x0001)
 289                ;
 290
 291        memset(stats, 0, sizeof(*stats));
 292
 293        stats->rx_packets = pm3386_get_stat(port, 0x510);
 294        stats->tx_packets = pm3386_get_stat(port, 0x590);
 295        stats->rx_bytes = pm3386_get_stat(port, 0x514);
 296        stats->tx_bytes = pm3386_get_stat(port, 0x594);
 297        /* @@@ Add other stats.  */
 298}
 299
 300void pm3386_set_carrier(int port, int state)
 301{
 302        pm3386_port_reg_write(port, 0x703, 0x10, state ? 0x1001 : 0x0000);
 303}
 304
 305int pm3386_is_link_up(int port)
 306{
 307        u16 temp;
 308
 309        temp = pm3386_port_reg_read(port, 0x31a, 0x100);
 310        temp = pm3386_port_reg_read(port, 0x31a, 0x100);
 311
 312        return !!(temp & 0x0002);
 313}
 314
 315void pm3386_enable_rx(int port)
 316{
 317        u16 temp;
 318
 319        temp = pm3386_port_reg_read(port, 0x303, 0x100);
 320        temp |= 0x1000;
 321        pm3386_port_reg_write(port, 0x303, 0x100, temp);
 322}
 323
 324void pm3386_disable_rx(int port)
 325{
 326        u16 temp;
 327
 328        temp = pm3386_port_reg_read(port, 0x303, 0x100);
 329        temp &= 0xefff;
 330        pm3386_port_reg_write(port, 0x303, 0x100, temp);
 331}
 332
 333void pm3386_enable_tx(int port)
 334{
 335        u16 temp;
 336
 337        temp = pm3386_port_reg_read(port, 0x303, 0x100);
 338        temp |= 0x4000;
 339        pm3386_port_reg_write(port, 0x303, 0x100, temp);
 340}
 341
 342void pm3386_disable_tx(int port)
 343{
 344        u16 temp;
 345
 346        temp = pm3386_port_reg_read(port, 0x303, 0x100);
 347        temp &= 0xbfff;
 348        pm3386_port_reg_write(port, 0x303, 0x100, temp);
 349}
 350
 351MODULE_LICENSE("GPL");
 352