1 /* $NetBSD: pcn.c,v 1.15 2009/01/12 09:41:59 tsutsui Exp $ */
4 * Copyright (c) 2007 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/param.h>
34 #include <netinet/in.h>
35 #include <netinet/in_systm.h>
37 #include <lib/libsa/stand.h>
38 #include <lib/libsa/net.h>
43 * - reverse endian access every CSR.
44 * - no vtophys() translation, vaddr_t == paddr_t.
45 * - PIPT writeback cache aware.
47 #define CSR_READ_4(l, r) in32rb((l)->csr+(r))
48 #define CSR_WRITE_4(l, r, v) out32rb((l)->csr+(r), (v))
49 #define CSR_READ_2(l, r) in16rb((l)->csr+(r))
50 #define CSR_WRITE_2(l, r, v) out16rb((l)->csr+(r), (v))
51 #define VTOPHYS(va) (uint32_t)(va)
52 #define DEVTOV(pa) (uint32_t)(pa)
53 #define wbinv(adr, siz) _wbinv(VTOPHYS(adr), (uint32_t)(siz))
54 #define inv(adr, siz) _inv(VTOPHYS(adr), (uint32_t)(siz))
55 #define DELAY(n) delay(n)
56 #define ALLOC(T,A) (T *)((unsigned)alloc(sizeof(T) + (A)) &~ ((A) - 1))
59 uint32_t xd0
, xd1
, xd2
;
62 #define T1_OWN (1U << 31) /* 1: empty for HW to load anew */
63 #define T1_STP (1U << 25) /* first frame segment */
64 #define T1_ENP (1U << 24) /* last frame segment */
65 #define T1_ONES 0xf000 /* filler */
66 #define T1_FLMASK 0x0fff /* Tx segment length */
67 #define R1_OWN (1U << 31) /* 1: loaded for HW to send */
68 #define R1_ERR (1U << 30) /* Rx error summary */
69 #define R1_ONES 0xf000 /* filler */
70 #define R1_FLMASK 0x0fff /* Rx frame length */
74 #define PCN_16RESET 0x14
75 #define PCN_32RESET 0x18
78 #define C0_IDON (1U << 8) /* initblk done indication */
79 #define C0_TXON (1U << 5)
80 #define C0_RXON (1U << 4)
81 #define C0_TDMD (1U << 3) /* immediate Tx descriptor poll */
82 #define C0_STOP (1U << 2) /* reset with abrupt abort */
83 #define C0_STRT (1U << 1) /* activate whole Tx/Rx DMA */
84 #define C0_INIT (1U << 0) /* instruct to process initblk */
88 #define C3_MISSM (1U << 12)
89 #define C3_IDONM (1U << 8)
90 #define C3_DXSUFLO (1U << 6)
92 #define C4_DMAPLUS (1U << 14)
93 #define C4_TXDPOLL (1U << 12) /* _disable_ Tx descriptor polling */
94 #define C4_APAD_XMT (1U << 11)
95 #define C4_MFCOM (1U << 8)
96 #define C4_RCVCCOM (1U << 4)
97 #define C4_TXSTRTM (1U << 6)
99 #define PCN_CSR12 0x0c
100 #define PCN_CSR13 0x0d
101 #define PCN_CSR14 0x0e
102 #define PCN_CSR58 0x4a /* mapped to BCR20 */
103 #define PCN_BCR20 0x14 /* "software style" */
104 #define PCN_BCR33 0x21
105 #define PCN_BCR34 0x22
109 uint32_t init_padr
[2];
110 uint16_t init_ladrf
[4];
116 #define FRAMESIZE 1536
121 uint8_t rxstore
[2][FRAMESIZE
];
123 unsigned phy
, bmsr
, anlpar
;
126 unsigned pcn_mii_read(struct local
*, int, int);
127 void pcn_mii_write(struct local
*, int, int, int);
128 static unsigned pcn_csr_read(struct local
*, int);
129 static void pcn_csr_write(struct local
*, int, int);
130 static unsigned pcn_bcr_read(struct local
*, int);
131 static void pcn_bcr_write(struct local
*, int, int);
132 static void mii_initphy(struct local
*l
);
135 pcn_match(unsigned tag
, void *data
)
139 v
= pcicfgread(tag
, PCI_ID_REG
);
140 return (v
== PCI_DEVICE(0x1022, 0x2000));
144 pcn_init(unsigned tag
, void *data
)
146 unsigned val
, fdx
, loop
;
148 struct desc
*txd
, *rxd
;
150 struct pcninit initblock
, *ib
;
152 l
= ALLOC(struct local
, sizeof(struct desc
)); /* desc alignment */
153 memset(l
, 0, sizeof(struct local
));
154 l
->csr
= DEVTOV(pcicfgread(tag
, 0x14)); /* use mem space */
156 (void)CSR_READ_2(l
, PCN_16RESET
);
157 (void)CSR_READ_4(l
, PCN_32RESET
);
158 DELAY(1000); /* 1 milli second */
159 /* go 32bit RW mode */
160 CSR_WRITE_4(l
, PCN_RDP
, 0);
161 /* use 32bit software structure design "2" */
162 pcn_bcr_write(l
, PCN_BCR20
, 2);
167 val
= pcn_csr_read(l
, PCN_CSR12
); en
[0] = val
; en
[1] = (val
>> 8);
168 val
= pcn_csr_read(l
, PCN_CSR13
); en
[2] = val
; en
[3] = (val
>> 8);
169 val
= pcn_csr_read(l
, PCN_CSR14
); en
[4] = val
; en
[5] = (val
>> 8);
171 printf("MAC address %02x:%02x:%02x:%02x:%02x:%02x\n",
172 en
[0], en
[1], en
[2], en
[3], en
[4], en
[5]);
174 /* speed and duplexity are found in MII ANR24 */
175 val
= pcn_mii_read(l
, l
->phy
, 24);
176 fdx
= !!(val
& (1U << 2));
177 printf("%s", (val
& (1U << 0)) ? "100Mbps" : "10Mbps");
184 rxd
[0].xd0
= htole32(VTOPHYS(l
->rxstore
[0]));
185 rxd
[0].xd1
= htole32(R1_OWN
| R1_ONES
| FRAMESIZE
);
186 rxd
[1].xd0
= htole32(VTOPHYS(l
->rxstore
[1]));
187 rxd
[1].xd1
= htole32(R1_OWN
| R1_ONES
| FRAMESIZE
);
191 ib
->init_mode
= htole32((0 << 28) | (1 << 20) | 0);
193 htole32(en
[0] | (en
[1] << 8) | (en
[2] << 16) | (en
[3] << 24));
195 htole32(en
[4] | (en
[5] << 8));
196 ib
->init_rdra
= htole32(VTOPHYS(rxd
));
197 ib
->init_tdra
= htole32(VTOPHYS(txd
));
199 pcn_csr_write(l
, PCN_CSR3
, C3_MISSM
|C3_IDONM
|C3_DXSUFLO
);
200 pcn_csr_write(l
, PCN_CSR4
, C4_DMAPLUS
|C4_APAD_XMT
|
201 C4_MFCOM
|C4_RCVCCOM
|C4_TXSTRTM
);
202 pcn_csr_write(l
, PCN_CSR5
, 0);
204 wbinv(&initblock
, sizeof(initblock
));
205 pcn_csr_write(l
, PCN_CSR1
, VTOPHYS(&initblock
) & 0xffff);
206 pcn_csr_write(l
, PCN_CSR2
, (VTOPHYS(&initblock
) >> 16) & 0xffff);
207 pcn_csr_write(l
, PCN_CSR0
, C0_INIT
);
211 } while (--loop
> 0 && !(pcn_csr_read(l
, PCN_CSR0
) & C0_IDON
));
213 printf("pcn: timeout processing init block\n");
215 pcn_csr_write(l
, PCN_CSR0
, C0_STRT
);
221 pcn_send(void *dev
, char *buf
, unsigned len
)
223 struct local
*l
= dev
;
224 volatile struct desc
*txd
;
229 tlen
= (-len
) & T1_FLMASK
; /* two's complement */
231 txd
->xd0
= htole32(VTOPHYS(buf
));
232 txd
->xd1
= htole32(T1_OWN
| T1_STP
| T1_ENP
| T1_ONES
| tlen
);
233 wbinv(txd
, sizeof(struct desc
));
234 /* pcn_csr_write(l, PCN_CSR0, C0_TDMD); */
237 if ((le32toh(txd
->xd1
) & T1_OWN
) == 0)
240 inv(txd
, sizeof(struct desc
));
241 } while (--loop
> 0);
242 printf("xmit failed\n");
249 pcn_recv(void *dev
, char *buf
, unsigned maxlen
, unsigned timo
)
251 struct local
*l
= dev
;
252 volatile struct desc
*rxd
;
253 unsigned bound
, rxstat
, len
;
257 printf("recving with %u sec. timeout\n", timo
);
259 rxd
= &l
->rxd
[l
->rx
];
261 inv(rxd
, sizeof(struct desc
));
262 rxstat
= le32toh(rxd
->xd1
);
263 if ((rxstat
& R1_OWN
) == 0)
265 DELAY(1000); /* 1 milli second */
266 } while (--bound
> 0);
270 if (rxstat
& R1_ERR
) {
271 rxd
->xd1
|= htole32(R1_OWN
);
273 wbinv(rxd
, sizeof(struct desc
));
278 len
= (rxstat
& R1_FLMASK
) - 4 /* HASFCS */;
281 ptr
= l
->rxstore
[l
->rx
];
283 memcpy(buf
, ptr
, len
);
284 rxd
->xd1
|= htole32(R1_OWN
);
286 wbinv(rxd
, sizeof(struct desc
));
291 #define MREG(v) ((v)<< 0)
292 #define MPHY(v) ((v)<< 5)
296 pcn_mii_read(struct local
*l
, int phy
, int reg
)
298 pcn_bcr_write(l
, PCN_BCR33
, MREG(reg
) | MPHY(phy
));
299 return (pcn_bcr_read(l
, PCN_BCR34
) & MIIMD
);
303 pcn_mii_write(struct local
*l
, int phy
, int reg
, int val
)
305 pcn_bcr_write(l
, PCN_BCR33
, MREG(reg
) | MPHY(phy
));
306 pcn_bcr_write(l
, PCN_BCR34
, val
);
310 pcn_csr_read(struct local
*l
, int r
)
312 CSR_WRITE_4(l
, PCN_RAP
, r
);
313 return CSR_READ_4(l
, PCN_RDP
);
317 pcn_csr_write(struct local
*l
, int r
, int v
)
319 CSR_WRITE_4(l
, PCN_RAP
, r
);
320 CSR_WRITE_4(l
, PCN_RDP
, v
);
324 pcn_bcr_read(struct local
*l
, int r
)
326 CSR_WRITE_4(l
, PCN_RAP
, r
);
327 return CSR_READ_4(l
, PCN_BDP
);
331 pcn_bcr_write(struct local
*l
, int r
, int v
)
333 CSR_WRITE_4(l
, PCN_RAP
, r
);
334 CSR_WRITE_4(l
, PCN_BDP
, v
);
337 #define MII_BMCR 0x00 /* Basic mode control register (rw) */
338 #define BMCR_RESET 0x8000 /* reset */
339 #define BMCR_AUTOEN 0x1000 /* autonegotiation enable */
340 #define BMCR_ISO 0x0400 /* isolate */
341 #define BMCR_STARTNEG 0x0200 /* restart autonegotiation */
342 #define MII_BMSR 0x01 /* Basic mode status register (ro) */
345 mii_initphy(struct local
*l
)
347 int phy
, ctl
, sts
, bound
;
349 for (phy
= 0; phy
< 32; phy
++) {
350 ctl
= pcn_mii_read(l
, phy
, MII_BMCR
);
351 sts
= pcn_mii_read(l
, phy
, MII_BMSR
);
352 if (ctl
!= 0xffff && sts
!= 0xffff)
355 printf("MII: no PHY found\n");
358 ctl
= pcn_mii_read(l
, phy
, MII_BMCR
);
359 pcn_mii_write(l
, phy
, MII_BMCR
, ctl
| BMCR_RESET
);
363 ctl
= pcn_mii_read(l
, phy
, MII_BMCR
);
365 printf("MII: PHY %d has died after reset\n", phy
);
368 } while (bound
-- > 0 && (ctl
& BMCR_RESET
));
370 printf("PHY %d reset failed\n", phy
);
373 pcn_mii_write(l
, phy
, MII_BMCR
, ctl
);
374 sts
= pcn_mii_read(l
, phy
, MII_BMSR
) |
375 pcn_mii_read(l
, phy
, MII_BMSR
); /* read twice */
382 mii_dealan(struct local
*, unsigned timo
)
384 unsigned anar
, bound
;
386 anar
= ANAR_TX_FD
| ANAR_TX
| ANAR_10_FD
| ANAR_10
| ANAR_CSMA
;
387 pcn_mii_write(l
, l
->phy
, MII_ANAR
, anar
);
388 pcn_mii_write(l
, l
->phy
, MII_BMCR
, BMCR_AUTOEN
| BMCR_STARTNEG
);
390 bound
= getsecs() + timo
;
392 l
->bmsr
= pcn_mii_read(l
, l
->phy
, MII_BMSR
) |
393 pcn_mii_read(l
, l
->phy
, MII_BMSR
); /* read twice */
394 if ((l
->bmsr
& BMSR_LINK
) && (l
->bmsr
& BMSR_ACOMP
)) {
395 l
->anlpar
= pcn_mii_read(l
, l
->phy
, MII_ANLPAR
);
399 } while (getsecs() < bound
);