MediaTek soc driver updates

This adds:
  - A socinfo entry for the MT8371 Genio 520 SoC
  - Support for the Dynamic Voltage and Frequency Scaling
    Resource Controller (DVFSRC) version 4, found in the
    new MediaTek Kompanio Ultra (MT8196) SoC
  - Initial support for the CMDQ mailbox found in the MT8196.
  - A memory leak fix in the MediaTek SVS driver's debug ops.
 -----BEGIN PGP SIGNATURE-----
 
 iLoEABYKAGIWIQQn3Xxr56ypAcSHzXSaNgTPrZeEeAUCaXNXhhsUgAAAAAAEAA5t
 YW51MiwyLjUrMS4xMSwyLDIoHGFuZ2Vsb2dpb2FjY2hpbm8uZGVscmVnbm9AY29s
 bGFib3JhLmNvbQAKCRCaNgTPrZeEeF2oAQDcnPsR1I6J92rmK75Dufy4ayA79Ko0
 j2cYEOzAoO+mzgD/SDq0VGMpPhyatUv5Lj8md69gARhNlylJVDIpC0jBQAQ=
 =v4Rp
 -----END PGP SIGNATURE-----
gpgsig -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAml7I8IACgkQmmx57+YA
 GNmr5g//fM9YAmf/oA9AT6Ta4bQWzzzp9dSIZ18I8v7G4RSpCK9uVK0mVhH4JceU
 QXt63dTL+wAZOHjCIXgpQzcZ6tXdCaD4XZux87Ab+EfrxVPBQbOkwMAA4rkj8Vbd
 LLJ36G2qOyBvpw8+whCmsQCcUn9UqWuDWkgnIRRt04tn/QOCi5XQcKIdvyzQdglB
 TlbGQKxW4XQXzKr1pDrgSF9mie3jtw9Z7povlTiUIhnfqCIkoS5X2upcyWAtGpjr
 ON2t5lIH3TvlrhanqREfHltRBB1GF4AvAadaj0q9dA9DoZLwTlr7SfoiSHiR8kf+
 QongU9Y4ObeLriAsyDT5/SNZ11lSIL+f2jpJMBTzdQ9+NpalE9mgWHA7O76iR4vf
 E3SdwDRSykSXiNg+wzi6GeccHYE1yrpRu05g/MSCuO3N7AMqIYHmvfbWg2sM0ruS
 8J0OYdEbr+JmVScYQ7GPHOdcwgdv4mAn5ENjhwmNjWTYMdjPNi1PnQwrXUzo3jYV
 s7s8+P8yg4aABdPuDH+vDDaVj5T/fHy+OedMkRtGBfHbInu2WG/g4x6kDutiCR1L
 b7Zu72psQEDAtfRrRnQP0/P+8/M7HLV0lbMVWf0H78lM3dQKOHTi6uuyT55IWCTv
 WIwITw4Lo9BzapGjBU7QU2YcEW+t6mJ126wo1UbcM7V4JPcID40=
 =i7PU
 -----END PGP SIGNATURE-----

Merge tag 'mtk-soc-for-v6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/mediatek/linux into soc/drivers

MediaTek soc driver updates

This adds:
 - A socinfo entry for the MT8371 Genio 520 SoC
 - Support for the Dynamic Voltage and Frequency Scaling
   Resource Controller (DVFSRC) version 4, found in the
   new MediaTek Kompanio Ultra (MT8196) SoC
 - Initial support for the CMDQ mailbox found in the MT8196.
 - A memory leak fix in the MediaTek SVS driver's debug ops.

* tag 'mtk-soc-for-v6.20' of https://git.kernel.org/pub/scm/linux/kernel/git/mediatek/linux:
  soc: mediatek: mtk-cmdq: Add mminfra_offset adjustment for DRAM addresses
  soc: mediatek: mtk-cmdq: Extend cmdq_pkt_write API for SoCs without subsys ID
  soc: mediatek: mtk-cmdq: Add pa_base parsing for hardware without subsys ID support
  soc: mediatek: mtk-cmdq: Add cmdq_get_mbox_priv() in cmdq_pkt_create()
  mailbox: mtk-cmdq: Add driver data to support for MT8196
  mailbox: mtk-cmdq: Add mminfra_offset configuration for DRAM transaction
  mailbox: mtk-cmdq: Add GCE hardware virtualization configuration
  mailbox: mtk-cmdq: Add cmdq private data to cmdq_pkt for generating instruction
  soc: mediatek: mtk-dvfsrc: Rework bandwidth calculations
  soc: mediatek: mtk-dvfsrc: Get and Enable DVFSRC clock
  soc: mediatek: mtk-dvfsrc: Add support for DVFSRCv4 and MT8196
  soc: mediatek: mtk-dvfsrc: Write bandwidth to EMI DDR if present
  soc: mediatek: mtk-dvfsrc: Add a new callback for calc_dram_bw
  soc: mediatek: mtk-dvfsrc: Add and propagate DVFSRC bandwidth type
  soc: mediatek: mtk-dvfsrc: Change error check for DVFSRCv4 START cmd
  dt-bindings: soc: mediatek: dvfsrc: Document clock
  soc: mediatek: mtk-socinfo: Add entry for MT8371AV/AZA Genio 520
  soc: mediatek: svs: Fix memory leak in svs_enable_debug_write()

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2026-01-29 10:09:19 +01:00
commit 35a53670ea
8 changed files with 599 additions and 42 deletions

View File

