Commit d6fd599c authored by Rafael J. Wysocki's avatar Rafael J. Wysocki
Browse files

Merge branches 'pm-em', 'pm-opp' and 'pm-devfreq'

Merge energy model management, OPP (operating performance points) and
devfreq updates for 6.18-rc1:

 - Prevent CPU capacity updates after registering a perf domain from
   failing on a first CPU that is not present (Christian Loehle)

 - Add support for the cases in which frequency alone is not sufficient
   to uniquely identify an OPP (Krishna Chaitanya Chundru)

 - Use to_result() for OPP error handling in Rust (Onur Özkan)

 - Add support for LPDDR5 on Rockhip RK3588 SoC to rockchip-dfi devfreq
   driver (Nicolas Frattaroli)

 - Fix an issue where DDR cycle counts on RK3588/RK3528 with LPDDR4(X)
   are reported as half by adding a cycle multiplier to the DFI driver
   in rockchip-dfi devfreq-event driver (Nicolas Frattaroli)

 - Fix missing error pointer dereference check of regulator instance in
   the mtk-cci devfreq driver probe and remove a redundant condition from
   an if () statement in that driver (Dan Carpenter, Liao Yuanhong)

* pm-em:
  PM: EM: Fix late boot with holes in CPU topology

* pm-opp:
  OPP: Add support to find OPP for a set of keys
  rust: opp: use to_result for error handling

* pm-devfreq:
  PM / devfreq: rockchip-dfi: add support for LPDDR5
  PM / devfreq: rockchip-dfi: double count on RK3588
  PM / devfreq: mtk-cci: avoid redundant conditions
  PM / devfreq: mtk-cci: Fix potential error pointer dereference in probe()
Loading
Loading
Loading
Loading
+72 −19
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,12 +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);

@@ -135,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;
@@ -146,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 */
@@ -435,7 +475,7 @@ static u64 rockchip_ddr_perf_event_get_count(struct perf_event *event)

	switch (event->attr.config) {
	case PERF_EVENT_CYCLES:
		count = total.c[0].clock_cycles;
		count = total.c[0].clock_cycles * dfi->count_multiplier;
		break;
	case PERF_EVENT_READ_BYTES:
		for (i = 0; i < dfi->max_channels; i++)
@@ -651,10 +691,14 @@ 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;
	}

	if (!dfi->count_multiplier)
		dfi->count_multiplier = 1;

	ret = perf_pmu_register(pmu, "rockchip_ddr", -1);
	if (ret)
		return ret;
