This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Linux/AM3359: DTS settings for two different Ethernet PHYs

Part Number: AM3359

Tool/software: Linux

Hi all,

                 We are working on a custom board based on am335x icev2.We have two ethernet phys connected to the SoC.I was able to successfully configure dp83867 alone.If I want to use both dp83867 and max24288 at the same time how should i configure in the dts file.

This is my pinmux for cpsw:

        cpsw_default: cpsw_default {
                pinctrl-single,pins = <
                                /* Slave 1 */
                AM33XX_IOPAD(0x914, PIN_OUTPUT_PULLDOWN | MUX_MODE2)    /* mii1_txen.rgmii1_tctl */
                AM33XX_IOPAD(0x918, PIN_INPUT_PULLDOWN | MUX_MODE2)     /* mii1_rxdv.rgmii1_rctl */
                AM33XX_IOPAD(0x91c, PIN_OUTPUT_PULLDOWN | MUX_MODE2)    /* mii1_txd3.rgmii1_td3 */
                AM33XX_IOPAD(0x920, PIN_OUTPUT_PULLDOWN | MUX_MODE2)    /* mii1_txd2.rgmii1_td2 */
                AM33XX_IOPAD(0x924, PIN_OUTPUT_PULLDOWN | MUX_MODE2)    /* mii1_txd1.rgmii1_td1 */
                AM33XX_IOPAD(0x928, PIN_OUTPUT_PULLDOWN | MUX_MODE2)    /* mii1_txd0.rgmii1_td0 */                     

                AM33XX_IOPAD(0x92c, PIN_OUTPUT_PULLDOWN | MUX_MODE2)    /* mii1_txclk.rgmii1_tclk */
                AM33XX_IOPAD(0x930, PIN_INPUT_PULLDOWN | MUX_MODE2)     /* mii1_rxclk.rgmii1_rclk */
                AM33XX_IOPAD(0x934, PIN_INPUT_PULLDOWN | MUX_MODE2)     /* mii1_rxd3.rgmii1_rd3 */
                AM33XX_IOPAD(0x938, PIN_INPUT_PULLDOWN | MUX_MODE2)     /* mii1_rxd2.rgmii1_rd2 */
                AM33XX_IOPAD(0x93c, PIN_INPUT_PULLDOWN | MUX_MODE2)     /* mii1_rxd1.rgmii1_rd1 */
                AM33XX_IOPAD(0x940, PIN_INPUT_PULLDOWN | MUX_MODE2)     /* mii1_rxd0.rgmii1_rd0 */
                AM33XX_IOPAD(0xA34, PIN_OUTPUT_PULLUP | MUX_MODE7) /* (F15) USB1_DRVVBUS.gpio3[13] */
                
                /*pinmux for max24288*/                 
                AM33XX_IOPAD(0x840, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (R13) gpmc_a0.rgmii2_tctl */
                AM33XX_IOPAD(0x844, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (V14) gpmc_a1.rgmii2_rctl */
                AM33XX_IOPAD(0x848, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (U14) gpmc_a2.rgmii2_td3 */
                AM33XX_IOPAD(0x84C, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (T14) gpmc_a3.rgmii2_td2 */
                AM33XX_IOPAD(0x850, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (R14) gpmc_a4.rgmii2_td1 */
                AM33XX_IOPAD(0x854, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (V15) gpmc_a5.rgmii2_td0 */
                AM33XX_IOPAD(0x858, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (U15) gpmc_a6.rgmii2_tclk */
                AM33XX_IOPAD(0x85C, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (T15) gpmc_a7.rgmii2_rclk */
                AM33XX_IOPAD(0x860, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (V16) gpmc_a8.rgmii2_rd3 */
                AM33XX_IOPAD(0x864, (PIN_INPUT_PULLDOWN | MUX_MODE2) ) /* (U16) gpmc_a9.rgmii2_rd2 */
                AM33XX_IOPAD(0x868, PIN_OUTPUT_PULLDOWN | MUX_MODE2)   /* (T16) gpmc_a10.rgmii2_rd1 */
                AM33XX_IOPAD(0x86C, PIN_OUTPUT_PULLDOWN | MUX_MODE2) /* (V17) gpmc_a11.rgmii2_rd0 */

        >;
        };

This the dts definition:

&mac {
    pinctrl-names = "default", "sleep";
    pinctrl-0 = <&cpsw_default>;
    pinctrl-1 = <&cpsw_sleep>;
    status = "okay";
};

&davinci_mdio {
    pinctrl-names = "default", "sleep";
    compatible = "ti,cpsw-mdio", "ti,davinci_mdio";
    pinctrl-0 = <&davinci_mdio_default>;
    pinctrl-1 = <&davinci_mdio_sleep>;
    status = "okay";

    dp83867_0: ethernet-phy@0 {
        reg = <0>;
        ti,rx-internal-delay = <DP83867_RGMIIDCTL_4_00_NS>;
        ti,tx-internal-delay = <DP83867_RGMIIDCTL_4_00_NS>;
        ti,fifo-depth = <DP83867_PHYCR_FIFO_DEPTH_8_B_NIB>;
        ti,min-output-impedance;
        ti,dp83867-rxctrl-strap-quirk;
    };
    max24287 : ethernet-phy@1{
        reg = <1>;
    };


};

&cpsw_emac0 {
    phy_id = <&davinci_mdio>, <0>;
    phy-mode = "rgmii-id";
};



&cpsw_emac1 {
    phy_id = <&davinci_mdio>, <0>;
    phy-mode = "rgmii-id";

};


Is this configuration correct?

Regards,

Murugan S

  • Hi,

    In the cpsw_emac1 node you need to change the PHY address from 0 to 1.

    &cpsw_emac1 {
        phy_id = <&davinci_mdio>, <0>;   <--- needs to be a 1 based on the max24287
        phy-mode = "rgmii-id";

    };

    I can't really comment much else since the device tree node specifics for the max24287 would need to be answered by the device vendor. The device vendor should provide a bindings document for the device that talks about specific properties needed for the device driver. An example would be something like already in the kernel tree, bindings/net/mscc-phy-vsc8531.txt.

    This node from above may need a compatible property defined in it so the kernel knows which driver to use with this PHY address. As mentioned above these hopefully would be defined in a binding document provided by the vendor specific to this device.

    max24287 : ethernet-phy@1{
            reg = <1>;
        };

    Best Regards,

    Schuyler

  • Hi Schuyler,

                             Thanks for the input.We contaced the vendor and got one driver.But it doesnt seem to have "compatible" field.For dp83867 also there is no compatible field.How do the ethernet drivers know which phy to probe and at which address without this property?Is is possible to hard code it in the driver itself?

     Im attaching max24288 driver for reference:

    /*
     * Framework for finding and configuring MAX24287 PHY device.
     *
     * Author: Boris-Ben Shapiro
     *
     * @ 01.04.15	-	revision 1
     * TODO: read GPIOS configuration from DT or platform data
     * TODO: read interface mode (RGMII/MII/GMII) from DT or platform data
     * TODO: read interrupts/poll configuration from DT or platform data
     *
     * This program is free software; you can redistribute  it and/or modify it
     * under  the terms of  the GNU General  Public License as published by the
     * Free Software Foundation;  either version 2 of the  License, or (at your
     * option) any later version.
     *
     */
    #undef BASEX_IN_PHYDEVICE
    
    #include <linux/kernel.h>
    #include <linux/string.h>
    #include <linux/errno.h>
    #include <linux/unistd.h>
    #include <linux/init.h>
    #include <linux/mm.h>
    #include <linux/module.h>
    #include <linux/mii.h>
    #include <linux/phy.h>
    #include <linux/i2c.h>
    #include <linux/gpio.h>
    #include <linux/delay.h>
    #include <linux/ethtool.h>
    #include <linux/netdevice.h>
    #include <linux/net_switch_config.h>
    
    #define PHY_PAGE0	   0
    #define PHY_PAGE1	   1
    #define PHY_PAGE2	   2
    #define PHY_PAGE3	   3
    /*added by murugan.s@fossilshale.com*/
    #if 0
    #define MII_PCSCTRL 0x11
    #define MII_EXT_PAGE 0x1F 
    #define MII_GMIICR 0x12
    #define MII_PTPCR1 0x50
    #endif
    
    #define MII_PCSCTRL 0x11
    #define MII_EXT_PAGE 0x1F 
    #define MII_GMIICR 0x12
    #define MII_PTPCR1 0x10
    
    #define PHY_BASEX_FEATURES (SUPPORTED_1000baseT_Full | SUPPORTED_FIBRE | SUPPORTED_Pause)
    #define DEVICE_NAME					 "max24287"
    #define PAGEBIT_MASK					 0x3	/* Mask used when opening pages */
    #define MAX24287_REALID_REG				0x10	/* phy id is stored there */
    #define MAX24287_REALID_PAGE				PHY_PAGE1	/* phy id is stored there */
    #define REALID_ID_MASK					0x0FFF
    #define REALID_REV_MASK					0xF000
    #define REALID_REV_OFFSET				12
    #define MAX24287_ID_REV_A				0xedf
    #define MAX24287_ID_REV_B				0xee0
    #define MAX24287_BMCR_SOFT_RESET			0x8000	/* soft reset bit */
    
    
    #define MAX24287_MINPAGE				PHY_PAGE0
    #define MAX24287_MAXPAGE				PHY_PAGE3
    
    #define MAX24287_TOTAL_IO		5
    #define IO_REG_PAGE			1
    #define IO_MASK		   		0x7		/* value mask */
    #define IO_REG1				18		/* GPIOS */
    #define IO_REG2				17		/* GPO + GPIOS */
    /* gpio registers values */
    #define GPVAL_HI				0x0
    #define GPVAL_LOGIC0			0x1
    #define GPVAL_LOGIC1			0x2
    #define GPVAL_IROUT			 0x3
    #define GPVAL_125OUT			0x4
    #define GPVAL_PLLOUT			0x5
    #define GPVAL_LINKST			0x6
    #define GPVAL_OUTCRS			GPVAL_LINKST
    
    #define PCSCR_BASEX_BIT			 (0x0010)
    #define GMIICR_SPD1000		  (1 << 15)
    #define GMIICR_SPD100		   (1 << 14)
    #define GMII_CTRL_SPD_10		~(0xc000)
    #define GMII_CTRL_SPD_100	   0x4000
    #define GMII_CTRL_SPD_1000	  0x8000
    #define GMII_CTRL_TBI		   0xc000
    #define GMII_CTRL_DTE_DCE	   0x1000
    #define GMII_CTRL_DDR		   0x800
    #define GMII_CTRL_TXCLKEN	   0x400
    #define GMII_CTRL_REFCLK_INV	0x8
    #define GMII_CTRL_RLB		   0x1
    
    /* LPA */
    #define LPAX_LINK_FAIL		  0x1000
    #define LPAX_OFF_LINE		   0x2000
    #define LPAX_ANEG_ERR		   (LPAX_LINK_FAIL | LPAX_OFF_LINE)
    
    /* pcs control register basex/sgmii control */
    #define PCSCTRL_BASEX_COMMA_MASK		0x11
    #define PCSCTRL_CFG_BASEX			   0x11
    #define PCSCTRL_CFG_SGMII			   0x1
    
    /* Advertisment basex/sgmii defines */
    #define MAX24287_DEFAULT_BASEX_ADV		0xA0
    #define MAX24287_DEFAULT_SGMII_ADV		0x1
    #define MAX24287_LPA_SGMII_LKACK		  0x8C00
    
    #define ANRX_BASEX_OFF_LINE			 (1 << 13)
    #define ANRX_BASEX_LINK_FAIL			(1 << 12)
    #define ANRX_BASEX_RF   (ANRX_BASEX_OFF_LINE | ANRX_BASEX_LINK_FAIL)
    
    /* autonegotiation link bit interpretation per mode */
    #define ANRX_SGMII_LINK				0x0020
    #define ANRX_BASEX_LINK				0x8000
    
    #define MAX24287_BASEX_LPAFULL			0x4020
    #define MAX24287_SGMII_LPA1000			0x800
    #define MAX24287_SGMII_LPA100			 0x400
    #define MAX24287_SGMII_FDX				0x1000
    
    /* Default interrupt mask: RFAULT + ALOS + RLOL + RLOS */
    #define MAX24287_IR					 (0x14)	/* Interrupt register */
    #define IR_MASK						 0x2B00
    
    static int max24287_config_advert(struct phy_device *);
    static int max24287_config_aneg(struct phy_device *);
    static int max24287_config_init(struct phy_device *phydev);
    static int max24287_rev_b_reset(struct phy_device *);
    
    static inline int max24287_soft_reset(struct phy_device *phydev)
    {
    	int _val;
    
    	_val = phy_read(phydev, MII_BMCR);
    	if (_val < 0)
    		return _val;
    	return phy_write(phydev, MII_BMCR, _val | 0x8000);
    }
    
    /* isbasex
     * Description: MII_PCSCTRL holds the current status of the timer (1000baseX/SGMII)
     * PHYLINK library handles writing the appropriate value to it.
     * it is possible to override it from sysfs using conf_basex file
     */
    static inline int isbasex(struct phy_device *phydev)
    {
    	int pcs = phy_read(phydev, MII_PCSCTRL);
    
    	return (pcs < 0 ? pcs : !!(pcs & PCSCR_BASEX_BIT));
    }
    
    static int max24287_open_page(struct phy_device *phydev, int page)
    {
    	int ret;
    	int regval, oldpage;
    
    	if (page > MAX24287_MAXPAGE || page < MAX24287_MINPAGE)
    		return -EINVAL;
    
    	ret = phy_read(phydev, MII_EXT_PAGE);
    	if (ret < 0)
    		return -ENXIO;
    
    	regval = oldpage = (ret & PAGEBIT_MASK);
    
    	if (oldpage == page)
    		return page;
    
    	regval |= (1 << 4);	/* This bit is required by HW */
    	regval &= ~PAGEBIT_MASK;
    	regval |= page;
    	ret = phy_write(phydev, MII_EXT_PAGE, regval);
    	if (ret < 0)
    		return -ENXIO;
    
    	return oldpage;
    }
    
    static int max24287_set_gpios(struct phy_device *phydev, u8 page, u16 reg,unsigned char bit, u8 configuration)
    {
    	int err, val, oldpage;
    
    	if (bit > 13)
    		return -EOVERFLOW;
    	if (configuration & ~(IO_MASK))
    		return -EINVAL;
    
    	oldpage = max24287_open_page(phydev, page);
    	if (oldpage < 0)
    		return oldpage;
    
    	val = phy_read(phydev, reg);
    	if (val < 0) {
    		max24287_open_page(phydev, oldpage);
    		return val;
    	}
    
    	val &= ~(IO_MASK << bit);
    	val |= (configuration << bit);
    
    	err = phy_write(phydev, reg, val);
    	if (err < 0) {
    		dev_crit(&phydev->mdio.dev, "Error %d writing new GPIO configuration 0x%04x to 0x%02xn", err, val , reg);
    		max24287_open_page(phydev, oldpage);
    		return err;
    	}
    	dev_dbg(&phydev->mdio.dev, "%s wrote new GPIO configuration 0x%04x to %d\n", DEVICE_NAME, val, reg);
    
    	err = max24287_open_page(phydev, oldpage);
    	if (err < 0)
    		return err;
    
    	return 0;
    }
    
    static int max24287_set_basex(struct phy_device *phydev, bool basex)
    {
    	int pcs;
    
    	if (basex == isbasex(phydev))
    		return 0;
    
    	pcs = phy_read(phydev, MII_PCSCTRL);
    	if (pcs < 0)
    		return pcs;
    
    	if (basex) {
    		phydev->supported = PHY_BASEX_FEATURES;
    		phy_write(phydev, MII_ADVERTISE, MAX24287_DEFAULT_BASEX_ADV);
    		pcs |= PCSCR_BASEX_BIT;
    	} else {
    		phydev->supported = PHY_GBIT_FEATURES;
    		phy_write(phydev, MII_ADVERTISE, MAX24287_DEFAULT_SGMII_ADV);
    		/* XXX: TODO: we support only slave SGMII autoneg, add option for master */
    		pcs &= ~(PCSCR_BASEX_BIT);
    	}
    	phydev->advertising &= phydev->supported;
    
    	pcs = phy_write(phydev, MII_PCSCTRL, pcs);
    	if (pcs < 0) {
    		max24287_set_basex(phydev, !basex);	/* Switch to corrent autoneg */
    		return pcs;
    	}
    
    	return genphy_restart_aneg(phydev);
    }
    
    static ssize_t max24287_sysfs_set_basex(struct device *dev,
    	struct device_attribute *attr, const char *buf, size_t count)
    {
    	struct phy_device *phyd = to_phy_device(dev);
    	int val = simple_strtol(buf, NULL, 10);
    	int res;
    
    	if (val > 1 || val < 0)
    		return -EINVAL;
    	dev_info(&phyd->mdio.dev, "Basex is %d, setting to %d\n", (int)isbasex(phyd), val);
    
    	res = max24287_set_basex(phyd, val);
    	if (res == 0)
    		return count;
    	else
    		return -EAGAIN;
    }
    
    static ssize_t max24287_sysfs_get_basex(struct device *dev,
    	struct device_attribute *attr, char *buf)
    {
    	struct phy_device *phyd = to_phy_device(dev);
    	int val = (isbasex(phyd) == true) ? 1 : 0;
    
    	return sprintf(buf, "%01d\n", val);
    }
    static DEVICE_ATTR(conf_basex, 0644, max24287_sysfs_get_basex, max24287_sysfs_set_basex);
    
    static int max24287_config_intr(struct phy_device *phydev)
    {
    	int	 val;
    	int	ret;
    
    	max24287_open_page(phydev, 0);
    	val = phy_read(phydev, MAX24287_IR);
    	if (val < 0)
    		return val;
    
    	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
    		val |= IR_MASK;
    	else
    		val &= ~(IR_MASK);
    
    	ret = phy_write(phydev, MAX24287_IR, val);
    	return ret;
    }
    
    static int max24287_ack_intr(struct phy_device *phydev)
    {
    	int	 val;
    
    	val = phy_read(phydev, MII_EXT_PAGE);
    	if (val < 0)
    		return val;
    
    	return 0;
    }
    
    /* max24287_gmiicr_set
     * Description: Sets rx clock speed. see datasheet, register description GMIICR
     */
    static int max24287_gmiicr_set(struct phy_device *phydev, int speed)
    {
    	int	gmiicr;
    
    	gmiicr = phy_read(phydev, MII_GMIICR);
    	if (gmiicr < 0)
    		return gmiicr;
    
    	gmiicr &= ~(GMIICR_SPD100 | GMIICR_SPD1000);
    
    	switch (speed) {
    	case SPEED_1000:
    		gmiicr |= GMIICR_SPD1000;
    		break;
    	case SPEED_100:
    		gmiicr |= GMIICR_SPD100;
    		break;
    	case SPEED_10:
    		break;
    	default:
    		dev_err(&phydev->mdio.dev, "%s unknown speed %d\n", DEVICE_NAME, speed);
    		return -EINVAL;
    	}
    	return phy_write(phydev, MII_GMIICR, gmiicr);
    }
    
    /* detect_basex
     * Description: If autodetect is chosen (no BASEX_IN_PHYDEVICE)
     * then we can only guess that we are working at a wrong mode by seeing reserved bits set
     * that has meaning for the other mode only. In that case try to revert the mode
     *
     * bool *r_basex - value/result storage
     */
    static inline int detect_basex(struct phy_device *phydev, bool *r_basex, int lpa)
    {
    	#ifndef BASEX_IN_PHYDEVICE
    	bool basex = r_basex;
    	int err;
    
    	/* Autodetection in case that PHYLINK cannot set basex */
    	if (basex == true && ((lpa & MAX24287_LPA_SGMII_LKACK) > 0x8000)) {
    		dev_info(&phydev->mdio.dev, "%s Detected SGMII status. but link timer is for Base-X. Changing (0x%04x)\n", DEVICE_NAME, lpa);
    		err = max24287_set_basex(phydev, false);
    		if (err < 0)
    			return err;
    		else
    			basex = false;
    	} else if (basex == false &&
    				((lpa & MAX24287_BASEX_LPAFULL) == MAX24287_BASEX_LPAFULL)) {
    		dev_info(&phydev->mdio.dev, "%s Detected 1000BASE-X status. but link timer is for SGMII. Changing (0x%04x)\n", DEVICE_NAME, lpa);
    		err = max24287_set_basex(phydev, true);
    		if (err < 0)
    			return err;
    		else
    			basex = true;
    	}
    	*r_basex = basex;
    	return 0;
    	#else
    	return 0;
    	#endif
    }
    
    static int _max24287_do_aneg_basex(struct phy_device *phydev, int lpa)
    {
    	int gmiicr;
    	/* NOTE: we do not handle symmetric pause */
    
    	if (!((phydev->supported & SUPPORTED_1000baseT_Full) &&
    				(phydev->advertising & ADVERTISED_1000baseT_Full))) {
    		dev_err(&phydev->mdio.dev, "1000BASE-T not supported in 1000basex mode\n");
    		return 1;
    	}
    	/* Handle Remote fault first */
    	if ((lpa & ANRX_BASEX_RF) == ANRX_BASEX_OFF_LINE) {
    		dev_err(&phydev->mdio.dev, "%s 0x%02x: Partner off-line!\n", DEVICE_NAME, lpa);
    		return 1;
    	} else if ((lpa & ANRX_BASEX_RF) == ANRX_BASEX_LINK_FAIL) {
    		dev_err(&phydev->mdio.dev, "%s: 0x%02x: Link Failure\n", DEVICE_NAME, lpa);
    		return 1;
    	} else if ((lpa & ANRX_BASEX_RF) ==  ANRX_BASEX_RF) {
    		dev_err(&phydev->mdio.dev, "%s: 0x%02x: Auto-Negotiation Error\n", DEVICE_NAME, lpa);
    		if (phydev->supported & SUPPORTED_Pause) {
    			dev_err(&phydev->mdio.dev, "%s: Pause supported. Toggling!\n", DEVICE_NAME);
    			if (phydev->advertising & ADVERTISED_Pause)
    				phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
    			else
    				phydev->advertising |= ADVERTISED_Pause;
    		}
    		return 1;
    	}
    		/* Handle pause capability recieved from link partner */
    	if (phydev->supported & SUPPORTED_Pause) {
    		if (lpa & LPA_1000XPAUSE)
    			phydev->advertising |= ADVERTISED_Pause;
    		else
    			phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
    	} else {
    		phydev->advertising &= ~(ADVERTISED_Pause | ADVERTISED_Asym_Pause);
    	}
    	phydev->pause = !!(phydev->advertising & ADVERTISED_Pause);
    	phydev->asym_pause = 0;		/* max24287 don't support this */
    
    	gmiicr = lpa & LPA_1000XFULL;
    	if (gmiicr) {
    		phydev->advertising |= ADVERTISED_1000baseT_Full;
    		phydev->speed = SPEED_1000;
    		phydev->duplex = DUPLEX_FULL;
    		gmiicr = max24287_gmiicr_set(phydev, SPEED_1000);
    		if (gmiicr != 0)
    			return 1;
    	}
    	return 0;
    }
    
    static int _max24287_do_aneg_sgmii(struct phy_device *phydev, int lpa)
    {
    	int gmiicr;
    
    	if (lpa & MAX24287_SGMII_LPA1000) {
    		if (((phydev->supported & SUPPORTED_1000baseT_Full) &&
    			 (phydev->advertising & ADVERTISED_1000baseT_Full)) ||
    				((phydev->supported & SUPPORTED_1000baseT_Half) &&
    				 (phydev->advertising & ADVERTISED_1000baseT_Half))) {
    					phydev->speed = SPEED_1000;
    					gmiicr = max24287_gmiicr_set(phydev, SPEED_1000);
    		} else {
    			dev_err(&phydev->mdio.dev, "1000BASET not supported. 0x%08x\n", lpa);
    			return 1;
    		}
    	} else if (lpa & MAX24287_SGMII_LPA100) {
    		if (((phydev->supported & SUPPORTED_100baseT_Full) &&
    			 (phydev->advertising & ADVERTISED_100baseT_Full)) ||
    				((phydev->supported & SUPPORTED_100baseT_Half) &&
    				 (phydev->advertising & ADVERTISED_100baseT_Half))) {
    			phydev->speed = SPEED_100;
    			gmiicr = max24287_gmiicr_set(phydev, SPEED_100);
    		} else {
    			dev_err(&phydev->mdio.dev, "100BASET not supported. 0x%08x\n", lpa);
    			return 1;
    		}
    	} else {
    		phydev->speed = SPEED_10;
    		gmiicr = max24287_gmiicr_set(phydev, SPEED_10);
    	}
    
    	if (lpa & MAX24287_SGMII_FDX) {
    		if (((phydev->supported & SUPPORTED_100baseT_Full) &&
    			 (phydev->advertising & ADVERTISED_100baseT_Full)) ||
    				((phydev->supported & SUPPORTED_1000baseT_Full) &&
    				 (phydev->advertising & ADVERTISED_1000baseT_Full)))
    		phydev->duplex = DUPLEX_FULL;
    	}
    
    	return 0;
    }
    
    /* max24287_update_link
     * The link on the SGMII side appears as different bits in SGMII or 1000baseX
     * advertisement register
     */
    static int max24287_update_link(struct phy_device *phydev)
    {
    	int lpa, basex = isbasex(phydev);
    
    	lpa = genphy_update_link(phydev);
    	if (lpa < 0) {
    		return lpa;
    	} else if (phydev->link == false) {
    		dev_err(&phydev->mdio.dev, "%s: NO LINK on RGMII side. check switch configuration and link\n", DEVICE_NAME);
    		return 1;
    	}
    
    	lpa = phy_read(phydev, MII_LPA);
    	if (lpa < 0)
    		return lpa;
    
    	if (basex)
    		phydev->link = (bool)(lpa & ANRX_SGMII_LINK);
    	else
    		phydev->link = (bool)(lpa & ANRX_BASEX_LINK);
    
    	return 0;
    }
    
    static int max24287_read_status(struct phy_device *phydev)
    {
    	int err, lpa;
    	bool basex = (bool)isbasex(phydev);
    
    	phydev->speed = SPEED_10;
    	phydev->duplex = DUPLEX_HALF;
    	phydev->pause = phydev->asym_pause = 0;
    
    	err = max24287_update_link(phydev);
    	if (err)
    		return err;
    	lpa = phy_read(phydev, MII_LPA);
    	if (lpa < 0)
    		return lpa;
    
    	err = detect_basex(phydev, &basex, lpa);
    	if (err < 0)
    		return err;
    
    	if (basex)
    		return _max24287_do_aneg_basex(phydev, lpa);
    	else
    		return _max24287_do_aneg_sgmii(phydev, lpa);
    }
    
    static int max24287_config_advert(struct phy_device *phydev)
    {
    	u32 advertise;
    	int orig_adv, err, adv = 0;
    	int basex = isbasex(phydev);
    
    	/* Only allow advertising what
    	 * this PHY supports */
    	phydev->advertising &= phydev->supported;
    	advertise = phydev->advertising;
    	orig_adv = phy_read(phydev, MII_ADVERTISE);
    
    	if (orig_adv < 0)
    		return orig_adv;
    
    	if (basex) {
    		if (advertise & ADVERTISED_1000baseT_Full)
    			adv |= ADVERTISE_1000XFULL;
    		else
    			adv |= ANRX_BASEX_RF;   /* Remote fault */
    
    		if (phydev->pause) {
    			adv |= ADVERTISE_1000XPAUSE;
    		}
    	} else {
    		adv = MAX24287_DEFAULT_SGMII_ADV;
    		/* XXX: TODO: setup master capability */
    	}
    
    	if (adv != orig_adv) {
    		err = phy_write(phydev, MII_ADVERTISE, adv);
    		if (err < 0)
    			return err;
    		return 1;
    		dev_dbg(&phydev->mdio.dev, "Autoneg changed 0x%x-0x%x\n", orig_adv, adv);
    	}
    	return 0;
    }
    
    static int max24287_config_aneg(struct phy_device *phydev)
    {
    	int 				err;
    
    	err = max24287_config_advert(phydev);
    
    	if (err < 0) {
    		return err;
    	} else if (err == 0) {
    		int	ctl = phy_read(phydev, MII_BMCR);
    
    		if (ctl < 0)
    			return ctl;
    
    		if (!(ctl & BMCR_ANENABLE))
    			err = 1;
    	}
    
    	if (err > 0)
    		err = genphy_restart_aneg(phydev);
    
    	return err;
    }
    
    static int max24287_config_init(struct phy_device *phydev)
    {
    	int val, basex;
    	u32 features = 0;
    
    	#if 0
    	features = MAX_FEATURES;
    	max24287_gmiicr_set(phydev, SPEED_1000);
    	max24287_set_basex(phydev, true);
    	#endif
    	val = phy_read(phydev, MII_BMSR);
    
    	if (val < 0)
    		return val;
    
    	basex = isbasex(phydev);
    	if (basex < 0)
    		return basex;
    
    	if (val & BMSR_ANEGCAPABLE)
    		features |= SUPPORTED_Autoneg;
    
    	if (basex)
    		features |= PHY_BASEX_FEATURES;
    	else
    		features |= PHY_GBIT_FEATURES;
    
    	phydev->supported = features;
    	phydev->advertising = features;
    
    	return 0;
    }
    
    /**
     * max24287_rev_b_reset
     * Description: max24287 soft reset procedure as stated in datasheet page 39
    */
    static int max24287_rev_b_reset(struct phy_device *phydev)
    {
    	int val, err = 0;
    
    	dev_dbg(&phydev->mdio.dev, "About to perform device reset (rev B).");
    	err = phy_read(phydev, MII_EXT_PAGE);
    	if (err < 0)
    		return err;
    	val = 0x0012;
    	err = phy_write(phydev, MII_EXT_PAGE, val);
    	if (err < 0)
    		return err;
    	val = 0x4004;
    	err = phy_write(phydev, MII_PTPCR1, val);
    	if (err < 0)
    		 return err;
    	udelay(1000);
    	val = 0x4000;
    	err = phy_write(phydev, MII_PTPCR1, val);
    	if (err < 0)
    		return err;
    	phy_write(phydev, MII_EXT_PAGE, 0x0010);
    	max24287_soft_reset(phydev);
    	return 0;
    }
    
    
    static void max24287_platform_config(struct phy_device *phydev)
    {
    	#ifdef CONFIG_ELSPEC_PLATFORM
    	int gpio4to7_val[MAX24287_TOTAL_IO] = {GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI};
    	/* All hi impedance. XXX: This needs to be passed from DT or board file */
    	int gpos_andgpios[MAX24287_TOTAL_IO] = {GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_IROUT};
    	/* GPO1-interrupt. XXX: This needs to be passed from DT or board file */
    	#else
    	int gpio4to7_val[MAX24287_TOTAL_IO] = {GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI};
    	int gpos_andgpios[MAX24287_TOTAL_IO] = {GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI, GPVAL_HI};
    	/* XXX: can be read from board file */
    	#endif
    	int i, err;
    
    	for (i = 0; i < MAX24287_TOTAL_IO; i++) {
    		err = max24287_set_gpios(phydev, IO_REG_PAGE, IO_REG1, i*3, gpio4to7_val[i]);
    		if (err < 0)
    			dev_info(&phydev->mdio.dev, "Error %d setting bit %d in register 1.18", err, i*3);
    		err = max24287_set_gpios(phydev, IO_REG_PAGE, IO_REG2, i*3, gpos_andgpios[i]);
    		if (err < 0)
    			dev_info(&phydev->mdio.dev, "Error %d setting bit %d in register 1.17", err, i*3);
    	}
    }
    
    /** max24287_phy_probe
     * Description: Driver probe method for max24287 driver. max24287 has phy_id of
     * 		0, and it's real id is stored in page1 ID register.
     */
    static int max24287_phy_probe(struct phy_device *phydev)
    {
    	int err = 0;
    	u16 phy_id = 0, rev = 0;
    
    	err = max24287_open_page(phydev, MAX24287_REALID_PAGE);
    	if (err < 0)
    		return err;
    	err = phy_read(phydev, MAX24287_REALID_REG);
    	if (err < 0)
    		return -EIO;
    
    	phy_id = err & REALID_ID_MASK;
    	rev = (err & REALID_REV_MASK) >> REALID_REV_OFFSET;
    
    	dev_info(&phydev->mdio.dev, "%s: Detected phy_id 0x%04x, revision 0x%01x\n", DEVICE_NAME, phy_id, rev);
    	if (phy_id == MAX24287_ID_REV_B) {
    		err = max24287_rev_b_reset(phydev);
    		if (err)
    			return err;
    	} else if (phy_id != MAX24287_ID_REV_A) {
    		return -ENODEV;
    	}
    
    	max24287_platform_config(phydev);
    	err = device_create_file(&phydev->mdio.dev, &dev_attr_conf_basex);
    	if (err != 0)
    		return err;
    
    	#ifdef CONFIG_ELSPEC_PLATFORM
    	/* XXX: This needs to be passed from DT or board file */
    	phydev->interface = PHY_INTERFACE_MODE_RGMII;
    	#endif
    
    	return 0;
    }
    
    static void max24287_phy_remove(struct phy_device *phydev)
    {
    	device_remove_file(&phydev->mdio.dev, &dev_attr_conf_basex);
    }
    
    
    static struct phy_driver max24287_phy_driver = {
    	.phy_id		 = 0x00000000,
    	.phy_id_mask	= 0xffffffff,
    	.name			= DEVICE_NAME,
    	.features		= PHY_GBIT_FEATURES | PHY_BASEX_FEATURES,
    	/* no assymetric pause see datasheet */
    	.flags		  =   0,
    	.config_init	= max24287_config_init,
    	.config_aneg	= max24287_config_aneg,
    	.read_status	= max24287_read_status,
    	.ack_interrupt	= max24287_ack_intr,
    	.config_intr	= max24287_config_intr,
    	#ifdef BASEX_IN_PHYDEVICE
    	/* XXX: add a prototype in struct phy_device in include/linux/phy.h */
    	.set_basex	= max24287_set_basex,
    	#endif
    	.probe		= max24287_phy_probe,
    	.remove		= max24287_phy_remove,
    	/*.driver		= {.owner = THIS_MODULE, },*/
    };
    
    static int __init max24287_init(void)
    {
    	int ret;
    
    	pr_info("%s: All bugs added by Ben Shapiro\n", DEVICE_NAME);
    
    	ret = phy_driver_register(&max24287_phy_driver, THIS_MODULE);
    	if (ret < 0)
    		return ret;
    
    	return 0;
    }
    
    static void __exit max24287_exit(void)
    {
    	phy_driver_unregister(&max24287_phy_driver);
    }
    
    module_init(max24287_init);
    module_exit(max24287_exit);
    
    /* XXX: max24287 has all 0's in PHYID1 and PHYID2. so we register 0 as
     * mdio_device phy_id then at probe function, we can read page1 ID registers
     * the real ID
     */
    static struct mdio_device_id __maybe_unused max24287_tbl[] = {
    		{ 0x00000000, 0xffffffff },
    	{ }
    };
    MODULE_DEVICE_TABLE(mdio, max24287_tbl);
    
    MODULE_DESCRIPTION("max24287 PHY driver");
    MODULE_AUTHOR("Boris-Ben Shapiro");
    MODULE_LICENSE("GPL");
    

    Regards,

    Murugan S

  • Hi,

    It may be my mistake concerning if the compatibility property is required. I should have mentioned that PHY drivers provide a numerical value for OUI and ID to the kernel during initialization so that as devices are found on the mdio bus the kernel will know which drivers go with which PHY and then call the appropriate driver probe function. The function module_init(max24287_init) in the driver you posted passes a pointer of a structure to the kernel that should contain the OUI and the function point to the PHY driver probe function.

    You should be seeing something like this when you boot your kernel with the PHY driver built in:

    mdio: phy[1]: device 4a101000.mdio:00, driver <vendor name> <device name> Gigabit PHY, the vendor should be Microsemi and the MAX24287 the device name.

    Best Regards,

    Schuyler

  • Hi Schuyler,

                                  Thanks for clearing my doubts.I appreciate your time and effort.

    Regards,

    Murugan S