@ -34,6 +34,10 @@ properties:
maxItems: 1
description: DVFSRC common register address and length.
clocks:
items:
- description: Clock that drives the DVFSRC MCU
regulators:
type: object
$ref: /schemas/regulator/mediatek,mt6873-dvfsrc-regulator.yaml#
@ -50,6 +54,7 @@ additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mt8195-clk.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
@ -57,6 +62,7 @@ examples:
system-controller@10012000 {
compatible = "mediatek,mt8195-dvfsrc";
reg = <0 0x10012000 0 0x1000>;
clocks = <&topckgen CLK_TOP_DVFSRC>;
regulators {
compatible = "mediatek,mt8195-dvfsrc-regulator";

View File

@ -14,6 +14,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/sizes.h>
#include <linux/mailbox_controller.h>
#include <linux/mailbox/mtk-cmdq-mailbox.h>
#include <linux/of.h>
@ -43,6 +44,13 @@
#define GCE_CTRL_BY_SW GENMASK(2, 0)
#define GCE_DDR_EN GENMASK(18, 16)
#define GCE_VM_ID_MAP(n) (0x5018 + (n) / 10 * 4)
#define GCE_VM_ID_MAP_THR_FLD_SHIFT(n) ((n) % 10 * 3)
#define GCE_VM_ID_MAP_HOST_VM GENMASK(2, 0)
#define GCE_VM_CPR_GSIZE 0x50c4
#define GCE_VM_CPR_GSIZE_FLD_SHIFT(vm_id) ((vm_id) * 4)
#define GCE_VM_CPR_GSIZE_MAX GENMASK(3, 0)
#define CMDQ_THR_ACTIVE_SLOT_CYCLES 0x3200
#define CMDQ_THR_ENABLED 0x1
#define CMDQ_THR_DISABLED 0x0
@ -87,23 +95,34 @@ struct cmdq {
struct gce_plat {
u32 thread_nr;
u8 shift;
dma_addr_t mminfra_offset;
bool control_by_sw;
bool sw_ddr_en;
bool gce_vm;
u32 gce_num;
};
static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata)
{
/* Convert DMA addr (PA or IOVA) to GCE readable addr */
return addr >> pdata->shift;
return (addr + pdata->mminfra_offset) >> pdata->shift;
}
static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata)
{
/* Revert GCE readable addr to DMA addr (PA or IOVA) */
return (dma_addr_t)addr << pdata->shift;
return ((dma_addr_t)addr << pdata->shift) - pdata->mminfra_offset;
}
void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv)
{
struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
priv->shift_pa = cmdq->pdata->shift;
priv->mminfra_offset = cmdq->pdata->mminfra_offset;
}
EXPORT_SYMBOL(cmdq_get_mbox_priv);
u8 cmdq_get_shift_pa(struct mbox_chan *chan)
{
struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox);
@ -112,6 +131,45 @@ u8 cmdq_get_shift_pa(struct mbox_chan *chan)
}
EXPORT_SYMBOL(cmdq_get_shift_pa);
static void cmdq_vm_init(struct cmdq *cmdq)
{
int i;
u32 vm_cpr_gsize = 0, vm_id_map = 0;
u32 *vm_map = NULL;
if (!cmdq->pdata->gce_vm)
return;
vm_map = kcalloc(cmdq->pdata->thread_nr, sizeof(*vm_map), GFP_KERNEL);
if (!vm_map)
return;
/* only configure the max CPR SRAM size to host vm (vm_id = 0) currently */
vm_cpr_gsize = GCE_VM_CPR_GSIZE_MAX << GCE_VM_CPR_GSIZE_FLD_SHIFT(0);
/* set all thread mapping to host vm currently */
for (i = 0; i < cmdq->pdata->thread_nr; i++)
vm_map[i] = GCE_VM_ID_MAP_HOST_VM << GCE_VM_ID_MAP_THR_FLD_SHIFT(i);
/* set the amount of CPR SRAM to allocate to each VM */
writel(vm_cpr_gsize, cmdq->base + GCE_VM_CPR_GSIZE);
/* config CPR_GSIZE before setting VM_ID_MAP to avoid data leakage */
for (i = 0; i < cmdq->pdata->thread_nr; i++) {
vm_id_map |= vm_map[i];
/* config every 10 threads, e.g., thread id=0~9, 10~19, ..., into one register */
if ((i + 1) % 10 == 0) {
writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(i));
vm_id_map = 0;
}
}
/* config remaining threads settings */
if (cmdq->pdata->thread_nr % 10 != 0)
writel(vm_id_map, cmdq->base + GCE_VM_ID_MAP(cmdq->pdata->thread_nr - 1));
kfree(vm_map);
}
static void cmdq_gctl_value_toggle(struct cmdq *cmdq, bool ddr_enable)
{
u32 val = cmdq->pdata->control_by_sw ? GCE_CTRL_BY_SW : 0;
@ -156,6 +214,7 @@ static void cmdq_init(struct cmdq *cmdq)
WARN_ON(clk_bulk_enable(cmdq->pdata->gce_num, cmdq->clocks));
cmdq_vm_init(cmdq);
cmdq_gctl_value_toggle(cmdq, true);
writel(CMDQ_THR_ACTIVE_SLOT_CYCLES, cmdq->base + CMDQ_THR_SLOT_CYCLES);
@ -782,6 +841,16 @@ static const struct gce_plat gce_plat_mt8195 = {
.gce_num = 2
};
static const struct gce_plat gce_plat_mt8196 = {
.thread_nr = 32,
.shift = 3,
.mminfra_offset = SZ_2G,
.control_by_sw = true,
.sw_ddr_en = true,
.gce_vm = true,
.gce_num = 2
};
static const struct of_device_id cmdq_of_ids[] = {
{.compatible = "mediatek,mt6779-gce", .data = (void *)&gce_plat_mt6779},
{.compatible = "mediatek,mt8173-gce", .data = (void *)&gce_plat_mt8173},
@ -790,6 +859,7 @@ static const struct of_device_id cmdq_of_ids[] = {
{.compatible = "mediatek,mt8188-gce", .data = (void *)&gce_plat_mt8188},
{.compatible = "mediatek,mt8192-gce", .data = (void *)&gce_plat_mt8192},
{.compatible = "mediatek,mt8195-gce", .data = (void *)&gce_plat_mt8195},
{.compatible = "mediatek,mt8196-gce", .data = (void *)&gce_plat_mt8196},
{}
};
MODULE_DEVICE_TABLE(of, cmdq_of_ids);

View File

@ -8,6 +8,7 @@
#include <linux/module.h>
#include <linux/mailbox_controller.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#define CMDQ_WRITE_ENABLE_MASK BIT(0)
@ -60,20 +61,41 @@ int cmdq_dev_get_client_reg(struct device *dev,
struct cmdq_client_reg *client_reg, int idx)
{
struct of_phandle_args spec;
struct resource res;
int err;
if (!client_reg)
return -ENOENT;
err = of_address_to_resource(dev->of_node, 0, &res);
if (err) {
dev_err(dev, "Missing reg in %s node\n", dev->of_node->full_name);
return -EINVAL;
}
client_reg->pa_base = res.start;
err = of_parse_phandle_with_fixed_args(dev->of_node,
"mediatek,gce-client-reg",
3, idx, &spec);
if (err < 0) {
dev_warn(dev,
dev_dbg(dev,
"error %d can't parse gce-client-reg property (%d)",
err, idx);
return err;
/* make subsys invalid */
client_reg->subsys = CMDQ_SUBSYS_INVALID;
/*
* All GCEs support writing register PA with mask without subsys,
* but this requires extra GCE instructions to convert the PA into
* a format that GCE can handle, which is less performance than
* directly using subsys. Therefore, when subsys is available,
* we prefer to use subsys for writing register PA.
*/
client_reg->pkt_write = cmdq_pkt_write_pa;
client_reg->pkt_write_mask = cmdq_pkt_write_mask_pa;
return 0;
}
client_reg->subsys = (u8)spec.args[0];
@ -81,6 +103,9 @@ int cmdq_dev_get_client_reg(struct device *dev,
client_reg->size = (u16)spec.args[2];
of_node_put(spec.np);
client_reg->pkt_write = cmdq_pkt_write_subsys;
client_reg->pkt_write_mask = cmdq_pkt_write_mask_subsys;
return 0;
}
EXPORT_SYMBOL(cmdq_dev_get_client_reg);
@ -140,6 +165,7 @@ int cmdq_pkt_create(struct cmdq_client *client, struct cmdq_pkt *pkt, size_t siz
}
pkt->pa_base = dma_addr;
cmdq_get_mbox_priv(client->chan, &pkt->priv);
return 0;
}
@ -201,6 +227,26 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value)
}
EXPORT_SYMBOL(cmdq_pkt_write);
int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base,
u16 offset, u32 value)
{
int err;
err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base));
if (err < 0)
return err;
return cmdq_pkt_write_s_value(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_LOW(offset), value);
}
EXPORT_SYMBOL(cmdq_pkt_write_pa);
int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/,
u16 offset, u32 value)
{
return cmdq_pkt_write(pkt, subsys, offset, value);
}
EXPORT_SYMBOL(cmdq_pkt_write_subsys);
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
u16 offset, u32 value, u32 mask)
{
@ -218,6 +264,27 @@ int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
}
EXPORT_SYMBOL(cmdq_pkt_write_mask);
int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/, u32 pa_base,
u16 offset, u32 value, u32 mask)
{
int err;
err = cmdq_pkt_assign(pkt, CMDQ_THR_SPR_IDX0, CMDQ_ADDR_HIGH(pa_base));
if (err < 0)
return err;
return cmdq_pkt_write_s_mask_value(pkt, CMDQ_THR_SPR_IDX0,
CMDQ_ADDR_LOW(offset), value, mask);
}
EXPORT_SYMBOL(cmdq_pkt_write_mask_pa);
int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base /*unused*/,
u16 offset, u32 value, u32 mask)
{
return cmdq_pkt_write_mask(pkt, subsys, offset, value, mask);
}
EXPORT_SYMBOL(cmdq_pkt_write_mask_subsys);
int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx, u16 addr_low,
u16 reg_idx)
{
@ -305,6 +372,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_
int ret;
/* read the value of src_addr into high_addr_reg_idx */
src_addr += pkt->priv.mminfra_offset;
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(src_addr));
if (ret < 0)
return ret;
@ -313,6 +381,7 @@ int cmdq_pkt_mem_move(struct cmdq_pkt *pkt, dma_addr_t src_addr, dma_addr_t dst_
return ret;
/* write the value of value_reg_idx into dst_addr */
dst_addr += pkt->priv.mminfra_offset;
ret = cmdq_pkt_assign(pkt, high_addr_reg_idx, CMDQ_ADDR_HIGH(dst_addr));
if (ret < 0)
return ret;
@ -438,7 +507,7 @@ int cmdq_pkt_poll_addr(struct cmdq_pkt *pkt, dma_addr_t addr, u32 value, u32 mas
inst.op = CMDQ_CODE_MASK;
inst.dst_t = CMDQ_REG_TYPE;
inst.sop = CMDQ_POLL_ADDR_GPR;
inst.value = addr;
inst.value = addr + pkt->priv.mminfra_offset;
ret = cmdq_pkt_append_command(pkt, inst);
if (ret < 0)
return ret;
@ -498,7 +567,7 @@ int cmdq_pkt_jump_abs(struct cmdq_pkt *pkt, dma_addr_t addr, u8 shift_pa)
struct cmdq_instruction inst = {
.op = CMDQ_CODE_JUMP,
.offset = CMDQ_JUMP_ABSOLUTE,
.value = addr >> shift_pa
.value = (addr + pkt->priv.mminfra_offset) >> pkt->priv.shift_pa
};
return cmdq_pkt_append_command(pkt, inst);
}

View File

@ -7,6 +7,7 @@
#include <linux/arm-smccc.h>
#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
@ -15,11 +16,17 @@
#include <linux/soc/mediatek/dvfsrc.h>
#include <linux/soc/mediatek/mtk_sip_svc.h>
/* DVFSRC_BASIC_CONTROL */
#define DVFSRC_V4_BASIC_CTRL_OPP_COUNT GENMASK(26, 20)
/* DVFSRC_LEVEL */
#define DVFSRC_V1_LEVEL_TARGET_LEVEL GENMASK(15, 0)
#define DVFSRC_TGT_LEVEL_IDLE 0x00
#define DVFSRC_V1_LEVEL_CURRENT_LEVEL GENMASK(31, 16)
#define DVFSRC_V4_LEVEL_TARGET_LEVEL GENMASK(15, 8)
#define DVFSRC_V4_LEVEL_TARGET_PRESENT BIT(16)
/* DVFSRC_SW_REQ, DVFSRC_SW_REQ2 */
#define DVFSRC_V1_SW_REQ2_DRAM_LEVEL GENMASK(1, 0)
#define DVFSRC_V1_SW_REQ2_VCORE_LEVEL GENMASK(3, 2)
@ -27,24 +34,40 @@
#define DVFSRC_V2_SW_REQ_DRAM_LEVEL GENMASK(3, 0)
#define DVFSRC_V2_SW_REQ_VCORE_LEVEL GENMASK(6, 4)
#define DVFSRC_V4_SW_REQ_EMI_LEVEL GENMASK(3, 0)
#define DVFSRC_V4_SW_REQ_DRAM_LEVEL GENMASK(15, 12)
/* DVFSRC_VCORE */
#define DVFSRC_V2_VCORE_REQ_VSCP_LEVEL GENMASK(14, 12)
/* DVFSRC_TARGET_GEAR */
#define DVFSRC_V4_GEAR_TARGET_DRAM GENMASK(7, 0)
#define DVFSRC_V4_GEAR_TARGET_VCORE GENMASK(15, 8)
/* DVFSRC_GEAR_INFO */
#define DVFSRC_V4_GEAR_INFO_REG_WIDTH 0x4
#define DVFSRC_V4_GEAR_INFO_REG_LEVELS 64
#define DVFSRC_V4_GEAR_INFO_VCORE GENMASK(3, 0)
#define DVFSRC_V4_GEAR_INFO_EMI GENMASK(7, 4)
#define DVFSRC_V4_GEAR_INFO_DRAM GENMASK(15, 12)
#define DVFSRC_POLL_TIMEOUT_US 1000
#define STARTUP_TIME_US 1
#define MTK_SIP_DVFSRC_INIT 0x0
#define MTK_SIP_DVFSRC_START 0x1
struct dvfsrc_bw_constraints {
u16 max_dram_nom_bw;
u16 max_dram_peak_bw;
u16 max_dram_hrt_bw;
enum mtk_dvfsrc_bw_type {
DVFSRC_BW_AVG,
DVFSRC_BW_PEAK,
DVFSRC_BW_HRT,
DVFSRC_BW_MAX,
};
struct dvfsrc_opp {
u32 vcore_opp;
u32 dram_opp;
u32 emi_opp;
};
struct dvfsrc_opp_desc {
@ -55,6 +78,7 @@ struct dvfsrc_opp_desc {
struct dvfsrc_soc_data;
struct mtk_dvfsrc {
struct device *dev;
struct clk *clk;
struct platform_device *icc;
struct platform_device *regulator;
const struct dvfsrc_soc_data *dvd;
@ -65,11 +89,16 @@ struct mtk_dvfsrc {
struct dvfsrc_soc_data {
const int *regs;
const u8 *bw_units;
const bool has_emi_ddr;
const struct dvfsrc_opp_desc *opps_desc;
u32 (*calc_dram_bw)(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw);
u32 (*get_target_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_current_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_vcore_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_vscp_level)(struct mtk_dvfsrc *dvfsrc);
u32 (*get_opp_count)(struct mtk_dvfsrc *dvfsrc);
int (*get_hw_opps)(struct mtk_dvfsrc *dvfsrc);
void (*set_dram_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
void (*set_dram_peak_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
void (*set_dram_hrt_bw)(struct mtk_dvfsrc *dvfsrc, u64 bw);
@ -78,7 +107,22 @@ struct dvfsrc_soc_data {
void (*set_vscp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
int (*wait_for_opp_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
int (*wait_for_vcore_level)(struct mtk_dvfsrc *dvfsrc, u32 level);
const struct dvfsrc_bw_constraints *bw_constraints;
/**
* @bw_max_constraints - array of maximum bandwidth for this hardware
*
* indexed by &enum mtk_dvfsrc_bw_type, storing the maximum permissible
* hardware value for each bandwidth type.
*/
const u32 *const bw_max_constraints;
/**
* @bw_min_constraints - array of minimum bandwidth for this hardware
*
* indexed by &enum mtk_dvfsrc_bw_type, storing the minimum permissible
* hardware value for each bandwidth type.
*/
const u32 *const bw_min_constraints;
};
static u32 dvfsrc_readl(struct mtk_dvfsrc *dvfs, u32 offset)
@ -92,6 +136,7 @@ static void dvfsrc_writel(struct mtk_dvfsrc *dvfs, u32 offset, u32 val)
}
enum dvfsrc_regs {
DVFSRC_BASIC_CONTROL,
DVFSRC_SW_REQ,
DVFSRC_SW_REQ2,
DVFSRC_LEVEL,
@ -99,7 +144,11 @@ enum dvfsrc_regs {
DVFSRC_SW_BW,
DVFSRC_SW_PEAK_BW,
DVFSRC_SW_HRT_BW,
DVFSRC_SW_EMI_BW,
DVFSRC_VCORE,
DVFSRC_TARGET_GEAR,
DVFSRC_GEAR_INFO_L,
DVFSRC_GEAR_INFO_H,
DVFSRC_REGS_MAX,
};
@ -120,6 +169,22 @@ static const int dvfsrc_mt8195_regs[] = {
[DVFSRC_TARGET_LEVEL] = 0xd48,
};
static const int dvfsrc_mt8196_regs[] = {
[DVFSRC_BASIC_CONTROL] = 0x0,
[DVFSRC_SW_REQ] = 0x18,
[DVFSRC_VCORE] = 0x80,
[DVFSRC_GEAR_INFO_L] = 0xfc,
[DVFSRC_SW_BW] = 0x1e8,
[DVFSRC_SW_PEAK_BW] = 0x1f4,
[DVFSRC_SW_HRT_BW] = 0x20c,
[DVFSRC_LEVEL] = 0x5f0,
[DVFSRC_TARGET_LEVEL] = 0x5f0,
[DVFSRC_SW_REQ2] = 0x604,
[DVFSRC_SW_EMI_BW] = 0x60c,
[DVFSRC_TARGET_GEAR] = 0x6ac,
[DVFSRC_GEAR_INFO_H] = 0x6b0,
};
static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc)
{
u32 level = dvfsrc->dvd->get_current_level(dvfsrc);
@ -127,6 +192,20 @@ static const struct dvfsrc_opp *dvfsrc_get_current_opp(struct mtk_dvfsrc *dvfsrc
return &dvfsrc->curr_opps->opps[level];
}
static u32 dvfsrc_get_current_target_vcore_gear(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR);
return FIELD_GET(DVFSRC_V4_GEAR_TARGET_VCORE, val);
}
static u32 dvfsrc_get_current_target_dram_gear(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_GEAR);
return FIELD_GET(DVFSRC_V4_GEAR_TARGET_DRAM, val);
}
static bool dvfsrc_is_idle(struct mtk_dvfsrc *dvfsrc)
{
if (!dvfsrc->dvd->get_target_level)
@ -183,6 +262,24 @@ static int dvfsrc_wait_for_opp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level)
return 0;
}
static int dvfsrc_wait_for_vcore_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
{
u32 val;
return readx_poll_timeout_atomic(dvfsrc_get_current_target_vcore_gear,
dvfsrc, val, val >= level,
STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US);
}
static int dvfsrc_wait_for_opp_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
{
u32 val;
return readx_poll_timeout_atomic(dvfsrc_get_current_target_dram_gear,
dvfsrc, val, val >= level,
STARTUP_TIME_US, DVFSRC_POLL_TIMEOUT_US);
}
static u32 dvfsrc_get_target_level_v1(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL);
@ -216,6 +313,27 @@ static u32 dvfsrc_get_current_level_v2(struct mtk_dvfsrc *dvfsrc)
return 0;
}
static u32 dvfsrc_get_target_level_v4(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_TARGET_LEVEL);
if (val & DVFSRC_V4_LEVEL_TARGET_PRESENT)
return FIELD_GET(DVFSRC_V4_LEVEL_TARGET_LEVEL, val) + 1;
return 0;
}
static u32 dvfsrc_get_current_level_v4(struct mtk_dvfsrc *dvfsrc)
{
u32 level = dvfsrc_readl(dvfsrc, DVFSRC_LEVEL) + 1;
/* Valid levels */
if (level < dvfsrc->curr_opps->num_opp)
return dvfsrc->curr_opps->num_opp - level;
/* Zero for level 0 or invalid level */
return 0;
}
static u32 dvfsrc_get_vcore_level_v1(struct mtk_dvfsrc *dvfsrc)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ2);
@ -267,39 +385,69 @@ static void dvfsrc_set_vscp_level_v2(struct mtk_dvfsrc *dvfsrc, u32 level)
dvfsrc_writel(dvfsrc, DVFSRC_VCORE, val);
}
static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg,
u16 max_bw, u16 min_bw, u64 bw)
static u32 dvfsrc_get_opp_count_v4(struct mtk_dvfsrc *dvfsrc)
{
u32 new_bw = (u32)div_u64(bw, 100 * 1000);
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_BASIC_CONTROL);
/* If bw constraints (in mbps) are defined make sure to respect them */
if (max_bw)
new_bw = min(new_bw, max_bw);
if (min_bw && new_bw > 0)
new_bw = max(new_bw, min_bw);
return FIELD_GET(DVFSRC_V4_BASIC_CTRL_OPP_COUNT, val) + 1;
}
dvfsrc_writel(dvfsrc, reg, new_bw);
static u32
dvfsrc_calc_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw)
{
return clamp_val(div_u64(bw, 100 * 1000), dvfsrc->dvd->bw_min_constraints[type],
dvfsrc->dvd->bw_max_constraints[type]);
}
/**
* dvfsrc_calc_dram_bw_v4 - convert kbps to hardware register bandwidth value
* @dvfsrc: pointer to the &struct mtk_dvfsrc of this driver instance
* @type: one of %DVFSRC_BW_AVG, %DVFSRC_BW_PEAK, or %DVFSRC_BW_HRT
* @bw: the bandwidth in kilobits per second
*
* Returns the hardware register value appropriate for expressing @bw, clamped
* to hardware limits.
*/
static u32
dvfsrc_calc_dram_bw_v4(struct mtk_dvfsrc *dvfsrc, enum mtk_dvfsrc_bw_type type, u64 bw)
{
u8 bw_unit = dvfsrc->dvd->bw_units[type];
u64 bw_mbps;
u32 bw_hw;
if (type < DVFSRC_BW_AVG || type >= DVFSRC_BW_MAX)
return 0;
bw_mbps = div_u64(bw, 1000);
bw_hw = div_u64((bw_mbps + bw_unit - 1), bw_unit);
return clamp_val(bw_hw, dvfsrc->dvd->bw_min_constraints[type],
dvfsrc->dvd->bw_max_constraints[type]);
}
static void __dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u32 reg,
enum mtk_dvfsrc_bw_type type, u64 bw)
{
u32 bw_hw = dvfsrc->dvd->calc_dram_bw(dvfsrc, type, bw);
dvfsrc_writel(dvfsrc, reg, bw_hw);
if (type == DVFSRC_BW_AVG && dvfsrc->dvd->has_emi_ddr)
dvfsrc_writel(dvfsrc, DVFSRC_SW_EMI_BW, bw_hw);
}
static void dvfsrc_set_dram_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_nom_bw;
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, max_bw, 0, bw);
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_BW, DVFSRC_BW_AVG, bw);
};
static void dvfsrc_set_dram_peak_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_peak_bw;
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, max_bw, 0, bw);
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_PEAK_BW, DVFSRC_BW_PEAK, bw);
}
static void dvfsrc_set_dram_hrt_bw_v1(struct mtk_dvfsrc *dvfsrc, u64 bw)
{
u64 max_bw = dvfsrc->dvd->bw_constraints->max_dram_hrt_bw;
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, max_bw, 0, bw);
__dvfsrc_set_dram_bw_v1(dvfsrc, DVFSRC_SW_HRT_BW, DVFSRC_BW_HRT, bw);
}
static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level)
@ -315,6 +463,100 @@ static void dvfsrc_set_opp_level_v1(struct mtk_dvfsrc *dvfsrc, u32 level)
dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val);
}
static u32 dvfsrc_get_opp_gear(struct mtk_dvfsrc *dvfsrc, u8 level)
{
u32 reg_ofst, val;
u8 idx;
/* Calculate register offset and index for requested gear */
if (level < DVFSRC_V4_GEAR_INFO_REG_LEVELS) {
reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_L];
idx = level;
} else {
reg_ofst = dvfsrc->dvd->regs[DVFSRC_GEAR_INFO_H];
idx = level - DVFSRC_V4_GEAR_INFO_REG_LEVELS;
}
reg_ofst += DVFSRC_V4_GEAR_INFO_REG_WIDTH * (level / 2);
/* Read the corresponding gear register */
val = readl(dvfsrc->regs + reg_ofst);
/* Each register contains two sets of data, 16 bits per gear */
val >>= 16 * (idx % 2);
return val;
}
static int dvfsrc_get_hw_opps_v4(struct mtk_dvfsrc *dvfsrc)
{
struct dvfsrc_opp *dvfsrc_opps;
struct dvfsrc_opp_desc *desc;
u32 num_opps, gear_info;
u8 num_vcore, num_dram;
u8 num_emi;
int i;
num_opps = dvfsrc_get_opp_count_v4(dvfsrc);
if (num_opps == 0) {
dev_err(dvfsrc->dev, "No OPPs programmed in DVFSRC MCU.\n");
return -EINVAL;
}
/*
* The first 16 bits set in the gear info table says how many OPPs
* and how many vcore, dram and emi table entries are available.
*/
gear_info = dvfsrc_readl(dvfsrc, DVFSRC_GEAR_INFO_L);
if (gear_info == 0) {
dev_err(dvfsrc->dev, "No gear info in DVFSRC MCU.\n");
return -EINVAL;
}
num_vcore = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info) + 1;
num_dram = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info) + 1;
num_emi = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info) + 1;
dev_info(dvfsrc->dev,
"Discovered %u gears and %u vcore, %u dram, %u emi table entries.\n",
num_opps, num_vcore, num_dram, num_emi);
/* Allocate everything now as anything else after that cannot fail */
desc = devm_kzalloc(dvfsrc->dev, sizeof(*desc), GFP_KERNEL);
if (!desc)
return -ENOMEM;
dvfsrc_opps = devm_kcalloc(dvfsrc->dev, num_opps + 1,
sizeof(*dvfsrc_opps), GFP_KERNEL);
if (!dvfsrc_opps)
return -ENOMEM;
/* Read the OPP table gear indices */
for (i = 0; i <= num_opps; i++) {
gear_info = dvfsrc_get_opp_gear(dvfsrc, num_opps - i);
dvfsrc_opps[i].vcore_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_VCORE, gear_info);
dvfsrc_opps[i].dram_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_DRAM, gear_info);
dvfsrc_opps[i].emi_opp = FIELD_GET(DVFSRC_V4_GEAR_INFO_EMI, gear_info);
};
desc->num_opp = num_opps + 1;
desc->opps = dvfsrc_opps;
/* Assign to main structure now that everything is done! */
dvfsrc->curr_opps = desc;
return 0;
}
static void dvfsrc_set_dram_level_v4(struct mtk_dvfsrc *dvfsrc, u32 level)
{
u32 val = dvfsrc_readl(dvfsrc, DVFSRC_SW_REQ);
val &= ~DVFSRC_V4_SW_REQ_DRAM_LEVEL;
val |= FIELD_PREP(DVFSRC_V4_SW_REQ_DRAM_LEVEL, level);
dev_dbg(dvfsrc->dev, "%s level=%u\n", __func__, level);
dvfsrc_writel(dvfsrc, DVFSRC_SW_REQ, val);
}
int mtk_dvfsrc_send_request(const struct device *dev, u32 cmd, u64 data)
{
struct mtk_dvfsrc *dvfsrc = dev_get_drvdata(dev);
@ -422,6 +664,11 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
if (IS_ERR(dvfsrc->regs))
return PTR_ERR(dvfsrc->regs);
dvfsrc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(dvfsrc->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(dvfsrc->clk),
"Couldn't get and enable DVFSRC clock\n");
arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_INIT,
0, 0, 0, 0, 0, 0, &ares);
if (ares.a0)
@ -430,7 +677,14 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
dvfsrc->dram_type = ares.a1;
dev_dbg(&pdev->dev, "DRAM Type: %d\n", dvfsrc->dram_type);
dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
/* Newer versions of the DVFSRC MCU have pre-programmed gear tables */
if (dvfsrc->dvd->get_hw_opps) {
ret = dvfsrc->dvd->get_hw_opps(dvfsrc);
if (ret)
return ret;
} else {
dvfsrc->curr_opps = &dvfsrc->dvd->opps_desc[dvfsrc->dram_type];
}
platform_set_drvdata(pdev, dvfsrc);
ret = devm_of_platform_populate(&pdev->dev);
@ -440,17 +694,28 @@ static int mtk_dvfsrc_probe(struct platform_device *pdev)
/* Everything is set up - make it run! */
arm_smccc_smc(MTK_SIP_DVFSRC_VCOREFS_CONTROL, MTK_SIP_DVFSRC_START,
0, 0, 0, 0, 0, 0, &ares);
if (ares.a0)
if (ares.a0 & BIT(0))
return dev_err_probe(&pdev->dev, -EINVAL, "Cannot start DVFSRC: %lu\n", ares.a0);
return 0;
}
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v1 = { 0, 0, 0 };
static const struct dvfsrc_bw_constraints dvfsrc_bw_constr_v2 = {
.max_dram_nom_bw = 255,
.max_dram_peak_bw = 255,
.max_dram_hrt_bw = 1023,
static const u32 dvfsrc_bw_min_constr_none[DVFSRC_BW_MAX] = {
[DVFSRC_BW_AVG] = 0,
[DVFSRC_BW_PEAK] = 0,
[DVFSRC_BW_HRT] = 0,
};
static const u32 dvfsrc_bw_max_constr_v1[DVFSRC_BW_MAX] = {
[DVFSRC_BW_AVG] = U32_MAX,
[DVFSRC_BW_PEAK] = U32_MAX,
[DVFSRC_BW_HRT] = U32_MAX,
};
static const u32 dvfsrc_bw_max_constr_v2[DVFSRC_BW_MAX] = {
[DVFSRC_BW_AVG] = 65535,
[DVFSRC_BW_PEAK] = 65535,
[DVFSRC_BW_HRT] = 1023,
};
static const struct dvfsrc_opp dvfsrc_opp_mt6893_lp4[] = {
@ -483,7 +748,8 @@ static const struct dvfsrc_soc_data mt6893_data = {
.set_vscp_level = dvfsrc_set_vscp_level_v2,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
.bw_constraints = &dvfsrc_bw_constr_v2,
.bw_max_constraints = dvfsrc_bw_max_constr_v2,
.bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const struct dvfsrc_opp dvfsrc_opp_mt8183_lp4[] = {
@ -512,6 +778,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8183_desc[] = {
static const struct dvfsrc_soc_data mt8183_data = {
.opps_desc = dvfsrc_opp_mt8183_desc,
.regs = dvfsrc_mt8183_regs,
.calc_dram_bw = dvfsrc_calc_dram_bw_v1,
.get_target_level = dvfsrc_get_target_level_v1,
.get_current_level = dvfsrc_get_current_level_v1,
.get_vcore_level = dvfsrc_get_vcore_level_v1,
@ -520,7 +787,8 @@ static const struct dvfsrc_soc_data mt8183_data = {
.set_vcore_level = dvfsrc_set_vcore_level_v1,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v1,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
.bw_constraints = &dvfsrc_bw_constr_v1,
.bw_max_constraints = dvfsrc_bw_max_constr_v1,
.bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const struct dvfsrc_opp dvfsrc_opp_mt8195_lp4[] = {
@ -542,6 +810,7 @@ static const struct dvfsrc_opp_desc dvfsrc_opp_mt8195_desc[] = {
static const struct dvfsrc_soc_data mt8195_data = {
.opps_desc = dvfsrc_opp_mt8195_desc,
.regs = dvfsrc_mt8195_regs,
.calc_dram_bw = dvfsrc_calc_dram_bw_v1,
.get_target_level = dvfsrc_get_target_level_v2,
.get_current_level = dvfsrc_get_current_level_v2,
.get_vcore_level = dvfsrc_get_vcore_level_v2,
@ -553,13 +822,44 @@ static const struct dvfsrc_soc_data mt8195_data = {
.set_vscp_level = dvfsrc_set_vscp_level_v2,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v2,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v1,
.bw_constraints = &dvfsrc_bw_constr_v2,
.bw_max_constraints = dvfsrc_bw_max_constr_v2,
.bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const u8 mt8196_bw_units[] = {
[DVFSRC_BW_AVG] = 64,
[DVFSRC_BW_PEAK] = 64,
[DVFSRC_BW_HRT] = 30,
};
static const struct dvfsrc_soc_data mt8196_data = {
.regs = dvfsrc_mt8196_regs,
.bw_units = mt8196_bw_units,
.has_emi_ddr = true,
.get_target_level = dvfsrc_get_target_level_v4,
.get_current_level = dvfsrc_get_current_level_v4,
.get_vcore_level = dvfsrc_get_vcore_level_v2,
.get_vscp_level = dvfsrc_get_vscp_level_v2,
.get_opp_count = dvfsrc_get_opp_count_v4,
.get_hw_opps = dvfsrc_get_hw_opps_v4,
.calc_dram_bw = dvfsrc_calc_dram_bw_v4,
.set_dram_bw = dvfsrc_set_dram_bw_v1,
.set_dram_peak_bw = dvfsrc_set_dram_peak_bw_v1,
.set_dram_hrt_bw = dvfsrc_set_dram_hrt_bw_v1,
.set_opp_level = dvfsrc_set_dram_level_v4,
.set_vcore_level = dvfsrc_set_vcore_level_v2,
.set_vscp_level = dvfsrc_set_vscp_level_v2,
.wait_for_opp_level = dvfsrc_wait_for_opp_level_v4,
.wait_for_vcore_level = dvfsrc_wait_for_vcore_level_v4,
.bw_max_constraints = dvfsrc_bw_max_constr_v2,
.bw_min_constraints = dvfsrc_bw_min_constr_none,
};
static const struct of_device_id mtk_dvfsrc_of_match[] = {
{ .compatible = "mediatek,mt6893-dvfsrc", .data = &mt6893_data },
{ .compatible = "mediatek,mt8183-dvfsrc", .data = &mt8183_data },
{ .compatible = "mediatek,mt8195-dvfsrc", .data = &mt8195_data },
{ .compatible = "mediatek,mt8196-dvfsrc", .data = &mt8196_data },
{ /* sentinel */ }
};

View File

@ -59,6 +59,7 @@ static struct socinfo_data socinfo_data_table[] = {
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
MTK_SOCINFO_ENTRY("MT8370", "MT8370AV/AZA", "Genio 510", 0x83700000, 0x00000081),
MTK_SOCINFO_ENTRY("MT8371", "MT8371AV/AZA", "Genio 520", 0x83710000, 0x00000081),
MTK_SOCINFO_ENTRY("MT8390", "MT8390AV/AZA", "Genio 700", 0x83900000, 0x00000080),
MTK_SOCINFO_ENTRY("MT8391", "MT8391AV/AZA", "Genio 720", 0x83910000, 0x00000080),
MTK_SOCINFO_ENTRY("MT8395", "MT8395AV/ZA", "Genio 1200", 0x83950100, CELL_NOT_USED),

View File

@ -9,6 +9,7 @@
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/cleanup.h>
#include <linux/cpu.h>
#include <linux/cpuidle.h>
#include <linux/debugfs.h>
@ -789,7 +790,7 @@ static ssize_t svs_enable_debug_write(struct file *filp,
struct svs_bank *svsb = file_inode(filp)->i_private;
struct svs_platform *svsp = dev_get_drvdata(svsb->dev);
int enabled, ret;
char *buf = NULL;
char *buf __free(kfree) = NULL;
if (count >= PAGE_SIZE)
return -EINVAL;
@ -807,8 +808,6 @@ static ssize_t svs_enable_debug_write(struct file *filp,
svsb->mode_support = SVSB_MODE_ALL_DISABLE;
}
kfree(buf);
return count;
}

View File

@ -70,13 +70,32 @@ struct cmdq_cb_data {
struct cmdq_pkt *pkt;
};
struct cmdq_mbox_priv {
u8 shift_pa;
dma_addr_t mminfra_offset;
};
struct cmdq_pkt {
void *va_base;
dma_addr_t pa_base;
size_t cmd_buf_size; /* command occupied size */
size_t buf_size; /* real buffer size */
struct cmdq_mbox_priv priv; /* for generating instruction */
};
/**
* cmdq_get_mbox_priv() - get the private data of mailbox channel
* @chan: mailbox channel
* @priv: pointer to store the private data of mailbox channel
*
* While generating the GCE instruction to command buffer, the private data
* of GCE hardware may need to be referenced, such as the shift bits of
* physical address.
*
* This function should be called before generating the GCE instruction.
*/
void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv);
/**
* cmdq_get_shift_pa() - get the shift bits of physical address
* @chan: mailbox channel

View File

@ -23,6 +23,8 @@
#define CMDQ_THR_SPR_IDX2 (2)
#define CMDQ_THR_SPR_IDX3 (3)
#define CMDQ_SUBSYS_INVALID (U8_MAX)
struct cmdq_pkt;
enum cmdq_logic_op {
@ -52,8 +54,20 @@ struct cmdq_operand {
struct cmdq_client_reg {
u8 subsys;
phys_addr_t pa_base;
u16 offset;
u16 size;
/*
* Client only uses these functions for MMIO access,
* so doesn't need to handle the mminfra_offset.
* The mminfra_offset is used for DRAM access and
* is handled internally by CMDQ APIs.
*/
int (*pkt_write)(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base,
u16 offset, u32 value);
int (*pkt_write_mask)(struct cmdq_pkt *pkt, u8 subsys, u32 pa_base,
u16 offset, u32 value, u32 mask);
};
struct cmdq_client {
@ -121,6 +135,32 @@ void cmdq_pkt_destroy(struct cmdq_client *client, struct cmdq_pkt *pkt);
*/
int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value);
/**
* cmdq_pkt_write_pa() - append write command to the CMDQ packet with pa_base
* @pkt: the CMDQ packet
* @subsys: unused parameter
* @pa_base: the physical address base of the hardware register
* @offset: register offset from CMDQ sub system
* @value: the specified target register value
*
* Return: 0 for success; else the error code is returned
*/
int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/,
u32 pa_base, u16 offset, u32 value);
/**
* cmdq_pkt_write_subsys() - append write command to the CMDQ packet with subsys
* @pkt: the CMDQ packet
* @subsys: the CMDQ sub system code
* @pa_base: unused parameter
* @offset: register offset from CMDQ sub system
* @value: the specified target register value
*
* Return: 0 for success; else the error code is returned
*/
int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys,
u32 pa_base /*unused*/, u16 offset, u32 value);
/**
* cmdq_pkt_write_mask() - append write command with mask to the CMDQ packet
* @pkt: the CMDQ packet
@ -134,6 +174,34 @@ int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u32 value);
int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
u16 offset, u32 value, u32 mask);
/**
* cmdq_pkt_write_mask_pa() - append write command with mask to the CMDQ packet with pa
* @pkt: the CMDQ packet
* @subsys: unused parameter
* @pa_base: the physical address base of the hardware register
* @offset: register offset from CMDQ sub system
* @value: the specified target register value
* @mask: the specified target register mask
*
* Return: 0 for success; else the error code is returned
*/
int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/,
u32 pa_base, u16 offset, u32 value, u32 mask);
/**
* cmdq_pkt_write_mask_subsys() - append write command with mask to the CMDQ packet with subsys
* @pkt: the CMDQ packet
* @subsys: the CMDQ sub system code
* @pa_base: unused parameter
* @offset: register offset from CMDQ sub system
* @value: the specified target register value
* @mask: the specified target register mask
*
* Return: 0 for success; else the error code is returned
*/
int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys,
u32 pa_base /*unused*/, u16 offset, u32 value, u32 mask);
/*
* cmdq_pkt_read_s() - append read_s command to the CMDQ packet
* @pkt: the CMDQ packet
@ -418,12 +486,37 @@ static inline int cmdq_pkt_write(struct cmdq_pkt *pkt, u8 subsys, u16 offset, u3
return -ENOENT;
}
static inline int cmdq_pkt_write_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/,
u32 pa_base, u16 offset, u32 value)
{
return -ENOENT;
}
static inline int cmdq_pkt_write_subsys(struct cmdq_pkt *pkt, u8 subsys,
u32 pa_base /*unused*/, u16 offset, u32 value)
{
return -ENOENT;
}
static inline int cmdq_pkt_write_mask(struct cmdq_pkt *pkt, u8 subsys,
u16 offset, u32 value, u32 mask)
{
return -ENOENT;
}
static inline int cmdq_pkt_write_mask_pa(struct cmdq_pkt *pkt, u8 subsys /*unused*/,
u32 pa_base, u16 offset, u32 value, u32 mask)
{
return -ENOENT;
}
static inline int cmdq_pkt_write_mask_subsys(struct cmdq_pkt *pkt, u8 subsys,
u32 pa_base /*unused*/, u16 offset,
u32 value, u32 mask)
{
return -ENOENT;
}
static inline int cmdq_pkt_read_s(struct cmdq_pkt *pkt, u16 high_addr_reg_idx,
u16 addr_low, u16 reg_idx)
{