Commit eddb5ba9 authored by Nicolas Frattaroli's avatar Nicolas Frattaroli Committed by Chanwoo Choi
Browse files

PM / devfreq: rockchip-dfi: add support for LPDDR5



The Rockchip RK3588 SoC can also support LPDDR5 memory. This type of
memory needs some special case handling in the rockchip-dfi driver.

Add support for it in rockchip-dfi, as well as the needed GRF register
definitions.

This has been tested as returning both the right cycle count and
bandwidth on a LPDDR5 board where the CKR bit is 1. I couldn't test
whether the values are correct on a system where CKR is 0, as I'm not
savvy enough with the Rockchip tooling to know whether this can be set
in the DDR init blob.

Downstream has some special case handling for a hardware version where
not just the control bits differ, but also the register. Since I don't
know whether that hardware version is in any production silicon, it's
left unimplemented for now, with an error message urging users to report
if they have such a system.

There is a slight change of behaviour for non-LPDDR5 systems: instead of
writing 0 as the control flags to the control register and pretending
everything is alright if the memory type is unknown, we now explicitly
return an error.

Signed-off-by: default avatarNicolas Frattaroli <nicolas.frattaroli@collabora.com>
Reviewed-by: default avatarSascha Hauer <s.hauer@pengutronix.de>
Acked-by: default avatarHeiko Stuebner <heiko@sntech.de>
Signed-off-by: default avatarChanwoo Choi <cw00.choi@samsung.com>
Link: https://patchwork.kernel.org/project/linux-pm/patch/20250530-rk3588-dfi-improvements-v1-2-6e077c243a95@collabora.com/
parent f89c7fb8
Loading
Loading
Loading
Loading
+66 −18
Original line number Diff line number Diff line
@@ -34,15 +34,18 @@

/* DDRMON_CTRL */
#define DDRMON_CTRL	0x04
#define DDRMON_CTRL_LPDDR5		BIT(6)
#define DDRMON_CTRL_DDR4		BIT(5)
#define DDRMON_CTRL_LPDDR4		BIT(4)
#define DDRMON_CTRL_HARDWARE_EN		BIT(3)
#define DDRMON_CTRL_LPDDR23		BIT(2)
#define DDRMON_CTRL_SOFTWARE_EN		BIT(1)
#define DDRMON_CTRL_TIMER_CNT_EN	BIT(0)
#define DDRMON_CTRL_DDR_TYPE_MASK	(DDRMON_CTRL_DDR4 | \
#define DDRMON_CTRL_DDR_TYPE_MASK	(DDRMON_CTRL_LPDDR5 | \
					 DDRMON_CTRL_DDR4 | \
					 DDRMON_CTRL_LPDDR4 | \
					 DDRMON_CTRL_LPDDR23)
#define DDRMON_CTRL_LP5_BANK_MODE_MASK	GENMASK(8, 7)

#define DDRMON_CH0_WR_NUM		0x20
#define DDRMON_CH0_RD_NUM		0x24
@@ -116,13 +119,60 @@ struct rockchip_dfi {
	int buswidth[DMC_MAX_CHANNELS];
	int ddrmon_stride;
	bool ddrmon_ctrl_single;
	u32 lp5_bank_mode;
	bool lp5_ckr;	/* true if in 4:1 command-to-data clock ratio mode */
	unsigned int count_multiplier;	/* number of data clocks per count */
};

static int rockchip_dfi_ddrtype_to_ctrl(struct rockchip_dfi *dfi, u32 *ctrl,
					u32 *mask)
{
	u32 ddrmon_ver;

	*mask = DDRMON_CTRL_DDR_TYPE_MASK;

	switch (dfi->ddr_type) {
	case ROCKCHIP_DDRTYPE_LPDDR2:
	case ROCKCHIP_DDRTYPE_LPDDR3:
		*ctrl = DDRMON_CTRL_LPDDR23;
		break;
	case ROCKCHIP_DDRTYPE_LPDDR4:
	case ROCKCHIP_DDRTYPE_LPDDR4X:
		*ctrl = DDRMON_CTRL_LPDDR4;
		break;
	case ROCKCHIP_DDRTYPE_LPDDR5:
		ddrmon_ver = readl_relaxed(dfi->regs);
		if (ddrmon_ver < 0x40) {
			*ctrl = DDRMON_CTRL_LPDDR5 | dfi->lp5_bank_mode;
			*mask |= DDRMON_CTRL_LP5_BANK_MODE_MASK;
			break;
		}

		/*
		 * As it is unknown whether the unpleasant special case
		 * behaviour used by the vendor kernel is needed for any
		 * shipping hardware, ask users to report if they have
		 * some of that hardware.
		 */
		dev_err(&dfi->edev->dev,
			"unsupported DDRMON version 0x%04X, please let linux-rockchip know!\n",
			ddrmon_ver);
		return -EOPNOTSUPP;
	default:
		dev_err(&dfi->edev->dev, "unsupported memory type 0x%X\n",
			dfi->ddr_type);
		return -EOPNOTSUPP;
	}

	return 0;
}