@@ -726,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);
@@ -751,6 +795,15 @@ static int rk3588_dfi_init(struct rockchip_dfi *dfi)
	dfi->max_channels = 4;

	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;
};
+3 −2
Original line number Diff line number Diff line
@@ -86,7 +86,7 @@ static int mtk_ccifreq_set_voltage(struct mtk_ccifreq_drv *drv, int new_voltage)
						      soc_data->sram_max_volt);
				return ret;
			}
		} else if (pre_voltage > new_voltage) {
		} else {
			voltage = max(new_voltage,
				      pre_vsram - soc_data->max_volt_shift);
			ret = regulator_set_voltage(drv->proc_reg, voltage,
@@ -386,7 +386,8 @@ static int mtk_ccifreq_probe(struct platform_device *pdev)
out_free_resources:
	if (regulator_is_enabled(drv->proc_reg))
		regulator_disable(drv->proc_reg);
	if (drv->sram_reg && regulator_is_enabled(drv->sram_reg))
	if (!IS_ERR_OR_NULL(drv->sram_reg) &&
	    regulator_is_enabled(drv->sram_reg))
		regulator_disable(drv->sram_reg);

	return ret;
+99 −0
Original line number Diff line number Diff line
@@ -476,6 +476,16 @@ static unsigned long _read_bw(struct dev_pm_opp *opp, int index)
	return opp->bandwidth[index].peak;
}

static unsigned long _read_opp_key(struct dev_pm_opp *opp, int index,
				   struct dev_pm_opp_key *key)
{
	key->bw = opp->bandwidth ? opp->bandwidth[index].peak : 0;
	key->freq = opp->rates[index];
	key->level = opp->level;

	return true;
}

/* Generic comparison helpers */
static bool _compare_exact(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
			   unsigned long opp_key, unsigned long key)
@@ -509,6 +519,22 @@ static bool _compare_floor(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
	return false;
}

static bool _compare_opp_key_exact(struct dev_pm_opp **opp,
		struct dev_pm_opp *temp_opp, struct dev_pm_opp_key *opp_key,
		struct dev_pm_opp_key *key)
{
	bool level_match = (key->level == OPP_LEVEL_UNSET || opp_key->level == key->level);
	bool freq_match = (key->freq == 0 || opp_key->freq == key->freq);
	bool bw_match = (key->bw == 0 || opp_key->bw == key->bw);

	if (freq_match && level_match && bw_match) {
		*opp = temp_opp;
		return true;
	}

	return false;
}

/* Generic key finding helpers */
static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table,
		unsigned long *key, int index, bool available,
@@ -541,6 +567,37 @@ static struct dev_pm_opp *_opp_table_find_key(struct opp_table *opp_table,
	return opp;
}

static struct dev_pm_opp *_opp_table_find_opp_key(struct opp_table *opp_table,
		struct dev_pm_opp_key *key, bool available,
		unsigned long (*read)(struct dev_pm_opp *opp, int index,
				      struct dev_pm_opp_key *key),
		bool (*compare)(struct dev_pm_opp **opp, struct dev_pm_opp *temp_opp,
				struct dev_pm_opp_key *opp_key, struct dev_pm_opp_key *key),
		bool (*assert)(struct opp_table *opp_table, unsigned int index))
{
	struct dev_pm_opp *temp_opp, *opp = ERR_PTR(-ERANGE);
	struct dev_pm_opp_key temp_key;

	/* Assert that the requirement is met */
	if (!assert(opp_table, 0))
		return ERR_PTR(-EINVAL);

	guard(mutex)(&opp_table->lock);

	list_for_each_entry(temp_opp, &opp_table->opp_list, node) {
		if (temp_opp->available == available) {
			read(temp_opp, 0, &temp_key);
			if (compare(&opp, temp_opp, &temp_key, key)) {
				/* Increment the reference count of OPP */
				dev_pm_opp_get(opp);
				break;
			}
		}
	}

	return opp;
}

static struct dev_pm_opp *
_find_key(struct device *dev, unsigned long *key, int index, bool available,
	  unsigned long (*read)(struct dev_pm_opp *opp, int index),
@@ -632,6 +689,48 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_freq_exact);

/**
 * dev_pm_opp_find_key_exact() - Search for an OPP with exact key set
 * @dev:		Device for which the OPP is being searched
 * @key:		OPP key set to match
 * @available:		true/false - match for available OPP
 *
 * Search for an exact match of the key set in the OPP table.
 *
 * Return: A matching opp on success, else ERR_PTR in case of error.
 * Possible error values:
 * EINVAL:	for bad pointers
 * ERANGE:	no match found for search
 * ENODEV:	if device not found in list of registered devices
 *
 * Note: 'available' is a modifier for the search. If 'available' == true,
 * then the match is for exact matching key and is available in the stored
 * OPP table. If false, the match is for exact key which is not available.
 *
 * This provides a mechanism to enable an OPP which is not available currently
 * or the opposite as well.
 *
 * The callers are required to call dev_pm_opp_put() for the returned OPP after
 * use.
 */
struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
					     struct dev_pm_opp_key *key,
					     bool available)
{
	struct opp_table *opp_table __free(put_opp_table) = _find_opp_table(dev);

	if (IS_ERR(opp_table)) {
		dev_err(dev, "%s: OPP table not found (%ld)\n", __func__,
			PTR_ERR(opp_table));
		return ERR_CAST(opp_table);
	}

	return _opp_table_find_opp_key(opp_table, key, available,
				       _read_opp_key, _compare_opp_key_exact,
				       assert_single_clk);
}
EXPORT_SYMBOL_GPL(dev_pm_opp_find_key_exact);

/**
 * dev_pm_opp_find_freq_exact_indexed() - Search for an exact freq for the
 *					 clock corresponding to the index
+30 −0
Original line number Diff line number Diff line
@@ -98,6 +98,25 @@ struct dev_pm_opp_data {
	unsigned long u_volt;
};

/**
 * struct dev_pm_opp_key - Key used to identify OPP entries
 * @freq:       Frequency in Hz. Use 0 if frequency is not to be matched.
 * @level:      Performance level associated with the OPP entry.
 *              Use OPP_LEVEL_UNSET if level is not to be matched.
 * @bw:         Bandwidth associated with the OPP entry.
 *              Use 0 if bandwidth is not to be matched.
 *
 * This structure is used to uniquely identify an OPP entry based on
 * frequency, performance level, and bandwidth. Each field can be
 * selectively ignored during matching by setting it to its respective
 * NOP value.
 */
struct dev_pm_opp_key {
	unsigned long freq;
	unsigned int level;
	u32 bw;
};

#if defined(CONFIG_PM_OPP)

struct opp_table *dev_pm_opp_get_opp_table(struct device *dev);
@@ -131,6 +150,10 @@ struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
					      unsigned long freq,
					      bool available);

struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
					     struct dev_pm_opp_key *key,
					     bool available);

struct dev_pm_opp *
dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq,
				   u32 index, bool available);
@@ -289,6 +312,13 @@ static inline struct dev_pm_opp *dev_pm_opp_find_freq_exact(struct device *dev,
	return ERR_PTR(-EOPNOTSUPP);
}

static inline struct dev_pm_opp *dev_pm_opp_find_key_exact(struct device *dev,
							   struct dev_pm_opp_key *key,
							   bool available)
{
	return ERR_PTR(-EOPNOTSUPP);
}

static inline struct dev_pm_opp *
dev_pm_opp_find_freq_exact_indexed(struct device *dev, unsigned long freq,
				   u32 index, bool available)
+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 */
Loading