cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

scu-pd.c (12655B)


      1// SPDX-License-Identifier: GPL-2.0+
      2/*
      3 * Copyright (C) 2016 Freescale Semiconductor, Inc.
      4 * Copyright 2017-2018 NXP
      5 *	Dong Aisheng <aisheng.dong@nxp.com>
      6 *
      7 * Implementation of the SCU based Power Domains
      8 *
      9 * NOTE: a better implementation suggested by Ulf Hansson is using a
     10 * single global power domain and implement the ->attach|detach_dev()
     11 * callback for the genpd and use the regular of_genpd_add_provider_simple().
     12 * From within the ->attach_dev(), we could get the OF node for
     13 * the device that is being attached and then parse the power-domain
     14 * cell containing the "resource id" and store that in the per device
     15 * struct generic_pm_domain_data (we have void pointer there for
     16 * storing these kind of things).
     17 *
     18 * Additionally, we need to implement the ->stop() and ->start()
     19 * callbacks of genpd, which is where you "power on/off" devices,
     20 * rather than using the above ->power_on|off() callbacks.
     21 *
     22 * However, there're two known issues:
     23 * 1. The ->attach_dev() of power domain infrastructure still does
     24 *    not support multi domains case as the struct device *dev passed
     25 *    in is a virtual PD device, it does not help for parsing the real
     26 *    device resource id from device tree, so it's unware of which
     27 *    real sub power domain of device should be attached.
     28 *
     29 *    The framework needs some proper extension to support multi power
     30 *    domain cases.
     31 *
     32 *    Update: Genpd assigns the ->of_node for the virtual device before it
     33 *    invokes ->attach_dev() callback, hence parsing for device resources via
     34 *    DT should work fine.
     35 *
     36 * 2. It also breaks most of current drivers as the driver probe sequence
     37 *    behavior changed if removing ->power_on|off() callback and use
     38 *    ->start() and ->stop() instead. genpd_dev_pm_attach will only power
     39 *    up the domain and attach device, but will not call .start() which
     40 *    relies on device runtime pm. That means the device power is still
     41 *    not up before running driver probe function. For SCU enabled
     42 *    platforms, all device drivers accessing registers/clock without power
     43 *    domain enabled will trigger a HW access error. That means we need fix
     44 *    most drivers probe sequence with proper runtime pm.
     45 *
     46 *    Update: Runtime PM support isn't necessary. Instead, this can easily be
     47 *    fixed in drivers by adding a call to dev_pm_domain_start() during probe.
     48 *
     49 * In summary, the second part needs to be addressed via minor updates to the
     50 * relevant drivers, before the "single global power domain" model can be used.
     51 *
     52 */
     53
     54#include <dt-bindings/firmware/imx/rsrc.h>
     55#include <linux/firmware/imx/sci.h>
     56#include <linux/firmware/imx/svc/rm.h>
     57#include <linux/io.h>
     58#include <linux/module.h>
     59#include <linux/of.h>
     60#include <linux/of_address.h>
     61#include <linux/of_platform.h>
     62#include <linux/platform_device.h>
     63#include <linux/pm.h>
     64#include <linux/pm_domain.h>
     65#include <linux/slab.h>
     66
     67/* SCU Power Mode Protocol definition */
     68struct imx_sc_msg_req_set_resource_power_mode {
     69	struct imx_sc_rpc_msg hdr;
     70	u16 resource;
     71	u8 mode;
     72} __packed __aligned(4);
     73
     74#define IMX_SCU_PD_NAME_SIZE 20
     75struct imx_sc_pm_domain {
     76	struct generic_pm_domain pd;
     77	char name[IMX_SCU_PD_NAME_SIZE];
     78	u32 rsrc;
     79};
     80
     81struct imx_sc_pd_range {
     82	char *name;
     83	u32 rsrc;
     84	u8 num;
     85
     86	/* add domain index */
     87	bool postfix;
     88	u8 start_from;
     89};
     90
     91struct imx_sc_pd_soc {
     92	const struct imx_sc_pd_range *pd_ranges;
     93	u8 num_ranges;
     94};
     95
     96static int imx_con_rsrc;
     97
     98static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = {
     99	/* LSIO SS */
    100	{ "pwm", IMX_SC_R_PWM_0, 8, true, 0 },
    101	{ "gpio", IMX_SC_R_GPIO_0, 8, true, 0 },
    102	{ "gpt", IMX_SC_R_GPT_0, 5, true, 0 },
    103	{ "kpp", IMX_SC_R_KPP, 1, false, 0 },
    104	{ "fspi", IMX_SC_R_FSPI_0, 2, true, 0 },
    105	{ "mu_a", IMX_SC_R_MU_0A, 14, true, 0 },
    106	{ "mu_b", IMX_SC_R_MU_5B, 9, true, 5 },
    107
    108	/* CONN SS */
    109	{ "usb", IMX_SC_R_USB_0, 2, true, 0 },
    110	{ "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 },
    111	{ "usb2", IMX_SC_R_USB_2, 1, false, 0 },
    112	{ "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 },
    113	{ "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 },
    114	{ "enet", IMX_SC_R_ENET_0, 2, true, 0 },
    115	{ "nand", IMX_SC_R_NAND, 1, false, 0 },
    116	{ "mlb", IMX_SC_R_MLB_0, 1, true, 0 },
    117
    118	/* AUDIO SS */
    119	{ "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 },
    120	{ "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 },
    121	{ "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 },
    122	{ "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 },
    123	{ "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 },
    124	{ "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 },
    125	{ "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 },
    126	{ "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 },
    127	{ "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 },
    128	{ "esai0", IMX_SC_R_ESAI_0, 1, false, 0 },
    129	{ "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 },
    130	{ "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 },
    131	{ "sai", IMX_SC_R_SAI_0, 3, true, 0 },
    132	{ "sai3", IMX_SC_R_SAI_3, 1, false, 0 },
    133	{ "sai4", IMX_SC_R_SAI_4, 1, false, 0 },
    134	{ "sai5", IMX_SC_R_SAI_5, 1, false, 0 },
    135	{ "sai6", IMX_SC_R_SAI_6, 1, false, 0 },
    136	{ "sai7", IMX_SC_R_SAI_7, 1, false, 0 },
    137	{ "amix", IMX_SC_R_AMIX, 1, false, 0 },
    138	{ "mqs0", IMX_SC_R_MQS_0, 1, false, 0 },
    139	{ "dsp", IMX_SC_R_DSP, 1, false, 0 },
    140	{ "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 },
    141
    142	/* DMA SS */
    143	{ "can", IMX_SC_R_CAN_0, 3, true, 0 },
    144	{ "ftm", IMX_SC_R_FTM_0, 2, true, 0 },
    145	{ "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 },
    146	{ "adc", IMX_SC_R_ADC_0, 2, true, 0 },
    147	{ "lcd", IMX_SC_R_LCD_0, 1, true, 0 },
    148	{ "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 },
    149	{ "lpuart", IMX_SC_R_UART_0, 4, true, 0 },
    150	{ "lpspi", IMX_SC_R_SPI_0, 4, true, 0 },
    151	{ "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 },
    152
    153	/* VPU SS */
    154	{ "vpu", IMX_SC_R_VPU, 1, false, 0 },
    155	{ "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 },
    156	{ "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 },
    157	{ "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 },
    158	{ "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 },
    159	{ "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 },
    160	{ "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 },
    161	{ "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 },
    162
    163	/* GPU SS */
    164	{ "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 },
    165
    166	/* HSIO SS */
    167	{ "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 },
    168	{ "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 },
    169	{ "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 },
    170
    171	/* MIPI SS */
    172	{ "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 },
    173	{ "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 },
    174	{ "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 },
    175
    176	{ "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 },
    177	{ "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 },
    178	{ "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 },
    179
    180	/* LVDS SS */
    181	{ "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 },
    182	{ "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 },
    183
    184	/* DC SS */
    185	{ "dc0", IMX_SC_R_DC_0, 1, false, 0 },
    186	{ "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 },
    187	{ "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 },
    188
    189	/* CM40 SS */
    190	{ "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 },
    191	{ "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 },
    192	{ "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0},
    193	{ "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0},
    194	{ "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0},
    195
    196	/* CM41 SS */
    197	{ "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 },
    198	{ "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 },
    199	{ "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0},
    200	{ "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0},
    201	{ "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0},
    202
    203	/* IMAGE SS */
    204	{ "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 },
    205	{ "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 },
    206	{ "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 },
    207	{ "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 },
    208};
    209
    210static const struct imx_sc_pd_soc imx8qxp_scu_pd = {
    211	.pd_ranges = imx8qxp_scu_pd_ranges,
    212	.num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges),
    213};
    214
    215static struct imx_sc_ipc *pm_ipc_handle;
    216
    217static inline struct imx_sc_pm_domain *
    218to_imx_sc_pd(struct generic_pm_domain *genpd)
    219{
    220	return container_of(genpd, struct imx_sc_pm_domain, pd);
    221}
    222
    223static void imx_sc_pd_get_console_rsrc(void)
    224{
    225	struct of_phandle_args specs;
    226	int ret;
    227
    228	if (!of_stdout)
    229		return;
    230
    231	ret = of_parse_phandle_with_args(of_stdout, "power-domains",
    232					 "#power-domain-cells",
    233					 0, &specs);
    234	if (ret)
    235		return;
    236
    237	imx_con_rsrc = specs.args[0];
    238}
    239
    240static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on)
    241{
    242	struct imx_sc_msg_req_set_resource_power_mode msg;
    243	struct imx_sc_rpc_msg *hdr = &msg.hdr;
    244	struct imx_sc_pm_domain *pd;
    245	int ret;
    246
    247	pd = to_imx_sc_pd(domain);
    248
    249	hdr->ver = IMX_SC_RPC_VERSION;
    250	hdr->svc = IMX_SC_RPC_SVC_PM;
    251	hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE;
    252	hdr->size = 2;
    253
    254	msg.resource = pd->rsrc;
    255	msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP;
    256
    257	ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true);
    258	if (ret)
    259		dev_err(&domain->dev, "failed to power %s resource %d ret %d\n",
    260			power_on ? "up" : "off", pd->rsrc, ret);
    261
    262	return ret;
    263}
    264
    265static int imx_sc_pd_power_on(struct generic_pm_domain *domain)
    266{
    267	return imx_sc_pd_power(domain, true);
    268}
    269
    270static int imx_sc_pd_power_off(struct generic_pm_domain *domain)
    271{
    272	return imx_sc_pd_power(domain, false);
    273}
    274
    275static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec,
    276						  void *data)
    277{
    278	struct generic_pm_domain *domain = ERR_PTR(-ENOENT);
    279	struct genpd_onecell_data *pd_data = data;
    280	unsigned int i;
    281
    282	for (i = 0; i < pd_data->num_domains; i++) {
    283		struct imx_sc_pm_domain *sc_pd;
    284
    285		sc_pd = to_imx_sc_pd(pd_data->domains[i]);
    286		if (sc_pd->rsrc == spec->args[0]) {
    287			domain = &sc_pd->pd;
    288			break;
    289		}
    290	}
    291
    292	return domain;
    293}
    294
    295static struct imx_sc_pm_domain *
    296imx_scu_add_pm_domain(struct device *dev, int idx,
    297		      const struct imx_sc_pd_range *pd_ranges)
    298{
    299	struct imx_sc_pm_domain *sc_pd;
    300	bool is_off = true;
    301	int ret;
    302
    303	if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx))
    304		return NULL;
    305
    306	sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL);
    307	if (!sc_pd)
    308		return ERR_PTR(-ENOMEM);
    309
    310	sc_pd->rsrc = pd_ranges->rsrc + idx;
    311	sc_pd->pd.power_off = imx_sc_pd_power_off;
    312	sc_pd->pd.power_on = imx_sc_pd_power_on;
    313
    314	if (pd_ranges->postfix)
    315		snprintf(sc_pd->name, sizeof(sc_pd->name),
    316			 "%s%i", pd_ranges->name, pd_ranges->start_from + idx);
    317	else
    318		snprintf(sc_pd->name, sizeof(sc_pd->name),
    319			 "%s", pd_ranges->name);
    320
    321	sc_pd->pd.name = sc_pd->name;
    322	if (imx_con_rsrc == sc_pd->rsrc) {
    323		sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON;
    324		is_off = false;
    325	}
    326
    327	if (sc_pd->rsrc >= IMX_SC_R_LAST) {
    328		dev_warn(dev, "invalid pd %s rsrc id %d found",
    329			 sc_pd->name, sc_pd->rsrc);
    330
    331		devm_kfree(dev, sc_pd);
    332		return NULL;
    333	}
    334
    335	ret = pm_genpd_init(&sc_pd->pd, NULL, is_off);
    336	if (ret) {
    337		dev_warn(dev, "failed to init pd %s rsrc id %d",
    338			 sc_pd->name, sc_pd->rsrc);
    339		devm_kfree(dev, sc_pd);
    340		return NULL;
    341	}
    342
    343	return sc_pd;
    344}
    345
    346static int imx_scu_init_pm_domains(struct device *dev,
    347				    const struct imx_sc_pd_soc *pd_soc)
    348{
    349	const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges;
    350	struct generic_pm_domain **domains;
    351	struct genpd_onecell_data *pd_data;
    352	struct imx_sc_pm_domain *sc_pd;
    353	u32 count = 0;
    354	int i, j;
    355
    356	for (i = 0; i < pd_soc->num_ranges; i++)
    357		count += pd_ranges[i].num;
    358
    359	domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL);
    360	if (!domains)
    361		return -ENOMEM;
    362
    363	pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL);
    364	if (!pd_data)
    365		return -ENOMEM;
    366
    367	count = 0;
    368	for (i = 0; i < pd_soc->num_ranges; i++) {
    369		for (j = 0; j < pd_ranges[i].num; j++) {
    370			sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]);
    371			if (IS_ERR_OR_NULL(sc_pd))
    372				continue;
    373
    374			domains[count++] = &sc_pd->pd;
    375			dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name);
    376		}
    377	}
    378
    379	pd_data->domains = domains;
    380	pd_data->num_domains = count;
    381	pd_data->xlate = imx_scu_pd_xlate;
    382
    383	of_genpd_add_provider_onecell(dev->of_node, pd_data);
    384
    385	return 0;
    386}
    387
    388static int imx_sc_pd_probe(struct platform_device *pdev)
    389{
    390	const struct imx_sc_pd_soc *pd_soc;
    391	int ret;
    392
    393	ret = imx_scu_get_handle(&pm_ipc_handle);
    394	if (ret)
    395		return ret;
    396
    397	pd_soc = of_device_get_match_data(&pdev->dev);
    398	if (!pd_soc)
    399		return -ENODEV;
    400
    401	imx_sc_pd_get_console_rsrc();
    402
    403	return imx_scu_init_pm_domains(&pdev->dev, pd_soc);
    404}
    405
    406static const struct of_device_id imx_sc_pd_match[] = {
    407	{ .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd},
    408	{ .compatible = "fsl,scu-pd", &imx8qxp_scu_pd},
    409	{ /* sentinel */ }
    410};
    411
    412static struct platform_driver imx_sc_pd_driver = {
    413	.driver = {
    414		.name = "imx-scu-pd",
    415		.of_match_table = imx_sc_pd_match,
    416	},
    417	.probe = imx_sc_pd_probe,
    418};
    419builtin_platform_driver(imx_sc_pd_driver);
    420
    421MODULE_AUTHOR("Dong Aisheng <aisheng.dong@nxp.com>");
    422MODULE_DESCRIPTION("IMX SCU Power Domain driver");
    423MODULE_LICENSE("GPL v2");