Commit ff6c2269 authored by Arnd Bergmann's avatar Arnd Bergmann

Merge tag 'v5.19-next-soc' of...

Merge tag 'v5.19-next-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/matthias.bgg/linux into arm/drivers

pmic wrapper:
- code style improvements

devapc:
- add support for MT8186

Smart Voltage Scaling (SVS)
- add support for MT8183 and MT8192

MMSYS:
- Add more display paths for MT8365

Mutex:
- Add common interface for MOD and SOF table
- Add support for MDP on MT8183
- Move binding to soc folder
- Add support to use CMDQ to enable the mutex, needed by MDP3

Power domains:
- Add support for MT6795

* tag 'v5.19-next-soc' of git://git.kernel.org/pub/scm/linux/kernel/git/matthias.bgg/linux: (29 commits)
  soc: mediatek: mutex: Simplify with devm_platform_get_and_ioremap_resource()
  soc: mediatek: pm-domains: Add support for Helio X10 MT6795
  dt-bindings: power: Add MediaTek Helio X10 MT6795 power domains
  soc: mediatek: SVS: Use DEFINE_SIMPLE_DEV_PM_OPS for svs_pm_ops
  soc: mediatek: mtk-pm-domains: Allow probing vreg supply on two MFGs
  soc: mediatek: fix missing clk_disable_unprepare() on err in svs_resume()
  soc: mediatek: mutex: Use DDP_COMPONENT_DITHER0 mod index for MT8365
  soc: mediatek: mutex: add functions that operate registers by CMDQ
  dt-bindings: soc: mediatek: add gce-client-reg for MUTEX
  dt-bindings: soc: mediatek: move out common module from display folder
  soc: mediatek: mutex: add 8183 MUTEX MOD settings for MDP
  soc: mediatek: mutex: add common interface for modules setting
  soc: mediatek: pm-domains: Add support always on flag
  soc: mediatek: mt8365-mmsys: add DPI/HDMI display path
  soc: mediatek: mutex: add MT8365 support
  soc: mediatek: SVS: add mt8192 SVS GPU driver
  dt-bindings: soc: mediatek: add mt8192 svs dt-bindings
  soc: mediatek: SVS: add debug commands
  soc: mediatek: SVS: add monitor mode
  soc: mediatek: SVS: introduce MTK SVS engine
  ...

