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 <asm/hardware.h> 17#include <asm/arch/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 size_bits = mp->rx_mode & IXP2400_RX_MODE_RBUF_SIZE_MASK; 99 if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_64) { 100 for (i = 1; i < 128; i++) { 101 if (i == 9 || i == 18 || i == 27) 102 continue; 103 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i); 104 } 105 } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_128) { 106 for (i = 1; i < 64; i++) { 107 if (i == 4 || i == 9 || i == 13) 108 continue; 109 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i); 110 } 111 } else if (size_bits == IXP2400_RX_MODE_RBUF_SIZE_256) { 112 for (i = 1; i < 32; i++) { 113 if (i == 2 || i == 4 || i == 6) 114 continue; 115 ixp2000_reg_write(IXP2000_MSF_RBUF_ELEMENT_DONE, i); 116 } 117 } 118} 119 120static u32 ixp2400_msf_valid_channels(u32 reg) 121{ 122 u32 channels; 123 124 channels = 0; 125 switch (reg & IXP2400_RX_MODE_WIDTH_MASK) { 126 case IXP2400_RX_MODE_1x32: 127 channels = 0x1; 128 if (reg & IXP2400_RX_MODE_MPHY && 129 !(reg & IXP2400_RX_MODE_MPHY_32)) 130 channels = 0xf; 131 break; 132 133 case IXP2400_RX_MODE_2x16: 134 channels = 0x5; 135 break; 136 137 case IXP2400_RX_MODE_4x8: 138 channels = 0xf; 139 break; 140 141 case IXP2400_RX_MODE_1x16_2x8: 142 channels = 0xd; 143 break; 144 } 145 146 return channels; 147} 148 149static void ixp2400_msf_enable_rx(struct ixp2400_msf_parameters *mp) 150{ 151 u32 value; 152 153 value = ixp2000_reg_read(IXP2000_MSF_RX_CONTROL) & 0x0fffffff; 154 value |= ixp2400_msf_valid_channels(mp->rx_mode) << 28; 155 ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, value); 156} 157 158static void ixp2400_msf_enable_tx(struct ixp2400_msf_parameters *mp) 159{ 160 u32 value; 161 162 value = ixp2000_reg_read(IXP2000_MSF_TX_CONTROL) & 0x0fffffff; 163 value |= ixp2400_msf_valid_channels(mp->tx_mode) << 28; 164 ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, value); 165} 166 167 168void ixp2400_msf_init(struct ixp2400_msf_parameters *mp) 169{ 170 u32 value; 171 int i; 172 173 /* 174 * Init the RX/TX PLLs based on the passed parameter block. 175 */ 176 ixp2400_pll_init(mp); 177 178 /* 179 * Reset MSF. Bit 7 in IXP_RESET_0 resets the MSF. 180 */ 181 value = ixp2000_reg_read(IXP2000_RESET0); 182 ixp2000_reg_write(IXP2000_RESET0, value | 0x80); 183 ixp2000_reg_write(IXP2000_RESET0, value & ~0x80); 184 185 /* 186 * Initialise the RX section. 187 */ 188 ixp2000_reg_write(IXP2000_MSF_RX_MPHY_POLL_LIMIT, mp->rx_poll_ports - 1); 189 ixp2000_reg_write(IXP2000_MSF_RX_CONTROL, mp->rx_mode); 190 for (i = 0; i < 4; i++) { 191 ixp2000_reg_write(IXP2000_MSF_RX_UP_CONTROL_0 + i, 192 mp->rx_channel_mode[i]); 193 } 194 ixp2400_msf_free_rbuf_entries(mp); 195 ixp2400_msf_enable_rx(mp); 196 197 /* 198 * Initialise the TX section. 199 */ 200 ixp2000_reg_write(IXP2000_MSF_TX_MPHY_POLL_LIMIT, mp->tx_poll_ports - 1); 201 ixp2000_reg_write(IXP2000_MSF_TX_CONTROL, mp->tx_mode); 202 for (i = 0; i < 4; i++) { 203 ixp2000_reg_write(IXP2000_MSF_TX_UP_CONTROL_0 + i, 204 mp->tx_channel_mode[i]); 205 } 206 ixp2400_msf_enable_tx(mp); 207} 208