diff options
Diffstat (limited to 'drivers/net/wan/ixp4xx_hss.c')
-rw-r--r-- | drivers/net/wan/ixp4xx_hss.c | 95 |
1 files changed, 91 insertions, 4 deletions
diff --git a/drivers/net/wan/ixp4xx_hss.c b/drivers/net/wan/ixp4xx_hss.c index bb719b6114cb..c705046d8615 100644 --- a/drivers/net/wan/ixp4xx_hss.c +++ b/drivers/net/wan/ixp4xx_hss.c | |||
@@ -166,6 +166,29 @@ | |||
166 | #define CLK46X_SPEED_4096KHZ (( 16 << 22) | (280 << 12) | 1023) | 166 | #define CLK46X_SPEED_4096KHZ (( 16 << 22) | (280 << 12) | 1023) |
167 | #define CLK46X_SPEED_8192KHZ (( 8 << 22) | (280 << 12) | 2047) | 167 | #define CLK46X_SPEED_8192KHZ (( 8 << 22) | (280 << 12) | 2047) |
168 | 168 | ||
169 | /* | ||
170 | * HSS_CONFIG_CLOCK_CR register consists of 3 parts: | ||
171 | * A (10 bits), B (10 bits) and C (12 bits). | ||
172 | * IXP42x HSS clock generator operation (verified with an oscilloscope): | ||
173 | * Each clock bit takes 7.5 ns (1 / 133.xx MHz). | ||
174 | * The clock sequence consists of (C - B) states of 0s and 1s, each state is | ||
175 | * A bits wide. It's followed by (B + 1) states of 0s and 1s, each state is | ||
176 | * (A + 1) bits wide. | ||
177 | * | ||
178 | * The resulting average clock frequency (assuming 33.333 MHz oscillator) is: | ||
179 | * freq = 66.666 MHz / (A + (B + 1) / (C + 1)) | ||
180 | * minumum freq = 66.666 MHz / (A + 1) | ||
181 | * maximum freq = 66.666 MHz / A | ||
182 | * | ||
183 | * Example: A = 2, B = 2, C = 7, CLOCK_CR register = 2 << 22 | 2 << 12 | 7 | ||
184 | * freq = 66.666 MHz / (2 + (2 + 1) / (7 + 1)) = 28.07 MHz (Mb/s). | ||
185 | * The clock sequence is: 1100110011 (5 doubles) 000111000 (3 triples). | ||
186 | * The sequence takes (C - B) * A + (B + 1) * (A + 1) = 5 * 2 + 3 * 3 bits | ||
187 | * = 19 bits (each 7.5 ns long) = 142.5 ns (then the sequence repeats). | ||
188 | * The sequence consists of 4 complete clock periods, thus the average | ||
189 | * frequency (= clock rate) is 4 / 142.5 ns = 28.07 MHz (Mb/s). | ||
190 | * (max specified clock rate for IXP42x HSS is 8.192 Mb/s). | ||
191 | */ | ||
169 | 192 | ||
170 | /* hss_config, LUT entries */ | 193 | /* hss_config, LUT entries */ |
171 | #define TDMMAP_UNASSIGNED 0 | 194 | #define TDMMAP_UNASSIGNED 0 |
@@ -239,6 +262,7 @@ struct port { | |||
239 | unsigned int clock_type, clock_rate, loopback; | 262 | unsigned int clock_type, clock_rate, loopback; |
240 | unsigned int initialized, carrier; | 263 | unsigned int initialized, carrier; |
241 | u8 hdlc_cfg; | 264 | u8 hdlc_cfg; |
265 | u32 clock_reg; | ||
242 | }; | 266 | }; |
243 | 267 | ||
244 | /* NPE message structure */ | 268 | /* NPE message structure */ |
@@ -393,7 +417,7 @@ static void hss_config(struct port *port) | |||
393 | msg.cmd = PORT_CONFIG_WRITE; | 417 | msg.cmd = PORT_CONFIG_WRITE; |
394 | msg.hss_port = port->id; | 418 | msg.hss_port = port->id; |
395 | msg.index = HSS_CONFIG_CLOCK_CR; | 419 | msg.index = HSS_CONFIG_CLOCK_CR; |
396 | msg.data32 = CLK42X_SPEED_2048KHZ /* FIXME */; | 420 | msg.data32 = port->clock_reg; |
397 | hss_npe_send(port, &msg, "HSS_SET_CLOCK_CR"); | 421 | hss_npe_send(port, &msg, "HSS_SET_CLOCK_CR"); |
398 | 422 | ||
399 | memset(&msg, 0, sizeof(msg)); | 423 | memset(&msg, 0, sizeof(msg)); |
@@ -1160,6 +1184,62 @@ static int hss_hdlc_attach(struct net_device *dev, unsigned short encoding, | |||
1160 | } | 1184 | } |
1161 | } | 1185 | } |
1162 | 1186 | ||
1187 | static u32 check_clock(u32 rate, u32 a, u32 b, u32 c, | ||
1188 | u32 *best, u32 *best_diff, u32 *reg) | ||
1189 | { | ||
1190 | /* a is 10-bit, b is 10-bit, c is 12-bit */ | ||
1191 | u64 new_rate; | ||
1192 | u32 new_diff; | ||
1193 | |||
1194 | new_rate = ixp4xx_timer_freq * (u64)(c + 1); | ||
1195 | do_div(new_rate, a * (c + 1) + b + 1); | ||
1196 | new_diff = abs((u32)new_rate - rate); | ||
1197 | |||
1198 | if (new_diff < *best_diff) { | ||
1199 | *best = new_rate; | ||
1200 | *best_diff = new_diff; | ||
1201 | *reg = (a << 22) | (b << 12) | c; | ||
1202 | } | ||
1203 | return new_diff; | ||
1204 | } | ||
1205 | |||
1206 | static void find_best_clock(u32 rate, u32 *best, u32 *reg) | ||
1207 | { | ||
1208 | u32 a, b, diff = 0xFFFFFFFF; | ||
1209 | |||
1210 | a = ixp4xx_timer_freq / rate; | ||
1211 | |||
1212 | if (a > 0x3FF) { /* 10-bit value - we can go as slow as ca. 65 kb/s */ | ||
1213 | check_clock(rate, 0x3FF, 1, 1, best, &diff, reg); | ||
1214 | return; | ||
1215 | } | ||
1216 | if (a == 0) { /* > 66.666 MHz */ | ||
1217 | a = 1; /* minimum divider is 1 (a = 0, b = 1, c = 1) */ | ||
1218 | rate = ixp4xx_timer_freq; | ||
1219 | } | ||
1220 | |||
1221 | if (rate * a == ixp4xx_timer_freq) { /* don't divide by 0 later */ | ||
1222 | check_clock(rate, a - 1, 1, 1, best, &diff, reg); | ||
1223 | return; | ||
1224 | } | ||
1225 | |||
1226 | for (b = 0; b < 0x400; b++) { | ||
1227 | u64 c = (b + 1) * (u64)rate; | ||
1228 | do_div(c, ixp4xx_timer_freq - rate * a); | ||
1229 | c--; | ||
1230 | if (c >= 0xFFF) { /* 12-bit - no need to check more 'b's */ | ||
1231 | if (b == 0 && /* also try a bit higher rate */ | ||
1232 | !check_clock(rate, a - 1, 1, 1, best, &diff, reg)) | ||
1233 | return; | ||
1234 | check_clock(rate, a, b, 0xFFF, best, &diff, reg); | ||
1235 | return; | ||
1236 | } | ||
1237 | if (!check_clock(rate, a, b, c, best, &diff, reg)) | ||
1238 | return; | ||
1239 | if (!check_clock(rate, a, b, c + 1, best, &diff, reg)) | ||
1240 | return; | ||
1241 | } | ||
1242 | } | ||
1163 | 1243 | ||
1164 | static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | 1244 | static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) |
1165 | { | 1245 | { |
@@ -1182,7 +1262,7 @@ static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1182 | } | 1262 | } |
1183 | memset(&new_line, 0, sizeof(new_line)); | 1263 | memset(&new_line, 0, sizeof(new_line)); |
1184 | new_line.clock_type = port->clock_type; | 1264 | new_line.clock_type = port->clock_type; |
1185 | new_line.clock_rate = 2048000; /* FIXME */ | 1265 | new_line.clock_rate = port->clock_rate; |
1186 | new_line.loopback = port->loopback; | 1266 | new_line.loopback = port->loopback; |
1187 | if (copy_to_user(line, &new_line, size)) | 1267 | if (copy_to_user(line, &new_line, size)) |
1188 | return -EFAULT; | 1268 | return -EFAULT; |
@@ -1206,7 +1286,13 @@ static int hss_hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) | |||
1206 | return -EINVAL; | 1286 | return -EINVAL; |
1207 | 1287 | ||
1208 | port->clock_type = clk; /* Update settings */ | 1288 | port->clock_type = clk; /* Update settings */ |
1209 | /* FIXME port->clock_rate = new_line.clock_rate */; | 1289 | if (clk == CLOCK_INT) |
1290 | find_best_clock(new_line.clock_rate, &port->clock_rate, | ||
1291 | &port->clock_reg); | ||
1292 | else { | ||
1293 | port->clock_rate = 0; | ||
1294 | port->clock_reg = CLK42X_SPEED_2048KHZ; | ||
1295 | } | ||
1210 | port->loopback = new_line.loopback; | 1296 | port->loopback = new_line.loopback; |
1211 | 1297 | ||
1212 | spin_lock_irqsave(&npe_lock, flags); | 1298 | spin_lock_irqsave(&npe_lock, flags); |
@@ -1266,7 +1352,8 @@ static int __devinit hss_init_one(struct platform_device *pdev) | |||
1266 | dev->netdev_ops = &hss_hdlc_ops; | 1352 | dev->netdev_ops = &hss_hdlc_ops; |
1267 | dev->tx_queue_len = 100; | 1353 | dev->tx_queue_len = 100; |
1268 | port->clock_type = CLOCK_EXT; | 1354 | port->clock_type = CLOCK_EXT; |
1269 | port->clock_rate = 2048000; | 1355 | port->clock_rate = 0; |
1356 | port->clock_reg = CLK42X_SPEED_2048KHZ; | ||
1270 | port->id = pdev->id; | 1357 | port->id = pdev->id; |
1271 | port->dev = &pdev->dev; | 1358 | port->dev = &pdev->dev; |
1272 | port->plat = pdev->dev.platform_data; | 1359 | port->plat = pdev->dev.platform_data; |