static int rockchip_dfi_enable(struct rockchip_dfi *dfi)
{
	void __iomem *dfi_regs = dfi->regs;
	int i, ret = 0;
	u32 ctrl;
	u32 ctrl_mask;

	mutex_lock(&dfi->mutex);

@@ -136,8 +186,11 @@ static int rockchip_dfi_enable(struct rockchip_dfi *dfi)
		goto out;
	}

	ret = rockchip_dfi_ddrtype_to_ctrl(dfi, &ctrl, &ctrl_mask);
	if (ret)
		goto out;

	for (i = 0; i < dfi->max_channels; i++) {
		u32 ctrl = 0;

		if (!(dfi->channel_mask & BIT(i)))
			continue;
@@ -147,21 +200,7 @@ static int rockchip_dfi_enable(struct rockchip_dfi *dfi)
			       DDRMON_CTRL_SOFTWARE_EN | DDRMON_CTRL_HARDWARE_EN),
			       dfi_regs + i * dfi->ddrmon_stride + DDRMON_CTRL);

		/* set ddr type to dfi */
		switch (dfi->ddr_type) {
		case ROCKCHIP_DDRTYPE_LPDDR2:
		case ROCKCHIP_DDRTYPE_LPDDR3:
			ctrl = DDRMON_CTRL_LPDDR23;
			break;
		case ROCKCHIP_DDRTYPE_LPDDR4:
		case ROCKCHIP_DDRTYPE_LPDDR4X:
			ctrl = DDRMON_CTRL_LPDDR4;
			break;
		default:
			break;
		}

		writel_relaxed(HIWORD_UPDATE(ctrl, DDRMON_CTRL_DDR_TYPE_MASK),
		writel_relaxed(HIWORD_UPDATE(ctrl, ctrl_mask),
			       dfi_regs + i * dfi->ddrmon_stride + DDRMON_CTRL);

		/* enable count, use software mode */
@@ -652,6 +691,7 @@ static int rockchip_ddr_perf_init(struct rockchip_dfi *dfi)
		break;
	case ROCKCHIP_DDRTYPE_LPDDR4:
	case ROCKCHIP_DDRTYPE_LPDDR4X:
	case ROCKCHIP_DDRTYPE_LPDDR5:
		dfi->burst_len = 16;
		break;
	}
@@ -730,7 +770,7 @@ static int rk3568_dfi_init(struct rockchip_dfi *dfi)
static int rk3588_dfi_init(struct rockchip_dfi *dfi)
{
	struct regmap *regmap_pmu = dfi->regmap_pmu;
	u32 reg2, reg3, reg4;
	u32 reg2, reg3, reg4, reg6;

	regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG2, &reg2);
	regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG3, &reg3);
@@ -757,6 +797,14 @@ static int rk3588_dfi_init(struct rockchip_dfi *dfi)
	dfi->ddrmon_stride = 0x4000;
	dfi->count_multiplier = 2;

	if (dfi->ddr_type == ROCKCHIP_DDRTYPE_LPDDR5) {
		regmap_read(regmap_pmu, RK3588_PMUGRF_OS_REG6, &reg6);
		dfi->lp5_bank_mode = FIELD_GET(RK3588_PMUGRF_OS_REG6_LP5_BANK_MODE, reg6) << 7;
		dfi->lp5_ckr = FIELD_GET(RK3588_PMUGRF_OS_REG6_LP5_CKR, reg6);
		if (dfi->lp5_ckr)
			dfi->count_multiplier *= 2;
	}

	return 0;
};

+6 −2
Original line number Diff line number Diff line
@@ -14,5 +14,9 @@

#define RK3588_PMUGRF_OS_REG4				0x210
#define RK3588_PMUGRF_OS_REG5				0x214
#define RK3588_PMUGRF_OS_REG6				0x218
#define RK3588_PMUGRF_OS_REG6_LP5_BANK_MODE		GENMASK(2, 1)
/* Whether the LPDDR5 is in 2:1 (= 0) or 4:1 (= 1) CKR a.k.a. DQS mode */
#define RK3588_PMUGRF_OS_REG6_LP5_CKR			BIT(0)

#endif /* __SOC_RK3588_GRF_H */
+1 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ enum {
	ROCKCHIP_DDRTYPE_LPDDR3	= 6,
	ROCKCHIP_DDRTYPE_LPDDR4	= 7,
	ROCKCHIP_DDRTYPE_LPDDR4X = 8,
	ROCKCHIP_DDRTYPE_LPDDR5	= 9,
};

#endif /* __SOC_ROCKCHIP_GRF_H */