|
|
@@ -1015,26 +1015,31 @@ Signed-off-by: Weijie Gao <[email protected]>
|
|
|
+#endif /* __EN8801S_H */
|
|
|
--- /dev/null
|
|
|
+++ b/drivers/net/phy/air_en8811h.c
|
|
|
-@@ -0,0 +1,725 @@
|
|
|
-+// SPDX-License-Identifier: GPL-2.0
|
|
|
-+/*************************************************
|
|
|
-+ * FILE NAME: air_en8811h.c
|
|
|
-+ * PURPOSE:
|
|
|
-+ * EN8811H PHY Driver for Uboot
|
|
|
-+ * NOTES:
|
|
|
+@@ -0,0 +1,886 @@
|
|
|
++// SPDX-License-Identifier: GPL-2.0+
|
|
|
++/*
|
|
|
++ * Driver for the Airoha EN8811H 2.5 Gigabit PHY.
|
|
|
+ *
|
|
|
-+ * Copyright (C) 2023 Airoha Technology Corp.
|
|
|
-+ *************************************************/
|
|
|
-+
|
|
|
-+/* INCLUDE FILE DECLARATIONS
|
|
|
-+*/
|
|
|
-+#include <config.h>
|
|
|
-+#include <eth_phy.h>
|
|
|
++ * Limitations of the EN8811H:
|
|
|
++ * - Only full duplex supported
|
|
|
++ * - Forced speed (AN off) is not supported by hardware (100Mbps)
|
|
|
++ *
|
|
|
++ * Source originated from linux air_en8811h.c
|
|
|
++ *
|
|
|
++ * Copyright (C) 2025 Airoha Technology Corp.
|
|
|
++ */
|
|
|
+#include <phy.h>
|
|
|
+#include <errno.h>
|
|
|
++#include <log.h>
|
|
|
++#include <env.h>
|
|
|
+#include <malloc.h>
|
|
|
-+#include <version.h>
|
|
|
-+#include "air_en8811h.h"
|
|
|
++#include <fs.h>
|
|
|
++#include <asm/unaligned.h>
|
|
|
++#include <linux/iopoll.h>
|
|
|
++#include <linux/bitops.h>
|
|
|
++#include <linux/compat.h>
|
|
|
++#include <dm/device_compat.h>
|
|
|
++#include <u-boot/crc.h>
|
|
|
+
|
|
|
+#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI
|
|
|
+#include <ubi_uboot.h>
|
|
|
@@ -1048,882 +1053,852 @@ Signed-off-by: Weijie Gao <[email protected]>
|
|
|
+#include <mtd.h>
|
|
|
+#endif
|
|
|
+
|
|
|
-+#if AIR_UBOOT_REVISION > 0x202004
|
|
|
-+#include <linux/delay.h>
|
|
|
-+#endif
|
|
|
++#define EN8811H_PHY_ID 0x03a2a411
|
|
|
++
|
|
|
++#define AIR_FW_ADDR_DM 0x00000000
|
|
|
++#define AIR_FW_ADDR_DSP 0x00100000
|
|
|
++
|
|
|
++#define EN8811H_MD32_DM_SIZE 0x4000
|
|
|
++#define EN8811H_MD32_DSP_SIZE 0x20000
|
|
|
++
|
|
|
++ #define EN8811H_FW_CTRL_1 0x0f0018
|
|
|
++ #define EN8811H_FW_CTRL_1_START 0x0
|
|
|
++ #define EN8811H_FW_CTRL_1_FINISH 0x1
|
|
|
++ #define EN8811H_FW_CTRL_2 0x800000
|
|
|
++ #define EN8811H_FW_CTRL_2_LOADING BIT(11)
|
|
|
++
|
|
|
++ /* MII Registers */
|
|
|
++ #define AIR_AUX_CTRL_STATUS 0x1d
|
|
|
++ #define AIR_AUX_CTRL_STATUS_SPEED_MASK GENMASK(4, 2)
|
|
|
++ #define AIR_AUX_CTRL_STATUS_SPEED_100 0x4
|
|
|
++ #define AIR_AUX_CTRL_STATUS_SPEED_1000 0x8
|
|
|
++ #define AIR_AUX_CTRL_STATUS_SPEED_2500 0xc
|
|
|
++
|
|
|
++#define AIR_EXT_PAGE_ACCESS 0x1f
|
|
|
++#define AIR_PHY_PAGE_STANDARD 0x0000
|
|
|
++#define AIR_PHY_PAGE_EXTENDED_4 0x0004
|
|
|
++
|
|
|
++/* MII Registers Page 4*/
|
|
|
++#define AIR_BPBUS_MODE 0x10
|
|
|
++#define AIR_BPBUS_MODE_ADDR_FIXED 0x0000
|
|
|
++#define AIR_BPBUS_MODE_ADDR_INCR BIT(15)
|
|
|
++#define AIR_BPBUS_WR_ADDR_HIGH 0x11
|
|
|
++#define AIR_BPBUS_WR_ADDR_LOW 0x12
|
|
|
++#define AIR_BPBUS_WR_DATA_HIGH 0x13
|
|
|
++#define AIR_BPBUS_WR_DATA_LOW 0x14
|
|
|
++#define AIR_BPBUS_RD_ADDR_HIGH 0x15
|
|
|
++#define AIR_BPBUS_RD_ADDR_LOW 0x16
|
|
|
++#define AIR_BPBUS_RD_DATA_HIGH 0x17
|
|
|
++#define AIR_BPBUS_RD_DATA_LOW 0x18
|
|
|
++
|
|
|
++/* Registers on MDIO_MMD_VEND1 */
|
|
|
++#define EN8811H_PHY_FW_STATUS 0x8009
|
|
|
++#define EN8811H_PHY_READY 0x02
|
|
|
++
|
|
|
++/* Registers on MDIO_MMD_VEND2 */
|
|
|
++#define AIR_PHY_LED_BCR 0x021
|
|
|
++#define AIR_PHY_LED_BCR_MODE_MASK GENMASK(1, 0)
|
|
|
++#define AIR_PHY_LED_BCR_TIME_TEST BIT(2)
|
|
|
++#define AIR_PHY_LED_BCR_CLK_EN BIT(3)
|
|
|
++#define AIR_PHY_LED_BCR_EXT_CTRL BIT(15)
|
|
|
++
|
|
|
++#define AIR_PHY_LED_DUR_ON 0x022
|
|
|
++
|
|
|
++#define AIR_PHY_LED_DUR_BLINK 0x023
|
|
|
++
|
|
|
++#define AIR_PHY_LED_ON(i) (0x024 + ((i) * 2))
|
|
|
++#define AIR_PHY_LED_ON_MASK (GENMASK(6, 0) | BIT(8))
|
|
|
++#define AIR_PHY_LED_ON_LINK1000 BIT(0)
|
|
|
++#define AIR_PHY_LED_ON_LINK100 BIT(1)
|
|
|
++#define AIR_PHY_LED_ON_LINK10 BIT(2)
|
|
|
++#define AIR_PHY_LED_ON_LINKDOWN BIT(3)
|
|
|
++#define AIR_PHY_LED_ON_FDX BIT(4) /* Full duplex */
|
|
|
++#define AIR_PHY_LED_ON_HDX BIT(5) /* Half duplex */
|
|
|
++#define AIR_PHY_LED_ON_FORCE_ON BIT(6)
|
|
|
++#define AIR_PHY_LED_ON_LINK2500 BIT(8)
|
|
|
++#define AIR_PHY_LED_ON_POLARITY BIT(14)
|
|
|
++#define AIR_PHY_LED_ON_ENABLE BIT(15)
|
|
|
++
|
|
|
++#define AIR_PHY_LED_BLINK(i) (0x025 + ((i) * 2))
|
|
|
++#define AIR_PHY_LED_BLINK_1000TX BIT(0)
|
|
|
++#define AIR_PHY_LED_BLINK_1000RX BIT(1)
|
|
|
++#define AIR_PHY_LED_BLINK_100TX BIT(2)
|
|
|
++#define AIR_PHY_LED_BLINK_100RX BIT(3)
|
|
|
++#define AIR_PHY_LED_BLINK_10TX BIT(4)
|
|
|
++#define AIR_PHY_LED_BLINK_10RX BIT(5)
|
|
|
++#define AIR_PHY_LED_BLINK_COLLISION BIT(6)
|
|
|
++#define AIR_PHY_LED_BLINK_RX_CRC_ERR BIT(7)
|
|
|
++#define AIR_PHY_LED_BLINK_RX_IDLE_ERR BIT(8)
|
|
|
++#define AIR_PHY_LED_BLINK_FORCE_BLINK BIT(9)
|
|
|
++#define AIR_PHY_LED_BLINK_2500TX BIT(10)
|
|
|
++#define AIR_PHY_LED_BLINK_2500RX BIT(11)
|
|
|
++
|
|
|
++#define EN8811H_FW_VERSION 0x3b3c
|
|
|
++
|
|
|
++#define EN8811H_POLARITY 0xca0f8
|
|
|
++#define EN8811H_POLARITY_TX_NORMAL BIT(0)
|
|
|
++#define EN8811H_POLARITY_RX_REVERSE BIT(1)
|
|
|
++
|
|
|
++#define EN8811H_CLK_CGM 0xcf958
|
|
|
++#define EN8811H_CLK_CGM_CKO BIT(26)
|
|
|
++#define EN8811H_HWTRAP1 0xcf914
|
|
|
++#define EN8811H_HWTRAP1_CKO BIT(12)
|
|
|
++
|
|
|
++#define air_upper_16_bits(n) ((u16)((n) >> 16))
|
|
|
++#define air_lower_16_bits(n) ((u16)((n) & 0xffff))
|
|
|
++#define clear_bit(bit, bitmap) __clear_bit(bit, bitmap)
|
|
|
++
|
|
|
++/* Led definitions */
|
|
|
++#define EN8811H_LED_COUNT 3
|
|
|
++
|
|
|
++struct led {
|
|
|
++ unsigned long rules;
|
|
|
++ unsigned long state;
|
|
|
++};
|
|
|
+
|
|
|
-+/**************************
|
|
|
-+ * GPIO5 <-> BASE_T_LED0,
|
|
|
-+ * GPIO4 <-> BASE_T_LED1,
|
|
|
-+ * GPIO3 <-> BASE_T_LED2,
|
|
|
-+ **************************/
|
|
|
-+/* User-defined.B */
|
|
|
-+#define AIR_LED_SUPPORT
|
|
|
-+#ifdef AIR_LED_SUPPORT
|
|
|
-+static const struct air_base_t_led_cfg_s led_cfg[3] = {
|
|
|
-+/*********************************************************************
|
|
|
-+ *Enable, GPIO, LED Polarity, LED ON, LED Blink
|
|
|
-+**********************************************************************/
|
|
|
-+ {1, AIR_LED0_GPIO5, AIR_ACTIVE_HIGH, AIR_LED0_ON, AIR_LED0_BLK},
|
|
|
-+ {1, AIR_LED1_GPIO4, AIR_ACTIVE_HIGH, AIR_LED1_ON, AIR_LED1_BLK},
|
|
|
-+ {1, AIR_LED2_GPIO3, AIR_ACTIVE_HIGH, AIR_LED2_ON, AIR_LED2_BLK},
|
|
|
++enum {
|
|
|
++ AIR_PHY_LED_STATE_FORCE_ON,
|
|
|
++ AIR_PHY_LED_STATE_FORCE_BLINK,
|
|
|
+};
|
|
|
-+static const u16 led_dur = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
|
|
|
-+#endif
|
|
|
-+/* User-defined.E */
|
|
|
-+/*************************************************************
|
|
|
-+ * F U N C T I O N S
|
|
|
-+ **************************************************************/
|
|
|
-+/* Airoha MII read function */
|
|
|
-+static int air_mii_cl22_read(struct mii_dev *bus, int phy_addr, int phy_register)
|
|
|
-+{
|
|
|
-+ int read_data = bus->read(bus, phy_addr, MDIO_DEVAD_NONE, phy_register);
|
|
|
+
|
|
|
-+ if (read_data < 0)
|
|
|
-+ return -EIO;
|
|
|
-+ return read_data;
|
|
|
-+}
|
|
|
++enum {
|
|
|
++ AIR_PHY_LED_DUR_BLINK_32MS,
|
|
|
++ AIR_PHY_LED_DUR_BLINK_64MS,
|
|
|
++ AIR_PHY_LED_DUR_BLINK_128MS,
|
|
|
++ AIR_PHY_LED_DUR_BLINK_256MS,
|
|
|
++ AIR_PHY_LED_DUR_BLINK_512MS,
|
|
|
++ AIR_PHY_LED_DUR_BLINK_1024MS,
|
|
|
++};
|
|
|
+
|
|
|
-+/* Airoha MII write function */
|
|
|
-+static int air_mii_cl22_write(struct mii_dev *bus, int phy_addr, int phy_register, int write_data)
|
|
|
-+{
|
|
|
-+ int ret = 0;
|
|
|
++enum {
|
|
|
++ AIR_LED_DISABLE,
|
|
|
++ AIR_LED_ENABLE,
|
|
|
++};
|
|
|
+
|
|
|
-+ ret = bus->write(bus, phy_addr, MDIO_DEVAD_NONE, phy_register, write_data);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("bus->write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ return ret;
|
|
|
-+}
|
|
|
++enum {
|
|
|
++ AIR_ACTIVE_LOW,
|
|
|
++ AIR_ACTIVE_HIGH,
|
|
|
++};
|
|
|
+
|
|
|
-+static int air_mii_cl45_read(struct phy_device *phydev, int devad, u16 reg)
|
|
|
-+{
|
|
|
-+ int ret = 0;
|
|
|
-+ int data;
|
|
|
++enum {
|
|
|
++ AIR_LED_MODE_DISABLE,
|
|
|
++ AIR_LED_MODE_USER_DEFINE,
|
|
|
++};
|
|
|
+
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return INVALID_DATA;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return INVALID_DATA;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return INVALID_DATA;
|
|
|
-+ }
|
|
|
-+ data = phy_read(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG);
|
|
|
-+ return data;
|
|
|
-+}
|
|
|
++/* Trigger specific enum */
|
|
|
++enum air_led_trigger_netdev_modes {
|
|
|
++ AIR_TRIGGER_NETDEV_LINK = 0,
|
|
|
++ AIR_TRIGGER_NETDEV_LINK_10,
|
|
|
++ AIR_TRIGGER_NETDEV_LINK_100,
|
|
|
++ AIR_TRIGGER_NETDEV_LINK_1000,
|
|
|
++ AIR_TRIGGER_NETDEV_LINK_2500,
|
|
|
++ AIR_TRIGGER_NETDEV_LINK_5000,
|
|
|
++ AIR_TRIGGER_NETDEV_LINK_10000,
|
|
|
++ AIR_TRIGGER_NETDEV_HALF_DUPLEX,
|
|
|
++ AIR_TRIGGER_NETDEV_FULL_DUPLEX,
|
|
|
++ AIR_TRIGGER_NETDEV_TX,
|
|
|
++ AIR_TRIGGER_NETDEV_RX,
|
|
|
++ AIR_TRIGGER_NETDEV_TX_ERR,
|
|
|
++ AIR_TRIGGER_NETDEV_RX_ERR,
|
|
|
++
|
|
|
++ /* Keep last */
|
|
|
++ __AIR_TRIGGER_NETDEV_MAX,
|
|
|
++};
|
|
|
+
|
|
|
-+static int air_mii_cl45_write(struct phy_device *phydev, int devad, u16 reg, u16 write_data)
|
|
|
-+{
|
|
|
-+ int ret = 0;
|
|
|
++/* Default LED setup:
|
|
|
++ * GPIO5 <-> LED0 On: Link detected, blink Rx/Tx
|
|
|
++ * GPIO4 <-> LED1 On: Link detected at 2500 and 1000 Mbps
|
|
|
++ * GPIO3 <-> LED2 On: Link detected at 2500 and 100 Mbps
|
|
|
++ */
|
|
|
++#define AIR_DEFAULT_TRIGGER_LED0 (BIT(AIR_TRIGGER_NETDEV_LINK) | \
|
|
|
++ BIT(AIR_TRIGGER_NETDEV_RX) | \
|
|
|
++ BIT(AIR_TRIGGER_NETDEV_TX))
|
|
|
++#define AIR_DEFAULT_TRIGGER_LED1 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \
|
|
|
++ BIT(AIR_TRIGGER_NETDEV_LINK_1000))
|
|
|
++#define AIR_DEFAULT_TRIGGER_LED2 (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | \
|
|
|
++ BIT(AIR_TRIGGER_NETDEV_LINK_100))
|
|
|
++
|
|
|
++#define AIR_PHY_LED_DUR_UNIT 781
|
|
|
++#define AIR_PHY_LED_DUR (AIR_PHY_LED_DUR_UNIT << AIR_PHY_LED_DUR_BLINK_64MS)
|
|
|
++
|
|
|
++struct en8811h_priv {
|
|
|
++ int firmware_version;
|
|
|
++ bool mcu_needs_restart;
|
|
|
++ struct led led[EN8811H_LED_COUNT];
|
|
|
++};
|
|
|
+
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, devad);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, reg);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ACC_CTL_REG, MMD_OP_MODE_DATA | devad);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, MII_MMD_ADDR_DATA_REG, write_data);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+/* Use default PBUS_PHY_ID */
|
|
|
-+/* EN8811H PBUS write function */
|
|
|
-+static int air_pbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned long pbus_data)
|
|
|
++static int air_phy_read_page(struct phy_device *phydev)
|
|
|
+{
|
|
|
-+ int ret = 0;
|
|
|
-+ struct mii_dev *mbus = phydev->bus;
|
|
|
-+
|
|
|
-+ ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), 0x1F, (unsigned int)(pbus_address >> 6));
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), (unsigned int)((pbus_address >> 2) & 0xf), (unsigned int)(pbus_data & 0xFFFF));
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_mii_cl22_write(mbus, ((phydev->addr) + 8), 0x10, (unsigned int)(pbus_data >> 16));
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ return 0;
|
|
|
++ return phy_read(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS);
|
|
|
+}
|
|
|
+
|
|
|
-+/* EN8811H BUCK write function */
|
|
|
-+static int air_buckpbus_reg_write(struct phy_device *phydev, unsigned long pbus_address, unsigned int pbus_data)
|
|
|
++static int air_phy_write_page(struct phy_device *phydev, int page)
|
|
|
+{
|
|
|
-+ int ret = 0;
|
|
|
-+
|
|
|
-+ /* page 4 */
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (unsigned int)((pbus_address >> 16) & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (unsigned int)(pbus_address & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x13, (unsigned int)((pbus_data >> 16) & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, (unsigned int)(pbus_data & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ return 0;
|
|
|
++ return phy_write(phydev, MDIO_DEVAD_NONE, AIR_EXT_PAGE_ACCESS, page);
|
|
|
+}
|
|
|
+
|
|
|
-+/* EN8811H BUCK read function */
|
|
|
-+static unsigned int air_buckpbus_reg_read(struct phy_device *phydev, unsigned long pbus_address)
|
|
|
++int air_phy_select_page(struct phy_device *phydev, int page)
|
|
|
+{
|
|
|
-+ unsigned int pbus_data = 0, pbus_data_low, pbus_data_high;
|
|
|
-+ int ret = 0;
|
|
|
++ int ret, oldpage;
|
|
|
+
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4); /* page 4 */
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return PBUS_INVALID_DATA;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return PBUS_INVALID_DATA;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x15, (unsigned int)((pbus_address >> 16) & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return PBUS_INVALID_DATA;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x16, (unsigned int)(pbus_address & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return PBUS_INVALID_DATA;
|
|
|
-+ }
|
|
|
++ oldpage = air_phy_read_page(phydev);
|
|
|
++ if (oldpage < 0)
|
|
|
++ return oldpage;
|
|
|
+
|
|
|
-+ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, 0x17);
|
|
|
-+ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, 0x18);
|
|
|
-+ pbus_data = (pbus_data_high << 16) + pbus_data_low;
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)0);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ return pbus_data;
|
|
|
++ if (oldpage != page) {
|
|
|
++ ret = air_phy_write_page(phydev, page);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++
|
|
|
++ return oldpage;
|
|
|
+}
|
|
|
+
|
|
|
-+static int MDIOWriteBuf(struct phy_device *phydev, unsigned long address, unsigned long array_size, const unsigned char *buffer)
|
|
|
++int air_phy_restore_page(struct phy_device *phydev, int oldpage, int ret)
|
|
|
+{
|
|
|
-+ unsigned int write_data, offset ;
|
|
|
-+ int ret = 0;
|
|
|
++ int r;
|
|
|
+
|
|
|
-+ /* page 4 */
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)4);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ /* address increment*/
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x10, (unsigned int)0x8000);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x11, (unsigned int)((address >> 16) & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x12, (unsigned int)(address & 0xffff));
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
++ if (oldpage < 0)
|
|
|
++ return oldpage;
|
|
|
+
|
|
|
-+ for (offset = 0; offset < array_size; offset += 4) {
|
|
|
-+ write_data = (buffer[offset + 3] << 8) | buffer[offset + 2];
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x13, write_data);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ write_data = (buffer[offset + 1] << 8) | buffer[offset];
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x14, write_data);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ ret = phy_write(phydev, MDIO_DEVAD_NONE, 0x1F, (unsigned int)0);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("phy_write, ret: %d\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ return 0;
|
|
|
++ r = air_phy_write_page(phydev, oldpage);
|
|
|
++ if (ret >= 0 && r < 0)
|
|
|
++ ret = r;
|
|
|
++
|
|
|
++ return ret;
|
|
|
+}
|
|
|
+
|
|
|
-+#ifdef AIR_LED_SUPPORT
|
|
|
-+static int airoha_led_set_usr_def(struct phy_device *phydev, u8 entity, int polar,
|
|
|
-+ u16 on_evt, u16 blk_evt)
|
|
|
++static int air_buckpbus_reg_write(struct phy_device *phydev,
|
|
|
++ u32 pbus_address, u32 pbus_data)
|
|
|
+{
|
|
|
-+ int ret = 0;
|
|
|
-+
|
|
|
-+ if (AIR_ACTIVE_HIGH == polar)
|
|
|
-+ on_evt |= LED_ON_POL;
|
|
|
-+ else
|
|
|
-+ on_evt &= ~LED_ON_POL;
|
|
|
-+
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), on_evt | LED_ON_EN);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_CTRL(entity), blk_evt);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ return 0;
|
|
|
++ int ret, saved_page;
|
|
|
++
|
|
|
++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
|
|
|
++ if (saved_page < 0)
|
|
|
++ return saved_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
|
|
|
++ AIR_BPBUS_MODE_ADDR_FIXED);
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
|
|
|
++ air_upper_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
|
|
|
++ air_lower_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
|
|
|
++ air_upper_16_bits(pbus_data));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
|
|
|
++ air_lower_16_bits(pbus_data));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++restore_page:
|
|
|
++ if (ret < 0)
|
|
|
++ printf("%s 0x%08x failed: %d\n", __func__,
|
|
|
++ pbus_address, ret);
|
|
|
++
|
|
|
++ return air_phy_restore_page(phydev, saved_page, ret);
|
|
|
+}
|
|
|
+
|
|
|
-+static int airoha_led_set_mode(struct phy_device *phydev, u8 mode)
|
|
|
++static int air_buckpbus_reg_read(struct phy_device *phydev,
|
|
|
++ u32 pbus_address, u32 *pbus_data)
|
|
|
+{
|
|
|
-+ u16 cl45_data;
|
|
|
-+ int err = 0;
|
|
|
-+
|
|
|
-+ cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_BCR);
|
|
|
-+ switch (mode) {
|
|
|
-+ case AIR_LED_MODE_DISABLE:
|
|
|
-+ cl45_data &= ~LED_BCR_EXT_CTRL;
|
|
|
-+ cl45_data &= ~LED_BCR_MODE_MASK;
|
|
|
-+ cl45_data |= LED_BCR_MODE_DISABLE;
|
|
|
-+ break;
|
|
|
-+ case AIR_LED_MODE_USER_DEFINE:
|
|
|
-+ cl45_data |= LED_BCR_EXT_CTRL;
|
|
|
-+ cl45_data |= LED_BCR_CLK_EN;
|
|
|
-+ break;
|
|
|
-+ default:
|
|
|
-+ printf("LED mode%d is not supported!\n", mode);
|
|
|
-+ return -EINVAL;
|
|
|
-+ }
|
|
|
-+ err = air_mii_cl45_write(phydev, 0x1f, LED_BCR, cl45_data);
|
|
|
-+ if (err < 0)
|
|
|
-+ return err;
|
|
|
-+ return 0;
|
|
|
++ int pbus_data_low, pbus_data_high;
|
|
|
++ int ret = 0, saved_page;
|
|
|
++
|
|
|
++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
|
|
|
++ if (saved_page < 0)
|
|
|
++ return saved_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
|
|
|
++ AIR_BPBUS_MODE_ADDR_FIXED);
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
|
|
|
++ air_upper_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
|
|
|
++ air_lower_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
|
|
|
++ if (pbus_data_high < 0) {
|
|
|
++ ret = pbus_data_high;
|
|
|
++ goto restore_page;
|
|
|
++ }
|
|
|
++
|
|
|
++ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
|
|
|
++ if (pbus_data_low < 0) {
|
|
|
++ ret = pbus_data_low;
|
|
|
++ goto restore_page;
|
|
|
++ }
|
|
|
++
|
|
|
++ *pbus_data = pbus_data_low | (pbus_data_high << 16);
|
|
|
++
|
|
|
++restore_page:
|
|
|
++ if (ret < 0)
|
|
|
++ printf("%s 0x%08x failed: %d\n", __func__,
|
|
|
++ pbus_address, ret);
|
|
|
++
|
|
|
++ return air_phy_restore_page(phydev, saved_page, ret);
|
|
|
+}
|
|
|
+
|
|
|
-+static int airoha_led_set_state(struct phy_device *phydev, u8 entity, u8 state)
|
|
|
++static int air_buckpbus_reg_modify(struct phy_device *phydev,
|
|
|
++ u32 pbus_address, u32 mask, u32 set)
|
|
|
+{
|
|
|
-+ u16 cl45_data;
|
|
|
-+ int err;
|
|
|
-+
|
|
|
-+ cl45_data = air_mii_cl45_read(phydev, 0x1f, LED_ON_CTRL(entity));
|
|
|
-+ if (LED_ENABLE == state)
|
|
|
-+ cl45_data |= LED_ON_EN;
|
|
|
-+ else
|
|
|
-+ cl45_data &= ~LED_ON_EN;
|
|
|
-+
|
|
|
-+ err = air_mii_cl45_write(phydev, 0x1f, LED_ON_CTRL(entity), cl45_data);
|
|
|
-+ if (err < 0)
|
|
|
-+ return err;
|
|
|
-+ return 0;
|
|
|
++ int pbus_data_low, pbus_data_high;
|
|
|
++ u32 pbus_data_old, pbus_data_new;
|
|
|
++ int ret = 0, saved_page;
|
|
|
++
|
|
|
++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
|
|
|
++ if (saved_page < 0)
|
|
|
++ return saved_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
|
|
|
++ AIR_BPBUS_MODE_ADDR_FIXED);
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_HIGH,
|
|
|
++ air_upper_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_ADDR_LOW,
|
|
|
++ air_lower_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ pbus_data_high = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_HIGH);
|
|
|
++ if (pbus_data_high < 0)
|
|
|
++ return pbus_data_high;
|
|
|
++
|
|
|
++ pbus_data_low = phy_read(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_RD_DATA_LOW);
|
|
|
++ if (pbus_data_low < 0)
|
|
|
++ return pbus_data_low;
|
|
|
++
|
|
|
++ pbus_data_old = pbus_data_low | (pbus_data_high << 16);
|
|
|
++ pbus_data_new = (pbus_data_old & ~mask) | set;
|
|
|
++ if (pbus_data_new == pbus_data_old)
|
|
|
++ return 0;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
|
|
|
++ air_upper_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
|
|
|
++ air_lower_16_bits(pbus_address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH,
|
|
|
++ air_upper_16_bits(pbus_data_new));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW,
|
|
|
++ air_lower_16_bits(pbus_data_new));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++restore_page:
|
|
|
++ if (ret < 0)
|
|
|
++ printf("%s 0x%08x failed: %d\n", __func__,
|
|
|
++ pbus_address, ret);
|
|
|
++
|
|
|
++ return air_phy_restore_page(phydev, saved_page, ret);
|
|
|
+}
|
|
|
+
|
|
|
-+static int en8811h_led_init(struct phy_device *phydev)
|
|
|
++static int air_write_buf(struct phy_device *phydev, unsigned long address,
|
|
|
++ unsigned long array_size, const unsigned char *buffer)
|
|
|
+{
|
|
|
-+ unsigned int led_gpio = 0, reg_value = 0;
|
|
|
-+ u16 cl45_data = led_dur;
|
|
|
-+ int ret, led_id;
|
|
|
-+
|
|
|
-+ cl45_data = UNIT_LED_BLINK_DURATION << AIR_LED_BLK_DUR_64M;
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1f, LED_BLK_DUR, cl45_data);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ cl45_data >>= 1;
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1f, LED_ON_DUR, cl45_data);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
++ unsigned int offset;
|
|
|
++ int ret, saved_page;
|
|
|
++ u16 val;
|
|
|
++
|
|
|
++ saved_page = air_phy_select_page(phydev, AIR_PHY_PAGE_EXTENDED_4);
|
|
|
++ if (saved_page < 0)
|
|
|
++ return saved_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_MODE,
|
|
|
++ AIR_BPBUS_MODE_ADDR_INCR);
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_HIGH,
|
|
|
++ air_upper_16_bits(address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_ADDR_LOW,
|
|
|
++ air_lower_16_bits(address));
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ for (offset = 0; offset < array_size; offset += 4) {
|
|
|
++ val = get_unaligned_le16(&buffer[offset + 2]);
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_HIGH, val);
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++
|
|
|
++ val = get_unaligned_le16(&buffer[offset]);
|
|
|
++ ret = phy_write(phydev, MDIO_DEVAD_NONE, AIR_BPBUS_WR_DATA_LOW, val);
|
|
|
++ if (ret < 0)
|
|
|
++ goto restore_page;
|
|
|
++ }
|
|
|
++
|
|
|
++restore_page:
|
|
|
++ if (ret < 0)
|
|
|
++ printf("%s 0x%08lx failed: %d\n", __func__,
|
|
|
++ address, ret);
|
|
|
++
|
|
|
++ return air_phy_restore_page(phydev, saved_page, ret);
|
|
|
++}
|
|
|
+
|
|
|
-+ ret = airoha_led_set_mode(phydev, AIR_LED_MODE_USER_DEFINE);
|
|
|
-+ if (ret != 0) {
|
|
|
-+ printf("LED fail to set mode, ret %d !\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ for(led_id = 0; led_id < EN8811H_LED_COUNT; led_id++)
|
|
|
-+ {
|
|
|
-+ /* LED0 <-> GPIO5, LED1 <-> GPIO4, LED0 <-> GPIO3 */
|
|
|
-+ if ( led_cfg[led_id].gpio != (led_id + (AIR_LED0_GPIO5 - (2 * led_id)))) {
|
|
|
-+ printf("LED%d uses incorrect GPIO%d !\n", led_id, led_cfg[led_id].gpio);
|
|
|
-+ return -EINVAL;
|
|
|
-+ }
|
|
|
-+ reg_value = 0;
|
|
|
-+ if (led_cfg[led_id].en == LED_ENABLE)
|
|
|
-+ {
|
|
|
-+ led_gpio |= BIT(led_cfg[led_id].gpio);
|
|
|
-+ ret = airoha_led_set_state(phydev, led_id, led_cfg[led_id].en);
|
|
|
-+ if (ret != 0) {
|
|
|
-+ printf("LED fail to set state, ret %d !\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = airoha_led_set_usr_def(phydev, led_id, led_cfg[led_id].pol, led_cfg[led_id].on_cfg, led_cfg[led_id].blk_cfg);
|
|
|
-+ if (ret != 0) {
|
|
|
-+ printf("LED fail to set default, ret %d !\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ ret = air_buckpbus_reg_write(phydev, 0xcf8b8, led_gpio);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ printf("LED initialize OK !\n");
|
|
|
-+ return 0;
|
|
|
++static int en8811h_wait_mcu_ready(struct phy_device *phydev)
|
|
|
++{
|
|
|
++ int ret, reg_value;
|
|
|
++
|
|
|
++ /* Because of mdio-lock, may have to wait for multiple loads */
|
|
|
++ ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
|
|
|
++ EN8811H_PHY_FW_STATUS, reg_value,
|
|
|
++ reg_value == EN8811H_PHY_READY,
|
|
|
++ 20000, 7500000, true);
|
|
|
++ if (ret) {
|
|
|
++ printf("MCU not ready: 0x%x\n", reg_value);
|
|
|
++ return -ENODEV;
|
|
|
++ }
|
|
|
++
|
|
|
++ return 0;
|
|
|
+}
|
|
|
-+#endif /* AIR_LED_SUPPORT */
|
|
|
+
|
|
|
-+static char *firmware_buf;
|
|
|
-+static int en8811h_load_firmware(struct phy_device *phydev)
|
|
|
++__weak int en8811h_read_fw(void **addr)
|
|
|
+{
|
|
|
-+ u32 pbus_value;
|
|
|
-+ int ret = 0;
|
|
|
++ u32 ca_crc32;
|
|
|
++ void *buffer;
|
|
|
++ int ret;
|
|
|
+
|
|
|
-+ if (!firmware_buf) {
|
|
|
-+ firmware_buf = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
|
|
|
-+ if (!firmware_buf) {
|
|
|
-+ printf("[Airoha] cannot allocated buffer for firmware.\n");
|
|
|
-+ return -ENOMEM;
|
|
|
-+ }
|
|
|
++ /* Allocate memory to store both DM and DSP firmware */
|
|
|
++ buffer = malloc(EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
|
|
|
++ if (!buffer) {
|
|
|
++ printf("Failed to allocate memory for firmware\n");
|
|
|
++ return -ENOMEM;
|
|
|
++ }
|
|
|
+
|
|
|
+#ifdef CONFIG_PHY_AIROHA_FW_IN_UBI
|
|
|
-+ ret = ubi_volume_read("en8811h-fw", firmware_buf, 0, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
|
|
|
-+ if (ret) {
|
|
|
-+ printf("[Airoha] read firmware from UBI failed.\n");
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
++ ret = ubi_volume_read("en8811h-fw", buffer, 0, EN8811H_MD32_DM_SIZE + EN8811H_MD32_DSP_SIZE);
|
|
|
++ if (ret) {
|
|
|
++ printf("[Airoha] read firmware from UBI failed.\n");
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
+#elif defined(CONFIG_PHY_AIROHA_FW_IN_MMC)
|
|
|
-+ struct mmc *mmc = find_mmc_device(0);
|
|
|
-+ if (!mmc) {
|
|
|
-+ printf("[Airoha] opening MMC device failed.\n");
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return -ENODEV;
|
|
|
-+ }
|
|
|
-+ if (mmc_init(mmc)) {
|
|
|
-+ printf("[Airoha] initializing MMC device failed.\n");
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return -ENODEV;
|
|
|
-+ }
|
|
|
-+ if (IS_SD(mmc)) {
|
|
|
-+ printf("[Airoha] SD card is not supported.\n");
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return -EINVAL;
|
|
|
-+ }
|
|
|
-+ ret = mmc_set_part_conf(mmc, 1, 2, 2);
|
|
|
-+ if (ret) {
|
|
|
-+ printf("[Airoha] cannot access eMMC boot1 hw partition.\n");
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ ret = blk_dread(mmc_get_blk_desc(mmc), 0, 0x120, firmware_buf);
|
|
|
-+ mmc_set_part_conf(mmc, 1, 1, 0);
|
|
|
-+ if (ret != 0x120) {
|
|
|
-+ printf("[Airoha] cannot read firmware from eMMC.\n");
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return -EIO;
|
|
|
-+ }
|
|
|
++ struct mmc *mmc = find_mmc_device(0);
|
|
|
++ if (!mmc) {
|
|
|
++ printf("[Airoha] opening MMC device failed.\n");
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return -ENODEV;
|
|
|
++ }
|
|
|
++ if (mmc_init(mmc)) {
|
|
|
++ printf("[Airoha] initializing MMC device failed.\n");
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return -ENODEV;
|
|
|
++ }
|
|
|
++ if (IS_SD(mmc)) {
|
|
|
++ printf("[Airoha] SD card is not supported.\n");
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return -EINVAL;
|
|
|
++ }
|
|
|
++ ret = mmc_set_part_conf(mmc, 1, 2, 2);
|
|
|
++ if (ret) {
|
|
|
++ printf("[Airoha] cannot access eMMC boot1 hw partition.\n");
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++ ret = blk_dread(mmc_get_blk_desc(mmc), 0, 0x120, buffer);
|
|
|
++ mmc_set_part_conf(mmc, 1, 1, 0);
|
|
|
++ if (ret != 0x120) {
|
|
|
++ printf("[Airoha] cannot read firmware from eMMC.\n");
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return -EIO;
|
|
|
++ }
|
|
|
+#else
|
|
|
+#warning EN8811H firmware loading not implemented
|
|
|
-+ free(firmware_buf);
|
|
|
-+ firmware_buf = NULL;
|
|
|
-+ return -EOPNOTSUPP;
|
|
|
++ free(buffer);
|
|
|
++ buffer = NULL;
|
|
|
++ return -EOPNOTSUPP;
|
|
|
+#endif
|
|
|
-+ }
|
|
|
+
|
|
|
-+ ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x0);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
|
|
|
-+ pbus_value |= BIT(11);
|
|
|
-+ ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ /* Download DM */
|
|
|
-+ ret = MDIOWriteBuf(phydev, 0x00000000, EN8811H_MD32_DM_SIZE, firmware_buf);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("[Airoha] MDIOWriteBuf 0x00000000 fail.\n");
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ /* Download PM */
|
|
|
-+ ret = MDIOWriteBuf(phydev, 0x00100000, EN8811H_MD32_DSP_SIZE, firmware_buf + EN8811H_MD32_DM_SIZE);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("[Airoha] MDIOWriteBuf 0x00100000 fail.\n");
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0x800000);
|
|
|
-+ pbus_value &= ~BIT(11);
|
|
|
-+ ret = air_buckpbus_reg_write(phydev, 0x800000, pbus_value);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_buckpbus_reg_write(phydev, 0x0f0018, 0x01);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+
|
|
|
-+static int en8811h_config(struct phy_device *phydev)
|
|
|
-+{
|
|
|
-+ int ret = 0;
|
|
|
-+ int pid1 = 0, pid2 = 0;
|
|
|
++ /* Calculate CRC32 of DM firmware for verification */
|
|
|
++ ca_crc32 = crc32(0, (unsigned char *)buffer, EN8811H_MD32_DM_SIZE);
|
|
|
++ debug("DM crc32: 0x%x\n", ca_crc32);
|
|
|
+
|
|
|
-+ ret = air_pbus_reg_write(phydev, 0xcf928 , 0x0);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
++ /* Calculate CRC32 of DSP firmware for verification */
|
|
|
++ ca_crc32 = crc32(0, (unsigned char *)buffer + EN8811H_MD32_DM_SIZE,
|
|
|
++ EN8811H_MD32_DSP_SIZE);
|
|
|
++ debug("DSP crc32: 0x%x\n", ca_crc32);
|
|
|
+
|
|
|
-+ pid1 = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHYSID1);
|
|
|
-+ pid2 = phy_read(phydev, MDIO_DEVAD_NONE, MII_PHYSID2);
|
|
|
-+ if ((EN8811H_PHY_ID1 != pid1) || (EN8811H_PHY_ID2 != pid2)) {
|
|
|
-+ printf("EN8811H does not exist !\n");
|
|
|
-+ return -ENODEV;
|
|
|
-+ }
|
|
|
++ *addr = buffer;
|
|
|
++ debug("Found Airoha Firmware.\n");
|
|
|
+
|
|
|
-+ return 0;
|
|
|
++ return 0;
|
|
|
+}
|
|
|
+
|
|
|
-+static int en8811h_get_autonego(struct phy_device *phydev, int *an)
|
|
|
++static int en8811h_load_firmware(struct phy_device *phydev)
|
|
|
+{
|
|
|
-+ int reg;
|
|
|
-+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
|
|
|
-+ if (reg < 0)
|
|
|
-+ return -EINVAL;
|
|
|
-+ if (reg & BMCR_ANENABLE)
|
|
|
-+ *an = AUTONEG_ENABLE;
|
|
|
-+ else
|
|
|
-+ *an = AUTONEG_DISABLE;
|
|
|
-+ return 0;
|
|
|
++ struct en8811h_priv *priv = phydev->priv;
|
|
|
++ void *buffer;
|
|
|
++ int ret;
|
|
|
++
|
|
|
++ ret = en8811h_read_fw(&buffer);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
|
|
|
++ EN8811H_FW_CTRL_1_START);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
|
|
|
++ EN8811H_FW_CTRL_2_LOADING,
|
|
|
++ EN8811H_FW_CTRL_2_LOADING);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = air_write_buf(phydev, AIR_FW_ADDR_DM, EN8811H_MD32_DM_SIZE,
|
|
|
++ (unsigned char *)buffer);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = air_write_buf(phydev, AIR_FW_ADDR_DSP, EN8811H_MD32_DSP_SIZE,
|
|
|
++ (unsigned char *)buffer + EN8811H_MD32_DM_SIZE);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = air_buckpbus_reg_modify(phydev, EN8811H_FW_CTRL_2,
|
|
|
++ EN8811H_FW_CTRL_2_LOADING, 0);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
|
|
|
++ EN8811H_FW_CTRL_1_FINISH);
|
|
|
++ if (ret < 0)
|
|
|
++ goto en8811h_load_firmware_out;
|
|
|
++
|
|
|
++ ret = en8811h_wait_mcu_ready(phydev);
|
|
|
++
|
|
|
++ air_buckpbus_reg_read(phydev, EN8811H_FW_VERSION,
|
|
|
++ &priv->firmware_version);
|
|
|
++ printf("MD32 firmware version: %08x\n",
|
|
|
++ priv->firmware_version);
|
|
|
++
|
|
|
++en8811h_load_firmware_out:
|
|
|
++ free(buffer);
|
|
|
++ if (ret < 0)
|
|
|
++ printf("Firmware loading failed: %d\n", ret);
|
|
|
++
|
|
|
++ return ret;
|
|
|
+}
|
|
|
+
|
|
|
-+static int en8811h_startup(struct phy_device *phydev)
|
|
|
++static int en8811h_restart_mcu(struct phy_device *phydev)
|
|
|
+{
|
|
|
-+ ofnode node = phy_get_ofnode(phydev);
|
|
|
-+ int ret = 0, lpagb = 0, lpa = 0, common_adv_gb = 0, common_adv = 0, advgb = 0, adv = 0, reg = 0, an = AUTONEG_DISABLE, bmcr = 0, reg_value;
|
|
|
-+ int old_link = phydev->link;
|
|
|
-+ u32 pbus_value = 0, retry;
|
|
|
-+
|
|
|
-+ eth_phy_reset(phydev->dev, 1);
|
|
|
-+ mdelay(10);
|
|
|
-+ eth_phy_reset(phydev->dev, 0);
|
|
|
-+ mdelay(1);
|
|
|
-+
|
|
|
-+ ret = en8811h_load_firmware(phydev);
|
|
|
-+ if (ret) {
|
|
|
-+ printf("EN8811H load firmware fail.\n");
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+ retry = MAX_RETRY;
|
|
|
-+ do {
|
|
|
-+ mdelay(300);
|
|
|
-+ reg_value = air_mii_cl45_read(phydev, 0x1e, 0x8009);
|
|
|
-+ if (EN8811H_PHY_READY == reg_value) {
|
|
|
-+ printf("EN8811H PHY ready!\n");
|
|
|
-+ break;
|
|
|
-+ }
|
|
|
-+ retry--;
|
|
|
-+ } while (retry);
|
|
|
-+ if (0 == retry) {
|
|
|
-+ printf("EN8811H PHY is not ready. (MD32 FW Status reg: 0x%x)\n", reg_value);
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
|
|
|
-+ printf("Check MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
|
|
|
-+ printf("EN8811H initialize fail!\n");
|
|
|
-+ return 0;
|
|
|
-+ }
|
|
|
-+ /* Mode selection*/
|
|
|
-+ printf("EN8811H Mode 1 !\n");
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1e, 0x800c, 0x0);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1e, 0x800d, 0x0);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1e, 0x800e, 0x1101);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ ret = air_mii_cl45_write(phydev, 0x1e, 0x800f, 0x0002);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+
|
|
|
-+ /* Serdes polarity */
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
|
|
|
-+ pbus_value &= 0xfffffffc;
|
|
|
-+ pbus_value |= ofnode_read_bool(node, "airoha,rx-pol-reverse") ?
|
|
|
-+ EN8811H_RX_POLARITY_REVERSE : EN8811H_RX_POLARITY_NORMAL;
|
|
|
-+ pbus_value |= ofnode_read_bool(node, "airoha,tx-pol-reverse") ?
|
|
|
-+ EN8811H_TX_POLARITY_REVERSE : EN8811H_TX_POLARITY_NORMAL;
|
|
|
-+ ret = air_buckpbus_reg_write(phydev, 0xca0f8, pbus_value);
|
|
|
-+ if (ret < 0)
|
|
|
-+ return ret;
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0xca0f8);
|
|
|
-+ printf("Tx, Rx Polarity(0xca0f8): %08x\n", pbus_value);
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0x3b3c);
|
|
|
-+ printf("MD32 FW Version(0x3b3c) : %08x\n", pbus_value);
|
|
|
-+#if defined(AIR_LED_SUPPORT)
|
|
|
-+ ret = en8811h_led_init(phydev);
|
|
|
-+ if (ret < 0) {
|
|
|
-+ printf("en8811h_led_init fail\n");
|
|
|
-+ }
|
|
|
-+#endif
|
|
|
-+ printf("EN8811H initialize OK ! (%s)\n", EN8811H_DRIVER_VERSION);
|
|
|
-+
|
|
|
-+ ret = genphy_update_link(phydev);
|
|
|
-+ if (ret)
|
|
|
-+ {
|
|
|
-+ printf("ret %d!\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ ret = genphy_parse_link(phydev);
|
|
|
-+ if (ret)
|
|
|
-+ {
|
|
|
-+ printf("ret %d!\n", ret);
|
|
|
-+ return ret;
|
|
|
-+ }
|
|
|
++ int ret;
|
|
|
+
|
|
|
-+ if (old_link && phydev->link)
|
|
|
-+ return 0;
|
|
|
++ ret = phy_write_mmd(phydev, 0x1e, 0x8009, 0x0);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
+
|
|
|
-+ phydev->speed = SPEED_100;
|
|
|
-+ phydev->duplex = DUPLEX_FULL;
|
|
|
-+ phydev->pause = 0;
|
|
|
-+ phydev->asym_pause = 0;
|
|
|
++ ret = air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
|
|
|
++ EN8811H_FW_CTRL_1_START);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
+
|
|
|
-+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
|
|
|
-+ if (reg < 0)
|
|
|
-+ {
|
|
|
-+ printf("MII_BMSR reg %d!\n", reg);
|
|
|
-+ return reg;
|
|
|
-+ }
|
|
|
-+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
|
|
|
-+ if (reg < 0)
|
|
|
-+ {
|
|
|
-+ printf("MII_BMSR reg %d!\n", reg);
|
|
|
-+ return reg;
|
|
|
-+ }
|
|
|
-+ if(reg & BMSR_LSTATUS)
|
|
|
-+ {
|
|
|
-+ pbus_value = air_buckpbus_reg_read(phydev, 0x109D4);
|
|
|
-+ if (0x10 & pbus_value) {
|
|
|
-+ phydev->speed = SPEED_2500;
|
|
|
-+ phydev->duplex = DUPLEX_FULL;
|
|
|
-+ }
|
|
|
-+ else
|
|
|
-+ {
|
|
|
-+ ret = en8811h_get_autonego(phydev, &an);
|
|
|
-+ if ((AUTONEG_ENABLE == an) && (0 == ret))
|
|
|
-+ {
|
|
|
-+ printf("AN mode!\n");
|
|
|
-+ printf("SPEED 1000/100!\n");
|
|
|
-+ lpagb = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
|
|
|
-+ if (lpagb < 0 )
|
|
|
-+ return lpagb;
|
|
|
-+ advgb = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
|
|
|
-+ if (adv < 0 )
|
|
|
-+ return adv;
|
|
|
-+ common_adv_gb = (lpagb & (advgb << 2));
|
|
|
-+
|
|
|
-+ lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
|
|
|
-+ if (lpa < 0 )
|
|
|
-+ return lpa;
|
|
|
-+ adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
|
|
|
-+ if (adv < 0 )
|
|
|
-+ return adv;
|
|
|
-+ common_adv = (lpa & adv);
|
|
|
-+
|
|
|
-+ phydev->speed = SPEED_10;
|
|
|
-+ phydev->duplex = DUPLEX_HALF;
|
|
|
-+ if (common_adv_gb & (LPA_1000FULL | LPA_1000HALF))
|
|
|
-+ {
|
|
|
-+ phydev->speed = SPEED_1000;
|
|
|
-+ if (common_adv_gb & LPA_1000FULL)
|
|
|
-+
|
|
|
-+ phydev->duplex = DUPLEX_FULL;
|
|
|
-+ }
|
|
|
-+ else if (common_adv & (LPA_100FULL | LPA_100HALF))
|
|
|
-+ {
|
|
|
-+ phydev->speed = SPEED_100;
|
|
|
-+ if (common_adv & LPA_100FULL)
|
|
|
-+ phydev->duplex = DUPLEX_FULL;
|
|
|
-+ }
|
|
|
-+ else
|
|
|
-+ {
|
|
|
-+ if (common_adv & LPA_10FULL)
|
|
|
-+ phydev->duplex = DUPLEX_FULL;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ else
|
|
|
-+ {
|
|
|
-+ printf("Force mode!\n");
|
|
|
-+ bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
|
|
|
-+
|
|
|
-+ if (bmcr < 0)
|
|
|
-+ return bmcr;
|
|
|
-+
|
|
|
-+ if (bmcr & BMCR_FULLDPLX)
|
|
|
-+ phydev->duplex = DUPLEX_FULL;
|
|
|
-+ else
|
|
|
-+ phydev->duplex = DUPLEX_HALF;
|
|
|
-+
|
|
|
-+ if (bmcr & BMCR_SPEED1000)
|
|
|
-+ phydev->speed = SPEED_1000;
|
|
|
-+ else if (bmcr & BMCR_SPEED100)
|
|
|
-+ phydev->speed = SPEED_100;
|
|
|
-+ else
|
|
|
-+ phydev->speed = SPEED_100;
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+ }
|
|
|
-+
|
|
|
-+ return ret;
|
|
|
++ return air_buckpbus_reg_write(phydev, EN8811H_FW_CTRL_1,
|
|
|
++ EN8811H_FW_CTRL_1_FINISH);
|
|
|
+}
|
|
|
+
|
|
|
-+#if AIR_UBOOT_REVISION > 0x202303
|
|
|
-+U_BOOT_PHY_DRIVER(en8811h) = {
|
|
|
-+ .name = "Airoha EN8811H",
|
|
|
-+ .uid = EN8811H_PHY_ID,
|
|
|
-+ .mask = 0x0ffffff0,
|
|
|
-+ .config = &en8811h_config,
|
|
|
-+ .startup = &en8811h_startup,
|
|
|
-+ .shutdown = &genphy_shutdown,
|
|
|
-+};
|
|
|
-+#else
|
|
|
-+static struct phy_driver AIR_EN8811H_driver = {
|
|
|
-+ .name = "Airoha EN8811H",
|
|
|
-+ .uid = EN8811H_PHY_ID,
|
|
|
-+ .mask = 0x0ffffff0,
|
|
|
-+ .config = &en8811h_config,
|
|
|
-+ .startup = &en8811h_startup,
|
|
|
-+ .shutdown = &genphy_shutdown,
|
|
|
++static int air_led_hw_control_set(struct phy_device *phydev, u8 index,
|
|
|
++ unsigned long rules)
|
|
|
++{
|
|
|
++ struct en8811h_priv *priv = phydev->priv;
|
|
|
++ u16 on = 0, blink = 0;
|
|
|
++ int ret;
|
|
|
++
|
|
|
++ if (index >= EN8811H_LED_COUNT)
|
|
|
++ return -EINVAL;
|
|
|
++
|
|
|
++ priv->led[index].rules = rules;
|
|
|
++
|
|
|
++ if (rules & BIT(AIR_TRIGGER_NETDEV_FULL_DUPLEX))
|
|
|
++ on |= AIR_PHY_LED_ON_FDX;
|
|
|
++
|
|
|
++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_10) | BIT(AIR_TRIGGER_NETDEV_LINK)))
|
|
|
++ on |= AIR_PHY_LED_ON_LINK10;
|
|
|
++
|
|
|
++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_100) | BIT(AIR_TRIGGER_NETDEV_LINK)))
|
|
|
++ on |= AIR_PHY_LED_ON_LINK100;
|
|
|
++
|
|
|
++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_1000) | BIT(AIR_TRIGGER_NETDEV_LINK)))
|
|
|
++ on |= AIR_PHY_LED_ON_LINK1000;
|
|
|
++
|
|
|
++ if (rules & (BIT(AIR_TRIGGER_NETDEV_LINK_2500) | BIT(AIR_TRIGGER_NETDEV_LINK)))
|
|
|
++ on |= AIR_PHY_LED_ON_LINK2500;
|
|
|
++
|
|
|
++ if (rules & BIT(AIR_TRIGGER_NETDEV_RX)) {
|
|
|
++ blink |= AIR_PHY_LED_BLINK_10RX |
|
|
|
++ AIR_PHY_LED_BLINK_100RX |
|
|
|
++ AIR_PHY_LED_BLINK_1000RX |
|
|
|
++ AIR_PHY_LED_BLINK_2500RX;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (rules & BIT(AIR_TRIGGER_NETDEV_TX)) {
|
|
|
++ blink |= AIR_PHY_LED_BLINK_10TX |
|
|
|
++ AIR_PHY_LED_BLINK_100TX |
|
|
|
++ AIR_PHY_LED_BLINK_1000TX |
|
|
|
++ AIR_PHY_LED_BLINK_2500TX;
|
|
|
++ }
|
|
|
++
|
|
|
++ if (blink || on) {
|
|
|
++ /* switch hw-control on, so led-on and led-blink are off */
|
|
|
++ clear_bit(AIR_PHY_LED_STATE_FORCE_ON,
|
|
|
++ &priv->led[index].state);
|
|
|
++ clear_bit(AIR_PHY_LED_STATE_FORCE_BLINK,
|
|
|
++ &priv->led[index].state);
|
|
|
++ } else {
|
|
|
++ priv->led[index].rules = 0;
|
|
|
++ }
|
|
|
++
|
|
|
++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_ON(index),
|
|
|
++ AIR_PHY_LED_ON_MASK, on);
|
|
|
++
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++
|
|
|
++ return phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BLINK(index),
|
|
|
++ blink);
|
|
|
+};
|
|
|
+
|
|
|
-+int phy_air_en8811h_init(void)
|
|
|
++static int air_led_init(struct phy_device *phydev, u8 index, u8 state, u8 pol)
|
|
|
+{
|
|
|
-+ phy_register(&AIR_EN8811H_driver);
|
|
|
-+ return 0;
|
|
|
-+}
|
|
|
-+#endif
|
|
|
---- /dev/null
|
|
|
-+++ b/drivers/net/phy/air_en8811h.h
|
|
|
-@@ -0,0 +1,163 @@
|
|
|
-+/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
-+/*************************************************
|
|
|
-+ * FILE NAME: air_en8811h.h
|
|
|
-+ * PURPOSE:
|
|
|
-+ * EN8811H PHY Driver for Uboot
|
|
|
-+ * NOTES:
|
|
|
-+ *
|
|
|
-+ * Copyright (C) 2023 Airoha Technology Corp.
|
|
|
-+ *************************************************/
|
|
|
-+
|
|
|
-+#ifndef __EN8811H_H
|
|
|
-+#define __EN8811H_H
|
|
|
-+
|
|
|
-+#define AIR_UBOOT_REVISION ((((U_BOOT_VERSION_NUM / 1000) % 10) << 20) | \
|
|
|
-+ (((U_BOOT_VERSION_NUM / 100) % 10) << 16) | \
|
|
|
-+ (((U_BOOT_VERSION_NUM / 10) % 10) << 12) | \
|
|
|
-+ ((U_BOOT_VERSION_NUM % 10) << 8) | \
|
|
|
-+ (((U_BOOT_VERSION_NUM_PATCH / 10) % 10) << 4) | \
|
|
|
-+ ((U_BOOT_VERSION_NUM_PATCH % 10) << 0))
|
|
|
-+
|
|
|
-+#define EN8811H_PHY_ID1 0x03a2
|
|
|
-+#define EN8811H_PHY_ID2 0xa411
|
|
|
-+#define EN8811H_PHY_ID ((EN8811H_PHY_ID1 << 16) | EN8811H_PHY_ID2)
|
|
|
-+#define EN8811H_SPEED_2500 0x03
|
|
|
-+#define EN8811H_PHY_READY 0x02
|
|
|
-+#define MAX_RETRY 5
|
|
|
-+
|
|
|
-+#define EN8811H_MD32_DM_SIZE 0x4000
|
|
|
-+#define EN8811H_MD32_DSP_SIZE 0x20000
|
|
|
++ int val = 0;
|
|
|
++ int err;
|
|
|
+
|
|
|
-+#define EN8811H_TX_POLARITY_NORMAL 0x1
|
|
|
-+#define EN8811H_TX_POLARITY_REVERSE 0x0
|
|
|
++ if (index >= EN8811H_LED_COUNT)
|
|
|
++ return -EINVAL;
|
|
|
+
|
|
|
-+#define EN8811H_RX_POLARITY_NORMAL (0x0 << 1)
|
|
|
-+#define EN8811H_RX_POLARITY_REVERSE (0x1 << 1)
|
|
|
++ if (state == AIR_LED_ENABLE)
|
|
|
++ val |= AIR_PHY_LED_ON_ENABLE;
|
|
|
++ else
|
|
|
++ val &= ~AIR_PHY_LED_ON_ENABLE;
|
|
|
+
|
|
|
-+#ifndef BIT
|
|
|
-+#define BIT(nr) (1UL << (nr))
|
|
|
-+#endif
|
|
|
++ if (pol == AIR_ACTIVE_HIGH)
|
|
|
++ val |= AIR_PHY_LED_ON_POLARITY;
|
|
|
++ else
|
|
|
++ val &= ~AIR_PHY_LED_ON_POLARITY;
|
|
|
+
|
|
|
-+/* CL45 MDIO control */
|
|
|
-+#define MII_MMD_ACC_CTL_REG 0x0d
|
|
|
-+#define MII_MMD_ADDR_DATA_REG 0x0e
|
|
|
-+#define MMD_OP_MODE_DATA BIT(14)
|
|
|
-+/* MultiGBASE-T AN register */
|
|
|
-+#define MULTIG_ANAR_2500M (0x0080)
|
|
|
-+#define MULTIG_LPAR_2500M (0x0020)
|
|
|
-+
|
|
|
-+#define EN8811H_DRIVER_VERSION "v1.0.4"
|
|
|
-+
|
|
|
-+/************************************************************
|
|
|
-+ * For reference only
|
|
|
-+ * LED0 Link 2500/Blink 2500 TxRx (GPIO5) <-> BASE_T_LED0,
|
|
|
-+ * LED1 Link 1000/Blink 1000 TxRx (GPIO4) <-> BASE_T_LED1,
|
|
|
-+ * LED2 Link 100/Blink 100 TxRx (GPIO3) <-> BASE_T_LED2,
|
|
|
-+ ************************************************************/
|
|
|
-+/* User-defined.B */
|
|
|
-+#define AIR_LED0_ON (LED_ON_EVT_LINK_2500M)
|
|
|
-+#define AIR_LED0_BLK (LED_BLK_EVT_2500M_TX_ACT | LED_BLK_EVT_2500M_RX_ACT)
|
|
|
-+#define AIR_LED1_ON (LED_ON_EVT_LINK_1000M)
|
|
|
-+#define AIR_LED1_BLK (LED_BLK_EVT_1000M_TX_ACT | LED_BLK_EVT_1000M_RX_ACT)
|
|
|
-+#define AIR_LED2_ON (LED_ON_EVT_LINK_100M)
|
|
|
-+#define AIR_LED2_BLK (LED_BLK_EVT_100M_TX_ACT | LED_BLK_EVT_100M_RX_ACT)
|
|
|
-+/* User-defined.E */
|
|
|
++ err = phy_write_mmd(phydev, 0x1f, AIR_PHY_LED_ON(index), val);
|
|
|
++ if (err < 0)
|
|
|
++ return err;
|
|
|
+
|
|
|
-+#define LED_ON_CTRL(i) (0x024 + ((i)*2))
|
|
|
-+#define LED_ON_EN (1 << 15)
|
|
|
-+#define LED_ON_POL (1 << 14)
|
|
|
-+#define LED_ON_EVT_MASK (0x1ff)
|
|
|
-+/* LED ON Event Option.B */
|
|
|
-+#define LED_ON_EVT_LINK_2500M (1 << 8)
|
|
|
-+#define LED_ON_EVT_FORCE (1 << 6)
|
|
|
-+#define LED_ON_EVT_HDX (1 << 5)
|
|
|
-+#define LED_ON_EVT_FDX (1 << 4)
|
|
|
-+#define LED_ON_EVT_LINK_DOWN (1 << 3)
|
|
|
-+#define LED_ON_EVT_LINK_100M (1 << 1)
|
|
|
-+#define LED_ON_EVT_LINK_1000M (1 << 0)
|
|
|
-+/* LED ON Event Option.E */
|
|
|
-+
|
|
|
-+#define LED_BLK_CTRL(i) (0x025 + ((i)*2))
|
|
|
-+#define LED_BLK_EVT_MASK (0xfff)
|
|
|
-+/* LED Blinking Event Option.B*/
|
|
|
-+#define LED_BLK_EVT_2500M_RX_ACT (1 << 11)
|
|
|
-+#define LED_BLK_EVT_2500M_TX_ACT (1 << 10)
|
|
|
-+#define LED_BLK_EVT_FORCE (1 << 9)
|
|
|
-+#define LED_BLK_EVT_100M_RX_ACT (1 << 3)
|
|
|
-+#define LED_BLK_EVT_100M_TX_ACT (1 << 2)
|
|
|
-+#define LED_BLK_EVT_1000M_RX_ACT (1 << 1)
|
|
|
-+#define LED_BLK_EVT_1000M_TX_ACT (1 << 0)
|
|
|
-+/* LED Blinking Event Option.E*/
|
|
|
-+#define LED_ENABLE 1
|
|
|
-+#define LED_DISABLE 0
|
|
|
++ return 0;
|
|
|
++}
|
|
|
+
|
|
|
-+#define EN8811H_LED_COUNT 3
|
|
|
++static int air_leds_init(struct phy_device *phydev, int num, u16 dur, int mode)
|
|
|
++{
|
|
|
++ struct en8811h_priv *priv = phydev->priv;
|
|
|
++ int ret, i;
|
|
|
++
|
|
|
++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_BLINK,
|
|
|
++ dur);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++
|
|
|
++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_DUR_ON,
|
|
|
++ dur >> 1);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++
|
|
|
++ switch (mode) {
|
|
|
++ case AIR_LED_MODE_DISABLE:
|
|
|
++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
|
|
|
++ AIR_PHY_LED_BCR_EXT_CTRL |
|
|
|
++ AIR_PHY_LED_BCR_MODE_MASK, 0);
|
|
|
++ break;
|
|
|
++ case AIR_LED_MODE_USER_DEFINE:
|
|
|
++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND2, AIR_PHY_LED_BCR,
|
|
|
++ AIR_PHY_LED_BCR_EXT_CTRL |
|
|
|
++ AIR_PHY_LED_BCR_CLK_EN,
|
|
|
++ AIR_PHY_LED_BCR_EXT_CTRL |
|
|
|
++ AIR_PHY_LED_BCR_CLK_EN);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++ break;
|
|
|
++ default:
|
|
|
++ printf("LED mode %d is not supported\n", mode);
|
|
|
++ return -EINVAL;
|
|
|
++ }
|
|
|
++
|
|
|
++ for (i = 0; i < num; ++i) {
|
|
|
++ ret = air_led_init(phydev, i, AIR_LED_ENABLE, AIR_ACTIVE_HIGH);
|
|
|
++ if (ret < 0) {
|
|
|
++ printf("LED%d init failed: %d\n", i, ret);
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++ air_led_hw_control_set(phydev, i, priv->led[i].rules);
|
|
|
++ }
|
|
|
++
|
|
|
++ return 0;
|
|
|
++}
|
|
|
+
|
|
|
-+#define LED_BCR (0x021)
|
|
|
-+#define LED_BCR_EXT_CTRL (1 << 15)
|
|
|
-+#define LED_BCR_CLK_EN (1 << 3)
|
|
|
-+#define LED_BCR_TIME_TEST (1 << 2)
|
|
|
-+#define LED_BCR_MODE_MASK (3)
|
|
|
-+#define LED_BCR_MODE_DISABLE (0)
|
|
|
-+#define LED_BCR_MODE_2LED (1)
|
|
|
-+#define LED_BCR_MODE_3LED_1 (2)
|
|
|
-+#define LED_BCR_MODE_3LED_2 (3)
|
|
|
++static int en8811h_config(struct phy_device *phydev)
|
|
|
++{
|
|
|
++ struct en8811h_priv *priv = phydev->priv;
|
|
|
++ ofnode node = phy_get_ofnode(phydev);
|
|
|
++ u32 pbus_value = 0;
|
|
|
++ int ret = 0;
|
|
|
++
|
|
|
++ /* If restart happened in .probe(), no need to restart now */
|
|
|
++ if (priv->mcu_needs_restart) {
|
|
|
++ ret = en8811h_restart_mcu(phydev);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++ } else {
|
|
|
++ ret = en8811h_load_firmware(phydev);
|
|
|
++ if (ret) {
|
|
|
++ printf("Load firmware fail.\n");
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++ /* Next calls to .config() mcu needs to restart */
|
|
|
++ priv->mcu_needs_restart = true;
|
|
|
++ }
|
|
|
++
|
|
|
++ ret = phy_write_mmd(phydev, 0x1e, 0x800c, 0x0);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++ ret = phy_write_mmd(phydev, 0x1e, 0x800d, 0x0);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++ ret = phy_write_mmd(phydev, 0x1e, 0x800e, 0x1101);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++ ret = phy_write_mmd(phydev, 0x1e, 0x800f, 0x0002);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++
|
|
|
++ /* Serdes polarity */
|
|
|
++ pbus_value = 0;
|
|
|
++ if (ofnode_read_bool(node, "airoha,pnswap-rx"))
|
|
|
++ pbus_value |= EN8811H_POLARITY_RX_REVERSE;
|
|
|
++ else
|
|
|
++ pbus_value &= ~EN8811H_POLARITY_RX_REVERSE;
|
|
|
++ if (ofnode_read_bool(node, "airoha,pnswap-tx"))
|
|
|
++ pbus_value &= ~EN8811H_POLARITY_TX_NORMAL;
|
|
|
++ else
|
|
|
++ pbus_value |= EN8811H_POLARITY_TX_NORMAL;
|
|
|
++ ret = air_buckpbus_reg_modify(phydev, EN8811H_POLARITY,
|
|
|
++ EN8811H_POLARITY_RX_REVERSE |
|
|
|
++ EN8811H_POLARITY_TX_NORMAL, pbus_value);
|
|
|
++ if (ret < 0)
|
|
|
++ return ret;
|
|
|
++
|
|
|
++ ret = air_leds_init(phydev, EN8811H_LED_COUNT, AIR_PHY_LED_DUR,
|
|
|
++ AIR_LED_MODE_USER_DEFINE);
|
|
|
++ if (ret < 0) {
|
|
|
++ printf("Failed to disable leds: %d\n", ret);
|
|
|
++ return ret;
|
|
|
++ }
|
|
|
++
|
|
|
++ return 0;
|
|
|
++}
|
|
|
+
|
|
|
-+#define LED_ON_DUR (0x022)
|
|
|
-+#define LED_ON_DUR_MASK (0xffff)
|
|
|
++static int en8811h_parse_status(struct phy_device *phydev)
|
|
|
++{
|
|
|
++ int ret = 0, reg_value;
|
|
|
++
|
|
|
++ phydev->duplex = DUPLEX_FULL;
|
|
|
++
|
|
|
++ reg_value = phy_read(phydev, MDIO_DEVAD_NONE, AIR_AUX_CTRL_STATUS);
|
|
|
++ if (reg_value < 0)
|
|
|
++ return reg_value;
|
|
|
++
|
|
|
++ switch (reg_value & AIR_AUX_CTRL_STATUS_SPEED_MASK) {
|
|
|
++ case AIR_AUX_CTRL_STATUS_SPEED_2500:
|
|
|
++ phydev->speed = SPEED_2500;
|
|
|
++ break;
|
|
|
++ case AIR_AUX_CTRL_STATUS_SPEED_1000:
|
|
|
++ phydev->speed = SPEED_1000;
|
|
|
++ break;
|
|
|
++ case AIR_AUX_CTRL_STATUS_SPEED_100:
|
|
|
++ phydev->speed = SPEED_100;
|
|
|
++ break;
|
|
|
++ default:
|
|
|
++ printf("Auto-neg error, defaulting to 100M/FD\n");
|
|
|
++ phydev->speed = SPEED_100;
|
|
|
++ break;
|
|
|
++ }
|
|
|
++
|
|
|
++ return ret;
|
|
|
++}
|
|
|
+
|
|
|
-+#define LED_BLK_DUR (0x023)
|
|
|
-+#define LED_BLK_DUR_MASK (0xffff)
|
|
|
++static int en8811h_startup(struct phy_device *phydev)
|
|
|
++{
|
|
|
++ int ret = 0;
|
|
|
+
|
|
|
-+#define LED_GPIO_SEL_MASK 0x7FFFFFF
|
|
|
++ ret = genphy_update_link(phydev);
|
|
|
++ if (ret)
|
|
|
++ return ret;
|
|
|
+
|
|
|
-+#define UNIT_LED_BLINK_DURATION 1024
|
|
|
++ return en8811h_parse_status(phydev);
|
|
|
++}
|
|
|
+
|
|
|
-+#define INVALID_DATA 0xffff
|
|
|
-+#define PBUS_INVALID_DATA 0xffffffff
|
|
|
++static int en8811h_probe(struct phy_device *phydev)
|
|
|
++{
|
|
|
++ struct en8811h_priv *priv;
|
|
|
+
|
|
|
-+struct air_base_t_led_cfg_s {
|
|
|
-+ u16 en;
|
|
|
-+ u16 gpio;
|
|
|
-+ u16 pol;
|
|
|
-+ u16 on_cfg;
|
|
|
-+ u16 blk_cfg;
|
|
|
-+};
|
|
|
++ priv = malloc(sizeof(*priv));
|
|
|
++ if (!priv)
|
|
|
++ return -ENOMEM;
|
|
|
++ memset(priv, 0, sizeof(*priv));
|
|
|
+
|
|
|
-+enum {
|
|
|
-+ AIR_LED2_GPIO3 = 3,
|
|
|
-+ AIR_LED1_GPIO4,
|
|
|
-+ AIR_LED0_GPIO5,
|
|
|
-+ AIR_LED_LAST
|
|
|
-+};
|
|
|
++ priv->led[0].rules = AIR_DEFAULT_TRIGGER_LED0;
|
|
|
++ priv->led[1].rules = AIR_DEFAULT_TRIGGER_LED1;
|
|
|
++ priv->led[2].rules = AIR_DEFAULT_TRIGGER_LED2;
|
|
|
+
|
|
|
-+enum {
|
|
|
-+ AIR_BASE_T_LED0,
|
|
|
-+ AIR_BASE_T_LED1,
|
|
|
-+ AIR_BASE_T_LED2,
|
|
|
-+ AIR_BASE_T_LED3
|
|
|
-+};
|
|
|
++ /* mcu has just restarted after firmware load */
|
|
|
++ priv->mcu_needs_restart = false;
|
|
|
+
|
|
|
-+enum {
|
|
|
-+ AIR_LED_BLK_DUR_32M,
|
|
|
-+ AIR_LED_BLK_DUR_64M,
|
|
|
-+ AIR_LED_BLK_DUR_128M,
|
|
|
-+ AIR_LED_BLK_DUR_256M,
|
|
|
-+ AIR_LED_BLK_DUR_512M,
|
|
|
-+ AIR_LED_BLK_DUR_1024M,
|
|
|
-+ AIR_LED_BLK_DUR_LAST
|
|
|
-+};
|
|
|
++ phydev->priv = priv;
|
|
|
+
|
|
|
-+enum {
|
|
|
-+ AIR_ACTIVE_LOW,
|
|
|
-+ AIR_ACTIVE_HIGH,
|
|
|
-+};
|
|
|
++ return 0;
|
|
|
++}
|
|
|
+
|
|
|
-+enum {
|
|
|
-+ AIR_LED_MODE_DISABLE,
|
|
|
-+ AIR_LED_MODE_USER_DEFINE,
|
|
|
-+ AIR_LED_MODE_LAST
|
|
|
++U_BOOT_PHY_DRIVER(en8811h) = {
|
|
|
++ .name = "Airoha EN8811H",
|
|
|
++ .uid = EN8811H_PHY_ID,
|
|
|
++ .mask = 0x0ffffff0,
|
|
|
++ .config = &en8811h_config,
|
|
|
++ .probe = &en8811h_probe,
|
|
|
++ .startup = &en8811h_startup,
|
|
|
++ .shutdown = &genphy_shutdown,
|
|
|
+};
|
|
|
-+
|
|
|
-+#endif /* End of __EN8811H_MD32_H */
|
|
|
-+
|
|
|
---- a/drivers/net/eth-phy-uclass.c
|
|
|
-+++ b/drivers/net/eth-phy-uclass.c
|
|
|
-@@ -154,7 +154,7 @@ static int eth_phy_of_to_plat(struct ude
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
--static void eth_phy_reset(struct udevice *dev, int value)
|
|
|
-+void eth_phy_reset(struct udevice *dev, int value)
|
|
|
- {
|
|
|
- struct eth_phy_device_priv *uc_priv = dev_get_uclass_priv(dev);
|
|
|
- u32 delay;
|
|
|
---- a/include/eth_phy.h
|
|
|
-+++ b/include/eth_phy.h
|
|
|
-@@ -14,5 +14,6 @@ int eth_phy_binds_nodes(struct udevice *
|
|
|
- int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus);
|
|
|
- struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev);
|
|
|
- int eth_phy_get_addr(struct udevice *dev);
|
|
|
-+void eth_phy_reset(struct udevice *dev, int value);
|
|
|
-
|
|
|
- #endif
|