Author: ian
Date: Mon Jul 14 20:58:57 2014
New Revision: 268633
URL: http://svnweb.freebsd.org/changeset/base/268633

Log:
  Fix the Zedboard/Zynq ethernet driver to handle media speed changes so
  that it can connect to switches at speeds other than 1gb.
  
  This requires changing the reference clock speed.  Since we still don't
  have a general clock API that lets a SoC-independant driver manipulate its
  own clocks, this change includes a weak reference to a routine named
  cgem_set_ref_clk().  The default implementation is a no-op; SoC-specific
  code can provide an implementation that actually changes the speed.
  
  Submitted by: Thomas Skibo <thomassk...@sbcglobal.net>

Modified:
  head/sys/arm/xilinx/zy7_slcr.c
  head/sys/arm/xilinx/zy7_slcr.h
  head/sys/boot/fdt/dts/arm/zedboard.dts
  head/sys/dev/cadence/if_cgem.c

Modified: head/sys/arm/xilinx/zy7_slcr.c
==============================================================================
--- head/sys/arm/xilinx/zy7_slcr.c      Mon Jul 14 20:58:02 2014        
(r268632)
+++ head/sys/arm/xilinx/zy7_slcr.c      Mon Jul 14 20:58:57 2014        
(r268633)
@@ -71,12 +71,14 @@ extern void (*zynq7_cpu_reset);
 #define        ZSLCR_UNLOCK(sc)                mtx_unlock(&(sc)->sc_mtx)
 #define ZSLCR_LOCK_INIT(sc) \
        mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \
-           "zy7_slcr", MTX_SPIN)
+           "zy7_slcr", MTX_DEF)
 #define ZSLCR_LOCK_DESTROY(_sc)        mtx_destroy(&_sc->sc_mtx);
 
 #define RD4(sc, off)           (bus_read_4((sc)->mem_res, (off)))
 #define WR4(sc, off, val)      (bus_write_4((sc)->mem_res, (off), (val)))
 
+#define ZYNQ_DEFAULT_PS_CLK_FREQUENCY  33333333        /* 33.3 Mhz */
+
 
 SYSCTL_NODE(_hw, OID_AUTO, zynq, CTLFLAG_RD, 0, "Xilinx Zynq-7000");
 
@@ -84,7 +86,7 @@ static char zynq_bootmode[64];
 SYSCTL_STRING(_hw_zynq, OID_AUTO, bootmode, CTLFLAG_RD, zynq_bootmode, 0,
              "Zynq boot mode");
 
-static char zynq_pssid[80];
+static char zynq_pssid[100];
 SYSCTL_STRING(_hw_zynq, OID_AUTO, pssid, CTLFLAG_RD, zynq_pssid, 0,
           "Zynq PSS IDCODE");
 
@@ -92,6 +94,22 @@ static uint32_t zynq_reboot_status;
 SYSCTL_INT(_hw_zynq, OID_AUTO, reboot_status, CTLFLAG_RD, &zynq_reboot_status,
           0, "Zynq REBOOT_STATUS register");
 