Link: https://lore.kernel.org/r/b733bd82-6d99-23ef-0541-98e98eb8d3bc@gmail.comSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 611f6810 4d3ddc9b
......@@ -23,6 +23,7 @@ properties:
compatible:
enum:
- mediatek,mt6795-power-controller
- mediatek,mt8167-power-controller
- mediatek,mt8173-power-controller
- mediatek,mt8183-power-controller
......@@ -62,6 +63,7 @@ patternProperties:
reg:
description: |
Power domain index. Valid values are defined in:
"include/dt-bindings/power/mt6795-power.h" - for MT8167 type power domain.
"include/dt-bindings/power/mt8167-power.h" - for MT8167 type power domain.
"include/dt-bindings/power/mt8173-power.h" - for MT8173 type power domain.
"include/dt-bindings/power/mt8183-power.h" - for MT8183 type power domain.
......
......@@ -20,6 +20,7 @@ properties:
compatible:
enum:
- mediatek,mt6779-devapc
- mediatek,mt8186-devapc
reg:
description: The base address of devapc register bank
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/display/mediatek/mediatek,mutex.yaml#
$id: http://devicetree.org/schemas/soc/mediatek/mediatek,mutex.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Mediatek mutex
......@@ -55,6 +55,18 @@ properties:
include/dt-bindings/gce/<chip>-gce.h of each chips.
$ref: /schemas/types.yaml#/definitions/uint32-array
mediatek,gce-client-reg:
$ref: /schemas/types.yaml#/definitions/phandle-array
items:
items:
- description: phandle of GCE
- description: GCE subsys id
- description: register offset
- description: register size
description: The register of client driver can be configured by gce with
4 arguments defined in this property. Each GCE subsys id is mapping to
a client defined in the header include/dt-bindings/gce/<chip>-gce.h.
required:
- compatible
- reg
......
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/soc/mediatek/mtk-svs.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: MediaTek Smart Voltage Scaling (SVS) Device Tree Bindings
maintainers:
- Roger Lu <roger.lu@mediatek.com>
- Matthias Brugger <matthias.bgg@gmail.com>
- Kevin Hilman <khilman@kernel.org>
description: |+
The SVS engine is a piece of hardware which has several
controllers(banks) for calculating suitable voltage to
different power domains(CPU/GPU/CCI) according to
chip process corner, temperatures and other factors. Then DVFS
driver could apply SVS bank voltage to PMIC/Buck.
properties:
compatible:
enum:
- mediatek,mt8183-svs
- mediatek,mt8192-svs
reg:
maxItems: 1
description: Address range of the MTK SVS controller.
interrupts:
maxItems: 1
clocks:
maxItems: 1
description: Main clock for MTK SVS controller to work.
clock-names:
const: main
nvmem-cells:
minItems: 1
description:
Phandle to the calibration data provided by a nvmem device.
items:
- description: SVS efuse for SVS controller
- description: Thermal efuse for SVS controller
nvmem-cell-names:
items:
- const: svs-calibration-data
- const: t-calibration-data
resets:
maxItems: 1
reset-names:
items:
- const: svs_rst
required:
- compatible
- reg
- interrupts
- clocks
- clock-names
- nvmem-cells
- nvmem-cell-names
additionalProperties: false
examples:
- |
#include <dt-bindings/clock/mt8183-clk.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
#include <dt-bindings/interrupt-controller/irq.h>
soc {
#address-cells = <2>;
#size-cells = <2>;
svs@1100b000 {
compatible = "mediatek,mt8183-svs";
reg = <0 0x1100b000 0 0x1000>;
interrupts = <GIC_SPI 127 IRQ_TYPE_LEVEL_LOW>;
clocks = <&infracfg CLK_INFRA_THERM>;
clock-names = "main";
nvmem-cells = <&svs_calibration>, <&thermal_calibration>;
nvmem-cell-names = "svs-calibration-data", "t-calibration-data";
};
};
......@@ -73,4 +73,14 @@ config MTK_MMSYS
Say yes here to add support for the MediaTek Multimedia
Subsystem (MMSYS).
config MTK_SVS
tristate "MediaTek Smart Voltage Scaling(SVS)"
depends on MTK_EFUSE && NVMEM
help
The Smart Voltage Scaling(SVS) engine is a piece of hardware
which has several controllers(banks) for calculating suitable
voltage to different power domains(CPU/GPU/CCI) according to
chip process corner, temperatures and other factors. Then DVFS
driver could apply SVS bank voltage to PMIC/Buck.
endmenu
......@@ -7,3 +7,4 @@ obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o
obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
obj-$(CONFIG_MTK_SVS) += mtk-svs.o
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __SOC_MEDIATEK_MT6795_PM_DOMAINS_H
#define __SOC_MEDIATEK_MT6795_PM_DOMAINS_H
#include "mtk-pm-domains.h"
#include <dt-bindings/power/mt6795-power.h>
/*
* MT6795 power domain support
*/
static const struct scpsys_domain_data scpsys_domain_data_mt6795[] = {
[MT6795_POWER_DOMAIN_VDEC] = {
.name = "vdec",
.sta_mask = PWR_STATUS_VDEC,
.ctl_offs = SPM_VDE_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
},
[MT6795_POWER_DOMAIN_VENC] = {
.name = "venc",
.sta_mask = PWR_STATUS_VENC,
.ctl_offs = SPM_VEN_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
},
[MT6795_POWER_DOMAIN_ISP] = {
.name = "isp",
.sta_mask = PWR_STATUS_ISP,
.ctl_offs = SPM_ISP_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(13, 12),
},
[MT6795_POWER_DOMAIN_MM] = {
.name = "mm",
.sta_mask = PWR_STATUS_DISP,
.ctl_offs = SPM_DIS_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
.bp_infracfg = {
BUS_PROT_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MM_M0 |
MT8173_TOP_AXI_PROT_EN_MM_M1),
},
},
[MT6795_POWER_DOMAIN_MJC] = {
.name = "mjc",
.sta_mask = BIT(20),
.ctl_offs = 0x298,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
},
[MT6795_POWER_DOMAIN_AUDIO] = {
.name = "audio",
.sta_mask = PWR_STATUS_AUDIO,
.ctl_offs = SPM_AUDIO_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(15, 12),
},
[MT6795_POWER_DOMAIN_MFG_ASYNC] = {
.name = "mfg_async",
.sta_mask = PWR_STATUS_MFG_ASYNC,
.ctl_offs = SPM_MFG_ASYNC_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = 0,
},
[MT6795_POWER_DOMAIN_MFG_2D] = {
.name = "mfg_2d",
.sta_mask = PWR_STATUS_MFG_2D,
.ctl_offs = SPM_MFG_2D_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(11, 8),
.sram_pdn_ack_bits = GENMASK(13, 12),
},
[MT6795_POWER_DOMAIN_MFG] = {
.name = "mfg",
.sta_mask = PWR_STATUS_MFG,
.ctl_offs = SPM_MFG_PWR_CON,
.pwr_sta_offs = SPM_PWR_STATUS,
.pwr_sta2nd_offs = SPM_PWR_STATUS_2ND,
.sram_pdn_bits = GENMASK(13, 8),
.sram_pdn_ack_bits = GENMASK(21, 16),
.bp_infracfg = {
BUS_PROT_UPDATE_TOPAXI(MT8173_TOP_AXI_PROT_EN_MFG_S |
MT8173_TOP_AXI_PROT_EN_MFG_M0 |
MT8173_TOP_AXI_PROT_EN_MFG_M1 |
MT8173_TOP_AXI_PROT_EN_MFG_SNOOP_OUT),
},
},
};
static const struct scpsys_soc_data mt6795_scpsys_data = {
.domains_data = scpsys_domain_data_mt6795,
.num_domains = ARRAY_SIZE(scpsys_domain_data_mt6795),
};
#endif /* __SOC_MEDIATEK_MT6795_PM_DOMAINS_H */
......@@ -41,6 +41,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = {
.pwr_sta2nd_offs = 0x0184,
.sram_pdn_bits = 0,
.sram_pdn_ack_bits = 0,
.caps = MTK_SCPD_DOMAIN_SUPPLY,
},
[MT8183_POWER_DOMAIN_MFG] = {
.name = "mfg",
......
......@@ -51,7 +51,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8186[] = {
MT8186_TOP_AXI_PROT_EN_1_CLR,
MT8186_TOP_AXI_PROT_EN_1_STA),
},
.caps = MTK_SCPD_KEEP_DEFAULT_OFF,
.caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY,
},
[MT8186_POWER_DOMAIN_MFG2] = {
.name = "mfg2",
......
......@@ -58,6 +58,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = {
.pwr_sta2nd_offs = 0x0170,
.sram_pdn_bits = GENMASK(8, 8),
.sram_pdn_ack_bits = GENMASK(12, 12),
.caps = MTK_SCPD_DOMAIN_SUPPLY,
},
[MT8192_POWER_DOMAIN_MFG1] = {
.name = "mfg1",
......@@ -85,6 +86,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = {
MT8192_TOP_AXI_PROT_EN_2_CLR,
MT8192_TOP_AXI_PROT_EN_2_STA1),
},
.caps = MTK_SCPD_DOMAIN_SUPPLY,
},
[MT8192_POWER_DOMAIN_MFG2] = {
.name = "mfg2",
......
......@@ -67,7 +67,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = {
.ctl_offs = 0x334,
.pwr_sta_offs = 0x174,
.pwr_sta2nd_offs = 0x178,
.caps = MTK_SCPD_ACTIVE_WAKEUP,
.caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_ALWAYS_ON,
},
[MT8195_POWER_DOMAIN_CSI_RX_TOP] = {
.name = "csi_rx_top",
......@@ -162,7 +162,7 @@ static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = {
MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR,
MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1),
},
.caps = MTK_SCPD_KEEP_DEFAULT_OFF,
.caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY,
},
[MT8195_POWER_DOMAIN_MFG2] = {
.name = "mfg2",
......
......@@ -10,6 +10,9 @@
#define MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN 0xf60
#define MT8365_DISP_REG_CONFIG_DISP_COLOR0_SEL_IN 0xf64
#define MT8365_DISP_REG_CONFIG_DISP_DSI0_SEL_IN 0xf68
#define MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL 0xfd0
#define MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN 0xfd8
#define MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00 0xfdc
#define MT8365_RDMA0_SOUT_COLOR0 0x1
#define MT8365_DITHER_MOUT_EN_DSI0 0x1
......@@ -18,6 +21,10 @@
#define MT8365_RDMA0_RSZ0_SEL_IN_RDMA0 0x0
#define MT8365_DISP_COLOR_SEL_IN_COLOR0 0x0
#define MT8365_OVL0_MOUT_PATH0_SEL BIT(0)
#define MT8365_RDMA1_SOUT_DPI0 0x1
#define MT8365_DPI0_SEL_IN_RDMA1 0x0
#define MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK 0x1
#define MT8365_DPI0_SEL_IN_RDMA1 0x0
static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = {
{
......@@ -55,6 +62,21 @@ static const struct mtk_mmsys_routes mt8365_mmsys_routing_table[] = {
MT8365_DISP_REG_CONFIG_DISP_RDMA0_RSZ0_SEL_IN,
MT8365_RDMA0_RSZ0_SEL_IN_RDMA0, MT8365_RDMA0_RSZ0_SEL_IN_RDMA0
},
{
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
MT8365_DISP_REG_CONFIG_DISP_LVDS_SYS_CFG_00,
MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK, MT8365_LVDS_SYS_CFG_00_SEL_LVDS_PXL_CLK
},
{
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
MT8365_DISP_REG_CONFIG_DISP_DPI0_SEL_IN,
MT8365_DPI0_SEL_IN_RDMA1, MT8365_DPI0_SEL_IN_RDMA1
},
{
DDP_COMPONENT_RDMA1, DDP_COMPONENT_DPI0,
MT8365_DISP_REG_CONFIG_DISP_RDMA1_SOUT_SEL,
MT8365_RDMA1_SOUT_DPI0, MT8365_RDMA1_SOUT_DPI0
},
};
#endif /* __SOC_MEDIATEK_MT8365_MMSYS_H */
......@@ -31,10 +31,7 @@ struct mtk_devapc_vio_dbgs {
u32 vio_dbg1;
};
struct mtk_devapc_data {
/* numbers of violation index */
u32 vio_idx_num;
struct mtk_devapc_regs_ofs {
/* reg offset */
u32 vio_mask_offset;
u32 vio_sta_offset;
......@@ -46,6 +43,12 @@ struct mtk_devapc_data {
u32 vio_shift_con_offset;
};
struct mtk_devapc_data {
/* numbers of violation index */
u32 vio_idx_num;
const struct mtk_devapc_regs_ofs *regs_ofs;
};
struct mtk_devapc_context {
struct device *dev;
void __iomem *infra_base;
......@@ -58,7 +61,7 @@ static void clear_vio_status(struct mtk_devapc_context *ctx)
void __iomem *reg;
int i;
reg = ctx->infra_base + ctx->data->vio_sta_offset;
reg = ctx->infra_base + ctx->data->regs_ofs->vio_sta_offset;
for (i = 0; i < VIO_MOD_TO_REG_IND(ctx->data->vio_idx_num) - 1; i++)
writel(GENMASK(31, 0), reg + 4 * i);
......@@ -73,7 +76,7 @@ static void mask_module_irq(struct mtk_devapc_context *ctx, bool mask)
u32 val;
int i;
reg = ctx->infra_base + ctx->data->vio_mask_offset;
reg = ctx->infra_base + ctx->data->regs_ofs->vio_mask_offset;
if (mask)
val = GENMASK(31, 0);
......@@ -116,11 +119,11 @@ static int devapc_sync_vio_dbg(struct mtk_devapc_context *ctx)
u32 val;
pd_vio_shift_sta_reg = ctx->infra_base +
ctx->data->vio_shift_sta_offset;
ctx->data->regs_ofs->vio_shift_sta_offset;
pd_vio_shift_sel_reg = ctx->infra_base +
ctx->data->vio_shift_sel_offset;
ctx->data->regs_ofs->vio_shift_sel_offset;
pd_vio_shift_con_reg = ctx->infra_base +
ctx->data->vio_shift_con_offset;
ctx->data->regs_ofs->vio_shift_con_offset;
/* Find the minimum shift group which has violation */
val = readl(pd_vio_shift_sta_reg);
......@@ -161,8 +164,8 @@ static void devapc_extract_vio_dbg(struct mtk_devapc_context *ctx)
void __iomem *vio_dbg0_reg;
void __iomem *vio_dbg1_reg;
vio_dbg0_reg = ctx->infra_base + ctx->data->vio_dbg0_offset;
vio_dbg1_reg = ctx->infra_base + ctx->data->vio_dbg1_offset;
vio_dbg0_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg0_offset;
vio_dbg1_reg = ctx->infra_base + ctx->data->regs_ofs->vio_dbg1_offset;
vio_dbgs.vio_dbg0 = readl(vio_dbg0_reg);
vio_dbgs.vio_dbg1 = readl(vio_dbg1_reg);
......@@ -200,7 +203,7 @@ static irqreturn_t devapc_violation_irq(int irq_number, void *data)
*/
static void start_devapc(struct mtk_devapc_context *ctx)
{
writel(BIT(31), ctx->infra_base + ctx->data->apc_con_offset);
writel(BIT(31), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
mask_module_irq(ctx, false);
}
......@@ -212,11 +215,10 @@ static void stop_devapc(struct mtk_devapc_context *ctx)
{
mask_module_irq(ctx, true);
writel(BIT(2), ctx->infra_base + ctx->data->apc_con_offset);
writel(BIT(2), ctx->infra_base + ctx->data->regs_ofs->apc_con_offset);
}
static const struct mtk_devapc_data devapc_mt6779 = {
.vio_idx_num = 511,
static const struct mtk_devapc_regs_ofs devapc_regs_ofs_mt6779 = {
.vio_mask_offset = 0x0,
.vio_sta_offset = 0x400,
.vio_dbg0_offset = 0x900,
......@@ -227,10 +229,23 @@ static const struct mtk_devapc_data devapc_mt6779 = {
.vio_shift_con_offset = 0xF20,
};
static const struct mtk_devapc_data devapc_mt6779 = {
.vio_idx_num = 511,
.regs_ofs = &devapc_regs_ofs_mt6779,
};
static const struct mtk_devapc_data devapc_mt8186 = {
.vio_idx_num = 519,
.regs_ofs = &devapc_regs_ofs_mt6779,
};
static const struct of_device_id mtk_devapc_dt_match[] = {
{
.compatible = "mediatek,mt6779-devapc",
.data = &devapc_mt6779,
}, {
.compatible = "mediatek,mt8186-devapc",
.data = &devapc_mt8186,
}, {
},
};
......
......@@ -7,10 +7,12 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/soc/mediatek/mtk-mmsys.h>
#include <linux/soc/mediatek/mtk-mutex.h>
#include <linux/soc/mediatek/mtk-cmdq.h>
#define MT2701_MUTEX0_MOD0 0x2c
#define MT2701_MUTEX0_SOF0 0x30
......@@ -80,6 +82,15 @@
#define MT8183_MUTEX_MOD_DISP_GAMMA0 16
#define MT8183_MUTEX_MOD_DISP_DITHER0 17
#define MT8183_MUTEX_MOD_MDP_RDMA0 2
#define MT8183_MUTEX_MOD_MDP_RSZ0 4
#define MT8183_MUTEX_MOD_MDP_RSZ1 5
#define MT8183_MUTEX_MOD_MDP_TDSHP0 6
#define MT8183_MUTEX_MOD_MDP_WROT0 7
#define MT8183_MUTEX_MOD_MDP_WDMA 8
#define MT8183_MUTEX_MOD_MDP_AAL0 23
#define MT8183_MUTEX_MOD_MDP_CCORR0 24
#define MT8173_MUTEX_MOD_DISP_OVL0 11
#define MT8173_MUTEX_MOD_DISP_OVL1 12
#define MT8173_MUTEX_MOD_DISP_RDMA0 13
......@@ -110,6 +121,20 @@
#define MT8195_MUTEX_MOD_DISP_DP_INTF0 21
#define MT8195_MUTEX_MOD_DISP_PWM0 27
#define MT8365_MUTEX_MOD_DISP_OVL0 7
#define MT8365_MUTEX_MOD_DISP_OVL0_2L 8
#define MT8365_MUTEX_MOD_DISP_RDMA0 9
#define MT8365_MUTEX_MOD_DISP_RDMA1 10
#define MT8365_MUTEX_MOD_DISP_WDMA0 11
#define MT8365_MUTEX_MOD_DISP_COLOR0 12
#define MT8365_MUTEX_MOD_DISP_CCORR 13
#define MT8365_MUTEX_MOD_DISP_AAL 14
#define MT8365_MUTEX_MOD_DISP_GAMMA 15
#define MT8365_MUTEX_MOD_DISP_DITHER 16
#define MT8365_MUTEX_MOD_DISP_DSI0 17
#define MT8365_MUTEX_MOD_DISP_PWM0 20
#define MT8365_MUTEX_MOD_DISP_DPI0 22
#define MT2712_MUTEX_MOD_DISP_PWM2 10
#define MT2712_MUTEX_MOD_DISP_OVL0 11
#define MT2712_MUTEX_MOD_DISP_OVL1 12
......@@ -185,6 +210,7 @@ struct mtk_mutex_data {
const unsigned int *mutex_sof;
const unsigned int mutex_mod_reg;
const unsigned int mutex_sof_reg;
const unsigned int *mutex_table_mod;
const bool no_clk;
};
......@@ -194,6 +220,8 @@ struct mtk_mutex_ctx {
void __iomem *regs;
struct mtk_mutex mutex[10];
const struct mtk_mutex_data *data;
phys_addr_t addr;
struct cmdq_client_reg cmdq_reg;
};
static const unsigned int mt2701_mutex_mod[DDP_COMPONENT_ID_MAX] = {
......@@ -272,6 +300,17 @@ static const unsigned int mt8183_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_WDMA0] = MT8183_MUTEX_MOD_DISP_WDMA0,
};
static const unsigned int mt8183_mutex_table_mod[MUTEX_MOD_IDX_MAX] = {
[MUTEX_MOD_IDX_MDP_RDMA0] = MT8183_MUTEX_MOD_MDP_RDMA0,
[MUTEX_MOD_IDX_MDP_RSZ0] = MT8183_MUTEX_MOD_MDP_RSZ0,
[MUTEX_MOD_IDX_MDP_RSZ1] = MT8183_MUTEX_MOD_MDP_RSZ1,
[MUTEX_MOD_IDX_MDP_TDSHP0] = MT8183_MUTEX_MOD_MDP_TDSHP0,
[MUTEX_MOD_IDX_MDP_WROT0] = MT8183_MUTEX_MOD_MDP_WROT0,
[MUTEX_MOD_IDX_MDP_WDMA] = MT8183_MUTEX_MOD_MDP_WDMA,
[MUTEX_MOD_IDX_MDP_AAL0] = MT8183_MUTEX_MOD_MDP_AAL0,
[MUTEX_MOD_IDX_MDP_CCORR0] = MT8183_MUTEX_MOD_MDP_CCORR0,
};
static const unsigned int mt8186_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_AAL0] = MT8186_MUTEX_MOD_DISP_AAL0,
[DDP_COMPONENT_CCORR] = MT8186_MUTEX_MOD_DISP_CCORR0,
......@@ -315,6 +354,22 @@ static const unsigned int mt8195_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_DP_INTF0] = MT8195_MUTEX_MOD_DISP_DP_INTF0,
};
static const unsigned int mt8365_mutex_mod[DDP_COMPONENT_ID_MAX] = {
[DDP_COMPONENT_AAL0] = MT8365_MUTEX_MOD_DISP_AAL,
[DDP_COMPONENT_CCORR] = MT8365_MUTEX_MOD_DISP_CCORR,
[DDP_COMPONENT_COLOR0] = MT8365_MUTEX_MOD_DISP_COLOR0,
[DDP_COMPONENT_DITHER0] = MT8365_MUTEX_MOD_DISP_DITHER,
[DDP_COMPONENT_DPI0] = MT8365_MUTEX_MOD_DISP_DPI0,
[DDP_COMPONENT_DSI0] = MT8365_MUTEX_MOD_DISP_DSI0,
[DDP_COMPONENT_GAMMA] = MT8365_MUTEX_MOD_DISP_GAMMA,
[DDP_COMPONENT_OVL0] = MT8365_MUTEX_MOD_DISP_OVL0,
[DDP_COMPONENT_OVL_2L0] = MT8365_MUTEX_MOD_DISP_OVL0_2L,
[DDP_COMPONENT_PWM0] = MT8365_MUTEX_MOD_DISP_PWM0,
[DDP_COMPONENT_RDMA0] = MT8365_MUTEX_MOD_DISP_RDMA0,
[DDP_COMPONENT_RDMA1] = MT8365_MUTEX_MOD_DISP_RDMA1,
[DDP_COMPONENT_WDMA0] = MT8365_MUTEX_MOD_DISP_WDMA0,
};
static const unsigned int mt2712_mutex_sof[DDP_MUTEX_SOF_MAX] = {
[MUTEX_SOF_SINGLE_MODE] = MUTEX_SOF_SINGLE_MODE,
[MUTEX_SOF_DSI0] = MUTEX_SOF_DSI0,
......@@ -399,6 +454,7 @@ static const struct mtk_mutex_data mt8183_mutex_driver_data = {
.mutex_sof = mt8183_mutex_sof,
.mutex_mod_reg = MT8183_MUTEX0_MOD0,
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
.mutex_table_mod = mt8183_mutex_table_mod,
.no_clk = true,
};
......@@ -423,6 +479,14 @@ static const struct mtk_mutex_data mt8195_mutex_driver_data = {
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
};
static const struct mtk_mutex_data mt8365_mutex_driver_data = {
.mutex_mod = mt8365_mutex_mod,
.mutex_sof = mt8183_mutex_sof,
.mutex_mod_reg = MT8183_MUTEX0_MOD0,
.mutex_sof_reg = MT8183_MUTEX0_SOF0,
.no_clk = true,
};
struct mtk_mutex *mtk_mutex_get(struct device *dev)
{
struct mtk_mutex_ctx *mtx = dev_get_drvdata(dev);
......@@ -572,6 +636,30 @@ void mtk_mutex_enable(struct mtk_mutex *mutex)
}
EXPORT_SYMBOL_GPL(mtk_mutex_enable);
int mtk_mutex_enable_by_cmdq(struct mtk_mutex *mutex, void *pkt)
{
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
mutex[mutex->id]);
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
struct cmdq_pkt *cmdq_pkt = (struct cmdq_pkt *)pkt;
WARN_ON(&mtx->mutex[mutex->id] != mutex);
if (!mtx->cmdq_reg.size) {
dev_err(mtx->dev, "mediatek,gce-client-reg hasn't been set");
return -EINVAL;
}
cmdq_pkt_write(cmdq_pkt, mtx->cmdq_reg.subsys,
mtx->addr + DISP_REG_MUTEX_EN(mutex->id), 1);
return 0;
#else
dev_err(mtx->dev, "Not support for enable MUTEX by CMDQ");
return -ENODEV;
#endif
}
EXPORT_SYMBOL_GPL(mtk_mutex_enable_by_cmdq);
void mtk_mutex_disable(struct mtk_mutex *mutex)
{
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
......@@ -606,12 +694,67 @@ void mtk_mutex_release(struct mtk_mutex *mutex)
}
EXPORT_SYMBOL_GPL(mtk_mutex_release);
int mtk_mutex_write_mod(struct mtk_mutex *mutex,
enum mtk_mutex_mod_index idx, bool clear)
{
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
mutex[mutex->id]);
unsigned int reg;
unsigned int offset;
WARN_ON(&mtx->mutex[mutex->id] != mutex);
if (idx < MUTEX_MOD_IDX_MDP_RDMA0 ||
idx >= MUTEX_MOD_IDX_MAX) {
dev_err(mtx->dev, "Not supported MOD table index : %d", idx);
return -EINVAL;
}
offset = DISP_REG_MUTEX_MOD(mtx->data->mutex_mod_reg,
mutex->id);
reg = readl_relaxed(mtx->regs + offset);
if (clear)
reg &= ~BIT(mtx->data->mutex_table_mod[idx]);
else
reg |= BIT(mtx->data->mutex_table_mod[idx]);
writel_relaxed(reg, mtx->regs + offset);
return 0;
}
EXPORT_SYMBOL_GPL(mtk_mutex_write_mod);
int mtk_mutex_write_sof(struct mtk_mutex *mutex,
enum mtk_mutex_sof_index idx)
{
struct mtk_mutex_ctx *mtx = container_of(mutex, struct mtk_mutex_ctx,
mutex[mutex->id]);
WARN_ON(&mtx->mutex[mutex->id] != mutex);
if (idx < MUTEX_SOF_IDX_SINGLE_MODE ||
idx >= MUTEX_SOF_IDX_MAX) {
dev_err(mtx->dev, "Not supported SOF index : %d", idx);
return -EINVAL;
}
writel_relaxed(idx, mtx->regs +
DISP_REG_MUTEX_SOF(mtx->data->mutex_sof_reg, mutex->id));
return 0;
}
EXPORT_SYMBOL_GPL(mtk_mutex_write_sof);
static int mtk_mutex_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct mtk_mutex_ctx *mtx;
struct resource *regs;
int i;
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
int ret;
#endif
mtx = devm_kzalloc(dev, sizeof(*mtx), GFP_KERNEL);
if (!mtx)
......@@ -631,12 +774,18 @@ static int mtk_mutex_probe(struct platform_device *pdev)
}
}
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mtx->regs = devm_ioremap_resource(dev, regs);
mtx->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &regs);
if (IS_ERR(mtx->regs)) {
dev_err(dev, "Failed to map mutex registers\n");
return PTR_ERR(mtx->regs);
}
mtx->addr = regs->start;
#if IS_REACHABLE(CONFIG_MTK_CMDQ)
ret = cmdq_dev_get_client_reg(dev, &mtx->cmdq_reg, 0);
if (ret)
dev_dbg(dev, "No mediatek,gce-client-reg!\n");
#endif
platform_set_drvdata(pdev, mtx);
......@@ -665,6 +814,8 @@ static const struct of_device_id mutex_driver_dt_match[] = {
.data = &mt8192_mutex_driver_data},
{ .compatible = "mediatek,mt8195-disp-mutex",
.data = &mt8195_mutex_driver_data},
{ .compatible = "mediatek,mt8365-disp-mutex",
.data = &mt8365_mutex_driver_data},
{},
};
MODULE_DEVICE_TABLE(of, mutex_driver_dt_match);
......
......@@ -16,6 +16,7 @@
#include <linux/regulator/consumer.h>
#include <linux/soc/mediatek/infracfg.h>
#include "mt6795-pm-domains.h"
#include "mt8167-pm-domains.h"
#include "mt8173-pm-domains.h"
#include "mt8183-pm-domains.h"
......@@ -428,6 +429,9 @@ generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_no
dev_err(scpsys->dev, "%pOF: failed to power on domain: %d\n", node, ret);
goto err_put_subsys_clocks;
}
if (MTK_SCPD_CAPS(pd, MTK_SCPD_ALWAYS_ON))
pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON;
}
if (scpsys->domains[id]) {
......@@ -555,6 +559,10 @@ static void scpsys_domain_cleanup(struct scpsys *scpsys)
}
static const struct of_device_id scpsys_of_match[] = {
{
.compatible = "mediatek,mt6795-power-controller",
.data = &mt6795_scpsys_data,
},
{
.compatible = "mediatek,mt8167-power-controller",
.data = &mt8167_scpsys_data,
......
......@@ -8,6 +8,8 @@
#define MTK_SCPD_SRAM_ISO BIT(2)
#define MTK_SCPD_KEEP_DEFAULT_OFF BIT(3)
#define MTK_SCPD_DOMAIN_SUPPLY BIT(4)
/* can't set MTK_SCPD_KEEP_DEFAULT_OFF at the same time */
#define MTK_SCPD_ALWAYS_ON BIT(5)
#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x))
#define SPM_VDE_PWR_CON 0x0210
......
......@@ -13,6 +13,9 @@
#include <linux/regmap.h>
#include <linux/reset.h>
#define PWRAP_POLL_DELAY_US 10
#define PWRAP_POLL_TIMEOUT_US 10000
#define PWRAP_MT8135_BRIDGE_IORD_ARB_EN 0x4
#define PWRAP_MT8135_BRIDGE_WACS3_EN 0x10
#define PWRAP_MT8135_BRIDGE_INIT_DONE3 0x14
......@@ -1140,12 +1143,9 @@ enum pwrap_type {
};
struct pmic_wrapper;
struct pwrap_slv_type {
const u32 *dew_regs;
enum pmic_type type;
struct pwrap_slv_regops {
const struct regmap_config *regmap;
/* Flags indicating the capability for the target slave */
u32 caps;
/*
* pwrap operations are highly associated with the PMIC types,
* so the pointers added increases flexibility allowing determination
......@@ -1155,6 +1155,14 @@ struct pwrap_slv_type {
int (*pwrap_write)(struct pmic_wrapper *wrp, u32 adr, u32 wdata);
};
struct pwrap_slv_type {
const u32 *dew_regs;
enum pmic_type type;
const struct pwrap_slv_regops *regops;
/* Flags indicating the capability for the target slave */
u32 caps;
};
struct pmic_wrapper {
struct device *dev;
void __iomem *base;
......@@ -1241,27 +1249,14 @@ static bool pwrap_is_fsm_idle_and_sync_idle(struct pmic_wrapper *wrp)
(val & PWRAP_STATE_SYNC_IDLE0);
}
static int pwrap_wait_for_state(struct pmic_wrapper *wrp,
bool (*fp)(struct pmic_wrapper *))
{
unsigned long timeout;
timeout = jiffies + usecs_to_jiffies(10000);
do {
if (time_after(jiffies, timeout))
return fp(wrp) ? 0 : -ETIMEDOUT;
if (fp(wrp))
return 0;
} while (1);
}
static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
bool tmp;
int ret;
u32 val;
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
......@@ -1273,7 +1268,8 @@ static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
val = (adr >> 1) << 16;
pwrap_writel(wrp, val, PWRAP_WACS2_CMD);
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr);
ret = readx_poll_timeout(pwrap_is_fsm_vldclr, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret)
return ret;
......@@ -1290,11 +1286,14 @@ static int pwrap_read16(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
bool tmp;
int ret, msb;
*rdata = 0;
for (msb = 0; msb < 2; msb++) {
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
......@@ -1303,7 +1302,8 @@ static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
pwrap_writel(wrp, ((msb << 30) | (adr << 16)),
PWRAP_WACS2_CMD);
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_vldclr);
ret = readx_poll_timeout(pwrap_is_fsm_vldclr, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret)
return ret;
......@@ -1318,14 +1318,16 @@ static int pwrap_read32(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
static int pwrap_read(struct pmic_wrapper *wrp, u32 adr, u32 *rdata)
{
return wrp->slave->pwrap_read(wrp, adr, rdata);
return wrp->slave->regops->pwrap_read(wrp, adr, rdata);
}
static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
bool tmp;
int ret;
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
......@@ -1344,10 +1346,12 @@ static int pwrap_write16(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
bool tmp;
int ret, msb, rdata;
for (msb = 0; msb < 2; msb++) {
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle);
ret = readx_poll_timeout(pwrap_is_fsm_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
pwrap_leave_fsm_vldclr(wrp);
return ret;
......@@ -1373,7 +1377,7 @@ static int pwrap_write32(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
static int pwrap_write(struct pmic_wrapper *wrp, u32 adr, u32 wdata)
{
return wrp->slave->pwrap_write(wrp, adr, wdata);
return wrp->slave->regops->pwrap_write(wrp, adr, wdata);
}
static int pwrap_regmap_read(void *context, u32 adr, u32 *rdata)
......@@ -1388,6 +1392,7 @@ static int pwrap_regmap_write(void *context, u32 adr, u32 wdata)
static int pwrap_reset_spislave(struct pmic_wrapper *wrp)
{
bool tmp;
int ret, i;
pwrap_writel(wrp, 0, PWRAP_HIPRIO_ARB_EN);
......@@ -1407,7 +1412,8 @@ static int pwrap_reset_spislave(struct pmic_wrapper *wrp)
pwrap_writel(wrp, wrp->master->spi_w | PWRAP_MAN_CMD_OP_OUTS,
PWRAP_MAN_CMD);
ret = pwrap_wait_for_state(wrp, pwrap_is_sync_idle);
ret = readx_poll_timeout(pwrap_is_sync_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
return ret;
......@@ -1458,14 +1464,15 @@ static int pwrap_init_sidly(struct pmic_wrapper *wrp)
static int pwrap_init_dual_io(struct pmic_wrapper *wrp)
{
int ret;
bool tmp;
u32 rdata;
/* Enable dual IO mode */
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_DIO_EN], 1);
/* Check IDLE & INIT_DONE in advance */
ret = pwrap_wait_for_state(wrp,
pwrap_is_fsm_idle_and_sync_idle);
ret = readx_poll_timeout(pwrap_is_fsm_idle_and_sync_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
dev_err(wrp->dev, "%s fail, ret=%d\n", __func__, ret);
return ret;
......@@ -1570,6 +1577,7 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp)
static int pwrap_init_cipher(struct pmic_wrapper *wrp)
{
int ret;
bool tmp;
u32 rdata = 0;
pwrap_writel(wrp, 0x1, PWRAP_CIPHER_SWRST);
......@@ -1624,14 +1632,16 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
}
/* wait for cipher data ready@AP */
ret = pwrap_wait_for_state(wrp, pwrap_is_cipher_ready);
ret = readx_poll_timeout(pwrap_is_cipher_ready, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
dev_err(wrp->dev, "cipher data ready@AP fail, ret=%d\n", ret);
return ret;
}
/* wait for cipher data ready@PMIC */
ret = pwrap_wait_for_state(wrp, pwrap_is_pmic_cipher_ready);
ret = readx_poll_timeout(pwrap_is_pmic_cipher_ready, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
dev_err(wrp->dev,
"timeout waiting for cipher data ready@PMIC\n");
......@@ -1640,7 +1650,8 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp)
/* wait for cipher mode idle */
pwrap_write(wrp, wrp->slave->dew_regs[PWRAP_DEW_CIPHER_MODE], 0x1);
ret = pwrap_wait_for_state(wrp, pwrap_is_fsm_idle_and_sync_idle);
ret = readx_poll_timeout(pwrap_is_fsm_idle_and_sync_idle, wrp, tmp, tmp,
PWRAP_POLL_DELAY_US, PWRAP_POLL_TIMEOUT_US);
if (ret) {
dev_err(wrp->dev, "cipher mode idle fail, ret=%d\n", ret);
return ret;
......@@ -1885,99 +1896,82 @@ static const struct regmap_config pwrap_regmap_config32 = {
.max_register = 0xffff,
};
static const struct pwrap_slv_regops pwrap_regops16 = {
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
.regmap = &pwrap_regmap_config16,
};
static const struct pwrap_slv_regops pwrap_regops32 = {
.pwrap_read = pwrap_read32,
.pwrap_write = pwrap_write32,
.regmap = &pwrap_regmap_config32,
};
static const struct pwrap_slv_type pmic_mt6323 = {
.dew_regs = mt6323_regs,
.type = PMIC_MT6323,
.regmap = &pwrap_regmap_config16,
.regops = &pwrap_regops16,
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO |
PWRAP_SLV_CAP_SECURITY,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6351 = {
.dew_regs = mt6351_regs,
.type = PMIC_MT6351,
.regmap = &pwrap_regmap_config16,
.regops = &pwrap_regops16,
.caps = 0,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6357 = {
.dew_regs = mt6357_regs,
.type = PMIC_MT6357,
.regmap = &pwrap_regmap_config16,
.regops = &pwrap_regops16,
.caps = 0,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6358 = {
.dew_regs = mt6358_regs,
.type = PMIC_MT6358,
.regmap = &pwrap_regmap_config16,
.regops = &pwrap_regops16,
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6359 = {
.dew_regs = mt6359_regs,
.type = PMIC_MT6359,
.regmap = &pwrap_regmap_config16,
.regops = &pwrap_regops16,
.caps = PWRAP_SLV_CAP_DUALIO,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct pwrap_slv_type pmic_mt6380 = {
.dew_regs = NULL,
.type = PMIC_MT6380,
.regmap = &pwrap_regmap_config32,
.regops = &pwrap_regops32,
.caps = 0,
.pwrap_read = pwrap_read32,
.pwrap_write = pwrap_write32,
};
static const struct pwrap_slv_type pmic_mt6397 = {
.dew_regs = mt6397_regs,
.type = PMIC_MT6397,
.regmap = &pwrap_regmap_config16,
.regops = &pwrap_regops16,
.caps = PWRAP_SLV_CAP_SPI | PWRAP_SLV_CAP_DUALIO |
PWRAP_SLV_CAP_SECURITY,
.pwrap_read = pwrap_read16,
.pwrap_write = pwrap_write16,
};
static const struct of_device_id of_slave_match_tbl[] = {
{
.compatible = "mediatek,mt6323",
.data = &pmic_mt6323,
}, {
.compatible = "mediatek,mt6351",
.data = &pmic_mt6351,
}, {
.compatible = "mediatek,mt6357",
.data = &pmic_mt6357,
}, {
.compatible = "mediatek,mt6358",
.data = &pmic_mt6358,
}, {
.compatible = "mediatek,mt6359",
.data = &pmic_mt6359,
}, {
/* The MT6380 PMIC only implements a regulator, so we bind it
* directly instead of using a MFD.
*/
.compatible = "mediatek,mt6380-regulator",
.data = &pmic_mt6380,
}, {
.compatible = "mediatek,mt6397",
.data = &pmic_mt6397,
}, {
/* sentinel */
}
{ .compatible = "mediatek,mt6323", .data = &pmic_mt6323 },
{ .compatible = "mediatek,mt6351", .data = &pmic_mt6351 },
{ .compatible = "mediatek,mt6357", .data = &pmic_mt6357 },
{ .compatible = "mediatek,mt6358", .data = &pmic_mt6358 },
{ .compatible = "mediatek,mt6359", .data = &pmic_mt6359 },
/* The MT6380 PMIC only implements a regulator, so we bind it
* directly instead of using a MFD.
*/
{ .compatible = "mediatek,mt6380-regulator", .data = &pmic_mt6380 },
{ .compatible = "mediatek,mt6397", .data = &pmic_mt6397 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_slave_match_tbl);
......@@ -2136,45 +2130,19 @@ static struct pmic_wrapper_type pwrap_mt8186 = {
};
static const struct of_device_id of_pwrap_match_tbl[] = {
{
.compatible = "mediatek,mt2701-pwrap",
.data = &pwrap_mt2701,
}, {
.compatible = "mediatek,mt6765-pwrap",
.data = &pwrap_mt6765,
}, {
.compatible = "mediatek,mt6779-pwrap",
.data = &pwrap_mt6779,
}, {
.compatible = "mediatek,mt6797-pwrap",
.data = &pwrap_mt6797,
}, {
.compatible = "mediatek,mt6873-pwrap",
.data = &pwrap_mt6873,
}, {
.compatible = "mediatek,mt7622-pwrap",
.data = &pwrap_mt7622,
}, {
.compatible = "mediatek,mt8135-pwrap",
.data = &pwrap_mt8135,
}, {
.compatible = "mediatek,mt8173-pwrap",
.data = &pwrap_mt8173,
}, {
.compatible = "mediatek,mt8183-pwrap",
.data = &pwrap_mt8183,
}, {
.compatible = "mediatek,mt8186-pwrap",
.data = &pwrap_mt8186,
}, {
.compatible = "mediatek,mt8195-pwrap",
.data = &pwrap_mt8195,
}, {
.compatible = "mediatek,mt8516-pwrap",
.data = &pwrap_mt8516,
}, {
/* sentinel */
}
{ .compatible = "mediatek,mt2701-pwrap", .data = &pwrap_mt2701 },
{ .compatible = "mediatek,mt6765-pwrap", .data = &pwrap_mt6765 },
{ .compatible = "mediatek,mt6779-pwrap", .data = &pwrap_mt6779 },
{ .compatible = "mediatek,mt6797-pwrap", .data = &pwrap_mt6797 },
{ .compatible = "mediatek,mt6873-pwrap", .data = &pwrap_mt6873 },
{ .compatible = "mediatek,mt7622-pwrap", .data = &pwrap_mt7622 },
{ .compatible = "mediatek,mt8135-pwrap", .data = &pwrap_mt8135 },
{ .compatible = "mediatek,mt8173-pwrap", .data = &pwrap_mt8173 },
{ .compatible = "mediatek,mt8183-pwrap", .data = &pwrap_mt8183 },
{ .compatible = "mediatek,mt8186-pwrap", .data = &pwrap_mt8186 },
{ .compatible = "mediatek,mt8195-pwrap", .data = &pwrap_mt8195 },
{ .compatible = "mediatek,mt8516-pwrap", .data = &pwrap_mt8516 },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, of_pwrap_match_tbl);
......@@ -2185,7 +2153,6 @@ static int pwrap_probe(struct platform_device *pdev)
struct pmic_wrapper *wrp;
struct device_node *np = pdev->dev.of_node;
const struct of_device_id *of_slave_id = NULL;
struct resource *res;
if (np->child)
of_slave_id = of_match_node(of_slave_match_tbl, np->child);
......@@ -2205,8 +2172,7 @@ static int pwrap_probe(struct platform_device *pdev)
wrp->slave = of_slave_id->data;
wrp->dev = &pdev->dev;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwrap");
wrp->base = devm_ioremap_resource(wrp->dev, res);
wrp->base = devm_platform_ioremap_resource_byname(pdev, "pwrap");
if (IS_ERR(wrp->base))
return PTR_ERR(wrp->base);
......@@ -2220,9 +2186,7 @@ static int pwrap_probe(struct platform_device *pdev)
}
if (HAS_CAP(wrp->master->caps, PWRAP_CAP_BRIDGE)) {
res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"pwrap-bridge");
wrp->bridge_base = devm_ioremap_resource(wrp->dev, res);
wrp->bridge_base = devm_platform_ioremap_resource_byname(pdev, "pwrap-bridge");
if (IS_ERR(wrp->bridge_base))
return PTR_ERR(wrp->bridge_base);
......@@ -2315,13 +2279,18 @@ static int pwrap_probe(struct platform_device *pdev)
pwrap_writel(wrp, wrp->master->int1_en_all, PWRAP_INT1_EN);
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err_out2;
}
ret = devm_request_irq(wrp->dev, irq, pwrap_interrupt,
IRQF_TRIGGER_HIGH,
"mt-pmic-pwrap", wrp);
if (ret)
goto err_out2;
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regmap);
wrp->regmap = devm_regmap_init(wrp->dev, NULL, wrp, wrp->slave->regops->regmap);
if (IS_ERR(wrp->regmap)) {
ret = PTR_ERR(wrp->regmap);
goto err_out2;
......
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2022 MediaTek Inc.
*/
#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/completion.h>
#include <linux/cpuidle.h>
#include <linux/debugfs.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/nvmem-consumer.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/reset.h>
#include <linux/seq_file.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/thermal.h>
/* svs bank 1-line software id */
#define SVSB_CPU_LITTLE BIT(0)
#define SVSB_CPU_BIG BIT(1)
#define SVSB_CCI BIT(2)
#define SVSB_GPU BIT(3)
/* svs bank 2-line type */
#define SVSB_LOW BIT(8)
#define SVSB_HIGH BIT(9)
/* svs bank mode support */
#define SVSB_MODE_ALL_DISABLE 0
#define SVSB_MODE_INIT01 BIT(1)
#define SVSB_MODE_INIT02 BIT(2)
#define SVSB_MODE_MON BIT(3)
/* svs bank volt flags */
#define SVSB_INIT01_PD_REQ BIT(0)
#define SVSB_INIT01_VOLT_IGNORE BIT(1)
#define SVSB_INIT01_VOLT_INC_ONLY BIT(2)
#define SVSB_MON_VOLT_IGNORE BIT(16)
#define SVSB_REMOVE_DVTFIXED_VOLT BIT(24)
/* svs bank register common configuration */
#define SVSB_DET_MAX 0xffff
#define SVSB_DET_WINDOW 0xa28
#define SVSB_DTHI 0x1
#define SVSB_DTLO 0xfe
#define SVSB_EN_INIT01 0x1
#define SVSB_EN_INIT02 0x5
#define SVSB_EN_MON 0x2
#define SVSB_EN_OFF 0x0
#define SVSB_INTEN_INIT0x 0x00005f01
#define SVSB_INTEN_MONVOPEN 0x00ff0000
#define SVSB_INTSTS_CLEAN 0x00ffffff
#define SVSB_INTSTS_COMPLETE 0x1
#define SVSB_INTSTS_MONVOP 0x00ff0000
#define SVSB_RUNCONFIG_DEFAULT 0x80000000
/* svs bank related setting */
#define BITS8 8
#define MAX_OPP_ENTRIES 16
#define REG_BYTES 4
#define SVSB_DC_SIGNED_BIT BIT(15)
#define SVSB_DET_CLK_EN BIT(31)
#define SVSB_TEMP_LOWER_BOUND 0xb2
#define SVSB_TEMP_UPPER_BOUND 0x64
static DEFINE_SPINLOCK(svs_lock);
#define debug_fops_ro(name) \
static int svs_##name##_debug_open(struct inode *inode, \
struct file *filp) \
{ \
return single_open(filp, svs_##name##_debug_show, \
inode->i_private); \
} \
static const struct file_operations svs_##name##_debug_fops = { \
.owner = THIS_MODULE, \
.open = svs_##name##_debug_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
#define debug_fops_rw(name) \
static int svs_##name##_debug_open(struct inode *inode, \
struct file *filp) \
{ \
return single_open(filp, svs_##name##_debug_show, \
inode->i_private); \
} \
static const struct file_operations svs_##name##_debug_fops = { \
.owner = THIS_MODULE, \
.open = svs_##name##_debug_open, \
.read = seq_read, \
.write = svs_##name##_debug_write, \
.llseek = seq_lseek, \
.release = single_release, \
}
#define svs_dentry_data(name) {__stringify(name), &svs_##name##_debug_fops}
/**
* enum svsb_phase - svs bank phase enumeration
* @SVSB_PHASE_ERROR: svs bank encounters unexpected condition
* @SVSB_PHASE_INIT01: svs bank basic init for data calibration
* @SVSB_PHASE_INIT02: svs bank can provide voltages to opp table
* @SVSB_PHASE_MON: svs bank can provide voltages with thermal effect
* @SVSB_PHASE_MAX: total number of svs bank phase (debug purpose)
*
* Each svs bank has its own independent phase and we enable each svs bank by
* running their phase orderly. However, when svs bank encounters unexpected
* condition, it will fire an irq (PHASE_ERROR) to inform svs software.
*
* svs bank general phase-enabled order:
* SVSB_PHASE_INIT01 -> SVSB_PHASE_INIT02 -> SVSB_PHASE_MON
*/
enum svsb_phase {
SVSB_PHASE_ERROR = 0,
SVSB_PHASE_INIT01,
SVSB_PHASE_INIT02,
SVSB_PHASE_MON,
SVSB_PHASE_MAX,
};
enum svs_reg_index {
DESCHAR = 0,
TEMPCHAR,
DETCHAR,
AGECHAR,
DCCONFIG,
AGECONFIG,
FREQPCT30,
FREQPCT74,
LIMITVALS,
VBOOT,
DETWINDOW,
CONFIG,
TSCALCS,
RUNCONFIG,
SVSEN,
INIT2VALS,
DCVALUES,
AGEVALUES,
VOP30,
VOP74,
TEMP,
INTSTS,
INTSTSRAW,
INTEN,
CHKINT,
CHKSHIFT,
STATUS,
VDESIGN30,
VDESIGN74,
DVT30,
DVT74,
AGECOUNT,
SMSTATE0,
SMSTATE1,
CTL0,
DESDETSEC,
TEMPAGESEC,
CTRLSPARE0,
CTRLSPARE1,
CTRLSPARE2,
CTRLSPARE3,
CORESEL,
THERMINTST,
INTST,
THSTAGE0ST,
THSTAGE1ST,
THSTAGE2ST,
THAHBST0,
THAHBST1,
SPARE0,
SPARE1,
SPARE2,
SPARE3,
THSLPEVEB,
SVS_REG_MAX,
};
static const u32 svs_regs_v2[] = {
[DESCHAR] = 0xc00,
[TEMPCHAR] = 0xc04,
[DETCHAR] = 0xc08,
[AGECHAR] = 0xc0c,
[DCCONFIG] = 0xc10,
[AGECONFIG] = 0xc14,
[FREQPCT30] = 0xc18,
[FREQPCT74] = 0xc1c,
[LIMITVALS] = 0xc20,
[VBOOT] = 0xc24,
[DETWINDOW] = 0xc28,
[CONFIG] = 0xc2c,
[TSCALCS] = 0xc30,
[RUNCONFIG] = 0xc34,
[SVSEN] = 0xc38,
[INIT2VALS] = 0xc3c,
[DCVALUES] = 0xc40,
[AGEVALUES] = 0xc44,
[VOP30] = 0xc48,
[VOP74] = 0xc4c,
[TEMP] = 0xc50,
[INTSTS] = 0xc54,
[INTSTSRAW] = 0xc58,
[INTEN] = 0xc5c,
[CHKINT] = 0xc60,
[CHKSHIFT] = 0xc64,
[STATUS] = 0xc68,
[VDESIGN30] = 0xc6c,
[VDESIGN74] = 0xc70,
[DVT30] = 0xc74,
[DVT74] = 0xc78,
[AGECOUNT] = 0xc7c,
[SMSTATE0] = 0xc80,
[SMSTATE1] = 0xc84,
[CTL0] = 0xc88,
[DESDETSEC] = 0xce0,
[TEMPAGESEC] = 0xce4,
[CTRLSPARE0] = 0xcf0,
[CTRLSPARE1] = 0xcf4,
[CTRLSPARE2] = 0xcf8,
[CTRLSPARE3] = 0xcfc,
[CORESEL] = 0xf00,
[THERMINTST] = 0xf04,
[INTST] = 0xf08,
[THSTAGE0ST] = 0xf0c,
[THSTAGE1ST] = 0xf10,
[THSTAGE2ST] = 0xf14,
[THAHBST0] = 0xf18,
[THAHBST1] = 0xf1c,
[SPARE0] = 0xf20,
[SPARE1] = 0xf24,
[SPARE2] = 0xf28,
[SPARE3] = 0xf2c,
[THSLPEVEB] = 0xf30,
};
/**
* struct svs_platform - svs platform control
* @name: svs platform name
* @base: svs platform register base
* @dev: svs platform device
* @main_clk: main clock for svs bank
* @pbank: svs bank pointer needing to be protected by spin_lock section
* @banks: svs banks that svs platform supports
* @rst: svs platform reset control
* @efuse_parsing: svs platform efuse parsing function pointer
* @probe: svs platform probe function pointer
* @irqflags: svs platform irq settings flags
* @efuse_max: total number of svs efuse
* @tefuse_max: total number of thermal efuse
* @regs: svs platform registers map
* @bank_max: total number of svs banks
* @efuse: svs efuse data received from NVMEM framework
* @tefuse: thermal efuse data received from NVMEM framework
*/
struct svs_platform {
char *name;
void __iomem *base;
struct device *dev;
struct clk *main_clk;
struct svs_bank *pbank;
struct svs_bank *banks;
struct reset_control *rst;
bool (*efuse_parsing)(struct svs_platform *svsp);
int (*probe)(struct svs_platform *svsp);
unsigned long irqflags;
size_t efuse_max;
size_t tefuse_max;
const u32 *regs;
u32 bank_max;
u32 *efuse;
u32 *tefuse;
};
struct svs_platform_data {
char *name;
struct svs_bank *banks;
bool (*efuse_parsing)(struct svs_platform *svsp);
int (*probe)(struct svs_platform *svsp);
unsigned long irqflags;
const u32 *regs;
u32 bank_max;
};
/**
* struct svs_bank - svs bank representation
* @dev: bank device
* @opp_dev: device for opp table/buck control
* @init_completion: the timeout completion for bank init
* @buck: regulator used by opp_dev
* @tzd: thermal zone device for getting temperature
* @lock: mutex lock to protect voltage update process
* @set_freq_pct: function pointer to set bank frequency percent table
* @get_volts: function pointer to get bank voltages
* @name: bank name
* @buck_name: regulator name
* @tzone_name: thermal zone name
* @phase: bank current phase
* @volt_od: bank voltage overdrive
* @reg_data: bank register data in different phase for debug purpose
* @pm_runtime_enabled_count: bank pm runtime enabled count
* @mode_support: bank mode support.
* @freq_base: reference frequency for bank init
* @turn_freq_base: refenrece frequency for 2-line turn point
* @vboot: voltage request for bank init01 only
* @opp_dfreq: default opp frequency table
* @opp_dvolt: default opp voltage table
* @freq_pct: frequency percent table for bank init
* @volt: bank voltage table
* @volt_step: bank voltage step
* @volt_base: bank voltage base
* @volt_flags: bank voltage flags
* @vmax: bank voltage maximum
* @vmin: bank voltage minimum
* @age_config: bank age configuration
* @age_voffset_in: bank age voltage offset
* @dc_config: bank dc configuration
* @dc_voffset_in: bank dc voltage offset
* @dvt_fixed: bank dvt fixed value
* @vco: bank VCO value
* @chk_shift: bank chicken shift
* @core_sel: bank selection
* @opp_count: bank opp count
* @int_st: bank interrupt identification
* @sw_id: bank software identification
* @cpu_id: cpu core id for SVS CPU bank use only
* @ctl0: TS-x selection
* @temp: bank temperature
* @tzone_htemp: thermal zone high temperature threshold
* @tzone_htemp_voffset: thermal zone high temperature voltage offset
* @tzone_ltemp: thermal zone low temperature threshold
* @tzone_ltemp_voffset: thermal zone low temperature voltage offset
* @bts: svs efuse data
* @mts: svs efuse data
* @bdes: svs efuse data
* @mdes: svs efuse data
* @mtdes: svs efuse data
* @dcbdet: svs efuse data
* @dcmdet: svs efuse data
* @turn_pt: 2-line turn point tells which opp_volt calculated by high/low bank
* @type: bank type to represent it is 2-line (high/low) bank or 1-line bank
*
* Svs bank will generate suitalbe voltages by below general math equation
* and provide these voltages to opp voltage table.
*
* opp_volt[i] = (volt[i] * volt_step) + volt_base;
*/
struct svs_bank {
struct device *dev;
struct device *opp_dev;
struct completion init_completion;
struct regulator *buck;
struct thermal_zone_device *tzd;
struct mutex lock; /* lock to protect voltage update process */
void (*set_freq_pct)(struct svs_platform *svsp);
void (*get_volts)(struct svs_platform *svsp);
char *name;
char *buck_name;
char *tzone_name;
enum svsb_phase phase;
s32 volt_od;
u32 reg_data[SVSB_PHASE_MAX][SVS_REG_MAX];
u32 pm_runtime_enabled_count;
u32 mode_support;
u32 freq_base;
u32 turn_freq_base;
u32 vboot;
u32 opp_dfreq[MAX_OPP_ENTRIES];
u32 opp_dvolt[MAX_OPP_ENTRIES];
u32 freq_pct[MAX_OPP_ENTRIES];
u32 volt[MAX_OPP_ENTRIES];
u32 volt_step;
u32 volt_base;
u32 volt_flags;
u32 vmax;
u32 vmin;
u32 age_config;
u32 age_voffset_in;
u32 dc_config;
u32 dc_voffset_in;
u32 dvt_fixed;
u32 vco;
u32 chk_shift;
u32 core_sel;
u32 opp_count;
u32 int_st;
u32 sw_id;
u32 cpu_id;
u32 ctl0;
u32 temp;
u32 tzone_htemp;
u32 tzone_htemp_voffset;
u32 tzone_ltemp;
u32 tzone_ltemp_voffset;
u32 bts;
u32 mts;
u32 bdes;
u32 mdes;
u32 mtdes;
u32 dcbdet;
u32 dcmdet;
u32 turn_pt;
u32 type;
};
static u32 percent(u32 numerator, u32 denominator)
{
/* If not divide 1000, "numerator * 100" will have data overflow. */
numerator /= 1000;
denominator /= 1000;
return DIV_ROUND_UP(numerator * 100, denominator);
}
static u32 svs_readl_relaxed(struct svs_platform *svsp, enum svs_reg_index rg_i)
{
return readl_relaxed(svsp->base + svsp->regs[rg_i]);
}
static void svs_writel_relaxed(struct svs_platform *svsp, u32 val,
enum svs_reg_index rg_i)
{
writel_relaxed(val, svsp->base + svsp->regs[rg_i]);
}
static void svs_switch_bank(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
svs_writel_relaxed(svsp, svsb->core_sel, CORESEL);
}
static u32 svs_bank_volt_to_opp_volt(u32 svsb_volt, u32 svsb_volt_step,
u32 svsb_volt_base)
{
return (svsb_volt * svsb_volt_step) + svsb_volt_base;
}
static u32 svs_opp_volt_to_bank_volt(u32 opp_u_volt, u32 svsb_volt_step,
u32 svsb_volt_base)
{
return (opp_u_volt - svsb_volt_base) / svsb_volt_step;
}
static int svs_sync_bank_volts_from_opp(struct svs_bank *svsb)
{
struct dev_pm_opp *opp;
u32 i, opp_u_volt;
for (i = 0; i < svsb->opp_count; i++) {
opp = dev_pm_opp_find_freq_exact(svsb->opp_dev,
svsb->opp_dfreq[i],
true);
if (IS_ERR(opp)) {
dev_err(svsb->dev, "cannot find freq = %u (%ld)\n",
svsb->opp_dfreq[i], PTR_ERR(opp));
return PTR_ERR(opp);
}
opp_u_volt = dev_pm_opp_get_voltage(opp);
svsb->volt[i] = svs_opp_volt_to_bank_volt(opp_u_volt,
svsb->volt_step,
svsb->volt_base);
dev_pm_opp_put(opp);
}
return 0;
}
static int svs_adjust_pm_opp_volts(struct svs_bank *svsb)
{
int ret = -EPERM, tzone_temp = 0;
u32 i, svsb_volt, opp_volt, temp_voffset = 0, opp_start, opp_stop;
mutex_lock(&svsb->lock);
/*
* 2-line bank updates its corresponding opp volts.
* 1-line bank updates all opp volts.
*/
if (svsb->type == SVSB_HIGH) {
opp_start = 0;
opp_stop = svsb->turn_pt;
} else if (svsb->type == SVSB_LOW) {
opp_start = svsb->turn_pt;
opp_stop = svsb->opp_count;
} else {
opp_start = 0;
opp_stop = svsb->opp_count;
}
/* Get thermal effect */
if (svsb->phase == SVSB_PHASE_MON) {
ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp);
if (ret || (svsb->temp > SVSB_TEMP_UPPER_BOUND &&
svsb->temp < SVSB_TEMP_LOWER_BOUND)) {
dev_err(svsb->dev, "%s: %d (0x%x), run default volts\n",
svsb->tzone_name, ret, svsb->temp);
svsb->phase = SVSB_PHASE_ERROR;
}
if (tzone_temp >= svsb->tzone_htemp)
temp_voffset += svsb->tzone_htemp_voffset;
else if (tzone_temp <= svsb->tzone_ltemp)
temp_voffset += svsb->tzone_ltemp_voffset;
/* 2-line bank update all opp volts when running mon mode */
if (svsb->type == SVSB_HIGH || svsb->type == SVSB_LOW) {
opp_start = 0;
opp_stop = svsb->opp_count;
}
}
/* vmin <= svsb_volt (opp_volt) <= default opp voltage */
for (i = opp_start; i < opp_stop; i++) {
switch (svsb->phase) {
case SVSB_PHASE_ERROR:
opp_volt = svsb->opp_dvolt[i];
break;
case SVSB_PHASE_INIT01:
/* do nothing */
goto unlock_mutex;
case SVSB_PHASE_INIT02:
svsb_volt = max(svsb->volt[i], svsb->vmin);
opp_volt = svs_bank_volt_to_opp_volt(svsb_volt,
svsb->volt_step,
svsb->volt_base);
break;
case SVSB_PHASE_MON:
svsb_volt = max(svsb->volt[i] + temp_voffset, svsb->vmin);
opp_volt = svs_bank_volt_to_opp_volt(svsb_volt,
svsb->volt_step,
svsb->volt_base);
break;
default:
dev_err(svsb->dev, "unknown phase: %u\n", svsb->phase);
ret = -EINVAL;
goto unlock_mutex;
}
opp_volt = min(opp_volt, svsb->opp_dvolt[i]);
ret = dev_pm_opp_adjust_voltage(svsb->opp_dev,
svsb->opp_dfreq[i],
opp_volt, opp_volt,
svsb->opp_dvolt[i]);
if (ret) {
dev_err(svsb->dev, "set %uuV fail: %d\n",
opp_volt, ret);
goto unlock_mutex;
}
}
unlock_mutex:
mutex_unlock(&svsb->lock);
return ret;
}
static int svs_dump_debug_show(struct seq_file *m, void *p)
{
struct svs_platform *svsp = (struct svs_platform *)m->private;
struct svs_bank *svsb;
unsigned long svs_reg_addr;
u32 idx, i, j, bank_id;
for (i = 0; i < svsp->efuse_max; i++)
if (svsp->efuse && svsp->efuse[i])
seq_printf(m, "M_HW_RES%d = 0x%08x\n",
i, svsp->efuse[i]);
for (i = 0; i < svsp->tefuse_max; i++)
if (svsp->tefuse)
seq_printf(m, "THERMAL_EFUSE%d = 0x%08x\n",
i, svsp->tefuse[i]);
for (bank_id = 0, idx = 0; idx < svsp->bank_max; idx++, bank_id++) {
svsb = &svsp->banks[idx];
for (i = SVSB_PHASE_INIT01; i <= SVSB_PHASE_MON; i++) {
seq_printf(m, "Bank_number = %u\n", bank_id);
if (i == SVSB_PHASE_INIT01 || i == SVSB_PHASE_INIT02)
seq_printf(m, "mode = init%d\n", i);
else if (i == SVSB_PHASE_MON)
seq_puts(m, "mode = mon\n");
else
seq_puts(m, "mode = error\n");
for (j = DESCHAR; j < SVS_REG_MAX; j++) {
svs_reg_addr = (unsigned long)(svsp->base +
svsp->regs[j]);
seq_printf(m, "0x%08lx = 0x%08x\n",
svs_reg_addr, svsb->reg_data[i][j]);
}
}
}
return 0;
}
debug_fops_ro(dump);
static int svs_enable_debug_show(struct seq_file *m, void *v)
{
struct svs_bank *svsb = (struct svs_bank *)m->private;
switch (svsb->phase) {
case SVSB_PHASE_ERROR:
seq_puts(m, "disabled\n");
break;
case SVSB_PHASE_INIT01:
seq_puts(m, "init1\n");
break;
case SVSB_PHASE_INIT02:
seq_puts(m, "init2\n");
break;
case SVSB_PHASE_MON:
seq_puts(m, "mon mode\n");
break;
default:
seq_puts(m, "unknown\n");
break;
}
return 0;
}
static ssize_t svs_enable_debug_write(struct file *filp,
const char __user *buffer,
size_t count, loff_t *pos)
{
struct svs_bank *svsb = file_inode(filp)->i_private;
struct svs_platform *svsp = dev_get_drvdata(svsb->dev);
unsigned long flags;
int enabled, ret;
char *buf = NULL;
if (count >= PAGE_SIZE)
return -EINVAL;
buf = (char *)memdup_user_nul(buffer, count);
if (IS_ERR(buf))
return PTR_ERR(buf);
ret = kstrtoint(buf, 10, &enabled);
if (ret)
return ret;
if (!enabled) {
spin_lock_irqsave(&svs_lock, flags);
svsp->pbank = svsb;
svsb->mode_support = SVSB_MODE_ALL_DISABLE;
svs_switch_bank(svsp);
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
spin_unlock_irqrestore(&svs_lock, flags);
svsb->phase = SVSB_PHASE_ERROR;
svs_adjust_pm_opp_volts(svsb);
}
kfree(buf);
return count;
}
debug_fops_rw(enable);
static int svs_status_debug_show(struct seq_file *m, void *v)
{
struct svs_bank *svsb = (struct svs_bank *)m->private;
struct dev_pm_opp *opp;
int tzone_temp = 0, ret;
u32 i;
ret = thermal_zone_get_temp(svsb->tzd, &tzone_temp);
if (ret)
seq_printf(m, "%s: temperature ignore, turn_pt = %u\n",
svsb->name, svsb->turn_pt);
else
seq_printf(m, "%s: temperature = %d, turn_pt = %u\n",
svsb->name, tzone_temp, svsb->turn_pt);
for (i = 0; i < svsb->opp_count; i++) {
opp = dev_pm_opp_find_freq_exact(svsb->opp_dev,
svsb->opp_dfreq[i], true);
if (IS_ERR(opp)) {
seq_printf(m, "%s: cannot find freq = %u (%ld)\n",
svsb->name, svsb->opp_dfreq[i],
PTR_ERR(opp));
return PTR_ERR(opp);
}
seq_printf(m, "opp_freq[%02u]: %u, opp_volt[%02u]: %lu, ",
i, svsb->opp_dfreq[i], i,
dev_pm_opp_get_voltage(opp));
seq_printf(m, "svsb_volt[%02u]: 0x%x, freq_pct[%02u]: %u\n",
i, svsb->volt[i], i, svsb->freq_pct[i]);
dev_pm_opp_put(opp);
}
return 0;
}
debug_fops_ro(status);
static int svs_create_debug_cmds(struct svs_platform *svsp)
{
struct svs_bank *svsb;
struct dentry *svs_dir, *svsb_dir, *file_entry;
const char *d = "/sys/kernel/debug/svs";
u32 i, idx;
struct svs_dentry {
const char *name;
const struct file_operations *fops;
};
struct svs_dentry svs_entries[] = {
svs_dentry_data(dump),
};
struct svs_dentry svsb_entries[] = {
svs_dentry_data(enable),
svs_dentry_data(status),
};
svs_dir = debugfs_create_dir("svs", NULL);
if (IS_ERR(svs_dir)) {
dev_err(svsp->dev, "cannot create %s: %ld\n",
d, PTR_ERR(svs_dir));
return PTR_ERR(svs_dir);
}
for (i = 0; i < ARRAY_SIZE(svs_entries); i++) {
file_entry = debugfs_create_file(svs_entries[i].name, 0664,
svs_dir, svsp,
svs_entries[i].fops);
if (IS_ERR(file_entry)) {
dev_err(svsp->dev, "cannot create %s/%s: %ld\n",
d, svs_entries[i].name, PTR_ERR(file_entry));
return PTR_ERR(file_entry);
}
}
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (svsb->mode_support == SVSB_MODE_ALL_DISABLE)
continue;
svsb_dir = debugfs_create_dir(svsb->name, svs_dir);
if (IS_ERR(svsb_dir)) {
dev_err(svsp->dev, "cannot create %s/%s: %ld\n",
d, svsb->name, PTR_ERR(svsb_dir));
return PTR_ERR(svsb_dir);
}
for (i = 0; i < ARRAY_SIZE(svsb_entries); i++) {
file_entry = debugfs_create_file(svsb_entries[i].name,
0664, svsb_dir, svsb,
svsb_entries[i].fops);
if (IS_ERR(file_entry)) {
dev_err(svsp->dev, "no %s/%s/%s?: %ld\n",
d, svsb->name, svsb_entries[i].name,
PTR_ERR(file_entry));
return PTR_ERR(file_entry);
}
}
}
return 0;
}
static u32 interpolate(u32 f0, u32 f1, u32 v0, u32 v1, u32 fx)
{
u32 vx;
if (v0 == v1 || f0 == f1)
return v0;
/* *100 to have decimal fraction factor */
vx = (v0 * 100) - ((((v0 - v1) * 100) / (f0 - f1)) * (f0 - fx));
return DIV_ROUND_UP(vx, 100);
}
static void svs_get_bank_volts_v3(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
u32 i, j, *vop, vop74, vop30, turn_pt = svsb->turn_pt;
u32 b_sft, shift_byte = 0, opp_start = 0, opp_stop = 0;
u32 middle_index = (svsb->opp_count / 2);
if (svsb->phase == SVSB_PHASE_MON &&
svsb->volt_flags & SVSB_MON_VOLT_IGNORE)
return;
vop74 = svs_readl_relaxed(svsp, VOP74);
vop30 = svs_readl_relaxed(svsp, VOP30);
/* Target is to set svsb->volt[] by algorithm */
if (turn_pt < middle_index) {
if (svsb->type == SVSB_HIGH) {
/* volt[0] ~ volt[turn_pt - 1] */
for (i = 0; i < turn_pt; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
vop = (shift_byte < REG_BYTES) ? &vop30 :
&vop74;
svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0);
shift_byte++;
}
} else if (svsb->type == SVSB_LOW) {
/* volt[turn_pt] + volt[j] ~ volt[opp_count - 1] */
j = svsb->opp_count - 7;
svsb->volt[turn_pt] = vop30 & GENMASK(7, 0);
shift_byte++;
for (i = j; i < svsb->opp_count; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
vop = (shift_byte < REG_BYTES) ? &vop30 :
&vop74;
svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0);
shift_byte++;
}
/* volt[turn_pt + 1] ~ volt[j - 1] by interpolate */
for (i = turn_pt + 1; i < j; i++)
svsb->volt[i] = interpolate(svsb->freq_pct[turn_pt],
svsb->freq_pct[j],
svsb->volt[turn_pt],
svsb->volt[j],
svsb->freq_pct[i]);
}
} else {
if (svsb->type == SVSB_HIGH) {
/* volt[0] + volt[j] ~ volt[turn_pt - 1] */
j = turn_pt - 7;
svsb->volt[0] = vop30 & GENMASK(7, 0);
shift_byte++;
for (i = j; i < turn_pt; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
vop = (shift_byte < REG_BYTES) ? &vop30 :
&vop74;
svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0);
shift_byte++;
}
/* volt[1] ~ volt[j - 1] by interpolate */
for (i = 1; i < j; i++)
svsb->volt[i] = interpolate(svsb->freq_pct[0],
svsb->freq_pct[j],
svsb->volt[0],
svsb->volt[j],
svsb->freq_pct[i]);
} else if (svsb->type == SVSB_LOW) {
/* volt[turn_pt] ~ volt[opp_count - 1] */
for (i = turn_pt; i < svsb->opp_count; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
vop = (shift_byte < REG_BYTES) ? &vop30 :
&vop74;
svsb->volt[i] = (*vop >> b_sft) & GENMASK(7, 0);
shift_byte++;
}
}
}
if (svsb->type == SVSB_HIGH) {
opp_start = 0;
opp_stop = svsb->turn_pt;
} else if (svsb->type == SVSB_LOW) {
opp_start = svsb->turn_pt;
opp_stop = svsb->opp_count;
}
for (i = opp_start; i < opp_stop; i++)
if (svsb->volt_flags & SVSB_REMOVE_DVTFIXED_VOLT)
svsb->volt[i] -= svsb->dvt_fixed;
}
static void svs_set_bank_freq_pct_v3(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
u32 i, j, *freq_pct, freq_pct74 = 0, freq_pct30 = 0;
u32 b_sft, shift_byte = 0, turn_pt;
u32 middle_index = (svsb->opp_count / 2);
for (i = 0; i < svsb->opp_count; i++) {
if (svsb->opp_dfreq[i] <= svsb->turn_freq_base) {
svsb->turn_pt = i;
break;
}
}
turn_pt = svsb->turn_pt;
/* Target is to fill out freq_pct74 / freq_pct30 by algorithm */
if (turn_pt < middle_index) {
if (svsb->type == SVSB_HIGH) {
/*
* If we don't handle this situation,
* SVSB_HIGH's FREQPCT74 / FREQPCT30 would keep "0"
* and this leads SVSB_LOW to work abnormally.
*/
if (turn_pt == 0)
freq_pct30 = svsb->freq_pct[0];
/* freq_pct[0] ~ freq_pct[turn_pt - 1] */
for (i = 0; i < turn_pt; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
freq_pct = (shift_byte < REG_BYTES) ?
&freq_pct30 : &freq_pct74;
*freq_pct |= (svsb->freq_pct[i] << b_sft);
shift_byte++;
}
} else if (svsb->type == SVSB_LOW) {
/*
* freq_pct[turn_pt] +
* freq_pct[opp_count - 7] ~ freq_pct[opp_count -1]
*/
freq_pct30 = svsb->freq_pct[turn_pt];
shift_byte++;
j = svsb->opp_count - 7;
for (i = j; i < svsb->opp_count; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
freq_pct = (shift_byte < REG_BYTES) ?
&freq_pct30 : &freq_pct74;
*freq_pct |= (svsb->freq_pct[i] << b_sft);
shift_byte++;
}
}
} else {
if (svsb->type == SVSB_HIGH) {
/*
* freq_pct[0] +
* freq_pct[turn_pt - 7] ~ freq_pct[turn_pt - 1]
*/
freq_pct30 = svsb->freq_pct[0];
shift_byte++;
j = turn_pt - 7;
for (i = j; i < turn_pt; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
freq_pct = (shift_byte < REG_BYTES) ?
&freq_pct30 : &freq_pct74;
*freq_pct |= (svsb->freq_pct[i] << b_sft);
shift_byte++;
}
} else if (svsb->type == SVSB_LOW) {
/* freq_pct[turn_pt] ~ freq_pct[opp_count - 1] */
for (i = turn_pt; i < svsb->opp_count; i++) {
b_sft = BITS8 * (shift_byte % REG_BYTES);
freq_pct = (shift_byte < REG_BYTES) ?
&freq_pct30 : &freq_pct74;
*freq_pct |= (svsb->freq_pct[i] << b_sft);
shift_byte++;
}
}
}
svs_writel_relaxed(svsp, freq_pct74, FREQPCT74);
svs_writel_relaxed(svsp, freq_pct30, FREQPCT30);
}
static void svs_get_bank_volts_v2(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
u32 temp, i;
temp = svs_readl_relaxed(svsp, VOP74);
svsb->volt[14] = (temp >> 24) & GENMASK(7, 0);
svsb->volt[12] = (temp >> 16) & GENMASK(7, 0);
svsb->volt[10] = (temp >> 8) & GENMASK(7, 0);
svsb->volt[8] = (temp & GENMASK(7, 0));
temp = svs_readl_relaxed(svsp, VOP30);
svsb->volt[6] = (temp >> 24) & GENMASK(7, 0);
svsb->volt[4] = (temp >> 16) & GENMASK(7, 0);
svsb->volt[2] = (temp >> 8) & GENMASK(7, 0);
svsb->volt[0] = (temp & GENMASK(7, 0));
for (i = 0; i <= 12; i += 2)
svsb->volt[i + 1] = interpolate(svsb->freq_pct[i],
svsb->freq_pct[i + 2],
svsb->volt[i],
svsb->volt[i + 2],
svsb->freq_pct[i + 1]);
svsb->volt[15] = interpolate(svsb->freq_pct[12],
svsb->freq_pct[14],
svsb->volt[12],
svsb->volt[14],
svsb->freq_pct[15]);
for (i = 0; i < svsb->opp_count; i++)
svsb->volt[i] += svsb->volt_od;
}
static void svs_set_bank_freq_pct_v2(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
svs_writel_relaxed(svsp,
(svsb->freq_pct[14] << 24) |
(svsb->freq_pct[12] << 16) |
(svsb->freq_pct[10] << 8) |
svsb->freq_pct[8],
FREQPCT74);
svs_writel_relaxed(svsp,
(svsb->freq_pct[6] << 24) |
(svsb->freq_pct[4] << 16) |
(svsb->freq_pct[2] << 8) |
svsb->freq_pct[0],
FREQPCT30);
}
static void svs_set_bank_phase(struct svs_platform *svsp,
enum svsb_phase target_phase)
{
struct svs_bank *svsb = svsp->pbank;
u32 des_char, temp_char, det_char, limit_vals, init2vals, ts_calcs;
svs_switch_bank(svsp);
des_char = (svsb->bdes << 8) | svsb->mdes;
svs_writel_relaxed(svsp, des_char, DESCHAR);
temp_char = (svsb->vco << 16) | (svsb->mtdes << 8) | svsb->dvt_fixed;
svs_writel_relaxed(svsp, temp_char, TEMPCHAR);
det_char = (svsb->dcbdet << 8) | svsb->dcmdet;
svs_writel_relaxed(svsp, det_char, DETCHAR);
svs_writel_relaxed(svsp, svsb->dc_config, DCCONFIG);
svs_writel_relaxed(svsp, svsb->age_config, AGECONFIG);
svs_writel_relaxed(svsp, SVSB_RUNCONFIG_DEFAULT, RUNCONFIG);
svsb->set_freq_pct(svsp);
limit_vals = (svsb->vmax << 24) | (svsb->vmin << 16) |
(SVSB_DTHI << 8) | SVSB_DTLO;
svs_writel_relaxed(svsp, limit_vals, LIMITVALS);
svs_writel_relaxed(svsp, SVSB_DET_WINDOW, DETWINDOW);
svs_writel_relaxed(svsp, SVSB_DET_MAX, CONFIG);
svs_writel_relaxed(svsp, svsb->chk_shift, CHKSHIFT);
svs_writel_relaxed(svsp, svsb->ctl0, CTL0);
svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
switch (target_phase) {
case SVSB_PHASE_INIT01:
svs_writel_relaxed(svsp, svsb->vboot, VBOOT);
svs_writel_relaxed(svsp, SVSB_INTEN_INIT0x, INTEN);
svs_writel_relaxed(svsp, SVSB_EN_INIT01, SVSEN);
break;
case SVSB_PHASE_INIT02:
svs_writel_relaxed(svsp, SVSB_INTEN_INIT0x, INTEN);
init2vals = (svsb->age_voffset_in << 16) | svsb->dc_voffset_in;
svs_writel_relaxed(svsp, init2vals, INIT2VALS);
svs_writel_relaxed(svsp, SVSB_EN_INIT02, SVSEN);
break;
case SVSB_PHASE_MON:
ts_calcs = (svsb->bts << 12) | svsb->mts;
svs_writel_relaxed(svsp, ts_calcs, TSCALCS);
svs_writel_relaxed(svsp, SVSB_INTEN_MONVOPEN, INTEN);
svs_writel_relaxed(svsp, SVSB_EN_MON, SVSEN);
break;
default:
dev_err(svsb->dev, "requested unknown target phase: %u\n",
target_phase);
break;
}
}
static inline void svs_save_bank_register_data(struct svs_platform *svsp,
enum svsb_phase phase)
{
struct svs_bank *svsb = svsp->pbank;
enum svs_reg_index rg_i;
for (rg_i = DESCHAR; rg_i < SVS_REG_MAX; rg_i++)
svsb->reg_data[phase][rg_i] = svs_readl_relaxed(svsp, rg_i);
}
static inline void svs_error_isr_handler(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
dev_err(svsb->dev, "%s: CORESEL = 0x%08x\n",
__func__, svs_readl_relaxed(svsp, CORESEL));
dev_err(svsb->dev, "SVSEN = 0x%08x, INTSTS = 0x%08x\n",
svs_readl_relaxed(svsp, SVSEN),
svs_readl_relaxed(svsp, INTSTS));
dev_err(svsb->dev, "SMSTATE0 = 0x%08x, SMSTATE1 = 0x%08x\n",
svs_readl_relaxed(svsp, SMSTATE0),
svs_readl_relaxed(svsp, SMSTATE1));
dev_err(svsb->dev, "TEMP = 0x%08x\n", svs_readl_relaxed(svsp, TEMP));
svs_save_bank_register_data(svsp, SVSB_PHASE_ERROR);
svsb->phase = SVSB_PHASE_ERROR;
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
}
static inline void svs_init01_isr_handler(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
dev_info(svsb->dev, "%s: VDN74~30:0x%08x~0x%08x, DC:0x%08x\n",
__func__, svs_readl_relaxed(svsp, VDESIGN74),
svs_readl_relaxed(svsp, VDESIGN30),
svs_readl_relaxed(svsp, DCVALUES));
svs_save_bank_register_data(svsp, SVSB_PHASE_INIT01);
svsb->phase = SVSB_PHASE_INIT01;
svsb->dc_voffset_in = ~(svs_readl_relaxed(svsp, DCVALUES) &
GENMASK(15, 0)) + 1;
if (svsb->volt_flags & SVSB_INIT01_VOLT_IGNORE ||
(svsb->dc_voffset_in & SVSB_DC_SIGNED_BIT &&
svsb->volt_flags & SVSB_INIT01_VOLT_INC_ONLY))
svsb->dc_voffset_in = 0;
svsb->age_voffset_in = svs_readl_relaxed(svsp, AGEVALUES) &
GENMASK(15, 0);
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
svs_writel_relaxed(svsp, SVSB_INTSTS_COMPLETE, INTSTS);
svsb->core_sel &= ~SVSB_DET_CLK_EN;
}
static inline void svs_init02_isr_handler(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
dev_info(svsb->dev, "%s: VOP74~30:0x%08x~0x%08x, DC:0x%08x\n",
__func__, svs_readl_relaxed(svsp, VOP74),
svs_readl_relaxed(svsp, VOP30),
svs_readl_relaxed(svsp, DCVALUES));
svs_save_bank_register_data(svsp, SVSB_PHASE_INIT02);
svsb->phase = SVSB_PHASE_INIT02;
svsb->get_volts(svsp);
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
svs_writel_relaxed(svsp, SVSB_INTSTS_COMPLETE, INTSTS);
}
static inline void svs_mon_mode_isr_handler(struct svs_platform *svsp)
{
struct svs_bank *svsb = svsp->pbank;
svs_save_bank_register_data(svsp, SVSB_PHASE_MON);
svsb->phase = SVSB_PHASE_MON;
svsb->get_volts(svsp);
svsb->temp = svs_readl_relaxed(svsp, TEMP) & GENMASK(7, 0);
svs_writel_relaxed(svsp, SVSB_INTSTS_MONVOP, INTSTS);
}
static irqreturn_t svs_isr(int irq, void *data)
{
struct svs_platform *svsp = data;
struct svs_bank *svsb = NULL;
unsigned long flags;
u32 idx, int_sts, svs_en;
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
WARN(!svsb, "%s: svsb(%s) is null", __func__, svsb->name);
spin_lock_irqsave(&svs_lock, flags);
svsp->pbank = svsb;
/* Find out which svs bank fires interrupt */
if (svsb->int_st & svs_readl_relaxed(svsp, INTST)) {
spin_unlock_irqrestore(&svs_lock, flags);
continue;
}
svs_switch_bank(svsp);
int_sts = svs_readl_relaxed(svsp, INTSTS);
svs_en = svs_readl_relaxed(svsp, SVSEN);
if (int_sts == SVSB_INTSTS_COMPLETE &&
svs_en == SVSB_EN_INIT01)
svs_init01_isr_handler(svsp);
else if (int_sts == SVSB_INTSTS_COMPLETE &&
svs_en == SVSB_EN_INIT02)
svs_init02_isr_handler(svsp);
else if (int_sts & SVSB_INTSTS_MONVOP)
svs_mon_mode_isr_handler(svsp);
else
svs_error_isr_handler(svsp);
spin_unlock_irqrestore(&svs_lock, flags);
break;
}
svs_adjust_pm_opp_volts(svsb);
if (svsb->phase == SVSB_PHASE_INIT01 ||
svsb->phase == SVSB_PHASE_INIT02)
complete(&svsb->init_completion);
return IRQ_HANDLED;
}
static int svs_init01(struct svs_platform *svsp)
{
struct svs_bank *svsb;
unsigned long flags, time_left;
bool search_done;
int ret = 0, r;
u32 opp_freq, opp_vboot, buck_volt, idx, i;
/* Keep CPUs' core power on for svs_init01 initialization */
cpuidle_pause_and_lock();
/* Svs bank init01 preparation - power enable */
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_INIT01))
continue;
ret = regulator_enable(svsb->buck);
if (ret) {
dev_err(svsb->dev, "%s enable fail: %d\n",
svsb->buck_name, ret);
goto svs_init01_resume_cpuidle;
}
/* Some buck doesn't support mode change. Show fail msg only */
ret = regulator_set_mode(svsb->buck, REGULATOR_MODE_FAST);
if (ret)
dev_notice(svsb->dev, "set fast mode fail: %d\n", ret);
if (svsb->volt_flags & SVSB_INIT01_PD_REQ) {
if (!pm_runtime_enabled(svsb->opp_dev)) {
pm_runtime_enable(svsb->opp_dev);
svsb->pm_runtime_enabled_count++;
}
ret = pm_runtime_get_sync(svsb->opp_dev);
if (ret < 0) {
dev_err(svsb->dev, "mtcmos on fail: %d\n", ret);
goto svs_init01_resume_cpuidle;
}
}
}
/*
* Svs bank init01 preparation - vboot voltage adjustment
* Sometimes two svs banks use the same buck. Therefore,
* we have to set each svs bank to target voltage(vboot) first.
*/
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_INIT01))
continue;
/*
* Find the fastest freq that can be run at vboot and
* fix to that freq until svs_init01 is done.
*/
search_done = false;
opp_vboot = svs_bank_volt_to_opp_volt(svsb->vboot,
svsb->volt_step,
svsb->volt_base);
for (i = 0; i < svsb->opp_count; i++) {
opp_freq = svsb->opp_dfreq[i];
if (!search_done && svsb->opp_dvolt[i] <= opp_vboot) {
ret = dev_pm_opp_adjust_voltage(svsb->opp_dev,
opp_freq,
opp_vboot,
opp_vboot,
opp_vboot);
if (ret) {
dev_err(svsb->dev,
"set opp %uuV vboot fail: %d\n",
opp_vboot, ret);
goto svs_init01_finish;
}
search_done = true;
} else {
ret = dev_pm_opp_disable(svsb->opp_dev,
svsb->opp_dfreq[i]);
if (ret) {
dev_err(svsb->dev,
"opp %uHz disable fail: %d\n",
svsb->opp_dfreq[i], ret);
goto svs_init01_finish;
}
}
}
}
/* Svs bank init01 begins */
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_INIT01))
continue;
opp_vboot = svs_bank_volt_to_opp_volt(svsb->vboot,
svsb->volt_step,
svsb->volt_base);
buck_volt = regulator_get_voltage(svsb->buck);
if (buck_volt != opp_vboot) {
dev_err(svsb->dev,
"buck voltage: %uuV, expected vboot: %uuV\n",
buck_volt, opp_vboot);
ret = -EPERM;
goto svs_init01_finish;
}
spin_lock_irqsave(&svs_lock, flags);
svsp->pbank = svsb;
svs_set_bank_phase(svsp, SVSB_PHASE_INIT01);
spin_unlock_irqrestore(&svs_lock, flags);
time_left = wait_for_completion_timeout(&svsb->init_completion,
msecs_to_jiffies(5000));
if (!time_left) {
dev_err(svsb->dev, "init01 completion timeout\n");
ret = -EBUSY;
goto svs_init01_finish;
}
}
svs_init01_finish:
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_INIT01))
continue;
for (i = 0; i < svsb->opp_count; i++) {
r = dev_pm_opp_enable(svsb->opp_dev,
svsb->opp_dfreq[i]);
if (r)
dev_err(svsb->dev, "opp %uHz enable fail: %d\n",
svsb->opp_dfreq[i], r);
}
if (svsb->volt_flags & SVSB_INIT01_PD_REQ) {
r = pm_runtime_put_sync(svsb->opp_dev);
if (r)
dev_err(svsb->dev, "mtcmos off fail: %d\n", r);
if (svsb->pm_runtime_enabled_count > 0) {
pm_runtime_disable(svsb->opp_dev);
svsb->pm_runtime_enabled_count--;
}
}
r = regulator_set_mode(svsb->buck, REGULATOR_MODE_NORMAL);
if (r)
dev_notice(svsb->dev, "set normal mode fail: %d\n", r);
r = regulator_disable(svsb->buck);
if (r)
dev_err(svsb->dev, "%s disable fail: %d\n",
svsb->buck_name, r);
}
svs_init01_resume_cpuidle:
cpuidle_resume_and_unlock();
return ret;
}
static int svs_init02(struct svs_platform *svsp)
{
struct svs_bank *svsb;
unsigned long flags, time_left;
u32 idx;
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_INIT02))
continue;
reinit_completion(&svsb->init_completion);
spin_lock_irqsave(&svs_lock, flags);
svsp->pbank = svsb;
svs_set_bank_phase(svsp, SVSB_PHASE_INIT02);
spin_unlock_irqrestore(&svs_lock, flags);
time_left = wait_for_completion_timeout(&svsb->init_completion,
msecs_to_jiffies(5000));
if (!time_left) {
dev_err(svsb->dev, "init02 completion timeout\n");
return -EBUSY;
}
}
/*
* 2-line high/low bank update its corresponding opp voltages only.
* Therefore, we sync voltages from opp for high/low bank voltages
* consistency.
*/
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_INIT02))
continue;
if (svsb->type == SVSB_HIGH || svsb->type == SVSB_LOW) {
if (svs_sync_bank_volts_from_opp(svsb)) {
dev_err(svsb->dev, "sync volt fail\n");
return -EPERM;
}
}
}
return 0;
}
static void svs_mon_mode(struct svs_platform *svsp)
{
struct svs_bank *svsb;
unsigned long flags;
u32 idx;
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (!(svsb->mode_support & SVSB_MODE_MON))
continue;
spin_lock_irqsave(&svs_lock, flags);
svsp->pbank = svsb;
svs_set_bank_phase(svsp, SVSB_PHASE_MON);
spin_unlock_irqrestore(&svs_lock, flags);
}
}
static int svs_start(struct svs_platform *svsp)
{
int ret;
ret = svs_init01(svsp);
if (ret)
return ret;
ret = svs_init02(svsp);
if (ret)
return ret;
svs_mon_mode(svsp);
return 0;
}
static int svs_suspend(struct device *dev)
{
struct svs_platform *svsp = dev_get_drvdata(dev);
struct svs_bank *svsb;
unsigned long flags;
int ret;
u32 idx;
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
/* This might wait for svs_isr() process */
spin_lock_irqsave(&svs_lock, flags);
svsp->pbank = svsb;
svs_switch_bank(svsp);
svs_writel_relaxed(svsp, SVSB_EN_OFF, SVSEN);
svs_writel_relaxed(svsp, SVSB_INTSTS_CLEAN, INTSTS);
spin_unlock_irqrestore(&svs_lock, flags);
svsb->phase = SVSB_PHASE_ERROR;
svs_adjust_pm_opp_volts(svsb);
}
ret = reset_control_assert(svsp->rst);
if (ret) {
dev_err(svsp->dev, "cannot assert reset %d\n", ret);
return ret;
}
clk_disable_unprepare(svsp->main_clk);
return 0;
}
static int svs_resume(struct device *dev)
{
struct svs_platform *svsp = dev_get_drvdata(dev);
int ret;
ret = clk_prepare_enable(svsp->main_clk);
if (ret) {
dev_err(svsp->dev, "cannot enable main_clk, disable svs\n");
return ret;
}
ret = reset_control_deassert(svsp->rst);
if (ret) {
dev_err(svsp->dev, "cannot deassert reset %d\n", ret);
goto out_of_resume;
}
ret = svs_init02(svsp);
if (ret)
goto out_of_resume;
svs_mon_mode(svsp);
return 0;
out_of_resume:
clk_disable_unprepare(svsp->main_clk);
return ret;
}
static int svs_bank_resource_setup(struct svs_platform *svsp)
{
struct svs_bank *svsb;
struct dev_pm_opp *opp;
unsigned long freq;
int count, ret;
u32 idx, i;
dev_set_drvdata(svsp->dev, svsp);
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
switch (svsb->sw_id) {
case SVSB_CPU_LITTLE:
svsb->name = "SVSB_CPU_LITTLE";
break;
case SVSB_CPU_BIG:
svsb->name = "SVSB_CPU_BIG";
break;
case SVSB_CCI:
svsb->name = "SVSB_CCI";
break;
case SVSB_GPU:
if (svsb->type == SVSB_HIGH)
svsb->name = "SVSB_GPU_HIGH";
else if (svsb->type == SVSB_LOW)
svsb->name = "SVSB_GPU_LOW";
else
svsb->name = "SVSB_GPU";
break;
default:
dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id);
return -EINVAL;
}
svsb->dev = devm_kzalloc(svsp->dev, sizeof(*svsb->dev),
GFP_KERNEL);
if (!svsb->dev)
return -ENOMEM;
ret = dev_set_name(svsb->dev, "%s", svsb->name);
if (ret)
return ret;
dev_set_drvdata(svsb->dev, svsp);
ret = dev_pm_opp_of_add_table(svsb->opp_dev);
if (ret) {
dev_err(svsb->dev, "add opp table fail: %d\n", ret);
return ret;
}
mutex_init(&svsb->lock);
init_completion(&svsb->init_completion);
if (svsb->mode_support & SVSB_MODE_INIT01) {
svsb->buck = devm_regulator_get_optional(svsb->opp_dev,
svsb->buck_name);
if (IS_ERR(svsb->buck)) {
dev_err(svsb->dev, "cannot get \"%s-supply\"\n",
svsb->buck_name);
return PTR_ERR(svsb->buck);
}
}
if (svsb->mode_support & SVSB_MODE_MON) {
svsb->tzd = thermal_zone_get_zone_by_name(svsb->tzone_name);
if (IS_ERR(svsb->tzd)) {
dev_err(svsb->dev, "cannot get \"%s\" thermal zone\n",
svsb->tzone_name);
return PTR_ERR(svsb->tzd);
}
}
count = dev_pm_opp_get_opp_count(svsb->opp_dev);
if (svsb->opp_count != count) {
dev_err(svsb->dev,
"opp_count not \"%u\" but get \"%d\"?\n",
svsb->opp_count, count);
return count;
}
for (i = 0, freq = U32_MAX; i < svsb->opp_count; i++, freq--) {
opp = dev_pm_opp_find_freq_floor(svsb->opp_dev, &freq);
if (IS_ERR(opp)) {
dev_err(svsb->dev, "cannot find freq = %ld\n",
PTR_ERR(opp));
return PTR_ERR(opp);
}
svsb->opp_dfreq[i] = freq;
svsb->opp_dvolt[i] = dev_pm_opp_get_voltage(opp);
svsb->freq_pct[i] = percent(svsb->opp_dfreq[i],
svsb->freq_base);
dev_pm_opp_put(opp);
}
}
return 0;
}
static bool svs_mt8192_efuse_parsing(struct svs_platform *svsp)
{
struct svs_bank *svsb;
struct nvmem_cell *cell;
u32 idx, i, vmin, golden_temp;
for (i = 0; i < svsp->efuse_max; i++)
if (svsp->efuse[i])
dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n",
i, svsp->efuse[i]);
if (!svsp->efuse[9]) {
dev_notice(svsp->dev, "svs_efuse[9] = 0x0?\n");
return false;
}
/* Svs efuse parsing */
vmin = (svsp->efuse[19] >> 4) & GENMASK(1, 0);
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (vmin == 0x1)
svsb->vmin = 0x1e;
if (svsb->type == SVSB_LOW) {
svsb->mtdes = svsp->efuse[10] & GENMASK(7, 0);
svsb->bdes = (svsp->efuse[10] >> 16) & GENMASK(7, 0);
svsb->mdes = (svsp->efuse[10] >> 24) & GENMASK(7, 0);
svsb->dcbdet = (svsp->efuse[17]) & GENMASK(7, 0);
svsb->dcmdet = (svsp->efuse[17] >> 8) & GENMASK(7, 0);
} else if (svsb->type == SVSB_HIGH) {
svsb->mtdes = svsp->efuse[9] & GENMASK(7, 0);
svsb->bdes = (svsp->efuse[9] >> 16) & GENMASK(7, 0);
svsb->mdes = (svsp->efuse[9] >> 24) & GENMASK(7, 0);
svsb->dcbdet = (svsp->efuse[17] >> 16) & GENMASK(7, 0);
svsb->dcmdet = (svsp->efuse[17] >> 24) & GENMASK(7, 0);
}
svsb->vmax += svsb->dvt_fixed;
}
/* Thermal efuse parsing */
cell = nvmem_cell_get(svsp->dev, "t-calibration-data");
if (IS_ERR_OR_NULL(cell)) {
dev_err(svsp->dev, "no \"t-calibration-data\"? %ld\n",
PTR_ERR(cell));
return false;
}
svsp->tefuse = nvmem_cell_read(cell, &svsp->tefuse_max);
if (IS_ERR(svsp->tefuse)) {
dev_err(svsp->dev, "cannot read thermal efuse: %ld\n",
PTR_ERR(svsp->tefuse));
nvmem_cell_put(cell);
return false;
}
svsp->tefuse_max /= sizeof(u32);
nvmem_cell_put(cell);
for (i = 0; i < svsp->tefuse_max; i++)
if (svsp->tefuse[i] != 0)
break;
if (i == svsp->tefuse_max)
golden_temp = 50; /* All thermal efuse data are 0 */
else
golden_temp = (svsp->tefuse[0] >> 24) & GENMASK(7, 0);
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
svsb->mts = 500;
svsb->bts = (((500 * golden_temp + 250460) / 1000) - 25) * 4;
}
return true;
}
static bool svs_mt8183_efuse_parsing(struct svs_platform *svsp)
{
struct svs_bank *svsb;
struct nvmem_cell *cell;
int format[6], x_roomt[6], o_vtsmcu[5], o_vtsabb, tb_roomt = 0;
int adc_ge_t, adc_oe_t, ge, oe, gain, degc_cali, adc_cali_en_t;
int o_slope, o_slope_sign, ts_id;
u32 idx, i, ft_pgm, mts, temp0, temp1, temp2;
for (i = 0; i < svsp->efuse_max; i++)
if (svsp->efuse[i])
dev_info(svsp->dev, "M_HW_RES%d: 0x%08x\n",
i, svsp->efuse[i]);
if (!svsp->efuse[2]) {
dev_notice(svsp->dev, "svs_efuse[2] = 0x0?\n");
return false;
}
/* Svs efuse parsing */
ft_pgm = (svsp->efuse[0] >> 4) & GENMASK(3, 0);
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (ft_pgm <= 1)
svsb->volt_flags |= SVSB_INIT01_VOLT_IGNORE;
switch (svsb->sw_id) {
case SVSB_CPU_LITTLE:
svsb->bdes = svsp->efuse[16] & GENMASK(7, 0);
svsb->mdes = (svsp->efuse[16] >> 8) & GENMASK(7, 0);
svsb->dcbdet = (svsp->efuse[16] >> 16) & GENMASK(7, 0);
svsb->dcmdet = (svsp->efuse[16] >> 24) & GENMASK(7, 0);
svsb->mtdes = (svsp->efuse[17] >> 16) & GENMASK(7, 0);
if (ft_pgm <= 3)
svsb->volt_od += 10;
else
svsb->volt_od += 2;
break;
case SVSB_CPU_BIG:
svsb->bdes = svsp->efuse[18] & GENMASK(7, 0);
svsb->mdes = (svsp->efuse[18] >> 8) & GENMASK(7, 0);
svsb->dcbdet = (svsp->efuse[18] >> 16) & GENMASK(7, 0);
svsb->dcmdet = (svsp->efuse[18] >> 24) & GENMASK(7, 0);
svsb->mtdes = svsp->efuse[17] & GENMASK(7, 0);
if (ft_pgm <= 3)
svsb->volt_od += 15;
else
svsb->volt_od += 12;
break;
case SVSB_CCI:
svsb->bdes = svsp->efuse[4] & GENMASK(7, 0);
svsb->mdes = (svsp->efuse[4] >> 8) & GENMASK(7, 0);
svsb->dcbdet = (svsp->efuse[4] >> 16) & GENMASK(7, 0);
svsb->dcmdet = (svsp->efuse[4] >> 24) & GENMASK(7, 0);
svsb->mtdes = (svsp->efuse[5] >> 16) & GENMASK(7, 0);
if (ft_pgm <= 3)
svsb->volt_od += 10;
else
svsb->volt_od += 2;
break;
case SVSB_GPU:
svsb->bdes = svsp->efuse[6] & GENMASK(7, 0);
svsb->mdes = (svsp->efuse[6] >> 8) & GENMASK(7, 0);
svsb->dcbdet = (svsp->efuse[6] >> 16) & GENMASK(7, 0);
svsb->dcmdet = (svsp->efuse[6] >> 24) & GENMASK(7, 0);
svsb->mtdes = svsp->efuse[5] & GENMASK(7, 0);
if (ft_pgm >= 2) {
svsb->freq_base = 800000000; /* 800MHz */
svsb->dvt_fixed = 2;
}
break;
default:
dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id);
return false;
}
}
/* Get thermal efuse by nvmem */
cell = nvmem_cell_get(svsp->dev, "t-calibration-data");
if (IS_ERR(cell)) {
dev_err(svsp->dev, "no \"t-calibration-data\"? %ld\n",
PTR_ERR(cell));
goto remove_mt8183_svsb_mon_mode;
}
svsp->tefuse = nvmem_cell_read(cell, &svsp->tefuse_max);
if (IS_ERR(svsp->tefuse)) {
dev_err(svsp->dev, "cannot read thermal efuse: %ld\n",
PTR_ERR(svsp->tefuse));
nvmem_cell_put(cell);
goto remove_mt8183_svsb_mon_mode;
}
svsp->tefuse_max /= sizeof(u32);
nvmem_cell_put(cell);
/* Thermal efuse parsing */
adc_ge_t = (svsp->tefuse[1] >> 22) & GENMASK(9, 0);
adc_oe_t = (svsp->tefuse[1] >> 12) & GENMASK(9, 0);
o_vtsmcu[0] = (svsp->tefuse[0] >> 17) & GENMASK(8, 0);
o_vtsmcu[1] = (svsp->tefuse[0] >> 8) & GENMASK(8, 0);
o_vtsmcu[2] = svsp->tefuse[1] & GENMASK(8, 0);
o_vtsmcu[3] = (svsp->tefuse[2] >> 23) & GENMASK(8, 0);
o_vtsmcu[4] = (svsp->tefuse[2] >> 5) & GENMASK(8, 0);
o_vtsabb = (svsp->tefuse[2] >> 14) & GENMASK(8, 0);
degc_cali = (svsp->tefuse[0] >> 1) & GENMASK(5, 0);
adc_cali_en_t = svsp->tefuse[0] & BIT(0);
o_slope_sign = (svsp->tefuse[0] >> 7) & BIT(0);
ts_id = (svsp->tefuse[1] >> 9) & BIT(0);
o_slope = (svsp->tefuse[0] >> 26) & GENMASK(5, 0);
if (adc_cali_en_t == 1) {
if (!ts_id)
o_slope = 0;
if (adc_ge_t < 265 || adc_ge_t > 758 ||
adc_oe_t < 265 || adc_oe_t > 758 ||
o_vtsmcu[0] < -8 || o_vtsmcu[0] > 484 ||
o_vtsmcu[1] < -8 || o_vtsmcu[1] > 484 ||
o_vtsmcu[2] < -8 || o_vtsmcu[2] > 484 ||
o_vtsmcu[3] < -8 || o_vtsmcu[3] > 484 ||
o_vtsmcu[4] < -8 || o_vtsmcu[4] > 484 ||
o_vtsabb < -8 || o_vtsabb > 484 ||
degc_cali < 1 || degc_cali > 63) {
dev_err(svsp->dev, "bad thermal efuse, no mon mode\n");
goto remove_mt8183_svsb_mon_mode;
}
} else {
dev_err(svsp->dev, "no thermal efuse, no mon mode\n");
goto remove_mt8183_svsb_mon_mode;
}
ge = ((adc_ge_t - 512) * 10000) / 4096;
oe = (adc_oe_t - 512);
gain = (10000 + ge);
format[0] = (o_vtsmcu[0] + 3350 - oe);
format[1] = (o_vtsmcu[1] + 3350 - oe);
format[2] = (o_vtsmcu[2] + 3350 - oe);
format[3] = (o_vtsmcu[3] + 3350 - oe);
format[4] = (o_vtsmcu[4] + 3350 - oe);
format[5] = (o_vtsabb + 3350 - oe);
for (i = 0; i < 6; i++)
x_roomt[i] = (((format[i] * 10000) / 4096) * 10000) / gain;
temp0 = (10000 * 100000 / gain) * 15 / 18;
if (!o_slope_sign)
mts = (temp0 * 10) / (1534 + o_slope * 10);
else
mts = (temp0 * 10) / (1534 - o_slope * 10);
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
svsb->mts = mts;
switch (svsb->sw_id) {
case SVSB_CPU_LITTLE:
tb_roomt = x_roomt[3];
break;
case SVSB_CPU_BIG:
tb_roomt = x_roomt[4];
break;
case SVSB_CCI:
tb_roomt = x_roomt[3];
break;
case SVSB_GPU:
tb_roomt = x_roomt[1];
break;
default:
dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id);
goto remove_mt8183_svsb_mon_mode;
}
temp0 = (degc_cali * 10 / 2);
temp1 = ((10000 * 100000 / 4096 / gain) *
oe + tb_roomt * 10) * 15 / 18;
if (!o_slope_sign)
temp2 = temp1 * 100 / (1534 + o_slope * 10);
else
temp2 = temp1 * 100 / (1534 - o_slope * 10);
svsb->bts = (temp0 + temp2 - 250) * 4 / 10;
}
return true;
remove_mt8183_svsb_mon_mode:
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
svsb->mode_support &= ~SVSB_MODE_MON;
}
return true;
}
static bool svs_is_efuse_data_correct(struct svs_platform *svsp)
{
struct nvmem_cell *cell;
/* Get svs efuse by nvmem */
cell = nvmem_cell_get(svsp->dev, "svs-calibration-data");
if (IS_ERR(cell)) {
dev_err(svsp->dev, "no \"svs-calibration-data\"? %ld\n",
PTR_ERR(cell));
return false;
}
svsp->efuse = nvmem_cell_read(cell, &svsp->efuse_max);
if (IS_ERR(svsp->efuse)) {
dev_err(svsp->dev, "cannot read svs efuse: %ld\n",
PTR_ERR(svsp->efuse));
nvmem_cell_put(cell);
return false;
}
svsp->efuse_max /= sizeof(u32);
nvmem_cell_put(cell);
return svsp->efuse_parsing(svsp);
}
static struct device *svs_get_subsys_device(struct svs_platform *svsp,
const char *node_name)
{
struct platform_device *pdev;
struct device_node *np;
np = of_find_node_by_name(NULL, node_name);
if (!np) {
dev_err(svsp->dev, "cannot find %s node\n", node_name);
return ERR_PTR(-ENODEV);
}
pdev = of_find_device_by_node(np);
if (!pdev) {
of_node_put(np);
dev_err(svsp->dev, "cannot find pdev by %s\n", node_name);
return ERR_PTR(-ENXIO);
}
of_node_put(np);
return &pdev->dev;
}
static struct device *svs_add_device_link(struct svs_platform *svsp,
const char *node_name)
{
struct device *dev;
struct device_link *sup_link;
if (!node_name) {
dev_err(svsp->dev, "node name cannot be null\n");
return ERR_PTR(-EINVAL);
}
dev = svs_get_subsys_device(svsp, node_name);
if (IS_ERR(dev))
return dev;
sup_link = device_link_add(svsp->dev, dev,
DL_FLAG_AUTOREMOVE_CONSUMER);
if (!sup_link) {
dev_err(svsp->dev, "sup_link is NULL\n");
return ERR_PTR(-EINVAL);
}
if (sup_link->supplier->links.status != DL_DEV_DRIVER_BOUND)
return ERR_PTR(-EPROBE_DEFER);
return dev;
}
static int svs_mt8192_platform_probe(struct svs_platform *svsp)
{
struct device *dev;
struct svs_bank *svsb;
u32 idx;
svsp->rst = devm_reset_control_get_optional(svsp->dev, "svs_rst");
if (IS_ERR(svsp->rst))
return dev_err_probe(svsp->dev, PTR_ERR(svsp->rst),
"cannot get svs reset control\n");
dev = svs_add_device_link(svsp, "lvts");
if (IS_ERR(dev))
return dev_err_probe(svsp->dev, PTR_ERR(dev),
"failed to get lvts device\n");
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
if (svsb->type == SVSB_HIGH)
svsb->opp_dev = svs_add_device_link(svsp, "mali");
else if (svsb->type == SVSB_LOW)
svsb->opp_dev = svs_get_subsys_device(svsp, "mali");
if (IS_ERR(svsb->opp_dev))
return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev),
"failed to get OPP device for bank %d\n",
idx);
}
return 0;
}
static int svs_mt8183_platform_probe(struct svs_platform *svsp)
{
struct device *dev;
struct svs_bank *svsb;
u32 idx;
dev = svs_add_device_link(svsp, "thermal");
if (IS_ERR(dev))
return dev_err_probe(svsp->dev, PTR_ERR(dev),
"failed to get thermal device\n");
for (idx = 0; idx < svsp->bank_max; idx++) {
svsb = &svsp->banks[idx];
switch (svsb->sw_id) {
case SVSB_CPU_LITTLE:
case SVSB_CPU_BIG:
svsb->opp_dev = get_cpu_device(svsb->cpu_id);
break;
case SVSB_CCI:
svsb->opp_dev = svs_add_device_link(svsp, "cci");
break;
case SVSB_GPU:
svsb->opp_dev = svs_add_device_link(svsp, "gpu");
break;
default:
dev_err(svsb->dev, "unknown sw_id: %u\n", svsb->sw_id);
return -EINVAL;
}
if (IS_ERR(svsb->opp_dev))
return dev_err_probe(svsp->dev, PTR_ERR(svsb->opp_dev),
"failed to get OPP device for bank %d\n",
idx);
}
return 0;
}
static struct svs_bank svs_mt8192_banks[] = {
{
.sw_id = SVSB_GPU,
.type = SVSB_LOW,
.set_freq_pct = svs_set_bank_freq_pct_v3,
.get_volts = svs_get_bank_volts_v3,
.volt_flags = SVSB_REMOVE_DVTFIXED_VOLT,
.mode_support = SVSB_MODE_INIT02,
.opp_count = MAX_OPP_ENTRIES,
.freq_base = 688000000,
.turn_freq_base = 688000000,
.volt_step = 6250,
.volt_base = 400000,
.vmax = 0x60,
.vmin = 0x1a,
.age_config = 0x555555,
.dc_config = 0x1,
.dvt_fixed = 0x1,
.vco = 0x18,
.chk_shift = 0x87,
.core_sel = 0x0fff0100,
.int_st = BIT(0),
.ctl0 = 0x00540003,
},
{
.sw_id = SVSB_GPU,
.type = SVSB_HIGH,
.set_freq_pct = svs_set_bank_freq_pct_v3,
.get_volts = svs_get_bank_volts_v3,
.tzone_name = "gpu1",
.volt_flags = SVSB_REMOVE_DVTFIXED_VOLT |
SVSB_MON_VOLT_IGNORE,
.mode_support = SVSB_MODE_INIT02 | SVSB_MODE_MON,
.opp_count = MAX_OPP_ENTRIES,
.freq_base = 902000000,
.turn_freq_base = 688000000,
.volt_step = 6250,
.volt_base = 400000,
.vmax = 0x60,
.vmin = 0x1a,
.age_config = 0x555555,
.dc_config = 0x1,
.dvt_fixed = 0x6,
.vco = 0x18,
.chk_shift = 0x87,
.core_sel = 0x0fff0101,
.int_st = BIT(1),
.ctl0 = 0x00540003,
.tzone_htemp = 85000,
.tzone_htemp_voffset = 0,
.tzone_ltemp = 25000,
.tzone_ltemp_voffset = 7,
},
};
static struct svs_bank svs_mt8183_banks[] = {
{
.sw_id = SVSB_CPU_LITTLE,
.set_freq_pct = svs_set_bank_freq_pct_v2,
.get_volts = svs_get_bank_volts_v2,
.cpu_id = 0,
.buck_name = "proc",
.volt_flags = SVSB_INIT01_VOLT_INC_ONLY,
.mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02,
.opp_count = MAX_OPP_ENTRIES,
.freq_base = 1989000000,
.vboot = 0x30,
.volt_step = 6250,
.volt_base = 500000,
.vmax = 0x64,
.vmin = 0x18,
.age_config = 0x555555,
.dc_config = 0x555555,
.dvt_fixed = 0x7,
.vco = 0x10,
.chk_shift = 0x77,
.core_sel = 0x8fff0000,
.int_st = BIT(0),
.ctl0 = 0x00010001,
},
{
.sw_id = SVSB_CPU_BIG,
.set_freq_pct = svs_set_bank_freq_pct_v2,
.get_volts = svs_get_bank_volts_v2,
.cpu_id = 4,
.buck_name = "proc",
.volt_flags = SVSB_INIT01_VOLT_INC_ONLY,
.mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02,
.opp_count = MAX_OPP_ENTRIES,
.freq_base = 1989000000,
.vboot = 0x30,
.volt_step = 6250,
.volt_base = 500000,
.vmax = 0x58,
.vmin = 0x10,
.age_config = 0x555555,
.dc_config = 0x555555,
.dvt_fixed = 0x7,
.vco = 0x10,
.chk_shift = 0x77,
.core_sel = 0x8fff0001,
.int_st = BIT(1),
.ctl0 = 0x00000001,
},
{
.sw_id = SVSB_CCI,
.set_freq_pct = svs_set_bank_freq_pct_v2,
.get_volts = svs_get_bank_volts_v2,
.buck_name = "proc",
.volt_flags = SVSB_INIT01_VOLT_INC_ONLY,
.mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02,
.opp_count = MAX_OPP_ENTRIES,
.freq_base = 1196000000,
.vboot = 0x30,
.volt_step = 6250,
.volt_base = 500000,
.vmax = 0x64,
.vmin = 0x18,
.age_config = 0x555555,
.dc_config = 0x555555,
.dvt_fixed = 0x7,
.vco = 0x10,
.chk_shift = 0x77,
.core_sel = 0x8fff0002,
.int_st = BIT(2),
.ctl0 = 0x00100003,
},
{
.sw_id = SVSB_GPU,
.set_freq_pct = svs_set_bank_freq_pct_v2,
.get_volts = svs_get_bank_volts_v2,
.buck_name = "mali",
.tzone_name = "tzts2",
.volt_flags = SVSB_INIT01_PD_REQ |
SVSB_INIT01_VOLT_INC_ONLY,
.mode_support = SVSB_MODE_INIT01 | SVSB_MODE_INIT02 |
SVSB_MODE_MON,
.opp_count = MAX_OPP_ENTRIES,
.freq_base = 900000000,
.vboot = 0x30,
.volt_step = 6250,
.volt_base = 500000,
.vmax = 0x40,
.vmin = 0x14,
.age_config = 0x555555,
.dc_config = 0x555555,
.dvt_fixed = 0x3,
.vco = 0x10,
.chk_shift = 0x77,
.core_sel = 0x8fff0003,
.int_st = BIT(3),
.ctl0 = 0x00050001,
.tzone_htemp = 85000,
.tzone_htemp_voffset = 0,
.tzone_ltemp = 25000,
.tzone_ltemp_voffset = 3,
},
};
static const struct svs_platform_data svs_mt8192_platform_data = {
.name = "mt8192-svs",
.banks = svs_mt8192_banks,
.efuse_parsing = svs_mt8192_efuse_parsing,
.probe = svs_mt8192_platform_probe,
.irqflags = IRQF_TRIGGER_HIGH,
.regs = svs_regs_v2,
.bank_max = ARRAY_SIZE(svs_mt8192_banks),
};
static const struct svs_platform_data svs_mt8183_platform_data = {
.name = "mt8183-svs",
.banks = svs_mt8183_banks,
.efuse_parsing = svs_mt8183_efuse_parsing,
.probe = svs_mt8183_platform_probe,
.irqflags = IRQF_TRIGGER_LOW,
.regs = svs_regs_v2,
.bank_max = ARRAY_SIZE(svs_mt8183_banks),
};
static const struct of_device_id svs_of_match[] = {
{
.compatible = "mediatek,mt8192-svs",
.data = &svs_mt8192_platform_data,
}, {
.compatible = "mediatek,mt8183-svs",
.data = &svs_mt8183_platform_data,
}, {
/* Sentinel */
},
};
static struct svs_platform *svs_platform_probe(struct platform_device *pdev)
{
struct svs_platform *svsp;
const struct svs_platform_data *svsp_data;
int ret;
svsp_data = of_device_get_match_data(&pdev->dev);
if (!svsp_data) {
dev_err(&pdev->dev, "no svs platform data?\n");
return ERR_PTR(-EPERM);
}
svsp = devm_kzalloc(&pdev->dev, sizeof(*svsp), GFP_KERNEL);
if (!svsp)
return ERR_PTR(-ENOMEM);
svsp->dev = &pdev->dev;
svsp->name = svsp_data->name;
svsp->banks = svsp_data->banks;
svsp->efuse_parsing = svsp_data->efuse_parsing;
svsp->probe = svsp_data->probe;
svsp->irqflags = svsp_data->irqflags;
svsp->regs = svsp_data->regs;
svsp->bank_max = svsp_data->bank_max;
ret = svsp->probe(svsp);
if (ret)
return ERR_PTR(ret);
return svsp;
}
static int svs_probe(struct platform_device *pdev)
{
struct svs_platform *svsp;
unsigned int svsp_irq;
int ret;
svsp = svs_platform_probe(pdev);
if (IS_ERR(svsp))
return PTR_ERR(svsp);
if (!svs_is_efuse_data_correct(svsp)) {
dev_notice(svsp->dev, "efuse data isn't correct\n");
ret = -EPERM;
goto svs_probe_free_resource;
}
ret = svs_bank_resource_setup(svsp);
if (ret) {
dev_err(svsp->dev, "svs bank resource setup fail: %d\n", ret);
goto svs_probe_free_resource;
}
svsp_irq = irq_of_parse_and_map(svsp->dev->of_node, 0);
ret = devm_request_threaded_irq(svsp->dev, svsp_irq, NULL, svs_isr,
svsp->irqflags | IRQF_ONESHOT,
svsp->name, svsp);
if (ret) {
dev_err(svsp->dev, "register irq(%d) failed: %d\n",
svsp_irq, ret);
goto svs_probe_free_resource;
}
svsp->main_clk = devm_clk_get(svsp->dev, "main");
if (IS_ERR(svsp->main_clk)) {
dev_err(svsp->dev, "failed to get clock: %ld\n",
PTR_ERR(svsp->main_clk));
ret = PTR_ERR(svsp->main_clk);
goto svs_probe_free_resource;
}
ret = clk_prepare_enable(svsp->main_clk);
if (ret) {
dev_err(svsp->dev, "cannot enable main clk: %d\n", ret);
goto svs_probe_free_resource;
}
svsp->base = of_iomap(svsp->dev->of_node, 0);
if (IS_ERR_OR_NULL(svsp->base)) {
dev_err(svsp->dev, "cannot find svs register base\n");
ret = -EINVAL;
goto svs_probe_clk_disable;
}
ret = svs_start(svsp);
if (ret) {
dev_err(svsp->dev, "svs start fail: %d\n", ret);
goto svs_probe_iounmap;
}
ret = svs_create_debug_cmds(svsp);
if (ret) {
dev_err(svsp->dev, "svs create debug cmds fail: %d\n", ret);
goto svs_probe_iounmap;
}
return 0;
svs_probe_iounmap:
iounmap(svsp->base);
svs_probe_clk_disable:
clk_disable_unprepare(svsp->main_clk);
svs_probe_free_resource:
if (!IS_ERR_OR_NULL(svsp->efuse))
kfree(svsp->efuse);
if (!IS_ERR_OR_NULL(svsp->tefuse))
kfree(svsp->tefuse);
return ret;
}
static DEFINE_SIMPLE_DEV_PM_OPS(svs_pm_ops, svs_suspend, svs_resume);
static struct platform_driver svs_driver = {
.probe = svs_probe,
.driver = {
.name = "mtk-svs",
.pm = &svs_pm_ops,
.of_match_table = of_match_ptr(svs_of_match),
},
};
module_platform_driver(svs_driver);
MODULE_AUTHOR("Roger Lu <roger.lu@mediatek.com>");
MODULE_DESCRIPTION("MediaTek SVS driver");
MODULE_LICENSE("GPL");
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
#ifndef _DT_BINDINGS_POWER_MT6795_POWER_H
#define _DT_BINDINGS_POWER_MT6795_POWER_H
#define MT6795_POWER_DOMAIN_MM 0
#define MT6795_POWER_DOMAIN_VDEC 1
#define MT6795_POWER_DOMAIN_VENC 2
#define MT6795_POWER_DOMAIN_ISP 3
#define MT6795_POWER_DOMAIN_MJC 4
#define MT6795_POWER_DOMAIN_AUDIO 5
#define MT6795_POWER_DOMAIN_MFG_ASYNC 6
#define MT6795_POWER_DOMAIN_MFG_2D 7
#define MT6795_POWER_DOMAIN_MFG 8
#define MT6795_POWER_DOMAIN_MODEM 9
#endif /* _DT_BINDINGS_POWER_MT6795_POWER_H */
......@@ -10,11 +10,33 @@ struct regmap;
struct device;
struct mtk_mutex;
enum mtk_mutex_mod_index {
/* MDP table index */
MUTEX_MOD_IDX_MDP_RDMA0,
MUTEX_MOD_IDX_MDP_RSZ0,
MUTEX_MOD_IDX_MDP_RSZ1,
MUTEX_MOD_IDX_MDP_TDSHP0,
MUTEX_MOD_IDX_MDP_WROT0,
MUTEX_MOD_IDX_MDP_WDMA,
MUTEX_MOD_IDX_MDP_AAL0,
MUTEX_MOD_IDX_MDP_CCORR0,
MUTEX_MOD_IDX_MAX /* ALWAYS keep at the end */
};
enum mtk_mutex_sof_index {
MUTEX_SOF_IDX_SINGLE_MODE,
MUTEX_SOF_IDX_MAX /* ALWAYS keep at the end */
};
struct mtk_mutex *mtk_mutex_get(struct device *dev);
int mtk_mutex_prepare(struct mtk_mutex *mutex);
void mtk_mutex_add_comp(struct mtk_mutex *mutex,
enum mtk_ddp_comp_id id);
void mtk_mutex_enable(struct mtk_mutex *mutex);
int mtk_mutex_enable_by_cmdq(struct mtk_mutex *mutex,
void *pkt);
void mtk_mutex_disable(struct mtk_mutex *mutex);
void mtk_mutex_remove_comp(struct mtk_mutex *mutex,
enum mtk_ddp_comp_id id);
......@@ -22,5 +44,10 @@ void mtk_mutex_unprepare(struct mtk_mutex *mutex);
void mtk_mutex_put(struct mtk_mutex *mutex);
void mtk_mutex_acquire(struct mtk_mutex *mutex);
void mtk_mutex_release(struct mtk_mutex *mutex);
int mtk_mutex_write_mod(struct mtk_mutex *mutex,
enum mtk_mutex_mod_index idx,
bool clear);
int mtk_mutex_write_sof(struct mtk_mutex *mutex,
enum mtk_mutex_sof_index idx);
#endif /* MTK_MUTEX_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment