From 0c961312c53d47cd7fe0942b6cd13f361d0efcec Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 10 Apr 2025 16:41:38 +0800 Subject: [PATCH 1/9] net: phy: rockchip-fephy: Change off-energy level0 threshold between link up/down Change-Id: I635d8ed3b3afd4a3e271f4071472aa4cd572dda0 Signed-off-by: David Wu --- drivers/net/phy/rockchip-fephy.c | 67 ++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/drivers/net/phy/rockchip-fephy.c b/drivers/net/phy/rockchip-fephy.c index 6c5ac91a0c77..4cf793a4a789 100644 --- a/drivers/net/phy/rockchip-fephy.c +++ b/drivers/net/phy/rockchip-fephy.c @@ -44,47 +44,37 @@ #define TSTCNTL_READ(bank, reg) (TSTCNTL_RD | ((bank) << TSTCNTL_BANK_SEL) \ | ((reg) << TSTCNTL_READ_ADDR)) -#define TSTMODE_ENABLE 0x400 -#define TSTMODE_DISABLE 0x0 - +#define GAIN_PRE GENMASK(5, 2) #define WR_ADDR_A7CFG 0x18 enum { BANK_DSP0 = 0, BANK_WOL, - BANK_BIST = 3, + BANK_DSP0_READ, + BANK_BIST, BANK_AFE, BANK_DSP1 }; struct rockchip_fephy_priv { struct phy_device *phydev; + int old_link; int wol_irq; }; -static int rockchip_fephy_init_tstmode(struct phy_device *phydev) +static int rockchip_fephy_bank_read(struct phy_device *phydev, u8 bank, u32 reg) { int ret; - ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); + ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_READ(bank, reg)); if (ret) return ret; - ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE); - if (ret) - return ret; - - ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); - if (ret) - return ret; - - return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_ENABLE); -} - -static int rockchip_fephy_close_tstmode(struct phy_device *phydev) -{ - /* Back to basic register bank */ - return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTMODE_DISABLE); + if (bank) + return phy_read(phydev, SMI_ADDR_TSTREAD1); + else + return (phy_read(phydev, SMI_ADDR_TSTREAD1) | + (phy_read(phydev, SMI_ADDR_TSTREAD2) << 16)); } static int rockchip_fephy_bank_write(struct phy_device *phydev, u8 bank, @@ -108,10 +98,6 @@ static int rockchip_fephy_config_init(struct phy_device *phydev) if (ret) return ret; - ret = rockchip_fephy_init_tstmode(phydev); - if (ret) - return ret; - /* off-energy level0 threshold */ ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6664); if (ret) @@ -122,10 +108,6 @@ static int rockchip_fephy_config_init(struct phy_device *phydev) if (ret) return ret; - ret = rockchip_fephy_close_tstmode(phydev); - if (ret) - return ret; - return ret; } @@ -134,6 +116,32 @@ static int rockchip_fephy_config_aneg(struct phy_device *phydev) return genphy_config_aneg(phydev); } +static void rockchip_feph_link_change_notify(struct phy_device *phydev) +{ + struct rockchip_fephy_priv *priv = phydev->priv; + int ret; + + if (priv->old_link && !phydev->link) { + priv->old_link = 0; + ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6664); + if (ret) + return; + } else if (!priv->old_link && phydev->link) { + int gain; + + priv->old_link = 1; + /* read gain level */ + gain = rockchip_fephy_bank_read(phydev, BANK_DSP0, 0x0); + if (gain < 0) + return; + if (!(gain & GAIN_PRE)) { + ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6666); + if (ret) + return; + } + } +} + static int rockchip_fephy_wol_enable(struct phy_device *phydev) { struct net_device *ndev = phydev->attached_dev; @@ -263,6 +271,7 @@ static struct phy_driver rockchip_fephy_driver[] = { /* PHY_BASIC_FEATURES */ .features = PHY_BASIC_FEATURES, .flags = 0, + .link_change_notify = rockchip_feph_link_change_notify, .soft_reset = genphy_soft_reset, .config_init = rockchip_fephy_config_init, .config_aneg = rockchip_fephy_config_aneg, From 7a2e9b2ecc7890de025eda43bfb7207e0ddd5e73 Mon Sep 17 00:00:00 2001 From: David Wu Date: Fri, 25 Apr 2025 12:05:56 +0800 Subject: [PATCH 2/9] net: phy: rockchip-fephy: Add 24M clock rate setting Change-Id: Ie1f51e419bddb458e03be1e048260660a63f020a Signed-off-by: David Wu --- drivers/net/phy/rockchip-fephy.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/net/phy/rockchip-fephy.c b/drivers/net/phy/rockchip-fephy.c index 4cf793a4a789..2acae6b0321c 100644 --- a/drivers/net/phy/rockchip-fephy.c +++ b/drivers/net/phy/rockchip-fephy.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #define INTERNAL_FEPHY_ID 0x06808101 @@ -58,6 +59,7 @@ enum { struct rockchip_fephy_priv { struct phy_device *phydev; + unsigned int clk_rate; int old_link; int wol_irq; }; @@ -91,6 +93,7 @@ static int rockchip_fephy_bank_write(struct phy_device *phydev, u8 bank, static int rockchip_fephy_config_init(struct phy_device *phydev) { + struct rockchip_fephy_priv *priv = phydev->priv; int ret; /* LED Control, default:0x7f */ @@ -108,6 +111,23 @@ static int rockchip_fephy_config_init(struct phy_device *phydev) if (ret) return ret; + if (priv->clk_rate == 24000000) { + int sel; + + /* pll cp cur sel */ + sel = rockchip_fephy_bank_read(phydev, BANK_AFE, 0x3); + if (sel < 0) + return sel; + ret = rockchip_fephy_bank_write(phydev, BANK_AFE, 0x3, sel | 0x2); + if (ret) + return ret; + + /* pll lpf res sel */ + ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0x1a, 0x6); + if (ret) + return ret; + } + return ret; } @@ -210,6 +230,9 @@ static int rockchip_fephy_probe(struct phy_device *phydev) return -ENOMEM; phydev->priv = priv; + if (device_property_read_u32(&phydev->mdio.dev, "clock-frequency", &priv->clk_rate)) + priv->clk_rate = 24000000; + priv->wol_irq = platform_get_irq_byname_optional(to_platform_device(&phydev->mdio.dev), "wol_irq"); if (priv->wol_irq == -EPROBE_DEFER) From 9efda1e814560478aea098fc0a633d5a8ed44765 Mon Sep 17 00:00:00 2001 From: David Wu Date: Fri, 23 May 2025 10:08:52 +0800 Subject: [PATCH 3/9] net: phy: rockchip-fephy: Fix for the correct names Change-Id: I14f536ff6b817764ad716a26874c1162eacebd73 Signed-off-by: David Wu --- drivers/net/phy/rockchip-fephy.c | 76 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/drivers/net/phy/rockchip-fephy.c b/drivers/net/phy/rockchip-fephy.c index 2acae6b0321c..594e371f8008 100644 --- a/drivers/net/phy/rockchip-fephy.c +++ b/drivers/net/phy/rockchip-fephy.c @@ -19,7 +19,7 @@ #define INTERNAL_FEPHY_ID 0x06808101 #define MII_INTERNAL_CTRL_STATUS 17 -#define SMI_ADDR_TSTCNTL 20 +#define SMI_ADDR_CFGCNTL 20 #define SMI_ADDR_TSTREAD1 21 #define SMI_ADDR_TSTREAD2 22 #define SMI_ADDR_TSTWRITE 23 @@ -34,27 +34,27 @@ #define MII_SPEED_10 BIT(2) #define MII_SPEED_100 BIT(3) -#define TSTCNTL_WRITE_ADDR 0 -#define TSTCNTL_READ_ADDR 5 -#define TSTCNTL_BANK_SEL 11 -#define TSTCNTL_RD (BIT(15) | BIT(10)) -#define TSTCNTL_WR (BIT(14) | BIT(10)) +#define CFGCNTL_WRITE_ADDR 0 +#define CFGCNTL_READ_ADDR 5 +#define CFGCNTL_GROUP_SEL 11 +#define CFGCNTL_RD (BIT(15) | BIT(10)) +#define CFGCNTL_WR (BIT(14) | BIT(10)) -#define TSTCNTL_WRITE(bank, reg) (TSTCNTL_WR | ((bank) << TSTCNTL_BANK_SEL) \ - | ((reg) << TSTCNTL_WRITE_ADDR)) -#define TSTCNTL_READ(bank, reg) (TSTCNTL_RD | ((bank) << TSTCNTL_BANK_SEL) \ - | ((reg) << TSTCNTL_READ_ADDR)) +#define CFGCNTL_WRITE(group, reg) (CFGCNTL_WR | ((group) << CFGCNTL_GROUP_SEL) \ + | ((reg) << CFGCNTL_WRITE_ADDR)) +#define CFGCNTL_READ(group, reg) (CFGCNTL_RD | ((group) << CFGCNTL_GROUP_SEL) \ + | ((reg) << CFGCNTL_READ_ADDR)) #define GAIN_PRE GENMASK(5, 2) #define WR_ADDR_A7CFG 0x18 enum { - BANK_DSP0 = 0, - BANK_WOL, - BANK_DSP0_READ, - BANK_BIST, - BANK_AFE, - BANK_DSP1 + GROUP_CFG0 = 0, + GROUP_WOL, + GROUP_CFG0_READ, + GROUP_BIST, + GROUP_AFE, + GROUP_CFG1 }; struct rockchip_fephy_priv { @@ -64,23 +64,23 @@ struct rockchip_fephy_priv { int wol_irq; }; -static int rockchip_fephy_bank_read(struct phy_device *phydev, u8 bank, u32 reg) +static int rockchip_fephy_group_read(struct phy_device *phydev, u8 group, u32 reg) { int ret; - ret = phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_READ(bank, reg)); + ret = phy_write(phydev, SMI_ADDR_CFGCNTL, CFGCNTL_READ(group, reg)); if (ret) return ret; - if (bank) + if (group) return phy_read(phydev, SMI_ADDR_TSTREAD1); else return (phy_read(phydev, SMI_ADDR_TSTREAD1) | (phy_read(phydev, SMI_ADDR_TSTREAD2) << 16)); } -static int rockchip_fephy_bank_write(struct phy_device *phydev, u8 bank, - u32 reg, u16 val) +static int rockchip_fephy_group_write(struct phy_device *phydev, u8 group, + u32 reg, u16 val) { int ret; @@ -88,7 +88,7 @@ static int rockchip_fephy_bank_write(struct phy_device *phydev, u8 bank, if (ret) return ret; - return phy_write(phydev, SMI_ADDR_TSTCNTL, TSTCNTL_WRITE(bank, reg)); + return phy_write(phydev, SMI_ADDR_CFGCNTL, CFGCNTL_WRITE(group, reg)); } static int rockchip_fephy_config_init(struct phy_device *phydev) @@ -102,12 +102,12 @@ static int rockchip_fephy_config_init(struct phy_device *phydev) return ret; /* off-energy level0 threshold */ - ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6664); + ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6664); if (ret) return ret; /* 100M amplitude control */ - ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0x18, 0xc); + ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0x18, 0xc); if (ret) return ret; @@ -115,15 +115,15 @@ static int rockchip_fephy_config_init(struct phy_device *phydev) int sel; /* pll cp cur sel */ - sel = rockchip_fephy_bank_read(phydev, BANK_AFE, 0x3); + sel = rockchip_fephy_group_read(phydev, GROUP_AFE, 0x3); if (sel < 0) return sel; - ret = rockchip_fephy_bank_write(phydev, BANK_AFE, 0x3, sel | 0x2); + ret = rockchip_fephy_group_write(phydev, GROUP_AFE, 0x3, sel | 0x2); if (ret) return ret; /* pll lpf res sel */ - ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0x1a, 0x6); + ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0x1a, 0x6); if (ret) return ret; } @@ -143,7 +143,7 @@ static void rockchip_feph_link_change_notify(struct phy_device *phydev) if (priv->old_link && !phydev->link) { priv->old_link = 0; - ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6664); + ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6664); if (ret) return; } else if (!priv->old_link && phydev->link) { @@ -151,11 +151,11 @@ static void rockchip_feph_link_change_notify(struct phy_device *phydev) priv->old_link = 1; /* read gain level */ - gain = rockchip_fephy_bank_read(phydev, BANK_DSP0, 0x0); + gain = rockchip_fephy_group_read(phydev, GROUP_CFG0, 0x0); if (gain < 0) return; if (!(gain & GAIN_PRE)) { - ret = rockchip_fephy_bank_write(phydev, BANK_DSP0, 0xa, 0x6666); + ret = rockchip_fephy_group_write(phydev, GROUP_CFG0, 0xa, 0x6666); if (ret) return; } @@ -167,22 +167,22 @@ static int rockchip_fephy_wol_enable(struct phy_device *phydev) struct net_device *ndev = phydev->attached_dev; int ret; - ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x0, - ((u16)ndev->dev_addr[4] << 8) + ndev->dev_addr[5]); + ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x0, + ((u16)ndev->dev_addr[4] << 8) + ndev->dev_addr[5]); if (ret) return ret; - ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x1, - ((u16)ndev->dev_addr[2] << 8) + ndev->dev_addr[3]); + ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x1, + ((u16)ndev->dev_addr[2] << 8) + ndev->dev_addr[3]); if (ret) return ret; - ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x2, - ((u16)ndev->dev_addr[0] << 8) + ndev->dev_addr[1]); + ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x2, + ((u16)ndev->dev_addr[0] << 8) + ndev->dev_addr[1]); if (ret) return ret; - ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x3, 0xf); + ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x3, 0xf); if (ret) return ret; @@ -198,7 +198,7 @@ static int rockchip_fephy_wol_disable(struct phy_device *phydev) { int ret; - ret = rockchip_fephy_bank_write(phydev, BANK_WOL, 0x3, 0x0); + ret = rockchip_fephy_group_write(phydev, GROUP_WOL, 0x3, 0x0); if (ret) return ret; From 42db19d682ee16c5c09a62afdefc3d81aa635bfc Mon Sep 17 00:00:00 2001 From: David Wu Date: Fri, 21 Mar 2025 19:22:29 +0800 Subject: [PATCH 4/9] net: phy: rockchip-fephy: Add param to access group registers Signed-off-by: David Wu Change-Id: Ia5a777c66ae71082256cefa37cff151d993c0a0a --- drivers/net/phy/rockchip-fephy.c | 356 +++++++++++++++++++++++++++++++ 1 file changed, 356 insertions(+) diff --git a/drivers/net/phy/rockchip-fephy.c b/drivers/net/phy/rockchip-fephy.c index 594e371f8008..1b7fdd4685a4 100644 --- a/drivers/net/phy/rockchip-fephy.c +++ b/drivers/net/phy/rockchip-fephy.c @@ -62,6 +62,7 @@ struct rockchip_fephy_priv { unsigned int clk_rate; int old_link; int wol_irq; + int current_group; }; static int rockchip_fephy_group_read(struct phy_device *phydev, u8 group, u32 reg) @@ -220,6 +221,355 @@ static irqreturn_t rockchip_fephy_wol_irq_thread(int irq, void *dev_id) return IRQ_HANDLED; } +static void rockchip_fephy_dump_cfg1_group_regs(struct phy_device *phydev, int group, char *buf) +{ + int reg = 0, val = 0; + + for (reg = 0; reg < 18; reg++) { + val = rockchip_fephy_group_read(phydev, GROUP_CFG1, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + if (buf) + sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val); + else + pr_info("group%d reg_%02d: 0x%x\n", group, reg, val); + } +} + +static void rockchip_fephy_dump_afe_group_regs(struct phy_device *phydev, int group, char *buf) +{ + int reg = 0, val = 0; + + for (reg = 0; reg < 32; reg++) { + val = rockchip_fephy_group_read(phydev, GROUP_AFE, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + if (buf) + sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val); + else + pr_info("group%d reg_%02d: 0x%x\n", group, reg, val); + } +} + +static void rockchip_fephy_dump_bist_group_regs(struct phy_device *phydev, int group, char *buf) +{ + int reg = 0, val = 0; + + for (reg = 0; reg < 32; reg++) { + val = rockchip_fephy_group_read(phydev, GROUP_BIST, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + if (buf) + sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val); + else + pr_info("group%d reg_%02d: 0x%x\n", group, reg, val); + } +} + +static void rockchip_fephy_dump_cfg_read_group_regs(struct phy_device *phydev, int group, char *buf) +{ + int reg = 0, val = 0; + + for (reg = 0; reg < 32; reg++) { + val = rockchip_fephy_group_read(phydev, GROUP_CFG0_READ, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + if (buf) + sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val); + else + pr_info("group%d reg_%02d: 0x%x\n", group, reg, val); + } +} + +static void rockchip_fephy_dump_wol_group_regs(struct phy_device *phydev, int group, char *buf) +{ + int reg = 0, val = 0; + + for (reg = 0; reg < 13; reg++) { + val = rockchip_fephy_group_read(phydev, GROUP_WOL, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + if (buf) + sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val); + else + pr_info("group%d reg_%02d: 0x%x\n", group, reg, val); + } +} + +static void rockchip_fephy_dump_cfg_group_regs(struct phy_device *phydev, int group, char *buf) +{ + int reg = 0, val = 0; + + for (reg = 0; reg < 32; reg++) { + val = rockchip_fephy_group_read(phydev, GROUP_CFG0, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + if (buf) + sprintf(buf, "%sgroup%d %2d: 0x%x\n", buf, group, reg, val); + else + pr_info("group%d reg_%02d: 0x%x\n", group, reg, val); + } +} + +static void rockchip_fephy_phy_read_priv_reg(struct phy_device *phydev, int group, int reg) +{ + int val = 0; + + switch (group) { + case GROUP_CFG0: /* CFG0 register group */ + val = rockchip_fephy_group_read(phydev, GROUP_CFG0, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_WOL: /* WOL register group */ + val = rockchip_fephy_group_read(phydev, GROUP_WOL, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_CFG0_READ: /* CFG0_read register group */ + val = rockchip_fephy_group_read(phydev, GROUP_CFG0_READ, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_BIST: /* BIST register group */ + val = rockchip_fephy_group_read(phydev, GROUP_BIST, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_AFE: /* AFE register group */ + val = rockchip_fephy_group_read(phydev, GROUP_AFE, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_CFG1: /* CFG1 register group */ + val = rockchip_fephy_group_read(phydev, GROUP_CFG1, reg); + if (val < 0) { + pr_err("group%d %2d read error: %d\n", group, reg, val); + return; + } + pr_info("read group%d reg_%02d: 0x%x\n", group, reg, val); + break; + default: + pr_err("error group num: %d\n", group); + break; + } +} + +static void +rockchip_fephy_phy_write_priv_reg(struct phy_device *phydev, int group, int reg, int rval) +{ + int val = 0; + + switch (group) { + case GROUP_CFG0: /* CFG0 register group */ + val = rockchip_fephy_group_write(phydev, GROUP_CFG0, reg, rval); + if (val) { + pr_err("group%d %2d write error: %d\n", group, reg, val); + return; + } + pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_WOL: /* WOL register group */ + val = rockchip_fephy_group_write(phydev, GROUP_WOL, reg, rval); + if (val) { + pr_err("group%d %2d write error: %d\n", group, reg, val); + return; + } + pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_CFG0_READ: /* CFG0_read register group */ + val = rockchip_fephy_group_write(phydev, GROUP_CFG0_READ, reg, rval); + if (val) { + pr_err("group%d %2d write error: %d\n", group, reg, val); + return; + } + pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_BIST: /* BIST register group */ + val = rockchip_fephy_group_write(phydev, GROUP_BIST, reg, rval); + if (val) { + pr_err("group%d %2d write error: %d\n", group, reg, val); + return; + } + pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_AFE: /* AFE register group */ + val = rockchip_fephy_group_write(phydev, GROUP_AFE, reg, rval); + if (val) { + pr_err("group%d %2d write error: %d\n", group, reg, val); + return; + } + pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val); + break; + case GROUP_CFG1: /* CFG1 register group */ + val = rockchip_fephy_group_write(phydev, GROUP_CFG1, reg, rval); + if (val) { + pr_err("group%d %2d write error: %d\n", group, reg, val); + return; + } + pr_info("write group%d reg_%02d: 0x%x\n", group, reg, val); + break; + default: + pr_err("error group num: %d\n", group); + break; + } +} + +static ssize_t +phy_param_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct phy_device *phydev = to_phy_device(dev); + struct rockchip_fephy_priv *priv = phydev->priv; + + switch (priv->current_group) { + case GROUP_CFG0: + rockchip_fephy_dump_cfg_group_regs(phydev, GROUP_CFG0, buf); + break; + case GROUP_WOL: + rockchip_fephy_dump_wol_group_regs(phydev, GROUP_WOL, buf); + break; + case GROUP_CFG0_READ: + rockchip_fephy_dump_cfg_read_group_regs(phydev, GROUP_CFG0_READ, buf); + break; + case GROUP_BIST: + rockchip_fephy_dump_bist_group_regs(phydev, GROUP_BIST, buf); + break; + case GROUP_AFE: + rockchip_fephy_dump_afe_group_regs(phydev, GROUP_AFE, buf); + break; + case GROUP_CFG1: + rockchip_fephy_dump_cfg1_group_regs(phydev, GROUP_CFG1, buf); + break; + default: + pr_err("error group num: %d\n", priv->current_group); + break; + } + + return strlen(buf); +} + +static ssize_t phy_param_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct phy_device *phydev = to_phy_device(dev); + struct rockchip_fephy_priv *priv = phydev->priv; + int arg1 = 0, arg2 = 0, arg3 = 0, ret; + char *buff, *p, *para; + char *argv[4]; + int argc; + char cmd; + + buff = kstrdup(buf, GFP_KERNEL); + if (!buff) + return -EINVAL; + + p = buff; + for (argc = 0; argc < 4; argc++) { + para = strsep(&p, " "); + if (!para) { + argv[argc] = NULL; + continue; + } + argv[argc] = para; + } + if (argc < 1 || argc > 4) + goto end; + + if (argv[1]) { + ret = kstrtoint(argv[1], 0, &arg1); + if (ret) + pr_err("kstrtoint failed\n"); + } + if (argv[2]) { + ret = kstrtoint(argv[2], 0, &arg2); + if (ret) + pr_err("kstrtoint failed\n"); + } + if (argv[3]) { + ret = kstrtoint(argv[3], 0, &arg3); + if (ret) + pr_err("kstrtoint failed\n"); + } + + cmd = argv[0][0]; + switch (cmd) { + case 'R': + rockchip_fephy_phy_read_priv_reg(phydev, arg1, arg2); + priv->current_group = arg1; + break; + case 'W': + rockchip_fephy_phy_write_priv_reg(phydev, arg1, arg2, arg3); + priv->current_group = arg1; + break; + case 'd': + priv->current_group = GROUP_CFG0; + rockchip_fephy_dump_cfg_group_regs(phydev, GROUP_CFG0, NULL); + break; + case 'w': + priv->current_group = GROUP_WOL; + rockchip_fephy_dump_wol_group_regs(phydev, GROUP_WOL, NULL); + break; + case 'p': + priv->current_group = GROUP_CFG0_READ; + rockchip_fephy_dump_cfg_read_group_regs(phydev, GROUP_CFG0_READ, NULL); + break; + case 'b': + priv->current_group = GROUP_BIST; + rockchip_fephy_dump_bist_group_regs(phydev, GROUP_BIST, NULL); + break; + case 'a': + priv->current_group = GROUP_AFE; + rockchip_fephy_dump_afe_group_regs(phydev, GROUP_AFE, NULL); + break; + case 's': + priv->current_group = GROUP_CFG1; + rockchip_fephy_dump_cfg1_group_regs(phydev, GROUP_CFG1, NULL); + break; + case 'r': + priv->current_group = GROUP_CFG0; + if (phydev && phydev->drv->soft_reset) + phydev->drv->soft_reset(phydev); + break; + default: + goto end; + } + + return count; + +end: + kfree(buff); + return 0; +} + +static DEVICE_ATTR_RW(phy_param); + static int rockchip_fephy_probe(struct phy_device *phydev) { struct rockchip_fephy_priv *priv; @@ -251,6 +601,11 @@ static int rockchip_fephy_probe(struct phy_device *phydev) } priv->phydev = phydev; + + ret = device_create_file(&phydev->mdio.dev, &dev_attr_phy_param); + if (ret) + goto irq_err; + return 0; irq_err: @@ -260,6 +615,7 @@ irq_err: static void rockchip_fephy_remove(struct phy_device *phydev) { + device_remove_file(&phydev->mdio.dev, &dev_attr_phy_param); } static int rockchip_fephy_suspend(struct phy_device *phydev) From 185b03cb0d9b93758f5bcde50cd1ee84c1ead061 Mon Sep 17 00:00:00 2001 From: David Wu Date: Thu, 24 Apr 2025 19:43:35 +0800 Subject: [PATCH 5/9] ethernet: stmmac: dwmac-rk: Correct clock input/output sel for RV1126B Change-Id: Ie1bfc0a598827493b23fd5d7c040d8e552adaaff Signed-off-by: David Wu --- drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c index 9c2d790d3a90..729740e02e2d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-rk.c @@ -2557,8 +2557,8 @@ static const struct rk_gmac_ops rv1126_ops = { #define RV1126B_RK_MACPHY_DISABLE 0 #define RV1126B_RK_MACPHY_ENABLE BIT(31) -#define RV1126B_RK_MACPHY_EXTCLK_SEL_OUTPUT 0 -#define RV1126B_RK_MACPHY_EXTCLK_SEL_INPUT BIT(8) +#define RV1126B_RK_MACPHY_EXTCLK_SEL_INPUT 0 +#define RV1126B_RK_MACPHY_EXTCLK_SEL_OUTPUT BIT(8) #define RV1126B_RK_MACPHY_CLK_24M 0 #define RV1126B_RK_MACPHY_CLK_50M BIT(11) @@ -2686,23 +2686,18 @@ static void rv1126b_integrated_phy_power(struct rk_priv_data *priv, bool up) value = RV1126B_RK_MACPHY_CLK_50M; else value = RV1126B_RK_MACPHY_CLK_24M; - value |= priv->clock_input ? RV1126B_RK_MACPHY_EXTCLK_SEL_INPUT : RV1126B_RK_MACPHY_EXTCLK_SEL_OUTPUT; - regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON2, value); regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON0, RV1126B_RK_MACPHY_PHY_ID | RV1126B_RK_MACPHY_PHY_ADDR); + value = RV1126B_RK_MACPHY_PHY_REVISION | RV1126B_RK_MACPHY_PHY_MODEL | RV1126B_RK_MACPHY_ENABLE; regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1, value); - usleep_range(200, 220); - reset_control_deassert(priv->phy_reset); - regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1, - RV1126B_RK_MACPHY_DISABLE); usleep_range(100, 120); - regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1, value); + reset_control_deassert(priv->phy_reset); } else { regmap_write(priv->grf, RV1126B_VI_GRF_RK_MACPHY_CON1, RV1126B_RK_MACPHY_DISABLE); From f58cc2736aa4811b30b2a02ea4ffae83ea66c834 Mon Sep 17 00:00:00 2001 From: David Wu Date: Fri, 25 Apr 2025 12:19:26 +0800 Subject: [PATCH 6/9] arm64: dts: rockchip: rv1126b-evb: Change clock rates to 24M for fephy Change-Id: I906b8a3e483f6db790701a10d6a0aa71080948bc Signed-off-by: David Wu --- arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi | 2 +- arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts | 2 +- arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi index 5c3fed632304..262b8c2674a2 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi +++ b/arch/arm64/boot/dts/rockchip/rv1126b-evb1-v10.dtsi @@ -325,7 +325,7 @@ compatible = "ethernet-phy-id0680.8101", "ethernet-phy-ieee802.3-c22"; reg = <2>; clocks = <&cru CLK_MACPHY>; - clock-frequency = <50000000>; + clock-frequency = <24000000>; resets = <&cru SRST_RESETN_MACPHY>; pinctrl-names = "default"; pinctrl-0 = <&fephym1_pins>; diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts b/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts index f3a7677ba283..24d56147893f 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rv1126b-evb2-v10.dts @@ -320,7 +320,7 @@ compatible = "ethernet-phy-id0680.8101", "ethernet-phy-ieee802.3-c22"; reg = <2>; clocks = <&cru CLK_MACPHY>; - clock-frequency = <50000000>; + clock-frequency = <24000000>; resets = <&cru SRST_RESETN_MACPHY>; pinctrl-names = "default"; pinctrl-0 = <&fephym2_pins>; diff --git a/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts b/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts index 7655959ea5ef..4f3ea4610f34 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts +++ b/arch/arm64/boot/dts/rockchip/rv1126b-iotest-v10.dts @@ -129,7 +129,7 @@ compatible = "ethernet-phy-id0680.8101", "ethernet-phy-ieee802.3-c22"; reg = <2>; clocks = <&cru CLK_MACPHY>; - clock-frequency = <50000000>; + clock-frequency = <24000000>; resets = <&cru SRST_RESETN_MACPHY>; phy-is-integrated; }; From 37f373979d6e55999081b9dd0f59c284fc59456d Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 1 Jul 2024 16:38:00 +0800 Subject: [PATCH 7/9] i2c: rk3x: Add dma feature Through testing, it is found that it is better to set the DMA threshold to 64 bytes, and there will be two more interrupts after 64 bytes without DMA, which will save time by using DMA, but for TX, the threshold should be one byte less, because there will be one more byte of device address. Signed-off-by: David Wu Change-Id: Iebc3cd81f62d7ee8887319e121a13ed0c27984ad --- drivers/i2c/busses/i2c-rk3x.c | 372 +++++++++++++++++++++++++++++++++- 1 file changed, 361 insertions(+), 11 deletions(-) diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 927d9ea6c597..37837e58193c 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -7,6 +7,8 @@ */ #include +#include +#include #include #include #include @@ -41,6 +43,10 @@ #define REG_IPD 0x1c /* interrupt pending */ #define REG_FCNT 0x20 /* finished count */ #define REG_SCL_OE_DB 0x24 /* Slave hold scl debounce */ +#define REG_DMA_SADDR 0x80 /* DMA Addr register for TX */ +#define REG_DMA_TXDATA 0x88 /* DMA TXDATA register */ +#define REG_DMA_RXDATA 0x8c /* DMA RXDATA register */ +#define REG_DMA_CONF 0x90 /* DMA conf register */ #define REG_CON1 0x228 /* control register1 */ /* Data buffer offsets */ @@ -62,6 +68,7 @@ enum { #define REG_CON_STOP BIT(4) #define REG_CON_LASTACK BIT(5) /* 1: send NACK after last received byte */ #define REG_CON_ACTACK BIT(6) /* 1: stop if NACK is received */ +#define REG_CON_CLEAN_DMA BIT(7) /* 1: clean DMA conf */ #define REG_CON_TUNING_MASK GENMASK_ULL(15, 8) @@ -90,7 +97,7 @@ enum { #define REG_INT_STOP BIT(5) /* STOP condition generated */ #define REG_INT_NAKRCV BIT(6) /* NACK received */ #define REG_INT_SLV_HDSCL BIT(7) /* slave hold scl */ -#define REG_INT_ALL 0xff +#define REG_INT_ALL 0x3ff /* Disable i2c all irqs */ #define IEN_ALL_DISABLE 0 @@ -104,9 +111,23 @@ enum { #define REG_CON1_TRANSFER_AUTO_STOP BIT(1) #define REG_CON1_NACK_AUTO_STOP BIT(2) +/* DMA */ +#define DMA_ENABLE_DMA_TX (BIT(16) | BIT(0)) +#define DMA_ENABLE_DMA_RX (BIT(16 + 1) | BIT(1)) +#define DMA_DISABLE_DMA_TX (BIT(16)) +#define DMA_DISABLE_DMA_RX (BIT(16 + 1)) +#define DMA_ENABLE_RK_DMA (BIT(16 + 2) | BIT(2)) +#define DMA_FLUSH_TXRX (BIT(16 + 4) | BIT(4)) + +#define DMA_TX_SLAVE_ADDR_OFFSET 0 +#define DMA_TX_SLAVE_ADDR1_OFFSE 8 /* for 10bit addr */ +#define DMA_TX_SLAVE_ADDR_VALID BIT(16) +#define DMA_TX_SLAVE_ADDR1_VALID BIT(17) /* for 10bit addr */ + /* Constants */ -#define WAIT_TIMEOUT 200 /* ms */ -#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ +#define WAIT_TIMEOUT 200 /* ms */ +#define DEFAULT_SCL_RATE (100 * 1000) /* Hz */ +#define TX_IDLE_WAIT_TIMEOUT 10 /* ms */ /** * struct i2c_spec_values - I2C specification values for various modes @@ -186,15 +207,22 @@ enum rk3x_i2c_state { STATE_STOP }; +enum rk3x_i2c_xfer_mode { + RK_I2C_FIFO, + RK_I2C_DMA +}; + /** * struct rk3x_i2c_soc_data - SOC-specific data * @grf_offset: offset inside the grf regmap for setting the i2c type * @calc_timings: Callback function for i2c timing information calculated + * @dma_control: DMA initialize or release function for some Socs support DMA */ struct rk3x_i2c_soc_data { int grf_offset; int (*calc_timings)(unsigned long, struct i2c_timings *, struct rk3x_i2c_calced_timings *); + void (*dma_control)(struct platform_device *pdev, bool init); }; /** @@ -232,6 +260,12 @@ struct rk3x_i2c { struct clk *clk; struct clk *pclk; struct notifier_block clk_rate_nb; + phys_addr_t dma_addr_rx; + phys_addr_t dma_addr_tx; + struct dma_chan *dma_rx; + struct dma_chan *dma_tx; + struct scatterlist sg; + enum dma_data_direction dma_direction; bool autostop_supported; struct reset_control *reset; @@ -251,12 +285,15 @@ struct rk3x_i2c { u8 addr; unsigned int mode; bool is_last_msg; + void *dma_buf; /* I2C state machine */ enum rk3x_i2c_state state; unsigned int processed; int error; unsigned int suspended:1; + enum rk3x_i2c_xfer_mode xfer_mode; + bool stop_after_dma; struct notifier_block i2c_restart_nb; bool system_restarting; @@ -342,16 +379,250 @@ out: return false; } -/** - * rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt. - * @i2c: target controller data - */ -static void rk3x_i2c_start(struct rk3x_i2c *i2c) +static void rk3x_i2c_dma_unmap(struct rk3x_i2c *i2c) +{ + struct dma_chan *chan = i2c->dma_direction == DMA_FROM_DEVICE + ? i2c->dma_rx : i2c->dma_tx; + + dma_unmap_single(chan->device->dev, sg_dma_address(&i2c->sg), + i2c->msg->len, i2c->dma_direction); + + i2c->dma_direction = DMA_NONE; +} + +static void rk3x_i2c_cleanup_dma(struct rk3x_i2c *i2c) +{ + if (i2c->dma_direction == DMA_NONE) + return; + else if (i2c->dma_direction == DMA_FROM_DEVICE) + dmaengine_terminate_all(i2c->dma_rx); + else if (i2c->dma_direction == DMA_TO_DEVICE) + dmaengine_terminate_all(i2c->dma_tx); + + rk3x_i2c_dma_unmap(i2c); +} + +static inline bool +rk3x_i2c_wait_for_tx_idle(struct rk3x_i2c *i2c) +{ + u32 ipd; + + if (!readl_relaxed_poll_timeout(i2c->regs + REG_IPD, ipd, + ipd & REG_INT_STOP, 100, + TX_IDLE_WAIT_TIMEOUT * 1000)) + return true; + + dev_warn(i2c->dev, "i2c controller is in busy state!\n"); + return false; +} + +static void rk3x_i2c_complete_dma(struct rk3x_i2c *i2c, unsigned long time_left) +{ + /* if it is no error happen, copy to rx buf */ + if (!i2c->error && time_left > 0) { + /* if it is dma tx, make sure tx is completed */ + if (!rk3x_i2c_wait_for_tx_idle(i2c)) + i2c->error = -EBUSY; + else + i2c->stop_after_dma = true; + } + + rk3x_i2c_cleanup_dma(i2c); + i2c_put_dma_safe_msg_buf(i2c->dma_buf, i2c->msg, + i2c->stop_after_dma); +} + +static void rk3x_i2c_handle_dma_xfer_stop(struct rk3x_i2c *i2c) +{ + u32 con = i2c_readl(i2c, REG_CON); + + /* disable start bit */ + con &= ~REG_CON_START; + i2c_writel(i2c, con, REG_CON); + + if (!i2c->error) + i2c->state = STATE_IDLE; + + rk3x_i2c_clean_ipd(i2c); + i2c->dma_buf = NULL; + i2c->msg = NULL; +} + +static void rk3x_i2c_dma_irq_callback(void *data) +{ + struct rk3x_i2c *i2c = (struct rk3x_i2c *)data; + + i2c->busy = false; + /* signal rk3x_i2c_xfer that we are finished */ + rk3x_i2c_wake_up(i2c); +} + +static int rk3x_i2c_prepate_dma_sg(struct rk3x_i2c *i2c, void *dma_buf) +{ + struct dma_async_tx_descriptor *rxdesc = NULL, *txdesc = NULL; + u32 val = DMA_FLUSH_TXRX | DMA_ENABLE_RK_DMA; + bool read = i2c->msg->flags & I2C_M_RD; + dma_cookie_t cookie; + dma_addr_t dma_addr; + + if (i2c->mode == REG_CON_MOD_TX) { + val |= DMA_ENABLE_DMA_TX | DMA_DISABLE_DMA_RX; + i2c_writel(i2c, val, REG_DMA_CONF); + val = ((i2c->addr & 0x7f) << 1) | DMA_TX_SLAVE_ADDR_VALID; + i2c_writel(i2c, val, REG_DMA_SADDR); + } else { + val |= DMA_ENABLE_DMA_RX | DMA_DISABLE_DMA_TX; + i2c_writel(i2c, val, REG_DMA_CONF); + } + + if (read) { + dma_addr = dma_map_single(i2c->dma_rx->device->dev, + dma_buf, i2c->msg->len, + DMA_FROM_DEVICE); + if (dma_mapping_error(i2c->dma_rx->device->dev, dma_addr)) { + dev_err(i2c->dev, "dma rx map failed\n"); + return -EINVAL; + } + } else { + dma_addr = dma_map_single(i2c->dma_tx->device->dev, + dma_buf, i2c->msg->len, + DMA_TO_DEVICE); + if (dma_mapping_error(i2c->dma_tx->device->dev, dma_addr)) { + dev_err(i2c->dev, "dma tx map failed\n"); + return -EINVAL; + } + } + sg_dma_len(&i2c->sg) = i2c->msg->len; + sg_dma_address(&i2c->sg) = dma_addr; + + if (read) { + struct dma_slave_config rxconf = { + .direction = DMA_DEV_TO_MEM, + .src_addr = i2c->dma_addr_rx, + .src_addr_width = 4, + .src_maxburst = 4, + }; + + i2c->dma_direction = DMA_FROM_DEVICE; + dmaengine_slave_config(i2c->dma_rx, &rxconf); + rxdesc = dmaengine_prep_slave_sg(i2c->dma_rx, &i2c->sg, + 1, DMA_DEV_TO_MEM, + DMA_PREP_INTERRUPT); + if (!rxdesc) { + if (txdesc) + dmaengine_terminate_sync(i2c->dma_tx); + return -EINVAL; + } + + rxdesc->callback = rk3x_i2c_dma_irq_callback; + rxdesc->callback_param = i2c; + + cookie = dmaengine_submit(rxdesc); + if (dma_submit_error(cookie)) { + dev_err(i2c->dev, "submitting dma rx failed\n"); + rk3x_i2c_cleanup_dma(i2c); + return -EINVAL; + } + dma_async_issue_pending(i2c->dma_rx); + } else { + struct dma_slave_config txconf = { + .direction = DMA_MEM_TO_DEV, + .dst_addr = i2c->dma_addr_tx, + .dst_addr_width = 4, + .dst_maxburst = 4, + }; + + i2c->dma_direction = DMA_TO_DEVICE; + dmaengine_slave_config(i2c->dma_tx, &txconf); + txdesc = dmaengine_prep_slave_sg(i2c->dma_tx, &i2c->sg, + 1, DMA_MEM_TO_DEV, + DMA_PREP_INTERRUPT); + if (!txdesc) { + if (rxdesc) + dmaengine_terminate_sync(i2c->dma_rx); + return -EINVAL; + } + + txdesc->callback = rk3x_i2c_dma_irq_callback; + txdesc->callback_param = i2c; + + cookie = dmaengine_submit(txdesc); + if (dma_submit_error(cookie)) { + dev_err(i2c->dev, "submitting dma tx failed\n"); + rk3x_i2c_cleanup_dma(i2c); + return -EINVAL; + } + dma_async_issue_pending(i2c->dma_tx); + } + + return 0; +} + +static void rk3x_i2c_start_dma(struct rk3x_i2c *i2c, void *dma_buf) +{ + u32 con = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; + u32 con1 = 0; + + i2c->xfer_mode = RK_I2C_DMA; + /* use dma should enable autostop, it is also the last msg */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) + con1 = REG_CON1_NACK_AUTO_STOP | REG_CON1_AUTO_STOP; + + con1 |= REG_CON1_TRANSFER_AUTO_STOP | REG_CON1_AUTO_STOP; + i2c_writel(i2c, con1, REG_CON1); + i2c->state = STATE_STOP; + + /* only enable nack irq for dma mode */ + i2c_writel(i2c, REG_INT_NAKRCV, REG_IEN); + + rk3x_i2c_prepate_dma_sg(i2c, dma_buf); + + /* enable adapter with correct mode, send START condition */ + con |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START | REG_CON_LASTACK; + /* if we want to react to NACK, set ACTACK bit */ + if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) + con |= REG_CON_ACTACK; + i2c_writel(i2c, con, REG_CON); + + /* enable transition */ + if (i2c->mode == REG_CON_MOD_TX) + /* Add one byte for device addr */ + i2c_writel(i2c, i2c->msg->len + 1, REG_MTXCNT); + else + i2c_writel(i2c, i2c->msg->len, REG_MRXCNT); +} + +static u8 *rk3x_i2c_get_dma_safe_msg_buf(struct i2c_msg *msg, unsigned int threshold) +{ + void *buf; + + /* also skip 0-length msgs for bogus thresholds of 0 */ + if (!threshold) + pr_debug("rk3x i2c DMA buffer for addr=0x%02x with length 0 is bogus\n", + msg->addr); + + if (msg->len < threshold || msg->len == 0) + return NULL; + + pr_debug("rk3x i2c using bounce buffer for addr=0x%02x, len=%d\n", + msg->addr, msg->len); + + if (msg->flags & I2C_M_RD) + return kzalloc(ALIGN(msg->len, cache_line_size()), GFP_KERNEL); + + buf = kzalloc(ALIGN(msg->len, cache_line_size()), GFP_KERNEL); + memcpy(buf, msg->buf, msg->len); + + return buf; +} + +static void rk3x_i2c_start_fifo(struct rk3x_i2c *i2c) { u32 val = i2c_readl(i2c, REG_CON) & REG_CON_TUNING_MASK; bool auto_stop = rk3x_i2c_auto_stop(i2c); int length = 0; + i2c->xfer_mode = RK_I2C_FIFO; /* enable appropriate interrupts */ if (i2c->mode == REG_CON_MOD_TX) { if (!auto_stop) { @@ -368,7 +639,7 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c) } /* enable adapter with correct mode, send START condition */ - val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START; + val |= REG_CON_EN | REG_CON_MOD(i2c->mode) | REG_CON_START | REG_CON_CLEAN_DMA; /* if we want to react to NACK, set ACTACK bit */ if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) @@ -383,6 +654,25 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c) rk3x_i2c_prepare_read(i2c); } +/** + * rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt. + * @i2c: target controller data + */ +static void rk3x_i2c_start(struct rk3x_i2c *i2c, bool polling) +{ + /* DMA or FIFO */ + if (i2c->dma_tx && i2c->dma_rx && !polling) { + if (i2c->msg->flags & I2C_M_RD) + i2c->dma_buf = rk3x_i2c_get_dma_safe_msg_buf(i2c->msg, 65); + else + i2c->dma_buf = rk3x_i2c_get_dma_safe_msg_buf(i2c->msg, 64); + + if (i2c->dma_buf) + return rk3x_i2c_start_dma(i2c, i2c->dma_buf); + } + rk3x_i2c_start_fifo(i2c); +} + /** * rk3x_i2c_stop - Generate a STOP condition, which triggers a REG_INT_STOP interrupt. * @i2c: target controller data @@ -1171,6 +1461,8 @@ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num) i2c->busy = true; i2c->processed = 0; i2c->error = 0; + i2c->xfer_mode = RK_I2C_FIFO; + i2c->stop_after_dma = false; rk3x_i2c_clean_ipd(i2c); if (i2c->autostop_supported) @@ -1261,21 +1553,29 @@ static int rk3x_i2c_xfer_common(struct i2c_adapter *adap, spin_unlock_irqrestore(&i2c->lock, flags); if (!polling) { - rk3x_i2c_start(i2c); + rk3x_i2c_start(i2c, polling); timeout = wait_event_timeout(i2c->wait, !i2c->busy, msecs_to_jiffies(xfer_time)); } else { disable_irq(i2c->irq); - rk3x_i2c_start(i2c); + rk3x_i2c_start(i2c, polling); timeout = rk3x_i2c_wait_xfer_poll(i2c, xfer_time); enable_irq(i2c->irq); } + /* 'stop_after_dma' tells if DMA xfer was complete */ + if (i2c->xfer_mode == RK_I2C_DMA) + rk3x_i2c_complete_dma(i2c, timeout); + spin_lock_irqsave(&i2c->lock, flags); + /* Handles states of the I2C controller itself for DMA xfer mode */ + if (i2c->xfer_mode == RK_I2C_DMA) + rk3x_i2c_handle_dma_xfer_stop(i2c); + if (timeout == 0) { ipd = i2c_readl(i2c, REG_IPD); dev_err(i2c->dev, "timeout, ipd: 0x%02x, state: %d\n", @@ -1450,6 +1750,38 @@ static u32 rk3x_i2c_func(struct i2c_adapter *adap) return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_PROTOCOL_MANGLING; } +static void rk3x_i2c_dma_control(struct platform_device *pdev, bool init) +{ + struct rk3x_i2c *i2c = platform_get_drvdata(pdev); + + if (init) { + struct resource *res; + + i2c->dma_tx = dma_request_chan(&pdev->dev, "tx"); + if (IS_ERR(i2c->dma_tx)) { + dev_dbg(&pdev->dev, "Failed to request TX DMA channel\n"); + i2c->dma_tx = NULL; + } + + i2c->dma_rx = dma_request_chan(&pdev->dev, "rx"); + if (IS_ERR(i2c->dma_rx)) { + dev_dbg(&pdev->dev, "Failed to request RX DMA channel\n"); + i2c->dma_rx = NULL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (i2c->dma_tx) + i2c->dma_addr_tx = res->start + REG_DMA_TXDATA; + if (i2c->dma_rx) + i2c->dma_addr_rx = res->start + REG_DMA_RXDATA; + } else { + if (i2c->dma_rx) + dma_release_channel(i2c->dma_rx); + if (i2c->dma_tx) + dma_release_channel(i2c->dma_tx); + } +} + static const struct i2c_algorithm rk3x_i2c_algorithm = { .master_xfer = rk3x_i2c_xfer, .master_xfer_atomic = rk3x_i2c_xfer_polling, @@ -1471,6 +1803,12 @@ static const struct rk3x_i2c_soc_data rk3066_soc_data = { .calc_timings = rk3x_i2c_v0_calc_timings, }; +static const struct rk3x_i2c_soc_data rv1126b_soc_data = { + .grf_offset = -1, + .calc_timings = rk3x_i2c_v1_calc_timings, + .dma_control = rk3x_i2c_dma_control, +}; + static const struct rk3x_i2c_soc_data rk3188_soc_data = { .grf_offset = 0x0a4, .calc_timings = rk3x_i2c_v0_calc_timings, @@ -1500,6 +1838,10 @@ static const struct of_device_id rk3x_i2c_match[] = { .compatible = "rockchip,rv1126-i2c", .data = &rv1126_soc_data }, + { + .compatible = "rockchip,rv1126b-i2c", + .data = &rv1126b_soc_data + }, { .compatible = "rockchip,rk3066-i2c", .data = &rk3066_soc_data @@ -1720,6 +2062,9 @@ static int rk3x_i2c_probe(struct platform_device *pdev) goto err_clk_notifier; } + if (i2c->soc_data->dma_control) + i2c->soc_data->dma_control(pdev, true); + ret = i2c_add_numbered_adapter(&i2c->adap); if (ret < 0) goto err_register_restart_handler; @@ -1727,6 +2072,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev) return 0; err_register_restart_handler: + if (i2c->soc_data->dma_control) + i2c->soc_data->dma_control(pdev, false); unregister_restart_handler(&i2c->i2c_restart_nb); err_clk_notifier: clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); @@ -1743,6 +2090,9 @@ static int rk3x_i2c_remove(struct platform_device *pdev) i2c_del_adapter(&i2c->adap); + if (i2c->soc_data->dma_control) + i2c->soc_data->dma_control(pdev, false); + unregister_restart_handler(&i2c->i2c_restart_nb); clk_notifier_unregister(i2c->clk, &i2c->clk_rate_nb); clk_unprepare(i2c->pclk); From ff069916e83800486f269bbf813fde8e6287245e Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 1 Jul 2024 16:38:00 +0800 Subject: [PATCH 8/9] dt-bindings: i2c: rockchip: add rockchip,rv1126b-i2c Change-Id: I4760657a7ff06738ba22461a578da62bb537b97f Signed-off-by: David Wu --- Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml index 82b9d6682297..90b90a24e0df 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.yaml @@ -21,6 +21,7 @@ properties: compatible: oneOf: - const: rockchip,rv1108-i2c + - const: rockchip,rv1126b-i2c - const: rockchip,rk3066-i2c - const: rockchip,rk3188-i2c - const: rockchip,rk3228-i2c From 3a8b94c5be2ca4a2a9dd5916074e2f466ebcc57f Mon Sep 17 00:00:00 2001 From: David Wu Date: Mon, 14 Apr 2025 19:36:38 +0800 Subject: [PATCH 9/9] arm64: dts: rockchip: rv1126b: Remove dma for i2c0, i2c4 and i2c5 Change-Id: I02b5f05f72bf91ecadc05e1ae705aaf16629d193 Signed-off-by: David Wu --- arch/arm64/boot/dts/rockchip/rv1126b.dtsi | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/arch/arm64/boot/dts/rockchip/rv1126b.dtsi b/arch/arm64/boot/dts/rockchip/rv1126b.dtsi index 43d450d2f2ca..d07837421cda 100644 --- a/arch/arm64/boot/dts/rockchip/rv1126b.dtsi +++ b/arch/arm64/boot/dts/rockchip/rv1126b.dtsi @@ -1576,7 +1576,7 @@ }; i2c2: i2c@20800000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x20800000 0x1000>; interrupts = ; #address-cells = <1>; @@ -2273,22 +2273,20 @@ }; i2c0: i2c@21100000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x21100000 0x1000>; interrupts = ; #address-cells = <1>; #size-cells = <0>; clocks = <&cru CLK_I2C0>, <&cru PCLK_I2C0>; clock-names = "i2c", "pclk"; - dmas = <&dmac 29>, <&dmac 28>; - dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&i2c0m0_pins>; status = "disabled"; }; i2c1: i2c@21110000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x21110000 0x1000>; interrupts = ; #address-cells = <1>; @@ -2303,7 +2301,7 @@ }; i2c3: i2c@21120000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x21120000 0x1000>; interrupts = ; #address-cells = <1>; @@ -2318,30 +2316,26 @@ }; i2c4: i2c@21130000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x21130000 0x1000>; interrupts = ; #address-cells = <1>; #size-cells = <0>; clocks = <&cru CLK_I2C4>, <&cru PCLK_I2C4>; clock-names = "i2c", "pclk"; - dmas = <&dmac 37>, <&dmac 36>; - dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&i2c4m0_pins>; status = "disabled"; }; i2c5: i2c@21140000 { - compatible = "rockchip,rv1126b-i2c", "rockchip,rk3399-i2c"; + compatible = "rockchip,rv1126b-i2c"; reg = <0x21140000 0x1000>; interrupts = ; #address-cells = <1>; #size-cells = <0>; clocks = <&cru CLK_I2C5>, <&cru PCLK_I2C5>; clock-names = "i2c", "pclk"; - dmas = <&dmac 39>, <&dmac 38>; - dma-names = "tx", "rx"; pinctrl-names = "default"; pinctrl-0 = <&i2c5m0_pins>; status = "disabled";