+static int ps_clk_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, ps_clk_frequency, CTLFLAG_RD, &ps_clk_frequency,
+          0, "Zynq PS_CLK Frequency");
+
+static int io_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, io_pll_frequency, CTLFLAG_RD, &io_pll_frequency,
+          0, "Zynq IO PLL Frequency");
+
+static int arm_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, arm_pll_frequency, CTLFLAG_RD,
+          &arm_pll_frequency, 0, "Zynq ARM PLL Frequency");
+
+static int ddr_pll_frequency;
+SYSCTL_INT(_hw_zynq, OID_AUTO, ddr_pll_frequency, CTLFLAG_RD,
+          &ddr_pll_frequency, 0, "Zynq DDR PLL Frequency");
+
 static void
 zy7_slcr_unlock(struct zy7_slcr_softc *sc)
 {
@@ -189,6 +207,54 @@ zy7_slcr_postload_pl(int en_level_shifte
        ZSLCR_UNLOCK(sc);
 }
 
+/* Override cgem_set_refclk() in gigabit ethernet driver
+ * (sys/dev/cadence/if_cgem.c).  This function is called to
+ * request a change in the gem's reference clock speed.
+ */
+int
+cgem_set_ref_clk(int unit, int frequency)
+{
+       struct zy7_slcr_softc *sc = zy7_slcr_softc_p;
+       int div0, div1;
+
+       if (!sc)
+               return (-1);
+
+       /* Find suitable divisor pairs.  Round result to nearest khz
+        * to test for match.
+        */
+       for (div1 = 1; div1 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX; div1++) {
+               div0 = (io_pll_frequency + div1 * frequency / 2) /
+                       div1 / frequency;
+               if (div0 > 0 && div0 <= ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX &&
+                   ((io_pll_frequency / div0 / div1) + 500) / 1000 ==
+                   (frequency + 500) / 1000)
+                       break;
+       }
+
+       if (div1 > ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX)
+               return (-1);
+
+       ZSLCR_LOCK(sc);
+
+       /* Unlock SLCR registers. */
+       zy7_slcr_unlock(sc);
+
+       /* Modify GEM reference clock. */
+       WR4(sc, unit ? ZY7_SLCR_GEM1_CLK_CTRL : ZY7_SLCR_GEM0_CLK_CTRL,
+           (div1 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT) |
+           (div0 << ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT) |
+           ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL |
+           ZY7_SLCR_GEM_CLK_CTRL_CLKACT);
+
+       /* Lock SLCR registers. */
+       zy7_slcr_lock(sc);
+
+       ZSLCR_UNLOCK(sc);
+
+       return (0);
+}
+
 static int
 zy7_slcr_probe(device_t dev)
 {
@@ -208,8 +274,13 @@ zy7_slcr_attach(device_t dev)
 {
        struct zy7_slcr_softc *sc = device_get_softc(dev);
        int rid;
+       phandle_t node;
+       pcell_t cell;
        uint32_t bootmode;
        uint32_t pss_idcode;
+       uint32_t arm_pll_ctrl;
+       uint32_t ddr_pll_ctrl;
+       uint32_t io_pll_ctrl;
        static char *bootdev_names[] = {
                "JTAG", "Quad-SPI", "NOR", "(3?)",
                "NAND", "SD Card", "(6?)", "(7?)"
@@ -260,6 +331,53 @@ zy7_slcr_attach(device_t dev)
 
        zynq_reboot_status = RD4(sc, ZY7_SLCR_REBOOT_STAT);
 
+       /* Derive PLL frequencies from PS_CLK. */
+       node = ofw_bus_get_node(dev);
+       if (OF_getprop(node, "clock-frequency", &cell, sizeof(cell)) > 0)
+               ps_clk_frequency = fdt32_to_cpu(cell);
+       else
+               ps_clk_frequency = ZYNQ_DEFAULT_PS_CLK_FREQUENCY;
+
+       arm_pll_ctrl = RD4(sc, ZY7_SLCR_ARM_PLL_CTRL);
+       ddr_pll_ctrl = RD4(sc, ZY7_SLCR_DDR_PLL_CTRL);
+       io_pll_ctrl = RD4(sc, ZY7_SLCR_IO_PLL_CTRL);
+
+       /* Determine ARM PLL frequency. */
+       if (((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+            (arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+           ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+            (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+               /* PLL is bypassed. */
+               arm_pll_frequency = ps_clk_frequency;
+       else
+               arm_pll_frequency = ps_clk_frequency *
+                       ((arm_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+                        ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+       /* Determine DDR PLL frequency. */
+       if (((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+            (ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+           ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+            (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+               /* PLL is bypassed. */
+               ddr_pll_frequency = ps_clk_frequency;
+       else
+               ddr_pll_frequency = ps_clk_frequency *
+                       ((ddr_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+                        ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
+       /* Determine IO PLL frequency. */
+       if (((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) == 0 &&
+            (io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_FORCE) != 0) ||
+           ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_BYPASS_QUAL) != 0 &&
+            (bootmode & ZY7_SLCR_BOOT_MODE_PLL_BYPASS) != 0))
+               /* PLL is bypassed. */
+               io_pll_frequency = ps_clk_frequency;
+       else
+               io_pll_frequency = ps_clk_frequency *
+                       ((io_pll_ctrl & ZY7_SLCR_PLL_CTRL_FDIV_MASK) >>
+                        ZY7_SLCR_PLL_CTRL_FDIV_SHIFT);
+
        /* Lock SLCR registers. */
        zy7_slcr_lock(sc);
 

Modified: head/sys/arm/xilinx/zy7_slcr.h
==============================================================================
--- head/sys/arm/xilinx/zy7_slcr.h      Mon Jul 14 20:58:02 2014        
(r268632)
+++ head/sys/arm/xilinx/zy7_slcr.h      Mon Jul 14 20:58:57 2014        
(r268633)
@@ -126,6 +126,18 @@
 #define ZY7_SLCR_GEM1_RCLK_CTRL                0x013c
 #define ZY7_SLCR_GEM0_CLK_CTRL         0x0140
 #define ZY7_SLCR_GEM1_CLK_CTRL         0x0144
+#define   ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MASK          (0x3f<<20)
+#define   ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_SHIFT         20
+#define   ZY7_SLCR_GEM_CLK_CTRL_DIVISOR1_MAX           0x3f
+#define   ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MASK           (0x3f<<8)
+#define   ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_SHIFT          8
+#define   ZY7_SLCR_GEM_CLK_CTRL_DIVISOR_MAX            0x3f
+#define   ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_MASK            (7<<4)
+#define   ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_IO_PLL          (0<<4)
+#define   ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_ARM_PLL         (2<<4)
+#define   ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_DDR_PLL         (3<<4)
+#define   ZY7_SLCR_GEM_CLK_CTRL_SRCSEL_EMIO_CLK                (4<<4)
+#define   ZY7_SLCR_GEM_CLK_CTRL_CLKACT                 1
 #define ZY7_SLCR_SMC_CLK_CTRL          0x0148
 #define ZY7_SLCR_LQSPI_CLK_CTRL                0x014c
 #define ZY7_SLCR_SDIO_CLK_CTRL         0x0150
@@ -274,6 +286,7 @@
 
 #ifdef _KERNEL
 extern void zy7_slcr_preload_pl(void);
-extern void zy7_slcr_postload_pl(int);
+extern void zy7_slcr_postload_pl(int en_level_shifters);
+extern int cgem_set_ref_clk(int unit, int frequency);
 #endif
 #endif /* _ZY7_SLCR_H_ */

Modified: head/sys/boot/fdt/dts/arm/zedboard.dts
==============================================================================
--- head/sys/boot/fdt/dts/arm/zedboard.dts      Mon Jul 14 20:58:02 2014        
(r268632)
+++ head/sys/boot/fdt/dts/arm/zedboard.dts      Mon Jul 14 20:58:57 2014        
(r268633)
@@ -63,6 +63,7 @@
                slcr: slcr@7000 {
                        compatible = "xlnx,zy7_slcr";
                        reg = <0x0 0x1000>;
+                       clock-frequency = <33333333>; // 33Mhz PS_CLK
                };
 
                // Interrupt controller
@@ -175,6 +176,7 @@
                        reg = <0xb000 0x1000>;
                        interrupts = <54 55>;
                        interrupt-parent = <&GIC>;
+                       ref-clock-num = <0>;
                };
 
                // SDIO

Modified: head/sys/dev/cadence/if_cgem.c
==============================================================================
--- head/sys/dev/cadence/if_cgem.c      Mon Jul 14 20:58:02 2014        
(r268632)
+++ head/sys/dev/cadence/if_cgem.c      Mon Jul 14 20:58:57 2014        
(r268633)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2012-2013 Thomas Skibo
+ * Copyright (c) 2012-2014 Thomas Skibo
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -108,6 +108,7 @@ struct cgem_softc {
        void                    *intrhand;
        struct callout          tick_ch;
        uint32_t                net_ctl_shadow;
+       int                     ref_clk_num;
        u_char                  eaddr[6];
 
        bus_dma_tag_t           desc_dma_tag;
@@ -149,6 +150,9 @@ struct cgem_softc {
 #define CGEM_LOCK_DESTROY(sc)  mtx_destroy(&(sc)->sc_mtx)
 #define CGEM_ASSERT_LOCKED(sc) mtx_assert(&(sc)->sc_mtx, MA_OWNED)
 
+/* Allow platforms to optionally provide a way to set the reference clock. */
+int cgem_set_ref_clk(int unit, int frequency);
+
 static devclass_t cgem_devclass;
 
 static int cgem_probe(device_t dev);
@@ -707,47 +711,18 @@ cgem_start(struct ifnet *ifp)
        CGEM_UNLOCK(sc);
 }
 
-/* Respond to changes in media. */
-static void
-cgem_media_update(struct cgem_softc *sc, int active)
-{
-       uint32_t net_cfg;
-
-       CGEM_ASSERT_LOCKED(sc);
-
-       /* Update hardware to reflect phy status. */
-       net_cfg = RD4(sc, CGEM_NET_CFG);
-       net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN |
-                    CGEM_NET_CFG_FULL_DUPLEX);
-
-       if (IFM_SUBTYPE(active) == IFM_1000_T)
-               net_cfg |= (CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN);
-       else if (IFM_SUBTYPE(active) == IFM_100_TX)
-               net_cfg |= CGEM_NET_CFG_SPEED100;
-
-       if ((active & IFM_FDX) != 0)
-               net_cfg |= CGEM_NET_CFG_FULL_DUPLEX;
-       WR4(sc, CGEM_NET_CFG, net_cfg);
-}
-
 static void
 cgem_tick(void *arg)
 {
        struct cgem_softc *sc = (struct cgem_softc *)arg;
        struct mii_data *mii;
-       int active;
 
        CGEM_ASSERT_LOCKED(sc);
 
        /* Poll the phy. */
        if (sc->miibus != NULL) {
                mii = device_get_softc(sc->miibus);
-               active = mii->mii_media_active;
                mii_tick(mii);
-               if ((mii->mii_media_status & (IFM_ACTIVE | IFM_AVALID)) ==
-                   (IFM_ACTIVE | IFM_AVALID) &&
-                   active != mii->mii_media_active)
-                       cgem_media_update(sc, mii->mii_media_active);
        }
 
        /* Next callout in one second. */
@@ -894,7 +869,6 @@ cgem_init_locked(struct cgem_softc *sc)
 
        mii = device_get_softc(sc->miibus);
        mii_pollstat(mii);
-       cgem_media_update(sc, mii->mii_media_active);
        cgem_start_locked(sc->ifp);
 
        callout_reset(&sc->tick_ch, hz, cgem_tick, sc);
@@ -1073,12 +1047,13 @@ cgem_ifmedia_upd(struct ifnet *ifp)
 {
        struct cgem_softc *sc = (struct cgem_softc *) ifp->if_softc;
        struct mii_data *mii;
+       int error;
 
        mii = device_get_softc(sc->miibus);
        CGEM_LOCK(sc);
-       mii_mediachg(mii);
+       error = mii_mediachg(mii);
        CGEM_UNLOCK(sc);
-       return (0);
+       return (error);
 }
 
 static void
@@ -1148,6 +1123,60 @@ cgem_miibus_writereg(device_t dev, int p
        return (0);
 }
 
+/*
+ * Overridable weak symbol cgem_set_ref_clk().  This allows platforms to
+ * provide a function to set the cgem's reference clock.
+ */
+static int __used
+cgem_default_set_ref_clk(int unit, int frequency)
+{
+
+       return 0;
+}
+__weak_reference(cgem_default_set_ref_clk, cgem_set_ref_clk);
+
+static void
+cgem_miibus_statchg(device_t dev)
+{
+       struct cgem_softc *sc;
+       struct mii_data *mii;
+       uint32_t net_cfg;
+       int ref_clk_freq;
+
+       sc  = device_get_softc(dev);
+
+       mii = device_get_softc(sc->miibus);
+
+       if ((mii->mii_media_status & IFM_AVALID) != 0) {
+               /* Update hardware to reflect phy status. */
+               net_cfg = RD4(sc, CGEM_NET_CFG);
+               net_cfg &= ~(CGEM_NET_CFG_SPEED100 | CGEM_NET_CFG_GIGE_EN |
+                            CGEM_NET_CFG_FULL_DUPLEX);
+
+               switch (IFM_SUBTYPE(mii->mii_media_active)) {
+               case IFM_1000_T:
+                       net_cfg |= (CGEM_NET_CFG_SPEED100 |
+                                   CGEM_NET_CFG_GIGE_EN);
+                       ref_clk_freq = 125000000;
+                       break;
+               case IFM_100_TX:
+                       net_cfg |= CGEM_NET_CFG_SPEED100;
+                       ref_clk_freq = 25000000;
+                       break;
+               default:
+                       ref_clk_freq = 2500000;
+               }
+
+               if ((mii->mii_media_active & IFM_FDX) != 0)
+                       net_cfg |= CGEM_NET_CFG_FULL_DUPLEX;
+               WR4(sc, CGEM_NET_CFG, net_cfg);
+
+               /* Set the reference clock if necessary. */
+               if (cgem_set_ref_clk(sc->ref_clk_num, ref_clk_freq))
+                       device_printf(dev, "could not set ref clk%d to %d.\n",
+                                     sc->ref_clk_num, ref_clk_freq);
+       }
+}
 
 static int
 cgem_probe(device_t dev)
@@ -1165,12 +1194,20 @@ cgem_attach(device_t dev)
 {
        struct cgem_softc *sc = device_get_softc(dev);
        struct ifnet *ifp = NULL;
+       phandle_t node;
+       pcell_t cell;
        int rid, err;
        u_char eaddr[ETHER_ADDR_LEN];
 
        sc->dev = dev;
        CGEM_LOCK_INIT(sc);
 
+       /* Get reference clock number and base divider from fdt. */
+       node = ofw_bus_get_node(dev);
+       sc->ref_clk_num = 0;
+       if (OF_getprop(node, "ref-clock-num", &cell, sizeof(cell)) > 0)
+               sc->ref_clk_num = fdt32_to_cpu(cell);
+
        /* Get memory resource. */
        rid = 0;
        sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
@@ -1372,6 +1409,7 @@ static device_method_t cgem_methods[] = 
        /* MII interface */
        DEVMETHOD(miibus_readreg,       cgem_miibus_readreg),
        DEVMETHOD(miibus_writereg,      cgem_miibus_writereg),
+       DEVMETHOD(miibus_statchg,       cgem_miibus_statchg),
 
        DEVMETHOD_END
 };
_______________________________________________
svn-src-all@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to