ubnt,usg)
ucidef_set_interfaces_lan_wan "eth1 eth2" "eth0"
;;
+snic10e)
+ ucidef_set_interfaces_lan_wan "xaui0" "xaui1"
+ ;;
ubnt,edgerouter-4)
ucidef_set_interfaces_lan_wan "lan1 lan2 lan3" "lan0"
;;
return 0
;;
+ "SNIC10E"*)
+ name="snic10e"
+ ;;
+
"ITUS_SHIELD"*)
name="itus,shield-router"
;;
--- /dev/null
+/dts-v1/;
+/*
+ * Cavium Inc. (Small) NIC10e board
+ */
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+ model = "cavium,snic10e";
+ compatible = "cavium,snic10e";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ interrupt-parent = <&ciu>;
+
+ soc@0 {
+ compatible = "simple-bus";
+ #address-cells = <2>;
+ #size-cells = <2>;
+ ranges; /* Direct mapping */
+
+ ciu: interrupt-controller@1070000000000 {
+ compatible = "cavium,octeon-3860-ciu";
+ interrupt-controller;
+ /* Interrupts are specified by two parts:
+ * 1) Controller register (0 or 1)
+ * 2) Bit within the register (0..63)
+ */
+ #address-cells = <0>;
+ #interrupt-cells = <2>;
+ reg = <0x10700 0x00000000 0x0 0x7000>;
+ };
+
+ gpio: gpio-controller@1070000000800 {
+ #gpio-cells = <2>;
+ compatible = "cavium,octeon-3860-gpio";
+ reg = <0x10700 0x00000800 0x0 0x100>;
+ gpio-controller;
+ /* Interrupts are specified by two parts:
+ * 1) GPIO pin number (0..15)
+ * 2) Triggering (1 - edge rising
+ * 2 - edge falling
+ * 4 - level active high
+ * 8 - level active low)
+ */
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ /* The GPIO pins connect to 16 consecutive CUI bits */
+ interrupts = <0 16>; /* <0 17> <0 18> <0 19>
+ <0 20> <0 21> <0 22> <0 23>
+ <0 24> <0 25> <0 26> <0 27>
+ <0 28> <0 29> <0 30> <0 31>; */
+ };
+
+ twsi0: i2c@1180000001000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "cavium,octeon-3860-twsi";
+ reg = <0x11800 0x00001000 0x0 0x200>;
+ interrupts = <0 45>;
+ /* NOTE: In order to get the proper delay between
+ * i2c bus transactions for the SFP we need to either
+ * slow the bus down to no more than 30KHz or else
+ * somehow insert a delay between transactions. Only
+ * U-Boot is capable of inserting the appropriate delay
+ * at this time.
+ */
+ clock-frequency = <30000>;
+
+ tmp@4c {
+ compatible = "ti,tmp421";
+ reg = <0x4c>;
+ };
+ sfp0: eeprom@50 {
+ compatible = "atmel,24c01";
+ reg = <0x50>;
+ };
+ tlv-eeprom@53 {
+ compatible = "atmel,24c256";
+ reg = <0x53>;
+ pagesize = <64>;
+ };
+ };
+
+ twsi1: i2c@1180000001200 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "cavium,octeon-3860-twsi";
+ reg = <0x11800 0x00001200 0x0 0x200>;
+ interrupts = <0 59>;
+ /* NOTE: In order to get the proper delay between
+ * i2c bus transactions for the SFP we need to either
+ * slow the bus down to no more than 30KHz or else
+ * somehow insert a delay between transactions. Only
+ * U-Boot is capable of inserting the appropriate delay
+ * at this time.
+ */
+ clock-frequency = <30000>;
+
+ sfp1: eeprom@50 {
+ compatible = "atmel,24c01";
+ reg = <0x50>;
+ };
+ gpio1: gpio@20 {
+ reg = <0x20>;
+ compatible = "nxp,pca9554";
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-parent = <&gpio>;
+ interrupt = <13 2>; /* OCTEON GPIO 13, falling edge */
+ #interrupt-cells = <1>;
+ };
+
+ };
+
+ smi0: mdio@1180000001800 {
+ compatible = "cavium,octeon-3860-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x11800 0x00001800 0x0 0x40>;
+
+ mphyA: ethernet-phy-nexus@A {
+ reg = <0>;
+ /* The Vitesse VSC8488 is a dual-PHY where
+ * some of the configuration is common across
+ * both of the phy devices such as the reset
+ * line and the base MDIO address.
+ */
+ compatible = "vitesse,vsc8488-nexus", "ethernet-phy-nexus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ranges;
+
+ /* Hardware reset signal */
+ reset = <&gpio 17 GPIO_ACTIVE_HIGH>;
+
+ phy0: ethernet-phy@0 {
+ /* Absolute address */
+ reg = <0>;
+ compatible = "vitesse,vsc8488", "ethernet-phy-ieee802.3-c45";
+ interrupt-parent = <&gpio>;
+ interrupts = <13 8>;
+
+ mod_abs = <0>;
+ /* TX Fault GPIO line */
+ tx_fault = <1>;
+ /* GPIO that enables output */
+ txon = <4>;
+ /* INT A GPIO output */
+ inta = <5>;
+
+ /* Optional equalization value to
+ * program into the PHY XS XAUI Rx
+ * Equalization control register.
+ * It is broken up into one nibble for
+ * each lane with lane 0 using bits
+ * 12 - 15.
+ * Use the following table:
+ * 0x0 - 0dB
+ * 0x1 - 1.41dB
+ * 0x2 - 2.24dB
+ * 0x3 - 2.83dB
+ * 0x5 - 4.48dB
+ * 0x6 - 5.39dB
+ * 0x7 - 6.07dB
+ * 0x9 - 6.18dB
+ * 0xA - 7.08dB (default)
+ * 0xB - 7.79dB
+ * 0xD - 9.96dB
+ * 0xE - 10.84dB
+ * 0xF - 11.55dB
+ *
+ * This is board specific and should
+ * only be defined by the hardware
+ * vendor.
+ */
+ vitesse,rx_equalization = <0x0000>;
+ /* Optional transmit pre-emphasis
+ * control. This sets the
+ * PHY XS XAUI TX pre-emphasis control
+ * register.
+ *
+ * It uses bits 13-14 for lane 0,
+ * 10-11 for lane 1, 7-8 for lane 2
+ * and 4-5 for lane 3.
+ *
+ * Bits 2-3 are the LOS threshold
+ * setting and bit 1 enables
+ * the XAUI output high swing mode.
+ *
+ * Use the following table for
+ * pre-emphasis:
+ * 0b00 - 0dB
+ * 0b01 - 2.5dB
+ * 0b10 - 6dB (default)
+ * 0b11 - 12dB
+ *
+ * Use the following table for the LOS
+ * threshold setting:
+ *
+ * 0b00 - 50mV - 175mV (default)
+ * 0b01 - 60mV - 185mV
+ * 0b10 - 70mV - 195mV
+ * 0b11 - 80mV - 205mV
+ */
+ vitesse,tx_preemphasis = <0x0000>;
+
+ /* TX output driver slew rate control
+ * is bits 8-11 where 0x0 is the minimum
+ * and 0xF is the maximum.
+ * Default is 0xA.
+ *
+ * The TX output driver C(-1)
+ * coefficient is bits 0-4 where
+ * 0b00000 is the minimum (-4ma) and
+ * 0b11111 is the maximum (4ma). The
+ * default 0x 0b01111.
+ */
+ vitesse,txout_driver_ctrl1 = <0x0A0F>;
+
+ /* The TX output driver C(0) coefficient
+ * is bits 8-12 with 0b00000 being the
+ * minimum (0mA) and 0b11111 being
+ * the maximum (16mA). The default is
+ * 0b10011
+ *
+ * The C(+1) coefficient is bits 0-5
+ * with 0b000000 being the minimum
+ * (-0.25mA) and 0b111111 being the
+ * maximum (-16mA). The default is
+ * 0b000000.
+ */
+ /*vitesse,txout_driver_ctrl2 = <0x1300>;*/
+
+ /* DC_AGC_LOS_CONFIG1:
+ * 15: Suppress_Coarse_Adj_on_LOS_Clear
+ * 0: DC offset correction performed using coarse
+ * resolution mode (default)
+ * 1: DC offset correction performed using fine resolution
+ * mode when correction resumes after LOPC/LOS alarms
+ * clear. This guarantees there will be no big jumps in
+ * the offset at the expense of taking longer to reach
+ * optimal setting.
+ * 14: Force_DC2_Fine_Adj:
+ * Forces the DC offset correction to operate in fine
+ * resolution adjustment mode at times when the algorithm.
+ * 0: DC offset correction makes coarse adjustments when
+ * correction mode is first enabled (default)
+ * 1: DC offset correction performed using fine resolution
+ * at all times. This is slower.
+ * 13: Force_DC1_Fine_Adj:
+ * Forces the DC offset correction to operate in fine
+ * resolution adjustment mode at times when the algorithm.
+ * 0: DC offset correction makes coarse adjustments when
+ * correction mode is first enabled (default)
+ * 1: DC offset correction performed using fine resolution
+ * at all times. This is slower.
+ * 12: Force_DC0_Fine_Adj:
+ * Forces the DC offset correction to operate in fine
+ * resolution adjustment mode at times when the algorithm.
+ * 0: DC offset correction makes coarse adjustments when
+ * correction mode is first enabled (default)
+ * 1: DC offset correction performed using fine resolution
+ * at all times. This is slower.
+ * 10: Skip_DC2_Adj, 1 = skip DC2 offset correction
+ * 9: Skip_DC1_Adj, 1 = skip DC1 offset correction
+ * 8: Skip_DC0_Adj, 1 = skip DC0 offset correction
+ *
+ * 6-4: DC_Offset_Alarm_Mode (default 1)
+ * Selects the alarm condition that will halt the DC offset
+ * correction logic when the alarm(s) are set.
+ * 111: reserved
+ * 110: reserved
+ * 101: LOPC and software LOS detection
+ * 100: LOPC and hardware LOS detection
+ * 011: Software LOS detection
+ * 010: Hardware LOS detection
+ * 001: LOPC
+ * 000: Never. DC offset correction will continue to make
+ * fine resolution adjustments to the offsets even
+ * when LOPC and LOS alarms are present.
+ *
+ * 3: AGC_Enable
+ * Selects when hardware AGC adjustment logic and LOS
+ * detection logic is enabled (default 1)
+ * 0: disabled
+ * 1: enabled
+ * 2: AGC_Suspend
+ * Suspends the LOS detection logic and AGC logic
+ * from making adjustments to the gain. Bit valid only
+ * if AGC_Enable=1
+ * 0: AGC adjustment enabled (default)
+ * 1: AGC adjustment suspended
+ * 1: DC_Offset_Adj_Enable
+ * Select when the hardware DC offset correction logic is
+ * enabled.
+ * 0: disable
+ * 1: enable (default)
+ * 0: DC_Offset_Adj_Suspend
+ * Suspends the DC offset correction logic from making
+ * adjustments to all offset settings. Bit valid only if
+ * DC_Offset_Adj_Enable=1
+ * 0: DC offset correction enabled (default)
+ * 1: DC offset correction suspended
+ *
+ * This setting is only applied for
+ * passive copper.
+ */
+ vitesse,copper_dc_agc_los_config1 = <0x000A>;
+
+ /* Disable aggressive track phase during
+ * firmware convergence if 0, enabled
+ * otherwise (default).
+ *
+ * This setting is only applied for
+ * passive copper.
+ */
+ vitesse,copper_agg_track_phase = <0>;
+
+ /* AGC_Config4
+ *
+ * 13-8: Ampl_Tolerance
+ * This defines the hysterisis
+ * built in to the AGC adjustment
+ * circuit. The VGA gain will not
+ * be adjusted as long as the
+ * measured input amplitude is
+ * Inp_Ampl_Target +/- Amnpl_Tolerance.
+ * Default is 4.
+ * 7-0: Inp_Ampl_Target
+ * This is the target amplitude
+ * desired to be measured at the
+ * peak detector when measuring
+ * input amplitude. The VGA gain
+ * is adjusted to achieve this
+ * target setting.
+ * Default is 0x6E.
+ *
+ * This setting is only applied for
+ * passive copper.
+ */
+ vitesse,copper_agc_config4 = <0x0496>;
+
+ /* The Vitesse 10G PHY does not
+ * automatically read the SFP EEPROM
+ * so the host needs to do it to put
+ * the PHY in the proper mode for
+ * copper or optical.
+ */
+ sfp-eeprom = <&sfp0>;
+ };
+
+ phy1: ethernet-phy@1 {
+ /* Absolute address */
+ reg = <0x1>;
+ compatible = "vitesse,vsc8488", "ethernet-phy-ieee802.3-c45";
+ interrupt-parent = <&gpio>;
+ interrupts = <13 8>;
+
+ mod_abs = <9>;
+ /* TX Fault GPIO line */
+ tx_fault = <8>;
+ /* GPIO that enables output */
+ txon = <10>;
+ /* INT A GPIO output */
+ inta = <5>;
+
+ /* Optional equalization value to
+ * program into the PHY XS XAUI Rx
+ * Equalization control register.
+ * It is broken up into one nibble for
+ * each lane with lane 0 using bits
+ * 12 - 15.
+ * Use the following table:
+ * 0x0 - 0dB
+ * 0x1 - 1.41dB
+ * 0x2 - 2.24dB
+ * 0x3 - 2.83dB
+ * 0x5 - 4.48dB
+ * 0x6 - 5.39dB
+ * 0x7 - 6.07dB
+ * 0x9 - 6.18dB
+ * 0xA - 7.08dB (default)
+ * 0xB - 7.79dB
+ * 0xD - 9.96dB
+ * 0xE - 10.84dB
+ * 0xF - 11.55dB
+ *
+ * This is board specific and should
+ * only be defined by the hardware
+ * vendor.
+ */
+ rx_equalization = <0x0000>;
+ /* Optional transmit pre-emphasis
+ * control. This sets the
+ * PHY XS XAUI TX pre-emphasis control
+ * register.
+ *
+ * It uses bits 13-14 for lane 0,
+ * 10-11 for lane 1, 7-8 for lane 2
+ * and 4-5 for lane 3.
+ *
+ * Bits 2-3 are the LOS threshold
+ * setting and bit 1 enables
+ * the XAUI output high swing mode.
+ *
+ * Use the following table for
+ * pre-emphasis:
+ * 0b00 - 0dB
+ * 0b01 - 2.5dB
+ * 0b10 - 6dB (default)
+ * 0b11 - 12dB
+ *
+ * Use the following table for the LOS
+ * threshold setting:
+ *
+ * 0b00 - 50mV - 175mV (default)
+ * 0b01 - 60mV - 185mV
+ * 0b10 - 70mV - 195mV
+ * 0b11 - 80mV - 205mV
+ */
+ tx_preemphasis = <0x0000>;
+
+ /* TX output driver slew rate control
+ * is bits 8-11 where 0x0 is the minimum
+ * and 0xF is the maximum.
+ * Default is 0xA.
+ *
+ * The TX output driver C(-1)
+ * coefficient is bits 0-4 where
+ * 0b00000 is the minimum (-4ma) and
+ * 0b11111 is the maximum (4ma). The
+ * default 0x 0b01111.
+ */
+ txout_driver_ctrl1 = <0x0A0F>;
+
+ /* The TX output driver C(0) coefficient
+ * is bits 8-12 with 0b00000 being the
+ * minimum (0mA) and 0b11111 being
+ * the maximum (16mA). The default is
+ * 0b10011
+ *
+ * The C(+1) coefficient is bits 0-5
+ * with 0b000000 being the minimum
+ * (-0.25mA) and 0b111111 being the
+ * maximum (-16mA). The default is
+ * 0b000000.
+ */
+ /*txout_driver_ctrl2 = <0x1300>;*/
+
+ /* DC_AGC_LOS_CONFIG1:
+ * 15: Suppress_Coarse_Adj_on_LOS_Clear
+ * 0: DC offset correction performed using coarse
+ * resolution mode (default)
+ * 1: DC offset correction performed using fine resolution
+ * mode when correction resumes after LOPC/LOS alarms
+ * clear. This guarantees there will be no big jumps in
+ * the offset at the expense of taking longer to reach
+ * optimal setting.
+ * 14: Force_DC2_Fine_Adj:
+ * Forces the DC offset correction to operate in fine
+ * resolution adjustment mode at times when the algorithm.
+ * 0: DC offset correction makes coarse adjustments when
+ * correction mode is first enabled (default)
+ * 1: DC offset correction performed using fine resolution
+ * at all times. This is slower.
+ * 13: Force_DC1_Fine_Adj:
+ * Forces the DC offset correction to operate in fine
+ * resolution adjustment mode at times when the algorithm.
+ * 0: DC offset correction makes coarse adjustments when
+ * correction mode is first enabled (default)
+ * 1: DC offset correction performed using fine resolution
+ * at all times. This is slower.
+ * 12: Force_DC0_Fine_Adj:
+ * Forces the DC offset correction to operate in fine
+ * resolution adjustment mode at times when the algorithm.
+ * 0: DC offset correction makes coarse adjustments when
+ * correction mode is first enabled (default)
+ * 1: DC offset correction performed using fine resolution
+ * at all times. This is slower.
+ * 10: Skip_DC2_Adj, 1 = skip DC2 offset correction
+ * 9: Skip_DC1_Adj, 1 = skip DC1 offset correction
+ * 8: Skip_DC0_Adj, 1 = skip DC0 offset correction
+ *
+ * 6-4: DC_Offset_Alarm_Mode (default 1)
+ * Selects the alarm condition that will halt the DC offset
+ * correction logic when the alarm(s) are set.
+ * 111: reserved
+ * 110: reserved
+ * 101: LOPC and software LOS detection
+ * 100: LOPC and hardware LOS detection
+ * 011: Software LOS detection
+ * 010: Hardware LOS detection
+ * 001: LOPC
+ * 000: Never. DC offset correction will continue to make
+ * fine resolution adjustments to the offsets even
+ * when LOPC and LOS alarms are present.
+ *
+ * 3: AGC_Enable
+ * Selects when hardware AGC adjustment logic and LOS
+ * detection logic is enabled (default 1)
+ * 0: disabled
+ * 1: enabled
+ * 2: AGC_Suspend
+ * Suspends the LOS detection logic and AGC logic
+ * from making adjustments to the gain. Bit valid only
+ * if AGC_Enable=1
+ * 0: AGC adjustment enabled (default)
+ * 1: AGC adjustment suspended
+ * 1: DC_Offset_Adj_Enable
+ * Select when the hardware DC offset correction logic is
+ * enabled.
+ * 0: disable
+ * 1: enable (default)
+ * 0: DC_Offset_Adj_Suspend
+ * Suspends the DC offset correction logic from making
+ * adjustments to all offset settings. Bit valid only if
+ * DC_Offset_Adj_Enable=1
+ * 0: DC offset correction enabled (default)
+ * 1: DC offset correction suspended
+ *
+ * This setting is only applied for
+ * passive copper.
+ */
+ vitesse,copper_dc_agc_los_config1 = <0x000A>;
+
+ /* Disable aggressive track phase during
+ * firmware convergence if 0, enabled
+ * otherwise (default).
+ *
+ * This setting is only applied for
+ * passive copper.
+ */
+ vitesse,copper_agg_track_phase = <0>;
+
+ /* AGC_Config4
+ *
+ * 13-8: Ampl_Tolerance
+ * This defines the hysterisis
+ * built in to the AGC adjustment
+ * circuit. The VGA gain will not
+ * be adjusted as long as the
+ * measured input amplitude is
+ * Inp_Ampl_Target +/- Amnpl_Tolerance.
+ * Default is 4.
+ * 7-0: Inp_Ampl_Target
+ * This is the target amplitude
+ * desired to be measured at the
+ * peak detector when measuring
+ * input amplitude. The VGA gain
+ * is adjusted to achieve this
+ * target setting.
+ * Default is 0x6E.
+ *
+ * This setting is only applied for
+ * passive copper.
+ */
+ vitesse,copper_agc_config4 = <0x0496>;
+
+ /* The Vitesse 10G PHY does not
+ * automatically read the SFP EEPROM
+ * so the host needs to do it to put
+ * the PHY in the proper mode for
+ * copper or optical.
+ */
+ sfp-eeprom = <&sfp1>;
+ };
+ };
+ mphyB: ethernet-phy-nexus@B {
+ reg = <0>;
+ /* The TI TLK10232 is a dual-PHY where
+ * some of the configuration is common across
+ * both of the phy devices such as the reset
+ * line and the base MDIO address.
+ */
+ compatible = "ti,tlk10232-nexus", "ethernet-phy-nexus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ ranges;
+
+ /* Hardware reset signal open-drain active low on GPIO 17, must not be driven high. */
+ reset = <&gpio 17 GPIO_LINE_OPEN_DRAIN>;
+
+ phy11: ethernet-phy@0 {
+ /* Absolute address */
+ reg = <0>;
+ compatible = "ti,tlk10232", "ethernet-phy-ieee802.3-c45";
+
+ /* The TI 10G PHY does not
+ * automatically read the SFP EEPROM
+ * so the host needs to do it to put
+ * the PHY in the proper mode for
+ * copper or optical.
+ */
+ sfp-eeprom = <&sfp0>;
+
+ /* TX fault input signal for PHY from SFP+ */
+ tx-fault = <&gpio1 4 GPIO_ACTIVE_HIGH>;
+ /* TX disable for PHY to SFP+ */
+ tx-disable = <&gpio1 5 GPIO_ACTIVE_HIGH>;
+ /* MOD ABS signal for PHY from SFP+ */
+ mod-abs = <&gpio1 6 GPIO_ACTIVE_HIGH>;
+ /* RX los of singal for PHY from SFP+ */
+ rx-los = <&gpio1 7 GPIO_ACTIVE_HIGH>;
+ };
+
+ phy10: ethernet-phy@1 {
+ /* Absolute address */
+ reg = <0x1>;
+ compatible = "ti,tlk10232", "ethernet-phy-ieee802.3-c45";
+
+ /* The TI 10G PHY does not
+ * automatically read the SFP EEPROM
+ * so the host needs to do it to put
+ * the PHY in the proper mode for
+ * copper or optical.
+ */
+ sfp-eeprom = <&sfp1>;
+ /* TX fault input signal for PHY */
+ tx-fault = <&gpio1 0 GPIO_ACTIVE_HIGH>;
+ /* TX disable for PHY */
+ tx-disable = <&gpio1 1 GPIO_ACTIVE_HIGH>;
+ /* MOD ABS signal for PHY */
+ mod-abs = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+ /* RX los of singal for PHY */
+ rx-los = <&gpio1 3 GPIO_ACTIVE_HIGH>;
+ };
+ };
+ };
+
+ pip: pip@11800a0000000 {
+ compatible = "cavium,octeon-3860-pip";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0x11800 0xa0000000 0x0 0x2000>;
+
+ interface@A {
+ compatible = "cavium,octeon-3860-pip-interface";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>; /* interface */
+
+ ethernet@0 {
+ compatible = "cavium,octeon-3860-pip-port";
+ reg = <0x0>; /* Port */
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-handle = <&phy0>;
+ };
+ };
+ interface@B {
+ compatible = "cavium,octeon-3860-pip-interface";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>; /* interface */
+
+ ethernet@0 {
+ compatible = "cavium,octeon-3860-pip-port";
+ reg = <0x0>; /* Port */
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-handle = <&phy1>;
+ };
+ };
+ interface@C {
+ compatible = "cavium,octeon-3860-pip-interface";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <0>; /* interface */
+
+ ethernet@0 {
+ compatible = "cavium,octeon-3860-pip-port";
+ reg = <0x0>; /* Port */
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-handle = <&phy10>;
+ };
+ };
+ interface@D {
+ compatible = "cavium,octeon-3860-pip-interface";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ reg = <1>; /* interface */
+
+ ethernet@0 {
+ compatible = "cavium,octeon-3860-pip-port";
+ reg = <0x0>; /* Port */
+ local-mac-address = [ 00 00 00 00 00 00 ];
+ phy-handle = <&phy11>;
+ };
+ };
+ };
+
+ uart0: serial@1180000000800 {
+ compatible = "cavium,octeon-3860-uart","ns16550";
+ reg = <0x11800 0x00000800 0x0 0x400>;
+ clock-frequency = <800000000>;
+ current-speed = <115200>;
+ reg-shift = <3>;
+ interrupts = <0 34>;
+ };
+
+ uart1: serial@1180000000c00 {
+ compatible = "cavium,octeon-3860-uart","ns16550";
+ reg = <0x11800 0x00000c00 0x0 0x400>;
+ clock-frequency = <800000000>;
+ current-speed = <115200>;
+ reg-shift = <3>;
+ interrupts = <0 35>;
+ };
+
+ bootbus: bootbus@1180000000000 {
+ compatible = "cavium,octeon-3860-bootbus";
+ reg = <0x11800 0x00000000 0x0 0x200>;
+ /* The chip select number and offset */
+ #address-cells = <2>;
+ /* The size of the chip select region */
+ #size-cells = <1>;
+ ranges = <0 0 0 0x1f400000 0xc00000>,
+ <1 0 0x10000 0x30000000 0>,
+ <2 0 0 0x1f000000 0x100000>,
+ <3 0 0x10000 0x50000000 0>,
+ <4 0 0x10000 0x60000000 0>,
+ <5 0 0x10000 0x70000000 0>,
+ <6 0 0x10000 0x80000000 0>,
+ <7 0 0x10000 0x90000000 0>;
+
+ cavium,cs-config@0 {
+ compatible = "cavium,octeon-3860-bootbus-config";
+ cavium,cs-index = <0>;
+ cavium,t-adr = <10>;
+ cavium,t-ce = <50>;
+ cavium,t-oe = <50>;
+ cavium,t-we = <35>;
+ cavium,t-rd-hld = <25>;
+ cavium,t-wr-hld = <35>;
+ cavium,t-pause = <0>;
+ cavium,t-wait = <300>;
+ cavium,t-page = <25>;
+ cavium,t-rd-dly = <0>;
+
+ cavium,pages = <0>;
+ cavium,bus-width = <8>;
+ };
+ cavium,cs-config@2 {
+ compatible = "cavium,octeon-3860-bootbus-config";
+ cavium,cs-index = <2>;
+ cavium,t-adr = <0>;
+ cavium,t-ce = <50>;
+ cavium,t-oe = <20>;
+ cavium,t-we = <46>;
+ cavium,t-rd-hld = <8>;
+ cavium,t-wr-hld = <10>;
+ cavium,t-pause = <0>;
+ cavium,t-wait = <0>;
+ cavium,t-page = <1>;
+ cavium,t-ale = <1>;
+ cavium,t-rd-dly = <0>;
+
+ cavium,pages = <0>;
+ cavium,bus-width = <8>;
+ };
+ flash0: nor@0,0 {
+ compatible = "cfi-flash";
+ reg = <0 0 0x800000>;
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ label = "bootloader";
+ reg = <0x0 0x1c0000>;
+ read-only;
+ };
+ partition@1c0000 {
+ label = "kernel";
+ reg = <0x1c0000 0x63e000>;
+ };
+ partition@7fe000 {
+ label = "environment";
+ reg = <0x7fe0000 0x2000>;
+ read-only;
+ };
+ };
+ psram0: psram@2,0 {
+ compatible = "micron,mt45w1mw16pd";
+ reg = <2 0x20 0x20>, <2 0 0x20>;
+ };
+ };
+
+ dma0: dma-engine@1180000000100 {
+ compatible = "cavium,octeon-5750-bootbus-dma";
+ reg = <0x11800 0x00000100 0x0 0x8>;
+ interrupts = <0 63>;
+ };
+ dma1: dma-engine@1180000000108 {
+ compatible = "cavium,octeon-5750-bootbus-dma";
+ reg = <0x11800 0x00000108 0x0 0x8>;
+ interrupts = <0 63>;
+ };
+ nand-flash-interface@1070001000000 {
+ compatible = "cavium,octeon-5230-nand";
+ reg = <0x10700 0x1000000 0x0 0x100 0x11800 0x168 0x0 0x20>;
+ #address-cells = <0x1>;
+ #size-cells = <0x0>;
+ interrupts = <0x0 0x3f>;
+ flash@1 {
+ compatible = "nand-flash";
+ reg = <0x1>;
+ nand-ecc-mode = "soft";
+ nand-ecc-size = <0x200>;
+ nand-ecc-bytes = <0x7>;
+ nand-bus-width = <0x8>;
+ };
+ };
+ };
+ gpio-leds {
+ compatible = "gpio-leds";
+
+ d1a {
+ label = "bottom";
+ gpios = <&gpio 1 GPIO_ACTIVE_HIGH>;
+ default-state = "keep";
+ };
+ d1b-t {
+ label = "top";
+ gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
+ default-state = "keep";
+ };
+ d1b-v {
+ label = "top";
+ gpios = <&gpio 2 GPIO_ACTIVE_HIGH>;
+ default-state = "keep";
+ };
+ };
+
+ aliases {
+ pip = &pip;
+ smi0 = &smi0;
+ twsi0 = &twsi0;
+ twsi1 = &twsi1;
+ uart0 = &uart0;
+ uart1 = &uart1;
+ flash0 = &flash0;
+ };
+ };
endef
TARGET_DEVICES += ubnt_edgerouter-6p
+define Device/snic10e
+ DEVICE_VENDOR := Cavium
+ DEVICE_MODEL := snic10e
+ DEVICE_DTS := snic10e
+ DEVICE_PACKAGES += kmod-gpio-button-hotplug kmod-hwmon-tmp421 kmod-leds-gpio kmod-of-mdio kmod-sfp
+ KERNEL := kernel-bin | append-dtb-to-elf
+ KERNEL_DEPENDS := $$(wildcard $(DTS_DIR)/$(DEVICE_DTS).dts)
+endef
+
+TARGET_DEVICES += snic10e
+
ERLITE_CMDLINE:=-mtdparts=phys_mapped_flash:512k(boot0)ro,512k(boot1)ro,64k(eeprom)ro root=/dev/sda2 rootfstype=squashfs,ext4 rootwait
define Device/ubnt_edgerouter-lite
DEVICE_VENDOR := Ubiquiti
--- /dev/null
+--- a/arch/mips/include/asm/octeon/cvmx-bootinfo.h
++++ b/arch/mips/include/asm/octeon/cvmx-bootinfo.h
+@@ -253,6 +253,7 @@ enum cvmx_board_types_enum {
+ CVMX_BOARD_TYPE_REDWING = 43,
+ CVMX_BOARD_TYPE_NIC68_4 = 44,
+ CVMX_BOARD_TYPE_NIC10E_66 = 45,
++ CVMX_BOARD_TYPE_SNIC10E = 50,
+ CVMX_BOARD_TYPE_MAX,
+
+ /*
+@@ -369,6 +370,7 @@ static inline const char *cvmx_board_typ
+ ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_REDWING)
+ ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_NIC68_4)
+ ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_NIC10E_66)
++ ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_SNIC10E)
+ ENUM_BRD_TYPE_CASE(CVMX_BOARD_TYPE_MAX)
+
+ /* Customer boards listed here */
--- /dev/null
+From 8ecd40e17c16d997e0a031dfaea229b1bb906ec1 Mon Sep 17 00:00:00 2001
+From: Stijn Tintel <stijn@linux-ipv6.be>
+Date: Mon, 19 Apr 2021 01:09:56 +0300
+Subject: [PATCH 4/5] MIPS: Octeon: fix CN6640 hang on XAUI init
+
+Some CN66XX series Octeon II chips seem to hang if a reset is issued on
+XAUI initialization. Avoid the hang by disabling the reset.
+
+Tested on SNIC10E.
+
+Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
+---
+ arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
++++ b/arch/mips/cavium-octeon/executive/cvmx-helper-xaui.c
+@@ -156,8 +156,9 @@ int __cvmx_helper_xaui_enable(int interf
+ xauiCtl.u64 = cvmx_read_csr(CVMX_PCSXX_CONTROL1_REG(interface));
+ xauiCtl.s.lo_pwr = 0;
+
+- /* Issuing a reset here seems to hang some CN68XX chips. */
+- if (!OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X) &&
++ /* Issuing a reset here seems to hang some CN66XX/CN68XX chips. */
++ if (!OCTEON_IS_MODEL(OCTEON_CN66XX) &&
++ !OCTEON_IS_MODEL(OCTEON_CN68XX_PASS1_X) &&
+ !OCTEON_IS_MODEL(OCTEON_CN68XX_PASS2_X))
+ xauiCtl.s.reset = 1;
+
--- /dev/null
+From 2334bd3f8627cf712fcba61aa22c8169b2326e50 Mon Sep 17 00:00:00 2001
+From: Stijn Tintel <stijn@linux-ipv6.be>
+Date: Mon, 19 Apr 2021 02:00:16 +0300
+Subject: [PATCH 5/5] MIPS: Octeon: support all interfaces on CN66XX
+
+CN66XX_PASS1_0 has 7 interfaces, other revisions have 8 interfaces.
+
+Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
+---
+ arch/mips/cavium-octeon/executive/cvmx-helper.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/arch/mips/cavium-octeon/executive/cvmx-helper.c
++++ b/arch/mips/cavium-octeon/executive/cvmx-helper.c
+@@ -61,6 +61,12 @@ int cvmx_helper_get_number_of_interfaces
+ {
+ if (OCTEON_IS_MODEL(OCTEON_CN68XX))
+ return 9;
++ if (OCTEON_IS_MODEL(OCTEON_CN66XX)) {
++ if (OCTEON_IS_MODEL(OCTEON_CN66XX_PASS1_0))
++ return 7;
++ else
++ return 8;
++ }
+ if (OCTEON_IS_MODEL(OCTEON_CN56XX) || OCTEON_IS_MODEL(OCTEON_CN52XX))
+ return 4;
+ if (OCTEON_IS_MODEL(OCTEON_CN7XXX))
--- /dev/null
+From c59d03f70e739eb7fec976f86fff7dd12968b3fc Mon Sep 17 00:00:00 2001
+From: Leonid Rosenboim <lrosenboim@caviumnetworks.com>
+Date: Fri, 13 Feb 2015 15:04:26 +0530
+Subject: [PATCH 2/5] netdev/phy/of: Handle nexus Ethernet PHY devices
+
+Some multi-phy devices have resources that are global between all of the
+PHYs on the same device such as the Vitesse vsc8488. In this case each
+individual PHY is contained within a phy nexus device.
+
+Signed-off-by: Leonid Rosenboim <lrosenboim@caviumnetworks.com>
+Signed-off-by: Aaron Williams <aaron.williams@cavium.com>
+Signed-off-by: Abhishek Paliwal <abhishek.paliwal@aricent.com>
+[rebase on 5.10]
+Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
+---
+ drivers/net/mdio/of_mdio.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/net/mdio/of_mdio.c
++++ b/drivers/net/mdio/of_mdio.c
+@@ -18,6 +18,7 @@
+ #include <linux/of_irq.h>
+ #include <linux/of_mdio.h>
+ #include <linux/of_net.h>
++#include <linux/of_platform.h>
+ #include <linux/module.h>
+
+ #define DEFAULT_GPIO_RESET_DELAY 10 /* in microseconds */
+@@ -278,6 +279,11 @@ int of_mdiobus_register(struct mii_bus *
+
+ /* Loop over the child nodes and register a phy_device for each phy */
+ for_each_available_child_of_node(np, child) {
++ if (of_device_is_compatible(child, "ethernet-phy-nexus")) {
++ of_platform_device_create(child, NULL, &mdio->dev);
++ continue;
++ }
++
+ addr = of_mdio_parse_addr(&mdio->dev, child);
+ if (addr < 0) {
+ scanphys = true;
--- /dev/null
+From f5985b22b20c704e33394a28a9add765fc2b8312 Mon Sep 17 00:00:00 2001
+From: David Daney <david.daney@cavium.com>
+Date: Fri, 13 Feb 2015 15:04:55 +0530
+Subject: [PATCH 3/5] netdev/phy: Add driver for Vitesse vsc848x single, dual
+ and quad 10G phys
+
+These phys implement the standard IEEE 802.3 clause 45 registers but
+require additional configuration. Some of these registers in the multi-phy
+devices are shared among all phys such as the GPIO registers.
+
+Additionally, this PHY does not automatically access the SFP+ serial EEPROM so
+it is up to the PHY driver to parse it and change certain parameters in the
+PHY according to the type of module installed and the length of the cable, if
+copper.
+
+This module has support for the vsc8488, vsc8486 and vsc8484 Vitesse devices
+but thus far has only been tested with the vsc8488 dual PHY.
+
+netdev/phy: Clean up structure names in vsc848x.c
+Cut-and-paste snafu left some bad names, no functional change.
+
+Signed-off-by: David Daney <david.daney@cavium.com>
+Signed-off-by: Aaron Williams <aaron.williams@cavium.com>
+Signed-off-by: Leonid Rosenboim <lrosenboim@caviumnetworks.com>
+Signed-off-by: Abhishek Paliwal <abhishek.paliwal@aricent.com>
+[Rebased on kernel 5.4 by Martin Kennedy <hurricos@gmail.com>]
+Signed-off-by: Martin Kennedy <hurricos@gmail.com>
+[rebase on 5.10, use nvmem instead of of_memory_accessor]
+Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
+---
+ drivers/net/phy/Kconfig | 7 +
+ drivers/net/phy/Makefile | 1 +
+ drivers/net/phy/vsc848x.c | 770 ++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 778 insertions(+)
+ create mode 100644 drivers/net/phy/vsc848x.c
+
+--- a/drivers/net/phy/Kconfig
++++ b/drivers/net/phy/Kconfig
+@@ -381,6 +381,13 @@ config VITESSE_PHY
+ help
+ Currently supports the vsc8244
+
++config VSC848X_PHY
++ tristate "Drivers for the Vitesse 10G PHYs"
++ depends on NVMEM
++ help
++ Driver for Vitesse vsc848x single, dual and quad 10G PHY devices.
++ Currently supports the vsc8488, vsc8486 and vsc8484 chips
++
+ config XILINX_GMII2RGMII
+ tristate "Xilinx GMII2RGMII converter driver"
+ help
+--- a/drivers/net/phy/Makefile
++++ b/drivers/net/phy/Makefile
+@@ -92,4 +92,5 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o
+ obj-$(CONFIG_STE10XP) += ste10Xp.o
+ obj-$(CONFIG_TERANETICS_PHY) += teranetics.o
+ obj-$(CONFIG_VITESSE_PHY) += vitesse.o
++obj-$(CONFIG_VSC848X_PHY) += vsc848x.o
+ obj-$(CONFIG_XILINX_GMII2RGMII) += xilinx_gmii2rgmii.o
+--- /dev/null
++++ b/drivers/net/phy/vsc848x.c
+@@ -0,0 +1,770 @@
++/*
++ * This file is subject to the terms and conditions of the GNU General Public
++ * License. See the file "COPYING" in the main directory of this archive
++ * for more details.
++ *
++ * Copyright (C) 2012 Cavium, Inc.
++ */
++
++#include <linux/platform_device.h>
++#include <linux/of_mdio.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/phy.h>
++#include <linux/memory.h>
++#include <linux/mutex.h>
++#include <linux/nvmem-consumer.h>
++#include <linux/delay.h>
++#include <linux/ctype.h>
++
++#define PMD_RX_SIGNAL_DETECT (MII_ADDR_C45 | 0x01000a)
++#define PMA_TXOUTCTRL2 (MII_ADDR_C45 | 0x018014)
++#define EDC_EYE_QUALITY (MII_ADDR_C45 | 0x018034)
++/* EDC Firmware State Machine Status and Lib Force
++ * 15: library force enable
++ * 14:8 - Library number
++ * 7:5 - N/A
++ * 4 - State force
++ * 3:0 - FW state (1=reset, 2=wait for alarm to clear, 3 = convergence,
++ * 4 = tracking, 5 = freeze)
++ */
++#define EDC_FW_SM_STATUS (MII_ADDR_C45 | 0x018036)
++
++#define BASER_PCS_STATUS (MII_ADDR_C45 | 0x030020)
++#define XGXS_LANE_STATUS (MII_ADDR_C45 | 0x040018)
++#define EWIS_INTR_PEND1 (MII_ADDR_C45 | 0x02EE00)
++#define EWIS_INTR_MASKA_1 (MII_ADDR_C45 | 0x02EE01)
++#define EWIS_INTR_MASKB_1 (MII_ADDR_C45 | 0x02EE02)
++#define EWIS_INTR_STAT2 (MII_ADDR_C45 | 0x02EE03)
++#define EWIS_INTR_PEND2 (MII_ADDR_C45 | 0x02EE04)
++#define EWIS_INTR_MASKA_2 (MII_ADDR_C45 | 0x02EE05)
++#define EWIS_INTR_MASKB_2 (MII_ADDR_C45 | 0x02EE06)
++#define EWIS_FAULT_MASK (MII_ADDR_C45 | 0x02EE07)
++#define EWIS_INTR_PEND3 (MII_ADDR_C45 | 0x02EE08)
++#define EWIS_INTR_MASKA_3 (MII_ADDR_C45 | 0x02EE09)
++#define EWIS_INTR_MASKB_3 (MII_ADDR_C45 | 0x02EE0A)
++
++/* Device ID
++ * 15:0 - device ID
++ */
++#define GBL_DEVICE_ID (MII_ADDR_C45 | 0x1e0000)
++/* Device revision
++ * 15:04 - reserved
++ * 03:00 - revision ID
++ */
++#define GBL_DEVICE_REVISION (MII_ADDR_C45 | 0x1e0001)
++/* Block Level Software Reset
++ * 15:14 - reserved
++ * 13: - software reset EDC 1 (1 = reset, autoclears)
++ * 12: - software reset EDC 0 (1 = reset, autoclears)
++ * 11:10 - reserved
++ * 09: - Software reset channel 1 (1 = reset, autoclears)
++ * 08: - Software reset channel 0 (1 = reset, autoclears)
++ * 07: - Microprocessor reset (0 = normal operation, 1 = reset)
++ * 06: - Software reset BIU (1 = reset, autoclears)
++ * 05: - Software reset TWS slave (1 = reset, autoclears)
++ * 04: - Software reset TWS master (1 = reset, autoclears)
++ * 03: - Software reset MDIO (1 = reset, autoclears)
++ * 02: - Software reset UART (1 = reset, autoclears)
++ * 01: - Global register reset (1 = reset, autoclears)
++ * 00: - Software reset chip (1 = reset, autoclears)
++ */
++#define GBL_BLOCK_LVL_SW_RESET (MII_ADDR_C45 | 0x1e0002)
++#define GBL_GPIO_0_CONFIG1_STATUS (MII_ADDR_C45 | 0x1e0100)
++#define GBL_GPIO_0_CONFIG2 (MII_ADDR_C45 | 0x1e0101)
++#define GBL_DEVICE_ID (MII_ADDR_C45 | 0x1e0000)
++#define GBL_FW_CHECKSUM (MII_ADDR_C45 | 0x1e7fe0)
++#define GBL_FW_WATCHDOG (MII_ADDR_C45 | 0x1e7fe1)
++#define GBL_FW_VERSION (MII_ADDR_C45 | 0x1e7fe2)
++#define GBL_FW_VAR_ACC_CTRL (MII_ADDR_C45 | 0x1e7fe3)
++#define GBL_FW_VAR_ACC_DATA (MII_ADDR_C45 | 0x1e7fe4)
++
++/* The Vitesse VSC848X series are 10G PHYs.
++ *
++ * Some of these devices contain multiple PHYs in a single package and
++ * some features are controlled by a global set of registers shared between
++ * all of the PHY devices. Because of this a nexus is used to handle all
++ * of the PHYs on the same device.
++ *
++ * Unlike some PHY devices, it is up to the driver to read the SFP module
++ * serial EEPROM in order to put the PHY into the right mode. The VSC848X
++ * does not provide an I2C interface so the PHY driver relies on the
++ * external AT24 I2C EEPROM driver to read the module whenever it is inserted.
++ *
++ */
++
++/* Enable LOPC detection (see 0x5B for target state)
++ * 15:12 - channel 3
++ * 11:08 - channel 2
++ * 07:04 - channel 1
++ * 03:00 - channel 0
++ * 1 = enable (default), 0 = disable
++ */
++#define FW_VAR_ENABLE_LOPC 0x58
++/* While in tracking mode, go to this state in response to LOPC assertion
++ * 1 = reset, 2 = wait (default), 3 = converging, 4 = tracking, 5 = freeze
++ */
++#define FW_VAR_LOPC_ASSERT_MODE 0x5B
++/* While in freeze mode, enable state transition upon deassertion of LOPC (see
++ * 0x61 for target state)
++ * 1 - reset, 2 = wait, 3 = converging, 4 = tracking (default), 5 = freeze
++ */
++#define FW_VAR_FREEZE_DEASSERT_MODE 0x61
++/* Current functional mode
++ * See VITESSE_FUNC_MODE_XXX below for values
++ * NOTE: When the firmware is done servicing the mode change request, bit 4
++ * will be set to 1.
++ */
++#define FW_VAR_FUNCTIONAL_MODE 0x94
++/* Current state of graded SPSA process
++ * 3: channel 3
++ * 2: channel 2
++ * 1: channel 1
++ * 0: channel 0
++ * 1 = busy, 2 = done
++ */
++#define FW_VAR_GRADED_SPSA_STATE 0x95
++/* BerScore at start of SPSA cycle */
++#define FW_VAR_BERSCORE_START 0x96
++/* BerScore at end of SPSA cycle */
++#define FW_VAR_BERSCORE_END 0x97
++/* Enable/Disable aggressive track phase on entering tracking state
++ * 15:12 - channel 3
++ * 11:08 - channel 2
++ * 07:04 - channel 1
++ * 03:00 - channel 0
++ * 0 = disable, 1 = enable (default)
++ */
++#define FW_VAR_AGG_TRACKING 0xAF
++
++/* Modes for the PHY firmware */
++#define VITESSE_FUNC_MODE_LIMITING 2 /* Optical */
++#define VITESSE_FUNC_MODE_COPPER 3 /* Copper */
++#define VITESSE_FUNC_MODE_LINEAR 4
++#define VITESSE_FUNC_MODE_KR 5
++#define VITESSE_FUNC_MODE_ZR 7
++#define VITESSE_FUNC_MODE_1G 8
++
++
++struct vsc848x_nexus_mdiobus {
++ struct mii_bus *mii_bus;
++ struct mii_bus *parent_mii_bus;
++ int reg_offset;
++ struct mutex lock; /* Lock used for global register sequences */
++ int phy_irq[PHY_MAX_ADDR];
++};
++
++struct vsc848x_phy_info {
++ int sfp_conn; /* Module connected? */
++ int tx_en_gpio; /* GPIO that enables transmit */
++ int mod_abs_gpio; /* Module Absent GPIO line */
++ int tx_fault_gpio; /* TX Fault GPIO line */
++ int inta_gpio, intb_gpio; /* Interrupt GPIO line (output) */
++ uint8_t mode; /* Mode for module */
++ uint8_t channel; /* channel in multi-phy devices */
++ struct device_node *sfp_node; /* EEPROM NODE for SFP */
++ struct vsc848x_nexus_mdiobus *nexus; /* Nexus for lock */
++};
++
++/**
++ * Maps GPIO lines to the global GPIO config registers.
++ *
++ * Please see the data sheet since the configuration for each GPIO line is
++ * different.
++ */
++static const struct {
++ uint32_t config1_status_reg;
++ uint32_t config2_reg;
++} vcs848x_gpio_to_reg[12] = {
++ { (MII_ADDR_C45 | 0x1e0100), (MII_ADDR_C45 | 0x1e0101) }, /* 0 */
++ { (MII_ADDR_C45 | 0x1e0102), (MII_ADDR_C45 | 0x1e0103) }, /* 1 */
++ { (MII_ADDR_C45 | 0x1e0104), (MII_ADDR_C45 | 0x1e0105) }, /* 2 */
++ { (MII_ADDR_C45 | 0x1e0106), (MII_ADDR_C45 | 0x1e0107) }, /* 3 */
++ { (MII_ADDR_C45 | 0x1e0108), (MII_ADDR_C45 | 0x1e0109) }, /* 4 */
++ { (MII_ADDR_C45 | 0x1e010A), (MII_ADDR_C45 | 0x1e010B) }, /* 5 */
++ { (MII_ADDR_C45 | 0x1e0124), (MII_ADDR_C45 | 0x1e0125) }, /* 6 */
++ { (MII_ADDR_C45 | 0x1e0126), (MII_ADDR_C45 | 0x1e0127) }, /* 7 */
++ { (MII_ADDR_C45 | 0x1e0128), (MII_ADDR_C45 | 0x1e0129) }, /* 8 */
++ { (MII_ADDR_C45 | 0x1e012a), (MII_ADDR_C45 | 0x1e012b) }, /* 9 */
++ { (MII_ADDR_C45 | 0x1e012c), (MII_ADDR_C45 | 0x1e012d) }, /* 10 */
++ { (MII_ADDR_C45 | 0x1e012e), (MII_ADDR_C45 | 0x1e012f) }, /* 11 */
++};
++
++static int vsc848x_probe(struct phy_device *phydev)
++{
++ struct vsc848x_phy_info *dev_info;
++ int ret;
++
++ dev_info = devm_kzalloc(&phydev->mdio.dev, sizeof(*dev_info), GFP_KERNEL);
++ if (dev_info == NULL)
++ return -ENOMEM;
++
++ phydev->priv = dev_info;
++ dev_info->mode = VITESSE_FUNC_MODE_LIMITING; /* Default to optical */
++ phydev->priv = dev_info;
++ dev_info->nexus = phydev->mdio.bus->priv;
++
++ ret = of_property_read_u32(phydev->mdio.dev.of_node, "mod_abs",
++ &dev_info->mod_abs_gpio);
++ if (ret) {
++ dev_err(&phydev->mdio.dev, "%s has invalid mod_abs address\n",
++ phydev->mdio.dev.of_node->full_name);
++ return ret;
++ }
++
++ ret = of_property_read_u32(phydev->mdio.dev.of_node, "tx_fault",
++ &dev_info->tx_fault_gpio);
++ if (ret) {
++ dev_err(&phydev->mdio.dev, "%s has invalid tx_fault address\n",
++ phydev->mdio.dev.of_node->full_name);
++ return ret;
++ }
++
++ ret = of_property_read_u32(phydev->mdio.dev.of_node, "inta",
++ &dev_info->inta_gpio);
++ if (ret)
++ dev_info->inta_gpio = -1;
++
++ ret = of_property_read_u32(phydev->mdio.dev.of_node, "intb",
++ &dev_info->intb_gpio);
++ if (ret)
++ dev_info->intb_gpio = -1;
++
++ ret = of_property_read_u32(phydev->mdio.dev.of_node, "txon",
++ &dev_info->tx_en_gpio);
++ if (ret) {
++ dev_err(&phydev->mdio.dev, "%s has invalid txon gpio address\n",
++ phydev->mdio.dev.of_node->full_name);
++ return -ENXIO;
++ }
++
++ dev_info->sfp_node = of_parse_phandle(phydev->mdio.dev.of_node,
++ "sfp-eeprom", 0);
++ if (!dev_info->sfp_node) {
++ dev_err(&phydev->mdio.dev, "%s has invalid sfp-eeprom node\n",
++ phydev->mdio.dev.of_node->full_name);
++ return -ENXIO;
++ }
++
++ ret = phy_read(phydev, GBL_DEVICE_ID);
++ if (ret < 0) {
++ dev_err(&phydev->mdio.dev, "%s error reading PHY\n",
++ phydev->mdio.dev.of_node->full_name);
++ return ret;
++ }
++
++ /* Check how many devices are in the package to figure out the channel
++ * number.
++ */
++ switch (ret) {
++ case 0x8487: /* Single */
++ case 0x8486:
++ dev_info->channel = 0;
++ break;
++ case 0x8488: /* Dual */
++ dev_info->channel = phydev->mdio.addr & 1;
++ break;
++ case 0x8484: /* Quad */
++ dev_info->channel = phydev->mdio.addr & 3;
++ break;
++ default:
++ dev_err(&phydev->mdio.dev, "%s Unknown Vitesse PHY model %04x\n",
++ phydev->mdio.dev.of_node->full_name, ret);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void vsc848x_remove(struct phy_device *phydev)
++{
++ struct vsc848x_phy_info *dev_info = phydev->priv;
++
++ dev_info(&phydev->mdio.dev, "%s Exiting\n", phydev->mdio.dev.of_node->full_name);
++
++ kfree(dev_info);
++}
++
++static int vsc848x_config_init(struct phy_device *phydev)
++{
++ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
++ phydev->advertising);
++
++ linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
++ phydev->supported);
++
++ phydev->autoneg = 0;
++ phydev->is_c45 = 1;
++ phydev->state = PHY_READY;
++
++ return 0;
++}
++
++static int vsc848x_config_aneg(struct phy_device *phydev)
++{
++ return 0;
++}
++
++static int vsc848x_write_global_var(struct phy_device *phydev, uint8_t channel,
++ uint8_t addr, uint16_t value)
++{
++ struct vsc848x_phy_info *dev_info = phydev->priv;
++ int timeout = 1000;
++ int ret = 0;
++
++ mutex_lock(&(dev_info->nexus->lock));
++
++ /* Wait for firmware download to complete */
++ timeout = 100000;
++ do {
++ ret = phy_read(phydev, MII_ADDR_C45 | 0x1e7fe0);
++ if (ret < 0)
++ goto error;
++ if (ret == 3)
++ break;
++ udelay(100);
++ } while (timeout-- > 0);
++ if (timeout <= 0) {
++ dev_err(&phydev->mdio.dev, "%s Timeout waiting for PHY firmware to load\n",
++ phydev->mdio.dev.of_node->full_name);
++ ret = -EIO;
++ goto error;
++ }
++
++ do {
++ ret = phy_read(phydev, (MII_ADDR_C45 | 0x1e7fe3));
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ break;
++ mdelay(1);
++ } while (timeout-- > 0);
++ if (timeout <= 0) {
++ dev_err(&phydev->mdio.dev, "%s timed out waiting to write global\n",
++ phydev->mdio.dev.of_node->full_name);
++ ret = -EIO;
++ goto error;
++ }
++ ret = phy_write(phydev, (MII_ADDR_C45 | 0x1e7fe4), value);
++ if (ret < 0)
++ goto error;
++
++ ret = phy_write(phydev, (MII_ADDR_C45 | 0x1e7fe3),
++ 0x8000 | ((channel & 3) << 8) | addr);
++ if (ret < 0)
++ goto error;
++
++ /* Wait for value to be written */
++ do {
++ ret = phy_read(phydev, (MII_ADDR_C45 | 0x1e7fe3));
++ if (ret < 0)
++ return ret;
++ if (ret == 0)
++ break;
++ mdelay(1);
++ } while (timeout-- > 0);
++ if (timeout <= 0) {
++ dev_err(&phydev->mdio.dev, "%s timed out waiting to write global\n",
++ phydev->mdio.dev.of_node->full_name);
++ ret = -EIO;
++ goto error;
++ }
++ ret = 0;
++
++error:
++ mutex_unlock(&(dev_info->nexus->lock));
++
++ return ret;
++}
++
++/**
++ * Dumps out the contents of the SFP EEPROM when errors are detected
++ *
++ * @param eeprom - contents of SFP+ EEPROM
++ */
++static void dump_sfp_eeprom(const uint8_t eeprom[64])
++{
++ int addr = 0;
++ int i;
++ char line[17];
++ line[16] = '\0';
++
++ pr_info("SFP+ EEPROM contents:\n");
++ while (addr < 64) {
++ pr_info(" %02x: ", addr);
++ for (i = 0; i < 16; i++)
++ pr_cont("%02x ", eeprom[addr + i]);
++ for (i = 0; i < 16; i++) {
++ if (!isprint(eeprom[addr + i]) ||
++ eeprom[addr + i] >= 0x80)
++ line[i] = '.';
++ else
++ line[i] = eeprom[addr + i];
++ }
++ pr_cont(" %s\n", line);
++ addr += 16;
++ }
++ pr_info("\n");
++}
++
++/**
++ * Read the SFP+ module EEPROM and program the Vitesse PHY accordingly.
++ *
++ * @param phydev - Phy device
++ *
++ * @returns 0 for success, error otherwise.
++ */
++static int vsc848x_read_sfp(struct phy_device *phydev)
++{
++ struct vsc848x_phy_info *dev_info = phydev->priv;
++ struct nvmem_device *nvmem;
++ uint8_t sfp_buffer[64];
++ uint8_t csum;
++ uint8_t mode = VITESSE_FUNC_MODE_LIMITING;
++ const char *mode_str = "Unknown";
++ int i;
++ int ret = 0;
++
++ nvmem = of_nvmem_device_get_by_phandle(dev_info->sfp_node);
++
++ /* For details on the SFP+ EEPROM contents see the SFF-8472
++ * Diagnostic Monitoring Interface for Optical Transceivers.
++ *
++ * This is based on revision 11.1, October 26, 2012.
++ */
++ if (!IS_ERR(nvmem)) {
++ ret = nvmem_device_read(nvmem, 0, 64, sfp_buffer);
++
++ if (!ret)
++ return -ENODEV;
++ }
++
++ /* Validate SFP checksum */
++ csum = 0;
++ for (i = 0; i < 63; i++)
++ csum += sfp_buffer[i];
++ if (csum != sfp_buffer[63]) {
++ dev_err(&phydev->mdio.dev, "%s SFP EEPROM checksum bad, calculated 0x%02x, should be 0x%02x\n",
++ phydev->mdio.dev.of_node->full_name, csum, sfp_buffer[63]);
++ dump_sfp_eeprom(sfp_buffer);
++ return -ENXIO;
++ }
++
++ /* Make sure it's a SFP or SFP+ module */
++ if (sfp_buffer[0] != 3) {
++ dev_err(&phydev->mdio.dev, "%s module is not SFP or SFP+\n",
++ phydev->mdio.dev.of_node->full_name);
++ dump_sfp_eeprom(sfp_buffer);
++ return -ENXIO;
++ }
++
++ /* Check connector type */
++ switch (sfp_buffer[2]) {
++ case 0x01: /* SC */
++ mode = VITESSE_FUNC_MODE_LIMITING;
++ break;
++ case 0x07: /* LC */
++ mode = VITESSE_FUNC_MODE_LIMITING;
++ break;
++ case 0x0B: /* Optical pigtail */
++ mode = VITESSE_FUNC_MODE_LIMITING;
++ break;
++ case 0x21: /* Copper pigtail */
++ case 0x22: /* RJ45 */
++ mode = VITESSE_FUNC_MODE_COPPER;
++ break;
++ default:
++ dev_err(&phydev->mdio.dev, "%s Unknown Connector Type 0x%x\n",
++ phydev->mdio.dev.of_node->full_name, sfp_buffer[2]);
++ dump_sfp_eeprom(sfp_buffer);
++ return -EINVAL;
++ }
++
++ if (mode == VITESSE_FUNC_MODE_LIMITING) {
++ if (mode_str[3] & 0x10)
++ mode_str = "10GBase-SR";
++ else if (mode_str[3] & 0x20)
++ mode_str = "10GBase-LR";
++ else if (mode_str[3] & 0x40)
++ mode_str = "10GBase-LRM";
++ else if (mode_str[3] & 0x80)
++ mode_str = "10GBase-ER";
++ else
++ dev_err(&phydev->mdio.dev, "%s unknown SFP compatibility\n"
++ "type ID: 0x%02x, extended ID: 0x%02x, Connector type code: 0x%02x\n"
++ "Transceiver compatibility code: (%02x) %02x %02x %02x %02x %02x %02x %02x %02x\n",
++ phydev->mdio.dev.of_node->full_name, sfp_buffer[0],
++ sfp_buffer[1], sfp_buffer[2], sfp_buffer[36],
++ sfp_buffer[3], sfp_buffer[4], sfp_buffer[5],
++ sfp_buffer[6], sfp_buffer[7], sfp_buffer[8],
++ sfp_buffer[9], sfp_buffer[10]);
++ } else if (mode == VITESSE_FUNC_MODE_COPPER) {
++ if (sfp_buffer[8] & 0x4) {
++ mode_str = "10G Passive Copper";
++ } else if (sfp_buffer[8] & 0x8) {
++ mode_str = "10G Active Copper";
++ mode = VITESSE_FUNC_MODE_LIMITING;
++ } else {
++ dev_err(&phydev->mdio.dev, "%s Unknown SFP+ copper cable capability 0x%02x\n"
++ "Transceiver compatibility code: (%02x) %02x %02x %02x %02x %02x %02x %02x %02x\n",
++ phydev->mdio.dev.of_node->full_name, sfp_buffer[8],
++ sfp_buffer[36], sfp_buffer[3], sfp_buffer[4],
++ sfp_buffer[5], sfp_buffer[6], sfp_buffer[7],
++ sfp_buffer[8], sfp_buffer[9], sfp_buffer[10]);
++ return -EINVAL;
++ }
++ } else {
++ dev_err(&phydev->mdio.dev, "%s Unsupported phy mode %d\n",
++ phydev->mdio.dev.of_node->full_name, mode);
++ dump_sfp_eeprom(sfp_buffer);
++ }
++
++ vsc848x_write_global_var(phydev, dev_info->channel, 0x94, mode);
++
++ /* Adjust PMA_TXOUTCTRL2 based on cable length. Vitesse recommends
++ * 0x1606 for copper cable lengths 5M and longer.
++ *
++ * The default value is 0x1300.
++ */
++ if (mode == VITESSE_FUNC_MODE_COPPER) {
++ if (sfp_buffer[18] >= 5)
++ ret = phy_write(phydev, PMA_TXOUTCTRL2, 0x1606);
++ else
++ ret = phy_write(phydev, PMA_TXOUTCTRL2, 0x1300);
++ if (ret)
++ return ret;
++ }
++
++ /* Reset the state machine */
++ ret = phy_write(phydev, MII_ADDR_C45 | 0x18034, 0x11);
++
++ dev_info(&phydev->mdio.dev, "%s configured for %s\n",
++ phydev->mdio.dev.of_node->full_name, mode_str);
++
++ return ret;
++}
++
++static int vsc848x_read_status(struct phy_device *phydev)
++{
++ struct vsc848x_phy_info *dev_info = phydev->priv;
++ int rx_signal_detect;
++ int pcs_status;
++ int xgxs_lane_status;
++ int value;
++ int sfp_conn;
++ int ret;
++
++ /* Check if a module is plugged in */
++ value = phy_read(phydev, vcs848x_gpio_to_reg[dev_info->mod_abs_gpio]
++ .config1_status_reg);
++ if (value < 0)
++ return value;
++
++ sfp_conn = !(value & 0x400);
++ if (sfp_conn != dev_info->sfp_conn) {
++ /* We detect a module being plugged in */
++ if (sfp_conn) {
++ ret = vsc848x_read_sfp(phydev);
++ if (ret < 0)
++ goto no_link;
++ dev_info->sfp_conn = sfp_conn;
++ } else {
++ dev_info(&phydev->mdio.dev, "%s module unplugged\n",
++ phydev->mdio.dev.of_node->full_name);
++ dev_info->sfp_conn = sfp_conn;
++ goto no_link;
++ }
++ }
++
++ rx_signal_detect = phy_read(phydev, PMD_RX_SIGNAL_DETECT);
++ if (rx_signal_detect < 0)
++ return rx_signal_detect;
++
++ if ((rx_signal_detect & 1) == 0)
++ goto no_link;
++
++ pcs_status = phy_read(phydev, BASER_PCS_STATUS);
++ if (pcs_status < 0)
++ return pcs_status;
++
++ if ((pcs_status & 1) == 0)
++ goto no_link;
++
++ xgxs_lane_status = phy_read(phydev, XGXS_LANE_STATUS);
++ if (xgxs_lane_status < 0)
++ return xgxs_lane_status;
++
++ if ((xgxs_lane_status & 0x1000) == 0)
++ goto no_link;
++
++ phydev->speed = 10000;
++ phydev->link = 1;
++ phydev->duplex = 1;
++ return 0;
++no_link:
++ phydev->link = 0;
++ return 0;
++}
++
++static struct of_device_id vsc848x_match[] = {
++ {
++ .compatible = "vitesse,vsc8488",
++ },
++ {
++ .compatible = "vitesse,vsc8486",
++ },
++ {
++ .compatible = "vitesse,vsc8484",
++ },
++ {},
++};
++MODULE_DEVICE_TABLE(of, vsc848x_match);
++
++static struct phy_driver vsc848x_phy_driver = {
++ .phy_id = 0x00070400,
++ .phy_id_mask = 0xfffffff0,
++ .name = "Vitesse VSC848X",
++ .config_init = vsc848x_config_init,
++ .probe = vsc848x_probe,
++ .remove = vsc848x_remove,
++ .config_aneg = vsc848x_config_aneg,
++ .read_status = vsc848x_read_status,
++/*
++ .driver = {
++ .owner = THIS_MODULE,
++ .of_match_table = vsc848x_match,
++ },
++*/
++};
++
++/* Phy nexus support below. */
++
++static int vsc848x_nexus_read(struct mii_bus *bus, int phy_id, int regnum)
++{
++ struct vsc848x_nexus_mdiobus *p = bus->priv;
++ return p->parent_mii_bus->read(p->parent_mii_bus,
++ phy_id + p->reg_offset,
++ regnum);
++}
++
++static int vsc848x_nexus_write(struct mii_bus *bus, int phy_id,
++ int regnum, u16 val)
++{
++ struct vsc848x_nexus_mdiobus *p = bus->priv;
++ return p->parent_mii_bus->write(p->parent_mii_bus,
++ phy_id + p->reg_offset,
++ regnum, val);
++}
++
++static int vsc848x_nexus_probe(struct platform_device *pdev)
++{
++ struct vsc848x_nexus_mdiobus *bus;
++ const char *bus_id;
++ int len;
++ int err = 0;
++
++ bus = devm_kzalloc(&pdev->dev, sizeof(*bus), GFP_KERNEL);
++ if (!bus)
++ return -ENOMEM;
++
++ bus->parent_mii_bus = container_of(pdev->dev.parent,
++ struct mii_bus, dev);
++
++ /* The PHY nexux must have a reg property in the range [0-31] */
++ err = of_property_read_u32(pdev->dev.of_node, "reg", &bus->reg_offset);
++ if (err) {
++ dev_err(&pdev->dev, "%s has invalid PHY address\n",
++ pdev->dev.of_node->full_name);
++ return err;
++ }
++
++ bus->mii_bus = mdiobus_alloc();
++ if (!bus->mii_bus)
++ return -ENOMEM;
++
++ bus->mii_bus->priv = bus;
++#if 0
++ bus->mii_bus->irq = bus->phy_irq;
++#endif
++ bus->mii_bus->name = "vsc848x_nexus";
++ bus_id = bus->parent_mii_bus->id;
++ len = strlen(bus_id);
++ if (len > MII_BUS_ID_SIZE - 4)
++ bus_id += len - (MII_BUS_ID_SIZE - 4);
++ snprintf(bus->mii_bus->id, MII_BUS_ID_SIZE, "%s:%02x",
++ bus_id, bus->reg_offset);
++ bus->mii_bus->parent = &pdev->dev;
++
++ bus->mii_bus->read = vsc848x_nexus_read;
++ bus->mii_bus->write = vsc848x_nexus_write;
++ mutex_init(&bus->lock);
++
++ dev_set_drvdata(&pdev->dev, bus);
++
++ err = of_mdiobus_register(bus->mii_bus, pdev->dev.of_node);
++ if (err) {
++ dev_err(&pdev->dev, "Error registering with device tree\n");
++ goto fail_register;
++ }
++
++ return 0;
++
++fail_register:
++ dev_err(&pdev->dev, "Failed to register\n");
++ mdiobus_free(bus->mii_bus);
++ return err;
++}
++
++static int vsc848x_nexus_remove(struct platform_device *pdev)
++{
++ return 0;
++}
++
++static struct of_device_id vsc848x_nexus_match[] = {
++ {
++ .compatible = "vitesse,vsc8488-nexus",
++ },
++ {
++ .compatible = "vitesse,vsc8486-nexus",
++ },
++ {
++ .compatible = "vitesse,vsc8484-nexus",
++ },
++ {},
++};
++MODULE_DEVICE_TABLE(of, vsc848x_nexus_match);
++
++static struct platform_driver vsc848x_nexus_driver = {
++ .driver = {
++ .name = "vsc848x-nexus",
++ .owner = THIS_MODULE,
++ .of_match_table = vsc848x_nexus_match,
++ },
++ .probe = vsc848x_nexus_probe,
++ .remove = vsc848x_nexus_remove,
++};
++
++static int __init vsc848x_mod_init(void)
++{
++ int rv;
++
++ rv = platform_driver_register(&vsc848x_nexus_driver);
++ if (rv)
++ return rv;
++
++ rv = phy_driver_register(&vsc848x_phy_driver, THIS_MODULE);
++
++ return rv;
++}
++module_init(vsc848x_mod_init);
++
++static void __exit vsc848x_mod_exit(void)
++{
++ phy_driver_unregister(&vsc848x_phy_driver);
++ platform_driver_unregister(&vsc848x_nexus_driver);
++}
++module_exit(vsc848x_mod_exit);
++
++MODULE_DESCRIPTION("Driver for Vitesse VSC848X PHY");
++MODULE_AUTHOR("David Daney and Aaron Williams");
++MODULE_LICENSE("GPL");
--- /dev/null
+From b4899d5d97b42cf994970d36f00e5cb1af51702f Mon Sep 17 00:00:00 2001
+From: Stijn Tintel <stijn@linux-ipv6.be>
+Date: Wed, 21 Apr 2021 19:09:41 +0300
+Subject: [PATCH 1/5] nvmem: core: add of_nvmem_device_get_by_phandle()
+
+This will be used by the vsc848x PHY driver to read the SFP+ eeprom.
+
+Signed-off-by: Stijn Tintel <stijn@linux-ipv6.be>
+---
+ drivers/nvmem/core.c | 14 ++++++++++++++
+ include/linux/nvmem-consumer.h | 7 +++++++
+ 2 files changed, 21 insertions(+)
+
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -868,6 +868,20 @@ struct nvmem_device *of_nvmem_device_get
+ return nvmem;
+ }
+ EXPORT_SYMBOL_GPL(of_nvmem_device_get);
++
++/**
++ * of_nvmem_device_get_by_phandle() - Get nvmem device by a phandle
++ *
++ * @np: Device tree node of the nvmem device.
++ *
++ * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device
++ * on success.
++ */
++struct nvmem_device *of_nvmem_device_get_by_phandle(struct device_node* np)
++{
++ return __nvmem_device_get(np, device_match_of_node);
++}
++EXPORT_SYMBOL_GPL(of_nvmem_device_get_by_phandle);
+ #endif
+
+ /**
+--- a/include/linux/nvmem-consumer.h
++++ b/include/linux/nvmem-consumer.h
+@@ -228,6 +228,7 @@ struct nvmem_cell *of_nvmem_cell_get(str
+ const char *id);
+ struct nvmem_device *of_nvmem_device_get(struct device_node *np,
+ const char *name);
++struct nvmem_device *of_nvmem_device_get_by_phandle(struct device_node* np);
+ #else
+ static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np,
+ const char *id)
+@@ -240,6 +241,12 @@ static inline struct nvmem_device *of_nv
+ {
+ return ERR_PTR(-EOPNOTSUPP);
+ }
++
++static inline struct nvmem_device
++*of_nvmem_device_get_by_phandle(struct device_node* np)
++{
++ return ERR_PTR(-EOPNOTSUPP);
++}
+ #endif /* CONFIG_NVMEM && CONFIG_OF */
+
+ #endif /* ifndef _LINUX_NVMEM_CONSUMER_H */