diff options
Diffstat (limited to 'drivers/bus')
-rw-r--r-- | drivers/bus/Kconfig | 15 | ||||
-rw-r--r-- | drivers/bus/Makefile | 1 | ||||
-rw-r--r-- | drivers/bus/brcmstb_gisb.c | 16 | ||||
-rw-r--r-- | drivers/bus/bt1-apb.c | 1 | ||||
-rw-r--r-- | drivers/bus/mhi/common.h | 38 | ||||
-rw-r--r-- | drivers/bus/mhi/ep/main.c | 7 | ||||
-rw-r--r-- | drivers/bus/mhi/host/boot.c | 11 | ||||
-rw-r--r-- | drivers/bus/mhi/host/init.c | 132 | ||||
-rw-r--r-- | drivers/bus/mhi/host/internal.h | 58 | ||||
-rw-r--r-- | drivers/bus/mhi/host/main.c | 35 | ||||
-rw-r--r-- | drivers/bus/mhi/host/pci_generic.c | 47 | ||||
-rw-r--r-- | drivers/bus/mhi/host/pm.c | 69 | ||||
-rw-r--r-- | drivers/bus/mhi/host/trace.h | 282 | ||||
-rw-r--r-- | drivers/bus/mips_cdmm.c | 2 | ||||
-rw-r--r-- | drivers/bus/stm32_etzpc.c | 141 | ||||
-rw-r--r-- | drivers/bus/stm32_firewall.c | 294 | ||||
-rw-r--r-- | drivers/bus/stm32_firewall.h | 83 | ||||
-rw-r--r-- | drivers/bus/stm32_rifsc.c | 252 | ||||
-rw-r--r-- | drivers/bus/sunxi-rsb.c | 4 | ||||
-rw-r--r-- | drivers/bus/ti-sysc.c | 167 | ||||
-rw-r--r-- | drivers/bus/ts-nbus.c | 81 |
21 files changed, 1434 insertions, 302 deletions
diff --git a/drivers/bus/Kconfig b/drivers/bus/Kconfig index e6742998f372..64cd2ee03aa3 100644 --- a/drivers/bus/Kconfig +++ b/drivers/bus/Kconfig @@ -163,6 +163,16 @@ config QCOM_SSC_BLOCK_BUS i2c/spi/uart controllers, a hexagon core, and a clock controller which provides clocks for the above. +config STM32_FIREWALL + bool "STM32 Firewall framework" + depends on (ARCH_STM32 || COMPILE_TEST) && OF + select OF_DYNAMIC + help + Say y to enable STM32 firewall framework and its services. Firewall + controllers will be able to register to the framework. Access for + hardware resources linked to a firewall controller can be requested + through this STM32 framework. + config SUN50I_DE2_BUS bool "Allwinner A64 DE2 Bus Driver" default ARM64 @@ -186,11 +196,12 @@ config SUNXI_RSB config TEGRA_ACONNECT tristate "Tegra ACONNECT Bus Driver" - depends on ARCH_TEGRA_210_SOC + depends on ARCH_TEGRA depends on OF && PM help Driver for the Tegra ACONNECT bus which is used to interface with - the devices inside the Audio Processing Engine (APE) for Tegra210. + the devices inside the Audio Processing Engine (APE) for + Tegra210 and later. config TEGRA_GMI tristate "Tegra Generic Memory Interface bus driver" diff --git a/drivers/bus/Makefile b/drivers/bus/Makefile index d90eed189a65..cddd4984d6af 100644 --- a/drivers/bus/Makefile +++ b/drivers/bus/Makefile @@ -26,6 +26,7 @@ obj-$(CONFIG_OMAP_INTERCONNECT) += omap_l3_smx.o omap_l3_noc.o obj-$(CONFIG_OMAP_OCP2SCP) += omap-ocp2scp.o obj-$(CONFIG_QCOM_EBI2) += qcom-ebi2.o obj-$(CONFIG_QCOM_SSC_BLOCK_BUS) += qcom-ssc-block-bus.o +obj-$(CONFIG_STM32_FIREWALL) += stm32_firewall.o stm32_rifsc.o stm32_etzpc.o obj-$(CONFIG_SUN50I_DE2_BUS) += sun50i-de2.o obj-$(CONFIG_SUNXI_RSB) += sunxi-rsb.o obj-$(CONFIG_OF) += simple-pm-bus.o diff --git a/drivers/bus/brcmstb_gisb.c b/drivers/bus/brcmstb_gisb.c index b6dfe4340da2..ee29162da4ee 100644 --- a/drivers/bus/brcmstb_gisb.c +++ b/drivers/bus/brcmstb_gisb.c @@ -96,6 +96,20 @@ static const int gisb_offsets_bcm7400[] = { [ARB_ERR_CAP_MASTER] = 0x0d8, }; +static const int gisb_offsets_bcm74165[] = { + [ARB_TIMER] = 0x008, + [ARB_BP_CAP_CLR] = 0x044, + [ARB_BP_CAP_HI_ADDR] = -1, + [ARB_BP_CAP_ADDR] = 0x048, + [ARB_BP_CAP_STATUS] = 0x058, + [ARB_BP_CAP_MASTER] = 0x05c, + [ARB_ERR_CAP_CLR] = 0x038, + [ARB_ERR_CAP_HI_ADDR] = -1, + [ARB_ERR_CAP_ADDR] = 0x020, + [ARB_ERR_CAP_STATUS] = 0x030, + [ARB_ERR_CAP_MASTER] = 0x034, +}; + static const int gisb_offsets_bcm7435[] = { [ARB_TIMER] = 0x00c, [ARB_BP_CAP_CLR] = 0x014, @@ -393,8 +407,10 @@ static const struct of_device_id brcmstb_gisb_arb_of_match[] = { { .compatible = "brcm,bcm7400-gisb-arb", .data = gisb_offsets_bcm7400 }, { .compatible = "brcm,bcm7278-gisb-arb", .data = gisb_offsets_bcm7278 }, { .compatible = "brcm,bcm7038-gisb-arb", .data = gisb_offsets_bcm7038 }, + { .compatible = "brcm,bcm74165-gisb-arb", .data = gisb_offsets_bcm74165 }, { }, }; +MODULE_DEVICE_TABLE(of, brcmstb_gisb_arb_of_match); static int __init brcmstb_gisb_arb_probe(struct platform_device *pdev) { diff --git a/drivers/bus/bt1-apb.c b/drivers/bus/bt1-apb.c index e97c1d1c7578..595fb22b73e0 100644 --- a/drivers/bus/bt1-apb.c +++ b/drivers/bus/bt1-apb.c @@ -22,7 +22,6 @@ #include <linux/clk.h> #include <linux/reset.h> #include <linux/time64.h> -#include <linux/clk.h> #include <linux/sysfs.h> #define APB_EHB_ISR 0x00 diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h index f794b9c8049e..dda340aaed95 100644 --- a/drivers/bus/mhi/common.h +++ b/drivers/bus/mhi/common.h @@ -297,30 +297,30 @@ struct mhi_ring_element { __le32 dword[2]; }; +#define MHI_STATE_LIST \ + mhi_state(RESET, "RESET") \ + mhi_state(READY, "READY") \ + mhi_state(M0, "M0") \ + mhi_state(M1, "M1") \ + mhi_state(M2, "M2") \ + mhi_state(M3, "M3") \ + mhi_state(M3_FAST, "M3_FAST") \ + mhi_state(BHI, "BHI") \ + mhi_state_end(SYS_ERR, "SYS ERROR") + +#undef mhi_state +#undef mhi_state_end + +#define mhi_state(a, b) case MHI_STATE_##a: return b; +#define mhi_state_end(a, b) case MHI_STATE_##a: return b; + static inline const char *mhi_state_str(enum mhi_state state) { switch (state) { - case MHI_STATE_RESET: - return "RESET"; - case MHI_STATE_READY: - return "READY"; - case MHI_STATE_M0: - return "M0"; - case MHI_STATE_M1: - return "M1"; - case MHI_STATE_M2: - return "M2"; - case MHI_STATE_M3: - return "M3"; - case MHI_STATE_M3_FAST: - return "M3 FAST"; - case MHI_STATE_BHI: - return "BHI"; - case MHI_STATE_SYS_ERR: - return "SYS ERROR"; + MHI_STATE_LIST default: return "Unknown state"; } -}; +} #endif /* _MHI_COMMON_H */ diff --git a/drivers/bus/mhi/ep/main.c b/drivers/bus/mhi/ep/main.c index 65fc1d738bec..f8f674adf1d4 100644 --- a/drivers/bus/mhi/ep/main.c +++ b/drivers/bus/mhi/ep/main.c @@ -1149,8 +1149,9 @@ int mhi_ep_power_up(struct mhi_ep_cntrl *mhi_cntrl) mhi_ep_mmio_mask_interrupts(mhi_cntrl); mhi_ep_mmio_init(mhi_cntrl); - mhi_cntrl->mhi_event = kzalloc(mhi_cntrl->event_rings * (sizeof(*mhi_cntrl->mhi_event)), - GFP_KERNEL); + mhi_cntrl->mhi_event = kcalloc(mhi_cntrl->event_rings, + sizeof(*mhi_cntrl->mhi_event), + GFP_KERNEL); if (!mhi_cntrl->mhi_event) return -ENOMEM; @@ -1496,7 +1497,7 @@ int mhi_ep_register_controller(struct mhi_ep_cntrl *mhi_cntrl, mhi_cntrl->ring_item_cache = kmem_cache_create("mhi_ep_ring_item", sizeof(struct mhi_ep_ring_item), 0, 0, NULL); - if (!mhi_cntrl->ev_ring_el_cache) { + if (!mhi_cntrl->ring_item_cache) { ret = -ENOMEM; goto err_destroy_tre_buf_cache; } diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index edc0ec5a0933..dedd29ca8db3 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -395,7 +395,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) void *buf; dma_addr_t dma_addr; size_t size, fw_sz; - int i, ret; + int ret; if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { dev_err(dev, "Device MHI is not in valid state\n"); @@ -408,15 +408,6 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) if (ret) dev_err(dev, "Could not capture serial number via BHI\n"); - for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) { - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), - &mhi_cntrl->oem_pk_hash[i]); - if (ret) { - dev_err(dev, "Could not capture OEM PK HASH via BHI\n"); - break; - } - } - /* wait for ready on pass through or any other execution environment */ if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee)) goto fw_load_ready_state; diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 65ceac1837f9..173f79918741 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -20,50 +20,49 @@ #include <linux/wait.h> #include "internal.h" +#define CREATE_TRACE_POINTS +#include "trace.h" + static DEFINE_IDA(mhi_controller_ida); +#undef mhi_ee +#undef mhi_ee_end + +#define mhi_ee(a, b) [MHI_EE_##a] = b, +#define mhi_ee_end(a, b) [MHI_EE_##a] = b, + const char * const mhi_ee_str[MHI_EE_MAX] = { - [MHI_EE_PBL] = "PRIMARY BOOTLOADER", - [MHI_EE_SBL] = "SECONDARY BOOTLOADER", - [MHI_EE_AMSS] = "MISSION MODE", - [MHI_EE_RDDM] = "RAMDUMP DOWNLOAD MODE", - [MHI_EE_WFW] = "WLAN FIRMWARE", - [MHI_EE_PTHRU] = "PASS THROUGH", - [MHI_EE_EDL] = "EMERGENCY DOWNLOAD", - [MHI_EE_FP] = "FLASH PROGRAMMER", - [MHI_EE_DISABLE_TRANSITION] = "DISABLE", - [MHI_EE_NOT_SUPPORTED] = "NOT SUPPORTED", + MHI_EE_LIST }; +#undef dev_st_trans +#undef dev_st_trans_end + +#define dev_st_trans(a, b) [DEV_ST_TRANSITION_##a] = b, +#define dev_st_trans_end(a, b) [DEV_ST_TRANSITION_##a] = b, + const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX] = { - [DEV_ST_TRANSITION_PBL] = "PBL", - [DEV_ST_TRANSITION_READY] = "READY", - [DEV_ST_TRANSITION_SBL] = "SBL", - [DEV_ST_TRANSITION_MISSION_MODE] = "MISSION MODE", - [DEV_ST_TRANSITION_FP] = "FLASH PROGRAMMER", - [DEV_ST_TRANSITION_SYS_ERR] = "SYS ERROR", - [DEV_ST_TRANSITION_DISABLE] = "DISABLE", + DEV_ST_TRANSITION_LIST }; +#undef ch_state_type +#undef ch_state_type_end + +#define ch_state_type(a, b) [MHI_CH_STATE_TYPE_##a] = b, +#define ch_state_type_end(a, b) [MHI_CH_STATE_TYPE_##a] = b, + const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX] = { - [MHI_CH_STATE_TYPE_RESET] = "RESET", - [MHI_CH_STATE_TYPE_STOP] = "STOP", - [MHI_CH_STATE_TYPE_START] = "START", + MHI_CH_STATE_TYPE_LIST }; +#undef mhi_pm_state +#undef mhi_pm_state_end + +#define mhi_pm_state(a, b) [MHI_PM_STATE_##a] = b, +#define mhi_pm_state_end(a, b) [MHI_PM_STATE_##a] = b, + static const char * const mhi_pm_state_str[] = { - [MHI_PM_STATE_DISABLE] = "DISABLE", - [MHI_PM_STATE_POR] = "POWER ON RESET", - [MHI_PM_STATE_M0] = "M0", - [MHI_PM_STATE_M2] = "M2", - [MHI_PM_STATE_M3_ENTER] = "M?->M3", - [MHI_PM_STATE_M3] = "M3", - [MHI_PM_STATE_M3_EXIT] = "M3->M0", - [MHI_PM_STATE_FW_DL_ERR] = "Firmware Download Error", - [MHI_PM_STATE_SYS_ERR_DETECT] = "SYS ERROR Detect", - [MHI_PM_STATE_SYS_ERR_PROCESS] = "SYS ERROR Process", - [MHI_PM_STATE_SHUTDOWN_PROCESS] = "SHUTDOWN Process", - [MHI_PM_STATE_LD_ERR_FATAL_DETECT] = "Linkdown or Error Fatal Detect", + MHI_PM_STATE_LIST }; const char *to_mhi_pm_state_str(u32 state) @@ -97,11 +96,19 @@ static ssize_t oem_pk_hash_show(struct device *dev, { struct mhi_device *mhi_dev = to_mhi_device(dev); struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; - int i, cnt = 0; + u32 hash_segment[MHI_MAX_OEM_PK_HASH_SEGMENTS]; + int i, cnt = 0, ret; - for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) - cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", - i, mhi_cntrl->oem_pk_hash[i]); + for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) { + ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), &hash_segment[i]); + if (ret) { + dev_err(dev, "Could not capture OEM PK HASH\n"); + return ret; + } + } + + for (i = 0; i < MHI_MAX_OEM_PK_HASH_SEGMENTS; i++) + cnt += sysfs_emit_at(buf, cnt, "OEMPKHASH[%d]: 0x%x\n", i, hash_segment[i]); return cnt; } @@ -120,6 +127,30 @@ static ssize_t soc_reset_store(struct device *dev, } static DEVICE_ATTR_WO(soc_reset); +static ssize_t trigger_edl_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct mhi_device *mhi_dev = to_mhi_device(dev); + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; + unsigned long val; + int ret; + + ret = kstrtoul(buf, 10, &val); + if (ret < 0) + return ret; + + if (!val) + return -EINVAL; + + ret = mhi_cntrl->edl_trigger(mhi_cntrl); + if (ret) + return ret; + + return count; +} +static DEVICE_ATTR_WO(trigger_edl); + static struct attribute *mhi_dev_attrs[] = { &dev_attr_serial_number.attr, &dev_attr_oem_pk_hash.attr, @@ -510,11 +541,9 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) dev_dbg(dev, "Initializing MHI registers\n"); /* Read channel db offset */ - ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val); - if (ret) { - dev_err(dev, "Unable to read CHDBOFF register\n"); - return -EIO; - } + ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val); + if (ret) + return ret; if (val >= mhi_cntrl->reg_len - (8 * MHI_DEV_WAKE_DB)) { dev_err(dev, "CHDB offset: 0x%x is out of range: 0x%zx\n", @@ -907,7 +936,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan; struct mhi_cmd *mhi_cmd; struct mhi_device *mhi_dev; - u32 soc_info; int ret, i; if (!mhi_cntrl || !mhi_cntrl->cntrl_dev || !mhi_cntrl->regs || @@ -982,17 +1010,6 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_cntrl->unmap_single = mhi_unmap_single_no_bb; } - /* Read the MHI device info */ - ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, - SOC_HW_VERSION_OFFS, &soc_info); - if (ret) - goto err_destroy_wq; - - mhi_cntrl->family_number = FIELD_GET(SOC_HW_VERSION_FAM_NUM_BMSK, soc_info); - mhi_cntrl->device_number = FIELD_GET(SOC_HW_VERSION_DEV_NUM_BMSK, soc_info); - mhi_cntrl->major_version = FIELD_GET(SOC_HW_VERSION_MAJOR_VER_BMSK, soc_info); - mhi_cntrl->minor_version = FIELD_GET(SOC_HW_VERSION_MINOR_VER_BMSK, soc_info); - mhi_cntrl->index = ida_alloc(&mhi_controller_ida, GFP_KERNEL); if (mhi_cntrl->index < 0) { ret = mhi_cntrl->index; @@ -1023,6 +1040,12 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, if (ret) goto err_release_dev; + if (mhi_cntrl->edl_trigger) { + ret = sysfs_create_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr); + if (ret) + goto err_release_dev; + } + mhi_cntrl->mhi_dev = mhi_dev; mhi_create_debugfs(mhi_cntrl); @@ -1056,6 +1079,9 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl) mhi_deinit_free_irq(mhi_cntrl); mhi_destroy_debugfs(mhi_cntrl); + if (mhi_cntrl->edl_trigger) + sysfs_remove_file(&mhi_dev->dev.kobj, &dev_attr_trigger_edl.attr); + destroy_workqueue(mhi_cntrl->hiprio_wq); kfree(mhi_cntrl->mhi_cmd); kfree(mhi_cntrl->mhi_event); diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 30ac415a3000..aaad40a07f69 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -15,12 +15,6 @@ extern struct bus_type mhi_bus_type; #define MHI_SOC_RESET_REQ_OFFSET 0xb0 #define MHI_SOC_RESET_REQ BIT(0) -#define SOC_HW_VERSION_OFFS 0x224 -#define SOC_HW_VERSION_FAM_NUM_BMSK GENMASK(31, 28) -#define SOC_HW_VERSION_DEV_NUM_BMSK GENMASK(27, 16) -#define SOC_HW_VERSION_MAJOR_VER_BMSK GENMASK(15, 8) -#define SOC_HW_VERSION_MINOR_VER_BMSK GENMASK(7, 0) - struct mhi_ctxt { struct mhi_event_ctxt *er_ctxt; struct mhi_chan_ctxt *chan_ctxt; @@ -42,6 +36,11 @@ enum mhi_ch_state_type { MHI_CH_STATE_TYPE_MAX, }; +#define MHI_CH_STATE_TYPE_LIST \ + ch_state_type(RESET, "RESET") \ + ch_state_type(STOP, "STOP") \ + ch_state_type_end(START, "START") + extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX]; #define TO_CH_STATE_TYPE_STR(state) (((state) >= MHI_CH_STATE_TYPE_MAX) ? \ "INVALID_STATE" : \ @@ -50,6 +49,18 @@ extern const char * const mhi_ch_state_type_str[MHI_CH_STATE_TYPE_MAX]; #define MHI_INVALID_BRSTMODE(mode) (mode != MHI_DB_BRST_DISABLE && \ mode != MHI_DB_BRST_ENABLE) +#define MHI_EE_LIST \ + mhi_ee(PBL, "PRIMARY BOOTLOADER") \ + mhi_ee(SBL, "SECONDARY BOOTLOADER") \ + mhi_ee(AMSS, "MISSION MODE") \ + mhi_ee(RDDM, "RAMDUMP DOWNLOAD MODE")\ + mhi_ee(WFW, "WLAN FIRMWARE") \ + mhi_ee(PTHRU, "PASS THROUGH") \ + mhi_ee(EDL, "EMERGENCY DOWNLOAD") \ + mhi_ee(FP, "FLASH PROGRAMMER") \ + mhi_ee(DISABLE_TRANSITION, "DISABLE") \ + mhi_ee_end(NOT_SUPPORTED, "NOT SUPPORTED") + extern const char * const mhi_ee_str[MHI_EE_MAX]; #define TO_MHI_EXEC_STR(ee) (((ee) >= MHI_EE_MAX) ? \ "INVALID_EE" : mhi_ee_str[ee]) @@ -69,9 +80,20 @@ enum dev_st_transition { DEV_ST_TRANSITION_FP, DEV_ST_TRANSITION_SYS_ERR, DEV_ST_TRANSITION_DISABLE, + DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE, DEV_ST_TRANSITION_MAX, }; +#define DEV_ST_TRANSITION_LIST \ + dev_st_trans(PBL, "PBL") \ + dev_st_trans(READY, "READY") \ + dev_st_trans(SBL, "SBL") \ + dev_st_trans(MISSION_MODE, "MISSION MODE") \ + dev_st_trans(FP, "FLASH PROGRAMMER") \ + dev_st_trans(SYS_ERR, "SYS ERROR") \ + dev_st_trans(DISABLE, "DISABLE") \ + dev_st_trans_end(DISABLE_DESTROY_DEVICE, "DISABLE (DESTROY DEVICE)") + extern const char * const dev_state_tran_str[DEV_ST_TRANSITION_MAX]; #define TO_DEV_STATE_TRANS_STR(state) (((state) >= DEV_ST_TRANSITION_MAX) ? \ "INVALID_STATE" : dev_state_tran_str[state]) @@ -88,11 +110,27 @@ enum mhi_pm_state { MHI_PM_STATE_FW_DL_ERR, MHI_PM_STATE_SYS_ERR_DETECT, MHI_PM_STATE_SYS_ERR_PROCESS, + MHI_PM_STATE_SYS_ERR_FAIL, MHI_PM_STATE_SHUTDOWN_PROCESS, MHI_PM_STATE_LD_ERR_FATAL_DETECT, MHI_PM_STATE_MAX }; +#define MHI_PM_STATE_LIST \ + mhi_pm_state(DISABLE, "DISABLE") \ + mhi_pm_state(POR, "POWER ON RESET") \ + mhi_pm_state(M0, "M0") \ + mhi_pm_state(M2, "M2") \ + mhi_pm_state(M3_ENTER, "M?->M3") \ + mhi_pm_state(M3, "M3") \ + mhi_pm_state(M3_EXIT, "M3->M0") \ + mhi_pm_state(FW_DL_ERR, "Firmware Download Error") \ + mhi_pm_state(SYS_ERR_DETECT, "SYS ERROR Detect") \ + mhi_pm_state(SYS_ERR_PROCESS, "SYS ERROR Process") \ + mhi_pm_state(SYS_ERR_FAIL, "SYS ERROR Failure") \ + mhi_pm_state(SHUTDOWN_PROCESS, "SHUTDOWN Process") \ + mhi_pm_state_end(LD_ERR_FATAL_DETECT, "Linkdown or Error Fatal Detect") + #define MHI_PM_DISABLE BIT(0) #define MHI_PM_POR BIT(1) #define MHI_PM_M0 BIT(2) @@ -104,14 +142,16 @@ enum mhi_pm_state { #define MHI_PM_FW_DL_ERR BIT(7) #define MHI_PM_SYS_ERR_DETECT BIT(8) #define MHI_PM_SYS_ERR_PROCESS BIT(9) -#define MHI_PM_SHUTDOWN_PROCESS BIT(10) +#define MHI_PM_SYS_ERR_FAIL BIT(10) +#define MHI_PM_SHUTDOWN_PROCESS BIT(11) /* link not accessible */ -#define MHI_PM_LD_ERR_FATAL_DETECT BIT(11) +#define MHI_PM_LD_ERR_FATAL_DETECT BIT(12) #define MHI_REG_ACCESS_VALID(pm_state) ((pm_state & (MHI_PM_POR | MHI_PM_M0 | \ MHI_PM_M2 | MHI_PM_M3_ENTER | MHI_PM_M3_EXIT | \ MHI_PM_SYS_ERR_DETECT | MHI_PM_SYS_ERR_PROCESS | \ - MHI_PM_SHUTDOWN_PROCESS | MHI_PM_FW_DL_ERR))) + MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | \ + MHI_PM_FW_DL_ERR))) #define MHI_PM_IN_ERROR_STATE(pm_state) (pm_state >= MHI_PM_FW_DL_ERR) #define MHI_PM_IN_FATAL_STATE(pm_state) (pm_state == MHI_PM_LD_ERR_FATAL_DETECT) #define MHI_DB_ACCESS_VALID(mhi_cntrl) (mhi_cntrl->pm_state & mhi_cntrl->db_access) diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index abb561db9ae1..4de75674f193 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -15,6 +15,7 @@ #include <linux/skbuff.h> #include <linux/slab.h> #include "internal.h" +#include "trace.h" int __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, u32 offset, u32 *out) @@ -493,11 +494,8 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) state = mhi_get_mhi_state(mhi_cntrl); ee = mhi_get_exec_env(mhi_cntrl); - dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n", - TO_MHI_EXEC_STR(mhi_cntrl->ee), - mhi_state_str(mhi_cntrl->dev_state), - TO_MHI_EXEC_STR(ee), mhi_state_str(state)); + trace_mhi_intvec_states(mhi_cntrl, ee, state); if (state == MHI_STATE_SYS_ERR) { dev_dbg(dev, "System error detected\n"); pm_state = mhi_tryset_pm_state(mhi_cntrl, @@ -838,6 +836,8 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, while (dev_rp != local_rp) { enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); + trace_mhi_ctrl_event(mhi_cntrl, local_rp); + switch (type) { case MHI_PKT_TYPE_BW_REQ_EVENT: { @@ -1003,6 +1003,8 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, while (dev_rp != local_rp && event_quota > 0) { enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); + trace_mhi_data_event(mhi_cntrl, local_rp); + chan = MHI_TRE_GET_EV_CHID(local_rp); WARN_ON(chan >= mhi_cntrl->max_chan); @@ -1243,6 +1245,7 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len); mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain); + trace_mhi_gen_tre(mhi_cntrl, mhi_chan, mhi_tre); /* increment WP */ mhi_add_ring_element(mhi_cntrl, tre_ring); mhi_add_ring_element(mhi_cntrl, buf_ring); @@ -1337,9 +1340,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, enum mhi_cmd_type cmd = MHI_CMD_NOP; int ret; - dev_dbg(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan, - TO_CH_STATE_TYPE_STR(to_state)); - + trace_mhi_channel_command_start(mhi_cntrl, mhi_chan, to_state, TPS("Updating")); switch (to_state) { case MHI_CH_STATE_TYPE_RESET: write_lock_irq(&mhi_chan->lock); @@ -1406,9 +1407,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, write_unlock_irq(&mhi_chan->lock); } - dev_dbg(dev, "%d: Channel state change to %s successful\n", - mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); - + trace_mhi_channel_command_end(mhi_cntrl, mhi_chan, to_state, TPS("Updated")); exit_channel_update: mhi_cntrl->runtime_put(mhi_cntrl); mhi_device_put(mhi_cntrl->mhi_dev); @@ -1692,3 +1691,19 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) } } EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer); + +int mhi_get_channel_doorbell_offset(struct mhi_controller *mhi_cntrl, u32 *chdb_offset) +{ + struct device *dev = &mhi_cntrl->mhi_dev->dev; + void __iomem *base = mhi_cntrl->regs; + int ret; + + ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, chdb_offset); + if (ret) { + dev_err(dev, "Unable to read CHDBOFF register\n"); + return -EIO; + } + + return 0; +} +EXPORT_SYMBOL_GPL(mhi_get_channel_doorbell_offset); diff --git a/drivers/bus/mhi/host/pci_generic.c b/drivers/bus/mhi/host/pci_generic.c index cd6cd14b3d29..08844ee79654 100644 --- a/drivers/bus/mhi/host/pci_generic.c +++ b/drivers/bus/mhi/host/pci_generic.c @@ -27,12 +27,16 @@ #define PCI_VENDOR_ID_THALES 0x1269 #define PCI_VENDOR_ID_QUECTEL 0x1eac +#define MHI_EDL_DB 91 +#define MHI_EDL_COOKIE 0xEDEDEDED + /** * struct mhi_pci_dev_info - MHI PCI device specific information * @config: MHI controller configuration * @name: name of the PCI module * @fw: firmware path (if any) * @edl: emergency download mode firmware path (if any) + * @edl_trigger: capable of triggering EDL mode in the device (if supported) * @bar_num: PCI base address register to use for MHI MMIO register space * @dma_data_width: DMA transfer word size (32 or 64 bits) * @mru_default: default MRU size for MBIM network packets @@ -44,6 +48,7 @@ struct mhi_pci_dev_info { const char *name; const char *fw; const char *edl; + bool edl_trigger; unsigned int bar_num; unsigned int dma_data_width; unsigned int mru_default; @@ -292,6 +297,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx75_info = { .name = "qcom-sdx75m", .fw = "qcom/sdx75m/xbl.elf", .edl = "qcom/sdx75m/edl.mbn", + .edl_trigger = true, .config = &modem_qcom_v2_mhiv_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -302,6 +308,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx65_info = { .name = "qcom-sdx65m", .fw = "qcom/sdx65m/xbl.elf", .edl = "qcom/sdx65m/edl.mbn", + .edl_trigger = true, .config = &modem_qcom_v1_mhiv_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -312,6 +319,7 @@ static const struct mhi_pci_dev_info mhi_qcom_sdx55_info = { .name = "qcom-sdx55m", .fw = "qcom/sdx55m/sbl1.mbn", .edl = "qcom/sdx55m/edl.mbn", + .edl_trigger = true, .config = &modem_qcom_v1_mhiv_config, .bar_num = MHI_PCI_DEFAULT_BAR_NUM, .dma_data_width = 32, @@ -538,7 +546,7 @@ static struct mhi_event_config mhi_telit_fn980_hw_v1_events[] = { MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101) }; -static struct mhi_controller_config modem_telit_fn980_hw_v1_config = { +static const struct mhi_controller_config modem_telit_fn980_hw_v1_config = { .max_channels = 128, .timeout_ms = 20000, .num_channels = ARRAY_SIZE(mhi_telit_fn980_hw_v1_channels), @@ -928,6 +936,40 @@ static void health_check(struct timer_list *t) mod_timer(&mhi_pdev->health_check_timer, jiffies + HEALTH_CHECK_PERIOD); } +static int mhi_pci_generic_edl_trigger(struct mhi_controller *mhi_cntrl) +{ + void __iomem *base = mhi_cntrl->regs; + void __iomem *edl_db; + int ret; + u32 val; + + ret = mhi_device_get_sync(mhi_cntrl->mhi_dev); + if (ret) { + dev_err(mhi_cntrl->cntrl_dev, "Failed to wakeup the device\n"); + return ret; + } + + pm_wakeup_event(&mhi_cntrl->mhi_dev->dev, 0); + mhi_cntrl->runtime_get(mhi_cntrl); + + ret = mhi_get_channel_doorbell_offset(mhi_cntrl, &val); + if (ret) + goto err_get_chdb; + + edl_db = base + val + (8 * MHI_EDL_DB); + + mhi_cntrl->write_reg(mhi_cntrl, edl_db + 4, upper_32_bits(MHI_EDL_COOKIE)); + mhi_cntrl->write_reg(mhi_cntrl, edl_db, lower_32_bits(MHI_EDL_COOKIE)); + + mhi_soc_reset(mhi_cntrl); + +err_get_chdb: + mhi_cntrl->runtime_put(mhi_cntrl); + mhi_device_put(mhi_cntrl->mhi_dev); + + return ret; +} + static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { const struct mhi_pci_dev_info *info = (struct mhi_pci_dev_info *) id->driver_data; @@ -962,6 +1004,9 @@ static int mhi_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) mhi_cntrl->runtime_put = mhi_pci_runtime_put; mhi_cntrl->mru = info->mru_default; + if (info->edl_trigger) + mhi_cntrl->edl_trigger = mhi_pci_generic_edl_trigger; + if (info->sideband_wake) { mhi_cntrl->wake_get = mhi_pci_wake_get_nop; mhi_cntrl->wake_put = mhi_pci_wake_put_nop; diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index a2f2feef1476..11c0e751f223 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -15,6 +15,7 @@ #include <linux/slab.h> #include <linux/wait.h> #include "internal.h" +#include "trace.h" /* * Not all MHI state transitions are synchronous. Transitions like Linkdown, @@ -36,7 +37,10 @@ * M0 <--> M0 * M0 -> FW_DL_ERR * M0 -> M3_ENTER -> M3 -> M3_EXIT --> M0 - * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS --> POR + * L1: SYS_ERR_DETECT -> SYS_ERR_PROCESS + * SYS_ERR_PROCESS -> SYS_ERR_FAIL + * SYS_ERR_FAIL -> SYS_ERR_DETECT + * SYS_ERR_PROCESS --> POR * L2: SHUTDOWN_PROCESS -> LD_ERR_FATAL_DETECT * SHUTDOWN_PROCESS -> DISABLE * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT @@ -93,7 +97,12 @@ static const struct mhi_pm_transitions dev_state_transitions[] = { }, { MHI_PM_SYS_ERR_PROCESS, - MHI_PM_POR | MHI_PM_SHUTDOWN_PROCESS | + MHI_PM_POR | MHI_PM_SYS_ERR_FAIL | MHI_PM_SHUTDOWN_PROCESS | + MHI_PM_LD_ERR_FATAL_DETECT + }, + { + MHI_PM_SYS_ERR_FAIL, + MHI_PM_SYS_ERR_DETECT | MHI_PM_SHUTDOWN_PROCESS | MHI_PM_LD_ERR_FATAL_DETECT }, /* L2 States */ @@ -123,6 +132,7 @@ enum mhi_pm_state __must_check mhi_tryset_pm_state(struct mhi_controller *mhi_cn if (unlikely(!(dev_state_transitions[index].to_states & state))) return cur_state; + trace_mhi_tryset_pm_state(mhi_cntrl, state); mhi_cntrl->pm_state = state; return mhi_cntrl->pm_state; } @@ -458,7 +468,8 @@ error_mission_mode: } /* Handle shutdown transitions */ -static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) +static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl, + bool destroy_device) { enum mhi_pm_state cur_state; struct mhi_event *mhi_event; @@ -520,8 +531,16 @@ skip_mhi_reset: dev_dbg(dev, "Waiting for all pending threads to complete\n"); wake_up_all(&mhi_cntrl->state_event); - dev_dbg(dev, "Reset all active channels and remove MHI devices\n"); - device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device); + /* + * Only destroy the 'struct device' for channels if indicated by the + * 'destroy_device' flag. Because, during system suspend or hibernation + * state, there is no need to destroy the 'struct device' as the endpoint + * device would still be physically attached to the machine. + */ + if (destroy_device) { + dev_dbg(dev, "Reset all active channels and remove MHI devices\n"); + device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device); + } mutex_lock(&mhi_cntrl->pm_mutex); @@ -629,7 +648,13 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) !in_reset, timeout); if (!ret || in_reset) { dev_err(dev, "Device failed to exit MHI Reset state\n"); - goto exit_sys_error_transition; + write_lock_irq(&mhi_cntrl->pm_lock); + cur_state = mhi_tryset_pm_state(mhi_cntrl, + MHI_PM_SYS_ERR_FAIL); + write_unlock_irq(&mhi_cntrl->pm_lock); + /* Shutdown may have occurred, otherwise cleanup now */ + if (cur_state != MHI_PM_SYS_ERR_FAIL) + goto exit_sys_error_transition; } /* @@ -758,7 +783,6 @@ void mhi_pm_st_worker(struct work_struct *work) struct mhi_controller *mhi_cntrl = container_of(work, struct mhi_controller, st_worker); - struct device *dev = &mhi_cntrl->mhi_dev->dev; spin_lock_irq(&mhi_cntrl->transition_lock); list_splice_tail_init(&mhi_cntrl->transition_list, &head); @@ -766,8 +790,8 @@ void mhi_pm_st_worker(struct work_struct *work) list_for_each_entry_safe(itr, tmp, &head, node) { list_del(&itr->node); - dev_dbg(dev, "Handling state transition: %s\n", - TO_DEV_STATE_TRANS_STR(itr->state)); + + trace_mhi_pm_st_transition(mhi_cntrl, itr->state); switch (itr->state) { case DEV_ST_TRANSITION_PBL: @@ -806,7 +830,10 @@ void mhi_pm_st_worker(struct work_struct *work) mhi_pm_sys_error_transition(mhi_cntrl); break; case DEV_ST_TRANSITION_DISABLE: - mhi_pm_disable_transition(mhi_cntrl); + mhi_pm_disable_transition(mhi_cntrl, false); + break; + case DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE: + mhi_pm_disable_transition(mhi_cntrl, true); break; default: break; @@ -1160,7 +1187,8 @@ error_exit: } EXPORT_SYMBOL_GPL(mhi_async_power_up); -void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) +static void __mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful, + bool destroy_device) { enum mhi_pm_state cur_state, transition_state; struct device *dev = &mhi_cntrl->mhi_dev->dev; @@ -1196,15 +1224,32 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) write_unlock_irq(&mhi_cntrl->pm_lock); mutex_unlock(&mhi_cntrl->pm_mutex); - mhi_queue_state_transition(mhi_cntrl, DEV_ST_TRANSITION_DISABLE); + if (destroy_device) + mhi_queue_state_transition(mhi_cntrl, + DEV_ST_TRANSITION_DISABLE_DESTROY_DEVICE); + else + mhi_queue_state_transition(mhi_cntrl, + DEV_ST_TRANSITION_DISABLE); /* Wait for shutdown to complete */ flush_work(&mhi_cntrl->st_worker); disable_irq(mhi_cntrl->irq[0]); } + +void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) +{ + __mhi_power_down(mhi_cntrl, graceful, true); +} EXPORT_SYMBOL_GPL(mhi_power_down); +void mhi_power_down_keep_dev(struct mhi_controller *mhi_cntrl, + bool graceful) +{ + __mhi_power_down(mhi_cntrl, graceful, false); +} +EXPORT_SYMBOL_GPL(mhi_power_down_keep_dev); + int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) { int ret = mhi_async_power_up(mhi_cntrl); diff --git a/drivers/bus/mhi/host/trace.h b/drivers/bus/mhi/host/trace.h new file mode 100644 index 000000000000..95613c8ebe06 --- /dev/null +++ b/drivers/bus/mhi/host/trace.h @@ -0,0 +1,282 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM mhi_host + +#if !defined(_TRACE_EVENT_MHI_HOST_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_EVENT_MHI_HOST_H + +#include <linux/tracepoint.h> +#include <linux/trace_seq.h> +#include "../common.h" +#include "internal.h" + +#undef mhi_state +#undef mhi_state_end + +#define mhi_state(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a); +#define mhi_state_end(a, b) TRACE_DEFINE_ENUM(MHI_STATE_##a); + +MHI_STATE_LIST + +#undef mhi_state +#undef mhi_state_end + +#define mhi_state(a, b) { MHI_STATE_##a, b }, +#define mhi_state_end(a, b) { MHI_STATE_##a, b } + +#undef mhi_pm_state +#undef mhi_pm_state_end + +#define mhi_pm_state(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a); +#define mhi_pm_state_end(a, b) TRACE_DEFINE_ENUM(MHI_PM_STATE_##a); + +MHI_PM_STATE_LIST + +#undef mhi_pm_state +#undef mhi_pm_state_end + +#define mhi_pm_state(a, b) { MHI_PM_STATE_##a, b }, +#define mhi_pm_state_end(a, b) { MHI_PM_STATE_##a, b } + +#undef mhi_ee +#undef mhi_ee_end + +#define mhi_ee(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a); +#define mhi_ee_end(a, b) TRACE_DEFINE_ENUM(MHI_EE_##a); + +MHI_EE_LIST + +#undef mhi_ee +#undef mhi_ee_end + +#define mhi_ee(a, b) { MHI_EE_##a, b }, +#define mhi_ee_end(a, b) { MHI_EE_##a, b } + +#undef ch_state_type +#undef ch_state_type_end + +#define ch_state_type(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a); +#define ch_state_type_end(a, b) TRACE_DEFINE_ENUM(MHI_CH_STATE_TYPE_##a); + +MHI_CH_STATE_TYPE_LIST + +#undef ch_state_type +#undef ch_state_type_end + +#define ch_state_type(a, b) { MHI_CH_STATE_TYPE_##a, b }, +#define ch_state_type_end(a, b) { MHI_CH_STATE_TYPE_##a, b } + +#undef dev_st_trans +#undef dev_st_trans_end + +#define dev_st_trans(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a); +#define dev_st_trans_end(a, b) TRACE_DEFINE_ENUM(DEV_ST_TRANSITION_##a); + +DEV_ST_TRANSITION_LIST + +#undef dev_st_trans +#undef dev_st_trans_end + +#define dev_st_trans(a, b) { DEV_ST_TRANSITION_##a, b }, +#define dev_st_trans_end(a, b) { DEV_ST_TRANSITION_##a, b } + +#define TPS(x) tracepoint_string(x) + +TRACE_EVENT(mhi_gen_tre, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, + struct mhi_ring_element *mhi_tre), + + TP_ARGS(mhi_cntrl, mhi_chan, mhi_tre), + + TP_STRUCT__entry( + __string(name, mhi_cntrl->mhi_dev->name) + __field(int, ch_num) + __field(void *, wp) + __field(__le64, tre_ptr) + __field(__le32, dword0) + __field(__le32, dword1) + ), + + TP_fast_assign( + __assign_str(name); + __entry->ch_num = mhi_chan->chan; + __entry->wp = mhi_tre; + __entry->tre_ptr = mhi_tre->ptr; + __entry->dword0 = mhi_tre->dword[0]; + __entry->dword1 = mhi_tre->dword[1]; + ), + + TP_printk("%s: Chan: %d TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x\n", + __get_str(name), __entry->ch_num, __entry->wp, __entry->tre_ptr, + __entry->dword0, __entry->dword1) +); + +TRACE_EVENT(mhi_intvec_states, + + TP_PROTO(struct mhi_controller *mhi_cntrl, int dev_ee, int dev_state), + + TP_ARGS(mhi_cntrl, dev_ee, dev_state), + + TP_STRUCT__entry( + __string(name, mhi_cntrl->mhi_dev->name) + __field(int, local_ee) + __field(int, state) + __field(int, dev_ee) + __field(int, dev_state) + ), + + TP_fast_assign( + __assign_str(name); + __entry->local_ee = mhi_cntrl->ee; + __entry->state = mhi_cntrl->dev_state; + __entry->dev_ee = dev_ee; + __entry->dev_state = dev_state; + ), + + TP_printk("%s: Local EE: %s State: %s Device EE: %s Dev State: %s\n", + __get_str(name), + __print_symbolic(__entry->local_ee, MHI_EE_LIST), + __print_symbolic(__entry->state, MHI_STATE_LIST), + __print_symbolic(__entry->dev_ee, MHI_EE_LIST), + __print_symbolic(__entry->dev_state, MHI_STATE_LIST)) +); + +TRACE_EVENT(mhi_tryset_pm_state, + + TP_PROTO(struct mhi_controller *mhi_cntrl, int pm_state), + + TP_ARGS(mhi_cntrl, pm_state), + + TP_STRUCT__entry( + __string(name, mhi_cntrl->mhi_dev->name) + __field(int, pm_state) + ), + + TP_fast_assign( + __assign_str(name); + if (pm_state) + pm_state = __fls(pm_state); + __entry->pm_state = pm_state; + ), + + TP_printk("%s: PM state: %s\n", __get_str(name), + __print_symbolic(__entry->pm_state, MHI_PM_STATE_LIST)) +); + +DECLARE_EVENT_CLASS(mhi_process_event_ring, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp), + + TP_ARGS(mhi_cntrl, rp), + + TP_STRUCT__entry( + __string(name, mhi_cntrl->mhi_dev->name) + __field(__le32, dword0) + __field(__le32, dword1) + __field(int, state) + __field(__le64, ptr) + __field(void *, rp) + ), + + TP_fast_assign( + __assign_str(name); + __entry->rp = rp; + __entry->ptr = rp->ptr; + __entry->dword0 = rp->dword[0]; + __entry->dword1 = rp->dword[1]; + __entry->state = MHI_TRE_GET_EV_STATE(rp); + ), + + TP_printk("%s: TRE: 0x%p TRE buf: 0x%llx DWORD0: 0x%08x DWORD1: 0x%08x State: %s\n", + __get_str(name), __entry->rp, __entry->ptr, __entry->dword0, + __entry->dword1, __print_symbolic(__entry->state, MHI_STATE_LIST)) +); + +DEFINE_EVENT(mhi_process_event_ring, mhi_data_event, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp), + + TP_ARGS(mhi_cntrl, rp) +); + +DEFINE_EVENT(mhi_process_event_ring, mhi_ctrl_event, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *rp), + + TP_ARGS(mhi_cntrl, rp) +); + +DECLARE_EVENT_CLASS(mhi_update_channel_state, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state, + const char *reason), + + TP_ARGS(mhi_cntrl, mhi_chan, state, reason), + + TP_STRUCT__entry( + __string(name, mhi_cntrl->mhi_dev->name) + __field(int, ch_num) + __field(int, state) + __field(const char *, reason) + ), + + TP_fast_assign( + __assign_str(name); + __entry->ch_num = mhi_chan->chan; + __entry->state = state; + __entry->reason = reason; + ), + + TP_printk("%s: chan%d: %s state to: %s\n", + __get_str(name), __entry->ch_num, __entry->reason, + __print_symbolic(__entry->state, MHI_CH_STATE_TYPE_LIST)) +); + +DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_start, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state, + const char *reason), + + TP_ARGS(mhi_cntrl, mhi_chan, state, reason) +); + +DEFINE_EVENT(mhi_update_channel_state, mhi_channel_command_end, + + TP_PROTO(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, int state, + const char *reason), + + TP_ARGS(mhi_cntrl, mhi_chan, state, reason) +); + +TRACE_EVENT(mhi_pm_st_transition, + + TP_PROTO(struct mhi_controller *mhi_cntrl, int state), + + TP_ARGS(mhi_cntrl, state), + + TP_STRUCT__entry( + __string(name, mhi_cntrl->mhi_dev->name) + __field(int, state) + ), + + TP_fast_assign( + __assign_str(name); + __entry->state = state; + ), + + TP_printk("%s: Handling state transition: %s\n", __get_str(name), + __print_symbolic(__entry->state, DEV_ST_TRANSITION_LIST)) +); + +#endif +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/bus/mhi/host +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include <trace/define_trace.h> diff --git a/drivers/bus/mips_cdmm.c b/drivers/bus/mips_cdmm.c index 554e1992edd4..8baf14bd5eff 100644 --- a/drivers/bus/mips_cdmm.c +++ b/drivers/bus/mips_cdmm.c @@ -118,7 +118,7 @@ static struct attribute *mips_cdmm_dev_attrs[] = { }; ATTRIBUTE_GROUPS(mips_cdmm_dev); -struct bus_type mips_cdmm_bustype = { +const struct bus_type mips_cdmm_bustype = { .name = "cdmm", .dev_groups = mips_cdmm_dev_groups, .match = mips_cdmm_match, diff --git a/drivers/bus/stm32_etzpc.c b/drivers/bus/stm32_etzpc.c new file mode 100644 index 000000000000..7fc0f16960be --- /dev/null +++ b/drivers/bus/stm32_etzpc.c @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include "stm32_firewall.h" + +/* + * ETZPC registers + */ +#define ETZPC_DECPROT 0x10 +#define ETZPC_HWCFGR 0x3F0 + +/* + * HWCFGR register + */ +#define ETZPC_HWCFGR_NUM_TZMA GENMASK(7, 0) +#define ETZPC_HWCFGR_NUM_PER_SEC GENMASK(15, 8) +#define ETZPC_HWCFGR_NUM_AHB_SEC GENMASK(23, 16) +#define ETZPC_HWCFGR_CHUNKS1N4 GENMASK(31, 24) + +/* + * ETZPC miscellaneous + */ +#define ETZPC_PROT_MASK GENMASK(1, 0) +#define ETZPC_PROT_A7NS 0x3 +#define ETZPC_DECPROT_SHIFT 1 + +#define IDS_PER_DECPROT_REGS 16 + +static int stm32_etzpc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id) +{ + u32 offset, reg_offset, sec_val; + + if (firewall_id >= ctrl->max_entries) { + dev_err(ctrl->dev, "Invalid sys bus ID %u", firewall_id); + return -EINVAL; + } + + /* Check access configuration, 16 peripherals per register */ + reg_offset = ETZPC_DECPROT + 0x4 * (firewall_id / IDS_PER_DECPROT_REGS); + offset = (firewall_id % IDS_PER_DECPROT_REGS) << ETZPC_DECPROT_SHIFT; + + /* Verify peripheral is non-secure and attributed to cortex A7 */ + sec_val = (readl(ctrl->mmio + reg_offset) >> offset) & ETZPC_PROT_MASK; + if (sec_val != ETZPC_PROT_A7NS) { + dev_dbg(ctrl->dev, "Invalid bus configuration: reg_offset %#x, value %d\n", + reg_offset, sec_val); + return -EACCES; + } + + return 0; +} + +static void stm32_etzpc_release_access(struct stm32_firewall_controller *ctrl __maybe_unused, + u32 firewall_id __maybe_unused) +{ +} + +static int stm32_etzpc_probe(struct platform_device *pdev) +{ + struct stm32_firewall_controller *etzpc_controller; + struct device_node *np = pdev->dev.of_node; + u32 nb_per, nb_master; + struct resource *res; + void __iomem *mmio; + int rc; + + etzpc_controller = devm_kzalloc(&pdev->dev, sizeof(*etzpc_controller), GFP_KERNEL); + if (!etzpc_controller) + return -ENOMEM; + + mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + etzpc_controller->dev = &pdev->dev; + etzpc_controller->mmio = mmio; + etzpc_controller->name = dev_driver_string(etzpc_controller->dev); + etzpc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL; + etzpc_controller->grant_access = stm32_etzpc_grant_access; + etzpc_controller->release_access = stm32_etzpc_release_access; + + /* Get number of etzpc entries*/ + nb_per = FIELD_GET(ETZPC_HWCFGR_NUM_PER_SEC, + readl(etzpc_controller->mmio + ETZPC_HWCFGR)); + nb_master = FIELD_GET(ETZPC_HWCFGR_NUM_AHB_SEC, + readl(etzpc_controller->mmio + ETZPC_HWCFGR)); + etzpc_controller->max_entries = nb_per + nb_master; + + platform_set_drvdata(pdev, etzpc_controller); + + rc = stm32_firewall_controller_register(etzpc_controller); + if (rc) { + dev_err(etzpc_controller->dev, "Couldn't register as a firewall controller: %d", + rc); + return rc; + } + + rc = stm32_firewall_populate_bus(etzpc_controller); + if (rc) { + dev_err(etzpc_controller->dev, "Couldn't populate ETZPC bus: %d", + rc); + return rc; + } + + /* Populate all allowed nodes */ + return of_platform_populate(np, NULL, NULL, &pdev->dev); +} + +static const struct of_device_id stm32_etzpc_of_match[] = { + { .compatible = "st,stm32-etzpc" }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_etzpc_of_match); + +static struct platform_driver stm32_etzpc_driver = { + .probe = stm32_etzpc_probe, + .driver = { + .name = "stm32-etzpc", + .of_match_table = stm32_etzpc_of_match, + }, +}; +module_platform_driver(stm32_etzpc_driver); + +MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>"); +MODULE_DESCRIPTION("STMicroelectronics ETZPC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/bus/stm32_firewall.c b/drivers/bus/stm32_firewall.c new file mode 100644 index 000000000000..2fc9761dadec --- /dev/null +++ b/drivers/bus/stm32_firewall.c @@ -0,0 +1,294 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/bus/stm32_firewall_device.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/types.h> +#include <linux/slab.h> + +#include "stm32_firewall.h" + +/* Corresponds to STM32_FIREWALL_MAX_EXTRA_ARGS + firewall ID */ +#define STM32_FIREWALL_MAX_ARGS (STM32_FIREWALL_MAX_EXTRA_ARGS + 1) + +static LIST_HEAD(firewall_controller_list); +static DEFINE_MUTEX(firewall_controller_list_lock); + +/* Firewall device API */ + +int stm32_firewall_get_firewall(struct device_node *np, struct stm32_firewall *firewall, + unsigned int nb_firewall) +{ + struct stm32_firewall_controller *ctrl; + struct of_phandle_iterator it; + unsigned int i, j = 0; + int err; + + if (!firewall || !nb_firewall) + return -EINVAL; + + /* Parse property with phandle parsed out */ + of_for_each_phandle(&it, err, np, "access-controllers", "#access-controller-cells", 0) { + struct of_phandle_args provider_args; + struct device_node *provider = it.node; + const char *fw_entry; + bool match = false; + + if (err) { + pr_err("Unable to get access-controllers property for node %s\n, err: %d", + np->full_name, err); + of_node_put(provider); + return err; + } + + if (j >= nb_firewall) { + pr_err("Too many firewall controllers"); + of_node_put(provider); + return -EINVAL; + } + + provider_args.args_count = of_phandle_iterator_args(&it, provider_args.args, + STM32_FIREWALL_MAX_ARGS); + + /* Check if the parsed phandle corresponds to a registered firewall controller */ + mutex_lock(&firewall_controller_list_lock); + list_for_each_entry(ctrl, &firewall_controller_list, entry) { + if (ctrl->dev->of_node->phandle == it.phandle) { + match = true; + firewall[j].firewall_ctrl = ctrl; + break; + } + } + mutex_unlock(&firewall_controller_list_lock); + + if (!match) { + firewall[j].firewall_ctrl = NULL; + pr_err("No firewall controller registered for %s\n", np->full_name); + of_node_put(provider); + return -ENODEV; + } + + err = of_property_read_string_index(np, "access-controller-names", j, &fw_entry); + if (err == 0) + firewall[j].entry = fw_entry; + + /* Handle the case when there are no arguments given along with the phandle */ + if (provider_args.args_count < 0 || + provider_args.args_count > STM32_FIREWALL_MAX_ARGS) { + of_node_put(provider); + return -EINVAL; + } else if (provider_args.args_count == 0) { + firewall[j].extra_args_size = 0; + firewall[j].firewall_id = U32_MAX; + j++; + continue; + } + + /* The firewall ID is always the first argument */ + firewall[j].firewall_id = provider_args.args[0]; + + /* Extra args start at the second argument */ + for (i = 0; i < provider_args.args_count - 1; i++) + firewall[j].extra_args[i] = provider_args.args[i + 1]; + + /* Remove the firewall ID arg that is not an extra argument */ + firewall[j].extra_args_size = provider_args.args_count - 1; + + j++; + } + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_firewall_get_firewall); + +int stm32_firewall_grant_access(struct stm32_firewall *firewall) +{ + struct stm32_firewall_controller *firewall_controller; + + if (!firewall || firewall->firewall_id == U32_MAX) + return -EINVAL; + + firewall_controller = firewall->firewall_ctrl; + + if (!firewall_controller) + return -ENODEV; + + return firewall_controller->grant_access(firewall_controller, firewall->firewall_id); +} +EXPORT_SYMBOL_GPL(stm32_firewall_grant_access); + +int stm32_firewall_grant_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id) +{ + struct stm32_firewall_controller *firewall_controller; + + if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) + return -EINVAL; + + firewall_controller = firewall->firewall_ctrl; + + if (!firewall_controller) + return -ENODEV; + + return firewall_controller->grant_access(firewall_controller, subsystem_id); +} +EXPORT_SYMBOL_GPL(stm32_firewall_grant_access_by_id); + +void stm32_firewall_release_access(struct stm32_firewall *firewall) +{ + struct stm32_firewall_controller *firewall_controller; + + if (!firewall || firewall->firewall_id == U32_MAX) { + pr_debug("Incorrect arguments when releasing a firewall access\n"); + return; + } + + firewall_controller = firewall->firewall_ctrl; + + if (!firewall_controller) { + pr_debug("No firewall controller to release\n"); + return; + } + + firewall_controller->release_access(firewall_controller, firewall->firewall_id); +} +EXPORT_SYMBOL_GPL(stm32_firewall_release_access); + +void stm32_firewall_release_access_by_id(struct stm32_firewall *firewall, u32 subsystem_id) +{ + struct stm32_firewall_controller *firewall_controller; + + if (!firewall || subsystem_id == U32_MAX || firewall->firewall_id == U32_MAX) { + pr_debug("Incorrect arguments when releasing a firewall access"); + return; + } + + firewall_controller = firewall->firewall_ctrl; + + if (!firewall_controller) { + pr_debug("No firewall controller to release"); + return; + } + + firewall_controller->release_access(firewall_controller, subsystem_id); +} +EXPORT_SYMBOL_GPL(stm32_firewall_release_access_by_id); + +/* Firewall controller API */ + +int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller) +{ + struct stm32_firewall_controller *ctrl; + + if (!firewall_controller) + return -ENODEV; + + pr_info("Registering %s firewall controller\n", firewall_controller->name); + + mutex_lock(&firewall_controller_list_lock); + list_for_each_entry(ctrl, &firewall_controller_list, entry) { + if (ctrl == firewall_controller) { + pr_debug("%s firewall controller already registered\n", + firewall_controller->name); + mutex_unlock(&firewall_controller_list_lock); + return 0; + } + } + list_add_tail(&firewall_controller->entry, &firewall_controller_list); + mutex_unlock(&firewall_controller_list_lock); + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_firewall_controller_register); + +void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller) +{ + struct stm32_firewall_controller *ctrl; + bool controller_removed = false; + + if (!firewall_controller) { + pr_debug("Null reference while unregistering firewall controller\n"); + return; + } + + mutex_lock(&firewall_controller_list_lock); + list_for_each_entry(ctrl, &firewall_controller_list, entry) { + if (ctrl == firewall_controller) { + controller_removed = true; + list_del_init(&ctrl->entry); + break; + } + } + mutex_unlock(&firewall_controller_list_lock); + + if (!controller_removed) + pr_debug("There was no firewall controller named %s to unregister\n", + firewall_controller->name); +} +EXPORT_SYMBOL_GPL(stm32_firewall_controller_unregister); + +int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller) +{ + struct stm32_firewall *firewalls; + struct device_node *child; + struct device *parent; + unsigned int i; + int len; + int err; + + parent = firewall_controller->dev; + + dev_dbg(parent, "Populating %s system bus\n", dev_name(firewall_controller->dev)); + + for_each_available_child_of_node(dev_of_node(parent), child) { + /* The access-controllers property is mandatory for firewall bus devices */ + len = of_count_phandle_with_args(child, "access-controllers", + "#access-controller-cells"); + if (len <= 0) { + of_node_put(child); + return -EINVAL; + } + + firewalls = kcalloc(len, sizeof(*firewalls), GFP_KERNEL); + if (!firewalls) { + of_node_put(child); + return -ENOMEM; + } + + err = stm32_firewall_get_firewall(child, firewalls, (unsigned int)len); + if (err) { + kfree(firewalls); + of_node_put(child); + return err; + } + + for (i = 0; i < len; i++) { + if (firewall_controller->grant_access(firewall_controller, + firewalls[i].firewall_id)) { + /* + * Peripheral access not allowed or not defined. + * Mark the node as populated so platform bus won't probe it + */ + of_detach_node(child); + dev_err(parent, "%s: Device driver will not be probed\n", + child->full_name); + } + } + + kfree(firewalls); + } + + return 0; +} +EXPORT_SYMBOL_GPL(stm32_firewall_populate_bus); diff --git a/drivers/bus/stm32_firewall.h b/drivers/bus/stm32_firewall.h new file mode 100644 index 000000000000..e5fac85fe346 --- /dev/null +++ b/drivers/bus/stm32_firewall.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#ifndef _STM32_FIREWALL_H +#define _STM32_FIREWALL_H + +#include <linux/kernel.h> +#include <linux/list.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +/** + * STM32_PERIPHERAL_FIREWALL: This type of firewall protects peripherals + * STM32_MEMORY_FIREWALL: This type of firewall protects memories/subsets of memory + * zones + * STM32_NOTYPE_FIREWALL: Undefined firewall type + */ + +#define STM32_PERIPHERAL_FIREWALL BIT(1) +#define STM32_MEMORY_FIREWALL BIT(2) +#define STM32_NOTYPE_FIREWALL BIT(3) + +/** + * struct stm32_firewall_controller - Information on firewall controller supplying services + * + * @name: Name of the firewall controller + * @dev: Device reference of the firewall controller + * @mmio: Base address of the firewall controller + * @entry: List entry of the firewall controller list + * @type: Type of firewall + * @max_entries: Number of entries covered by the firewall + * @grant_access: Callback used to grant access for a device access against a + * firewall controller + * @release_access: Callback used to release resources taken by a device when access was + * granted + * @grant_memory_range_access: Callback used to grant access for a device to a given memory region + */ +struct stm32_firewall_controller { + const char *name; + struct device *dev; + void __iomem *mmio; + struct list_head entry; + unsigned int type; + unsigned int max_entries; + + int (*grant_access)(struct stm32_firewall_controller *ctrl, u32 id); + void (*release_access)(struct stm32_firewall_controller *ctrl, u32 id); + int (*grant_memory_range_access)(struct stm32_firewall_controller *ctrl, phys_addr_t paddr, + size_t size); +}; + +/** + * stm32_firewall_controller_register - Register a firewall controller to the STM32 firewall + * framework + * @firewall_controller: Firewall controller to register + * + * Returns 0 in case of success or -ENODEV if no controller was given. + */ +int stm32_firewall_controller_register(struct stm32_firewall_controller *firewall_controller); + +/** + * stm32_firewall_controller_unregister - Unregister a firewall controller from the STM32 + * firewall framework + * @firewall_controller: Firewall controller to unregister + */ +void stm32_firewall_controller_unregister(struct stm32_firewall_controller *firewall_controller); + +/** + * stm32_firewall_populate_bus - Populate device tree nodes that have a correct firewall + * configuration. This is used at boot-time only, as a sanity check + * between device tree and firewalls hardware configurations to + * prevent a kernel crash when a device driver is not granted access + * + * @firewall_controller: Firewall controller which nodes will be populated or not + * + * Returns 0 in case of success or appropriate errno code if error occurred. + */ +int stm32_firewall_populate_bus(struct stm32_firewall_controller *firewall_controller); + +#endif /* _STM32_FIREWALL_H */ diff --git a/drivers/bus/stm32_rifsc.c b/drivers/bus/stm32_rifsc.c new file mode 100644 index 000000000000..4cf1b60014b7 --- /dev/null +++ b/drivers/bus/stm32_rifsc.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2023, STMicroelectronics - All Rights Reserved + */ + +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include "stm32_firewall.h" + +/* + * RIFSC offset register + */ +#define RIFSC_RISC_SECCFGR0 0x10 +#define RIFSC_RISC_PRIVCFGR0 0x30 +#define RIFSC_RISC_PER0_CIDCFGR 0x100 +#define RIFSC_RISC_PER0_SEMCR 0x104 +#define RIFSC_RISC_HWCFGR2 0xFEC + +/* + * SEMCR register + */ +#define SEMCR_MUTEX BIT(0) + +/* + * HWCFGR2 register + */ +#define HWCFGR2_CONF1_MASK GENMASK(15, 0) +#define HWCFGR2_CONF2_MASK GENMASK(23, 16) +#define HWCFGR2_CONF3_MASK GENMASK(31, 24) + +/* + * RIFSC miscellaneous + */ +#define RIFSC_RISC_CFEN_MASK BIT(0) +#define RIFSC_RISC_SEM_EN_MASK BIT(1) +#define RIFSC_RISC_SCID_MASK GENMASK(6, 4) +#define RIFSC_RISC_SEML_SHIFT 16 +#define RIFSC_RISC_SEMWL_MASK GENMASK(23, 16) +#define RIFSC_RISC_PER_ID_MASK GENMASK(31, 24) + +#define RIFSC_RISC_PERx_CID_MASK (RIFSC_RISC_CFEN_MASK | \ + RIFSC_RISC_SEM_EN_MASK | \ + RIFSC_RISC_SCID_MASK | \ + RIFSC_RISC_SEMWL_MASK) + +#define IDS_PER_RISC_SEC_PRIV_REGS 32 + +/* RIF miscellaneous */ +/* + * CIDCFGR register fields + */ +#define CIDCFGR_CFEN BIT(0) +#define CIDCFGR_SEMEN BIT(1) +#define CIDCFGR_SEMWL(x) BIT(RIFSC_RISC_SEML_SHIFT + (x)) + +#define SEMWL_SHIFT 16 + +/* Compartiment IDs */ +#define RIF_CID0 0x0 +#define RIF_CID1 0x1 + +static bool stm32_rifsc_is_semaphore_available(void __iomem *addr) +{ + return !(readl(addr) & SEMCR_MUTEX); +} + +static int stm32_rif_acquire_semaphore(struct stm32_firewall_controller *stm32_firewall_controller, + int id) +{ + void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id; + + writel(SEMCR_MUTEX, addr); + + /* Check that CID1 has the semaphore */ + if (stm32_rifsc_is_semaphore_available(addr) || + FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) != RIF_CID1) + return -EACCES; + + return 0; +} + +static void stm32_rif_release_semaphore(struct stm32_firewall_controller *stm32_firewall_controller, + int id) +{ + void __iomem *addr = stm32_firewall_controller->mmio + RIFSC_RISC_PER0_SEMCR + 0x8 * id; + + if (stm32_rifsc_is_semaphore_available(addr)) + return; + + writel(SEMCR_MUTEX, addr); + + /* Ok if another compartment takes the semaphore before the check */ + WARN_ON(!stm32_rifsc_is_semaphore_available(addr) && + FIELD_GET(RIFSC_RISC_SCID_MASK, readl(addr)) == RIF_CID1); +} + +static int stm32_rifsc_grant_access(struct stm32_firewall_controller *ctrl, u32 firewall_id) +{ + struct stm32_firewall_controller *rifsc_controller = ctrl; + u32 reg_offset, reg_id, sec_reg_value, cid_reg_value; + int rc; + + if (firewall_id >= rifsc_controller->max_entries) { + dev_err(rifsc_controller->dev, "Invalid sys bus ID %u", firewall_id); + return -EINVAL; + } + + /* + * RIFSC_RISC_PRIVCFGRx and RIFSC_RISC_SECCFGRx both handle configuration access for + * 32 peripherals. On the other hand, there is one _RIFSC_RISC_PERx_CIDCFGR register + * per peripheral + */ + reg_id = firewall_id / IDS_PER_RISC_SEC_PRIV_REGS; + reg_offset = firewall_id % IDS_PER_RISC_SEC_PRIV_REGS; + sec_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_SECCFGR0 + 0x4 * reg_id); + cid_reg_value = readl(rifsc_controller->mmio + RIFSC_RISC_PER0_CIDCFGR + 0x8 * firewall_id); + + /* First check conditions for semaphore mode, which doesn't take into account static CID. */ + if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { + if (cid_reg_value & BIT(RIF_CID1 + SEMWL_SHIFT)) { + /* Static CID is irrelevant if semaphore mode */ + goto skip_cid_check; + } else { + dev_dbg(rifsc_controller->dev, + "Invalid bus semaphore configuration: index %d\n", firewall_id); + return -EACCES; + } + } + + /* + * Skip CID check if CID filtering isn't enabled or filtering is enabled on CID0, which + * corresponds to whatever CID. + */ + if (!(cid_reg_value & CIDCFGR_CFEN) || + FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) == RIF_CID0) + goto skip_cid_check; + + /* Coherency check with the CID configuration */ + if (FIELD_GET(RIFSC_RISC_SCID_MASK, cid_reg_value) != RIF_CID1) { + dev_dbg(rifsc_controller->dev, "Invalid CID configuration for peripheral: %d\n", + firewall_id); + return -EACCES; + } + +skip_cid_check: + /* Check security configuration */ + if (sec_reg_value & BIT(reg_offset)) { + dev_dbg(rifsc_controller->dev, + "Invalid security configuration for peripheral: %d\n", firewall_id); + return -EACCES; + } + + /* + * If the peripheral is in semaphore mode, take the semaphore so that + * the CID1 has the ownership. + */ + if ((cid_reg_value & CIDCFGR_SEMEN) && (cid_reg_value & CIDCFGR_CFEN)) { + rc = stm32_rif_acquire_semaphore(rifsc_controller, firewall_id); + if (rc) { + dev_err(rifsc_controller->dev, + "Couldn't acquire semaphore for peripheral: %d\n", firewall_id); + return rc; + } + } + + return 0; +} + +static void stm32_rifsc_release_access(struct stm32_firewall_controller *ctrl, u32 firewall_id) +{ + stm32_rif_release_semaphore(ctrl, firewall_id); +} + +static int stm32_rifsc_probe(struct platform_device *pdev) +{ + struct stm32_firewall_controller *rifsc_controller; + struct device_node *np = pdev->dev.of_node; + u32 nb_risup, nb_rimu, nb_risal; + struct resource *res; + void __iomem *mmio; + int rc; + + rifsc_controller = devm_kzalloc(&pdev->dev, sizeof(*rifsc_controller), GFP_KERNEL); + if (!rifsc_controller) + return -ENOMEM; + + mmio = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(mmio)) + return PTR_ERR(mmio); + + rifsc_controller->dev = &pdev->dev; + rifsc_controller->mmio = mmio; + rifsc_controller->name = dev_driver_string(rifsc_controller->dev); + rifsc_controller->type = STM32_PERIPHERAL_FIREWALL | STM32_MEMORY_FIREWALL; + rifsc_controller->grant_access = stm32_rifsc_grant_access; + rifsc_controller->release_access = stm32_rifsc_release_access; + + /* Get number of RIFSC entries*/ + nb_risup = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF1_MASK; + nb_rimu = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF2_MASK; + nb_risal = readl(rifsc_controller->mmio + RIFSC_RISC_HWCFGR2) & HWCFGR2_CONF3_MASK; + rifsc_controller->max_entries = nb_risup + nb_rimu + nb_risal; + + platform_set_drvdata(pdev, rifsc_controller); + + rc = stm32_firewall_controller_register(rifsc_controller); + if (rc) { + dev_err(rifsc_controller->dev, "Couldn't register as a firewall controller: %d", + rc); + return rc; + } + + rc = stm32_firewall_populate_bus(rifsc_controller); + if (rc) { + dev_err(rifsc_controller->dev, "Couldn't populate RIFSC bus: %d", + rc); + return rc; + } + + /* Populate all allowed nodes */ + return of_platform_populate(np, NULL, NULL, &pdev->dev); +} + +static const struct of_device_id stm32_rifsc_of_match[] = { + { .compatible = "st,stm32mp25-rifsc" }, + {} +}; +MODULE_DEVICE_TABLE(of, stm32_rifsc_of_match); + +static struct platform_driver stm32_rifsc_driver = { + .probe = stm32_rifsc_probe, + .driver = { + .name = "stm32-rifsc", + .of_match_table = stm32_rifsc_of_match, + }, +}; +module_platform_driver(stm32_rifsc_driver); + +MODULE_AUTHOR("Gatien Chevallier <gatien.chevallier@foss.st.com>"); +MODULE_DESCRIPTION("STMicroelectronics RIFSC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index fd3e9d82340a..1e29ba76615d 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -128,7 +128,7 @@ struct sunxi_rsb { }; /* bus / slave device related functions */ -static struct bus_type sunxi_rsb_bus; +static const struct bus_type sunxi_rsb_bus; static int sunxi_rsb_device_match(struct device *dev, struct device_driver *drv) { @@ -177,7 +177,7 @@ static int sunxi_rsb_device_modalias(const struct device *dev, struct kobj_ueven return of_device_uevent_modalias(dev, env); } -static struct bus_type sunxi_rsb_bus = { +static const struct bus_type sunxi_rsb_bus = { .name = RSB_CTRL_NAME, .match = sunxi_rsb_device_match, .probe = sunxi_rsb_device_probe, diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index 245e5e827d0d..8767e04d6c89 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -1,6 +1,17 @@ // SPDX-License-Identifier: GPL-2.0 /* * ti-sysc.c - Texas Instruments sysc interconnect target driver + * + * TI SoCs have an interconnect target wrapper IP for many devices. The wrapper + * IP manages clock gating, resets, and PM capabilities for the connected devices. + * + * Copyright (C) 2017-2024 Texas Instruments Incorporated - https://www.ti.com/ + * + * Many features are based on the earlier omap_hwmod arch code with thanks to all + * the people who developed and debugged the code over the years: + * + * Copyright (C) 2009-2011 Nokia Corporation + * Copyright (C) 2011-2021 Texas Instruments Incorporated - https://www.ti.com/ */ #include <linux/io.h> @@ -1458,8 +1469,7 @@ static int __maybe_unused sysc_noirq_suspend(struct device *dev) ddata = dev_get_drvdata(dev); - if (ddata->cfg.quirks & - (SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE)) + if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE) return 0; if (!ddata->enabled) @@ -1477,8 +1487,7 @@ static int __maybe_unused sysc_noirq_resume(struct device *dev) ddata = dev_get_drvdata(dev); - if (ddata->cfg.quirks & - (SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_NO_IDLE)) + if (ddata->cfg.quirks & SYSC_QUIRK_NO_IDLE) return 0; if (ddata->cfg.quirks & SYSC_QUIRK_REINIT_ON_RESUME) { @@ -1529,19 +1538,6 @@ struct sysc_revision_quirk { } static const struct sysc_revision_quirk sysc_revision_quirks[] = { - /* These drivers need to be fixed to not use pm_runtime_irq_safe() */ - SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), - SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), - /* Uarts on omap4 and later */ - SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff, - SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), - SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), - SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff, - SYSC_QUIRK_SWSUP_SIDLE_ACT | SYSC_QUIRK_LEGACY_IDLE), - /* Quirks that need to be set based on the module address */ SYSC_QUIRK("mcpdm", 0x40132000, 0, 0x10, -ENODEV, 0x50000800, 0xffffffff, SYSC_QUIRK_EXT_OPT_CLOCK | SYSC_QUIRK_NO_RESET_ON_INIT | @@ -1599,6 +1595,17 @@ static const struct sysc_revision_quirk sysc_revision_quirks[] = { SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), SYSC_QUIRK("sata", 0, 0xfc, 0x1100, -ENODEV, 0x5e412000, 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), + SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000046, 0xffffffff, + SYSC_QUIRK_SWSUP_SIDLE_ACT), + SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x00000052, 0xffffffff, + SYSC_QUIRK_SWSUP_SIDLE_ACT), + /* Uarts on omap4 and later */ + SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x50411e03, 0xffff00ff, + SYSC_QUIRK_SWSUP_SIDLE_ACT), + SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47422e03, 0xffffffff, + SYSC_QUIRK_SWSUP_SIDLE_ACT), + SYSC_QUIRK("uart", 0, 0x50, 0x54, 0x58, 0x47424e03, 0xffffffff, + SYSC_QUIRK_SWSUP_SIDLE_ACT), SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, 0x14, 0x50700100, 0xffffffff, SYSC_QUIRK_SWSUP_SIDLE | SYSC_QUIRK_SWSUP_MSTANDBY), SYSC_QUIRK("usb_host_hs", 0, 0, 0x10, -ENODEV, 0x50700101, 0xffffffff, @@ -2145,8 +2152,7 @@ static int sysc_reset(struct sysc *ddata) sysc_offset = ddata->offsets[SYSC_SYSCONFIG]; if (ddata->legacy_mode || - ddata->cap->regbits->srst_shift < 0 || - ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT) + ddata->cap->regbits->srst_shift < 0) return 0; sysc_mask = BIT(ddata->cap->regbits->srst_shift); @@ -2240,12 +2246,14 @@ static int sysc_init_module(struct sysc *ddata) goto err_main_clocks; } - error = sysc_reset(ddata); - if (error) - dev_err(ddata->dev, "Reset failed with %d\n", error); + if (!(ddata->cfg.quirks & SYSC_QUIRK_NO_RESET_ON_INIT)) { + error = sysc_reset(ddata); + if (error) + dev_err(ddata->dev, "Reset failed with %d\n", error); - if (error && !ddata->legacy_mode) - sysc_disable_module(ddata->dev); + if (error && !ddata->legacy_mode) + sysc_disable_module(ddata->dev); + } err_main_clocks: if (error) @@ -2400,7 +2408,7 @@ static int sysc_child_add_clocks(struct sysc *ddata, return 0; } -static struct device_type sysc_device_type = { +static const struct device_type sysc_device_type = { }; static struct sysc *sysc_child_to_parent(struct device *dev) @@ -2447,89 +2455,6 @@ static int __maybe_unused sysc_child_runtime_resume(struct device *dev) return pm_generic_runtime_resume(dev); } -#ifdef CONFIG_PM_SLEEP -static int sysc_child_suspend_noirq(struct device *dev) -{ - struct sysc *ddata; - int error; - - ddata = sysc_child_to_parent(dev); - - dev_dbg(ddata->dev, "%s %s\n", __func__, - ddata->name ? ddata->name : ""); - - error = pm_generic_suspend_noirq(dev); - if (error) { - dev_err(dev, "%s error at %i: %i\n", - __func__, __LINE__, error); - - return error; - } - - if (!pm_runtime_status_suspended(dev)) { - error = pm_generic_runtime_suspend(dev); - if (error) { - dev_dbg(dev, "%s busy at %i: %i\n", - __func__, __LINE__, error); - - return 0; - } - - error = sysc_runtime_suspend(ddata->dev); - if (error) { - dev_err(dev, "%s error at %i: %i\n", - __func__, __LINE__, error); - - return error; - } - - ddata->child_needs_resume = true; - } - - return 0; -} - -static int sysc_child_resume_noirq(struct device *dev) -{ - struct sysc *ddata; - int error; - - ddata = sysc_child_to_parent(dev); - - dev_dbg(ddata->dev, "%s %s\n", __func__, - ddata->name ? ddata->name : ""); - - if (ddata->child_needs_resume) { - ddata->child_needs_resume = false; - - error = sysc_runtime_resume(ddata->dev); - if (error) - dev_err(ddata->dev, - "%s runtime resume error: %i\n", - __func__, error); - - error = pm_generic_runtime_resume(dev); - if (error) - dev_err(ddata->dev, - "%s generic runtime resume: %i\n", - __func__, error); - } - - return pm_generic_resume_noirq(dev); -} -#endif - -static struct dev_pm_domain sysc_child_pm_domain = { - .ops = { - SET_RUNTIME_PM_OPS(sysc_child_runtime_suspend, - sysc_child_runtime_resume, - NULL) - USE_PLATFORM_PM_SLEEP_OPS - SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_child_suspend_noirq, - sysc_child_resume_noirq) - } -}; - /* Caller needs to take list_lock if ever used outside of cpu_pm */ static void sysc_reinit_modules(struct sysc_soc_info *soc) { @@ -2600,25 +2525,6 @@ out_unlock: mutex_unlock(&sysc_soc->list_lock); } -/** - * sysc_legacy_idle_quirk - handle children in omap_device compatible way - * @ddata: device driver data - * @child: child device driver - * - * Allow idle for child devices as done with _od_runtime_suspend(). - * Otherwise many child devices will not idle because of the permanent - * parent usecount set in pm_runtime_irq_safe(). - * - * Note that the long term solution is to just modify the child device - * drivers to not set pm_runtime_irq_safe() and then this can be just - * dropped. - */ -static void sysc_legacy_idle_quirk(struct sysc *ddata, struct device *child) -{ - if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) - dev_pm_domain_set(child, &sysc_child_pm_domain); -} - static int sysc_notifier_call(struct notifier_block *nb, unsigned long event, void *device) { @@ -2635,7 +2541,6 @@ static int sysc_notifier_call(struct notifier_block *nb, error = sysc_child_add_clocks(ddata, dev); if (error) return error; - sysc_legacy_idle_quirk(ddata, dev); break; default: break; @@ -2859,8 +2764,7 @@ static const struct sysc_capabilities sysc_34xx_sr = { .type = TI_SYSC_OMAP34XX_SR, .sysc_mask = SYSC_OMAP2_CLOCKACTIVITY, .regbits = &sysc_regbits_omap34xx_sr, - .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED | - SYSC_QUIRK_LEGACY_IDLE, + .mod_quirks = SYSC_QUIRK_USE_CLOCKACT | SYSC_QUIRK_UNCACHED, }; /* @@ -2881,13 +2785,12 @@ static const struct sysc_capabilities sysc_36xx_sr = { .type = TI_SYSC_OMAP36XX_SR, .sysc_mask = SYSC_OMAP3_SR_ENAWAKEUP, .regbits = &sysc_regbits_omap36xx_sr, - .mod_quirks = SYSC_QUIRK_UNCACHED | SYSC_QUIRK_LEGACY_IDLE, + .mod_quirks = SYSC_QUIRK_UNCACHED, }; static const struct sysc_capabilities sysc_omap4_sr = { .type = TI_SYSC_OMAP4_SR, .regbits = &sysc_regbits_omap36xx_sr, - .mod_quirks = SYSC_QUIRK_LEGACY_IDLE, }; /* diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index 4fa932cb0915..baf22a82c47a 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -39,45 +39,39 @@ struct ts_nbus { /* * request all gpios required by the bus. */ -static int ts_nbus_init_pdata(struct platform_device *pdev, struct ts_nbus - *ts_nbus) +static int ts_nbus_init_pdata(struct platform_device *pdev, + struct ts_nbus *ts_nbus) { ts_nbus->data = devm_gpiod_get_array(&pdev->dev, "ts,data", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->data)) { - dev_err(&pdev->dev, "failed to retrieve ts,data-gpio from dts\n"); - return PTR_ERR(ts_nbus->data); - } + if (IS_ERR(ts_nbus->data)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->data), + "failed to retrieve ts,data-gpio from dts\n"); ts_nbus->csn = devm_gpiod_get(&pdev->dev, "ts,csn", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->csn)) { - dev_err(&pdev->dev, "failed to retrieve ts,csn-gpio from dts\n"); - return PTR_ERR(ts_nbus->csn); - } + if (IS_ERR(ts_nbus->csn)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->csn), + "failed to retrieve ts,csn-gpio from dts\n"); ts_nbus->txrx = devm_gpiod_get(&pdev->dev, "ts,txrx", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->txrx)) { - dev_err(&pdev->dev, "failed to retrieve ts,txrx-gpio from dts\n"); - return PTR_ERR(ts_nbus->txrx); - } + if (IS_ERR(ts_nbus->txrx)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->txrx), + "failed to retrieve ts,txrx-gpio from dts\n"); ts_nbus->strobe = devm_gpiod_get(&pdev->dev, "ts,strobe", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->strobe)) { - dev_err(&pdev->dev, "failed to retrieve ts,strobe-gpio from dts\n"); - return PTR_ERR(ts_nbus->strobe); - } + if (IS_ERR(ts_nbus->strobe)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->strobe), + "failed to retrieve ts,strobe-gpio from dts\n"); ts_nbus->ale = devm_gpiod_get(&pdev->dev, "ts,ale", GPIOD_OUT_HIGH); - if (IS_ERR(ts_nbus->ale)) { - dev_err(&pdev->dev, "failed to retrieve ts,ale-gpio from dts\n"); - return PTR_ERR(ts_nbus->ale); - } + if (IS_ERR(ts_nbus->ale)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->ale), + "failed to retrieve ts,ale-gpio from dts\n"); ts_nbus->rdy = devm_gpiod_get(&pdev->dev, "ts,rdy", GPIOD_IN); - if (IS_ERR(ts_nbus->rdy)) { - dev_err(&pdev->dev, "failed to retrieve ts,rdy-gpio from dts\n"); - return PTR_ERR(ts_nbus->rdy); - } + if (IS_ERR(ts_nbus->rdy)) + return dev_err_probe(&pdev->dev, PTR_ERR(ts_nbus->rdy), + "failed to retrieve ts,rdy-gpio from dts\n"); return 0; } @@ -273,7 +267,7 @@ EXPORT_SYMBOL_GPL(ts_nbus_write); static int ts_nbus_probe(struct platform_device *pdev) { struct pwm_device *pwm; - struct pwm_args pargs; + struct pwm_state state; struct device *dev = &pdev->dev; struct ts_nbus *ts_nbus; int ret; @@ -289,32 +283,24 @@ static int ts_nbus_probe(struct platform_device *pdev) return ret; pwm = devm_pwm_get(dev, NULL); - if (IS_ERR(pwm)) { - ret = PTR_ERR(pwm); - if (ret != -EPROBE_DEFER) - dev_err(dev, "unable to request PWM\n"); - return ret; - } + if (IS_ERR(pwm)) + return dev_err_probe(dev, PTR_ERR(pwm), + "unable to request PWM\n"); - pwm_get_args(pwm, &pargs); - if (!pargs.period) { - dev_err(&pdev->dev, "invalid PWM period\n"); - return -EINVAL; - } + pwm_init_state(pwm, &state); + if (!state.period) + return dev_err_probe(dev, -EINVAL, "invalid PWM period\n"); - /* - * FIXME: pwm_apply_args() should be removed when switching to - * the atomic PWM API. - */ - pwm_apply_args(pwm); - ret = pwm_config(pwm, pargs.period, pargs.period); + state.duty_cycle = state.period; + state.enabled = true; + + ret = pwm_apply_state(pwm, &state); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, "failed to configure PWM\n"); /* * we can now start the FPGA and populate the peripherals. */ - pwm_enable(pwm); ts_nbus->pwm = pwm; /* @@ -324,7 +310,8 @@ static int ts_nbus_probe(struct platform_device *pdev) ret = of_platform_populate(dev->of_node, NULL, NULL, dev); if (ret < 0) - return ret; + return dev_err_probe(dev, ret, + "failed to populate platform devices on bus\n"); dev_info(dev, "initialized\n"); |