xref: /openbsd-src/sys/arch/armv7/omap/if_cpsw.c (revision cf96265bb25b08cea5a0b5654925448396fd9df0)
1*cf96265bSbluhm /* $OpenBSD: if_cpsw.c,v 1.53 2023/11/10 15:51:19 bluhm Exp $ */
28eda2d14Spatrick /*	$NetBSD: if_cpsw.c,v 1.3 2013/04/17 14:36:34 bouyer Exp $	*/
38eda2d14Spatrick 
48eda2d14Spatrick /*
58eda2d14Spatrick  * Copyright (c) 2013 Jonathan A. Kollasch
68eda2d14Spatrick  * All rights reserved.
78eda2d14Spatrick  *
88eda2d14Spatrick  * Redistribution and use in source and binary forms, with or without
98eda2d14Spatrick  * modification, are permitted provided that the following conditions
108eda2d14Spatrick  * are met:
118eda2d14Spatrick  * 1. Redistributions of source code must retain the above copyright
128eda2d14Spatrick  *    notice, this list of conditions and the following disclaimer.
138eda2d14Spatrick  * 2. Redistributions in binary form must reproduce the above copyright
148eda2d14Spatrick  *    notice, this list of conditions and the following disclaimer in the
158eda2d14Spatrick  *    documentation and/or other materials provided with the distribution.
168eda2d14Spatrick  *
178eda2d14Spatrick  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
188eda2d14Spatrick  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
198eda2d14Spatrick  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
208eda2d14Spatrick  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
218eda2d14Spatrick  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
228eda2d14Spatrick  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
238eda2d14Spatrick  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
248eda2d14Spatrick  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
258eda2d14Spatrick  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
268eda2d14Spatrick  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
278eda2d14Spatrick  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
288eda2d14Spatrick  */
298eda2d14Spatrick 
308eda2d14Spatrick /*-
318eda2d14Spatrick  * Copyright (c) 2012 Damjan Marion <dmarion@Freebsd.org>
328eda2d14Spatrick  * All rights reserved.
338eda2d14Spatrick  *
348eda2d14Spatrick  * Redistribution and use in source and binary forms, with or without
358eda2d14Spatrick  * modification, are permitted provided that the following conditions
368eda2d14Spatrick  * are met:
378eda2d14Spatrick  * 1. Redistributions of source code must retain the above copyright
388eda2d14Spatrick  *    notice, this list of conditions and the following disclaimer.
398eda2d14Spatrick  * 2. Redistributions in binary form must reproduce the above copyright
408eda2d14Spatrick  *    notice, this list of conditions and the following disclaimer in the
418eda2d14Spatrick  *    documentation and/or other materials provided with the distribution.
428eda2d14Spatrick  *
438eda2d14Spatrick  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
448eda2d14Spatrick  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
458eda2d14Spatrick  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
468eda2d14Spatrick  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
478eda2d14Spatrick  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
488eda2d14Spatrick  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
498eda2d14Spatrick  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
508eda2d14Spatrick  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
518eda2d14Spatrick  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
528eda2d14Spatrick  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
538eda2d14Spatrick  * SUCH DAMAGE.
548eda2d14Spatrick  */
558eda2d14Spatrick 
568eda2d14Spatrick #include "bpfilter.h"
578eda2d14Spatrick 
588eda2d14Spatrick #include <sys/param.h>
598eda2d14Spatrick #include <sys/systm.h>
608eda2d14Spatrick #include <sys/sockio.h>
618eda2d14Spatrick #include <sys/mbuf.h>
62f3fcf042Smiod #include <sys/pool.h>
638eda2d14Spatrick #include <sys/queue.h>
648eda2d14Spatrick #include <sys/device.h>
658eda2d14Spatrick #include <sys/timeout.h>
668eda2d14Spatrick 
678eda2d14Spatrick #include <machine/bus.h>
68a2592955Sjsg #include <machine/fdt.h>
698eda2d14Spatrick 
708eda2d14Spatrick #include <net/if.h>
718eda2d14Spatrick #include <net/if_media.h>
728eda2d14Spatrick 
738eda2d14Spatrick #include <netinet/in.h>
748eda2d14Spatrick #include <netinet/if_ether.h>
758eda2d14Spatrick 
768eda2d14Spatrick #if NBPFILTER > 0
778eda2d14Spatrick #include <net/bpf.h>
788eda2d14Spatrick #endif
798eda2d14Spatrick 
808eda2d14Spatrick #include <dev/mii/miivar.h>
818eda2d14Spatrick 
828eda2d14Spatrick #include <arch/armv7/omap/if_cpswreg.h>
838eda2d14Spatrick 
84a2592955Sjsg #include <dev/ofw/openfirm.h>
85ae1367d5Skettenis #include <dev/ofw/ofw_clock.h>
8616f6b372Sjsg #include <dev/ofw/ofw_pinctrl.h>
87ac7c670eSpatrick #include <dev/ofw/fdt.h>
88a2592955Sjsg 
8999d034d9Skettenis #include <uvm/uvm_extern.h>
9099d034d9Skettenis 
918eda2d14Spatrick #define CPSW_TXFRAGS	16
928eda2d14Spatrick 
938eda2d14Spatrick #define OMAP2SCM_MAC_ID0_LO	0x630
948eda2d14Spatrick #define OMAP2SCM_MAC_ID0_HI	0x634
958eda2d14Spatrick 
968eda2d14Spatrick #define CPSW_CPPI_RAM_SIZE (0x2000)
978eda2d14Spatrick #define CPSW_CPPI_RAM_TXDESCS_SIZE (CPSW_CPPI_RAM_SIZE/2)
988eda2d14Spatrick #define CPSW_CPPI_RAM_RXDESCS_SIZE \
998eda2d14Spatrick     (CPSW_CPPI_RAM_SIZE - CPSW_CPPI_RAM_TXDESCS_SIZE)
1008eda2d14Spatrick #define CPSW_CPPI_RAM_TXDESCS_BASE (CPSW_CPPI_RAM_OFFSET + 0x0000)
1018eda2d14Spatrick #define CPSW_CPPI_RAM_RXDESCS_BASE \
1028eda2d14Spatrick     (CPSW_CPPI_RAM_OFFSET + CPSW_CPPI_RAM_TXDESCS_SIZE)
1038eda2d14Spatrick 
1048eda2d14Spatrick #define CPSW_NTXDESCS (CPSW_CPPI_RAM_TXDESCS_SIZE/sizeof(struct cpsw_cpdma_bd))
1058eda2d14Spatrick #define CPSW_NRXDESCS (CPSW_CPPI_RAM_RXDESCS_SIZE/sizeof(struct cpsw_cpdma_bd))
1068eda2d14Spatrick 
1078eda2d14Spatrick #define CPSW_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN)
1088eda2d14Spatrick 
1098eda2d14Spatrick #define TXDESC_NEXT(x) cpsw_txdesc_adjust((x), 1)
1108eda2d14Spatrick #define TXDESC_PREV(x) cpsw_txdesc_adjust((x), -1)
1118eda2d14Spatrick 
1128eda2d14Spatrick #define RXDESC_NEXT(x) cpsw_rxdesc_adjust((x), 1)
1138eda2d14Spatrick #define RXDESC_PREV(x) cpsw_rxdesc_adjust((x), -1)
1148eda2d14Spatrick 
1158eda2d14Spatrick struct cpsw_ring_data {
1168eda2d14Spatrick 	bus_dmamap_t		 tx_dm[CPSW_NTXDESCS];
1178eda2d14Spatrick 	struct mbuf		*tx_mb[CPSW_NTXDESCS];
1188eda2d14Spatrick 	bus_dmamap_t		 rx_dm[CPSW_NRXDESCS];
1198eda2d14Spatrick 	struct mbuf		*rx_mb[CPSW_NRXDESCS];
1208eda2d14Spatrick };
1218eda2d14Spatrick 
1229fa04b91Sjsg struct cpsw_port_config {
1239fa04b91Sjsg 	uint8_t			 enaddr[ETHER_ADDR_LEN];
1249fa04b91Sjsg 	int			 phy_id;
1259fa04b91Sjsg 	int			 rgmii;
1269fa04b91Sjsg 	int			 vlan;
1279fa04b91Sjsg };
1289fa04b91Sjsg 
1298eda2d14Spatrick struct cpsw_softc {
1308eda2d14Spatrick 	struct device		 sc_dev;
1318eda2d14Spatrick 	bus_space_tag_t		 sc_bst;
1328eda2d14Spatrick 	bus_space_handle_t	 sc_bsh;
1338eda2d14Spatrick 	bus_dma_tag_t		 sc_bdt;
1348eda2d14Spatrick 	bus_space_handle_t	 sc_bsh_txdescs;
1358eda2d14Spatrick 	bus_space_handle_t	 sc_bsh_rxdescs;
1368eda2d14Spatrick 	bus_addr_t		 sc_txdescs_pa;
1378eda2d14Spatrick 	bus_addr_t		 sc_rxdescs_pa;
13828122b11Sdlg 
139a484de46Sdlg 	struct arpcom		 sc_ac;
1408eda2d14Spatrick 	struct mii_data		 sc_mii;
14128122b11Sdlg 
1428eda2d14Spatrick 	struct cpsw_ring_data	*sc_rdp;
1438eda2d14Spatrick 	volatile u_int		 sc_txnext;
1448eda2d14Spatrick 	volatile u_int		 sc_txhead;
1458eda2d14Spatrick 	volatile u_int		 sc_rxhead;
14628122b11Sdlg 
1478eda2d14Spatrick 	void			*sc_rxthih;
1488eda2d14Spatrick 	void			*sc_rxih;
1498eda2d14Spatrick 	void			*sc_txih;
1508eda2d14Spatrick 	void			*sc_miscih;
15128122b11Sdlg 
1528eda2d14Spatrick 	void			*sc_txpad;
1538eda2d14Spatrick 	bus_dmamap_t		 sc_txpad_dm;
1548eda2d14Spatrick #define sc_txpad_pa sc_txpad_dm->dm_segs[0].ds_addr
15528122b11Sdlg 
1568eda2d14Spatrick 	volatile bool		 sc_txrun;
1578eda2d14Spatrick 	volatile bool		 sc_rxrun;
1588eda2d14Spatrick 	volatile bool		 sc_txeoq;
1598eda2d14Spatrick 	volatile bool		 sc_rxeoq;
1608eda2d14Spatrick 	struct timeout		 sc_tick;
16132d55a3aScanacar 	int			 sc_active_port;
1629fa04b91Sjsg 
1639fa04b91Sjsg 	struct cpsw_port_config	 sc_port_config[2];
1648eda2d14Spatrick };
1658eda2d14Spatrick 
166ce639ce1Sdlg #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
167ce639ce1Sdlg 
168a2592955Sjsg int	cpsw_match(struct device *, void *, void *);
1698eda2d14Spatrick void	cpsw_attach(struct device *, struct device *, void *);
1708eda2d14Spatrick 
1718eda2d14Spatrick void	cpsw_start(struct ifnet *);
1728eda2d14Spatrick int	cpsw_ioctl(struct ifnet *, u_long, caddr_t);
1738eda2d14Spatrick void	cpsw_watchdog(struct ifnet *);
1748eda2d14Spatrick int	cpsw_init(struct ifnet *);
1758eda2d14Spatrick void	cpsw_stop(struct ifnet *);
1768eda2d14Spatrick 
1778eda2d14Spatrick int	cpsw_mii_readreg(struct device *, int, int);
1788eda2d14Spatrick void	cpsw_mii_writereg(struct device *, int, int, int);
1798eda2d14Spatrick void	cpsw_mii_statchg(struct device *);
1808eda2d14Spatrick 
1818eda2d14Spatrick void	cpsw_tick(void *);
1828eda2d14Spatrick 
1838eda2d14Spatrick int	cpsw_new_rxbuf(struct cpsw_softc * const, const u_int);
1848eda2d14Spatrick int	cpsw_mediachange(struct ifnet *);
1858eda2d14Spatrick void	cpsw_mediastatus(struct ifnet *, struct ifmediareq *);
1868eda2d14Spatrick 
1878eda2d14Spatrick int	cpsw_rxthintr(void *);
1888eda2d14Spatrick int	cpsw_rxintr(void *);
1898eda2d14Spatrick int	cpsw_txintr(void *);
1908eda2d14Spatrick int	cpsw_miscintr(void *);
1918eda2d14Spatrick 
1929fa04b91Sjsg void	cpsw_get_port_config(struct cpsw_port_config *, int);
19328122b11Sdlg 
1949fdf0c62Smpi const struct cfattach cpsw_ca = {
1952e1b3e49Sdlg 	sizeof(struct cpsw_softc),
196a2592955Sjsg 	cpsw_match,
1972e1b3e49Sdlg 	cpsw_attach
1988eda2d14Spatrick };
1998eda2d14Spatrick 
2008eda2d14Spatrick struct cfdriver cpsw_cd = {
2012e1b3e49Sdlg 	NULL,
2022e1b3e49Sdlg 	"cpsw",
2032e1b3e49Sdlg 	DV_IFNET
2048eda2d14Spatrick };
2058eda2d14Spatrick 
2068eda2d14Spatrick static inline u_int
cpsw_txdesc_adjust(u_int x,int y)2078eda2d14Spatrick cpsw_txdesc_adjust(u_int x, int y)
2088eda2d14Spatrick {
2098eda2d14Spatrick 	return (((x) + y) & (CPSW_NTXDESCS - 1));
2108eda2d14Spatrick }
2118eda2d14Spatrick 
2128eda2d14Spatrick static inline u_int
cpsw_rxdesc_adjust(u_int x,int y)2138eda2d14Spatrick cpsw_rxdesc_adjust(u_int x, int y)
2148eda2d14Spatrick {
2158eda2d14Spatrick 	return (((x) + y) & (CPSW_NRXDESCS - 1));
2168eda2d14Spatrick }
2178eda2d14Spatrick 
2188eda2d14Spatrick static inline void
cpsw_set_txdesc_next(struct cpsw_softc * const sc,const u_int i,uint32_t n)2198eda2d14Spatrick cpsw_set_txdesc_next(struct cpsw_softc * const sc, const u_int i, uint32_t n)
2208eda2d14Spatrick {
2218eda2d14Spatrick 	const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i + 0;
2228eda2d14Spatrick 	bus_space_write_4(sc->sc_bst, sc->sc_bsh_txdescs, o, n);
2238eda2d14Spatrick }
2248eda2d14Spatrick 
2258eda2d14Spatrick static inline void
cpsw_set_rxdesc_next(struct cpsw_softc * const sc,const u_int i,uint32_t n)2268eda2d14Spatrick cpsw_set_rxdesc_next(struct cpsw_softc * const sc, const u_int i, uint32_t n)
2278eda2d14Spatrick {
2288eda2d14Spatrick 	const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i + 0;
2298eda2d14Spatrick 	bus_space_write_4(sc->sc_bst, sc->sc_bsh_rxdescs, o, n);
2308eda2d14Spatrick }
2318eda2d14Spatrick 
2328eda2d14Spatrick static inline void
cpsw_get_txdesc(struct cpsw_softc * const sc,const u_int i,struct cpsw_cpdma_bd * const bdp)2338eda2d14Spatrick cpsw_get_txdesc(struct cpsw_softc * const sc, const u_int i,
2348eda2d14Spatrick     struct cpsw_cpdma_bd * const bdp)
2358eda2d14Spatrick {
2368eda2d14Spatrick 	const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i;
2371c1360f7Sfgsch 	bus_space_read_region_4(sc->sc_bst, sc->sc_bsh_txdescs, o,
2381c1360f7Sfgsch 	    (uint32_t *)bdp, 4);
2398eda2d14Spatrick }
2408eda2d14Spatrick 
2418eda2d14Spatrick static inline void
cpsw_set_txdesc(struct cpsw_softc * const sc,const u_int i,struct cpsw_cpdma_bd * const bdp)2428eda2d14Spatrick cpsw_set_txdesc(struct cpsw_softc * const sc, const u_int i,
2438eda2d14Spatrick     struct cpsw_cpdma_bd * const bdp)
2448eda2d14Spatrick {
2458eda2d14Spatrick 	const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i;
2461c1360f7Sfgsch 	bus_space_write_region_4(sc->sc_bst, sc->sc_bsh_txdescs, o,
2471c1360f7Sfgsch 	    (uint32_t *)bdp, 4);
2488eda2d14Spatrick }
2498eda2d14Spatrick 
2508eda2d14Spatrick static inline void
cpsw_get_rxdesc(struct cpsw_softc * const sc,const u_int i,struct cpsw_cpdma_bd * const bdp)2518eda2d14Spatrick cpsw_get_rxdesc(struct cpsw_softc * const sc, const u_int i,
2528eda2d14Spatrick     struct cpsw_cpdma_bd * const bdp)
2538eda2d14Spatrick {
2548eda2d14Spatrick 	const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i;
2551c1360f7Sfgsch 	bus_space_read_region_4(sc->sc_bst, sc->sc_bsh_rxdescs, o,
2561c1360f7Sfgsch 	    (uint32_t *)bdp, 4);
2578eda2d14Spatrick }
2588eda2d14Spatrick 
2598eda2d14Spatrick static inline void
cpsw_set_rxdesc(struct cpsw_softc * const sc,const u_int i,struct cpsw_cpdma_bd * const bdp)2608eda2d14Spatrick cpsw_set_rxdesc(struct cpsw_softc * const sc, const u_int i,
2618eda2d14Spatrick     struct cpsw_cpdma_bd * const bdp)
2628eda2d14Spatrick {
2638eda2d14Spatrick 	const bus_size_t o = sizeof(struct cpsw_cpdma_bd) * i;
2641c1360f7Sfgsch 	bus_space_write_region_4(sc->sc_bst, sc->sc_bsh_rxdescs, o,
2651c1360f7Sfgsch 	    (uint32_t *)bdp, 4);
2668eda2d14Spatrick }
2678eda2d14Spatrick 
2688eda2d14Spatrick static inline bus_addr_t
cpsw_txdesc_paddr(struct cpsw_softc * const sc,u_int x)2698eda2d14Spatrick cpsw_txdesc_paddr(struct cpsw_softc * const sc, u_int x)
2708eda2d14Spatrick {
2718eda2d14Spatrick 	KASSERT(x < CPSW_NTXDESCS);
2728eda2d14Spatrick 	return sc->sc_txdescs_pa + sizeof(struct cpsw_cpdma_bd) * x;
2738eda2d14Spatrick }
2748eda2d14Spatrick 
2758eda2d14Spatrick static inline bus_addr_t
cpsw_rxdesc_paddr(struct cpsw_softc * const sc,u_int x)2768eda2d14Spatrick cpsw_rxdesc_paddr(struct cpsw_softc * const sc, u_int x)
2778eda2d14Spatrick {
2788eda2d14Spatrick 	KASSERT(x < CPSW_NRXDESCS);
2798eda2d14Spatrick 	return sc->sc_rxdescs_pa + sizeof(struct cpsw_cpdma_bd) * x;
2808eda2d14Spatrick }
2818eda2d14Spatrick 
28232d55a3aScanacar static void
cpsw_mdio_init(struct cpsw_softc * sc)28332d55a3aScanacar cpsw_mdio_init(struct cpsw_softc *sc)
28432d55a3aScanacar {
28532d55a3aScanacar 	uint32_t alive, link;
28632d55a3aScanacar 	u_int tries;
28732d55a3aScanacar 
28832d55a3aScanacar 	sc->sc_active_port = 0;
28932d55a3aScanacar 
29036fd90dcSjsg 	/* Initialize MDIO - ENABLE, PREAMBLE=0, FAULTENB, CLKDIV=0xFF */
29132d55a3aScanacar 	/* TODO Calculate MDCLK=CLK/(CLKDIV+1) */
29232d55a3aScanacar 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOCONTROL,
29332d55a3aScanacar 	    (1<<30) | (1<<18) | 0xFF);
29432d55a3aScanacar 
29532d55a3aScanacar 	for(tries = 0; tries < 1000; tries++) {
29632d55a3aScanacar 		alive = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOALIVE) & 3;
29732d55a3aScanacar 		if (alive)
29832d55a3aScanacar 			break;
29932d55a3aScanacar 		delay(1);
30032d55a3aScanacar 	}
30132d55a3aScanacar 
30232d55a3aScanacar 	if (alive == 0) {
30332d55a3aScanacar 		printf("%s: no PHY is alive\n", DEVNAME(sc));
30432d55a3aScanacar 		return;
30532d55a3aScanacar 	}
30632d55a3aScanacar 
30732d55a3aScanacar 	link = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOLINK) & 3;
30832d55a3aScanacar 
30932d55a3aScanacar 	if (alive == 3) {
31032d55a3aScanacar 		/* both ports are alive, prefer one with link */
31132d55a3aScanacar 		if (link == 2)
31232d55a3aScanacar 			sc->sc_active_port = 1;
31332d55a3aScanacar 	} else if (alive == 2)
31432d55a3aScanacar 		sc->sc_active_port = 1;
31532d55a3aScanacar 
31632d55a3aScanacar 	/* Select the port to monitor */
31732d55a3aScanacar 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOUSERPHYSEL0,
31832d55a3aScanacar 	    sc->sc_active_port);
31932d55a3aScanacar }
32032d55a3aScanacar 
321a2592955Sjsg int
cpsw_match(struct device * parent,void * match,void * aux)322a2592955Sjsg cpsw_match(struct device *parent, void *match, void *aux)
323a2592955Sjsg {
324a2592955Sjsg 	struct fdt_attach_args *faa = aux;
325a2592955Sjsg 
326a2592955Sjsg 	return OF_is_compatible(faa->fa_node, "ti,cpsw");
327a2592955Sjsg }
328a2592955Sjsg 
3298eda2d14Spatrick void
cpsw_attach(struct device * parent,struct device * self,void * aux)3308eda2d14Spatrick cpsw_attach(struct device *parent, struct device *self, void *aux)
3318eda2d14Spatrick {
3328eda2d14Spatrick 	struct cpsw_softc *sc = (struct cpsw_softc *)self;
333a2592955Sjsg 	struct fdt_attach_args *faa = aux;
334a484de46Sdlg 	struct arpcom * const ac = &sc->sc_ac;
335a484de46Sdlg 	struct ifnet * const ifp = &ac->ac_if;
33699d034d9Skettenis 	void *descs;
337c12df080Sdlg 	u_int32_t idver;
3388eda2d14Spatrick 	int error;
3391558a2f3Sjsg 	int node;
3408eda2d14Spatrick 	u_int i;
341a2592955Sjsg 	uint32_t memsize;
342a2592955Sjsg 
3437d7522cbSjsg 	if (faa->fa_nreg < 1)
344a2592955Sjsg 		return;
345a2592955Sjsg 
346a2592955Sjsg 	/*
347ac7c670eSpatrick 	 * fa_reg[0].size is size of CPSW_SS and CPSW_PORT
348ac7c670eSpatrick 	 * fa_reg[1].size is size of CPSW_WR
349a2592955Sjsg 	 * we map a size that is a superset of both
350a2592955Sjsg 	 */
351a2592955Sjsg 	memsize = 0x4000;
3528eda2d14Spatrick 
35316f6b372Sjsg 	pinctrl_byname(faa->fa_node, "default");
3541558a2f3Sjsg 
3551558a2f3Sjsg 	for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) {
356ae1367d5Skettenis 		if (OF_is_compatible(node, "ti,davinci_mdio")) {
357ae1367d5Skettenis 			clock_enable(node, "fck");
35816f6b372Sjsg 			pinctrl_byname(node, "default");
3591558a2f3Sjsg 		}
360ae1367d5Skettenis 	}
3611558a2f3Sjsg 
3628eda2d14Spatrick 	timeout_set(&sc->sc_tick, cpsw_tick, sc);
3638eda2d14Spatrick 
3649fa04b91Sjsg 	cpsw_get_port_config(sc->sc_port_config, faa->fa_node);
3659fa04b91Sjsg 	memcpy(sc->sc_ac.ac_enaddr, sc->sc_port_config[0].enaddr,
3669fa04b91Sjsg 	    ETHER_ADDR_LEN);
3678eda2d14Spatrick 
3687d7522cbSjsg 	sc->sc_rxthih = arm_intr_establish_fdt_idx(faa->fa_node, 0, IPL_NET,
3697d7522cbSjsg 	    cpsw_rxthintr, sc, DEVNAME(sc));
3707d7522cbSjsg 	sc->sc_rxih = arm_intr_establish_fdt_idx(faa->fa_node, 1, IPL_NET,
3717d7522cbSjsg 	    cpsw_rxintr, sc, DEVNAME(sc));
3727d7522cbSjsg 	sc->sc_txih = arm_intr_establish_fdt_idx(faa->fa_node, 2, IPL_NET,
3737d7522cbSjsg 	    cpsw_txintr, sc, DEVNAME(sc));
3747d7522cbSjsg 	sc->sc_miscih = arm_intr_establish_fdt_idx(faa->fa_node, 3, IPL_NET,
3757d7522cbSjsg 	    cpsw_miscintr, sc, DEVNAME(sc));
3768eda2d14Spatrick 
377a2592955Sjsg 	sc->sc_bst = faa->fa_iot;
378a2592955Sjsg 	sc->sc_bdt = faa->fa_dmat;
3798eda2d14Spatrick 
380ac7c670eSpatrick 	error = bus_space_map(sc->sc_bst, faa->fa_reg[0].addr,
38199d034d9Skettenis 	    memsize, BUS_SPACE_MAP_LINEAR, &sc->sc_bsh);
3828eda2d14Spatrick 	if (error) {
3838eda2d14Spatrick 		printf("can't map registers: %d\n", error);
3848eda2d14Spatrick 		return;
3858eda2d14Spatrick 	}
3868eda2d14Spatrick 
3878eda2d14Spatrick 	error = bus_space_subregion(sc->sc_bst, sc->sc_bsh,
3888eda2d14Spatrick 	    CPSW_CPPI_RAM_TXDESCS_BASE, CPSW_CPPI_RAM_TXDESCS_SIZE,
3898eda2d14Spatrick 	    &sc->sc_bsh_txdescs);
3908eda2d14Spatrick 	if (error) {
3918eda2d14Spatrick 		printf("can't subregion tx ring SRAM: %d\n", error);
3928eda2d14Spatrick 		return;
3938eda2d14Spatrick 	}
39499d034d9Skettenis 	descs = bus_space_vaddr(sc->sc_bst, sc->sc_bsh_txdescs);
39599d034d9Skettenis 	pmap_extract(pmap_kernel(), (vaddr_t)descs, &sc->sc_txdescs_pa);
3968eda2d14Spatrick 
3978eda2d14Spatrick 	error = bus_space_subregion(sc->sc_bst, sc->sc_bsh,
3988eda2d14Spatrick 	    CPSW_CPPI_RAM_RXDESCS_BASE, CPSW_CPPI_RAM_RXDESCS_SIZE,
3998eda2d14Spatrick 	    &sc->sc_bsh_rxdescs);
4008eda2d14Spatrick 	if (error) {
4018eda2d14Spatrick 		printf("can't subregion rx ring SRAM: %d\n", error);
4028eda2d14Spatrick 		return;
4038eda2d14Spatrick 	}
40499d034d9Skettenis 	descs = bus_space_vaddr(sc->sc_bst, sc->sc_bsh_rxdescs);
40599d034d9Skettenis 	pmap_extract(pmap_kernel(), (vaddr_t)descs, &sc->sc_rxdescs_pa);
4068eda2d14Spatrick 
4078eda2d14Spatrick 	sc->sc_rdp = malloc(sizeof(*sc->sc_rdp), M_TEMP, M_WAITOK);
4088eda2d14Spatrick 	KASSERT(sc->sc_rdp != NULL);
4098eda2d14Spatrick 
4108eda2d14Spatrick 	for (i = 0; i < CPSW_NTXDESCS; i++) {
4118eda2d14Spatrick 		if ((error = bus_dmamap_create(sc->sc_bdt, MCLBYTES,
4128eda2d14Spatrick 		    CPSW_TXFRAGS, MCLBYTES, 0, 0,
4138eda2d14Spatrick 		    &sc->sc_rdp->tx_dm[i])) != 0) {
4148eda2d14Spatrick 			printf("unable to create tx DMA map: %d\n", error);
4158eda2d14Spatrick 		}
4168eda2d14Spatrick 		sc->sc_rdp->tx_mb[i] = NULL;
4178eda2d14Spatrick 	}
4188eda2d14Spatrick 
4198eda2d14Spatrick 	for (i = 0; i < CPSW_NRXDESCS; i++) {
4208eda2d14Spatrick 		if ((error = bus_dmamap_create(sc->sc_bdt, MCLBYTES, 1,
4218eda2d14Spatrick 		    MCLBYTES, 0, 0, &sc->sc_rdp->rx_dm[i])) != 0) {
4228eda2d14Spatrick 			printf("unable to create rx DMA map: %d\n", error);
4238eda2d14Spatrick 		}
4248eda2d14Spatrick 		sc->sc_rdp->rx_mb[i] = NULL;
4258eda2d14Spatrick 	}
4268eda2d14Spatrick 
427386f0050Sdlg 	sc->sc_txpad = dma_alloc(ETHER_MIN_LEN, PR_WAITOK | PR_ZERO);
4288eda2d14Spatrick 	KASSERT(sc->sc_txpad != NULL);
4298eda2d14Spatrick 	bus_dmamap_create(sc->sc_bdt, ETHER_MIN_LEN, 1, ETHER_MIN_LEN, 0,
4308eda2d14Spatrick 	    BUS_DMA_WAITOK, &sc->sc_txpad_dm);
4318eda2d14Spatrick 	bus_dmamap_load(sc->sc_bdt, sc->sc_txpad_dm, sc->sc_txpad,
4328eda2d14Spatrick 	    ETHER_MIN_LEN, NULL, BUS_DMA_WAITOK|BUS_DMA_WRITE);
4338eda2d14Spatrick 	bus_dmamap_sync(sc->sc_bdt, sc->sc_txpad_dm, 0, ETHER_MIN_LEN,
4348eda2d14Spatrick 	    BUS_DMASYNC_PREWRITE);
4358eda2d14Spatrick 
436e85b21ccSjasper 	idver = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_IDVER);
437c12df080Sdlg 	printf(": version %d.%d (%d), address %s\n",
438c12df080Sdlg 	    CPSW_SS_IDVER_MAJ(idver), CPSW_SS_IDVER_MIN(idver),
439c12df080Sdlg 	    CPSW_SS_IDVER_RTL(idver), ether_sprintf(ac->ac_enaddr));
4408eda2d14Spatrick 
4418eda2d14Spatrick 	ifp->if_softc = sc;
4420ebcac1dSjsg 	ifp->if_capabilities = IFCAP_VLAN_MTU;
4438eda2d14Spatrick 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
4448eda2d14Spatrick 	ifp->if_start = cpsw_start;
4458eda2d14Spatrick 	ifp->if_ioctl = cpsw_ioctl;
4468eda2d14Spatrick 	ifp->if_watchdog = cpsw_watchdog;
447*cf96265bSbluhm 	ifq_init_maxlen(&ifp->if_snd, CPSW_NTXDESCS - 1);
448ce639ce1Sdlg 	memcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
4498eda2d14Spatrick 
4508eda2d14Spatrick 	cpsw_stop(ifp);
4518eda2d14Spatrick 
4528eda2d14Spatrick 	sc->sc_mii.mii_ifp = ifp;
4538eda2d14Spatrick 	sc->sc_mii.mii_readreg = cpsw_mii_readreg;
4548eda2d14Spatrick 	sc->sc_mii.mii_writereg = cpsw_mii_writereg;
4558eda2d14Spatrick 	sc->sc_mii.mii_statchg = cpsw_mii_statchg;
4568eda2d14Spatrick 
45732d55a3aScanacar 	cpsw_mdio_init(sc);
45832d55a3aScanacar 
4598eda2d14Spatrick 	ifmedia_init(&sc->sc_mii.mii_media, 0, cpsw_mediachange,
4608eda2d14Spatrick 	    cpsw_mediastatus);
4610d70ca2eSdlg 	mii_attach(self, &sc->sc_mii, 0xffffffff,
4629fa04b91Sjsg 	    sc->sc_port_config[0].phy_id, MII_OFFSET_ANY, 0);
4638eda2d14Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys) == NULL) {
4648eda2d14Spatrick 		printf("no PHY found!\n");
4658eda2d14Spatrick 		ifmedia_add(&sc->sc_mii.mii_media,
4668eda2d14Spatrick 		    IFM_ETHER|IFM_MANUAL, 0, NULL);
4678eda2d14Spatrick 		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_MANUAL);
4688eda2d14Spatrick 	} else {
4698eda2d14Spatrick 		ifmedia_set(&sc->sc_mii.mii_media, IFM_ETHER|IFM_AUTO);
4708eda2d14Spatrick 	}
4718eda2d14Spatrick 
4728eda2d14Spatrick 	if_attach(ifp);
4738eda2d14Spatrick 	ether_ifattach(ifp);
4748eda2d14Spatrick 
4758eda2d14Spatrick 	return;
4768eda2d14Spatrick }
4778eda2d14Spatrick 
4788eda2d14Spatrick int
cpsw_mediachange(struct ifnet * ifp)4798eda2d14Spatrick cpsw_mediachange(struct ifnet *ifp)
4808eda2d14Spatrick {
4818eda2d14Spatrick 	struct cpsw_softc *sc = ifp->if_softc;
4828eda2d14Spatrick 
4838eda2d14Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys))
4848eda2d14Spatrick 		mii_mediachg(&sc->sc_mii);
4858eda2d14Spatrick 
4868eda2d14Spatrick 	return (0);
4878eda2d14Spatrick }
4888eda2d14Spatrick 
4898eda2d14Spatrick void
cpsw_mediastatus(struct ifnet * ifp,struct ifmediareq * ifmr)4908eda2d14Spatrick cpsw_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr)
4918eda2d14Spatrick {
4928eda2d14Spatrick 	struct cpsw_softc *sc = ifp->if_softc;
4938eda2d14Spatrick 
4948eda2d14Spatrick 	if (LIST_FIRST(&sc->sc_mii.mii_phys)) {
4958eda2d14Spatrick 		mii_pollstat(&sc->sc_mii);
4968eda2d14Spatrick 		ifmr->ifm_active = sc->sc_mii.mii_media_active;
4978eda2d14Spatrick 		ifmr->ifm_status = sc->sc_mii.mii_media_status;
4988eda2d14Spatrick 	}
4998eda2d14Spatrick }
5008eda2d14Spatrick 
5018eda2d14Spatrick void
cpsw_start(struct ifnet * ifp)5028eda2d14Spatrick cpsw_start(struct ifnet *ifp)
5038eda2d14Spatrick {
5048eda2d14Spatrick 	struct cpsw_softc * const sc = ifp->if_softc;
5058eda2d14Spatrick 	struct cpsw_ring_data * const rdp = sc->sc_rdp;
5068eda2d14Spatrick 	struct cpsw_cpdma_bd bd;
5078eda2d14Spatrick 	struct mbuf *m;
5088eda2d14Spatrick 	bus_dmamap_t dm;
5098eda2d14Spatrick 	u_int eopi = ~0;
5108eda2d14Spatrick 	u_int seg;
5118eda2d14Spatrick 	u_int txfree;
5128eda2d14Spatrick 	int txstart = -1;
5138eda2d14Spatrick 	int error;
5148eda2d14Spatrick 	bool pad;
5158eda2d14Spatrick 	u_int mlen;
5168eda2d14Spatrick 
517d7fae90bSdlg 	if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
518de6cd8fbSdlg 	    ifq_is_oactive(&ifp->if_snd) ||
5190cae21bdSpatrick 	    ifq_empty(&ifp->if_snd))
5208eda2d14Spatrick 		return;
5218eda2d14Spatrick 
5228eda2d14Spatrick 	if (sc->sc_txnext >= sc->sc_txhead)
5238eda2d14Spatrick 		txfree = CPSW_NTXDESCS - 1 + sc->sc_txhead - sc->sc_txnext;
5248eda2d14Spatrick 	else
5258eda2d14Spatrick 		txfree = sc->sc_txhead - sc->sc_txnext - 1;
5268eda2d14Spatrick 
527d7fae90bSdlg 	for (;;) {
528d7fae90bSdlg 		if (txfree <= CPSW_TXFRAGS) {
529de6cd8fbSdlg 			ifq_set_oactive(&ifp->if_snd);
530d7fae90bSdlg 			break;
531d7fae90bSdlg 		}
532d7fae90bSdlg 
53363bcfa73Spatrick 		m = ifq_dequeue(&ifp->if_snd);
5348eda2d14Spatrick 		if (m == NULL)
5358eda2d14Spatrick 			break;
5368eda2d14Spatrick 
537d7fae90bSdlg 		dm = rdp->tx_dm[sc->sc_txnext];
538d7fae90bSdlg 		error = bus_dmamap_load_mbuf(sc->sc_bdt, dm, m, BUS_DMA_NOWAIT);
539d7fae90bSdlg 		switch (error) {
540d7fae90bSdlg 		case 0:
541d7fae90bSdlg 			break;
542d7fae90bSdlg 
543d7fae90bSdlg 		case EFBIG: /* mbuf chain is too fragmented */
544d7fae90bSdlg 			if (m_defrag(m, M_DONTWAIT) == 0 &&
545d7fae90bSdlg 			    bus_dmamap_load_mbuf(sc->sc_bdt, dm, m,
546d7fae90bSdlg 			    BUS_DMA_NOWAIT) == 0)
547d7fae90bSdlg 				break;
548d7fae90bSdlg 
549d7fae90bSdlg 			/* FALLTHROUGH */
550d7fae90bSdlg 		default:
5518eda2d14Spatrick 			m_freem(m);
5528eda2d14Spatrick 			ifp->if_oerrors++;
5538eda2d14Spatrick 			continue;
5548eda2d14Spatrick 		}
5558eda2d14Spatrick 
5568284c1c7Sdlg 		mlen = dm->dm_mapsize;
5578eda2d14Spatrick 		pad = mlen < CPSW_PAD_LEN;
5588eda2d14Spatrick 
5598eda2d14Spatrick 		KASSERT(rdp->tx_mb[sc->sc_txnext] == NULL);
5608eda2d14Spatrick 		rdp->tx_mb[sc->sc_txnext] = m;
561d7fae90bSdlg 
562d7fae90bSdlg #if NBPFILTER > 0
563d7fae90bSdlg 		if (ifp->if_bpf)
564d7fae90bSdlg 			bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
565d7fae90bSdlg #endif
5668eda2d14Spatrick 
5678eda2d14Spatrick 		bus_dmamap_sync(sc->sc_bdt, dm, 0, dm->dm_mapsize,
5688eda2d14Spatrick 		    BUS_DMASYNC_PREWRITE);
5698eda2d14Spatrick 
5708eda2d14Spatrick 		if (txstart == -1)
5718eda2d14Spatrick 			txstart = sc->sc_txnext;
5721c1360f7Sfgsch 		eopi = sc->sc_txnext;
5738eda2d14Spatrick 		for (seg = 0; seg < dm->dm_nsegs; seg++) {
5741c1360f7Sfgsch 			bd.next = cpsw_txdesc_paddr(sc,
5758eda2d14Spatrick 			    TXDESC_NEXT(sc->sc_txnext));
5761c1360f7Sfgsch 			bd.bufptr = dm->dm_segs[seg].ds_addr;
5771c1360f7Sfgsch 			bd.bufoff = 0;
5781c1360f7Sfgsch 			bd.buflen = dm->dm_segs[seg].ds_len;
5791c1360f7Sfgsch 			bd.pktlen = 0;
5801c1360f7Sfgsch 			bd.flags = 0;
5818eda2d14Spatrick 
5821c1360f7Sfgsch 			if (seg == 0) {
5831c1360f7Sfgsch 				bd.flags = CPDMA_BD_OWNER | CPDMA_BD_SOP;
5841c1360f7Sfgsch 				bd.pktlen = MAX(mlen, CPSW_PAD_LEN);
5851c1360f7Sfgsch 			}
5868eda2d14Spatrick 
5878eda2d14Spatrick 			if (seg == dm->dm_nsegs - 1 && !pad)
5881c1360f7Sfgsch 				bd.flags |= CPDMA_BD_EOP;
5898eda2d14Spatrick 
5908eda2d14Spatrick 			cpsw_set_txdesc(sc, sc->sc_txnext, &bd);
5918eda2d14Spatrick 			txfree--;
5928eda2d14Spatrick 			eopi = sc->sc_txnext;
5938eda2d14Spatrick 			sc->sc_txnext = TXDESC_NEXT(sc->sc_txnext);
5948eda2d14Spatrick 		}
5958eda2d14Spatrick 		if (pad) {
5961c1360f7Sfgsch 			bd.next = cpsw_txdesc_paddr(sc,
5978eda2d14Spatrick 			    TXDESC_NEXT(sc->sc_txnext));
5981c1360f7Sfgsch 			bd.bufptr = sc->sc_txpad_pa;
5991c1360f7Sfgsch 			bd.bufoff = 0;
6001c1360f7Sfgsch 			bd.buflen = CPSW_PAD_LEN - mlen;
6011c1360f7Sfgsch 			bd.pktlen = 0;
6021c1360f7Sfgsch 			bd.flags = CPDMA_BD_EOP;
6038eda2d14Spatrick 
6048eda2d14Spatrick 			cpsw_set_txdesc(sc, sc->sc_txnext, &bd);
6058eda2d14Spatrick 			txfree--;
6068eda2d14Spatrick 			eopi = sc->sc_txnext;
6078eda2d14Spatrick 			sc->sc_txnext = TXDESC_NEXT(sc->sc_txnext);
6088eda2d14Spatrick 		}
6098eda2d14Spatrick 	}
6108eda2d14Spatrick 
6118eda2d14Spatrick 	if (txstart >= 0) {
6128eda2d14Spatrick 		ifp->if_timer = 5;
6138eda2d14Spatrick 		/* terminate the new chain */
6148eda2d14Spatrick 		KASSERT(eopi == TXDESC_PREV(sc->sc_txnext));
6158eda2d14Spatrick 		cpsw_set_txdesc_next(sc, TXDESC_PREV(sc->sc_txnext), 0);
6168eda2d14Spatrick 
6178eda2d14Spatrick 		/* link the new chain on */
6188eda2d14Spatrick 		cpsw_set_txdesc_next(sc, TXDESC_PREV(txstart),
6198eda2d14Spatrick 		    cpsw_txdesc_paddr(sc, txstart));
6208eda2d14Spatrick 		if (sc->sc_txeoq) {
6218eda2d14Spatrick 			/* kick the dma engine */
6228eda2d14Spatrick 			sc->sc_txeoq = false;
623e85b21ccSjasper 			bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0),
6248eda2d14Spatrick 			    cpsw_txdesc_paddr(sc, txstart));
6258eda2d14Spatrick 		}
6268eda2d14Spatrick 	}
6278eda2d14Spatrick }
6288eda2d14Spatrick 
6298eda2d14Spatrick int
cpsw_ioctl(struct ifnet * ifp,u_long cmd,caddr_t data)6308eda2d14Spatrick cpsw_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
6318eda2d14Spatrick {
6328eda2d14Spatrick 	struct cpsw_softc *sc = ifp->if_softc;
6338eda2d14Spatrick 	struct ifreq *ifr = (struct ifreq *)data;
6348eda2d14Spatrick 	int s = splnet();
6358eda2d14Spatrick 	int error = 0;
6368eda2d14Spatrick 
6378eda2d14Spatrick 	switch (cmd) {
6388eda2d14Spatrick 	case SIOCSIFADDR:
6398eda2d14Spatrick 		ifp->if_flags |= IFF_UP;
64097a1a4c1Smpi 		/* FALLTHROUGH */
6418eda2d14Spatrick 	case SIOCSIFFLAGS:
6428eda2d14Spatrick 		if (ifp->if_flags & IFF_UP) {
6438eda2d14Spatrick 			if (ifp->if_flags & IFF_RUNNING)
6448eda2d14Spatrick 				error = ENETRESET;
6458eda2d14Spatrick 			else
6468eda2d14Spatrick 				cpsw_init(ifp);
6478eda2d14Spatrick 		} else {
6488eda2d14Spatrick 			if (ifp->if_flags & IFF_RUNNING)
6498eda2d14Spatrick 				cpsw_stop(ifp);
6508eda2d14Spatrick 		}
6518eda2d14Spatrick 		break;
6528eda2d14Spatrick 	case SIOCSIFMEDIA:
6538eda2d14Spatrick 		ifr->ifr_media &= ~IFM_ETH_FMASK;
6548eda2d14Spatrick 		/* FALLTHROUGH */
6558eda2d14Spatrick 	case SIOCGIFMEDIA:
6568eda2d14Spatrick 		error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd);
6578eda2d14Spatrick 		break;
6588eda2d14Spatrick 	default:
659a484de46Sdlg 		error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
6608eda2d14Spatrick 		break;
6618eda2d14Spatrick 	}
6628eda2d14Spatrick 	if (error == ENETRESET) {
6638eda2d14Spatrick 		if (ifp->if_flags & IFF_RUNNING)
6648eda2d14Spatrick 			cpsw_init(ifp);
6658eda2d14Spatrick 		error = 0;
6668eda2d14Spatrick 	}
6678eda2d14Spatrick 
6688eda2d14Spatrick 	splx(s);
6698eda2d14Spatrick 
6708eda2d14Spatrick 	return error;
6718eda2d14Spatrick }
6728eda2d14Spatrick 
6738eda2d14Spatrick void
cpsw_watchdog(struct ifnet * ifp)6748eda2d14Spatrick cpsw_watchdog(struct ifnet *ifp)
6758eda2d14Spatrick {
67606a810ecSbmercer 	printf("%s: device timeout\n", ifp->if_xname);
6778eda2d14Spatrick 
6788eda2d14Spatrick 	ifp->if_oerrors++;
6798eda2d14Spatrick 	cpsw_init(ifp);
6808eda2d14Spatrick 	cpsw_start(ifp);
6818eda2d14Spatrick }
6828eda2d14Spatrick 
6838eda2d14Spatrick static int
cpsw_mii_wait(struct cpsw_softc * const sc,int reg)6848eda2d14Spatrick cpsw_mii_wait(struct cpsw_softc * const sc, int reg)
6858eda2d14Spatrick {
6868eda2d14Spatrick 	u_int tries;
6878eda2d14Spatrick 
6888eda2d14Spatrick 	for(tries = 0; tries < 1000; tries++) {
689e85b21ccSjasper 		if ((bus_space_read_4(sc->sc_bst, sc->sc_bsh, reg) & (1U << 31)) == 0)
6908eda2d14Spatrick 			return 0;
6918eda2d14Spatrick 		delay(1);
6928eda2d14Spatrick 	}
6938eda2d14Spatrick 	return ETIMEDOUT;
6948eda2d14Spatrick }
6958eda2d14Spatrick 
6968eda2d14Spatrick int
cpsw_mii_readreg(struct device * dev,int phy,int reg)6978eda2d14Spatrick cpsw_mii_readreg(struct device *dev, int phy, int reg)
6988eda2d14Spatrick {
6998eda2d14Spatrick 	struct cpsw_softc * const sc = (struct cpsw_softc *)dev;
7008eda2d14Spatrick 	uint32_t v;
7018eda2d14Spatrick 
7028eda2d14Spatrick 	if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0)
7038eda2d14Spatrick 		return 0;
7048eda2d14Spatrick 
705e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0, (1U << 31) |
7068eda2d14Spatrick 	    ((reg & 0x1F) << 21) | ((phy & 0x1F) << 16));
7078eda2d14Spatrick 
7088eda2d14Spatrick 	if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0)
7098eda2d14Spatrick 		return 0;
7108eda2d14Spatrick 
711e85b21ccSjasper 	v = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0);
7121c1360f7Sfgsch 	if (v & (1 << 29))
7138eda2d14Spatrick 		return v & 0xffff;
7148eda2d14Spatrick 	else
7158eda2d14Spatrick 		return 0;
7168eda2d14Spatrick }
7178eda2d14Spatrick 
7188eda2d14Spatrick void
cpsw_mii_writereg(struct device * dev,int phy,int reg,int val)7198eda2d14Spatrick cpsw_mii_writereg(struct device *dev, int phy, int reg, int val)
7208eda2d14Spatrick {
7218eda2d14Spatrick 	struct cpsw_softc * const sc = (struct cpsw_softc *)dev;
7228eda2d14Spatrick 	uint32_t v;
7238eda2d14Spatrick 
7248eda2d14Spatrick 	KASSERT((val & 0xffff0000UL) == 0);
7258eda2d14Spatrick 
7268eda2d14Spatrick 	if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0)
7278eda2d14Spatrick 		goto out;
7288eda2d14Spatrick 
729e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0, (1U << 31) | (1 << 30) |
7308eda2d14Spatrick 	    ((reg & 0x1F) << 21) | ((phy & 0x1F) << 16) | val);
7318eda2d14Spatrick 
7328eda2d14Spatrick 	if (cpsw_mii_wait(sc, MDIOUSERACCESS0) != 0)
7338eda2d14Spatrick 		goto out;
7348eda2d14Spatrick 
735e85b21ccSjasper 	v = bus_space_read_4(sc->sc_bst, sc->sc_bsh, MDIOUSERACCESS0);
7361c1360f7Sfgsch 	if ((v & (1 << 29)) == 0)
7378eda2d14Spatrick out:
7388eda2d14Spatrick 		printf("%s error\n", __func__);
7398eda2d14Spatrick 
7408eda2d14Spatrick }
7418eda2d14Spatrick 
7428eda2d14Spatrick void
cpsw_mii_statchg(struct device * self)7438eda2d14Spatrick cpsw_mii_statchg(struct device *self)
7448eda2d14Spatrick {
7458eda2d14Spatrick 	return;
7468eda2d14Spatrick }
7478eda2d14Spatrick 
7488eda2d14Spatrick int
cpsw_new_rxbuf(struct cpsw_softc * const sc,const u_int i)7498eda2d14Spatrick cpsw_new_rxbuf(struct cpsw_softc * const sc, const u_int i)
7508eda2d14Spatrick {
7518eda2d14Spatrick 	struct cpsw_ring_data * const rdp = sc->sc_rdp;
7528eda2d14Spatrick 	const u_int h = RXDESC_PREV(i);
7538eda2d14Spatrick 	struct cpsw_cpdma_bd bd;
7548eda2d14Spatrick 	struct mbuf *m;
7558eda2d14Spatrick 	int error = ENOBUFS;
7568eda2d14Spatrick 
7578eda2d14Spatrick 	MGETHDR(m, M_DONTWAIT, MT_DATA);
7588eda2d14Spatrick 	if (m == NULL) {
7598eda2d14Spatrick 		goto reuse;
7608eda2d14Spatrick 	}
7618eda2d14Spatrick 
7628eda2d14Spatrick 	MCLGET(m, M_DONTWAIT);
7638eda2d14Spatrick 	if ((m->m_flags & M_EXT) == 0) {
7648eda2d14Spatrick 		m_freem(m);
7658eda2d14Spatrick 		goto reuse;
7668eda2d14Spatrick 	}
7678eda2d14Spatrick 
7688eda2d14Spatrick 	/* We have a new buffer, prepare it for the ring. */
7698eda2d14Spatrick 
7708eda2d14Spatrick 	if (rdp->rx_mb[i] != NULL)
7718eda2d14Spatrick 		bus_dmamap_unload(sc->sc_bdt, rdp->rx_dm[i]);
7728eda2d14Spatrick 
7738eda2d14Spatrick 	m->m_len = m->m_pkthdr.len = MCLBYTES;
7748eda2d14Spatrick 
7758eda2d14Spatrick 	rdp->rx_mb[i] = m;
7768eda2d14Spatrick 
7778eda2d14Spatrick 	error = bus_dmamap_load_mbuf(sc->sc_bdt, rdp->rx_dm[i], rdp->rx_mb[i],
7788eda2d14Spatrick 	    BUS_DMA_READ|BUS_DMA_NOWAIT);
7798eda2d14Spatrick 	if (error) {
7808eda2d14Spatrick 		printf("can't load rx DMA map %d: %d\n", i, error);
7818eda2d14Spatrick 	}
7828eda2d14Spatrick 
7838eda2d14Spatrick 	bus_dmamap_sync(sc->sc_bdt, rdp->rx_dm[i],
7848eda2d14Spatrick 	    0, rdp->rx_dm[i]->dm_mapsize, BUS_DMASYNC_PREREAD);
7858eda2d14Spatrick 
7868eda2d14Spatrick 	error = 0;
7878eda2d14Spatrick 
7888eda2d14Spatrick reuse:
7898eda2d14Spatrick 	/* (re-)setup the descriptor */
7901c1360f7Sfgsch 	bd.next = 0;
7911c1360f7Sfgsch 	bd.bufptr = rdp->rx_dm[i]->dm_segs[0].ds_addr;
7921c1360f7Sfgsch 	bd.bufoff = 0;
7931c1360f7Sfgsch 	bd.buflen = MIN(0x7ff, rdp->rx_dm[i]->dm_segs[0].ds_len);
7941c1360f7Sfgsch 	bd.pktlen = 0;
7951c1360f7Sfgsch 	bd.flags = CPDMA_BD_OWNER;
7968eda2d14Spatrick 
7978eda2d14Spatrick 	cpsw_set_rxdesc(sc, i, &bd);
7988eda2d14Spatrick 	/* and link onto ring */
7998eda2d14Spatrick 	cpsw_set_rxdesc_next(sc, h, cpsw_rxdesc_paddr(sc, i));
8008eda2d14Spatrick 
8018eda2d14Spatrick 	return error;
8028eda2d14Spatrick }
8038eda2d14Spatrick 
8048eda2d14Spatrick int
cpsw_init(struct ifnet * ifp)8058eda2d14Spatrick cpsw_init(struct ifnet *ifp)
8068eda2d14Spatrick {
8078eda2d14Spatrick 	struct cpsw_softc * const sc = ifp->if_softc;
808ac71c3ccSdlg 	struct arpcom *ac = &sc->sc_ac;
8098eda2d14Spatrick 	struct mii_data * const mii = &sc->sc_mii;
8108eda2d14Spatrick 	int i;
8118eda2d14Spatrick 
8128eda2d14Spatrick 	cpsw_stop(ifp);
8138eda2d14Spatrick 
8148eda2d14Spatrick 	sc->sc_txnext = 0;
8158eda2d14Spatrick 	sc->sc_txhead = 0;
8168eda2d14Spatrick 
8178eda2d14Spatrick 	/* Reset wrapper */
818e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET, 1);
819e85b21ccSjasper 	while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET) & 1);
8208eda2d14Spatrick 
8218eda2d14Spatrick 	/* Reset SS */
822e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET, 1);
823e85b21ccSjasper 	while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET) & 1);
8248eda2d14Spatrick 
8258eda2d14Spatrick 	/* Clear table (30) and enable ALE(31) and set passthrough (4) */
826e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_ALE_CONTROL, (3 << 30) | 0x10);
8278eda2d14Spatrick 
8288eda2d14Spatrick 	/* Reset and init Sliver port 1 and 2 */
8298eda2d14Spatrick 	for (i = 0; i < 2; i++) {
8308eda2d14Spatrick 		/* Reset */
831e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i), 1);
832e85b21ccSjasper 		while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i)) & 1);
8338eda2d14Spatrick 		/* Set Slave Mapping */
834e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_RX_PRI_MAP(i), 0x76543210);
835e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P_TX_PRI_MAP(i+1), 0x33221100);
836e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_RX_MAXLEN(i), 0x5f2);
8378eda2d14Spatrick 		/* Set MAC Address */
838e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P_SA_HI(i+1),
839ac71c3ccSdlg 		    ac->ac_enaddr[0] | (ac->ac_enaddr[1] << 8) |
840ac71c3ccSdlg 		    (ac->ac_enaddr[2] << 16) | (ac->ac_enaddr[3] << 24));
841e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P_SA_LO(i+1),
842ac71c3ccSdlg 		    ac->ac_enaddr[4] | (ac->ac_enaddr[5] << 8));
8438eda2d14Spatrick 
84432d55a3aScanacar 		/* Set MACCONTROL for ports 0,1: FULLDUPLEX(0), GMII_EN(5),
8458eda2d14Spatrick 		   IFCTL_A(15), IFCTL_B(16) FIXME */
846e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_MACCONTROL(i),
8470d70ca2eSdlg 		    1 | (1<<5) | (1<<15) | (1<<16));
8488eda2d14Spatrick 
84932d55a3aScanacar 		/* Set ALE port to forwarding(3) on the active port */
85032d55a3aScanacar 		if (i == sc->sc_active_port)
851e85b21ccSjasper 			bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_ALE_PORTCTL(i+1), 3);
8528eda2d14Spatrick 	}
8538eda2d14Spatrick 
8548eda2d14Spatrick 	/* Set Host Port Mapping */
855e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P0_CPDMA_TX_PRI_MAP, 0x76543210);
856e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_PORT_P0_CPDMA_RX_CH_MAP, 0);
8578eda2d14Spatrick 
8588eda2d14Spatrick 	/* Set ALE port to forwarding(3) */
859e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_ALE_PORTCTL(0), 3);
8608eda2d14Spatrick 
861e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_PTYPE, 0);
862e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_STAT_PORT_EN, 7);
8638eda2d14Spatrick 
864e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET, 1);
865e85b21ccSjasper 	while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET) & 1);
8668eda2d14Spatrick 
8678eda2d14Spatrick 	for (i = 0; i < 8; i++) {
868e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(i), 0);
869e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(i), 0);
870e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(i), 0);
871e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CP(i), 0);
8728eda2d14Spatrick 	}
8738eda2d14Spatrick 
8748eda2d14Spatrick 	bus_space_set_region_4(sc->sc_bst, sc->sc_bsh_txdescs, 0, 0,
8758eda2d14Spatrick 	    CPSW_CPPI_RAM_TXDESCS_SIZE/4);
8768eda2d14Spatrick 
8778eda2d14Spatrick 	sc->sc_txhead = 0;
8788eda2d14Spatrick 	sc->sc_txnext = 0;
8798eda2d14Spatrick 
880e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_FREEBUFFER(0), 0);
8818eda2d14Spatrick 
8828eda2d14Spatrick 	bus_space_set_region_4(sc->sc_bst, sc->sc_bsh_rxdescs, 0, 0,
8838eda2d14Spatrick 	    CPSW_CPPI_RAM_RXDESCS_SIZE/4);
8848eda2d14Spatrick 
8858eda2d14Spatrick 	/* Initialize RX Buffer Descriptors */
8868eda2d14Spatrick 	cpsw_set_rxdesc_next(sc, RXDESC_PREV(0), 0);
8878eda2d14Spatrick 	for (i = 0; i < CPSW_NRXDESCS; i++) {
8888eda2d14Spatrick 		cpsw_new_rxbuf(sc, i);
8898eda2d14Spatrick 	}
8908eda2d14Spatrick 	sc->sc_rxhead = 0;
8918eda2d14Spatrick 
8928eda2d14Spatrick 	/* align layer 3 header to 32-bit */
893e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_BUFFER_OFFSET, ETHER_ALIGN);
8948eda2d14Spatrick 
8958eda2d14Spatrick 	/* Clear all interrupt Masks */
896e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_INTMASK_CLEAR, 0xFFFFFFFF);
897e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_INTMASK_CLEAR, 0xFFFFFFFF);
8988eda2d14Spatrick 
8998eda2d14Spatrick 	/* Enable TX & RX DMA */
900e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CONTROL, 1);
901e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CONTROL, 1);
9028eda2d14Spatrick 
90332d55a3aScanacar 	/* Enable interrupt pacing for C0 RX/TX (IMAX set to max intr/ms allowed) */
90432d55a3aScanacar #define CPSW_VBUSP_CLK_MHZ	2400	/* hardcoded for BBB */
90532d55a3aScanacar 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_RX_IMAX(0), 2);
90632d55a3aScanacar 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_TX_IMAX(0), 2);
90732d55a3aScanacar 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_INT_CONTROL, 3 << 16 | CPSW_VBUSP_CLK_MHZ/4);
90832d55a3aScanacar 
9098eda2d14Spatrick 	/* Enable TX and RX interrupt receive for core 0 */
910e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_TX_EN(0), 1);
911e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_RX_EN(0), 1);
912e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_MISC_EN(0), 0x1F);
9138eda2d14Spatrick 
9148eda2d14Spatrick 	/* Enable host Error Interrupt */
915e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTMASK_SET, 2);
9168eda2d14Spatrick 
9178eda2d14Spatrick 	/* Enable interrupts for TX and RX Channel 0 */
918e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_INTMASK_SET, 1);
919e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_INTMASK_SET, 1);
9208eda2d14Spatrick 
9218eda2d14Spatrick 	/* Ack stalled irqs */
922e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_RXTH);
923e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_RX);
924e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_TX);
925e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_MISC);
9268eda2d14Spatrick 
92732d55a3aScanacar 	cpsw_mdio_init(sc);
9288eda2d14Spatrick 
9298eda2d14Spatrick 	mii_mediachg(mii);
9308eda2d14Spatrick 
9318eda2d14Spatrick 	/* Write channel 0 RX HDP */
932e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(0), cpsw_rxdesc_paddr(sc, 0));
9338eda2d14Spatrick 	sc->sc_rxrun = true;
9348eda2d14Spatrick 	sc->sc_rxeoq = false;
9358eda2d14Spatrick 
9368eda2d14Spatrick 	sc->sc_txrun = true;
9378eda2d14Spatrick 	sc->sc_txeoq = true;
9388eda2d14Spatrick 
9398eda2d14Spatrick 	ifp->if_flags |= IFF_RUNNING;
940de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
9418eda2d14Spatrick 
9428eda2d14Spatrick 	timeout_add_sec(&sc->sc_tick, 1);
9438eda2d14Spatrick 
9448eda2d14Spatrick 	return 0;
9458eda2d14Spatrick }
9468eda2d14Spatrick 
9478eda2d14Spatrick void
cpsw_stop(struct ifnet * ifp)9488eda2d14Spatrick cpsw_stop(struct ifnet *ifp)
9498eda2d14Spatrick {
9508eda2d14Spatrick 	struct cpsw_softc * const sc = ifp->if_softc;
9518eda2d14Spatrick 	struct cpsw_ring_data * const rdp = sc->sc_rdp;
9528eda2d14Spatrick 	u_int i;
9538eda2d14Spatrick 
9548eda2d14Spatrick #if 0
9558eda2d14Spatrick 	/* XXX find where disable comes from */
9568eda2d14Spatrick 	printf("%s: ifp %p disable %d\n", __func__, ifp, disable);
9578eda2d14Spatrick #endif
9588eda2d14Spatrick 	if ((ifp->if_flags & IFF_RUNNING) == 0)
9598eda2d14Spatrick 		return;
9608eda2d14Spatrick 
9618eda2d14Spatrick 	timeout_del(&sc->sc_tick);
9628eda2d14Spatrick 
9638eda2d14Spatrick 	mii_down(&sc->sc_mii);
9648eda2d14Spatrick 
965e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_INTMASK_CLEAR, 1);
966e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_INTMASK_CLEAR, 1);
967e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_TX_EN(0), 0x0);
968e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_RX_EN(0), 0x0);
969e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_MISC_EN(0), 0x1F);
9708eda2d14Spatrick 
971e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_TEARDOWN, 0);
972e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_TEARDOWN, 0);
9738eda2d14Spatrick 	i = 0;
9748eda2d14Spatrick 	while ((sc->sc_txrun || sc->sc_rxrun) && i < 10000) {
9758eda2d14Spatrick 		delay(10);
9768eda2d14Spatrick 		if ((sc->sc_txrun == true) && cpsw_txintr(sc) == 0)
9778eda2d14Spatrick 			sc->sc_txrun = false;
9788eda2d14Spatrick 		if ((sc->sc_rxrun == true) && cpsw_rxintr(sc) == 0)
9798eda2d14Spatrick 			sc->sc_rxrun = false;
9808eda2d14Spatrick 		i++;
9818eda2d14Spatrick 	}
9828eda2d14Spatrick 	/* printf("%s toredown complete in %u\n", __func__, i); */
9838eda2d14Spatrick 
9848eda2d14Spatrick 	/* Reset wrapper */
985e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET, 1);
986e85b21ccSjasper 	while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_SOFT_RESET) & 1);
9878eda2d14Spatrick 
9888eda2d14Spatrick 	/* Reset SS */
989e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET, 1);
990e85b21ccSjasper 	while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SS_SOFT_RESET) & 1);
9918eda2d14Spatrick 
9928eda2d14Spatrick 	for (i = 0; i < 2; i++) {
993e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i), 1);
994e85b21ccSjasper 		while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_SL_SOFT_RESET(i)) & 1);
9958eda2d14Spatrick 	}
9968eda2d14Spatrick 
9978eda2d14Spatrick 	/* Reset CPDMA */
998e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET, 1);
999e85b21ccSjasper 	while(bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_SOFT_RESET) & 1);
10008eda2d14Spatrick 
10018eda2d14Spatrick 	/* Release any queued transmit buffers. */
10028eda2d14Spatrick 	for (i = 0; i < CPSW_NTXDESCS; i++) {
10038eda2d14Spatrick 		bus_dmamap_unload(sc->sc_bdt, rdp->tx_dm[i]);
10048eda2d14Spatrick 		m_freem(rdp->tx_mb[i]);
10058eda2d14Spatrick 		rdp->tx_mb[i] = NULL;
10068eda2d14Spatrick 	}
10078eda2d14Spatrick 
1008834562faSmpi 	ifp->if_flags &= ~IFF_RUNNING;
10098eda2d14Spatrick 	ifp->if_timer = 0;
1010de6cd8fbSdlg 	ifq_clr_oactive(&ifp->if_snd);
10118eda2d14Spatrick 
10128eda2d14Spatrick 	/* XXX Not sure what this is doing calling disable here
10138eda2d14Spatrick 	    where is disable set?
10148eda2d14Spatrick 	*/
10158eda2d14Spatrick #if 0
10168eda2d14Spatrick 	if (!disable)
10178eda2d14Spatrick 		return;
10188eda2d14Spatrick #endif
10198eda2d14Spatrick 
10208eda2d14Spatrick 	for (i = 0; i < CPSW_NRXDESCS; i++) {
10218eda2d14Spatrick 		bus_dmamap_unload(sc->sc_bdt, rdp->rx_dm[i]);
10228eda2d14Spatrick 		m_freem(rdp->rx_mb[i]);
10238eda2d14Spatrick 		rdp->rx_mb[i] = NULL;
10248eda2d14Spatrick 	}
10258eda2d14Spatrick }
10268eda2d14Spatrick 
10278eda2d14Spatrick int
cpsw_rxthintr(void * arg)10288eda2d14Spatrick cpsw_rxthintr(void *arg)
10298eda2d14Spatrick {
10308eda2d14Spatrick 	struct cpsw_softc * const sc = arg;
10318eda2d14Spatrick 
10328eda2d14Spatrick 	/* this won't deassert the interrupt though */
1033e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_RXTH);
10348eda2d14Spatrick 
10358eda2d14Spatrick 	return 1;
10368eda2d14Spatrick }
10378eda2d14Spatrick 
10388eda2d14Spatrick int
cpsw_rxintr(void * arg)10398eda2d14Spatrick cpsw_rxintr(void *arg)
10408eda2d14Spatrick {
10418eda2d14Spatrick 	struct cpsw_softc * const sc = arg;
1042a484de46Sdlg 	struct ifnet * const ifp = &sc->sc_ac.ac_if;
10438eda2d14Spatrick 	struct cpsw_ring_data * const rdp = sc->sc_rdp;
10448eda2d14Spatrick 	struct cpsw_cpdma_bd bd;
10458eda2d14Spatrick 	bus_dmamap_t dm;
1046fc16ab7dSmpi 	struct mbuf_list ml = MBUF_LIST_INITIALIZER();
10478eda2d14Spatrick 	struct mbuf *m;
10488eda2d14Spatrick 	u_int i;
10498eda2d14Spatrick 	u_int len, off;
10508eda2d14Spatrick 
105140f05ebfScanacar 	sc->sc_rxeoq = false;
105240f05ebfScanacar 
10538eda2d14Spatrick 	for (;;) {
10548eda2d14Spatrick 		KASSERT(sc->sc_rxhead < CPSW_NRXDESCS);
10558eda2d14Spatrick 
10568eda2d14Spatrick 		i = sc->sc_rxhead;
10578eda2d14Spatrick 		dm = rdp->rx_dm[i];
10588eda2d14Spatrick 		m = rdp->rx_mb[i];
10598eda2d14Spatrick 
10608eda2d14Spatrick 		KASSERT(dm != NULL);
10618eda2d14Spatrick 		KASSERT(m != NULL);
10628eda2d14Spatrick 
10638eda2d14Spatrick 		cpsw_get_rxdesc(sc, i, &bd);
10648eda2d14Spatrick 
10651c1360f7Sfgsch 		if (bd.flags & CPDMA_BD_OWNER)
10668eda2d14Spatrick 			break;
10678eda2d14Spatrick 
10681c1360f7Sfgsch 		if (bd.flags & CPDMA_BD_TDOWNCMPLT) {
10698eda2d14Spatrick 			sc->sc_rxrun = false;
1070fc16ab7dSmpi 			goto done;
10718eda2d14Spatrick 		}
10728eda2d14Spatrick 
10738eda2d14Spatrick 		bus_dmamap_sync(sc->sc_bdt, dm, 0, dm->dm_mapsize,
10748eda2d14Spatrick 		    BUS_DMASYNC_POSTREAD);
10758eda2d14Spatrick 
10768eda2d14Spatrick 		if (cpsw_new_rxbuf(sc, i) != 0) {
10778eda2d14Spatrick 			/* drop current packet, reuse buffer for new */
10788eda2d14Spatrick 			ifp->if_ierrors++;
10798eda2d14Spatrick 			goto next;
10808eda2d14Spatrick 		}
10818eda2d14Spatrick 
108240f05ebfScanacar 		if ((bd.flags & (CPDMA_BD_SOP|CPDMA_BD_EOP)) !=
108340f05ebfScanacar 		    (CPDMA_BD_SOP|CPDMA_BD_EOP)) {
108440f05ebfScanacar 			if (bd.flags & CPDMA_BD_SOP) {
108540f05ebfScanacar 				printf("cpsw: rx packet too large\n");
108640f05ebfScanacar 				ifp->if_ierrors++;
108740f05ebfScanacar 			}
108840f05ebfScanacar 			m_freem(m);
108940f05ebfScanacar 			goto next;
109040f05ebfScanacar 		}
109140f05ebfScanacar 
10921c1360f7Sfgsch 		off = bd.bufoff;
10931c1360f7Sfgsch 		len = bd.pktlen;
10948eda2d14Spatrick 
10951c1360f7Sfgsch 		if (bd.flags & CPDMA_BD_PASSCRC)
10968eda2d14Spatrick 			len -= ETHER_CRC_LEN;
10978eda2d14Spatrick 
10988eda2d14Spatrick 		m->m_pkthdr.len = m->m_len = len;
10998eda2d14Spatrick 		m->m_data += off;
11008eda2d14Spatrick 
1101fc16ab7dSmpi 		ml_enqueue(&ml, m);
11028eda2d14Spatrick 
11038eda2d14Spatrick next:
11048eda2d14Spatrick 		sc->sc_rxhead = RXDESC_NEXT(sc->sc_rxhead);
11051c1360f7Sfgsch 		if (bd.flags & CPDMA_BD_EOQ) {
11068eda2d14Spatrick 			sc->sc_rxeoq = true;
110740f05ebfScanacar 			sc->sc_rxrun = false;
11088eda2d14Spatrick 		}
1109e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CP(0),
11108eda2d14Spatrick 		    cpsw_rxdesc_paddr(sc, i));
11118eda2d14Spatrick 	}
11128eda2d14Spatrick 
11138eda2d14Spatrick 	if (sc->sc_rxeoq) {
111440f05ebfScanacar 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(0),
111540f05ebfScanacar 				  cpsw_rxdesc_paddr(sc, sc->sc_rxhead));
111640f05ebfScanacar 		sc->sc_rxrun = true;
111740f05ebfScanacar 		sc->sc_rxeoq = false;
11188eda2d14Spatrick 	}
11198eda2d14Spatrick 
1120fc16ab7dSmpi 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR,
1121fc16ab7dSmpi 	    CPSW_INTROFF_RX);
1122fc16ab7dSmpi 
1123fc16ab7dSmpi done:
1124fc16ab7dSmpi 	if_input(ifp, &ml);
11258eda2d14Spatrick 
11268eda2d14Spatrick 	return 1;
11278eda2d14Spatrick }
11288eda2d14Spatrick 
11298eda2d14Spatrick void
cpsw_tick(void * arg)11308eda2d14Spatrick cpsw_tick(void *arg)
11318eda2d14Spatrick {
11328eda2d14Spatrick 	struct cpsw_softc *sc = arg;
11338eda2d14Spatrick 	int s;
11348eda2d14Spatrick 
11358eda2d14Spatrick 	s = splnet();
11368eda2d14Spatrick 	mii_tick(&sc->sc_mii);
11378eda2d14Spatrick 	splx(s);
11388eda2d14Spatrick 
11398eda2d14Spatrick 	timeout_add_sec(&sc->sc_tick, 1);
11408eda2d14Spatrick }
11418eda2d14Spatrick 
11428eda2d14Spatrick int
cpsw_txintr(void * arg)11438eda2d14Spatrick cpsw_txintr(void *arg)
11448eda2d14Spatrick {
11458eda2d14Spatrick 	struct cpsw_softc * const sc = arg;
1146a484de46Sdlg 	struct ifnet * const ifp = &sc->sc_ac.ac_if;
11478eda2d14Spatrick 	struct cpsw_ring_data * const rdp = sc->sc_rdp;
11488eda2d14Spatrick 	struct cpsw_cpdma_bd bd;
11498eda2d14Spatrick 	bool handled = false;
11508eda2d14Spatrick 	uint32_t tx0_cp;
11518eda2d14Spatrick 	u_int cpi;
11528eda2d14Spatrick 
11538eda2d14Spatrick 	KASSERT(sc->sc_txrun);
11548eda2d14Spatrick 
1155e85b21ccSjasper 	tx0_cp = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0));
11568eda2d14Spatrick 
11578eda2d14Spatrick 	if (tx0_cp == 0xfffffffc) {
1158e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0), 0xfffffffc);
1159e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0), 0);
11608eda2d14Spatrick 		sc->sc_txrun = false;
11618eda2d14Spatrick 		return 0;
11628eda2d14Spatrick 	}
11638eda2d14Spatrick 
11648eda2d14Spatrick 	for (;;) {
1165e85b21ccSjasper 		tx0_cp = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0));
11660d70ca2eSdlg 		cpi = (tx0_cp - sc->sc_txdescs_pa) /
11670d70ca2eSdlg 		    sizeof(struct cpsw_cpdma_bd);
11688eda2d14Spatrick 		KASSERT(sc->sc_txhead < CPSW_NTXDESCS);
11698eda2d14Spatrick 
11708eda2d14Spatrick 		cpsw_get_txdesc(sc, sc->sc_txhead, &bd);
11718eda2d14Spatrick 
11721c1360f7Sfgsch 		if (bd.buflen == 0) {
1173e97088d6Smpi 			/* db_enter(); */
11748eda2d14Spatrick 		}
11758eda2d14Spatrick 
11761c1360f7Sfgsch 		if ((bd.flags & CPDMA_BD_SOP) == 0)
11778eda2d14Spatrick 			goto next;
11788eda2d14Spatrick 
11791c1360f7Sfgsch 		if (bd.flags & CPDMA_BD_OWNER) {
11800d70ca2eSdlg 			printf("pwned %x %x %x\n", cpi, sc->sc_txhead,
11810d70ca2eSdlg 			    sc->sc_txnext);
11828eda2d14Spatrick 			break;
11838eda2d14Spatrick 		}
11848eda2d14Spatrick 
11851c1360f7Sfgsch 		if (bd.flags & CPDMA_BD_TDOWNCMPLT) {
11868eda2d14Spatrick 			sc->sc_txrun = false;
11878eda2d14Spatrick 			return 1;
11888eda2d14Spatrick 		}
11898eda2d14Spatrick 
11908eda2d14Spatrick 		bus_dmamap_sync(sc->sc_bdt, rdp->tx_dm[sc->sc_txhead],
11918eda2d14Spatrick 		    0, rdp->tx_dm[sc->sc_txhead]->dm_mapsize,
11928eda2d14Spatrick 		    BUS_DMASYNC_POSTWRITE);
11938eda2d14Spatrick 		bus_dmamap_unload(sc->sc_bdt, rdp->tx_dm[sc->sc_txhead]);
11948eda2d14Spatrick 
11958eda2d14Spatrick 		m_freem(rdp->tx_mb[sc->sc_txhead]);
11968eda2d14Spatrick 		rdp->tx_mb[sc->sc_txhead] = NULL;
11978eda2d14Spatrick 
11988eda2d14Spatrick 		handled = true;
11998eda2d14Spatrick 
1200de6cd8fbSdlg 		ifq_clr_oactive(&ifp->if_snd);
12018eda2d14Spatrick 
12028eda2d14Spatrick next:
12031c1360f7Sfgsch 		if ((bd.flags & (CPDMA_BD_EOP|CPDMA_BD_EOQ)) ==
12041c1360f7Sfgsch 		    (CPDMA_BD_EOP|CPDMA_BD_EOQ))
12058eda2d14Spatrick 			sc->sc_txeoq = true;
12061c1360f7Sfgsch 
12078eda2d14Spatrick 		if (sc->sc_txhead == cpi) {
1208e85b21ccSjasper 			bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0),
12098eda2d14Spatrick 			    cpsw_txdesc_paddr(sc, cpi));
12108eda2d14Spatrick 			sc->sc_txhead = TXDESC_NEXT(sc->sc_txhead);
12118eda2d14Spatrick 			break;
12128eda2d14Spatrick 		}
12138eda2d14Spatrick 		sc->sc_txhead = TXDESC_NEXT(sc->sc_txhead);
12141c1360f7Sfgsch 		if (sc->sc_txeoq == true)
12158eda2d14Spatrick 			break;
12168eda2d14Spatrick 	}
12178eda2d14Spatrick 
1218e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_TX);
12198eda2d14Spatrick 
12208eda2d14Spatrick 	if ((sc->sc_txnext != sc->sc_txhead) && sc->sc_txeoq) {
1221e85b21ccSjasper 		if (bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0)) == 0) {
12228eda2d14Spatrick 			sc->sc_txeoq = false;
1223e85b21ccSjasper 			bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0),
12248eda2d14Spatrick 			    cpsw_txdesc_paddr(sc, sc->sc_txhead));
12258eda2d14Spatrick 		}
12268eda2d14Spatrick 	}
12278eda2d14Spatrick 
12288eda2d14Spatrick 	if (handled && sc->sc_txnext == sc->sc_txhead)
12298eda2d14Spatrick 		ifp->if_timer = 0;
12308eda2d14Spatrick 
12318eda2d14Spatrick 	if (handled)
12328eda2d14Spatrick 		cpsw_start(ifp);
12338eda2d14Spatrick 
12348eda2d14Spatrick 	return handled;
12358eda2d14Spatrick }
12368eda2d14Spatrick 
12378eda2d14Spatrick int
cpsw_miscintr(void * arg)12388eda2d14Spatrick cpsw_miscintr(void *arg)
12398eda2d14Spatrick {
12408eda2d14Spatrick 	struct cpsw_softc * const sc = arg;
12418eda2d14Spatrick 	uint32_t miscstat;
12428eda2d14Spatrick 	uint32_t dmastat;
12438eda2d14Spatrick 	uint32_t stat;
12448eda2d14Spatrick 
1245e85b21ccSjasper 	miscstat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_WR_C_MISC_STAT(0));
12468eda2d14Spatrick 	printf("%s %x FIRE\n", __func__, miscstat);
12478eda2d14Spatrick 
12481c1360f7Sfgsch 	if (miscstat & CPSW_MISC_HOST_PEND) {
12498eda2d14Spatrick 		/* Host Error */
1250e85b21ccSjasper 		dmastat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTSTAT_MASKED);
12518eda2d14Spatrick 		printf("CPSW_CPDMA_DMA_INTSTAT_MASKED %x\n", dmastat);
12528eda2d14Spatrick 
12538eda2d14Spatrick 		printf("rxhead %02x\n", sc->sc_rxhead);
12548eda2d14Spatrick 
1255e85b21ccSjasper 		stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMASTATUS);
12568eda2d14Spatrick 		printf("CPSW_CPDMA_DMASTATUS %x\n", stat);
1257e85b21ccSjasper 		stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_HDP(0));
12588eda2d14Spatrick 		printf("CPSW_CPDMA_TX0_HDP %x\n", stat);
1259e85b21ccSjasper 		stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_TX_CP(0));
12608eda2d14Spatrick 		printf("CPSW_CPDMA_TX0_CP %x\n", stat);
1261e85b21ccSjasper 		stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_HDP(0));
12628eda2d14Spatrick 		printf("CPSW_CPDMA_RX0_HDP %x\n", stat);
1263e85b21ccSjasper 		stat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_RX_CP(0));
12648eda2d14Spatrick 		printf("CPSW_CPDMA_RX0_CP %x\n", stat);
12658eda2d14Spatrick 
1266e97088d6Smpi 		/* db_enter(); */
12678eda2d14Spatrick 
1268e85b21ccSjasper 		bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTMASK_CLEAR, dmastat);
1269e85b21ccSjasper 		dmastat = bus_space_read_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_DMA_INTSTAT_MASKED);
12708eda2d14Spatrick 		printf("CPSW_CPDMA_DMA_INTSTAT_MASKED %x\n", dmastat);
12718eda2d14Spatrick 	}
12728eda2d14Spatrick 
1273e85b21ccSjasper 	bus_space_write_4(sc->sc_bst, sc->sc_bsh, CPSW_CPDMA_CPDMA_EOI_VECTOR, CPSW_INTROFF_MISC);
12748eda2d14Spatrick 
12758eda2d14Spatrick 	return 1;
12768eda2d14Spatrick }
12779fa04b91Sjsg 
12789fa04b91Sjsg void
cpsw_get_port_config(struct cpsw_port_config * conf,int pnode)12799fa04b91Sjsg cpsw_get_port_config(struct cpsw_port_config *conf, int pnode)
12809fa04b91Sjsg {
12819fa04b91Sjsg 	char mode[32];
12829fa04b91Sjsg 	uint32_t phy_id[2];
128371d04594Sjsg 	int node, phy_handle, phy_node;
12849fa04b91Sjsg 	int port = 0;
12859fa04b91Sjsg 
12869fa04b91Sjsg 	for (node = OF_child(pnode); node; node = OF_peer(node)) {
12879fa04b91Sjsg 		if (OF_getprop(node, "local-mac-address", conf[port].enaddr,
12889fa04b91Sjsg 		    sizeof(conf[port].enaddr)) != sizeof(conf[port].enaddr))
12899fa04b91Sjsg 			continue;
12909fa04b91Sjsg 
12919fa04b91Sjsg 		conf[port].vlan = OF_getpropint(node, "dual_emac_res_vlan", 0);
12929fa04b91Sjsg 
12939fa04b91Sjsg 		if (OF_getpropintarray(node, "phy_id", phy_id,
12949fa04b91Sjsg 		    sizeof(phy_id)) == sizeof(phy_id))
12959fa04b91Sjsg 			conf[port].phy_id = phy_id[1];
129671d04594Sjsg 		else if ((phy_handle =
129771d04594Sjsg 		    OF_getpropint(node, "phy-handle", 0)) != 0) {
129871d04594Sjsg 			phy_node = OF_getnodebyphandle(phy_handle);
129971d04594Sjsg 			if (phy_node)
130071d04594Sjsg 				conf[port].phy_id = OF_getpropint(phy_node,
130171d04594Sjsg 				    "reg", MII_PHY_ANY);
130271d04594Sjsg 		}
13039fa04b91Sjsg 
13049fa04b91Sjsg 		if (OF_getprop(node, "phy-mode", mode, sizeof(mode)) > 0 &&
13059fa04b91Sjsg 		    !strcmp(mode, "rgmii"))
13069fa04b91Sjsg 			conf[port].rgmii = 1;
13079fa04b91Sjsg 		else
13089fa04b91Sjsg 			conf[port].rgmii = 0;
13099fa04b91Sjsg 
13109fa04b91Sjsg 		if (port == 0)
13119fa04b91Sjsg 			port = 1;
13129fa04b91Sjsg 	}
13139fa04b91Sjsg }
1314