1/* 2 * Generic library functions for the MSF (Media and Switch Fabric) unit 3 * found on the Intel IXP2400 network processor. 4 * 5 * Copyright (C) 2004, 2005 Lennert Buytenhek <buytenh@wantstofly.org> 6 * Dedicated to Marija Kulikova. 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as 10 * published by the Free Software Foundation; either version 2.1 of the 11 * License, or (at your option) any later version. 12 */ 13 14#include <linux/kernel.h> 15#include <linux/init.h> 16#include <mach/hardware.h> 17#include <mach/ixp2000-regs.h> 18#include <asm/delay.h> 19#include <asm/io.h> 20#include "ixp2400-msf.h" 21 22/* 23 * This is the Intel recommended PLL init procedure as described on 24 * page 340 of the IXP2400/IXP2800 Programmer's Reference Manual. 25 */ 26static void ixp2400_pll_init(struct ixp2400_msf_parameters *mp) 27{ 28 int rx_dual_clock; 29 int tx_dual_clock; 30 u32 value; 31 32 /* 33 * If the RX mode is not 1x32, we have to enable both RX PLLs 34 * (#0 and #1.) The same thing for the TX direction. 35 */ 36 rx_dual_clock = !!(mp->rx_mode & IXP2400_RX_MODE_WIDTH_MASK); 37 tx_dual_clock = !!(mp->tx_mode & IXP2400_TX_MODE_WIDTH_MASK); 38 39 /* 40 * Read initial value. 41 */ 42 value = ixp2000_reg_read(IXP2000_MSF_CLK_CNTRL); 43 44 /* 45 * Put PLLs in powerdown and bypass mode. 46 */ 47 value |= 0x0000f0f0; 48 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value); 49 50 /* 51 * Set single or dual clock mode bits. 52 */ 53 value &= ~0x03000000; 54 value |= (rx_dual_clock << 24) | (tx_dual_clock << 25); 55 56 /* 57 * Set multipliers. 58 */ 59 value &= ~0x00ff0000; 60 value |= mp->rxclk01_multiplier << 16; 61 value |= mp->rxclk23_multiplier << 18; 62 value |= mp->txclk01_multiplier << 20; 63 value |= mp->txclk23_multiplier << 22; 64 65 /* 66 * And write value. 67 */ 68 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value); 69 70 /* 71 * Disable PLL bypass mode. 72 */ 73 value &= ~(0x00005000 | rx_dual_clock << 13 | tx_dual_clock << 15); 74 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value); 75 76 /* 77 * Turn on PLLs. 78 */ 79 value &= ~(0x00000050 | rx_dual_clock << 5 | tx_dual_clock << 7); 80 ixp2000_reg_write(IXP2000_MSF_CLK_CNTRL, value); 81 82 /* 83 * Wait for PLLs to lock. There are lock status bits, but IXP2400 84 * erratum #65 says that these lock bits should not be relied upon 85 * as they might not accurately reflect the true state of the PLLs. 86 */ 87 udelay(100); 88} 89 90/* 91 * Needed according to p480 of Programmer's Reference Manual. 92 */ 93static void ixp2400_msf_free_rbuf_entries(struct ixp2400_msf_parameters *mp) 94{ 95 int size_bits; 96 int i; 97 98 /* 99 * Work around IXP2400 erratum #69 (silent RBUF-to-DRAM transfer 100 * corruption) in the Intel-recommended way: do not add the RBUF 101 * elements susceptible to corruption to the freelist. 102 */ 103 size_bits = mp->rx_mode & IXP2400_RX_MODE_RBUF_SIZE_MASK; 104 if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_64) { 105 for (i = 1; i < 128; i++) { 106 if (i == 9 || i == 18 || i == 27) 107 continue; 108 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i); 109 } 110 } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_128) { 111 for (i = 1; i < 64; i++) { 112 if (i == 4 || i == 9 || i == 13) 113 continue; 114 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i); 115 } 116 } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_256) { 117 for (i = 1; i < 32; i++) { 118 if (i == 2 || i == 4 || i == 6) 119 continue; 120 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i); 121 } 122 } 123} 124 125static u32 ixp2400_msf_valid_channels(u32 reg) 126{ 127 u32 channels; 128 129 channels = 0; 130 switch (reg & IXP2400_RX_MODE_WIDTH_MASK) { 131 case IXP2400_RX_MODE_1x32: 132 channels = 0x1; 133 if (reg & IXP2400_RX_MODE_MPHY && 134 !(reg & IXP2400_RX_MODE_MPHY_32)) 135 channels = 0xf; 136 break; 137 138 case IXP2400_RX_MODE_2x16: 139 channels = 0x5; 140 break; 141 142 case IXP2400_RX_MODE_4x8: 143 channels = 0xf; 144 break; 145 146 case IXP2400_RX_MODE_1x16_2x8: 147 channels = 0xd; 148 break; 149 } 150 151 return channels; 152} 153 154static void ixp2400_msf_enable_rx(struct ixp2400_msf_parameters *mp) 155{ 156 u32 value; 157 158 value = ixp2000_reg_read(IXP2000_MSF_RX_CONTROL) & 0x0fffffff; 159 value |= ixp2400_msf_valid_channels(mp->rx_mode) << 28; 160 ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, value); 161} 162 163static void ixp2400_msf_enable_tx(struct ixp2400_msf_parameters *mp) 164{ 165 u32 value; 166 167 value = ixp2000_reg_read(IXP2000_MSF_TX_CONTROL) & 0x0fffffff; 168 value |= ixp2400_msf_valid_channels(mp->tx_mode) << 28; 169 ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, value); 170} 171 172 173void ixp2400_msf_init(struct ixp2400_msf_parameters *mp) 174{ 175 u32 value; 176 int i; 177 178 /* 179 * Init the RX/TX PLLs based on the passed parameter block. 180 */ 181 ixp2400_pll_init(mp); 182 183 /* 184 * Reset MSF. Bit 7 in IXP_RESET_0 resets the MSF. 185 */ 186 value = ixp2000_reg_read(IXP2000_RESET0); 187 ixp2000_reg_write(IXP2000_RESET0, value | 0x80); 188 ixp2000_reg_write(IXP2000_RESET0, value & ~0x80); 189 190 /* 191 * Initialise the RX section. 192 */ 193 ixp2000_reg_write(IXP2000_MSF_RX_MPHY_POLL_LIMIT, mp->rx_poll_ports - 1); 194 ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, mp->rx_mode); 195 for (i = 0; i < 4; i++) { 196 ixp2000_reg_write(IXP2000_MSF_RX_UP_CONTROL_0 + i, 197 mp->rx_channel_mode[i]); 198 } 199 ixp2400_msf_free_rbuf_entries(mp); 200 ixp2400_msf_enable_rx(mp); 201 202 /* 203 * Initialise the TX section. 204 */ 205 ixp2000_reg_write(IXP2000_MSF_TX_MPHY_POLL_LIMIT, mp->tx_poll_ports - 1); 206 ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, mp->tx_mode); 207 for (i = 0; i < 4; i++) { 208 ixp2000_reg_write(IXP2000_MSF_TX_UP_CONTROL_0 + i, 209 mp->tx_channel_mode[i]); 210 } 211 ixp2400_msf_enable_tx(mp); 212} 213