From 1f0bbf28940cf5edad90ab57b62aa8197bf5e836 Mon Sep 17 00:00:00 2001 From: Varun Prakash Date: Wed, 9 Aug 2023 15:56:45 +0530 Subject: nvmet-tcp: pass iov_len instead of sg->length to bvec_set_page() iov_len is the valid data length, so pass iov_len instead of sg->length to bvec_set_page(). Fixes: 5bfaba275ae6 ("nvmet-tcp: don't map pages which can't come from HIGHMEM") Signed-off-by: Rakshana Sridhar Signed-off-by: Varun Prakash Reviewed-by: Sagi Grimberg Reviewed-by: Christoph Hellwig Signed-off-by: Keith Busch --- drivers/nvme/target/tcp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 868aa4de2e4c..cd92d7ddf5ed 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -348,7 +348,7 @@ static void nvmet_tcp_build_pdu_iovec(struct nvmet_tcp_cmd *cmd) while (length) { u32 iov_len = min_t(u32, length, sg->length - sg_offset); - bvec_set_page(iov, sg_page(sg), sg->length, + bvec_set_page(iov, sg_page(sg), iov_len, sg->offset + sg_offset); length -= iov_len; -- cgit v1.2.3 From 71be868472dc5beb82feb4da2d3eb9cba785d660 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Sun, 20 Aug 2023 11:21:39 +0200 Subject: nvme: host: hwmon: constify pointers to hwmon_channel_info Statically allocated array of pointed to hwmon_channel_info can be made const for safety. Signed-off-by: Krzysztof Kozlowski Acked-by: Christoph Hellwig Acked-by: Guenter Roeck Signed-off-by: Keith Busch --- drivers/nvme/host/hwmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/hwmon.c b/drivers/nvme/host/hwmon.c index 316f3e4ca7cc..8df73a0b3980 100644 --- a/drivers/nvme/host/hwmon.c +++ b/drivers/nvme/host/hwmon.c @@ -187,7 +187,7 @@ static umode_t nvme_hwmon_is_visible(const void *_data, return 0; } -static const struct hwmon_channel_info *nvme_hwmon_info[] = { +static const struct hwmon_channel_info *const nvme_hwmon_info[] = { HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ), HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_MIN | -- cgit v1.2.3 From 8ae5b3a685dc59a8cf7ccfe0e850999ba9727a3c Mon Sep 17 00:00:00 2001 From: Nigel Kirkland Date: Thu, 17 Aug 2023 12:43:01 -0700 Subject: nvme-fc: Prevent null pointer dereference in nvme_fc_io_getuuid() The nvme_fc_fcp_op structure describing an AEN operation is initialized with a null request structure pointer. An FC LLDD may make a call to nvme_fc_io_getuuid passing a pointer to an nvmefc_fcp_req for an AEN operation. Add validation of the request structure pointer before dereference. Signed-off-by: Nigel Kirkland Reviewed-by: James Smart Signed-off-by: Keith Busch --- drivers/nvme/host/fc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/nvme/host/fc.c b/drivers/nvme/host/fc.c index 1cd2bf82319a..a15b37750d6e 100644 --- a/drivers/nvme/host/fc.c +++ b/drivers/nvme/host/fc.c @@ -1924,7 +1924,7 @@ char *nvme_fc_io_getuuid(struct nvmefc_fcp_req *req) struct nvme_fc_fcp_op *op = fcp_req_to_fcp_op(req); struct request *rq = op->rq; - if (!IS_ENABLED(CONFIG_BLK_CGROUP_FC_APPID) || !rq->bio) + if (!IS_ENABLED(CONFIG_BLK_CGROUP_FC_APPID) || !rq || !rq->bio) return NULL; return blkcg_get_fc_appid(rq->bio); } -- cgit v1.2.3 From 0c3b063ef4136191312a88ea7a670a6a2a2dae5a Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Thu, 24 Aug 2023 08:37:01 +0100 Subject: drm/drm_connector: Provide short description of param 'supported_colorspaces' Fixes the following W=1 kernel build warning(s): drivers/gpu/drm/drm_connector.c:2215: warning: Function parameter or member 'supported_colorspaces' not described in 'drm_mode_create_hdmi_colorspace_property' drivers/gpu/drm/drm_connector.c:2239: warning: Function parameter or member 'supported_colorspaces' not described in 'drm_mode_create_dp_colorspace_property' Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20230824073710.2677348-17-lee@kernel.org Signed-off-by: Maxime Ripard --- drivers/gpu/drm/drm_connector.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/drm_connector.c b/drivers/gpu/drm/drm_connector.c index 3ed4cfcb350c..f28725736237 100644 --- a/drivers/gpu/drm/drm_connector.c +++ b/drivers/gpu/drm/drm_connector.c @@ -2203,6 +2203,7 @@ static int drm_mode_create_colorspace_property(struct drm_connector *connector, /** * drm_mode_create_hdmi_colorspace_property - create hdmi colorspace property * @connector: connector to create the Colorspace property on. + * @supported_colorspaces: bitmap of supported color spaces * * Called by a driver the first time it's needed, must be attached to desired * HDMI connectors. @@ -2227,6 +2228,7 @@ EXPORT_SYMBOL(drm_mode_create_hdmi_colorspace_property); /** * drm_mode_create_dp_colorspace_property - create dp colorspace property * @connector: connector to create the Colorspace property on. + * @supported_colorspaces: bitmap of supported color spaces * * Called by a driver the first time it's needed, must be attached to desired * DP connectors. -- cgit v1.2.3 From 6428bc7bd3f35e43c8cb7359cb89d83248d339d2 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 30 Aug 2023 07:56:04 +0200 Subject: parisc: sba_iommu: Fix build warning if procfs if disabled Clean up the code, e.g. make proc_mckinley_root static, drop the now empty mckinley header file and remove some unneeded ifdefs around procfs functions. Signed-off-by: Helge Deller Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202308300800.Jod4sHzM-lkp@intel.com/ Fixes: 77e0ddf097d6 ("parisc: ccio-dma: Create private runway procfs root entry") --- arch/parisc/include/asm/mckinley.h | 8 -------- drivers/parisc/sba_iommu.c | 10 ++-------- 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 arch/parisc/include/asm/mckinley.h diff --git a/arch/parisc/include/asm/mckinley.h b/arch/parisc/include/asm/mckinley.h deleted file mode 100644 index 1314390b9034..000000000000 --- a/arch/parisc/include/asm/mckinley.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef ASM_PARISC_MCKINLEY_H -#define ASM_PARISC_MCKINLEY_H - -/* declared in arch/parisc/kernel/setup.c */ -extern struct proc_dir_entry * proc_mckinley_root; - -#endif /*ASM_PARISC_MCKINLEY_H*/ diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index f6b510675318..ee5a2f4b7474 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -46,8 +46,6 @@ #include #include -#include /* for proc_mckinley_root */ -#include /* for proc_runway_root */ #include /* for PAGE0 */ #include /* for PDC_MODEL_* */ #include /* for is_pdc_pat() */ @@ -122,7 +120,7 @@ MODULE_PARM_DESC(sba_reserve_agpgart, "Reserve half of IO pdir as AGPGART"); #endif static struct proc_dir_entry *proc_runway_root __ro_after_init; -struct proc_dir_entry *proc_mckinley_root __ro_after_init; +static struct proc_dir_entry *proc_mckinley_root __ro_after_init; /************************************ ** SBA register read and write support @@ -1899,9 +1897,7 @@ static int __init sba_driver_callback(struct parisc_device *dev) int i; char *version; void __iomem *sba_addr = ioremap(dev->hpa.start, SBA_FUNC_SIZE); -#ifdef CONFIG_PROC_FS - struct proc_dir_entry *root; -#endif + struct proc_dir_entry *root __maybe_unused; sba_dump_ranges(sba_addr); @@ -1967,7 +1963,6 @@ static int __init sba_driver_callback(struct parisc_device *dev) hppa_dma_ops = &sba_ops; -#ifdef CONFIG_PROC_FS switch (dev->id.hversion) { case PLUTO_MCKINLEY_PORT: if (!proc_mckinley_root) @@ -1985,7 +1980,6 @@ static int __init sba_driver_callback(struct parisc_device *dev) proc_create_single("sba_iommu", 0, root, sba_proc_info); proc_create_single("sba_iommu-bitmap", 0, root, sba_proc_bitmap_info); -#endif return 0; } -- cgit v1.2.3 From eb3255ee8f6f4691471a28fbf22db5e8901116cd Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 30 Aug 2023 08:10:01 +0200 Subject: parisc: sba: Fix compile warning wrt list of SBA devices Fix this makecheck warning: drivers/parisc/sba_iommu.c:98:19: warning: symbol 'sba_list' was not declared. Should it be static? Signed-off-by: Helge Deller --- arch/parisc/include/asm/ropes.h | 3 +++ drivers/char/agp/parisc-agp.c | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arch/parisc/include/asm/ropes.h b/arch/parisc/include/asm/ropes.h index fd96706c7234..d7b941cfbccd 100644 --- a/arch/parisc/include/asm/ropes.h +++ b/arch/parisc/include/asm/ropes.h @@ -86,6 +86,9 @@ struct sba_device { struct ioc ioc[MAX_IOC]; }; +/* list of SBA's in system, see drivers/parisc/sba_iommu.c */ +extern struct sba_device *sba_list; + #define ASTRO_RUNWAY_PORT 0x582 #define IKE_MERCED_PORT 0x803 #define REO_MERCED_PORT 0x804 diff --git a/drivers/char/agp/parisc-agp.c b/drivers/char/agp/parisc-agp.c index 514f9f287a78..c6f181702b9a 100644 --- a/drivers/char/agp/parisc-agp.c +++ b/drivers/char/agp/parisc-agp.c @@ -394,8 +394,6 @@ find_quicksilver(struct device *dev, void *data) static int __init parisc_agp_init(void) { - extern struct sba_device *sba_list; - int err = -1; struct parisc_device *sba = NULL, *lba = NULL; struct lba_device *lbadev = NULL; -- cgit v1.2.3 From c1ebb94071cb4455177bafa619423acb3494d15d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 30 Aug 2023 11:49:57 +0200 Subject: parisc: sba-iommu: Fix sparse warnigs Fix sparse warnings, as pdir is __le64 *. Signed-off-by: Helge Deller --- arch/parisc/include/asm/ropes.h | 4 ++-- drivers/parisc/iommu-helpers.h | 4 ++-- drivers/parisc/sba_iommu.c | 28 ++++++++++++++-------------- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/arch/parisc/include/asm/ropes.h b/arch/parisc/include/asm/ropes.h index d7b941cfbccd..e2d2d7e9bfde 100644 --- a/arch/parisc/include/asm/ropes.h +++ b/arch/parisc/include/asm/ropes.h @@ -29,7 +29,7 @@ struct ioc { void __iomem *ioc_hpa; /* I/O MMU base address */ char *res_map; /* resource map, bit == pdir entry */ - u64 *pdir_base; /* physical base address */ + __le64 *pdir_base; /* physical base address */ unsigned long ibase; /* pdir IOV Space base - shared w/lba_pci */ unsigned long imask; /* pdir IOV Space mask - shared w/lba_pci */ #ifdef ZX1_SUPPORT @@ -113,7 +113,7 @@ static inline int IS_PLUTO(struct parisc_device *d) { #define SBA_PDIR_VALID_BIT 0x8000000000000000ULL -#define SBA_AGPGART_COOKIE 0x0000badbadc0ffeeULL +#define SBA_AGPGART_COOKIE (__force __le64) 0x0000badbadc0ffeeULL #define SBA_FUNC_ID 0x0000 /* function id */ #define SBA_FCLASS 0x0008 /* function class, bist, header, rev... */ diff --git a/drivers/parisc/iommu-helpers.h b/drivers/parisc/iommu-helpers.h index 0905be256de0..a00c38b6224a 100644 --- a/drivers/parisc/iommu-helpers.h +++ b/drivers/parisc/iommu-helpers.h @@ -14,13 +14,13 @@ static inline unsigned int iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, unsigned long hint, - void (*iommu_io_pdir_entry)(u64 *, space_t, unsigned long, + void (*iommu_io_pdir_entry)(__le64 *, space_t, unsigned long, unsigned long)) { struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ unsigned int n_mappings = 0; unsigned long dma_offset = 0, dma_len = 0; - u64 *pdirp = NULL; + __le64 *pdirp = NULL; /* Horrible hack. For efficiency's sake, dma_sg starts one * entry below the true start (it is immediately incremented diff --git a/drivers/parisc/sba_iommu.c b/drivers/parisc/sba_iommu.c index ee5a2f4b7474..05e7103d1d40 100644 --- a/drivers/parisc/sba_iommu.c +++ b/drivers/parisc/sba_iommu.c @@ -202,7 +202,7 @@ static void sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) { /* start printing from lowest pde in rval */ - u64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]); + __le64 *ptr = &(ioc->pdir_base[pide & (~0U * BITS_PER_LONG)]); unsigned long *rptr = (unsigned long *) &(ioc->res_map[(pide >>3) & ~(sizeof(unsigned long) - 1)]); uint rcnt; @@ -569,7 +569,7 @@ typedef unsigned long space_t; */ static void -sba_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, +sba_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba, unsigned long hint) { u64 pa; /* physical address */ @@ -613,7 +613,7 @@ static void sba_mark_invalid(struct ioc *ioc, dma_addr_t iova, size_t byte_cnt) { u32 iovp = (u32) SBA_IOVP(ioc,iova); - u64 *pdir_ptr = &ioc->pdir_base[PDIR_INDEX(iovp)]; + __le64 *pdir_ptr = &ioc->pdir_base[PDIR_INDEX(iovp)]; #ifdef ASSERT_PDIR_SANITY /* Assert first pdir entry is set. @@ -714,7 +714,7 @@ sba_map_single(struct device *dev, void *addr, size_t size, unsigned long flags; dma_addr_t iovp; dma_addr_t offset; - u64 *pdir_start; + __le64 *pdir_start; int pide; ioc = GET_IOC(dev); @@ -1432,7 +1432,7 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ioc->pdir_size = pdir_size = (iova_space_size/IOVP_SIZE) * sizeof(u64); - DBG_INIT("%s() hpa 0x%lx mem %ldMB IOV %dMB (%d bits)\n", + DBG_INIT("%s() hpa %px mem %ldMB IOV %dMB (%d bits)\n", __func__, ioc->ioc_hpa, (unsigned long) totalram_pages() >> (20 - PAGE_SHIFT), @@ -1469,7 +1469,7 @@ sba_ioc_init(struct parisc_device *sba, struct ioc *ioc, int ioc_num) ioc->iovp_mask = ~(iova_space_mask + PAGE_SIZE - 1); #endif - DBG_INIT("%s() IOV base 0x%lx mask 0x%0lx\n", + DBG_INIT("%s() IOV base %#lx mask %#0lx\n", __func__, ioc->ibase, ioc->imask); /* @@ -1581,7 +1581,7 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, if (!IS_PLUTO(sba_dev->dev)) { ioc_ctl = READ_REG(sba_dev->sba_hpa+IOC_CTRL); - DBG_INIT("%s() hpa 0x%lx ioc_ctl 0x%Lx ->", + DBG_INIT("%s() hpa %px ioc_ctl 0x%Lx ->", __func__, sba_dev->sba_hpa, ioc_ctl); ioc_ctl &= ~(IOC_CTRL_RM | IOC_CTRL_NC | IOC_CTRL_CE); ioc_ctl |= IOC_CTRL_DD | IOC_CTRL_D4 | IOC_CTRL_TC; @@ -1666,14 +1666,14 @@ printk("sba_hw_init(): mem_boot 0x%x 0x%x 0x%x 0x%x\n", PAGE0->mem_boot.hpa, /* flush out the last writes */ READ_REG(sba_dev->ioc[i].ioc_hpa + ROPE7_CTL); - DBG_INIT(" ioc[%d] ROPE_CFG 0x%Lx ROPE_DBG 0x%Lx\n", + DBG_INIT(" ioc[%d] ROPE_CFG %#lx ROPE_DBG %lx\n", i, - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40), - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50) + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x40), + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x50) ); - DBG_INIT(" STATUS_CONTROL 0x%Lx FLUSH_CTRL 0x%Lx\n", - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108), - READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) + DBG_INIT(" STATUS_CONTROL %#lx FLUSH_CTRL %#lx\n", + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x108), + (unsigned long) READ_REG(sba_dev->ioc[i].ioc_hpa + 0x400) ); if (IS_PLUTO(sba_dev->dev)) { @@ -1737,7 +1737,7 @@ sba_common_init(struct sba_device *sba_dev) #ifdef ASSERT_PDIR_SANITY /* Mark first bit busy - ie no IOVA 0 */ sba_dev->ioc[i].res_map[0] = 0x80; - sba_dev->ioc[i].pdir_base[0] = 0xeeffc0addbba0080ULL; + sba_dev->ioc[i].pdir_base[0] = (__force __le64) 0xeeffc0addbba0080ULL; #endif /* Third (and last) part of PIRANHA BUG */ -- cgit v1.2.3 From 9a47a710cf517801a8b4fff9949c4cecb5fd019a Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 30 Aug 2023 11:36:52 +0200 Subject: parisc: ccio-dma: Fix sparse warnings Signed-off-by: Helge Deller --- drivers/parisc/ccio-dma.c | 18 +++++++++--------- drivers/parisc/iommu-helpers.h | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/drivers/parisc/ccio-dma.c b/drivers/parisc/ccio-dma.c index 509a4072d50a..9ce0d20a6c58 100644 --- a/drivers/parisc/ccio-dma.c +++ b/drivers/parisc/ccio-dma.c @@ -214,7 +214,7 @@ struct ioa_registers { struct ioc { struct ioa_registers __iomem *ioc_regs; /* I/O MMU base address */ u8 *res_map; /* resource map, bit == pdir entry */ - u64 *pdir_base; /* physical base address */ + __le64 *pdir_base; /* physical base address */ u32 pdir_size; /* bytes, function of IOV Space size */ u32 res_hint; /* next available IOVP - circular search */ @@ -339,7 +339,7 @@ ccio_alloc_range(struct ioc *ioc, struct device *dev, size_t size) BUG_ON(pages_needed == 0); BUG_ON((pages_needed * IOVP_SIZE) > DMA_CHUNK_SIZE); - DBG_RES("%s() size: %d pages_needed %d\n", + DBG_RES("%s() size: %zu pages_needed %d\n", __func__, size, pages_needed); /* @@ -427,7 +427,7 @@ ccio_free_range(struct ioc *ioc, dma_addr_t iova, unsigned long pages_mapped) BUG_ON((pages_mapped * IOVP_SIZE) > DMA_CHUNK_SIZE); BUG_ON(pages_mapped > BITS_PER_LONG); - DBG_RES("%s(): res_idx: %d pages_mapped %d\n", + DBG_RES("%s(): res_idx: %d pages_mapped %lu\n", __func__, res_idx, pages_mapped); #ifdef CCIO_COLLECT_STATS @@ -543,7 +543,7 @@ static u32 hint_lookup[] = { * index are bits 12:19 of the value returned by LCI. */ static void -ccio_io_pdir_entry(u64 *pdir_ptr, space_t sid, unsigned long vba, +ccio_io_pdir_entry(__le64 *pdir_ptr, space_t sid, unsigned long vba, unsigned long hints) { register unsigned long pa; @@ -719,7 +719,7 @@ ccio_map_single(struct device *dev, void *addr, size_t size, unsigned long flags; dma_addr_t iovp; dma_addr_t offset; - u64 *pdir_start; + __le64 *pdir_start; unsigned long hint = hint_lookup[(int)direction]; BUG_ON(!dev); @@ -746,8 +746,8 @@ ccio_map_single(struct device *dev, void *addr, size_t size, pdir_start = &(ioc->pdir_base[idx]); - DBG_RUN("%s() 0x%p -> 0x%lx size: %0x%x\n", - __func__, addr, (long)iovp | offset, size); + DBG_RUN("%s() %px -> %#lx size: %zu\n", + __func__, addr, (long)(iovp | offset), size); /* If not cacheline aligned, force SAFE_DMA on the whole mess */ if((size % L1_CACHE_BYTES) || ((unsigned long)addr % L1_CACHE_BYTES)) @@ -805,7 +805,7 @@ ccio_unmap_page(struct device *dev, dma_addr_t iova, size_t size, return; } - DBG_RUN("%s() iovp 0x%lx/%x\n", + DBG_RUN("%s() iovp %#lx/%zx\n", __func__, (long)iova, size); iova ^= offset; /* clear offset bits */ @@ -1283,7 +1283,7 @@ ccio_ioc_init(struct ioc *ioc) iova_space_size>>20, iov_order + PAGE_SHIFT); - ioc->pdir_base = (u64 *)__get_free_pages(GFP_KERNEL, + ioc->pdir_base = (__le64 *)__get_free_pages(GFP_KERNEL, get_order(ioc->pdir_size)); if(NULL == ioc->pdir_base) { panic("%s() could not allocate I/O Page Table\n", __func__); diff --git a/drivers/parisc/iommu-helpers.h b/drivers/parisc/iommu-helpers.h index a00c38b6224a..c43f1a212a5c 100644 --- a/drivers/parisc/iommu-helpers.h +++ b/drivers/parisc/iommu-helpers.h @@ -31,8 +31,8 @@ iommu_fill_pdir(struct ioc *ioc, struct scatterlist *startsg, int nents, unsigned long vaddr; long size; - DBG_RUN_SG(" %d : %08lx/%05x %p/%05x\n", nents, - (unsigned long)sg_dma_address(startsg), cnt, + DBG_RUN_SG(" %d : %08lx %p/%05x\n", nents, + (unsigned long)sg_dma_address(startsg), sg_virt(startsg), startsg->length ); -- cgit v1.2.3 From 927c6c8aa27c284a799b8c18784e37d3373af908 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Wed, 30 Aug 2023 11:59:55 +0200 Subject: parisc: iosapic.c: Fix sparse warnings Signed-off-by: Helge Deller --- drivers/parisc/iosapic.c | 4 ++-- drivers/parisc/iosapic_private.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index a7df764f1a72..a4011461189b 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -202,9 +202,9 @@ static inline void iosapic_write(void __iomem *iosapic, unsigned int reg, u32 va static DEFINE_SPINLOCK(iosapic_lock); -static inline void iosapic_eoi(void __iomem *addr, unsigned int data) +static inline void iosapic_eoi(__le32 __iomem *addr, __le32 data) { - __raw_writel(data, addr); + __raw_writel((__force u32)data, addr); } /* diff --git a/drivers/parisc/iosapic_private.h b/drivers/parisc/iosapic_private.h index 73ecc657ad95..bd8ff40162b4 100644 --- a/drivers/parisc/iosapic_private.h +++ b/drivers/parisc/iosapic_private.h @@ -118,8 +118,8 @@ struct iosapic_irt { struct vector_info { struct iosapic_info *iosapic; /* I/O SAPIC this vector is on */ struct irt_entry *irte; /* IRT entry */ - u32 __iomem *eoi_addr; /* precalculate EOI reg address */ - u32 eoi_data; /* IA64: ? PA: swapped txn_data */ + __le32 __iomem *eoi_addr; /* precalculate EOI reg address */ + __le32 eoi_data; /* IA64: ? PA: swapped txn_data */ int txn_irq; /* virtual IRQ number for processor */ ulong txn_addr; /* IA64: id_eid PA: partial HPA */ u32 txn_data; /* CPU interrupt bit */ -- cgit v1.2.3 From b137b9d60b8add5620a06c687a71ce18776730b0 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 31 Aug 2023 22:08:32 +0200 Subject: parisc: drivers: Fix sparse warning Fix "warning: directive in macro's argument list" warning. Signed-off-by: Helge Deller --- arch/parisc/kernel/drivers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/drivers.c b/arch/parisc/kernel/drivers.c index 8f4b77648491..ed8b75948061 100644 --- a/arch/parisc/kernel/drivers.c +++ b/arch/parisc/kernel/drivers.c @@ -925,9 +925,9 @@ static __init void qemu_header(void) pr_info("#define PARISC_MODEL \"%s\"\n\n", boot_cpu_data.pdc.sys_model_name); + #define p ((unsigned long *)&boot_cpu_data.pdc.model) pr_info("#define PARISC_PDC_MODEL 0x%lx, 0x%lx, 0x%lx, " "0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx, 0x%lx\n\n", - #define p ((unsigned long *)&boot_cpu_data.pdc.model) p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7], p[8]); #undef p -- cgit v1.2.3 From b1bef1388c427cdad7331a9c8eb4ebbbe5b954b0 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 31 Aug 2023 22:36:12 +0200 Subject: parisc: irq: Make irq_stack_union static to avoid sparse warning Signed-off-by: Helge Deller --- arch/parisc/kernel/irq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/parisc/kernel/irq.c b/arch/parisc/kernel/irq.c index 12c4d4104ade..2f81bfd4f15e 100644 --- a/arch/parisc/kernel/irq.c +++ b/arch/parisc/kernel/irq.c @@ -365,7 +365,7 @@ union irq_stack_union { volatile unsigned int lock[1]; }; -DEFINE_PER_CPU(union irq_stack_union, irq_stack_union) = { +static DEFINE_PER_CPU(union irq_stack_union, irq_stack_union) = { .slock = { 1,1,1,1 }, }; #endif -- cgit v1.2.3 From e2884fe84a83c562346eb9d92783a3576ce67177 Mon Sep 17 00:00:00 2001 From: Simon Pilkington Date: Fri, 1 Sep 2023 08:17:38 +0100 Subject: drm/amd: Make fence wait in suballocator uninterruptible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit c103a23f2f29 ("drm/amd: Convert amdgpu to use suballocation helper.") made the fence wait in amdgpu_sa_bo_new() interruptible but there is no code to handle an interrupt. This caused the kernel to randomly explode in high-VRAM-pressure situations so make it uninterruptible again. Signed-off-by: Simon Pilkington Fixes: c103a23f2f29 ("drm/amd: Convert amdgpu to use suballocation helper.") Reviewed-by: Christian König Signed-off-by: Christian König Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2761 CC: stable@vger.kernel.org # 6.4+ --- drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c index c6b4337eb20c..10df731998b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sa.c @@ -81,7 +81,7 @@ int amdgpu_sa_bo_new(struct amdgpu_sa_manager *sa_manager, unsigned int size) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, true, 0); + GFP_KERNEL, false, 0); if (IS_ERR(sa)) { *sa_bo = NULL; -- cgit v1.2.3 From dce19a3fede254fd11e0013f1c6fe3bfc37a4d73 Mon Sep 17 00:00:00 2001 From: David Gow Date: Wed, 30 Aug 2023 08:21:15 +0800 Subject: kunit: test: Make filter strings in executor_test writable KUnit's attribute filtering feature needs the filter strings passed in to be writable, as it modifies them in-place during parsing. This works for the filters passed on the kernel command line, but the string literals used in the executor tests are at least theoretically read-only (though they work on x86_64 for some reason). s390 wasn't fooled, and crashed when these tests were run. Use a 'char[]' instead, (and make an explicit variable for the current filter in parse_filter_attr_test), which will store the string in a writable segment. Fixes: 76066f93f1df ("kunit: add tests for filtering attributes") Closes: https://lore.kernel.org/linux-kselftest/55950256-c00a-4d21-a2c0-cf9f0e5b8a9a@roeck-us.net/ Signed-off-by: David Gow Tested-by: Guenter Roeck Reviewed-by: Rae Moar Signed-off-by: Shuah Khan --- lib/kunit/executor_test.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/lib/kunit/executor_test.c b/lib/kunit/executor_test.c index 4084071d0eb5..b4f6f96b2844 100644 --- a/lib/kunit/executor_test.c +++ b/lib/kunit/executor_test.c @@ -119,7 +119,7 @@ static void parse_filter_attr_test(struct kunit *test) { int j, filter_count; struct kunit_attr_filter *parsed_filters; - char *filters = "speed>slow, module!=example"; + char filters[] = "speed>slow, module!=example", *filter = filters; int err = 0; filter_count = kunit_get_filter_count(filters); @@ -128,7 +128,7 @@ static void parse_filter_attr_test(struct kunit *test) parsed_filters = kunit_kcalloc(test, filter_count, sizeof(*parsed_filters), GFP_KERNEL); for (j = 0; j < filter_count; j++) { - parsed_filters[j] = kunit_next_attr_filter(&filters, &err); + parsed_filters[j] = kunit_next_attr_filter(&filter, &err); KUNIT_ASSERT_EQ_MSG(test, err, 0, "failed to parse filter '%s'", filters[j]); } @@ -154,6 +154,7 @@ static void filter_attr_test(struct kunit *test) .start = subsuite, .end = &subsuite[2], }; struct kunit_suite_set got; + char filter[] = "speed>slow"; int err = 0; subsuite[0] = alloc_fake_suite(test, "normal_suite", dummy_attr_test_cases); @@ -168,7 +169,7 @@ static void filter_attr_test(struct kunit *test) * attribute is unset and thus, the filtering is based on the parent attribute * of slow. */ - got = kunit_filter_suites(&suite_set, NULL, "speed>slow", NULL, &err); + got = kunit_filter_suites(&suite_set, NULL, filter, NULL, &err); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start); KUNIT_ASSERT_EQ(test, err, 0); kfree_at_end(test, got.start); @@ -191,12 +192,13 @@ static void filter_attr_empty_test(struct kunit *test) .start = subsuite, .end = &subsuite[2], }; struct kunit_suite_set got; + char filter[] = "module!=dummy"; int err = 0; subsuite[0] = alloc_fake_suite(test, "suite1", dummy_attr_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_attr_test_cases); - got = kunit_filter_suites(&suite_set, NULL, "module!=dummy", NULL, &err); + got = kunit_filter_suites(&suite_set, NULL, filter, NULL, &err); KUNIT_ASSERT_EQ(test, err, 0); kfree_at_end(test, got.start); /* just in case */ @@ -211,12 +213,13 @@ static void filter_attr_skip_test(struct kunit *test) .start = subsuite, .end = &subsuite[1], }; struct kunit_suite_set got; + char filter[] = "speed>slow"; int err = 0; subsuite[0] = alloc_fake_suite(test, "suite", dummy_attr_test_cases); /* Want: suite(slow, normal), NULL -> suite(slow with SKIP, normal), NULL */ - got = kunit_filter_suites(&suite_set, NULL, "speed>slow", "skip", &err); + got = kunit_filter_suites(&suite_set, NULL, filter, "skip", &err); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start); KUNIT_ASSERT_EQ(test, err, 0); kfree_at_end(test, got.start); -- cgit v1.2.3 From f8858d96061f5942216c6abb0194c3ea7b78e1e8 Mon Sep 17 00:00:00 2001 From: Shrikanth Hegde Date: Sat, 2 Sep 2023 13:42:04 +0530 Subject: sched/fair: Optimize should_we_balance() for large SMT systems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit should_we_balance() is called in load_balance() to find out if the CPU that is trying to do the load balance is the right one or not. With commit: b1bfeab9b002("sched/fair: Consider the idle state of the whole core for load balance") the code tries to find an idle core to do the load balancing and falls back on an idle sibling CPU if there is no idle core. However, on larger SMT systems, it could be needlessly iterating to find a idle by scanning all the CPUs in an non-idle core. If the core is not idle, and first SMT sibling which is idle has been found, then its not needed to check other SMT siblings for idleness Lets say in SMT4, Core0 has 0,2,4,6 and CPU0 is BUSY and rest are IDLE. balancing domain is MC/DIE. CPU2 will be set as the first idle_smt and same process would be repeated for CPU4 and CPU6 but this is unnecessary. Since calling is_core_idle loops through all CPU's in the SMT mask, effect is multiplied by weight of smt_mask. For example,when say 1 CPU is busy, we would skip loop for 2 CPU's and skip iterating over 8CPU's. That effect would be more in DIE/NUMA domain where there are more cores. Testing and performance evaluation ================================== The test has been done on this system which has 12 cores, i.e 24 small cores with SMT=4: lscpu Architecture: ppc64le Byte Order: Little Endian CPU(s): 96 On-line CPU(s) list: 0-95 Model name: POWER10 (architected), altivec supported Thread(s) per core: 8 Used funclatency bcc tool to evaluate the time taken by should_we_balance(). For base tip/sched/core the time taken is collected by making the should_we_balance() noinline. time is in nanoseconds. The values are collected by running the funclatency tracer for 60 seconds. values are average of 3 such runs. This represents the expected reduced time with patch. tip/sched/core was at commit: 2f88c8e802c8 ("sched/eevdf/doc: Modify the documented knob to base_slice_ns as well") Results: ------------------------------------------------------------------------------ workload tip/sched/core with_patch(%gain) ------------------------------------------------------------------------------ idle system 809.3 695.0(16.45) stress ng – 12 threads -l 100 1013.5 893.1(13.49) stress ng – 24 threads -l 100 1073.5 980.0(9.54) stress ng – 48 threads -l 100 683.0 641.0(6.55) stress ng – 96 threads -l 100 2421.0 2300(5.26) stress ng – 96 threads -l 15 375.5 377.5(-0.53) stress ng – 96 threads -l 25 635.5 637.5(-0.31) stress ng – 96 threads -l 35 934.0 891.0(4.83) Ran schbench(old), hackbench and stress_ng to evaluate the workload performance between tip/sched/core and with patch. No modification to tip/sched/core TL;DR: Good improvement is seen with schbench. when hackbench and stress_ng runs for longer good improvement is seen. ------------------------------------------------------------------------------ schbench(old) tip +patch(%gain) 10 iterations sched/core ------------------------------------------------------------------------------ 1 Threads 50.0th: 8.00 9.00(-12.50) 75.0th: 9.60 9.00(6.25) 90.0th: 11.80 10.20(13.56) 95.0th: 12.60 10.40(17.46) 99.0th: 13.60 11.90(12.50) 99.5th: 14.10 12.60(10.64) 99.9th: 15.90 14.60(8.18) 2 Threads 50.0th: 9.90 9.20(7.07) 75.0th: 12.60 10.10(19.84) 90.0th: 15.50 12.00(22.58) 95.0th: 17.70 14.00(20.90) 99.0th: 21.20 16.90(20.28) 99.5th: 22.60 17.50(22.57) 99.9th: 30.40 19.40(36.18) 4 Threads 50.0th: 12.50 10.60(15.20) 75.0th: 15.30 12.00(21.57) 90.0th: 18.60 14.10(24.19) 95.0th: 21.30 16.20(23.94) 99.0th: 26.00 20.70(20.38) 99.5th: 27.60 22.50(18.48) 99.9th: 33.90 31.40(7.37) 8 Threads 50.0th: 16.30 14.30(12.27) 75.0th: 20.20 17.40(13.86) 90.0th: 24.50 21.90(10.61) 95.0th: 27.30 24.70(9.52) 99.0th: 35.00 31.20(10.86) 99.5th: 46.40 33.30(28.23) 99.9th: 89.30 57.50(35.61) 16 Threads 50.0th: 22.70 20.70(8.81) 75.0th: 30.10 27.40(8.97) 90.0th: 36.00 32.80(8.89) 95.0th: 39.60 36.40(8.08) 99.0th: 49.20 44.10(10.37) 99.5th: 64.90 50.50(22.19) 99.9th: 143.50 100.60(29.90) 32 Threads 50.0th: 34.60 35.50(-2.60) 75.0th: 48.20 50.50(-4.77) 90.0th: 59.20 62.40(-5.41) 95.0th: 65.20 69.00(-5.83) 99.0th: 80.40 83.80(-4.23) 99.5th: 102.10 98.90(3.13) 99.9th: 727.10 506.80(30.30) schbench does improve in general. There is some run to run variation with schbench. Did a validation run to confirm that trend is similar. ------------------------------------------------------------------------------ hackbench tip +patch(%gain) 20 iterations, 50000 loops sched/core ------------------------------------------------------------------------------ Process 10 groups : 11.74 11.70(0.34) Process 20 groups : 22.73 22.69(0.18) Process 30 groups : 33.39 33.40(-0.03) Process 40 groups : 43.73 43.61(0.27) Process 50 groups : 53.82 54.35(-0.98) Process 60 groups : 64.16 65.29(-1.76) thread 10 Time : 12.81 12.79(0.16) thread 20 Time : 24.63 24.47(0.65) Process(Pipe) 10 Time : 6.40 6.34(0.94) Process(Pipe) 20 Time : 10.62 10.63(-0.09) Process(Pipe) 30 Time : 15.09 14.84(1.66) Process(Pipe) 40 Time : 19.42 19.01(2.11) Process(Pipe) 50 Time : 24.04 23.34(2.91) Process(Pipe) 60 Time : 28.94 27.51(4.94) thread(Pipe) 10 Time : 6.96 6.87(1.29) thread(Pipe) 20 Time : 11.74 11.73(0.09) hackbench shows slight improvement with pipe. Slight degradation in process. ------------------------------------------------------------------------------ stress_ng tip +patch(%gain) 10 iterations 100000 cpu_ops sched/core ------------------------------------------------------------------------------ --cpu=96 -util=100 Time taken : 5.30, 5.01(5.47) --cpu=48 -util=100 Time taken : 7.94, 6.73(15.24) --cpu=24 -util=100 Time taken : 11.67, 8.75(25.02) --cpu=12 -util=100 Time taken : 15.71, 15.02(4.39) --cpu=96 -util=10 Time taken : 22.71, 22.19(2.29) --cpu=96 -util=20 Time taken : 12.14, 12.37(-1.89) --cpu=96 -util=30 Time taken : 8.76, 8.86(-1.14) --cpu=96 -util=40 Time taken : 7.13, 7.14(-0.14) --cpu=96 -util=50 Time taken : 6.10, 6.13(-0.49) --cpu=96 -util=60 Time taken : 5.42, 5.41(0.18) --cpu=96 -util=70 Time taken : 4.94, 4.94(0.00) --cpu=96 -util=80 Time taken : 4.56, 4.53(0.66) --cpu=96 -util=90 Time taken : 4.27, 4.26(0.23) Good improvement seen with 24 CPUs. In this case only one CPU is busy, and no core is idle. Decent improvement with 100% utilization case. no difference in other utilization. Fixes: b1bfeab9b002 ("sched/fair: Consider the idle state of the whole core for load balance") Signed-off-by: Shrikanth Hegde Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/20230902081204.232218-1-sshegde@linux.vnet.ibm.com --- kernel/sched/fair.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 8dbff6e7ad4f..33a2b6bba676 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -6619,6 +6619,7 @@ dequeue_throttle: /* Working cpumask for: load_balance, load_balance_newidle. */ static DEFINE_PER_CPU(cpumask_var_t, load_balance_mask); static DEFINE_PER_CPU(cpumask_var_t, select_rq_mask); +static DEFINE_PER_CPU(cpumask_var_t, should_we_balance_tmpmask); #ifdef CONFIG_NO_HZ_COMMON @@ -10917,6 +10918,7 @@ static int active_load_balance_cpu_stop(void *data); static int should_we_balance(struct lb_env *env) { + struct cpumask *swb_cpus = this_cpu_cpumask_var_ptr(should_we_balance_tmpmask); struct sched_group *sg = env->sd->groups; int cpu, idle_smt = -1; @@ -10940,8 +10942,9 @@ static int should_we_balance(struct lb_env *env) return 1; } + cpumask_copy(swb_cpus, group_balance_mask(sg)); /* Try to find first idle CPU */ - for_each_cpu_and(cpu, group_balance_mask(sg), env->cpus) { + for_each_cpu_and(cpu, swb_cpus, env->cpus) { if (!idle_cpu(cpu)) continue; @@ -10953,6 +10956,14 @@ static int should_we_balance(struct lb_env *env) if (!(env->sd->flags & SD_SHARE_CPUCAPACITY) && !is_core_idle(cpu)) { if (idle_smt == -1) idle_smt = cpu; + /* + * If the core is not idle, and first SMT sibling which is + * idle has been found, then its not needed to check other + * SMT siblings for idleness: + */ +#ifdef CONFIG_SCHED_SMT + cpumask_andnot(swb_cpus, swb_cpus, cpu_smt_mask(cpu)); +#endif continue; } @@ -12918,6 +12929,8 @@ __init void init_sched_fair_class(void) for_each_possible_cpu(i) { zalloc_cpumask_var_node(&per_cpu(load_balance_mask, i), GFP_KERNEL, cpu_to_node(i)); zalloc_cpumask_var_node(&per_cpu(select_rq_mask, i), GFP_KERNEL, cpu_to_node(i)); + zalloc_cpumask_var_node(&per_cpu(should_we_balance_tmpmask, i), + GFP_KERNEL, cpu_to_node(i)); #ifdef CONFIG_CFS_BANDWIDTH INIT_CSD(&cpu_rq(i)->cfsb_csd, __cfsb_csd_unthrottle, cpu_rq(i)); -- cgit v1.2.3 From 45dc8fc07d01b6786db88b5b176c67f9e3487d1e Mon Sep 17 00:00:00 2001 From: Sudip Mukherjee Date: Sat, 2 Sep 2023 10:51:02 +0100 Subject: fbdev/g364fb: fix build failure with mips Fix the typo which resulted in the driver using FB_DEFAULT_IOMEM_HELPERS instead of FB_DEFAULT_IOMEM_OPS as the fbdev I/O helpers. Fixes: 501126083855 ("fbdev/g364fb: Use fbdev I/O helpers") Suggested-by: Linus Torvalds Signed-off-by: Sudip Mukherjee Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20230902095102.5908-1-sudip.mukherjee@codethink.co.uk --- drivers/video/fbdev/g364fb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/video/fbdev/g364fb.c b/drivers/video/fbdev/g364fb.c index 7a1013b22fa7..ee6fe51e0a6b 100644 --- a/drivers/video/fbdev/g364fb.c +++ b/drivers/video/fbdev/g364fb.c @@ -112,7 +112,7 @@ static int g364fb_blank(int blank, struct fb_info *info); static const struct fb_ops g364fb_ops = { .owner = THIS_MODULE, - FB_DEFAULT_IOMEM_HELPERS, + FB_DEFAULT_IOMEM_OPS, .fb_setcolreg = g364fb_setcolreg, .fb_pan_display = g364fb_pan_display, .fb_blank = g364fb_blank, -- cgit v1.2.3 From 7583028d359db3cd0072badcc576b4f9455fd27a Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Mon, 4 Sep 2023 10:14:20 +0800 Subject: drm: gm12u320: Fix the timeout usage for usb_bulk_msg() The timeout arg of usb_bulk_msg() is ms already, which has been converted to jiffies by msecs_to_jiffies() in usb_start_wait_urb(). So fix the usage by removing the redundant msecs_to_jiffies() in the macros. And as Hans suggested, also remove msecs_to_jiffies() for the IDLE_TIMEOUT macro to make it consistent here and so change IDLE_TIMEOUT to msecs_to_jiffies(IDLE_TIMEOUT) where it is used. Fixes: e4f86e437164 ("drm: Add Grain Media GM12U320 driver v2") Signed-off-by: Jinjie Ruan Suggested-by: Hans de Goede Reviewed-by: Hans de Goede Signed-off-by: Thomas Zimmermann Link: https://patchwork.freedesktop.org/patch/msgid/20230904021421.1663892-1-ruanjinjie@huawei.com --- drivers/gpu/drm/tiny/gm12u320.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/tiny/gm12u320.c b/drivers/gpu/drm/tiny/gm12u320.c index c5bb683e440c..0187539ff5ea 100644 --- a/drivers/gpu/drm/tiny/gm12u320.c +++ b/drivers/gpu/drm/tiny/gm12u320.c @@ -70,10 +70,10 @@ MODULE_PARM_DESC(eco_mode, "Turn on Eco mode (less bright, more silent)"); #define READ_STATUS_SIZE 13 #define MISC_VALUE_SIZE 4 -#define CMD_TIMEOUT msecs_to_jiffies(200) -#define DATA_TIMEOUT msecs_to_jiffies(1000) -#define IDLE_TIMEOUT msecs_to_jiffies(2000) -#define FIRST_FRAME_TIMEOUT msecs_to_jiffies(2000) +#define CMD_TIMEOUT 200 +#define DATA_TIMEOUT 1000 +#define IDLE_TIMEOUT 2000 +#define FIRST_FRAME_TIMEOUT 2000 #define MISC_REQ_GET_SET_ECO_A 0xff #define MISC_REQ_GET_SET_ECO_B 0x35 @@ -389,7 +389,7 @@ static void gm12u320_fb_update_work(struct work_struct *work) * switches back to showing its logo. */ queue_delayed_work(system_long_wq, &gm12u320->fb_update.work, - IDLE_TIMEOUT); + msecs_to_jiffies(IDLE_TIMEOUT)); return; err: -- cgit v1.2.3 From ab048302026d7701e7fbd718917e0dbcff0c4223 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Mon, 4 Sep 2023 14:17:56 +0300 Subject: ovl: fix failed copyup of fileattr on a symlink Some local filesystems support setting persistent fileattr flags (e.g. FS_NOATIME_FL) on directories and regular files via ioctl. Some of those persistent fileattr flags are reflected to vfs as in-memory inode flags (e.g. S_NOATIME). Overlayfs uses the in-memory inode flags (e.g. S_NOATIME) on a lower file as an indication that a the lower file may have persistent inode fileattr flags (e.g. FS_NOATIME_FL) that need to be copied to upper file. However, in some cases, the S_NOATIME in-memory flag could be a false indication for persistent FS_NOATIME_FL fileattr. For example, with NFS and FUSE lower fs, as was the case in the two bug reports, the S_NOATIME flag is set unconditionally for all inodes. Users cannot set persistent fileattr flags on symlinks and special files, but in some local fs, such as ext4/btrfs/tmpfs, the FS_NOATIME_FL fileattr flag are inheritted to symlinks and special files from parent directory. In both cases described above, when lower symlink has the S_NOATIME flag, overlayfs will try to copy the symlink's fileattrs and fail with error ENOXIO, because it could not open the symlink for the ioctl security hook. To solve this failure, do not attempt to copyup fileattrs for anything other than directories and regular files. Reported-by: Ruiwen Zhao Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217850 Fixes: 72db82115d2b ("ovl: copy up sync/noatime fileattr flags") Cc: # v5.15 Reviewed-by: Miklos Szeredi Signed-off-by: Amir Goldstein --- fs/overlayfs/copy_up.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/overlayfs/copy_up.c b/fs/overlayfs/copy_up.c index bae404a1bad4..d1761ec5866a 100644 --- a/fs/overlayfs/copy_up.c +++ b/fs/overlayfs/copy_up.c @@ -618,7 +618,8 @@ static int ovl_copy_up_metadata(struct ovl_copy_up_ctx *c, struct dentry *temp) if (err) return err; - if (inode->i_flags & OVL_COPY_I_FLAGS_MASK) { + if (inode->i_flags & OVL_COPY_I_FLAGS_MASK && + (S_ISREG(c->stat.mode) || S_ISDIR(c->stat.mode))) { /* * Copy the fileattr inode flags that are the source of already * copied i_flags -- cgit v1.2.3 From 724768a39374d35b70eaeae8dd87048a2ec7ae8e Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Tue, 22 Aug 2023 20:50:59 +0300 Subject: ovl: fix incorrect fdput() on aio completion ovl_{read,write}_iter() always call fdput(real) to put one or zero refcounts of the real file, but for aio, whether it was submitted or not, ovl_aio_put() also calls fdput(), which is not balanced. This is only a problem in the less common case when FDPUT_FPUT flag is set. To fix the problem use get_file() to take file refcount and use fput() instead of fdput() in ovl_aio_put(). Fixes: 2406a307ac7d ("ovl: implement async IO routines") Cc: # v5.6 Reviewed-by: Miklos Szeredi Signed-off-by: Amir Goldstein --- fs/overlayfs/file.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/fs/overlayfs/file.c b/fs/overlayfs/file.c index 3b4cc633d763..4193633c4c7a 100644 --- a/fs/overlayfs/file.c +++ b/fs/overlayfs/file.c @@ -19,7 +19,6 @@ struct ovl_aio_req { struct kiocb iocb; refcount_t ref; struct kiocb *orig_iocb; - struct fd fd; }; static struct kmem_cache *ovl_aio_request_cachep; @@ -280,7 +279,7 @@ static rwf_t ovl_iocb_to_rwf(int ifl) static inline void ovl_aio_put(struct ovl_aio_req *aio_req) { if (refcount_dec_and_test(&aio_req->ref)) { - fdput(aio_req->fd); + fput(aio_req->iocb.ki_filp); kmem_cache_free(ovl_aio_request_cachep, aio_req); } } @@ -342,10 +341,9 @@ static ssize_t ovl_read_iter(struct kiocb *iocb, struct iov_iter *iter) if (!aio_req) goto out; - aio_req->fd = real; real.flags = 0; aio_req->orig_iocb = iocb; - kiocb_clone(&aio_req->iocb, iocb, real.file); + kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); aio_req->iocb.ki_complete = ovl_aio_rw_complete; refcount_set(&aio_req->ref, 2); ret = vfs_iocb_iter_read(real.file, &aio_req->iocb, iter); @@ -409,10 +407,9 @@ static ssize_t ovl_write_iter(struct kiocb *iocb, struct iov_iter *iter) if (!aio_req) goto out; - aio_req->fd = real; real.flags = 0; aio_req->orig_iocb = iocb; - kiocb_clone(&aio_req->iocb, iocb, real.file); + kiocb_clone(&aio_req->iocb, iocb, get_file(real.file)); aio_req->iocb.ki_flags = ifl; aio_req->iocb.ki_complete = ovl_aio_rw_complete; refcount_set(&aio_req->ref, 2); -- cgit v1.2.3 From 0b0747d507bffb827e40fc0f9fb5883fffc23477 Mon Sep 17 00:00:00 2001 From: Junxiao Bi Date: Mon, 28 Aug 2023 15:10:18 -0700 Subject: scsi: megaraid_sas: Fix deadlock on firmware crashdump The following processes run into a deadlock. CPU 41 was waiting for CPU 29 to handle a CSD request while holding spinlock "crashdump_lock", but CPU 29 was hung by that spinlock with IRQs disabled. PID: 17360 TASK: ffff95c1090c5c40 CPU: 41 COMMAND: "mrdiagd" !# 0 [ffffb80edbf37b58] __read_once_size at ffffffff9b871a40 include/linux/compiler.h:185:0 !# 1 [ffffb80edbf37b58] atomic_read at ffffffff9b871a40 arch/x86/include/asm/atomic.h:27:0 !# 2 [ffffb80edbf37b58] dump_stack at ffffffff9b871a40 lib/dump_stack.c:54:0 # 3 [ffffb80edbf37b78] csd_lock_wait_toolong at ffffffff9b131ad5 kernel/smp.c:364:0 # 4 [ffffb80edbf37b78] __csd_lock_wait at ffffffff9b131ad5 kernel/smp.c:384:0 # 5 [ffffb80edbf37bf8] csd_lock_wait at ffffffff9b13267a kernel/smp.c:394:0 # 6 [ffffb80edbf37bf8] smp_call_function_many at ffffffff9b13267a kernel/smp.c:843:0 # 7 [ffffb80edbf37c50] smp_call_function at ffffffff9b13279d kernel/smp.c:867:0 # 8 [ffffb80edbf37c50] on_each_cpu at ffffffff9b13279d kernel/smp.c:976:0 # 9 [ffffb80edbf37c78] flush_tlb_kernel_range at ffffffff9b085c4b arch/x86/mm/tlb.c:742:0 #10 [ffffb80edbf37cb8] __purge_vmap_area_lazy at ffffffff9b23a1e0 mm/vmalloc.c:701:0 #11 [ffffb80edbf37ce0] try_purge_vmap_area_lazy at ffffffff9b23a2cc mm/vmalloc.c:722:0 #12 [ffffb80edbf37ce0] free_vmap_area_noflush at ffffffff9b23a2cc mm/vmalloc.c:754:0 #13 [ffffb80edbf37cf8] free_unmap_vmap_area at ffffffff9b23bb3b mm/vmalloc.c:764:0 #14 [ffffb80edbf37cf8] remove_vm_area at ffffffff9b23bb3b mm/vmalloc.c:1509:0 #15 [ffffb80edbf37d18] __vunmap at ffffffff9b23bb8a mm/vmalloc.c:1537:0 #16 [ffffb80edbf37d40] vfree at ffffffff9b23bc85 mm/vmalloc.c:1612:0 #17 [ffffb80edbf37d58] megasas_free_host_crash_buffer [megaraid_sas] at ffffffffc020b7f2 drivers/scsi/megaraid/megaraid_sas_fusion.c:3932:0 #18 [ffffb80edbf37d80] fw_crash_state_store [megaraid_sas] at ffffffffc01f804d drivers/scsi/megaraid/megaraid_sas_base.c:3291:0 #19 [ffffb80edbf37dc0] dev_attr_store at ffffffff9b56dd7b drivers/base/core.c:758:0 #20 [ffffb80edbf37dd0] sysfs_kf_write at ffffffff9b326acf fs/sysfs/file.c:144:0 #21 [ffffb80edbf37de0] kernfs_fop_write at ffffffff9b325fd4 fs/kernfs/file.c:316:0 #22 [ffffb80edbf37e20] __vfs_write at ffffffff9b29418a fs/read_write.c:480:0 #23 [ffffb80edbf37ea8] vfs_write at ffffffff9b294462 fs/read_write.c:544:0 #24 [ffffb80edbf37ee8] SYSC_write at ffffffff9b2946ec fs/read_write.c:590:0 #25 [ffffb80edbf37ee8] SyS_write at ffffffff9b2946ec fs/read_write.c:582:0 #26 [ffffb80edbf37f30] do_syscall_64 at ffffffff9b003ca9 arch/x86/entry/common.c:298:0 #27 [ffffb80edbf37f58] entry_SYSCALL_64 at ffffffff9ba001b1 arch/x86/entry/entry_64.S:238:0 PID: 17355 TASK: ffff95c1090c3d80 CPU: 29 COMMAND: "mrdiagd" !# 0 [ffffb80f2d3c7d30] __read_once_size at ffffffff9b0f2ab0 include/linux/compiler.h:185:0 !# 1 [ffffb80f2d3c7d30] native_queued_spin_lock_slowpath at ffffffff9b0f2ab0 kernel/locking/qspinlock.c:368:0 # 2 [ffffb80f2d3c7d58] pv_queued_spin_lock_slowpath at ffffffff9b0f244b arch/x86/include/asm/paravirt.h:674:0 # 3 [ffffb80f2d3c7d58] queued_spin_lock_slowpath at ffffffff9b0f244b arch/x86/include/asm/qspinlock.h:53:0 # 4 [ffffb80f2d3c7d68] queued_spin_lock at ffffffff9b8961a6 include/asm-generic/qspinlock.h:90:0 # 5 [ffffb80f2d3c7d68] do_raw_spin_lock_flags at ffffffff9b8961a6 include/linux/spinlock.h:173:0 # 6 [ffffb80f2d3c7d68] __raw_spin_lock_irqsave at ffffffff9b8961a6 include/linux/spinlock_api_smp.h:122:0 # 7 [ffffb80f2d3c7d68] _raw_spin_lock_irqsave at ffffffff9b8961a6 kernel/locking/spinlock.c:160:0 # 8 [ffffb80f2d3c7d88] fw_crash_buffer_store [megaraid_sas] at ffffffffc01f8129 drivers/scsi/megaraid/megaraid_sas_base.c:3205:0 # 9 [ffffb80f2d3c7dc0] dev_attr_store at ffffffff9b56dd7b drivers/base/core.c:758:0 #10 [ffffb80f2d3c7dd0] sysfs_kf_write at ffffffff9b326acf fs/sysfs/file.c:144:0 #11 [ffffb80f2d3c7de0] kernfs_fop_write at ffffffff9b325fd4 fs/kernfs/file.c:316:0 #12 [ffffb80f2d3c7e20] __vfs_write at ffffffff9b29418a fs/read_write.c:480:0 #13 [ffffb80f2d3c7ea8] vfs_write at ffffffff9b294462 fs/read_write.c:544:0 #14 [ffffb80f2d3c7ee8] SYSC_write at ffffffff9b2946ec fs/read_write.c:590:0 #15 [ffffb80f2d3c7ee8] SyS_write at ffffffff9b2946ec fs/read_write.c:582:0 #16 [ffffb80f2d3c7f30] do_syscall_64 at ffffffff9b003ca9 arch/x86/entry/common.c:298:0 #17 [ffffb80f2d3c7f58] entry_SYSCALL_64 at ffffffff9ba001b1 arch/x86/entry/entry_64.S:238:0 The lock is used to synchronize different sysfs operations, it doesn't protect any resource that will be touched by an interrupt. Consequently it's not required to disable IRQs. Replace the spinlock with a mutex to fix the deadlock. Signed-off-by: Junxiao Bi Link: https://lore.kernel.org/r/20230828221018.19471-1-junxiao.bi@oracle.com Reviewed-by: Mike Christie Cc: stable@vger.kernel.org Signed-off-by: Martin K. Petersen --- drivers/scsi/megaraid/megaraid_sas.h | 2 +- drivers/scsi/megaraid/megaraid_sas_base.c | 21 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/megaraid/megaraid_sas.h b/drivers/scsi/megaraid/megaraid_sas.h index 3554f6b07727..94abba57582d 100644 --- a/drivers/scsi/megaraid/megaraid_sas.h +++ b/drivers/scsi/megaraid/megaraid_sas.h @@ -2332,7 +2332,7 @@ struct megasas_instance { u32 support_morethan256jbod; /* FW support for more than 256 PD/JBOD */ bool use_seqnum_jbod_fp; /* Added for PD sequence */ bool smp_affinity_enable; - spinlock_t crashdump_lock; + struct mutex crashdump_lock; struct megasas_register_set __iomem *reg_set; u32 __iomem *reply_post_host_index_addr[MR_MAX_MSIX_REG_ARRAY]; diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index b9d46dcb5210..e1aa667dae66 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3271,14 +3271,13 @@ fw_crash_buffer_store(struct device *cdev, struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; int val = 0; - unsigned long flags; if (kstrtoint(buf, 0, &val) != 0) return -EINVAL; - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); instance->fw_crash_buffer_offset = val; - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return strlen(buf); } @@ -3293,24 +3292,23 @@ fw_crash_buffer_show(struct device *cdev, unsigned long dmachunk = CRASH_DMA_BUF_SIZE; unsigned long chunk_left_bytes; unsigned long src_addr; - unsigned long flags; u32 buff_offset; - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); buff_offset = instance->fw_crash_buffer_offset; if (!instance->crash_dump_buf || !((instance->fw_crash_state == AVAILABLE) || (instance->fw_crash_state == COPYING))) { dev_err(&instance->pdev->dev, "Firmware crash dump is not available\n"); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return -EINVAL; } if (buff_offset > (instance->fw_crash_buffer_size * dmachunk)) { dev_err(&instance->pdev->dev, "Firmware crash dump offset is out of range\n"); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return 0; } @@ -3322,7 +3320,7 @@ fw_crash_buffer_show(struct device *cdev, src_addr = (unsigned long)instance->crash_buf[buff_offset / dmachunk] + (buff_offset % dmachunk); memcpy(buf, (void *)src_addr, size); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); return size; } @@ -3347,7 +3345,6 @@ fw_crash_state_store(struct device *cdev, struct megasas_instance *instance = (struct megasas_instance *) shost->hostdata; int val = 0; - unsigned long flags; if (kstrtoint(buf, 0, &val) != 0) return -EINVAL; @@ -3361,9 +3358,9 @@ fw_crash_state_store(struct device *cdev, instance->fw_crash_state = val; if ((val == COPIED) || (val == COPY_ERROR)) { - spin_lock_irqsave(&instance->crashdump_lock, flags); + mutex_lock(&instance->crashdump_lock); megasas_free_host_crash_buffer(instance); - spin_unlock_irqrestore(&instance->crashdump_lock, flags); + mutex_unlock(&instance->crashdump_lock); if (val == COPY_ERROR) dev_info(&instance->pdev->dev, "application failed to " "copy Firmware crash dump\n"); @@ -7422,7 +7419,7 @@ static inline void megasas_init_ctrl_params(struct megasas_instance *instance) init_waitqueue_head(&instance->int_cmd_wait_q); init_waitqueue_head(&instance->abort_cmd_wait_q); - spin_lock_init(&instance->crashdump_lock); + mutex_init(&instance->crashdump_lock); spin_lock_init(&instance->mfi_pool_lock); spin_lock_init(&instance->hba_lock); spin_lock_init(&instance->stream_lock); -- cgit v1.2.3 From 31a0865bf593e59c4433a3624b4c87c40049ed9a Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Wed, 30 Aug 2023 23:19:42 -0600 Subject: scsi: ppa: Fix accidentally reversed conditions for 16-bit and 32-bit EPP The conditions were correct in the ppa_in() function but not in the ppa_out() function. Fixes: 68a4f84a17c1 ("scsi: ppa: Add a module parameter for the transfer mode") Signed-off-by: Alex Henrie Link: https://lore.kernel.org/r/20230831051945.515476-1-alexhenrie24@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/ppa.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c index 19f0b93fa3d8..d592ee9170c1 100644 --- a/drivers/scsi/ppa.c +++ b/drivers/scsi/ppa.c @@ -307,9 +307,9 @@ static int ppa_out(ppa_struct *dev, char *buffer, int len) case PPA_EPP_8: epp_reset(ppb); w_ctr(ppb, 0x4); - if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x01)) + if (dev->mode == PPA_EPP_32 && !(((long) buffer | len) & 0x03)) outsl(ppb + 4, buffer, len >> 2); - else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x03)) + else if (dev->mode == PPA_EPP_16 && !(((long) buffer | len) & 0x01)) outsw(ppb + 4, buffer, len >> 1); else outsb(ppb + 4, buffer, len); -- cgit v1.2.3 From 0be7592885d7b4c20595c388adc13930b653b847 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Thu, 31 Aug 2023 16:51:45 +0530 Subject: scsi: qla2xxx: Correct endianness for rqstlen and rsplen rqstlen and rsplen were changed to __le32 to fix sparse warnings: drivers/scsi/qla2xxx/qla_nvme.c:402:30: warning: incorrect type in assignment (different base types) drivers/scsi/qla2xxx/qla_nvme.c:402:30: expected restricted __le32 [usertype] cmd_len drivers/scsi/qla2xxx/qla_nvme.c:402:30: got unsigned short [usertype] rsplen drivers/scsi/qla2xxx/qla_nvme.c:507:30: warning: incorrect type in assignment (different base types) drivers/scsi/qla2xxx/qla_nvme.c:507:30: expected restricted __le32 [usertype] cmd_len drivers/scsi/qla2xxx/qla_nvme.c:507:30: got unsigned int [usertype] rqstlen drivers/scsi/qla2xxx/qla_nvme.c:508:30: warning: incorrect type in assignment (different base types) drivers/scsi/qla2xxx/qla_nvme.c:508:30: expected restricted __le32 [usertype] rsp_len drivers/scsi/qla2xxx/qla_nvme.c:508:30: got unsigned int [usertype] rsplen Correct the endianness in qla2xxx driver thus avoiding changes in nvme-fc-driver.h. Fixes: 875386b98857 ("scsi: qla2xxx: Add Unsolicited LS Request and Response Support for NVMe") Signed-off-by: Nilesh Javali Link: https://lore.kernel.org/r/20230831112146.32595-1-njavali@marvell.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_nvme.c | 10 +++++----- include/linux/nvme-fc-driver.h | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index db753d712991..a8ddf356e662 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -399,14 +399,14 @@ static int qla_nvme_xmt_ls_rsp(struct nvme_fc_local_port *lport, nvme->u.nvme.dl = 0; nvme->u.nvme.timeout_sec = 0; nvme->u.nvme.cmd_dma = fd_resp->rspdma; - nvme->u.nvme.cmd_len = fd_resp->rsplen; + nvme->u.nvme.cmd_len = cpu_to_le32(fd_resp->rsplen); nvme->u.nvme.rsp_len = 0; nvme->u.nvme.rsp_dma = 0; nvme->u.nvme.exchange_address = uctx->exchange_address; nvme->u.nvme.nport_handle = uctx->nport_handle; nvme->u.nvme.ox_id = uctx->ox_id; dma_sync_single_for_device(&ha->pdev->dev, nvme->u.nvme.cmd_dma, - le32_to_cpu(fd_resp->rsplen), DMA_TO_DEVICE); + fd_resp->rsplen, DMA_TO_DEVICE); ql_dbg(ql_dbg_unsol, vha, 0x2122, "Unsol lsreq portid=%06x %8phC exchange_address 0x%x ox_id 0x%x hdl 0x%x\n", @@ -504,13 +504,13 @@ static int qla_nvme_ls_req(struct nvme_fc_local_port *lport, nvme->u.nvme.desc = fd; nvme->u.nvme.dir = 0; nvme->u.nvme.dl = 0; - nvme->u.nvme.cmd_len = fd->rqstlen; - nvme->u.nvme.rsp_len = fd->rsplen; + nvme->u.nvme.cmd_len = cpu_to_le32(fd->rqstlen); + nvme->u.nvme.rsp_len = cpu_to_le32(fd->rsplen); nvme->u.nvme.rsp_dma = fd->rspdma; nvme->u.nvme.timeout_sec = fd->timeout; nvme->u.nvme.cmd_dma = fd->rqstdma; dma_sync_single_for_device(&ha->pdev->dev, nvme->u.nvme.cmd_dma, - le32_to_cpu(fd->rqstlen), DMA_TO_DEVICE); + fd->rqstlen, DMA_TO_DEVICE); rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) { diff --git a/include/linux/nvme-fc-driver.h b/include/linux/nvme-fc-driver.h index f6ef8cf5d774..4109f1bd6128 100644 --- a/include/linux/nvme-fc-driver.h +++ b/include/linux/nvme-fc-driver.h @@ -53,10 +53,10 @@ struct nvmefc_ls_req { void *rqstaddr; dma_addr_t rqstdma; - __le32 rqstlen; + u32 rqstlen; void *rspaddr; dma_addr_t rspdma; - __le32 rsplen; + u32 rsplen; u32 timeout; void *private; @@ -120,7 +120,7 @@ struct nvmefc_ls_req { struct nvmefc_ls_rsp { void *rspbuf; dma_addr_t rspdma; - __le32 rsplen; + u16 rsplen; void (*done)(struct nvmefc_ls_rsp *rsp); void *nvme_fc_private; /* LLDD is not to access !! */ -- cgit v1.2.3 From 59f10a05b5c7b675256a66e3161741239889ff80 Mon Sep 17 00:00:00 2001 From: Nilesh Javali Date: Thu, 31 Aug 2023 16:51:46 +0530 Subject: scsi: qla2xxx: Use raw_smp_processor_id() instead of smp_processor_id() The following call trace was observed: localhost kernel: nvme nvme0: NVME-FC{0}: controller connect complete localhost kernel: BUG: using smp_processor_id() in preemptible [00000000] code: kworker/u129:4/75092 localhost kernel: nvme nvme0: NVME-FC{0}: new ctrl: NQN "nqn.1992-08.com.netapp:sn.b42d198afb4d11ecad6d00a098d6abfa:subsystem.PR_Channel2022_RH84_subsystem_291" localhost kernel: caller is qla_nvme_post_cmd+0x216/0x1380 [qla2xxx] localhost kernel: CPU: 6 PID: 75092 Comm: kworker/u129:4 Kdump: loaded Tainted: G B W OE --------- --- 5.14.0-70.22.1.el9_0.x86_64+debug #1 localhost kernel: Hardware name: HPE ProLiant XL420 Gen10/ProLiant XL420 Gen10, BIOS U39 01/13/2022 localhost kernel: Workqueue: nvme-wq nvme_async_event_work [nvme_core] localhost kernel: Call Trace: localhost kernel: dump_stack_lvl+0x57/0x7d localhost kernel: check_preemption_disabled+0xc8/0xd0 localhost kernel: qla_nvme_post_cmd+0x216/0x1380 [qla2xxx] Use raw_smp_processor_id() instead of smp_processor_id(). Also use queue_work() across the driver instead of queue_work_on() thus avoiding usage of smp_processor_id() when CONFIG_DEBUG_PREEMPT is enabled. Cc: stable@vger.kernel.org Suggested-by: John Garry Signed-off-by: Nilesh Javali Link: https://lore.kernel.org/r/20230831112146.32595-2-njavali@marvell.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_inline.h | 2 +- drivers/scsi/qla2xxx/qla_isr.c | 6 +++--- drivers/scsi/qla2xxx/qla_target.c | 3 +-- drivers/scsi/qla2xxx/tcm_qla2xxx.c | 4 ++-- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 0556969f6dc1..a4a56ab0ba74 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -577,7 +577,7 @@ fcport_is_bigger(fc_port_t *fcport) static inline struct qla_qpair * qla_mapq_nvme_select_qpair(struct qla_hw_data *ha, struct qla_qpair *qpair) { - int cpuid = smp_processor_id(); + int cpuid = raw_smp_processor_id(); if (qpair->cpuid != cpuid && ha->qp_cpu_map[cpuid]) { diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index e98788191897..d48007e18288 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -3965,7 +3965,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, if (!ha->flags.fw_started) return; - if (rsp->qpair->cpuid != smp_processor_id() || !rsp->qpair->rcv_intr) { + if (rsp->qpair->cpuid != raw_smp_processor_id() || !rsp->qpair->rcv_intr) { rsp->qpair->rcv_intr = 1; if (!rsp->qpair->cpu_mapped) @@ -4468,7 +4468,7 @@ qla2xxx_msix_rsp_q(int irq, void *dev_id) } ha = qpair->hw; - queue_work_on(smp_processor_id(), ha->wq, &qpair->q_work); + queue_work(ha->wq, &qpair->q_work); return IRQ_HANDLED; } @@ -4494,7 +4494,7 @@ qla2xxx_msix_rsp_q_hs(int irq, void *dev_id) wrt_reg_dword(®->hccr, HCCRX_CLR_RISC_INT); spin_unlock_irqrestore(&ha->hardware_lock, flags); - queue_work_on(smp_processor_id(), ha->wq, &qpair->q_work); + queue_work(ha->wq, &qpair->q_work); return IRQ_HANDLED; } diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 2b815a9928ea..2ef2dbac0db2 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -4425,8 +4425,7 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq, &cmd->work); } else if (ha->msix_count) { if (cmd->atio.u.isp24.fcp_cmnd.rddata) - queue_work_on(smp_processor_id(), qla_tgt_wq, - &cmd->work); + queue_work(qla_tgt_wq, &cmd->work); else queue_work_on(cmd->se_cmd.cpuid, qla_tgt_wq, &cmd->work); diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c index 3b5ba4b47b3b..68a0e6a2fb6e 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -310,7 +310,7 @@ static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd) cmd->trc_flags |= TRC_CMD_DONE; INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free); - queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); } /* @@ -547,7 +547,7 @@ static void tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd) cmd->trc_flags |= TRC_DATA_IN; cmd->cmd_in_wq = 1; INIT_WORK(&cmd->work, tcm_qla2xxx_handle_data_work); - queue_work_on(smp_processor_id(), tcm_qla2xxx_free_wq, &cmd->work); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); } static int tcm_qla2xxx_chk_dif_tags(uint32_t tag) -- cgit v1.2.3 From d0b0822e32dbae80bbcb3cc86f34d28539d913df Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 31 Aug 2023 22:09:29 +0800 Subject: scsi: qla2xxx: Fix NULL vs IS_ERR() bug for debugfs_create_dir() Since both debugfs_create_dir() and debugfs_create_file() return ERR_PTR and never NULL, use IS_ERR() instead of checking for NULL. Fixes: 1e98fb0f9208 ("scsi: qla2xxx: Setup debugfs entries for remote ports") Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230831140930.3166359-1-ruanjinjie@huawei.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qla2xxx/qla_dfs.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index f060e593685d..a7a364760b80 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -116,7 +116,7 @@ qla2x00_dfs_create_rport(scsi_qla_host_t *vha, struct fc_port *fp) sprintf(wwn, "pn-%016llx", wwn_to_u64(fp->port_name)); fp->dfs_rport_dir = debugfs_create_dir(wwn, vha->dfs_rport_root); - if (!fp->dfs_rport_dir) + if (IS_ERR(fp->dfs_rport_dir)) return; if (NVME_TARGET(vha->hw, fp)) debugfs_create_file("dev_loss_tmo", 0600, fp->dfs_rport_dir, @@ -708,14 +708,14 @@ create_nodes: if (IS_QLA27XX(ha) || IS_QLA83XX(ha) || IS_QLA28XX(ha)) { ha->tgt.dfs_naqp = debugfs_create_file("naqp", 0400, ha->dfs_dir, vha, &dfs_naqp_ops); - if (!ha->tgt.dfs_naqp) { + if (IS_ERR(ha->tgt.dfs_naqp)) { ql_log(ql_log_warn, vha, 0xd011, "Unable to create debugFS naqp node.\n"); goto out; } } vha->dfs_rport_root = debugfs_create_dir("rports", ha->dfs_dir); - if (!vha->dfs_rport_root) { + if (IS_ERR(vha->dfs_rport_root)) { ql_log(ql_log_warn, vha, 0xd012, "Unable to create debugFS rports node.\n"); goto out; -- cgit v1.2.3 From 5c584fe6098ae1727650acbabdef0669cefec7be Mon Sep 17 00:00:00 2001 From: Azeem Shaikh Date: Thu, 31 Aug 2023 14:36:38 +0000 Subject: scsi: target: Replace strlcpy() with strscpy() strlcpy() reads the entire source buffer first. This read may exceed the destination size limit. This is both inefficient and can lead to linear read overflows if a source string is not NUL-terminated [1]. In an effort to remove strlcpy() completely [2], replace strlcpy() here with strscpy(). Direct replacement is safe here since return value of -errno is used to check for truncation instead of sizeof(dest). [1] https://www.kernel.org/doc/html/latest/process/deprecated.html#strlcpy [2] https://github.com/KSPP/linux/issues/89 Signed-off-by: Azeem Shaikh Link: https://lore.kernel.org/r/20230831143638.232596-1-azeemshaikh38@gmail.com Reviewed-by: Kees Cook Signed-off-by: Martin K. Petersen --- drivers/target/target_core_configfs.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index 936e5ff1b209..d5860c1c1f46 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -1392,16 +1392,16 @@ static ssize_t target_wwn_vendor_id_store(struct config_item *item, /* +2 to allow for a trailing (stripped) '\n' and null-terminator */ unsigned char buf[INQUIRY_VENDOR_LEN + 2]; char *stripped = NULL; - size_t len; + ssize_t len; ssize_t ret; - len = strlcpy(buf, page, sizeof(buf)); - if (len < sizeof(buf)) { + len = strscpy(buf, page, sizeof(buf)); + if (len > 0) { /* Strip any newline added from userspace. */ stripped = strstrip(buf); len = strlen(stripped); } - if (len > INQUIRY_VENDOR_LEN) { + if (len < 0 || len > INQUIRY_VENDOR_LEN) { pr_err("Emulated T10 Vendor Identification exceeds" " INQUIRY_VENDOR_LEN: " __stringify(INQUIRY_VENDOR_LEN) "\n"); @@ -1448,16 +1448,16 @@ static ssize_t target_wwn_product_id_store(struct config_item *item, /* +2 to allow for a trailing (stripped) '\n' and null-terminator */ unsigned char buf[INQUIRY_MODEL_LEN + 2]; char *stripped = NULL; - size_t len; + ssize_t len; ssize_t ret; - len = strlcpy(buf, page, sizeof(buf)); - if (len < sizeof(buf)) { + len = strscpy(buf, page, sizeof(buf)); + if (len > 0) { /* Strip any newline added from userspace. */ stripped = strstrip(buf); len = strlen(stripped); } - if (len > INQUIRY_MODEL_LEN) { + if (len < 0 || len > INQUIRY_MODEL_LEN) { pr_err("Emulated T10 Vendor exceeds INQUIRY_MODEL_LEN: " __stringify(INQUIRY_MODEL_LEN) "\n"); @@ -1504,16 +1504,16 @@ static ssize_t target_wwn_revision_store(struct config_item *item, /* +2 to allow for a trailing (stripped) '\n' and null-terminator */ unsigned char buf[INQUIRY_REVISION_LEN + 2]; char *stripped = NULL; - size_t len; + ssize_t len; ssize_t ret; - len = strlcpy(buf, page, sizeof(buf)); - if (len < sizeof(buf)) { + len = strscpy(buf, page, sizeof(buf)); + if (len > 0) { /* Strip any newline added from userspace. */ stripped = strstrip(buf); len = strlen(stripped); } - if (len > INQUIRY_REVISION_LEN) { + if (len < 0 || len > INQUIRY_REVISION_LEN) { pr_err("Emulated T10 Revision exceeds INQUIRY_REVISION_LEN: " __stringify(INQUIRY_REVISION_LEN) "\n"); -- cgit v1.2.3 From 7df0b2605489bef3f4223ad66f1f9bb8d50d4cd2 Mon Sep 17 00:00:00 2001 From: Javed Hasan Date: Fri, 1 Sep 2023 11:36:46 +0530 Subject: scsi: qedf: Add synchronization between I/O completions and abort Avoid race condition between I/O completion and abort processing by protecting the cmd_type with the rport lock. Signed-off-by: Javed Hasan Signed-off-by: Saurav Kashyap Link: https://lore.kernel.org/r/20230901060646.27885-1-skashyap@marvell.com Signed-off-by: Martin K. Petersen --- drivers/scsi/qedf/qedf_io.c | 10 ++++++++-- drivers/scsi/qedf/qedf_main.c | 7 ++++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/drivers/scsi/qedf/qedf_io.c b/drivers/scsi/qedf/qedf_io.c index 4750ec5789a8..10fe3383855c 100644 --- a/drivers/scsi/qedf/qedf_io.c +++ b/drivers/scsi/qedf/qedf_io.c @@ -1904,6 +1904,7 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) goto drop_rdata_kref; } + spin_lock_irqsave(&fcport->rport_lock, flags); if (!test_bit(QEDF_CMD_OUTSTANDING, &io_req->flags) || test_bit(QEDF_CMD_IN_CLEANUP, &io_req->flags) || test_bit(QEDF_CMD_IN_ABORT, &io_req->flags)) { @@ -1911,17 +1912,20 @@ int qedf_initiate_abts(struct qedf_ioreq *io_req, bool return_scsi_cmd_on_abts) "io_req xid=0x%x sc_cmd=%p already in cleanup or abort processing or already completed.\n", io_req->xid, io_req->sc_cmd); rc = 1; + spin_unlock_irqrestore(&fcport->rport_lock, flags); goto drop_rdata_kref; } + /* Set the command type to abort */ + io_req->cmd_type = QEDF_ABTS; + spin_unlock_irqrestore(&fcport->rport_lock, flags); + kref_get(&io_req->refcount); xid = io_req->xid; qedf->control_requests++; qedf->packet_aborts++; - /* Set the command type to abort */ - io_req->cmd_type = QEDF_ABTS; io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts; set_bit(QEDF_CMD_IN_ABORT, &io_req->flags); @@ -2210,7 +2214,9 @@ process_els: refcount, fcport, fcport->rdata->ids.port_id); /* Cleanup cmds re-use the same TID as the original I/O */ + spin_lock_irqsave(&fcport->rport_lock, flags); io_req->cmd_type = QEDF_CLEANUP; + spin_unlock_irqrestore(&fcport->rport_lock, flags); io_req->return_scsi_cmd_on_abts = return_scsi_cmd_on_abts; init_completion(&io_req->cleanup_done); diff --git a/drivers/scsi/qedf/qedf_main.c b/drivers/scsi/qedf/qedf_main.c index 7825765c936c..91f3f1d7098e 100644 --- a/drivers/scsi/qedf/qedf_main.c +++ b/drivers/scsi/qedf/qedf_main.c @@ -2805,6 +2805,8 @@ void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe) struct qedf_ioreq *io_req; struct qedf_rport *fcport; u32 comp_type; + u8 io_comp_type; + unsigned long flags; comp_type = (cqe->cqe_data >> FCOE_CQE_CQE_TYPE_SHIFT) & FCOE_CQE_CQE_TYPE_MASK; @@ -2838,11 +2840,14 @@ void qedf_process_cqe(struct qedf_ctx *qedf, struct fcoe_cqe *cqe) return; } + spin_lock_irqsave(&fcport->rport_lock, flags); + io_comp_type = io_req->cmd_type; + spin_unlock_irqrestore(&fcport->rport_lock, flags); switch (comp_type) { case FCOE_GOOD_COMPLETION_CQE_TYPE: atomic_inc(&fcport->free_sqes); - switch (io_req->cmd_type) { + switch (io_comp_type) { case QEDF_SCSI_CMD: qedf_scsi_completion(qedf, cqe, io_req); break; -- cgit v1.2.3 From 2d3f59cf868b4a2dd678a96cd49bdd91411bd59f Mon Sep 17 00:00:00 2001 From: Kiwoong Kim Date: Mon, 4 Sep 2023 10:30:44 +0900 Subject: scsi: ufs: core: Move __ufshcd_send_uic_cmd() outside host_lock __ufshcd_send_uic_cmd() is wrapped by uic_cmd_mutex and its related contexts are accessed within the section wrapped by uic_cmd_mutex. Thus, wrapping with host_lock is redundant. Signed-off-by: Kiwoong Kim Link: https://lore.kernel.org/r/782ba5f26f0a96e58d85dff50751787d2d2a6b2b.1693790060.git.kwmad.kim@samsung.com Reviewed-by: Bart Van Assche Reviewed-by: Chanwoo Lee Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 93417518c04d..f5e66d775b10 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -2392,7 +2392,6 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, bool completion) { lockdep_assert_held(&hba->uic_cmd_mutex); - lockdep_assert_held(hba->host->host_lock); if (!ufshcd_ready_for_uic_cmd(hba)) { dev_err(hba->dev, @@ -2419,7 +2418,6 @@ __ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd, int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) { int ret; - unsigned long flags; if (hba->quirks & UFSHCD_QUIRK_BROKEN_UIC_CMD) return 0; @@ -2428,9 +2426,7 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd) mutex_lock(&hba->uic_cmd_mutex); ufshcd_add_delay_before_dme_cmd(hba); - spin_lock_irqsave(hba->host->host_lock, flags); ret = __ufshcd_send_uic_cmd(hba, uic_cmd, true); - spin_unlock_irqrestore(hba->host->host_lock, flags); if (!ret) ret = ufshcd_wait_for_uic_cmd(hba, uic_cmd); @@ -4133,8 +4129,8 @@ static int ufshcd_uic_pwr_ctrl(struct ufs_hba *hba, struct uic_command *cmd) wmb(); reenable_intr = true; } - ret = __ufshcd_send_uic_cmd(hba, cmd, false); spin_unlock_irqrestore(hba->host->host_lock, flags); + ret = __ufshcd_send_uic_cmd(hba, cmd, false); if (ret) { dev_err(hba->dev, "pwr ctrl cmd 0x%x with mode 0x%x uic error %d\n", -- cgit v1.2.3 From d32533d30e2119b0c0aa17596734f1f842f750df Mon Sep 17 00:00:00 2001 From: Kiwoong Kim Date: Mon, 4 Sep 2023 10:30:45 +0900 Subject: scsi: ufs: core: Poll HCS.UCRDY before issuing a UIC command With auto hibern8 enabled, UIC could be busy processing a hibern8 operation and the HCI would reports UIC not ready for a short while through HCS.UCRDY. The UFS driver doesn't currently handle this situation. The UFSHCI spec specifies UCRDY like this: whether the host controller is ready to process UIC COMMAND The 'ready' could be seen as many different meanings. If the meaning includes not processing any request from HCI, processing a hibern8 operation can be 'not ready'. In this situation, the driver needs to wait until the operations is completed. Signed-off-by: Kiwoong Kim Link: https://lore.kernel.org/r/550484ffb66300bdcec63d3e304dfd55cb432f1f.1693790060.git.kwmad.kim@samsung.com Reviewed-by: Adrian Hunter Reviewed-by: Chanwoo Lee Signed-off-by: Martin K. Petersen --- drivers/ufs/core/ufshcd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index f5e66d775b10..c2df07545f96 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -2299,7 +2300,11 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba) */ static inline bool ufshcd_ready_for_uic_cmd(struct ufs_hba *hba) { - return ufshcd_readl(hba, REG_CONTROLLER_STATUS) & UIC_COMMAND_READY; + u32 val; + int ret = read_poll_timeout(ufshcd_readl, val, val & UIC_COMMAND_READY, + 500, UIC_CMD_TIMEOUT * 1000, false, hba, + REG_CONTROLLER_STATUS); + return ret == 0 ? true : false; } /** -- cgit v1.2.3 From 2810c1e99867a811e631dd24e63e6c1e3b78a59d Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sun, 3 Sep 2023 15:10:25 +0800 Subject: kunit: Fix wild-memory-access bug in kunit_free_suite_set() Inject fault while probing kunit-example-test.ko, if kstrdup() fails in mod_sysfs_setup() in load_module(), the mod->state will switch from MODULE_STATE_COMING to MODULE_STATE_GOING instead of from MODULE_STATE_LIVE to MODULE_STATE_GOING, so only kunit_module_exit() will be called without kunit_module_init(), and the mod->kunit_suites is no set correctly and the free in kunit_free_suite_set() will cause below wild-memory-access bug. The mod->state state machine when load_module() succeeds: MODULE_STATE_UNFORMED ---> MODULE_STATE_COMING ---> MODULE_STATE_LIVE ^ | | | delete_module +---------------- MODULE_STATE_GOING <---------+ The mod->state state machine when load_module() fails at mod_sysfs_setup(): MODULE_STATE_UNFORMED ---> MODULE_STATE_COMING ---> MODULE_STATE_GOING ^ | | | +-----------------------------------------------+ Call kunit_module_init() at MODULE_STATE_COMING state to fix the issue because MODULE_STATE_LIVE is transformed from it. Unable to handle kernel paging request at virtual address ffffff341e942a88 KASAN: maybe wild-memory-access in range [0x0003f9a0f4a15440-0x0003f9a0f4a15447] Mem abort info: ESR = 0x0000000096000004 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x04: level 0 translation fault Data abort info: ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 swapper pgtable: 4k pages, 48-bit VAs, pgdp=00000000441ea000 [ffffff341e942a88] pgd=0000000000000000, p4d=0000000000000000 Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP Modules linked in: kunit_example_test(-) cfg80211 rfkill 8021q garp mrp stp llc ipv6 [last unloaded: kunit_example_test] CPU: 3 PID: 2035 Comm: modprobe Tainted: G W N 6.5.0-next-20230828+ #136 Hardware name: linux,dummy-virt (DT) pstate: a0000005 (NzCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : kfree+0x2c/0x70 lr : kunit_free_suite_set+0xcc/0x13c sp : ffff8000829b75b0 x29: ffff8000829b75b0 x28: ffff8000829b7b90 x27: 0000000000000000 x26: dfff800000000000 x25: ffffcd07c82a7280 x24: ffffcd07a50ab300 x23: ffffcd07a50ab2e8 x22: 1ffff00010536ec0 x21: dfff800000000000 x20: ffffcd07a50ab2f0 x19: ffffcd07a50ab2f0 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: ffffcd07c24b6764 x14: ffffcd07c24b63c0 x13: ffffcd07c4cebb94 x12: ffff700010536ec7 x11: 1ffff00010536ec6 x10: ffff700010536ec6 x9 : dfff800000000000 x8 : 00008fffefac913a x7 : 0000000041b58ab3 x6 : 0000000000000000 x5 : 1ffff00010536ec5 x4 : ffff8000829b7628 x3 : dfff800000000000 x2 : ffffff341e942a80 x1 : ffffcd07a50aa000 x0 : fffffc0000000000 Call trace: kfree+0x2c/0x70 kunit_free_suite_set+0xcc/0x13c kunit_module_notify+0xd8/0x360 blocking_notifier_call_chain+0xc4/0x128 load_module+0x382c/0x44a4 init_module_from_file+0xd4/0x128 idempotent_init_module+0x2c8/0x524 __arm64_sys_finit_module+0xac/0x100 invoke_syscall+0x6c/0x258 el0_svc_common.constprop.0+0x160/0x22c do_el0_svc+0x44/0x5c el0_svc+0x38/0x78 el0t_64_sync_handler+0x13c/0x158 el0t_64_sync+0x190/0x194 Code: aa0003e1 b25657e0 d34cfc42 8b021802 (f9400440) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception SMP: stopping secondary CPUs Kernel Offset: 0x4d0742200000 from 0xffff800080000000 PHYS_OFFSET: 0xffffee43c0000000 CPU features: 0x88000203,3c020000,1000421b Memory Limit: none Rebooting in 1 seconds.. Fixes: 3d6e44623841 ("kunit: unify module and builtin suite definitions") Signed-off-by: Jinjie Ruan Reviewed-by: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/test.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/kunit/test.c b/lib/kunit/test.c index 49698a168437..421f13981412 100644 --- a/lib/kunit/test.c +++ b/lib/kunit/test.c @@ -784,12 +784,13 @@ static int kunit_module_notify(struct notifier_block *nb, unsigned long val, switch (val) { case MODULE_STATE_LIVE: - kunit_module_init(mod); break; case MODULE_STATE_GOING: kunit_module_exit(mod); break; case MODULE_STATE_COMING: + kunit_module_init(mod); + break; case MODULE_STATE_UNFORMED: break; } -- cgit v1.2.3 From 4b00920da1dd2bbb33baeb2e7b9808af4c68de97 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sun, 3 Sep 2023 15:10:26 +0800 Subject: kunit: Fix the wrong err path and add goto labels in kunit_filter_suites() Take the last kfree(parsed_filters) and add it to be the first. Take the first kfree(copy) and add it to be the last. The Best practice is to return these errors reversely. And as David suggested, add several labels which target only the things which actually have been allocated so far. Fixes: 529534e8cba3 ("kunit: Add ability to filter attributes") Fixes: abbf73816b6f ("kunit: fix possible memory leak in kunit_filter_suites()") Signed-off-by: Jinjie Ruan Reviewed-by: Rae Moar Suggested-by: David Gow Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/executor.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index 5181aa2e760b..0eda42b0c9bb 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -166,7 +166,7 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, for (j = 0; j < filter_count; j++) parsed_filters[j] = kunit_next_attr_filter(&filters, err); if (*err) - goto err; + goto free_parsed_filters; } for (i = 0; &suite_set->start[i] != suite_set->end; i++) { @@ -178,7 +178,7 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, parsed_glob.test_glob); if (IS_ERR(filtered_suite)) { *err = PTR_ERR(filtered_suite); - goto err; + goto free_parsed_filters; } } if (filter_count > 0 && parsed_filters != NULL) { @@ -195,10 +195,11 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, filtered_suite = new_filtered_suite; if (*err) - goto err; + goto free_parsed_filters; + if (IS_ERR(filtered_suite)) { *err = PTR_ERR(filtered_suite); - goto err; + goto free_parsed_filters; } if (!filtered_suite) break; @@ -213,17 +214,19 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, filtered.start = copy_start; filtered.end = copy; -err: - if (*err) - kfree(copy); +free_parsed_filters: + if (filter_count) + kfree(parsed_filters); +free_parsed_glob: if (filter_glob) { kfree(parsed_glob.suite_glob); kfree(parsed_glob.test_glob); } - if (filter_count) - kfree(parsed_filters); +free_copy: + if (*err) + kfree(copy); return filtered; } -- cgit v1.2.3 From 2b56a4b79b7b3086e842d39611db4e19b19dbe2a Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sun, 3 Sep 2023 15:10:27 +0800 Subject: kunit: Fix possible null-ptr-deref in kunit_parse_glob_filter() Inject fault while probing kunit-example-test.ko, if kzalloc fails in kunit_parse_glob_filter(), strcpy() or strncpy() to NULL will cause below null-ptr-deref bug. So check NULL for kzalloc() and return int instead of void for kunit_parse_glob_filter(). Unable to handle kernel paging request at virtual address dfff800000000000 KASAN: null-ptr-deref in range [0x0000000000000000-0x0000000000000007] Mem abort info: ESR = 0x0000000096000005 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x05: level 1 translation fault Data abort info: ISV = 0, ISS = 0x00000005, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 [dfff800000000000] address between user and kernel address ranges Internal error: Oops: 0000000096000005 [#1] PREEMPT SMP Modules linked in: kunit_example_test cfg80211 rfkill 8021q garp mrp stp llc ipv6 [last unloaded: kunit_example_test] CPU: 4 PID: 6047 Comm: modprobe Tainted: G W N 6.5.0-next-20230829+ #141 Hardware name: linux,dummy-virt (DT) pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : strncpy+0x58/0xc0 lr : kunit_filter_suites+0x15c/0xa84 sp : ffff800082a17420 x29: ffff800082a17420 x28: 0000000000000000 x27: 0000000000000004 x26: 0000000000000000 x25: ffffa847e40a5320 x24: 0000000000000001 x23: 0000000000000000 x22: 0000000000000001 x21: dfff800000000000 x20: 000000000000002a x19: 0000000000000000 x18: 00000000750b3b54 x17: 0000000000000000 x16: 0000000000000000 x15: 0000000000000000 x14: 0000000000000000 x13: 34393178302f3039 x12: ffff7508fcea4ec1 x11: 1ffff508fcea4ec0 x10: ffff7508fcea4ec0 x9 : dfff800000000000 x8 : ffff6051b1a7f86a x7 : ffff800082a17270 x6 : 0000000000000002 x5 : 0000000000000098 x4 : ffff028d9817b250 x3 : 0000000000000000 x2 : 0000000000000000 x1 : ffffa847e40a5320 x0 : 0000000000000000 Call trace: strncpy+0x58/0xc0 kunit_filter_suites+0x15c/0xa84 kunit_module_notify+0x1b0/0x3ac blocking_notifier_call_chain+0xc4/0x128 do_init_module+0x250/0x594 load_module+0x37b0/0x44b4 init_module_from_file+0xd4/0x128 idempotent_init_module+0x2c8/0x524 __arm64_sys_finit_module+0xac/0x100 invoke_syscall+0x6c/0x258 el0_svc_common.constprop.0+0x160/0x22c do_el0_svc+0x44/0x5c el0_svc+0x38/0x78 el0t_64_sync_handler+0x13c/0x158 el0t_64_sync+0x190/0x194 Code: 5400028a d343fe63 12000a62 39400034 (38f56863) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception SMP: stopping secondary CPUs Kernel Offset: 0x284761400000 from 0xffff800080000000 PHYS_OFFSET: 0xfffffd7380000000 CPU features: 0x88000203,3c020000,1000421b Memory Limit: none Rebooting in 1 seconds.. Fixes: a127b154a8f2 ("kunit: tool: allow filtering test cases via glob") Signed-off-by: Jinjie Ruan Reviewed-by: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/executor.c | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index 0eda42b0c9bb..28f144de748b 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -65,7 +65,7 @@ struct kunit_glob_filter { }; /* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */ -static void kunit_parse_glob_filter(struct kunit_glob_filter *parsed, +static int kunit_parse_glob_filter(struct kunit_glob_filter *parsed, const char *filter_glob) { const int len = strlen(filter_glob); @@ -73,16 +73,28 @@ static void kunit_parse_glob_filter(struct kunit_glob_filter *parsed, if (!period) { parsed->suite_glob = kzalloc(len + 1, GFP_KERNEL); + if (!parsed->suite_glob) + return -ENOMEM; + parsed->test_glob = NULL; strcpy(parsed->suite_glob, filter_glob); - return; + return 0; } parsed->suite_glob = kzalloc(period - filter_glob + 1, GFP_KERNEL); + if (!parsed->suite_glob) + return -ENOMEM; + parsed->test_glob = kzalloc(len - (period - filter_glob) + 1, GFP_KERNEL); + if (!parsed->test_glob) { + kfree(parsed->suite_glob); + return -ENOMEM; + } strncpy(parsed->suite_glob, filter_glob, period - filter_glob); strncpy(parsed->test_glob, period + 1, len - (period - filter_glob)); + + return 0; } /* Create a copy of suite with only tests that match test_glob. */ @@ -152,8 +164,11 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, } copy_start = copy; - if (filter_glob) - kunit_parse_glob_filter(&parsed_glob, filter_glob); + if (filter_glob) { + *err = kunit_parse_glob_filter(&parsed_glob, filter_glob); + if (*err) + goto free_copy; + } /* Parse attribute filters */ if (filters) { -- cgit v1.2.3 From 9076bc476d7ebf0565903c4b048442131825c1c3 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Sun, 3 Sep 2023 15:10:28 +0800 Subject: kunit: Fix possible memory leak in kunit_filter_suites() If both filter_glob and filters are not NULL, and kunit_parse_glob_filter() succeed, but kcalloc parsed_filters fails, the suite_glob and test_glob of parsed kzalloc in kunit_parse_glob_filter() will be leaked. As Rae suggested, assign -ENOMEM to *err to correctly free copy and goto free_parsed_glob to free the suite/test_glob of parsed. Fixes: 1c9fd080dffe ("kunit: fix uninitialized variables bug in attributes filtering") Signed-off-by: Jinjie Ruan Suggested-by: Rae Moar Reviewed-by: David Gow Signed-off-by: Shuah Khan --- lib/kunit/executor.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/kunit/executor.c b/lib/kunit/executor.c index 28f144de748b..a6348489d45f 100644 --- a/lib/kunit/executor.c +++ b/lib/kunit/executor.c @@ -175,8 +175,8 @@ kunit_filter_suites(const struct kunit_suite_set *suite_set, filter_count = kunit_get_filter_count(filters); parsed_filters = kcalloc(filter_count, sizeof(*parsed_filters), GFP_KERNEL); if (!parsed_filters) { - kfree(copy); - return filtered; + *err = -ENOMEM; + goto free_parsed_glob; } for (j = 0; j < filter_count; j++) parsed_filters[j] = kunit_next_attr_filter(&filters, err); -- cgit v1.2.3 From f4e4ada586995b17f828c6d147d1800eb1471450 Mon Sep 17 00:00:00 2001 From: Zheng Yejian Date: Mon, 26 Jun 2023 08:11:44 +0800 Subject: selftests/ftrace: Correctly enable event in instance-event.tc Function instance_set() expects to enable event 'sched_switch', so we should set 1 to its 'enable' file. Testcase passed after this patch: # ./ftracetest test.d/instances/instance-event.tc === Ftrace unit tests === [1] Test creation and deletion of trace instances while setting an event [PASS] # of passed: 1 # of failed: 0 # of unresolved: 0 # of untested: 0 # of unsupported: 0 # of xfailed: 0 # of undefined(test bug): 0 Signed-off-by: Zheng Yejian Acked-by: Masami Hiramatsu (Google) Acked-by: Steven Rostedt (Google) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/test.d/instances/instance-event.tc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc index 0eb47fbb3f44..42422e425107 100644 --- a/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc +++ b/tools/testing/selftests/ftrace/test.d/instances/instance-event.tc @@ -39,7 +39,7 @@ instance_read() { instance_set() { while :; do - echo 1 > foo/events/sched/sched_switch + echo 1 > foo/events/sched/sched_switch/enable done 2> /dev/null } -- cgit v1.2.3 From 51aab5ffceb43e05119eb059048fd75765d2bc21 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Tue, 5 Sep 2023 14:26:08 -0400 Subject: tracefs: Add missing lockdown check to tracefs_create_dir() The function tracefs_create_dir() was missing a lockdown check and was called by the RV code. This gave an inconsistent behavior of this function returning success while other tracefs functions failed. This caused the inode being freed by the wrong kmem_cache. Link: https://lkml.kernel.org/r/20230905182711.692687042@goodmis.org Link: https://lore.kernel.org/all/202309050916.58201dc6-oliver.sang@intel.com/ Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Ajay Kaher Cc: Ching-lin Yu Fixes: bf8e602186ec4 ("tracing: Do not create tracefs files if tracefs lockdown is in effect") Reported-by: kernel test robot Signed-off-by: Steven Rostedt (Google) --- fs/tracefs/inode.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index de5b72216b1a..3b8dd938b1c8 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -673,6 +673,9 @@ static struct dentry *__create_dir(const char *name, struct dentry *parent, */ struct dentry *tracefs_create_dir(const char *name, struct dentry *parent) { + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + return __create_dir(name, parent, &simple_dir_inode_operations); } -- cgit v1.2.3 From e24709454c458283146485b76abfcc6d8ea964c7 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Tue, 5 Sep 2023 14:26:09 -0400 Subject: tracefs/eventfs: Add missing lockdown checks All the eventfs external functions do not check if TRACEFS_LOCKDOWN was set or not. This can caused some functions to return success while others fail, which can trigger unexpected errors. Add the missing lockdown checks. Link: https://lkml.kernel.org/r/20230905182711.899724045@goodmis.org Link: https://lore.kernel.org/all/202309050916.58201dc6-oliver.sang@intel.com/ Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Ajay Kaher Cc: Ching-lin Yu Reported-by: kernel test robot Signed-off-by: Steven Rostedt (Google) --- fs/tracefs/event_inode.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 237c6f370ad9..fa1a1679a886 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -491,6 +491,9 @@ struct dentry *eventfs_create_events_dir(const char *name, struct tracefs_inode *ti; struct inode *inode; + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + if (IS_ERR(dentry)) return dentry; @@ -538,6 +541,9 @@ struct eventfs_file *eventfs_add_subsystem_dir(const char *name, struct eventfs_inode *ei_parent; struct eventfs_file *ef; + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + if (!parent) return ERR_PTR(-EINVAL); @@ -569,6 +575,9 @@ struct eventfs_file *eventfs_add_dir(const char *name, { struct eventfs_file *ef; + if (security_locked_down(LOCKDOWN_TRACEFS)) + return NULL; + if (!ef_parent) return ERR_PTR(-EINVAL); @@ -606,6 +615,9 @@ int eventfs_add_events_file(const char *name, umode_t mode, struct eventfs_inode *ei; struct eventfs_file *ef; + if (security_locked_down(LOCKDOWN_TRACEFS)) + return -ENODEV; + if (!parent) return -EINVAL; @@ -654,6 +666,9 @@ int eventfs_add_file(const char *name, umode_t mode, { struct eventfs_file *ef; + if (security_locked_down(LOCKDOWN_TRACEFS)) + return -ENODEV; + if (!ef_parent) return -EINVAL; -- cgit v1.2.3 From 3f091387a39795812aab4303949bbc9baa22c077 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 1 Sep 2023 08:04:31 +0200 Subject: parisc: shmparam.h: Document aliasing requirements of PA-RISC Add some documentation why PA-RISC uses SHMLBA and SHM_COLOUR. Signed-off-by: Helge Deller --- arch/parisc/include/asm/shmparam.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/arch/parisc/include/asm/shmparam.h b/arch/parisc/include/asm/shmparam.h index 74f74e4d35b7..5a95b0f62b87 100644 --- a/arch/parisc/include/asm/shmparam.h +++ b/arch/parisc/include/asm/shmparam.h @@ -2,6 +2,21 @@ #ifndef _ASMPARISC_SHMPARAM_H #define _ASMPARISC_SHMPARAM_H +/* + * PA-RISC uses virtually indexed & physically tagged (VIPT) caches + * which has strict requirements when two pages to the same physical + * address are accessed through different mappings. Read the section + * "Address Aliasing" in the arch docs for more detail: + * PA-RISC 1.1 (page 3-6): + * https://parisc.wiki.kernel.org/images-parisc/6/68/Pa11_acd.pdf + * PA-RISC 2.0 (page F-5): + * https://parisc.wiki.kernel.org/images-parisc/7/73/Parisc2.0.pdf + * + * For Linux we allow kernel and userspace to map pages on page size + * granularity (SHMLBA) but have to ensure that, if two pages are + * mapped to the same physical address, the virtual and physical + * addresses modulo SHM_COLOUR are identical. + */ #define SHMLBA PAGE_SIZE /* attach addr a multiple of this */ #define SHM_COLOUR 0x00400000 /* shared mappings colouring */ -- cgit v1.2.3 From 70bd68d5b61a63f87b5e986d9100c8ce46c5df4d Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Fri, 1 Sep 2023 16:09:23 +0200 Subject: parisc: Prepare for Block-TLB support on 32-bit kernel Change HUGEPAGE_SIZE to become 4 MB on 32-bit kernels, which leads that kernel code and kernel data will start on 4 MB boundaries. Although a 32-bit kernel does not support huge pages, most machines have support for Block-TLBs (BTLB) which allow to configure the system to use large pages (block TLBs) to minimize the TLB contention. This is done through calls to PDC and the 32-bit kernel can then call BTLB PDC functions to tell the machine to optimize the TLBs. Signed-off-by: Helge Deller --- arch/parisc/kernel/asm-offsets.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/parisc/kernel/asm-offsets.c b/arch/parisc/kernel/asm-offsets.c index 94652e13c260..757816a7bd4b 100644 --- a/arch/parisc/kernel/asm-offsets.c +++ b/arch/parisc/kernel/asm-offsets.c @@ -275,6 +275,8 @@ int main(void) * and kernel data on physical huge pages */ #ifdef CONFIG_HUGETLB_PAGE DEFINE(HUGEPAGE_SIZE, 1UL << REAL_HPAGE_SHIFT); +#elif !defined(CONFIG_64BIT) + DEFINE(HUGEPAGE_SIZE, 4*1024*1024); #else DEFINE(HUGEPAGE_SIZE, PAGE_SIZE); #endif -- cgit v1.2.3 From eda205211a522312b667d5bd25d58bee8504c09e Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 7 Sep 2023 08:03:35 +0200 Subject: parisc: BTLB: Clear possibly existing BTLB entries Call PDC to remove all existing BTLB entries (which may exist from some previous operating system runs) before switching to virtual mode. Signed-off-by: Helge Deller --- arch/parisc/kernel/head.S | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/arch/parisc/kernel/head.S b/arch/parisc/kernel/head.S index fd15fd4bbb61..a171bf3c6b31 100644 --- a/arch/parisc/kernel/head.S +++ b/arch/parisc/kernel/head.S @@ -180,10 +180,10 @@ $pgt_fill_loop: std %dp,0x18(%r10) #endif -#ifdef CONFIG_64BIT - /* Get PDCE_PROC for monarch CPU. */ #define MEM_PDC_LO 0x388 #define MEM_PDC_HI 0x35C +#ifdef CONFIG_64BIT + /* Get PDCE_PROC for monarch CPU. */ ldw MEM_PDC_LO(%r0),%r3 ldw MEM_PDC_HI(%r0),%r10 depd %r10, 31, 32, %r3 /* move to upper word */ @@ -269,7 +269,17 @@ stext_pdc_ret: tovirt_r1 %r6 mtctl %r6,%cr30 /* restore task thread info */ #endif - + +#ifndef CONFIG_64BIT + /* clear all BTLBs */ + ldi PDC_BLOCK_TLB,%arg0 + load32 PA(stext_pdc_btlb_ret), %rp + ldw MEM_PDC_LO(%r0),%r3 + bv (%r3) + ldi PDC_BTLB_PURGE_ALL,%arg1 +stext_pdc_btlb_ret: +#endif + /* PARANOID: clear user scratch/user space SR's */ mtsp %r0,%sr0 mtsp %r0,%sr1 -- cgit v1.2.3 From 510610f96d65277940a02f47d7bc7a06c8a2ab7a Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 7 Sep 2023 08:06:37 +0200 Subject: parisc: BTLB: Add BTLB insert and purge firmware function wrappers Signed-off-by: Helge Deller --- arch/parisc/include/asm/pdc.h | 3 +++ arch/parisc/kernel/firmware.c | 42 +++++++++++++++++++++++++++++++++--------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index 269b9a159f01..2fd7eb2f3746 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -46,6 +46,9 @@ int pdc_cache_info(struct pdc_cache_info *cache); int pdc_spaceid_bits(unsigned long *space_bits); #ifndef CONFIG_PA20 int pdc_btlb_info(struct pdc_btlb_info *btlb); +int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, + unsigned long entry_info, unsigned long slot); +int pdc_btlb_purge_all(void); int pdc_mem_map_hpa(struct pdc_memory_map *r_addr, struct pdc_module_path *mod_path); #endif /* !CONFIG_PA20 */ int pdc_pim_toc11(struct pdc_toc_pim_11 *ret); diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c index 8f37e75f2fb9..79bf44d04aa8 100644 --- a/arch/parisc/kernel/firmware.c +++ b/arch/parisc/kernel/firmware.c @@ -696,18 +696,42 @@ int pdc_spaceid_bits(unsigned long *space_bits) */ int pdc_btlb_info(struct pdc_btlb_info *btlb) { - int retval; + int retval; unsigned long flags; - spin_lock_irqsave(&pdc_lock, flags); - retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0); - memcpy(btlb, pdc_result, sizeof(*btlb)); - spin_unlock_irqrestore(&pdc_lock, flags); + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0); + memcpy(btlb, pdc_result, sizeof(*btlb)); + spin_unlock_irqrestore(&pdc_lock, flags); - if(retval < 0) { - btlb->max_size = 0; - } - return retval; + if(retval < 0) { + btlb->max_size = 0; + } + return retval; +} + +int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, + unsigned long entry_info, unsigned long slot) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INSERT, (unsigned long) (vpage >> 32), + (unsigned long) vpage, physpage, len, entry_info, slot); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; +} + +int pdc_btlb_purge_all(void) +{ + int retval; + unsigned long flags; + + spin_lock_irqsave(&pdc_lock, flags); + retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_PURGE_ALL); + spin_unlock_irqrestore(&pdc_lock, flags); + return retval; } /** -- cgit v1.2.3 From 4695e45ec0cd4d422694b69e9f39c742bb1f35fc Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 7 Sep 2023 08:07:55 +0200 Subject: parisc: BTLB: _edata symbol has to be page aligned for BTLB support Signed-off-by: Helge Deller --- arch/parisc/kernel/vmlinux.lds.S | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/parisc/kernel/vmlinux.lds.S b/arch/parisc/kernel/vmlinux.lds.S index 1aaa2ca09800..58694d1989c2 100644 --- a/arch/parisc/kernel/vmlinux.lds.S +++ b/arch/parisc/kernel/vmlinux.lds.S @@ -154,6 +154,7 @@ SECTIONS } /* End of data section */ + . = ALIGN(PAGE_SIZE); _edata = .; /* BSS */ -- cgit v1.2.3 From 3756597a684da9fbf966f91ed351033ac1a0f55f Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 7 Sep 2023 08:51:20 +0200 Subject: parisc: firmware: Simplify calling non-PA20 functions Instead of usig #ifdefs, simply return PDC_BAD_PROC for functions which aren't available on 64-bit CPUs. Signed-off-by: Helge Deller --- arch/parisc/include/asm/pdc.h | 2 -- arch/parisc/kernel/firmware.c | 14 ++++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/arch/parisc/include/asm/pdc.h b/arch/parisc/include/asm/pdc.h index 2fd7eb2f3746..5d2d9737e579 100644 --- a/arch/parisc/include/asm/pdc.h +++ b/arch/parisc/include/asm/pdc.h @@ -44,13 +44,11 @@ int pdc_model_capabilities(unsigned long *capabilities); int pdc_model_platform_info(char *orig_prod_num, char *current_prod_num, char *serial_no); int pdc_cache_info(struct pdc_cache_info *cache); int pdc_spaceid_bits(unsigned long *space_bits); -#ifndef CONFIG_PA20 int pdc_btlb_info(struct pdc_btlb_info *btlb); int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned long len, unsigned long entry_info, unsigned long slot); int pdc_btlb_purge_all(void); int pdc_mem_map_hpa(struct pdc_memory_map *r_addr, struct pdc_module_path *mod_path); -#endif /* !CONFIG_PA20 */ int pdc_pim_toc11(struct pdc_toc_pim_11 *ret); int pdc_pim_toc20(struct pdc_toc_pim_20 *ret); int pdc_lan_station_id(char *lan_addr, unsigned long net_hpa); diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c index 79bf44d04aa8..81078abec521 100644 --- a/arch/parisc/kernel/firmware.c +++ b/arch/parisc/kernel/firmware.c @@ -687,7 +687,6 @@ int pdc_spaceid_bits(unsigned long *space_bits) return retval; } -#ifndef CONFIG_PA20 /** * pdc_btlb_info - Return block TLB information. * @btlb: The return buffer. @@ -699,6 +698,9 @@ int pdc_btlb_info(struct pdc_btlb_info *btlb) int retval; unsigned long flags; + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INFO, __pa(pdc_result), 0); memcpy(btlb, pdc_result, sizeof(*btlb)); @@ -716,6 +718,9 @@ int pdc_btlb_insert(unsigned long long vpage, unsigned long physpage, unsigned l int retval; unsigned long flags; + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_INSERT, (unsigned long) (vpage >> 32), (unsigned long) vpage, physpage, len, entry_info, slot); @@ -728,6 +733,9 @@ int pdc_btlb_purge_all(void) int retval; unsigned long flags; + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + spin_lock_irqsave(&pdc_lock, flags); retval = mem_pdc_call(PDC_BLOCK_TLB, PDC_BTLB_PURGE_ALL); spin_unlock_irqrestore(&pdc_lock, flags); @@ -752,6 +760,9 @@ int pdc_mem_map_hpa(struct pdc_memory_map *address, int retval; unsigned long flags; + if (IS_ENABLED(CONFIG_PA20)) + return PDC_BAD_PROC; + spin_lock_irqsave(&pdc_lock, flags); memcpy(pdc_result2, mod_path, sizeof(*mod_path)); retval = mem_pdc_call(PDC_MEM_MAP, PDC_MEM_MAP_HPA, __pa(pdc_result), @@ -761,7 +772,6 @@ int pdc_mem_map_hpa(struct pdc_memory_map *address, return retval; } -#endif /* !CONFIG_PA20 */ /** * pdc_lan_station_id - Get the LAN address. -- cgit v1.2.3 From e5ef93d02d6c9cc3a14e7348481c9e41a528caa1 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Thu, 7 Sep 2023 08:57:44 +0200 Subject: parisc: BTLB: Initialize BTLB tables at CPU startup Initialize the BTLB entries when starting up a CPU. Note that BTLBs are not available on 64-bit CPUs. Signed-off-by: Helge Deller --- arch/parisc/include/asm/cache.h | 1 + arch/parisc/include/asm/processor.h | 1 + arch/parisc/kernel/cache.c | 8 +---- arch/parisc/kernel/processor.c | 2 ++ arch/parisc/mm/init.c | 72 +++++++++++++++++++++++++++++++++++++ 5 files changed, 77 insertions(+), 7 deletions(-) diff --git a/arch/parisc/include/asm/cache.h b/arch/parisc/include/asm/cache.h index e23d06b51a20..2a60d7a72f1f 100644 --- a/arch/parisc/include/asm/cache.h +++ b/arch/parisc/include/asm/cache.h @@ -37,6 +37,7 @@ extern int split_tlb; extern int dcache_stride; extern int icache_stride; extern struct pdc_cache_info cache_info; +extern struct pdc_btlb_info btlb_info; void parisc_setup_cache_timing(void); #define pdtlb(sr, addr) asm volatile("pdtlb 0(%%sr%0,%1)" \ diff --git a/arch/parisc/include/asm/processor.h b/arch/parisc/include/asm/processor.h index d77c43d32974..ff6cbdb6903b 100644 --- a/arch/parisc/include/asm/processor.h +++ b/arch/parisc/include/asm/processor.h @@ -310,6 +310,7 @@ extern void do_syscall_trace_exit(struct pt_regs *); struct seq_file; extern void early_trap_init(void); extern void collect_boot_cpu_data(void); +extern void btlb_init_per_cpu(void); extern int show_cpuinfo (struct seq_file *m, void *v); /* driver code in driver/parisc */ diff --git a/arch/parisc/kernel/cache.c b/arch/parisc/kernel/cache.c index 442109a48940..268d90a9325b 100644 --- a/arch/parisc/kernel/cache.c +++ b/arch/parisc/kernel/cache.c @@ -58,7 +58,7 @@ int pa_serialize_tlb_flushes __ro_after_init; struct pdc_cache_info cache_info __ro_after_init; #ifndef CONFIG_PA20 -static struct pdc_btlb_info btlb_info __ro_after_init; +struct pdc_btlb_info btlb_info __ro_after_init; #endif DEFINE_STATIC_KEY_TRUE(parisc_has_cache); @@ -264,12 +264,6 @@ parisc_cache_init(void) icache_stride = CAFL_STRIDE(cache_info.ic_conf); #undef CAFL_STRIDE -#ifndef CONFIG_PA20 - if (pdc_btlb_info(&btlb_info) < 0) { - memset(&btlb_info, 0, sizeof btlb_info); - } -#endif - if ((boot_cpu_data.pdc.capabilities & PDC_MODEL_NVA_MASK) == PDC_MODEL_NVA_UNSUPPORTED) { printk(KERN_WARNING "parisc_cache_init: Only equivalent aliasing supported!\n"); diff --git a/arch/parisc/kernel/processor.c b/arch/parisc/kernel/processor.c index a0e2d37c5b3b..1fc89fa2c2d2 100644 --- a/arch/parisc/kernel/processor.c +++ b/arch/parisc/kernel/processor.c @@ -368,6 +368,8 @@ int init_per_cpu(int cpunum) /* FUTURE: Enable Performance Monitor : ccr bit 0x20 */ init_percpu_prof(cpunum); + btlb_init_per_cpu(); + return ret; } diff --git a/arch/parisc/mm/init.c b/arch/parisc/mm/init.c index a088c243edea..a2a3e89f2d9a 100644 --- a/arch/parisc/mm/init.c +++ b/arch/parisc/mm/init.c @@ -32,6 +32,7 @@ #include #include #include +#include extern int data_start; extern void parisc_kernel_start(void); /* Kernel entry point in head.S */ @@ -720,6 +721,77 @@ void __init paging_init(void) parisc_bootmem_free(); } +static void alloc_btlb(unsigned long start, unsigned long end, int *slot, + unsigned long entry_info) +{ + const int slot_max = btlb_info.fixed_range_info.num_comb; + int min_num_pages = btlb_info.min_size; + unsigned long size; + + /* map at minimum 4 pages */ + if (min_num_pages < 4) + min_num_pages = 4; + + size = HUGEPAGE_SIZE; + while (start < end && *slot < slot_max && size >= PAGE_SIZE) { + /* starting address must have same alignment as size! */ + /* if correctly aligned and fits in double size, increase */ + if (((start & (2 * size - 1)) == 0) && + (end - start) >= (2 * size)) { + size <<= 1; + continue; + } + /* if current size alignment is too big, try smaller size */ + if ((start & (size - 1)) != 0) { + size >>= 1; + continue; + } + if ((end - start) >= size) { + if ((size >> PAGE_SHIFT) >= min_num_pages) + pdc_btlb_insert(start >> PAGE_SHIFT, __pa(start) >> PAGE_SHIFT, + size >> PAGE_SHIFT, entry_info, *slot); + (*slot)++; + start += size; + continue; + } + size /= 2; + continue; + } +} + +void btlb_init_per_cpu(void) +{ + unsigned long s, t, e; + int slot; + + /* BTLBs are not available on 64-bit CPUs */ + if (IS_ENABLED(CONFIG_PA20)) + return; + else if (pdc_btlb_info(&btlb_info) < 0) { + memset(&btlb_info, 0, sizeof btlb_info); + } + + /* insert BLTLBs for code and data segments */ + s = (uintptr_t) dereference_function_descriptor(&_stext); + e = (uintptr_t) dereference_function_descriptor(&_etext); + t = (uintptr_t) dereference_function_descriptor(&_sdata); + BUG_ON(t != e); + + /* code segments */ + slot = 0; + alloc_btlb(s, e, &slot, 0x13800000); + + /* sanity check */ + t = (uintptr_t) dereference_function_descriptor(&_edata); + e = (uintptr_t) dereference_function_descriptor(&__bss_start); + BUG_ON(t != e); + + /* data segments */ + s = (uintptr_t) dereference_function_descriptor(&_sdata); + e = (uintptr_t) dereference_function_descriptor(&__bss_stop); + alloc_btlb(s, e, &slot, 0x11800000); +} + #ifdef CONFIG_PA20 /* -- cgit v1.2.3 From d20b484c674d2eae816978a98fa38b4054aeca3b Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Wed, 6 Sep 2023 11:50:39 +0200 Subject: drm/drm_exec: Work around a WW mutex lockdep oddity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If *any* object of a certain WW mutex class is locked, lockdep will consider *all* mutexes of that class as locked. Also the lock allocation tracking code will apparently register only the address of the first mutex of a given class locked in a sequence. This has the odd consequence that if that first mutex is unlocked while other mutexes of the same class remain locked and then its memory then freed, the lock alloc tracking code will incorrectly assume that memory is freed with a held lock in there. For now, work around that for drm_exec by releasing the first grabbed object lock last. v2: - Fix a typo (Danilo Krummrich) - Reword the commit message a bit. - Add a Fixes: tag Related lock alloc tracking warning: [ 322.660067] ========================= [ 322.660070] WARNING: held lock freed! [ 322.660074] 6.5.0-rc7+ #155 Tainted: G U N [ 322.660078] ------------------------- [ 322.660081] kunit_try_catch/4981 is freeing memory ffff888112adc000-ffff888112adc3ff, with a lock still held there! [ 322.660089] ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec] [ 322.660104] 2 locks held by kunit_try_catch/4981: [ 322.660108] #0: ffffc9000343fe18 (reservation_ww_class_acquire){+.+.}-{0:0}, at: test_early_put+0x22f/0x490 [drm_exec_test] [ 322.660123] #1: ffff888112adc1a0 (reservation_ww_class_mutex){+.+.}-{3:3}, at: drm_exec_lock_obj+0x11a/0x600 [drm_exec] [ 322.660135] stack backtrace: [ 322.660139] CPU: 7 PID: 4981 Comm: kunit_try_catch Tainted: G U N 6.5.0-rc7+ #155 [ 322.660146] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021 [ 322.660152] Call Trace: [ 322.660155] [ 322.660158] dump_stack_lvl+0x57/0x90 [ 322.660164] debug_check_no_locks_freed+0x20b/0x2b0 [ 322.660172] slab_free_freelist_hook+0xa1/0x160 [ 322.660179] ? drm_exec_unlock_all+0x168/0x2a0 [drm_exec] [ 322.660186] __kmem_cache_free+0xb2/0x290 [ 322.660192] drm_exec_unlock_all+0x168/0x2a0 [drm_exec] [ 322.660200] drm_exec_fini+0xf/0x1c0 [drm_exec] [ 322.660206] test_early_put+0x289/0x490 [drm_exec_test] [ 322.660215] ? __pfx_test_early_put+0x10/0x10 [drm_exec_test] [ 322.660222] ? __kasan_check_byte+0xf/0x40 [ 322.660227] ? __ksize+0x63/0x140 [ 322.660233] ? drmm_add_final_kfree+0x3e/0xa0 [drm] [ 322.660289] ? _raw_spin_unlock_irqrestore+0x30/0x60 [ 322.660294] ? lockdep_hardirqs_on+0x7d/0x100 [ 322.660301] ? __pfx_kunit_try_run_case+0x10/0x10 [kunit] [ 322.660310] ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit] [ 322.660319] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [ 322.660328] kthread+0x2e7/0x3c0 [ 322.660334] ? __pfx_kthread+0x10/0x10 [ 322.660339] ret_from_fork+0x2d/0x70 [ 322.660345] ? __pfx_kthread+0x10/0x10 [ 322.660349] ret_from_fork_asm+0x1b/0x30 [ 322.660358] [ 322.660818] ok 8 test_early_put Cc: Christian König Cc: Boris Brezillon Cc: Danilo Krummrich Cc: dri-devel@lists.freedesktop.org Fixes: 09593216bff1 ("drm: execution context for GEM buffers v7") Signed-off-by: Thomas Hellström Reviewed-by: Boris Brezillon Reviewed-by: Danilo Krummrich Reviewed-by: Christian König Link: https://patchwork.freedesktop.org/patch/msgid/20230906095039.3320-4-thomas.hellstrom@linux.intel.com --- drivers/gpu/drm/drm_exec.c | 2 +- include/drm/drm_exec.h | 35 +++++++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/drm_exec.c b/drivers/gpu/drm/drm_exec.c index ff69cf0fb42a..5d2809de4517 100644 --- a/drivers/gpu/drm/drm_exec.c +++ b/drivers/gpu/drm/drm_exec.c @@ -56,7 +56,7 @@ static void drm_exec_unlock_all(struct drm_exec *exec) struct drm_gem_object *obj; unsigned long index; - drm_exec_for_each_locked_object(exec, index, obj) { + drm_exec_for_each_locked_object_reverse(exec, index, obj) { dma_resv_unlock(obj->resv); drm_gem_object_put(obj); } diff --git a/include/drm/drm_exec.h b/include/drm/drm_exec.h index e0462361adf9..b5bf0b6da791 100644 --- a/include/drm/drm_exec.h +++ b/include/drm/drm_exec.h @@ -51,6 +51,20 @@ struct drm_exec { struct drm_gem_object *prelocked; }; +/** + * drm_exec_obj() - Return the object for a give drm_exec index + * @exec: Pointer to the drm_exec context + * @index: The index. + * + * Return: Pointer to the locked object corresponding to @index if + * index is within the number of locked objects. NULL otherwise. + */ +static inline struct drm_gem_object * +drm_exec_obj(struct drm_exec *exec, unsigned long index) +{ + return index < exec->num_objects ? exec->objects[index] : NULL; +} + /** * drm_exec_for_each_locked_object - iterate over all the locked objects * @exec: drm_exec object @@ -59,10 +73,23 @@ struct drm_exec { * * Iterate over all the locked GEM objects inside the drm_exec object. */ -#define drm_exec_for_each_locked_object(exec, index, obj) \ - for (index = 0, obj = (exec)->objects[0]; \ - index < (exec)->num_objects; \ - ++index, obj = (exec)->objects[index]) +#define drm_exec_for_each_locked_object(exec, index, obj) \ + for ((index) = 0; ((obj) = drm_exec_obj(exec, index)); ++(index)) + +/** + * drm_exec_for_each_locked_object_reverse - iterate over all the locked + * objects in reverse locking order + * @exec: drm_exec object + * @index: unsigned long index for the iteration + * @obj: the current GEM object + * + * Iterate over all the locked GEM objects inside the drm_exec object in + * reverse locking order. Note that @index may go below zero and wrap, + * but that will be caught by drm_exec_obj(), returning a NULL object. + */ +#define drm_exec_for_each_locked_object_reverse(exec, index, obj) \ + for ((index) = (exec)->num_objects - 1; \ + ((obj) = drm_exec_obj(exec, index)); --(index)) /** * drm_exec_until_all_locked - loop until all GEM objects are locked -- cgit v1.2.3 From dcbad727513d277144aee482b2ffbcd2255c37aa Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 6 Sep 2023 15:55:17 -0400 Subject: drm/radeon: make fence wait in suballocator uninterrruptable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 254986e324ad ("drm/radeon: Use the drm suballocation manager implementation.") made the fence wait in amdgpu_sa_bo_new() interruptible but there is no code to handle an interrupt. This caused the kernel to randomly explode in high-VRAM-pressure situations so make it uninterruptible again. Fixes: 254986e324ad ("drm/radeon: Use the drm suballocation manager implementation.") Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2769 Signed-off-by: Alex Deucher CC: stable@vger.kernel.org # 6.4+ CC: Simon Pilkington Link: https://patchwork.freedesktop.org/patch/msgid/20230906195517.1345717-1-alexander.deucher@amd.com Signed-off-by: Christian König --- drivers/gpu/drm/radeon/radeon_sa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/radeon/radeon_sa.c b/drivers/gpu/drm/radeon/radeon_sa.c index c87a57c9c592..22dd8b445685 100644 --- a/drivers/gpu/drm/radeon/radeon_sa.c +++ b/drivers/gpu/drm/radeon/radeon_sa.c @@ -123,7 +123,7 @@ int radeon_sa_bo_new(struct radeon_sa_manager *sa_manager, unsigned int size, unsigned int align) { struct drm_suballoc *sa = drm_suballoc_new(&sa_manager->base, size, - GFP_KERNEL, true, align); + GFP_KERNEL, false, align); if (IS_ERR(sa)) { *sa_bo = NULL; -- cgit v1.2.3 From f94cf2206b066bd6d761d3347fd35f77b828c376 Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Thu, 7 Sep 2023 09:40:07 -0400 Subject: buffer: Make bh_offset() work for compound pages If the buffer pointed to by the buffer_head is part of a compound page, bh_offset() assumes that b_page is the precise page that contains the data. A recent change to jbd2 inadvertently violated that assumption. By using page_size(), we support both b_page being set to the head page (as page_size() will return the size of the entire folio) and the precise page (as it will return PAGE_SIZE for a tail page). Fixes: 8147c4c4546f ("jbd2: use a folio in jbd2_journal_write_metadata_buffer()") Reported-by: Zorro Lang Tested-by: Ritesh Harjani (IBM) Signed-off-by: Matthew Wilcox (Oracle) --- include/linux/buffer_head.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/linux/buffer_head.h b/include/linux/buffer_head.h index 6cb3e9af78c9..4ba242073adc 100644 --- a/include/linux/buffer_head.h +++ b/include/linux/buffer_head.h @@ -173,7 +173,10 @@ static __always_inline int buffer_uptodate(const struct buffer_head *bh) return test_bit_acquire(BH_Uptodate, &bh->b_state); } -#define bh_offset(bh) ((unsigned long)(bh)->b_data & ~PAGE_MASK) +static inline unsigned long bh_offset(const struct buffer_head *bh) +{ + return (unsigned long)(bh)->b_data & (page_size(bh->b_page) - 1); +} /* If we *know* page->private refers to buffer_heads */ #define page_buffers(page) \ -- cgit v1.2.3 From 147d4a092e9a726ce706dbf0d329d2b96a025459 Mon Sep 17 00:00:00 2001 From: "Ritesh Harjani (IBM)" Date: Thu, 7 Sep 2023 09:47:32 -0400 Subject: jbd2: Remove page size assumptions jbd2_alloc() allocates a buffer from slab when the block size is smaller than PAGE_SIZE, and slab may be using a compound page. Before commit 8147c4c4546f, we set b_page to the precise page containing the buffer and this code worked well. Now we set b_page to the head page of the allocation, so we can no longer use offset_in_page(). While we could do a 1:1 replacement with offset_in_folio(), use the more idiomatic bh_offset() and the folio APIs to map the buffer. This isn't enough to support a b_size larger than PAGE_SIZE on HIGHMEM machines, but this is good enough to fix the actual bug we're seeing. Fixes: 8147c4c4546f ("jbd2: use a folio in jbd2_journal_write_metadata_buffer()") Reported-by: Zorro Lang Signed-off-by: Ritesh Harjani (IBM) [converted to be more folio] Signed-off-by: Matthew Wilcox (Oracle) --- fs/jbd2/commit.c | 16 ++++++---------- fs/jbd2/transaction.c | 12 ++++-------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 1073259902a6..8d6f934c3d95 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -298,14 +298,12 @@ static int journal_finish_inode_data_buffers(journal_t *journal, static __u32 jbd2_checksum_data(__u32 crc32_sum, struct buffer_head *bh) { - struct page *page = bh->b_page; char *addr; __u32 checksum; - addr = kmap_atomic(page); - checksum = crc32_be(crc32_sum, - (void *)(addr + offset_in_page(bh->b_data)), bh->b_size); - kunmap_atomic(addr); + addr = kmap_local_folio(bh->b_folio, bh_offset(bh)); + checksum = crc32_be(crc32_sum, addr, bh->b_size); + kunmap_local(addr); return checksum; } @@ -322,7 +320,6 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, struct buffer_head *bh, __u32 sequence) { journal_block_tag3_t *tag3 = (journal_block_tag3_t *)tag; - struct page *page = bh->b_page; __u8 *addr; __u32 csum32; __be32 seq; @@ -331,11 +328,10 @@ static void jbd2_block_tag_csum_set(journal_t *j, journal_block_tag_t *tag, return; seq = cpu_to_be32(sequence); - addr = kmap_atomic(page); + addr = kmap_local_folio(bh->b_folio, bh_offset(bh)); csum32 = jbd2_chksum(j, j->j_csum_seed, (__u8 *)&seq, sizeof(seq)); - csum32 = jbd2_chksum(j, csum32, addr + offset_in_page(bh->b_data), - bh->b_size); - kunmap_atomic(addr); + csum32 = jbd2_chksum(j, csum32, addr, bh->b_size); + kunmap_local(addr); if (jbd2_has_feature_csum3(j)) tag3->t_checksum = cpu_to_be32(csum32); diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index 4d1fda1f7143..5f08b5fd105a 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -935,19 +935,15 @@ static void warn_dirty_buffer(struct buffer_head *bh) /* Call t_frozen trigger and copy buffer data into jh->b_frozen_data. */ static void jbd2_freeze_jh_data(struct journal_head *jh) { - struct page *page; - int offset; char *source; struct buffer_head *bh = jh2bh(jh); J_EXPECT_JH(jh, buffer_uptodate(bh), "Possible IO failure.\n"); - page = bh->b_page; - offset = offset_in_page(bh->b_data); - source = kmap_atomic(page); + source = kmap_local_folio(bh->b_folio, bh_offset(bh)); /* Fire data frozen trigger just before we copy the data */ - jbd2_buffer_frozen_trigger(jh, source + offset, jh->b_triggers); - memcpy(jh->b_frozen_data, source + offset, bh->b_size); - kunmap_atomic(source); + jbd2_buffer_frozen_trigger(jh, source, jh->b_triggers); + memcpy(jh->b_frozen_data, source, bh->b_size); + kunmap_local(source); /* * Now that the frozen data is saved off, we need to store any matching -- cgit v1.2.3 From 9879e5e1c528d1ee9c4473b1d0668ba988bfb6ca Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 6 Sep 2023 22:47:11 -0400 Subject: tracefs/eventfs: Use dput to free the toplevel events directory Currently when rmdir on an instance is done, eventfs_remove_events_dir() is called and it does a dput on the dentry and then frees the eventfs_inode that represents the events directory. But there's no protection against a reader reading the top level events directory at the same time and we can get a use after free error. Instead, use the dput() associated to the dentry to also free the eventfs_inode associated to the events directory, as that will get called when the last reference to the directory is released. This issue triggered the following KASAN report: ================================================================== BUG: KASAN: slab-use-after-free in eventfs_root_lookup+0x88/0x1b0 Read of size 8 at addr ffff888120130ca0 by task ftracetest/1201 CPU: 4 PID: 1201 Comm: ftracetest Not tainted 6.5.0-test-10737-g469e0a8194e7 #13 Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 Call Trace: dump_stack_lvl+0x57/0x90 print_report+0xcf/0x670 ? __pfx_ring_buffer_record_off+0x10/0x10 ? _raw_spin_lock_irqsave+0x2b/0x70 ? __virt_addr_valid+0xd9/0x160 kasan_report+0xd4/0x110 ? eventfs_root_lookup+0x88/0x1b0 ? eventfs_root_lookup+0x88/0x1b0 eventfs_root_lookup+0x88/0x1b0 ? eventfs_root_lookup+0x33/0x1b0 __lookup_slow+0x194/0x2a0 ? __pfx___lookup_slow+0x10/0x10 ? down_read+0x11c/0x330 walk_component+0x166/0x220 link_path_walk.part.0.constprop.0+0x3a3/0x5a0 ? seqcount_lockdep_reader_access+0x82/0x90 ? __pfx_link_path_walk.part.0.constprop.0+0x10/0x10 path_openat+0x143/0x11f0 ? __lock_acquire+0xa1a/0x3220 ? __pfx_path_openat+0x10/0x10 ? __pfx___lock_acquire+0x10/0x10 do_filp_open+0x166/0x290 ? __pfx_do_filp_open+0x10/0x10 ? lock_is_held_type+0xce/0x120 ? preempt_count_sub+0xb7/0x100 ? _raw_spin_unlock+0x29/0x50 ? alloc_fd+0x1a0/0x320 do_sys_openat2+0x126/0x160 ? rcu_is_watching+0x34/0x60 ? __pfx_do_sys_openat2+0x10/0x10 ? __might_resched+0x2cf/0x3b0 ? __fget_light+0xdf/0x100 __x64_sys_openat+0xcd/0x140 ? __pfx___x64_sys_openat+0x10/0x10 ? syscall_enter_from_user_mode+0x22/0x90 ? lockdep_hardirqs_on+0x7d/0x100 do_syscall_64+0x3b/0xc0 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 RIP: 0033:0x7f1dceef5e51 Code: 75 57 89 f0 25 00 00 41 00 3d 00 00 41 00 74 49 80 3d 9a 27 0e 00 00 74 6d 89 da 48 89 ee bf 9c ff ff ff b8 01 01 00 00 0f 05 <48> 3d 00 f0 ff ff 0f 87 93 00 00 00 48 8b 54 24 28 64 48 2b 14 25 RSP: 002b:00007fff2cddf380 EFLAGS: 00000202 ORIG_RAX: 0000000000000101 RAX: ffffffffffffffda RBX: 0000000000000241 RCX: 00007f1dceef5e51 RDX: 0000000000000241 RSI: 000055d7520677d0 RDI: 00000000ffffff9c RBP: 000055d7520677d0 R08: 000000000000001e R09: 0000000000000001 R10: 00000000000001b6 R11: 0000000000000202 R12: 0000000000000000 R13: 0000000000000003 R14: 000055d752035678 R15: 000055d752067788 Allocated by task 1200: kasan_save_stack+0x2f/0x50 kasan_set_track+0x21/0x30 __kasan_kmalloc+0x8b/0x90 eventfs_create_events_dir+0x54/0x220 create_event_toplevel_files+0x42/0x130 event_trace_add_tracer+0x33/0x180 trace_array_create_dir+0x52/0xf0 trace_array_create+0x361/0x410 instance_mkdir+0x6b/0xb0 tracefs_syscall_mkdir+0x57/0x80 vfs_mkdir+0x275/0x380 do_mkdirat+0x1da/0x210 __x64_sys_mkdir+0x74/0xa0 do_syscall_64+0x3b/0xc0 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Freed by task 1251: kasan_save_stack+0x2f/0x50 kasan_set_track+0x21/0x30 kasan_save_free_info+0x27/0x40 __kasan_slab_free+0x106/0x180 __kmem_cache_free+0x149/0x2e0 event_trace_del_tracer+0xcb/0x120 __remove_instance+0x16a/0x340 instance_rmdir+0x77/0xa0 tracefs_syscall_rmdir+0x77/0xc0 vfs_rmdir+0xed/0x2d0 do_rmdir+0x235/0x280 __x64_sys_rmdir+0x5f/0x90 do_syscall_64+0x3b/0xc0 entry_SYSCALL_64_after_hwframe+0x6e/0xd8 The buggy address belongs to the object at ffff888120130ca0 which belongs to the cache kmalloc-16 of size 16 The buggy address is located 0 bytes inside of freed 16-byte region [ffff888120130ca0, ffff888120130cb0) The buggy address belongs to the physical page: page:000000004dbddbb0 refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x120130 flags: 0x17ffffc0000800(slab|node=0|zone=2|lastcpupid=0x1fffff) page_type: 0xffffffff() raw: 0017ffffc0000800 ffff8881000423c0 dead000000000122 0000000000000000 raw: 0000000000000000 0000000000800080 00000001ffffffff 0000000000000000 page dumped because: kasan: bad access detected Memory state around the buggy address: ffff888120130b80: 00 00 fc fc 00 05 fc fc 00 00 fc fc 00 02 fc fc ffff888120130c00: 00 07 fc fc 00 00 fc fc 00 00 fc fc fa fb fc fc >ffff888120130c80: 00 00 fc fc fa fb fc fc 00 00 fc fc 00 00 fc fc ^ ffff888120130d00: 00 00 fc fc 00 00 fc fc 00 00 fc fc fa fb fc fc ffff888120130d80: 00 00 fc fc 00 00 fc fc 00 00 fc fc 00 00 fc fc ================================================================== Link: https://lkml.kernel.org/r/20230907024803.250873643@goodmis.org Link: https://lore.kernel.org/all/1cb3aee2-19af-c472-e265-05176fe9bd84@huawei.com/ Cc: Ajay Kaher Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Fixes: 5bdcd5f5331a2 eventfs: ("Implement removal of meta data from eventfs") Tested-by: Linux Kernel Functional Testing Tested-by: Naresh Kamboju Reported-by: Zheng Yejian Signed-off-by: Steven Rostedt (Google) --- fs/tracefs/event_inode.c | 17 ++++++++++++----- fs/tracefs/inode.c | 2 +- fs/tracefs/internal.h | 5 +++-- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index fa1a1679a886..609ccb5b7cfc 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -185,17 +185,27 @@ static struct dentry *create_dir(const char *name, struct dentry *parent, void * /** * eventfs_set_ef_status_free - set the ef->status to free + * @ti: the tracefs_inode of the dentry * @dentry: dentry who's status to be freed * * eventfs_set_ef_status_free will be called if no more * references remain */ -void eventfs_set_ef_status_free(struct dentry *dentry) +void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry) { struct tracefs_inode *ti_parent; + struct eventfs_inode *ei; struct eventfs_file *ef; mutex_lock(&eventfs_mutex); + + /* The top level events directory may be freed by this */ + if (unlikely(ti->flags & TRACEFS_EVENT_TOP_INODE)) { + ei = ti->private; + kfree(ei); + goto out; + } + ti_parent = get_tracefs(dentry->d_parent->d_inode); if (!ti_parent || !(ti_parent->flags & TRACEFS_EVENT_INODE)) goto out; @@ -510,7 +520,7 @@ struct dentry *eventfs_create_events_dir(const char *name, INIT_LIST_HEAD(&ei->e_top_files); ti = get_tracefs(inode); - ti->flags |= TRACEFS_EVENT_INODE; + ti->flags |= TRACEFS_EVENT_INODE | TRACEFS_EVENT_TOP_INODE; ti->private = ei; inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; @@ -806,7 +816,6 @@ void eventfs_remove(struct eventfs_file *ef) void eventfs_remove_events_dir(struct dentry *dentry) { struct tracefs_inode *ti; - struct eventfs_inode *ei; if (!dentry || !dentry->d_inode) return; @@ -815,8 +824,6 @@ void eventfs_remove_events_dir(struct dentry *dentry) if (!ti || !(ti->flags & TRACEFS_EVENT_INODE)) return; - ei = ti->private; d_invalidate(dentry); dput(dentry); - kfree(ei); } diff --git a/fs/tracefs/inode.c b/fs/tracefs/inode.c index 3b8dd938b1c8..891653ba9cf3 100644 --- a/fs/tracefs/inode.c +++ b/fs/tracefs/inode.c @@ -385,7 +385,7 @@ static void tracefs_dentry_iput(struct dentry *dentry, struct inode *inode) ti = get_tracefs(inode); if (ti && ti->flags & TRACEFS_EVENT_INODE) - eventfs_set_ef_status_free(dentry); + eventfs_set_ef_status_free(ti, dentry); iput(inode); } diff --git a/fs/tracefs/internal.h b/fs/tracefs/internal.h index 69c2b1d87c46..4f2e49e2197b 100644 --- a/fs/tracefs/internal.h +++ b/fs/tracefs/internal.h @@ -3,7 +3,8 @@ #define _TRACEFS_INTERNAL_H enum { - TRACEFS_EVENT_INODE = BIT(1), + TRACEFS_EVENT_INODE = BIT(1), + TRACEFS_EVENT_TOP_INODE = BIT(2), }; struct tracefs_inode { @@ -24,6 +25,6 @@ struct inode *tracefs_get_inode(struct super_block *sb); struct dentry *eventfs_start_creating(const char *name, struct dentry *parent); struct dentry *eventfs_failed_creating(struct dentry *dentry); struct dentry *eventfs_end_creating(struct dentry *dentry); -void eventfs_set_ef_status_free(struct dentry *dentry); +void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry); #endif /* _TRACEFS_INTERNAL_H */ -- cgit v1.2.3 From f5ca233e2e66dc1c249bf07eefa37e34a6c9346a Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 6 Sep 2023 22:47:12 -0400 Subject: tracing: Increase trace array ref count on enable and filter files When the trace event enable and filter files are opened, increment the trace array ref counter, otherwise they can be accessed when the trace array is being deleted. The ref counter keeps the trace array from being deleted while those files are opened. Link: https://lkml.kernel.org/r/20230907024803.456187066@goodmis.org Link: https://lore.kernel.org/all/1cb3aee2-19af-c472-e265-05176fe9bd84@huawei.com/ Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Fixes: 8530dec63e7b4 ("tracing: Add tracing_check_open_get_tr()") Tested-by: Linux Kernel Functional Testing Tested-by: Naresh Kamboju Reported-by: Zheng Yejian Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 27 +++++++++++++++++++++++++++ kernel/trace/trace.h | 2 ++ kernel/trace/trace_events.c | 6 ++++-- 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 35783a7baf15..0827037ee3b8 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -4973,6 +4973,33 @@ int tracing_open_generic_tr(struct inode *inode, struct file *filp) return 0; } +/* + * The private pointer of the inode is the trace_event_file. + * Update the tr ref count associated to it. + */ +int tracing_open_file_tr(struct inode *inode, struct file *filp) +{ + struct trace_event_file *file = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(file->tr); + if (ret) + return ret; + + filp->private_data = inode->i_private; + + return 0; +} + +int tracing_release_file_tr(struct inode *inode, struct file *filp) +{ + struct trace_event_file *file = inode->i_private; + + trace_array_put(file->tr); + + return 0; +} + static int tracing_mark_open(struct inode *inode, struct file *filp) { stream_open(inode, filp); diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index 5669dd1f90d9..77debe53f07c 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -610,6 +610,8 @@ void tracing_reset_all_online_cpus(void); void tracing_reset_all_online_cpus_unlocked(void); int tracing_open_generic(struct inode *inode, struct file *filp); int tracing_open_generic_tr(struct inode *inode, struct file *filp); +int tracing_open_file_tr(struct inode *inode, struct file *filp); +int tracing_release_file_tr(struct inode *inode, struct file *filp); bool tracing_is_disabled(void); bool tracer_tracing_is_on(struct trace_array *tr); void tracer_tracing_on(struct trace_array *tr); diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index ed367d713be0..2af92177b765 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2103,9 +2103,10 @@ static const struct file_operations ftrace_set_event_notrace_pid_fops = { }; static const struct file_operations ftrace_enable_fops = { - .open = tracing_open_generic, + .open = tracing_open_file_tr, .read = event_enable_read, .write = event_enable_write, + .release = tracing_release_file_tr, .llseek = default_llseek, }; @@ -2122,9 +2123,10 @@ static const struct file_operations ftrace_event_id_fops = { }; static const struct file_operations ftrace_event_filter_fops = { - .open = tracing_open_generic, + .open = tracing_open_file_tr, .read = event_filter_read, .write = event_filter_write, + .release = tracing_release_file_tr, .llseek = default_llseek, }; -- cgit v1.2.3 From 7d660c9b2bc95107f90a9f4c4759be85309a6550 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 6 Sep 2023 22:47:13 -0400 Subject: tracing: Have tracing_max_latency inc the trace array ref count The tracing_max_latency file points to the trace_array max_latency field. For an instance, if the file is opened and the instance is deleted, reading or writing to the file will cause a use after free. Up the ref count of the trace_array when tracing_max_latency is opened. Link: https://lkml.kernel.org/r/20230907024803.666889383@goodmis.org Link: https://lore.kernel.org/all/1cb3aee2-19af-c472-e265-05176fe9bd84@huawei.com/ Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Zheng Yejian Fixes: 8530dec63e7b4 ("tracing: Add tracing_check_open_get_tr()") Tested-by: Linux Kernel Functional Testing Tested-by: Naresh Kamboju Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0827037ee3b8..c8b8b4c6feaf 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -1772,7 +1772,7 @@ static void trace_create_maxlat_file(struct trace_array *tr, init_irq_work(&tr->fsnotify_irqwork, latency_fsnotify_workfn_irq); tr->d_max_latency = trace_create_file("tracing_max_latency", TRACE_MODE_WRITE, - d_tracer, &tr->max_latency, + d_tracer, tr, &tracing_max_lat_fops); } @@ -1805,7 +1805,7 @@ void latency_fsnotify(struct trace_array *tr) #define trace_create_maxlat_file(tr, d_tracer) \ trace_create_file("tracing_max_latency", TRACE_MODE_WRITE, \ - d_tracer, &tr->max_latency, &tracing_max_lat_fops) + d_tracer, tr, &tracing_max_lat_fops) #endif @@ -6717,14 +6717,18 @@ static ssize_t tracing_max_lat_read(struct file *filp, char __user *ubuf, size_t cnt, loff_t *ppos) { - return tracing_nsecs_read(filp->private_data, ubuf, cnt, ppos); + struct trace_array *tr = filp->private_data; + + return tracing_nsecs_read(&tr->max_latency, ubuf, cnt, ppos); } static ssize_t tracing_max_lat_write(struct file *filp, const char __user *ubuf, size_t cnt, loff_t *ppos) { - return tracing_nsecs_write(filp->private_data, ubuf, cnt, ppos); + struct trace_array *tr = filp->private_data; + + return tracing_nsecs_write(&tr->max_latency, ubuf, cnt, ppos); } #endif @@ -7778,10 +7782,11 @@ static const struct file_operations tracing_thresh_fops = { #ifdef CONFIG_TRACER_MAX_TRACE static const struct file_operations tracing_max_lat_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .read = tracing_max_lat_read, .write = tracing_max_lat_write, .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, }; #endif -- cgit v1.2.3 From 9b37febc578b2e1ad76a105aab11d00af5ec3d27 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 6 Sep 2023 22:47:14 -0400 Subject: tracing: Have current_trace inc the trace array ref count The current_trace updates the trace array tracer. For an instance, if the file is opened and the instance is deleted, reading or writing to the file will cause a use after free. Up the ref count of the trace array when current_trace is opened. Link: https://lkml.kernel.org/r/20230907024803.877687227@goodmis.org Link: https://lore.kernel.org/all/1cb3aee2-19af-c472-e265-05176fe9bd84@huawei.com/ Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Zheng Yejian Fixes: 8530dec63e7b4 ("tracing: Add tracing_check_open_get_tr()") Tested-by: Linux Kernel Functional Testing Tested-by: Naresh Kamboju Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index c8b8b4c6feaf..b82df33d20ff 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -7791,10 +7791,11 @@ static const struct file_operations tracing_max_lat_fops = { #endif static const struct file_operations set_tracer_fops = { - .open = tracing_open_generic, + .open = tracing_open_generic_tr, .read = tracing_set_trace_read, .write = tracing_set_trace_write, .llseek = generic_file_llseek, + .release = tracing_release_generic_tr, }; static const struct file_operations tracing_pipe_fops = { -- cgit v1.2.3 From 7e2cfbd2d3c86afcd5c26b5c4b1dd251f63c5838 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 6 Sep 2023 22:47:15 -0400 Subject: tracing: Have option files inc the trace array ref count The option files update the options for a given trace array. For an instance, if the file is opened and the instance is deleted, reading or writing to the file will cause a use after free. Up the ref count of the trace_array when an option file is opened. Link: https://lkml.kernel.org/r/20230907024804.086679464@goodmis.org Link: https://lore.kernel.org/all/1cb3aee2-19af-c472-e265-05176fe9bd84@huawei.com/ Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Zheng Yejian Fixes: 8530dec63e7b4 ("tracing: Add tracing_check_open_get_tr()") Tested-by: Linux Kernel Functional Testing Tested-by: Naresh Kamboju Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index b82df33d20ff..0608ad20cf30 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -8988,12 +8988,33 @@ trace_options_write(struct file *filp, const char __user *ubuf, size_t cnt, return cnt; } +static int tracing_open_options(struct inode *inode, struct file *filp) +{ + struct trace_option_dentry *topt = inode->i_private; + int ret; + + ret = tracing_check_open_get_tr(topt->tr); + if (ret) + return ret; + + filp->private_data = inode->i_private; + return 0; +} + +static int tracing_release_options(struct inode *inode, struct file *file) +{ + struct trace_option_dentry *topt = file->private_data; + + trace_array_put(topt->tr); + return 0; +} static const struct file_operations trace_options_fops = { - .open = tracing_open_generic, + .open = tracing_open_options, .read = trace_options_read, .write = trace_options_write, .llseek = generic_file_llseek, + .release = tracing_release_options, }; /* -- cgit v1.2.3 From e5c624f027ac74f97e97c8f36c69228ac9f1102d Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Wed, 6 Sep 2023 22:47:16 -0400 Subject: tracing: Have event inject files inc the trace array ref count The event inject files add events for a specific trace array. For an instance, if the file is opened and the instance is deleted, reading or writing to the file will cause a use after free. Up the ref count of the trace_array when a event inject file is opened. Link: https://lkml.kernel.org/r/20230907024804.292337868@goodmis.org Link: https://lore.kernel.org/all/1cb3aee2-19af-c472-e265-05176fe9bd84@huawei.com/ Cc: stable@vger.kernel.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Zheng Yejian Fixes: 6c3edaf9fd6a ("tracing: Introduce trace event injection") Tested-by: Linux Kernel Functional Testing Tested-by: Naresh Kamboju Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_events_inject.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/trace/trace_events_inject.c b/kernel/trace/trace_events_inject.c index abe805d471eb..8650562bdaa9 100644 --- a/kernel/trace/trace_events_inject.c +++ b/kernel/trace/trace_events_inject.c @@ -328,7 +328,8 @@ event_inject_read(struct file *file, char __user *buf, size_t size, } const struct file_operations event_inject_fops = { - .open = tracing_open_generic, + .open = tracing_open_file_tr, .read = event_inject_read, .write = event_inject_write, + .release = tracing_release_file_tr, }; -- cgit v1.2.3 From f6bd2c92488c30ef53b5bd80c52f0a7eee9d545a Mon Sep 17 00:00:00 2001 From: Zheng Yejian Date: Wed, 6 Sep 2023 16:19:30 +0800 Subject: ring-buffer: Avoid softlockup in ring_buffer_resize() When user resize all trace ring buffer through file 'buffer_size_kb', then in ring_buffer_resize(), kernel allocates buffer pages for each cpu in a loop. If the kernel preemption model is PREEMPT_NONE and there are many cpus and there are many buffer pages to be allocated, it may not give up cpu for a long time and finally cause a softlockup. To avoid it, call cond_resched() after each cpu buffer allocation. Link: https://lore.kernel.org/linux-trace-kernel/20230906081930.3939106-1-zhengyejian1@huawei.com Cc: Signed-off-by: Zheng Yejian Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 78502d4c7214..72ccf75defd0 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2198,6 +2198,8 @@ int ring_buffer_resize(struct trace_buffer *buffer, unsigned long size, err = -ENOMEM; goto out_err; } + + cond_resched(); } cpus_read_lock(); -- cgit v1.2.3 From ac28b1ec6135649b5d78b028e47264cb3ebca5ea Mon Sep 17 00:00:00 2001 From: Liu Jian Date: Thu, 7 Sep 2023 10:57:09 +0800 Subject: net: ipv4: fix one memleak in __inet_del_ifa() I got the below warning when do fuzzing test: unregister_netdevice: waiting for bond0 to become free. Usage count = 2 It can be repoduced via: ip link add bond0 type bond sysctl -w net.ipv4.conf.bond0.promote_secondaries=1 ip addr add 4.117.174.103/0 scope 0x40 dev bond0 ip addr add 192.168.100.111/255.255.255.254 scope 0 dev bond0 ip addr add 0.0.0.4/0 scope 0x40 secondary dev bond0 ip addr del 4.117.174.103/0 scope 0x40 dev bond0 ip link delete bond0 type bond In this reproduction test case, an incorrect 'last_prim' is found in __inet_del_ifa(), as a result, the secondary address(0.0.0.4/0 scope 0x40) is lost. The memory of the secondary address is leaked and the reference of in_device and net_device is leaked. Fix this problem: Look for 'last_prim' starting at location of the deleted IP and inserting the promoted IP into the location of 'last_prim'. Fixes: 0ff60a45678e ("[IPV4]: Fix secondary IP addresses after promotion") Signed-off-by: Liu Jian Signed-off-by: Julian Anastasov Signed-off-by: David S. Miller --- net/ipv4/devinet.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 9cf64ee47dd2..ca0ff15dc8fa 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -355,14 +355,14 @@ static void __inet_del_ifa(struct in_device *in_dev, { struct in_ifaddr *promote = NULL; struct in_ifaddr *ifa, *ifa1; - struct in_ifaddr *last_prim; + struct in_ifaddr __rcu **last_prim; struct in_ifaddr *prev_prom = NULL; int do_promote = IN_DEV_PROMOTE_SECONDARIES(in_dev); ASSERT_RTNL(); ifa1 = rtnl_dereference(*ifap); - last_prim = rtnl_dereference(in_dev->ifa_list); + last_prim = ifap; if (in_dev->dead) goto no_promotions; @@ -376,7 +376,7 @@ static void __inet_del_ifa(struct in_device *in_dev, while ((ifa = rtnl_dereference(*ifap1)) != NULL) { if (!(ifa->ifa_flags & IFA_F_SECONDARY) && ifa1->ifa_scope <= ifa->ifa_scope) - last_prim = ifa; + last_prim = &ifa->ifa_next; if (!(ifa->ifa_flags & IFA_F_SECONDARY) || ifa1->ifa_mask != ifa->ifa_mask || @@ -440,9 +440,9 @@ no_promotions: rcu_assign_pointer(prev_prom->ifa_next, next_sec); - last_sec = rtnl_dereference(last_prim->ifa_next); + last_sec = rtnl_dereference(*last_prim); rcu_assign_pointer(promote->ifa_next, last_sec); - rcu_assign_pointer(last_prim->ifa_next, promote); + rcu_assign_pointer(*last_prim, promote); } promote->ifa_flags &= ~IFA_F_SECONDARY; -- cgit v1.2.3 From 2d6cd791e63ec0c68ae95ecd55dc6c50ac7829cf Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 4 Sep 2023 12:10:31 +0100 Subject: btrfs: fix race between finishing block group creation and its item update Commit 675dfe1223a6 ("btrfs: fix block group item corruption after inserting new block group") fixed one race that resulted in not persisting a block group's item when its "used" bytes field decreases to zero. However there's another race that can happen in a much shorter time window that results in the same problem. The following sequence of steps explains how it can happen: 1) Task A creates a metadata block group X, its "used" and "commit_used" fields are initialized to 0; 2) Two extents are allocated from block group X, so its "used" field is updated to 32K, and its "commit_used" field remains as 0; 3) Transaction commit starts, by some task B, and it enters btrfs_start_dirty_block_groups(). There it tries to update the block group item for block group X, which currently has its "used" field with a value of 32K and its "commit_used" field with a value of 0. However that fails since the block group item was not yet inserted, so at update_block_group_item(), the btrfs_search_slot() call returns 1, and then we set 'ret' to -ENOENT. Before jumping to the label 'fail'... 4) The block group item is inserted by task A, when for example btrfs_create_pending_block_groups() is called when releasing its transaction handle. This results in insert_block_group_item() inserting the block group item in the extent tree (or block group tree), with a "used" field having a value of 32K and setting "commit_used", in struct btrfs_block_group, to the same value (32K); 5) Task B jumps to the 'fail' label and then resets the "commit_used" field to 0. At btrfs_start_dirty_block_groups(), because -ENOENT was returned from update_block_group_item(), we add the block group again to the list of dirty block groups, so that we will try again in the critical section of the transaction commit when calling btrfs_write_dirty_block_groups(); 6) Later the two extents from block group X are freed, so its "used" field becomes 0; 7) If no more extents are allocated from block group X before we get into btrfs_write_dirty_block_groups(), then when we call update_block_group_item() again for block group X, we will not update the block group item to reflect that it has 0 bytes used, because the "used" and "commit_used" fields in struct btrfs_block_group have the same value, a value of 0. As a result after committing the transaction we have an empty block group with its block group item having a 32K value for its "used" field. This will trigger errors from fsck ("btrfs check" command) and after mounting again the fs, the cleaner kthread will not automatically delete the empty block group, since its "used" field is not 0. Possibly there are other issues due to this inconsistency. When this issue happens, the error reported by fsck is like this: [1/7] checking root items [2/7] checking extents block group [1104150528 1073741824] used 39796736 but extent items used 0 ERROR: errors found in extent allocation tree or chunk allocation (...) So fix this by not resetting the "commit_used" field of a block group when we don't find the block group item at update_block_group_item(). Fixes: 7248e0cebbef ("btrfs: skip update of block group item if used bytes are the same") CC: stable@vger.kernel.org # 6.2+ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/block-group.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/btrfs/block-group.c b/fs/btrfs/block-group.c index 0cb1dee965a0..b2e5107b7cec 100644 --- a/fs/btrfs/block-group.c +++ b/fs/btrfs/block-group.c @@ -3028,8 +3028,16 @@ static int update_block_group_item(struct btrfs_trans_handle *trans, btrfs_mark_buffer_dirty(leaf); fail: btrfs_release_path(path); - /* We didn't update the block group item, need to revert @commit_used. */ - if (ret < 0) { + /* + * We didn't update the block group item, need to revert commit_used + * unless the block group item didn't exist yet - this is to prevent a + * race with a concurrent insertion of the block group item, with + * insert_block_group_item(), that happened just after we attempted to + * update. In that case we would reset commit_used to 0 just after the + * insertion set it to a value greater than 0 - if the block group later + * becomes with 0 used bytes, we would incorrectly skip its update. + */ + if (ret < 0 && ret != -ENOENT) { spin_lock(&cache->lock); cache->commit_used = old_commit_used; spin_unlock(&cache->lock); -- cgit v1.2.3 From ee34a82e890a7babb5585daf1a6dd7d4d1cf142a Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Sat, 26 Aug 2023 11:28:20 +0100 Subject: btrfs: release path before inode lookup during the ino lookup ioctl During the ino lookup ioctl we can end up calling btrfs_iget() to get an inode reference while we are holding on a root's btree. If btrfs_iget() needs to lookup the inode from the root's btree, because it's not currently loaded in memory, then it will need to lock another or the same path in the same root btree. This may result in a deadlock and trigger the following lockdep splat: WARNING: possible circular locking dependency detected 6.5.0-rc7-syzkaller-00004-gf7757129e3de #0 Not tainted ------------------------------------------------------ syz-executor277/5012 is trying to acquire lock: ffff88802df41710 (btrfs-tree-01){++++}-{3:3}, at: __btrfs_tree_read_lock+0x2f/0x220 fs/btrfs/locking.c:136 but task is already holding lock: ffff88802df418e8 (btrfs-tree-00){++++}-{3:3}, at: __btrfs_tree_read_lock+0x2f/0x220 fs/btrfs/locking.c:136 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (btrfs-tree-00){++++}-{3:3}: down_read_nested+0x49/0x2f0 kernel/locking/rwsem.c:1645 __btrfs_tree_read_lock+0x2f/0x220 fs/btrfs/locking.c:136 btrfs_search_slot+0x13a4/0x2f80 fs/btrfs/ctree.c:2302 btrfs_init_root_free_objectid+0x148/0x320 fs/btrfs/disk-io.c:4955 btrfs_init_fs_root fs/btrfs/disk-io.c:1128 [inline] btrfs_get_root_ref+0x5ae/0xae0 fs/btrfs/disk-io.c:1338 btrfs_get_fs_root fs/btrfs/disk-io.c:1390 [inline] open_ctree+0x29c8/0x3030 fs/btrfs/disk-io.c:3494 btrfs_fill_super+0x1c7/0x2f0 fs/btrfs/super.c:1154 btrfs_mount_root+0x7e0/0x910 fs/btrfs/super.c:1519 legacy_get_tree+0xef/0x190 fs/fs_context.c:611 vfs_get_tree+0x8c/0x270 fs/super.c:1519 fc_mount fs/namespace.c:1112 [inline] vfs_kern_mount+0xbc/0x150 fs/namespace.c:1142 btrfs_mount+0x39f/0xb50 fs/btrfs/super.c:1579 legacy_get_tree+0xef/0x190 fs/fs_context.c:611 vfs_get_tree+0x8c/0x270 fs/super.c:1519 do_new_mount+0x28f/0xae0 fs/namespace.c:3335 do_mount fs/namespace.c:3675 [inline] __do_sys_mount fs/namespace.c:3884 [inline] __se_sys_mount+0x2d9/0x3c0 fs/namespace.c:3861 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd -> #0 (btrfs-tree-01){++++}-{3:3}: check_prev_add kernel/locking/lockdep.c:3142 [inline] check_prevs_add kernel/locking/lockdep.c:3261 [inline] validate_chain kernel/locking/lockdep.c:3876 [inline] __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144 lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761 down_read_nested+0x49/0x2f0 kernel/locking/rwsem.c:1645 __btrfs_tree_read_lock+0x2f/0x220 fs/btrfs/locking.c:136 btrfs_tree_read_lock fs/btrfs/locking.c:142 [inline] btrfs_read_lock_root_node+0x292/0x3c0 fs/btrfs/locking.c:281 btrfs_search_slot_get_root fs/btrfs/ctree.c:1832 [inline] btrfs_search_slot+0x4ff/0x2f80 fs/btrfs/ctree.c:2154 btrfs_lookup_inode+0xdc/0x480 fs/btrfs/inode-item.c:412 btrfs_read_locked_inode fs/btrfs/inode.c:3892 [inline] btrfs_iget_path+0x2d9/0x1520 fs/btrfs/inode.c:5716 btrfs_search_path_in_tree_user fs/btrfs/ioctl.c:1961 [inline] btrfs_ioctl_ino_lookup_user+0x77a/0xf50 fs/btrfs/ioctl.c:2105 btrfs_ioctl+0xb0b/0xd40 fs/btrfs/ioctl.c:4683 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:870 [inline] __se_sys_ioctl+0xf8/0x170 fs/ioctl.c:856 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- rlock(btrfs-tree-00); lock(btrfs-tree-01); lock(btrfs-tree-00); rlock(btrfs-tree-01); *** DEADLOCK *** 1 lock held by syz-executor277/5012: #0: ffff88802df418e8 (btrfs-tree-00){++++}-{3:3}, at: __btrfs_tree_read_lock+0x2f/0x220 fs/btrfs/locking.c:136 stack backtrace: CPU: 1 PID: 5012 Comm: syz-executor277 Not tainted 6.5.0-rc7-syzkaller-00004-gf7757129e3de #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/26/2023 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x1e7/0x2d0 lib/dump_stack.c:106 check_noncircular+0x375/0x4a0 kernel/locking/lockdep.c:2195 check_prev_add kernel/locking/lockdep.c:3142 [inline] check_prevs_add kernel/locking/lockdep.c:3261 [inline] validate_chain kernel/locking/lockdep.c:3876 [inline] __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144 lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761 down_read_nested+0x49/0x2f0 kernel/locking/rwsem.c:1645 __btrfs_tree_read_lock+0x2f/0x220 fs/btrfs/locking.c:136 btrfs_tree_read_lock fs/btrfs/locking.c:142 [inline] btrfs_read_lock_root_node+0x292/0x3c0 fs/btrfs/locking.c:281 btrfs_search_slot_get_root fs/btrfs/ctree.c:1832 [inline] btrfs_search_slot+0x4ff/0x2f80 fs/btrfs/ctree.c:2154 btrfs_lookup_inode+0xdc/0x480 fs/btrfs/inode-item.c:412 btrfs_read_locked_inode fs/btrfs/inode.c:3892 [inline] btrfs_iget_path+0x2d9/0x1520 fs/btrfs/inode.c:5716 btrfs_search_path_in_tree_user fs/btrfs/ioctl.c:1961 [inline] btrfs_ioctl_ino_lookup_user+0x77a/0xf50 fs/btrfs/ioctl.c:2105 btrfs_ioctl+0xb0b/0xd40 fs/btrfs/ioctl.c:4683 vfs_ioctl fs/ioctl.c:51 [inline] __do_sys_ioctl fs/ioctl.c:870 [inline] __se_sys_ioctl+0xf8/0x170 fs/ioctl.c:856 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f0bec94ea39 Fix this simply by releasing the path before calling btrfs_iget() as at point we don't need the path anymore. Reported-by: syzbot+bf66ad948981797d2f1d@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/00000000000045fa140603c4a969@google.com/ Fixes: 23d0b79dfaed ("btrfs: Add unprivileged version of ino_lookup ioctl") CC: stable@vger.kernel.org # 4.19+ Reviewed-by: Josef Bacik Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ioctl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index a895d105464b..d27b0d86b8e2 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c @@ -1958,6 +1958,13 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, goto out_put; } + /* + * We don't need the path anymore, so release it and + * avoid deadlocks and lockdep warnings in case + * btrfs_iget() needs to lookup the inode from its root + * btree and lock the same leaf. + */ + btrfs_release_path(path); temp_inode = btrfs_iget(sb, key2.objectid, root); if (IS_ERR(temp_inode)) { ret = PTR_ERR(temp_inode); @@ -1978,7 +1985,6 @@ static int btrfs_search_path_in_tree_user(struct mnt_idmap *idmap, goto out_put; } - btrfs_release_path(path); key.objectid = key.offset; key.offset = (u64)-1; dirid = key.objectid; -- cgit v1.2.3 From 77d20c685b6baeb942606a93ed861c191381b73e Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 24 Aug 2023 16:59:22 -0400 Subject: btrfs: do not block starts waiting on previous transaction commit Internally I got a report of very long stalls on normal operations like creating a new file when auto relocation was running. The reporter used the 'bpf offcputime' tracer to show that we would get stuck in start_transaction for 5 to 30 seconds, and were always being woken up by the transaction commit. Using my timing-everything script, which times how long a function takes and what percentage of that total time is taken up by its children, I saw several traces like this 1083 took 32812902424 ns 29929002926 ns 91.2110% wait_for_commit_duration 25568 ns 7.7920e-05% commit_fs_roots_duration 1007751 ns 0.00307% commit_cowonly_roots_duration 446855602 ns 1.36182% btrfs_run_delayed_refs_duration 271980 ns 0.00082% btrfs_run_delayed_items_duration 2008 ns 6.1195e-06% btrfs_apply_pending_changes_duration 9656 ns 2.9427e-05% switch_commit_roots_duration 1598 ns 4.8700e-06% btrfs_commit_device_sizes_duration 4314 ns 1.3147e-05% btrfs_free_log_root_tree_duration Here I was only tracing functions that happen where we are between START_COMMIT and UNBLOCKED in order to see what would be keeping us blocked for so long. The wait_for_commit() we do is where we wait for a previous transaction that hasn't completed it's commit. This can include all of the unpin work and other cleanups, which tends to be the longest part of our transaction commit. There is no reason we should be blocking new things from entering the transaction at this point, it just adds to random latency spikes for no reason. Fix this by adding a PREP stage. This allows us to properly deal with multiple committers coming in at the same time, we retain the behavior that the winner waits on the previous transaction and the losers all wait for this transaction commit to occur. Nothing else is blocked during the PREP stage, and then once the wait is complete we switch to COMMIT_START and all of the same behavior as before is maintained. Reviewed-by: Filipe Manana Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 8 ++++---- fs/btrfs/locking.h | 2 +- fs/btrfs/transaction.c | 39 ++++++++++++++++++++++++--------------- fs/btrfs/transaction.h | 1 + 4 files changed, 30 insertions(+), 20 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 0a96ea8c1d3a..639f1e308e4c 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -1547,7 +1547,7 @@ static int transaction_kthread(void *arg) delta = ktime_get_seconds() - cur->start_time; if (!test_and_clear_bit(BTRFS_FS_COMMIT_TRANS, &fs_info->flags) && - cur->state < TRANS_STATE_COMMIT_START && + cur->state < TRANS_STATE_COMMIT_PREP && delta < fs_info->commit_interval) { spin_unlock(&fs_info->trans_lock); delay -= msecs_to_jiffies((delta - 1) * 1000); @@ -2682,8 +2682,8 @@ void btrfs_init_fs_info(struct btrfs_fs_info *fs_info) btrfs_lockdep_init_map(fs_info, btrfs_trans_num_extwriters); btrfs_lockdep_init_map(fs_info, btrfs_trans_pending_ordered); btrfs_lockdep_init_map(fs_info, btrfs_ordered_extent); - btrfs_state_lockdep_init_map(fs_info, btrfs_trans_commit_start, - BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_state_lockdep_init_map(fs_info, btrfs_trans_commit_prep, + BTRFS_LOCKDEP_TRANS_COMMIT_PREP); btrfs_state_lockdep_init_map(fs_info, btrfs_trans_unblocked, BTRFS_LOCKDEP_TRANS_UNBLOCKED); btrfs_state_lockdep_init_map(fs_info, btrfs_trans_super_committed, @@ -4870,7 +4870,7 @@ static int btrfs_cleanup_transaction(struct btrfs_fs_info *fs_info) while (!list_empty(&fs_info->trans_list)) { t = list_first_entry(&fs_info->trans_list, struct btrfs_transaction, list); - if (t->state >= TRANS_STATE_COMMIT_START) { + if (t->state >= TRANS_STATE_COMMIT_PREP) { refcount_inc(&t->use_count); spin_unlock(&fs_info->trans_lock); btrfs_wait_for_commit(fs_info, t->transid); diff --git a/fs/btrfs/locking.h b/fs/btrfs/locking.h index edb9b4a0dba1..7d6ee1e609bf 100644 --- a/fs/btrfs/locking.h +++ b/fs/btrfs/locking.h @@ -79,7 +79,7 @@ enum btrfs_lock_nesting { }; enum btrfs_lockdep_trans_states { - BTRFS_LOCKDEP_TRANS_COMMIT_START, + BTRFS_LOCKDEP_TRANS_COMMIT_PREP, BTRFS_LOCKDEP_TRANS_UNBLOCKED, BTRFS_LOCKDEP_TRANS_SUPER_COMMITTED, BTRFS_LOCKDEP_TRANS_COMPLETED, diff --git a/fs/btrfs/transaction.c b/fs/btrfs/transaction.c index ab09542f2170..341363beaf10 100644 --- a/fs/btrfs/transaction.c +++ b/fs/btrfs/transaction.c @@ -56,12 +56,17 @@ static struct kmem_cache *btrfs_trans_handle_cachep; * | Call btrfs_commit_transaction() on any trans handle attached to * | transaction N * V - * Transaction N [[TRANS_STATE_COMMIT_START]] + * Transaction N [[TRANS_STATE_COMMIT_PREP]] + * | + * | If there are simultaneous calls to btrfs_commit_transaction() one will win + * | the race and the rest will wait for the winner to commit the transaction. + * | + * | The winner will wait for previous running transaction to completely finish + * | if there is one. * | - * | Will wait for previous running transaction to completely finish if there - * | is one + * Transaction N [[TRANS_STATE_COMMIT_START]] * | - * | Then one of the following happes: + * | Then one of the following happens: * | - Wait for all other trans handle holders to release. * | The btrfs_commit_transaction() caller will do the commit work. * | - Wait for current transaction to be committed by others. @@ -112,6 +117,7 @@ static struct kmem_cache *btrfs_trans_handle_cachep; */ static const unsigned int btrfs_blocked_trans_types[TRANS_STATE_MAX] = { [TRANS_STATE_RUNNING] = 0U, + [TRANS_STATE_COMMIT_PREP] = 0U, [TRANS_STATE_COMMIT_START] = (__TRANS_START | __TRANS_ATTACH), [TRANS_STATE_COMMIT_DOING] = (__TRANS_START | __TRANS_ATTACH | @@ -1983,7 +1989,7 @@ void btrfs_commit_transaction_async(struct btrfs_trans_handle *trans) * Wait for the current transaction commit to start and block * subsequent transaction joins */ - btrfs_might_wait_for_state(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_might_wait_for_state(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); wait_event(fs_info->transaction_blocked_wait, cur_trans->state >= TRANS_STATE_COMMIT_START || TRANS_ABORTED(cur_trans)); @@ -2130,7 +2136,7 @@ static void add_pending_snapshot(struct btrfs_trans_handle *trans) return; lockdep_assert_held(&trans->fs_info->trans_lock); - ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_START); + ASSERT(cur_trans->state >= TRANS_STATE_COMMIT_PREP); list_add(&trans->pending_snapshot->list, &cur_trans->pending_snapshots); } @@ -2154,7 +2160,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) ktime_t interval; ASSERT(refcount_read(&trans->use_count) == 1); - btrfs_trans_state_lockdep_acquire(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_trans_state_lockdep_acquire(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); clear_bit(BTRFS_FS_NEED_TRANS_COMMIT, &fs_info->flags); @@ -2214,7 +2220,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) } spin_lock(&fs_info->trans_lock); - if (cur_trans->state >= TRANS_STATE_COMMIT_START) { + if (cur_trans->state >= TRANS_STATE_COMMIT_PREP) { enum btrfs_trans_state want_state = TRANS_STATE_COMPLETED; add_pending_snapshot(trans); @@ -2226,7 +2232,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) want_state = TRANS_STATE_SUPER_COMMITTED; btrfs_trans_state_lockdep_release(fs_info, - BTRFS_LOCKDEP_TRANS_COMMIT_START); + BTRFS_LOCKDEP_TRANS_COMMIT_PREP); ret = btrfs_end_transaction(trans); wait_for_commit(cur_trans, want_state); @@ -2238,9 +2244,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) return ret; } - cur_trans->state = TRANS_STATE_COMMIT_START; + cur_trans->state = TRANS_STATE_COMMIT_PREP; wake_up(&fs_info->transaction_blocked_wait); - btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); if (cur_trans->list.prev != &fs_info->trans_list) { enum btrfs_trans_state want_state = TRANS_STATE_COMPLETED; @@ -2261,11 +2267,9 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) btrfs_put_transaction(prev_trans); if (ret) goto lockdep_release; - } else { - spin_unlock(&fs_info->trans_lock); + spin_lock(&fs_info->trans_lock); } } else { - spin_unlock(&fs_info->trans_lock); /* * The previous transaction was aborted and was already removed * from the list of transactions at fs_info->trans_list. So we @@ -2273,11 +2277,16 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans) * corrupt state (pointing to trees with unwritten nodes/leafs). */ if (BTRFS_FS_ERROR(fs_info)) { + spin_unlock(&fs_info->trans_lock); ret = -EROFS; goto lockdep_release; } } + cur_trans->state = TRANS_STATE_COMMIT_START; + wake_up(&fs_info->transaction_blocked_wait); + spin_unlock(&fs_info->trans_lock); + /* * Get the time spent on the work done by the commit thread and not * the time spent waiting on a previous commit @@ -2587,7 +2596,7 @@ lockdep_release: goto cleanup_transaction; lockdep_trans_commit_start_release: - btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_START); + btrfs_trans_state_lockdep_release(fs_info, BTRFS_LOCKDEP_TRANS_COMMIT_PREP); btrfs_end_transaction(trans); return ret; } diff --git a/fs/btrfs/transaction.h b/fs/btrfs/transaction.h index 8e9fa23bd7fe..6b309f8a99a8 100644 --- a/fs/btrfs/transaction.h +++ b/fs/btrfs/transaction.h @@ -14,6 +14,7 @@ enum btrfs_trans_state { TRANS_STATE_RUNNING, + TRANS_STATE_COMMIT_PREP, TRANS_STATE_COMMIT_START, TRANS_STATE_COMMIT_DOING, TRANS_STATE_UNBLOCKED, -- cgit v1.2.3 From e110f8911ddb93e6f55da14ccbbe705397b30d0b Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Tue, 29 Aug 2023 11:34:52 +0100 Subject: btrfs: fix lockdep splat and potential deadlock after failure running delayed items When running delayed items we are holding a delayed node's mutex and then we will attempt to modify a subvolume btree to insert/update/delete the delayed items. However if have an error during the insertions for example, btrfs_insert_delayed_items() may return with a path that has locked extent buffers (a leaf at the very least), and then we attempt to release the delayed node at __btrfs_run_delayed_items(), which requires taking the delayed node's mutex, causing an ABBA type of deadlock. This was reported by syzbot and the lockdep splat is the following: WARNING: possible circular locking dependency detected 6.5.0-rc7-syzkaller-00024-g93f5de5f648d #0 Not tainted ------------------------------------------------------ syz-executor.2/13257 is trying to acquire lock: ffff88801835c0c0 (&delayed_node->mutex){+.+.}-{3:3}, at: __btrfs_release_delayed_node+0x9a/0xaa0 fs/btrfs/delayed-inode.c:256 but task is already holding lock: ffff88802a5ab8e8 (btrfs-tree-00){++++}-{3:3}, at: __btrfs_tree_lock+0x3c/0x2a0 fs/btrfs/locking.c:198 which lock already depends on the new lock. the existing dependency chain (in reverse order) is: -> #1 (btrfs-tree-00){++++}-{3:3}: __lock_release kernel/locking/lockdep.c:5475 [inline] lock_release+0x36f/0x9d0 kernel/locking/lockdep.c:5781 up_write+0x79/0x580 kernel/locking/rwsem.c:1625 btrfs_tree_unlock_rw fs/btrfs/locking.h:189 [inline] btrfs_unlock_up_safe+0x179/0x3b0 fs/btrfs/locking.c:239 search_leaf fs/btrfs/ctree.c:1986 [inline] btrfs_search_slot+0x2511/0x2f80 fs/btrfs/ctree.c:2230 btrfs_insert_empty_items+0x9c/0x180 fs/btrfs/ctree.c:4376 btrfs_insert_delayed_item fs/btrfs/delayed-inode.c:746 [inline] btrfs_insert_delayed_items fs/btrfs/delayed-inode.c:824 [inline] __btrfs_commit_inode_delayed_items+0xd24/0x2410 fs/btrfs/delayed-inode.c:1111 __btrfs_run_delayed_items+0x1db/0x430 fs/btrfs/delayed-inode.c:1153 flush_space+0x269/0xe70 fs/btrfs/space-info.c:723 btrfs_async_reclaim_metadata_space+0x106/0x350 fs/btrfs/space-info.c:1078 process_one_work+0x92c/0x12c0 kernel/workqueue.c:2600 worker_thread+0xa63/0x1210 kernel/workqueue.c:2751 kthread+0x2b8/0x350 kernel/kthread.c:389 ret_from_fork+0x2e/0x60 arch/x86/kernel/process.c:145 ret_from_fork_asm+0x11/0x20 arch/x86/entry/entry_64.S:304 -> #0 (&delayed_node->mutex){+.+.}-{3:3}: check_prev_add kernel/locking/lockdep.c:3142 [inline] check_prevs_add kernel/locking/lockdep.c:3261 [inline] validate_chain kernel/locking/lockdep.c:3876 [inline] __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144 lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761 __mutex_lock_common+0x1d8/0x2530 kernel/locking/mutex.c:603 __mutex_lock kernel/locking/mutex.c:747 [inline] mutex_lock_nested+0x1b/0x20 kernel/locking/mutex.c:799 __btrfs_release_delayed_node+0x9a/0xaa0 fs/btrfs/delayed-inode.c:256 btrfs_release_delayed_node fs/btrfs/delayed-inode.c:281 [inline] __btrfs_run_delayed_items+0x2b5/0x430 fs/btrfs/delayed-inode.c:1156 btrfs_commit_transaction+0x859/0x2ff0 fs/btrfs/transaction.c:2276 btrfs_sync_file+0xf56/0x1330 fs/btrfs/file.c:1988 vfs_fsync_range fs/sync.c:188 [inline] vfs_fsync fs/sync.c:202 [inline] do_fsync fs/sync.c:212 [inline] __do_sys_fsync fs/sync.c:220 [inline] __se_sys_fsync fs/sync.c:218 [inline] __x64_sys_fsync+0x196/0x1e0 fs/sync.c:218 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd other info that might help us debug this: Possible unsafe locking scenario: CPU0 CPU1 ---- ---- lock(btrfs-tree-00); lock(&delayed_node->mutex); lock(btrfs-tree-00); lock(&delayed_node->mutex); *** DEADLOCK *** 3 locks held by syz-executor.2/13257: #0: ffff88802c1ee370 (btrfs_trans_num_writers){++++}-{0:0}, at: spin_unlock include/linux/spinlock.h:391 [inline] #0: ffff88802c1ee370 (btrfs_trans_num_writers){++++}-{0:0}, at: join_transaction+0xb87/0xe00 fs/btrfs/transaction.c:287 #1: ffff88802c1ee398 (btrfs_trans_num_extwriters){++++}-{0:0}, at: join_transaction+0xbb2/0xe00 fs/btrfs/transaction.c:288 #2: ffff88802a5ab8e8 (btrfs-tree-00){++++}-{3:3}, at: __btrfs_tree_lock+0x3c/0x2a0 fs/btrfs/locking.c:198 stack backtrace: CPU: 0 PID: 13257 Comm: syz-executor.2 Not tainted 6.5.0-rc7-syzkaller-00024-g93f5de5f648d #0 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/26/2023 Call Trace: __dump_stack lib/dump_stack.c:88 [inline] dump_stack_lvl+0x1e7/0x2d0 lib/dump_stack.c:106 check_noncircular+0x375/0x4a0 kernel/locking/lockdep.c:2195 check_prev_add kernel/locking/lockdep.c:3142 [inline] check_prevs_add kernel/locking/lockdep.c:3261 [inline] validate_chain kernel/locking/lockdep.c:3876 [inline] __lock_acquire+0x39ff/0x7f70 kernel/locking/lockdep.c:5144 lock_acquire+0x1e3/0x520 kernel/locking/lockdep.c:5761 __mutex_lock_common+0x1d8/0x2530 kernel/locking/mutex.c:603 __mutex_lock kernel/locking/mutex.c:747 [inline] mutex_lock_nested+0x1b/0x20 kernel/locking/mutex.c:799 __btrfs_release_delayed_node+0x9a/0xaa0 fs/btrfs/delayed-inode.c:256 btrfs_release_delayed_node fs/btrfs/delayed-inode.c:281 [inline] __btrfs_run_delayed_items+0x2b5/0x430 fs/btrfs/delayed-inode.c:1156 btrfs_commit_transaction+0x859/0x2ff0 fs/btrfs/transaction.c:2276 btrfs_sync_file+0xf56/0x1330 fs/btrfs/file.c:1988 vfs_fsync_range fs/sync.c:188 [inline] vfs_fsync fs/sync.c:202 [inline] do_fsync fs/sync.c:212 [inline] __do_sys_fsync fs/sync.c:220 [inline] __se_sys_fsync fs/sync.c:218 [inline] __x64_sys_fsync+0x196/0x1e0 fs/sync.c:218 do_syscall_x64 arch/x86/entry/common.c:50 [inline] do_syscall_64+0x41/0xc0 arch/x86/entry/common.c:80 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f3ad047cae9 Code: 28 00 00 00 75 (...) RSP: 002b:00007f3ad12510c8 EFLAGS: 00000246 ORIG_RAX: 000000000000004a RAX: ffffffffffffffda RBX: 00007f3ad059bf80 RCX: 00007f3ad047cae9 RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000005 RBP: 00007f3ad04c847a R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000 R13: 000000000000000b R14: 00007f3ad059bf80 R15: 00007ffe56af92f8 ------------[ cut here ]------------ Fix this by releasing the path before releasing the delayed node in the error path at __btrfs_run_delayed_items(). Reported-by: syzbot+a379155f07c134ea9879@syzkaller.appspotmail.com Link: https://lore.kernel.org/linux-btrfs/000000000000abba27060403b5bd@google.com/ CC: stable@vger.kernel.org # 4.14+ Signed-off-by: Filipe Manana Signed-off-by: David Sterba --- fs/btrfs/delayed-inode.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 85dcf0024137..9300e09d339b 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1153,20 +1153,33 @@ static int __btrfs_run_delayed_items(struct btrfs_trans_handle *trans, int nr) ret = __btrfs_commit_inode_delayed_items(trans, path, curr_node); if (ret) { - btrfs_release_delayed_node(curr_node); - curr_node = NULL; btrfs_abort_transaction(trans, ret); break; } prev_node = curr_node; curr_node = btrfs_next_delayed_node(curr_node); + /* + * See the comment below about releasing path before releasing + * node. If the commit of delayed items was successful the path + * should always be released, but in case of an error, it may + * point to locked extent buffers (a leaf at the very least). + */ + ASSERT(path->nodes[0] == NULL); btrfs_release_delayed_node(prev_node); } + /* + * Release the path to avoid a potential deadlock and lockdep splat when + * releasing the delayed node, as that requires taking the delayed node's + * mutex. If another task starts running delayed items before we take + * the mutex, it will first lock the mutex and then it may try to lock + * the same btree path (leaf). + */ + btrfs_free_path(path); + if (curr_node) btrfs_release_delayed_node(curr_node); - btrfs_free_path(path); trans->block_rsv = block_rsv; return ret; -- cgit v1.2.3 From 4ca8e03cf2bfaeef7c85939fa1ea0c749cd116ab Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Thu, 24 Aug 2023 16:59:04 -0400 Subject: btrfs: check for BTRFS_FS_ERROR in pending ordered assert If we do fast tree logging we increment a counter on the current transaction for every ordered extent we need to wait for. This means we expect the transaction to still be there when we clear pending on the ordered extent. However if we happen to abort the transaction and clean it up, there could be no running transaction, and thus we'll trip the "ASSERT(trans)" check. This is obviously incorrect, and the code properly deals with the case that the transaction doesn't exist. Fix this ASSERT() to only fire if there's no trans and we don't have BTRFS_FS_ERROR() set on the file system. CC: stable@vger.kernel.org # 4.14+ Reviewed-by: Filipe Manana Signed-off-by: Josef Bacik Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/ordered-data.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c index b46ab348e8e5..345c449d588c 100644 --- a/fs/btrfs/ordered-data.c +++ b/fs/btrfs/ordered-data.c @@ -639,7 +639,7 @@ void btrfs_remove_ordered_extent(struct btrfs_inode *btrfs_inode, refcount_inc(&trans->use_count); spin_unlock(&fs_info->trans_lock); - ASSERT(trans); + ASSERT(trans || BTRFS_FS_ERROR(fs_info)); if (trans) { if (atomic_dec_and_test(&trans->pending_ordered)) wake_up(&trans->pending_wait); -- cgit v1.2.3 From 5e0e879926c1ce7e1f5e0dfaacaf2d105f7d8a05 Mon Sep 17 00:00:00 2001 From: Qu Wenruo Date: Tue, 22 Aug 2023 13:50:51 +0800 Subject: btrfs: fix a compilation error if DEBUG is defined in btree_dirty_folio [BUG] After commit 72a69cd03082 ("btrfs: subpage: pack all subpage bitmaps into a larger bitmap"), the DEBUG section of btree_dirty_folio() would no longer compile. [CAUSE] If DEBUG is defined, we would do extra checks for btree_dirty_folio(), mostly to make sure the range we marked dirty has an extent buffer and that extent buffer is dirty. For subpage, we need to iterate through all the extent buffers covered by that page range, and make sure they all matches the criteria. However commit 72a69cd03082 ("btrfs: subpage: pack all subpage bitmaps into a larger bitmap") changes how we store the bitmap, we pack all the 16 bits bitmaps into a larger bitmap, which would save some space. This means we no longer have btrfs_subpage::dirty_bitmap, instead the dirty bitmap is starting at btrfs_subpage_info::dirty_offset, and has a length of btrfs_subpage_info::bitmap_nr_bits. [FIX] Although I'm not sure if it still makes sense to maintain such code, at least let it compile. This patch would let us test the bits one by one through the bitmaps. CC: stable@vger.kernel.org # 6.1+ Signed-off-by: Qu Wenruo Signed-off-by: David Sterba --- fs/btrfs/disk-io.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c index 639f1e308e4c..68f60d50e1fd 100644 --- a/fs/btrfs/disk-io.c +++ b/fs/btrfs/disk-io.c @@ -520,6 +520,7 @@ static bool btree_dirty_folio(struct address_space *mapping, struct folio *folio) { struct btrfs_fs_info *fs_info = btrfs_sb(mapping->host->i_sb); + struct btrfs_subpage_info *spi = fs_info->subpage_info; struct btrfs_subpage *subpage; struct extent_buffer *eb; int cur_bit = 0; @@ -533,18 +534,19 @@ static bool btree_dirty_folio(struct address_space *mapping, btrfs_assert_tree_write_locked(eb); return filemap_dirty_folio(mapping, folio); } + + ASSERT(spi); subpage = folio_get_private(folio); - ASSERT(subpage->dirty_bitmap); - while (cur_bit < BTRFS_SUBPAGE_BITMAP_SIZE) { + for (cur_bit = spi->dirty_offset; + cur_bit < spi->dirty_offset + spi->bitmap_nr_bits; + cur_bit++) { unsigned long flags; u64 cur; - u16 tmp = (1 << cur_bit); spin_lock_irqsave(&subpage->lock, flags); - if (!(tmp & subpage->dirty_bitmap)) { + if (!test_bit(cur_bit, subpage->bitmaps)) { spin_unlock_irqrestore(&subpage->lock, flags); - cur_bit++; continue; } spin_unlock_irqrestore(&subpage->lock, flags); @@ -557,7 +559,7 @@ static bool btree_dirty_folio(struct address_space *mapping, btrfs_assert_tree_write_locked(eb); free_extent_buffer(eb); - cur_bit += (fs_info->nodesize >> fs_info->sectorsize_bits); + cur_bit += (fs_info->nodesize >> fs_info->sectorsize_bits) - 1; } return filemap_dirty_folio(mapping, folio); } -- cgit v1.2.3 From 91bfe3104b8db0310f76f2dcb6aacef24c889366 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 28 Aug 2023 09:06:42 +0100 Subject: btrfs: improve error message after failure to add delayed dir index item If we fail to add a delayed dir index item because there's already another item with the same index number, we print an error message (and then BUG). However that message isn't very helpful to debug anything because we don't know what's the index number and what are the values of index counters in the inode and its delayed inode (index_cnt fields of struct btrfs_inode and struct btrfs_delayed_node). So update the error message to include the index number and counters. We actually had a recent case where this issue was hit by a syzbot report (see the link below). Link: https://lore.kernel.org/linux-btrfs/00000000000036e1290603e097e0@google.com/ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-inode.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 9300e09d339b..bb9908cbabfe 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1511,9 +1511,10 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, ret = __btrfs_add_delayed_item(delayed_node, delayed_item); if (unlikely(ret)) { btrfs_err(trans->fs_info, - "err add delayed dir index item(name: %.*s) into the insertion tree of the delayed node(root id: %llu, inode id: %llu, errno: %d)", - name_len, name, delayed_node->root->root_key.objectid, - delayed_node->inode_id, ret); +"error adding delayed dir index item, name: %.*s, index: %llu, root: %llu, dir: %llu, dir->index_cnt: %llu, delayed_node->index_cnt: %llu, error: %d", + name_len, name, index, btrfs_root_id(delayed_node->root), + delayed_node->inode_id, dir->index_cnt, + delayed_node->index_cnt, ret); BUG(); } mutex_unlock(&delayed_node->mutex); -- cgit v1.2.3 From 2c58c3931ede7cd08cbecf1f1a4acaf0a04a41a9 Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 28 Aug 2023 09:06:43 +0100 Subject: btrfs: remove BUG() after failure to insert delayed dir index item Instead of calling BUG() when we fail to insert a delayed dir index item into the delayed node's tree, we can just release all the resources we have allocated/acquired before and return the error to the caller. This is fine because all existing call chains undo anything they have done before calling btrfs_insert_delayed_dir_index() or BUG_ON (when creating pending snapshots in the transaction commit path). So remove the BUG() call and do proper error handling. This relates to a syzbot report linked below, but does not fix it because it only prevents hitting a BUG(), it does not fix the issue where somehow we attempt to use twice the same index number for different index items. Link: https://lore.kernel.org/linux-btrfs/00000000000036e1290603e097e0@google.com/ CC: stable@vger.kernel.org # 5.4+ Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-inode.c | 74 ++++++++++++++++++++++++++++++------------------ 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index bb9908cbabfe..6e779bc16340 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -1426,7 +1426,29 @@ void btrfs_balance_delayed_items(struct btrfs_fs_info *fs_info) btrfs_wq_run_delayed_node(delayed_root, fs_info, BTRFS_DELAYED_BATCH); } -/* Will return 0 or -ENOMEM */ +static void btrfs_release_dir_index_item_space(struct btrfs_trans_handle *trans) +{ + struct btrfs_fs_info *fs_info = trans->fs_info; + const u64 bytes = btrfs_calc_insert_metadata_size(fs_info, 1); + + if (test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags)) + return; + + /* + * Adding the new dir index item does not require touching another + * leaf, so we can release 1 unit of metadata that was previously + * reserved when starting the transaction. This applies only to + * the case where we had a transaction start and excludes the + * transaction join case (when replaying log trees). + */ + trace_btrfs_space_reservation(fs_info, "transaction", + trans->transid, bytes, 0); + btrfs_block_rsv_release(fs_info, trans->block_rsv, bytes, NULL); + ASSERT(trans->bytes_reserved >= bytes); + trans->bytes_reserved -= bytes; +} + +/* Will return 0, -ENOMEM or -EEXIST (index number collision, unexpected). */ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, const char *name, int name_len, struct btrfs_inode *dir, @@ -1468,6 +1490,27 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, mutex_lock(&delayed_node->mutex); + /* + * First attempt to insert the delayed item. This is to make the error + * handling path simpler in case we fail (-EEXIST). There's no risk of + * any other task coming in and running the delayed item before we do + * the metadata space reservation below, because we are holding the + * delayed node's mutex and that mutex must also be locked before the + * node's delayed items can be run. + */ + ret = __btrfs_add_delayed_item(delayed_node, delayed_item); + if (unlikely(ret)) { + btrfs_err(trans->fs_info, +"error adding delayed dir index item, name: %.*s, index: %llu, root: %llu, dir: %llu, dir->index_cnt: %llu, delayed_node->index_cnt: %llu, error: %d", + name_len, name, index, btrfs_root_id(delayed_node->root), + delayed_node->inode_id, dir->index_cnt, + delayed_node->index_cnt, ret); + btrfs_release_delayed_item(delayed_item); + btrfs_release_dir_index_item_space(trans); + mutex_unlock(&delayed_node->mutex); + goto release_node; + } + if (delayed_node->index_item_leaves == 0 || delayed_node->curr_index_batch_size + data_len > leaf_data_size) { delayed_node->curr_index_batch_size = data_len; @@ -1485,37 +1528,14 @@ int btrfs_insert_delayed_dir_index(struct btrfs_trans_handle *trans, * impossible. */ if (WARN_ON(ret)) { - mutex_unlock(&delayed_node->mutex); btrfs_release_delayed_item(delayed_item); + mutex_unlock(&delayed_node->mutex); goto release_node; } delayed_node->index_item_leaves++; - } else if (!test_bit(BTRFS_FS_LOG_RECOVERING, &fs_info->flags)) { - const u64 bytes = btrfs_calc_insert_metadata_size(fs_info, 1); - - /* - * Adding the new dir index item does not require touching another - * leaf, so we can release 1 unit of metadata that was previously - * reserved when starting the transaction. This applies only to - * the case where we had a transaction start and excludes the - * transaction join case (when replaying log trees). - */ - trace_btrfs_space_reservation(fs_info, "transaction", - trans->transid, bytes, 0); - btrfs_block_rsv_release(fs_info, trans->block_rsv, bytes, NULL); - ASSERT(trans->bytes_reserved >= bytes); - trans->bytes_reserved -= bytes; - } - - ret = __btrfs_add_delayed_item(delayed_node, delayed_item); - if (unlikely(ret)) { - btrfs_err(trans->fs_info, -"error adding delayed dir index item, name: %.*s, index: %llu, root: %llu, dir: %llu, dir->index_cnt: %llu, delayed_node->index_cnt: %llu, error: %d", - name_len, name, index, btrfs_root_id(delayed_node->root), - delayed_node->inode_id, dir->index_cnt, - delayed_node->index_cnt, ret); - BUG(); + } else { + btrfs_release_dir_index_item_space(trans); } mutex_unlock(&delayed_node->mutex); -- cgit v1.2.3 From a57c2d4e46f519b24558ae0752c17eec416ac72a Mon Sep 17 00:00:00 2001 From: Filipe Manana Date: Mon, 28 Aug 2023 09:06:44 +0100 Subject: btrfs: assert delayed node locked when removing delayed item When removing a delayed item, or releasing which will remove it as well, we will modify one of the delayed node's rbtrees and item counter if the delayed item is in one of the rbtrees. This require having the delayed node's mutex locked, otherwise we will race with other tasks modifying the rbtrees and the counter. This is motivated by a previous version of another patch actually calling btrfs_release_delayed_item() after unlocking the delayed node's mutex and against a delayed item that is in a rbtree. So assert at __btrfs_remove_delayed_item() that the delayed node's mutex is locked. Reviewed-by: Qu Wenruo Signed-off-by: Filipe Manana Reviewed-by: David Sterba Signed-off-by: David Sterba --- fs/btrfs/delayed-inode.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/fs/btrfs/delayed-inode.c b/fs/btrfs/delayed-inode.c index 6e779bc16340..dfe29b29915f 100644 --- a/fs/btrfs/delayed-inode.c +++ b/fs/btrfs/delayed-inode.c @@ -412,6 +412,7 @@ static void finish_one_item(struct btrfs_delayed_root *delayed_root) static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) { + struct btrfs_delayed_node *delayed_node = delayed_item->delayed_node; struct rb_root_cached *root; struct btrfs_delayed_root *delayed_root; @@ -419,18 +420,21 @@ static void __btrfs_remove_delayed_item(struct btrfs_delayed_item *delayed_item) if (RB_EMPTY_NODE(&delayed_item->rb_node)) return; - delayed_root = delayed_item->delayed_node->root->fs_info->delayed_root; + /* If it's in a rbtree, then we need to have delayed node locked. */ + lockdep_assert_held(&delayed_node->mutex); + + delayed_root = delayed_node->root->fs_info->delayed_root; BUG_ON(!delayed_root); if (delayed_item->type == BTRFS_DELAYED_INSERTION_ITEM) - root = &delayed_item->delayed_node->ins_root; + root = &delayed_node->ins_root; else - root = &delayed_item->delayed_node->del_root; + root = &delayed_node->del_root; rb_erase_cached(&delayed_item->rb_node, root); RB_CLEAR_NODE(&delayed_item->rb_node); - delayed_item->delayed_node->count--; + delayed_node->count--; finish_one_item(delayed_root); } -- cgit v1.2.3 From 5facccc9402301d67d48bef06159b91f7e41efc0 Mon Sep 17 00:00:00 2001 From: Bhaskar Chowdhury Date: Wed, 23 Aug 2023 03:17:47 +0530 Subject: MAINTAINERS: remove links to obsolete btrfs.wiki.kernel.org The wiki has been archived and is not updated anymore. Remove or replace the links in files that contain it (MAINTAINERS, Kconfig, docs). Signed-off-by: Bhaskar Chowdhury Reviewed-by: David Sterba Signed-off-by: David Sterba --- Documentation/filesystems/btrfs.rst | 1 - MAINTAINERS | 1 - fs/btrfs/Kconfig | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Documentation/filesystems/btrfs.rst b/Documentation/filesystems/btrfs.rst index 992eddb0e11b..a81db8f54d68 100644 --- a/Documentation/filesystems/btrfs.rst +++ b/Documentation/filesystems/btrfs.rst @@ -37,7 +37,6 @@ For more information please refer to the documentation site or wiki https://btrfs.readthedocs.io - https://btrfs.wiki.kernel.org that maintains information about administration tasks, frequently asked questions, use cases, mount options, comprehensible changelogs, features, diff --git a/MAINTAINERS b/MAINTAINERS index d590ce31aa72..dea8c26efbca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4360,7 +4360,6 @@ M: David Sterba L: linux-btrfs@vger.kernel.org S: Maintained W: https://btrfs.readthedocs.io -W: https://btrfs.wiki.kernel.org/ Q: https://patchwork.kernel.org/project/linux-btrfs/list/ C: irc://irc.libera.chat/btrfs T: git git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git diff --git a/fs/btrfs/Kconfig b/fs/btrfs/Kconfig index 3282adc84d52..a25c9910d90b 100644 --- a/fs/btrfs/Kconfig +++ b/fs/btrfs/Kconfig @@ -31,7 +31,7 @@ config BTRFS_FS continue to be mountable and usable by newer kernels. For more information, please see the web pages at - http://btrfs.wiki.kernel.org. + https://btrfs.readthedocs.io To compile this file system support as a module, choose M here. The module will be called btrfs. -- cgit v1.2.3 From 9616cb34b08ec86642b162eae75c5a7ca8debe3c Mon Sep 17 00:00:00 2001 From: Björn Töpel Date: Wed, 5 Jul 2023 13:53:17 +0200 Subject: kselftest/runner.sh: Propagate SIGTERM to runner child MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Timeouts in kselftest are done using the "timeout" command with the "--foreground" option. Without the "foreground" option, it is not possible for a user to cancel the runner using SIGINT, because the signal is not propagated to timeout which is running in a different process group. The "forground" options places the timeout in the same process group as its parent, but only sends the SIGTERM (on timeout) signal to the forked process. Unfortunately, this does not play nice with all kselftests, e.g. "net:fcnal-test.sh", where the child processes will linger because timeout does not send SIGTERM to the group. Some users have noted these hangs [1]. Fix this by nesting the timeout with an additional timeout without the foreground option. Link: https://lore.kernel.org/all/7650b2eb-0aee-a2b0-2e64-c9bc63210f67@alu.unizg.hr/ # [1] Fixes: 651e0d881461 ("kselftest/runner: allow to properly deliver signals to tests") Signed-off-by: Björn Töpel Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest/runner.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/kselftest/runner.sh b/tools/testing/selftests/kselftest/runner.sh index 1c952d1401d4..70e0a465e30d 100644 --- a/tools/testing/selftests/kselftest/runner.sh +++ b/tools/testing/selftests/kselftest/runner.sh @@ -36,7 +36,8 @@ tap_timeout() { # Make sure tests will time out if utility is available. if [ -x /usr/bin/timeout ] ; then - /usr/bin/timeout --foreground "$kselftest_timeout" $1 + /usr/bin/timeout --foreground "$kselftest_timeout" \ + /usr/bin/timeout "$kselftest_timeout" $1 else $1 fi -- cgit v1.2.3 From 5f9dd2e896a91bfca90f8463eb6808c03d535d8a Mon Sep 17 00:00:00 2001 From: "Ricardo B. Marliere" Date: Tue, 22 Aug 2023 18:09:40 -0300 Subject: selftests: fix dependency checker script This patch fixes inconsistencies in the parsing rules of the levels 1 and 2 of the kselftest_deps.sh. It was added the levels 4 and 5 to account for a few edge cases that are present in some tests, also some minor identation styling have been fixed (s/ /\t/g). Signed-off-by: Ricardo B. Marliere Signed-off-by: Shuah Khan --- tools/testing/selftests/kselftest_deps.sh | 77 ++++++++++++++++++++++++++----- 1 file changed, 65 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/kselftest_deps.sh b/tools/testing/selftests/kselftest_deps.sh index 4bc14d9e8ff1..de59cc8f03c3 100755 --- a/tools/testing/selftests/kselftest_deps.sh +++ b/tools/testing/selftests/kselftest_deps.sh @@ -46,11 +46,11 @@ fi print_targets=0 while getopts "p" arg; do - case $arg in - p) + case $arg in + p) print_targets=1 shift;; - esac + esac done if [ $# -eq 0 ] @@ -92,6 +92,10 @@ pass_cnt=0 # Get all TARGETS from selftests Makefile targets=$(grep -E "^TARGETS +|^TARGETS =" Makefile | cut -d "=" -f2) +# Initially, in LDLIBS related lines, the dep checker needs +# to ignore lines containing the following strings: +filter="\$(VAR_LDLIBS)\|pkg-config\|PKG_CONFIG\|IOURING_EXTRA_LIBS" + # Single test case if [ $# -eq 2 ] then @@ -100,6 +104,8 @@ then l1_test $test l2_test $test l3_test $test + l4_test $test + l5_test $test print_results $1 $2 exit $? @@ -113,7 +119,7 @@ fi # Append space at the end of the list to append more tests. l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \ - grep -v "VAR_LDLIBS" | awk -F: '{print $1}') + grep -v "$filter" | awk -F: '{print $1}' | uniq) # Level 2: LDLIBS set dynamically. # @@ -126,7 +132,7 @@ l1_tests=$(grep -r --include=Makefile "^LDLIBS" | \ # Append space at the end of the list to append more tests. l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ - grep -v "VAR_LDLIBS" | awk -F: '{print $1}') + grep -v "$filter" | awk -F: '{print $1}' | uniq) # Level 3 # memfd and others use pkg-config to find mount and fuse libs @@ -138,11 +144,32 @@ l2_tests=$(grep -r --include=Makefile ": LDLIBS" | \ # VAR_LDLIBS := $(shell pkg-config fuse --libs 2>/dev/null) l3_tests=$(grep -r --include=Makefile "^VAR_LDLIBS" | \ - grep -v "pkg-config" | awk -F: '{print $1}') + grep -v "pkg-config\|PKG_CONFIG" | awk -F: '{print $1}' | uniq) -#echo $l1_tests -#echo $l2_1_tests -#echo $l3_tests +# Level 4 +# some tests may fall back to default using `|| echo -l` +# if pkg-config doesn't find the libs, instead of using VAR_LDLIBS +# as per level 3 checks. +# e.g: +# netfilter/Makefile +# LDLIBS += $(shell $(HOSTPKG_CONFIG) --libs libmnl 2>/dev/null || echo -lmnl) +l4_tests=$(grep -r --include=Makefile "^LDLIBS" | \ + grep "pkg-config\|PKG_CONFIG" | awk -F: '{print $1}' | uniq) + +# Level 5 +# some tests may use IOURING_EXTRA_LIBS to add extra libs to LDLIBS, +# which in turn may be defined in a sub-Makefile +# e.g.: +# mm/Makefile +# $(OUTPUT)/gup_longterm: LDLIBS += $(IOURING_EXTRA_LIBS) +l5_tests=$(grep -r --include=Makefile "LDLIBS +=.*\$(IOURING_EXTRA_LIBS)" | \ + awk -F: '{print $1}' | uniq) + +#echo l1_tests $l1_tests +#echo l2_tests $l2_tests +#echo l3_tests $l3_tests +#echo l4_tests $l4_tests +#echo l5_tests $l5_tests all_tests print_results $1 $2 @@ -164,24 +191,32 @@ all_tests() for test in $l3_tests; do l3_test $test done + + for test in $l4_tests; do + l4_test $test + done + + for test in $l5_tests; do + l5_test $test + done } # Use same parsing used for l1_tests and pick libraries this time. l1_test() { test_libs=$(grep --include=Makefile "^LDLIBS" $test | \ - grep -v "VAR_LDLIBS" | \ + grep -v "$filter" | \ sed -e 's/\:/ /' | \ sed -e 's/+/ /' | cut -d "=" -f 2) check_libs $test $test_libs } -# Use same parsing used for l2__tests and pick libraries this time. +# Use same parsing used for l2_tests and pick libraries this time. l2_test() { test_libs=$(grep --include=Makefile ": LDLIBS" $test | \ - grep -v "VAR_LDLIBS" | \ + grep -v "$filter" | \ sed -e 's/\:/ /' | sed -e 's/+/ /' | \ cut -d "=" -f 2) @@ -197,6 +232,24 @@ l3_test() check_libs $test $test_libs } +l4_test() +{ + test_libs=$(grep --include=Makefile "^VAR_LDLIBS\|^LDLIBS" $test | \ + grep "\(pkg-config\|PKG_CONFIG\).*|| echo " | \ + sed -e 's/.*|| echo //' | sed -e 's/)$//') + + check_libs $test $test_libs +} + +l5_test() +{ + tests=$(find $(dirname "$test") -type f -name "*.mk") + test_libs=$(grep "^IOURING_EXTRA_LIBS +\?=" $tests | \ + cut -d "=" -f 2) + + check_libs $test $test_libs +} + check_libs() { -- cgit v1.2.3 From 3f3f384139ed147c71e1d770accf610133d5309b Mon Sep 17 00:00:00 2001 From: Björn Töpel Date: Tue, 22 Aug 2023 15:58:37 +0200 Subject: selftests: Keep symlinks, when possible MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When kselftest is built/installed with the 'gen_tar' target, rsync is used for the installation step to copy files. Extra care is needed for tests that have symlinks. Commit ae108c48b5d2 ("selftests: net: Fix cross-tree inclusion of scripts") added '-L' (transform symlink into referent file/dir) to rsync, to fix dangling links. However, that broke some tests where the symlink (being a symlink) is part of the test (e.g. exec:execveat). Use rsync's '--copy-unsafe-links' that does right thing. Fixes: ae108c48b5d2 ("selftests: net: Fix cross-tree inclusion of scripts") Signed-off-by: Björn Töpel Reviewed-by: Benjamin Poirier Signed-off-by: Shuah Khan --- tools/testing/selftests/lib.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/lib.mk b/tools/testing/selftests/lib.mk index d17854285f2b..118e0964bda9 100644 --- a/tools/testing/selftests/lib.mk +++ b/tools/testing/selftests/lib.mk @@ -106,7 +106,7 @@ endef run_tests: all ifdef building_out_of_srctree @if [ "X$(TEST_PROGS)$(TEST_PROGS_EXTENDED)$(TEST_FILES)" != "X" ]; then \ - rsync -aLq $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT); \ + rsync -aq --copy-unsafe-links $(TEST_PROGS) $(TEST_PROGS_EXTENDED) $(TEST_FILES) $(OUTPUT); \ fi @if [ "X$(TEST_PROGS)" != "X" ]; then \ $(call RUN_TESTS, $(TEST_GEN_PROGS) $(TEST_CUSTOM_PROGS) \ @@ -120,7 +120,7 @@ endif define INSTALL_SINGLE_RULE $(if $(INSTALL_LIST),@mkdir -p $(INSTALL_PATH)) - $(if $(INSTALL_LIST),rsync -aL $(INSTALL_LIST) $(INSTALL_PATH)/) + $(if $(INSTALL_LIST),rsync -a --copy-unsafe-links $(INSTALL_LIST) $(INSTALL_PATH)/) endef define INSTALL_RULE -- cgit v1.2.3 From 7deac114be5fb25a4e865212ed0feaf5f85f2a28 Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 25 Aug 2023 10:55:31 +0800 Subject: md: don't dereference mddev after export_rdev() Except for initial reference, mddev->kobject is referenced by rdev->kobject, and if the last rdev is freed, there is no guarantee that mddev is still valid. Hence mddev should not be used anymore after export_rdev(). This problem can be triggered by following test for mdadm at very low rate: New file: mdadm/tests/23rdev-lifetime devname=${dev0##*/} devt=`cat /sys/block/$devname/dev` pid="" runtime=2 clean_up_test() { pill -9 $pid echo clear > /sys/block/md0/md/array_state } trap 'clean_up_test' EXIT add_by_sysfs() { while true; do echo $devt > /sys/block/md0/md/new_dev done } remove_by_sysfs(){ while true; do echo remove > /sys/block/md0/md/dev-${devname}/state done } echo md0 > /sys/module/md_mod/parameters/new_array || die "create md0 failed" add_by_sysfs & pid="$pid $!" remove_by_sysfs & pid="$pid $!" sleep $runtime exit 0 Test cmd: ./test --save-logs --logdir=/tmp/ --keep-going --dev=loop --tests=23rdev-lifetime Test result: general protection fault, probably for non-canonical address 0x6b6b6b6b6b6b6bcb: 0000 [#4] PREEMPT SMP CPU: 0 PID: 1292 Comm: test Tainted: G D W 6.5.0-rc2-00121-g01e55c376936 #562 RIP: 0010:md_wakeup_thread+0x9e/0x320 [md_mod] Call Trace: mddev_unlock+0x1b6/0x310 [md_mod] rdev_attr_store+0xec/0x190 [md_mod] sysfs_kf_write+0x52/0x70 kernfs_fop_write_iter+0x19a/0x2a0 vfs_write+0x3b5/0x770 ksys_write+0x74/0x150 __x64_sys_write+0x22/0x30 do_syscall_64+0x40/0x90 entry_SYSCALL_64_after_hwframe+0x63/0xcd Fix this problem by don't dereference mddev after export_rdev(). Fixes: 3ce94ce5d05a ("md: fix duplicate filename for rdev") Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230825025532.1523008-2-yukuai1@huaweicloud.com --- drivers/md/md.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 0fe7ab6e8ab9..590aee057aca 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -798,14 +798,14 @@ void mddev_unlock(struct mddev *mddev) } else mutex_unlock(&mddev->reconfig_mutex); + md_wakeup_thread(mddev->thread); + wake_up(&mddev->sb_wait); + list_for_each_entry_safe(rdev, tmp, &delete, same_set) { list_del_init(&rdev->same_set); kobject_del(&rdev->kobj); export_rdev(rdev, mddev); } - - md_wakeup_thread(mddev->thread); - wake_up(&mddev->sb_wait); } EXPORT_SYMBOL_GPL(mddev_unlock); -- cgit v1.2.3 From 99892147f028d711f9d40fefad4f33632593864c Mon Sep 17 00:00:00 2001 From: Yu Kuai Date: Fri, 25 Aug 2023 10:55:32 +0800 Subject: md: fix warning for holder mismatch from export_rdev() Commit a1d767191096 ("md: use mddev->external to select holder in export_rdev()") fix the problem that 'claim_rdev' is used for blkdev_get_by_dev() while 'rdev' is used for blkdev_put(). However, if mddev->external is changed from 0 to 1, then 'rdev' is used for blkdev_get_by_dev() while 'claim_rdev' is used for blkdev_put(). And this problem can be reporduced reliably by following: New file: mdadm/tests/23rdev-lifetime devname=${dev0##*/} devt=`cat /sys/block/$devname/dev` pid="" runtime=2 clean_up_test() { pill -9 $pid echo clear > /sys/block/md0/md/array_state } trap 'clean_up_test' EXIT add_by_sysfs() { while true; do echo $devt > /sys/block/md0/md/new_dev done } remove_by_sysfs(){ while true; do echo remove > /sys/block/md0/md/dev-${devname}/state done } echo md0 > /sys/module/md_mod/parameters/new_array || die "create md0 failed" add_by_sysfs & pid="$pid $!" remove_by_sysfs & pid="$pid $!" sleep $runtime exit 0 Test cmd: ./test --save-logs --logdir=/tmp/ --keep-going --dev=loop --tests=23rdev-lifetime Test result: ------------[ cut here ]------------ WARNING: CPU: 0 PID: 960 at block/bdev.c:618 blkdev_put+0x27c/0x330 Modules linked in: multipath md_mod loop CPU: 0 PID: 960 Comm: test Not tainted 6.5.0-rc2-00121-g01e55c376936-dirty #50 RIP: 0010:blkdev_put+0x27c/0x330 Call Trace: export_rdev.isra.23+0x50/0xa0 [md_mod] mddev_unlock+0x19d/0x300 [md_mod] rdev_attr_store+0xec/0x190 [md_mod] sysfs_kf_write+0x52/0x70 kernfs_fop_write_iter+0x19a/0x2a0 vfs_write+0x3b5/0x770 ksys_write+0x74/0x150 __x64_sys_write+0x22/0x30 do_syscall_64+0x40/0x90 entry_SYSCALL_64_after_hwframe+0x63/0xcd Fix the problem by recording if 'rdev' is used as holder. Fixes: a1d767191096 ("md: use mddev->external to select holder in export_rdev()") Signed-off-by: Yu Kuai Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230825025532.1523008-3-yukuai1@huaweicloud.com --- drivers/md/md.c | 15 ++++++++++++--- drivers/md/md.h | 3 +++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 590aee057aca..73758b754127 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -2452,7 +2452,8 @@ static void export_rdev(struct md_rdev *rdev, struct mddev *mddev) if (test_bit(AutoDetected, &rdev->flags)) md_autodetect_dev(rdev->bdev->bd_dev); #endif - blkdev_put(rdev->bdev, mddev->external ? &claim_rdev : rdev); + blkdev_put(rdev->bdev, + test_bit(Holder, &rdev->flags) ? rdev : &claim_rdev); rdev->bdev = NULL; kobject_put(&rdev->kobj); } @@ -3632,6 +3633,7 @@ EXPORT_SYMBOL_GPL(md_rdev_init); static struct md_rdev *md_import_device(dev_t newdev, int super_format, int super_minor) { struct md_rdev *rdev; + struct md_rdev *holder; sector_t size; int err; @@ -3646,8 +3648,15 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe if (err) goto out_clear_rdev; + if (super_format == -2) { + holder = &claim_rdev; + } else { + holder = rdev; + set_bit(Holder, &rdev->flags); + } + rdev->bdev = blkdev_get_by_dev(newdev, BLK_OPEN_READ | BLK_OPEN_WRITE, - super_format == -2 ? &claim_rdev : rdev, NULL); + holder, NULL); if (IS_ERR(rdev->bdev)) { pr_warn("md: could not open device unknown-block(%u,%u).\n", MAJOR(newdev), MINOR(newdev)); @@ -3684,7 +3693,7 @@ static struct md_rdev *md_import_device(dev_t newdev, int super_format, int supe return rdev; out_blkdev_put: - blkdev_put(rdev->bdev, super_format == -2 ? &claim_rdev : rdev); + blkdev_put(rdev->bdev, holder); out_clear_rdev: md_rdev_clear(rdev); out_free_rdev: diff --git a/drivers/md/md.h b/drivers/md/md.h index 9bcb77bca963..7c9c13abd7ca 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -211,6 +211,9 @@ enum flag_bits { * check if there is collision between raid1 * serial bios. */ + Holder, /* rdev is used as holder while opening + * underlying disk exclusively. + */ }; static inline int is_badblock(struct md_rdev *rdev, sector_t s, int sectors, -- cgit v1.2.3 From d508ee2dd5574b1e84d140b927c25807c9b66d7e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 7 Sep 2023 17:58:59 -0400 Subject: tracefs/eventfs: Free top level files on removal When an instance is removed, the top level files of the eventfs directory are not cleaned up. Call the eventfs_remove() on each of the entries to free them. This was found via kmemleak: unreferenced object 0xffff8881047c1280 (size 96): comm "mkdir", pid 924, jiffies 4294906489 (age 2013.077s) hex dump (first 32 bytes): 18 31 ed 03 81 88 ff ff 00 31 09 24 81 88 ff ff .1.......1.$.... 00 00 00 00 00 00 00 00 98 19 7c 04 81 88 ff ff ..........|..... backtrace: [<000000000fa46b4d>] kmalloc_trace+0x2a/0xa0 [<00000000e729cd0c>] eventfs_prepare_ef.constprop.0+0x3a/0x160 [<000000009032e6a8>] eventfs_add_events_file+0xa0/0x160 [<00000000fe968442>] create_event_toplevel_files+0x6f/0x130 [<00000000e364d173>] event_trace_add_tracer+0x14/0x140 [<00000000411840fa>] trace_array_create_dir+0x52/0xf0 [<00000000967804fa>] trace_array_create+0x208/0x370 [<00000000da505565>] instance_mkdir+0x6b/0xb0 [<00000000dc1215af>] tracefs_syscall_mkdir+0x5b/0x90 [<00000000a8aca289>] vfs_mkdir+0x272/0x380 [<000000007709b242>] do_mkdirat+0xfc/0x1d0 [<00000000c0b6d219>] __x64_sys_mkdir+0x78/0xa0 [<0000000097b5dd4b>] do_syscall_64+0x3f/0x90 [<00000000a3f00cfa>] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 unreferenced object 0xffff888103ed3118 (size 8): comm "mkdir", pid 924, jiffies 4294906489 (age 2013.077s) hex dump (first 8 bytes): 65 6e 61 62 6c 65 00 00 enable.. backtrace: [<0000000010f75127>] __kmalloc_node_track_caller+0x51/0x160 [<000000004b3eca91>] kstrdup+0x34/0x60 [<0000000050074d7a>] eventfs_prepare_ef.constprop.0+0x53/0x160 [<000000009032e6a8>] eventfs_add_events_file+0xa0/0x160 [<00000000fe968442>] create_event_toplevel_files+0x6f/0x130 [<00000000e364d173>] event_trace_add_tracer+0x14/0x140 [<00000000411840fa>] trace_array_create_dir+0x52/0xf0 [<00000000967804fa>] trace_array_create+0x208/0x370 [<00000000da505565>] instance_mkdir+0x6b/0xb0 [<00000000dc1215af>] tracefs_syscall_mkdir+0x5b/0x90 [<00000000a8aca289>] vfs_mkdir+0x272/0x380 [<000000007709b242>] do_mkdirat+0xfc/0x1d0 [<00000000c0b6d219>] __x64_sys_mkdir+0x78/0xa0 [<0000000097b5dd4b>] do_syscall_64+0x3f/0x90 [<00000000a3f00cfa>] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Link: https://lore.kernel.org/linux-trace-kernel/20230907175859.6fedbaa2@gandalf.local.home Cc: Mark Rutland Cc: Ajay Kaher Cc: Zheng Yejian Cc: Naresh Kamboju Reviewed-by: Masami Hiramatsu (Google) Fixes: 5bdcd5f5331a2 eventfs: ("Implement removal of meta data from eventfs") Signed-off-by: Steven Rostedt (Google) --- fs/tracefs/event_inode.c | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index 609ccb5b7cfc..f168aca45458 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -195,17 +195,39 @@ void eventfs_set_ef_status_free(struct tracefs_inode *ti, struct dentry *dentry) { struct tracefs_inode *ti_parent; struct eventfs_inode *ei; - struct eventfs_file *ef; - - mutex_lock(&eventfs_mutex); + struct eventfs_file *ef, *tmp; /* The top level events directory may be freed by this */ if (unlikely(ti->flags & TRACEFS_EVENT_TOP_INODE)) { + LIST_HEAD(ef_del_list); + + mutex_lock(&eventfs_mutex); + ei = ti->private; + + /* Record all the top level files */ + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + lockdep_is_held(&eventfs_mutex)) { + list_add_tail(&ef->del_list, &ef_del_list); + } + + /* Nothing should access this, but just in case! */ + ti->private = NULL; + + mutex_unlock(&eventfs_mutex); + + /* Now safely free the top level files and their children */ + list_for_each_entry_safe(ef, tmp, &ef_del_list, del_list) { + list_del(&ef->del_list); + eventfs_remove(ef); + } + kfree(ei); - goto out; + return; } + mutex_lock(&eventfs_mutex); + ti_parent = get_tracefs(dentry->d_parent->d_inode); if (!ti_parent || !(ti_parent->flags & TRACEFS_EVENT_INODE)) goto out; -- cgit v1.2.3 From 95a404bd60af6c4d9d8db01ad14fe8957ece31ca Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 7 Sep 2023 12:28:20 -0400 Subject: ring-buffer: Do not attempt to read past "commit" When iterating over the ring buffer while the ring buffer is active, the writer can corrupt the reader. There's barriers to help detect this and handle it, but that code missed the case where the last event was at the very end of the page and has only 4 bytes left. The checks to detect the corruption by the writer to reads needs to see the length of the event. If the length in the first 4 bytes is zero then the length is stored in the second 4 bytes. But if the writer is in the process of updating that code, there's a small window where the length in the first 4 bytes could be zero even though the length is only 4 bytes. That will cause rb_event_length() to read the next 4 bytes which could happen to be off the allocated page. To protect against this, fail immediately if the next event pointer is less than 8 bytes from the end of the commit (last byte of data), as all events must be a minimum of 8 bytes anyway. Link: https://lore.kernel.org/all/20230905141245.26470-1-Tze-nan.Wu@mediatek.com/ Link: https://lore.kernel.org/linux-trace-kernel/20230907122820.0899019c@gandalf.local.home Cc: Masami Hiramatsu Cc: Mark Rutland Reported-by: Tze-nan Wu Signed-off-by: Steven Rostedt (Google) --- kernel/trace/ring_buffer.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c index 72ccf75defd0..a1651edc48d5 100644 --- a/kernel/trace/ring_buffer.c +++ b/kernel/trace/ring_buffer.c @@ -2390,6 +2390,11 @@ rb_iter_head_event(struct ring_buffer_iter *iter) */ commit = rb_page_commit(iter_head_page); smp_rmb(); + + /* An event needs to be at least 8 bytes in size */ + if (iter->head > commit - 8) + goto reset; + event = __rb_page_index(iter_head_page, iter->head); length = rb_event_length(event); -- cgit v1.2.3 From 1ef26d8b2ca5d8715563c951083cf6c385c77d1f Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 7 Sep 2023 22:19:11 -0400 Subject: tracing: Use the new eventfs descriptor for print trigger The check to create the print event "trigger" was using the obsolete "dir" value of the trace_event_file to determine if it should create the trigger or not. But that value will now be NULL because it uses the event file descriptor. Change it to test the "ef" field of the trace_event_file structure so that the trace_marker "trigger" file appears again. Link: https://lkml.kernel.org/r/20230908022001.371815239@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Ajay Kaher Fixes: 27152bceea1df ("eventfs: Move tracing/events to eventfs") Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c index 0608ad20cf30..122c23c9eb28 100644 --- a/kernel/trace/trace.c +++ b/kernel/trace/trace.c @@ -9792,8 +9792,8 @@ init_tracer_tracefs(struct trace_array *tr, struct dentry *d_tracer) tr, &tracing_mark_fops); file = __find_event_file(tr, "ftrace", "print"); - if (file && file->dir) - trace_create_file("trigger", TRACE_MODE_WRITE, file->dir, + if (file && file->ef) + eventfs_add_file("trigger", TRACE_MODE_WRITE, file->ef, file, &event_trigger_fops); tr->trace_marker_file = file; -- cgit v1.2.3 From 6fdac58c560e4d164eb8161987bee045147cabe4 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Thu, 7 Sep 2023 22:19:12 -0400 Subject: tracing: Remove unused trace_event_file dir field Now that eventfs structure is used to create the events directory via the eventfs dynamically allocate code, the "dir" field of the trace_event_file structure is no longer used. Remove it. Link: https://lkml.kernel.org/r/20230908022001.580400115@goodmis.org Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Andrew Morton Cc: Ajay Kaher Signed-off-by: Steven Rostedt (Google) --- include/linux/trace_events.h | 1 - kernel/trace/trace_events.c | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index eb5c3add939b..12f875e9e69a 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -650,7 +650,6 @@ struct trace_event_file { struct trace_event_call *event_call; struct event_filter __rcu *filter; struct eventfs_file *ef; - struct dentry *dir; struct trace_array *tr; struct trace_subsystem_dir *system; struct list_head triggers; diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 2af92177b765..065c63991858 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -992,19 +992,6 @@ static void remove_subsystem(struct trace_subsystem_dir *dir) static void remove_event_file_dir(struct trace_event_file *file) { - struct dentry *dir = file->dir; - struct dentry *child; - - if (dir) { - spin_lock(&dir->d_lock); /* probably unneeded */ - list_for_each_entry(child, &dir->d_subdirs, d_child) { - if (d_really_is_positive(child)) /* probably unneeded */ - d_inode(child)->i_private = NULL; - } - spin_unlock(&dir->d_lock); - - tracefs_remove(dir); - } eventfs_remove(file->ef); list_del(&file->list); remove_subsystem(file->system); -- cgit v1.2.3 From 145036f88d693d7ef3aa8537a4b1aa22f8764647 Mon Sep 17 00:00:00 2001 From: Naveen N Rao Date: Wed, 14 Jun 2023 14:40:46 +0530 Subject: selftests/ftrace: Fix dependencies for some of the synthetic event tests Commit b81a3a100cca1b ("tracing/histogram: Add simple tests for stacktrace usage of synthetic events") changed the output text in tracefs README, but missed updating some of the dependencies specified in selftests. This causes some of the tests to exit as unsupported. Fix this by changing the grep pattern. Since we want these tests to work on older kernels, match only against the common last part of the pattern. Link: https://lore.kernel.org/linux-trace-kernel/20230614091046.2178539-1-naveen@kernel.org Cc: Cc: Masami Hiramatsu Cc: Shuah Khan Fixes: b81a3a100cca ("tracing/histogram: Add simple tests for stacktrace usage of synthetic events") Signed-off-by: Naveen N Rao Signed-off-by: Steven Rostedt (Google) --- .../test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc | 2 +- .../test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc index 213d890ed188..174376ddbc6c 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic-event-dynstring.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test inter-event histogram trigger trace action with dynamic string param -# requires: set_event synthetic_events events/sched/sched_process_exec/hist "char name[]' >> synthetic_events":README ping:program +# requires: set_event synthetic_events events/sched/sched_process_exec/hist "' >> synthetic_events":README ping:program fail() { #msg echo $1 diff --git a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc index 955e3ceea44b..b927ee54c02d 100644 --- a/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc +++ b/tools/testing/selftests/ftrace/test.d/trigger/inter-event/trigger-synthetic_event_syntax_errors.tc @@ -1,7 +1,7 @@ #!/bin/sh # SPDX-License-Identifier: GPL-2.0 # description: event trigger - test synthetic_events syntax parser errors -# requires: synthetic_events error_log "char name[]' >> synthetic_events":README +# requires: synthetic_events error_log "' >> synthetic_events":README check_error() { # command-with-error-pos-by-^ ftrace_errlog_check 'synthetic_events' "$1" 'synthetic_events' -- cgit v1.2.3 From fdd2630a7398191e84822612e589062063bd4f3d Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Sat, 9 Sep 2023 07:12:30 -0400 Subject: nfsd: fix change_info in NFSv4 RENAME replies nfsd sends the transposed directory change info in the RENAME reply. The source directory is in save_fh and the target is in current_fh. Reported-by: Zhi Li Reported-by: Benjamin Coddington Closes: https://bugzilla.redhat.com/show_bug.cgi?id=2218844 Signed-off-by: Jeff Layton Cc: Signed-off-by: Chuck Lever --- fs/nfsd/nfs4proc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 5ca748309c26..4199ede0583c 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c @@ -1058,8 +1058,8 @@ nfsd4_rename(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, rename->rn_tname, rename->rn_tnamelen); if (status) return status; - set_change_info(&rename->rn_sinfo, &cstate->current_fh); - set_change_info(&rename->rn_tinfo, &cstate->save_fh); + set_change_info(&rename->rn_sinfo, &cstate->save_fh); + set_change_info(&rename->rn_tinfo, &cstate->current_fh); return nfs_ok; } -- cgit v1.2.3 From ced33ca07d8d99435ca3320c740ea947843005ca Mon Sep 17 00:00:00 2001 From: Juntong Deng Date: Thu, 7 Sep 2023 00:26:03 +0800 Subject: selftests/net: Improve bind_bhash.sh to accommodate predictable network interface names Starting with v197, systemd uses predictable interface network names, the traditional interface naming scheme (eth0) is deprecated, therefore it cannot be assumed that the eth0 interface exists on the host. This modification makes the bind_bhash test program run in a separate network namespace and no longer needs to consider the name of the network interface on the host. Signed-off-by: Juntong Deng Signed-off-by: David S. Miller --- tools/testing/selftests/net/bind_bhash.sh | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/testing/selftests/net/bind_bhash.sh b/tools/testing/selftests/net/bind_bhash.sh index ca0292d4b441..a28563bdaae0 100755 --- a/tools/testing/selftests/net/bind_bhash.sh +++ b/tools/testing/selftests/net/bind_bhash.sh @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-2.0 NR_FILES=32768 -SAVED_NR_FILES=$(ulimit -n) +readonly NETNS="ns-$(mktemp -u XXXXXX)" # default values port=443 @@ -36,21 +36,21 @@ while getopts "ha:p:64" opt; do done setup() { + ip netns add "${NETNS}" + ip -netns "${NETNS}" link add veth0 type veth peer name veth1 + ip -netns "${NETNS}" link set lo up + ip -netns "${NETNS}" link set veth0 up + ip -netns "${NETNS}" link set veth1 up + if [[ "$use_v6" == true ]]; then - ip addr add $addr_v6 nodad dev eth0 + ip -netns "${NETNS}" addr add $addr_v6 nodad dev veth0 else - ip addr add $addr_v4 dev lo + ip -netns "${NETNS}" addr add $addr_v4 dev lo fi - ulimit -n $NR_FILES } cleanup() { - if [[ "$use_v6" == true ]]; then - ip addr del $addr_v6 dev eth0 - else - ip addr del $addr_v4/32 dev lo - fi - ulimit -n $SAVED_NR_FILES + ip netns del "${NETNS}" } if [[ "$addr" != "" ]]; then @@ -59,8 +59,10 @@ if [[ "$addr" != "" ]]; then fi setup if [[ "$use_v6" == true ]] ; then - ./bind_bhash $port "ipv6" $addr_v6 + ip netns exec "${NETNS}" sh -c \ + "ulimit -n ${NR_FILES};./bind_bhash ${port} ipv6 ${addr_v6}" else - ./bind_bhash $port "ipv4" $addr_v4 + ip netns exec "${NETNS}" sh -c \ + "ulimit -n ${NR_FILES};./bind_bhash ${port} ipv4 ${addr_v4}" fi cleanup -- cgit v1.2.3 From e73d1ab6cd7e7190bd891e521d270cd26ad8e40d Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 7 Sep 2023 11:55:12 +0200 Subject: net: bcmasp: add missing of_node_put for_each_available_child_of_node performs an of_node_get on each iteration, so a break out of the loop requires an of_node_put. This was done using the Coccinelle semantic patch iterators/for_each_child.cocci Signed-off-by: Julia Lawall Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/asp2/bcmasp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.c b/drivers/net/ethernet/broadcom/asp2/bcmasp.c index d63d321f3e7b..f048e3d45119 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.c @@ -1300,6 +1300,7 @@ static int bcmasp_probe(struct platform_device *pdev) if (!intf) { dev_err(dev, "Cannot create eth interface %d\n", i); bcmasp_remove_intfs(priv); + of_node_put(intf_node); goto of_put_exit; } list_add_tail(&intf->list, &priv->intfs); -- cgit v1.2.3 From 281f65d29d6da1a9b6907fb0b145aaf34f4e4822 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Thu, 7 Sep 2023 22:03:58 +0800 Subject: net: microchip: vcap api: Fix possible memory leak for vcap_dup_rule() Inject fault When select CONFIG_VCAP_KUNIT_TEST, the below memory leak occurs. If kzalloc() for duprule succeeds, but the following kmemdup() fails, the duprule, ckf and caf memory will be leaked. So kfree them in the error path. unreferenced object 0xffff122744c50600 (size 192): comm "kunit_try_catch", pid 346, jiffies 4294896122 (age 911.812s) hex dump (first 32 bytes): 10 27 00 00 04 00 00 00 1e 00 00 00 2c 01 00 00 .'..........,... 00 00 00 00 00 00 00 00 18 06 c5 44 27 12 ff ff ...........D'... backtrace: [<00000000394b0db8>] __kmem_cache_alloc_node+0x274/0x2f8 [<0000000001bedc67>] kmalloc_trace+0x38/0x88 [<00000000b0612f98>] vcap_dup_rule+0x50/0x460 [<000000005d2d3aca>] vcap_add_rule+0x8cc/0x1038 [<00000000eef9d0f8>] test_vcap_xn_rule_creator.constprop.0.isra.0+0x238/0x494 [<00000000cbda607b>] vcap_api_rule_remove_in_front_test+0x1ac/0x698 [<00000000c8766299>] kunit_try_run_case+0xe0/0x20c [<00000000c4fe9186>] kunit_generic_run_threadfn_adapter+0x50/0x94 [<00000000f6864acf>] kthread+0x2e8/0x374 [<0000000022e639b3>] ret_from_fork+0x10/0x20 Fixes: 814e7693207f ("net: microchip: vcap api: Add a storage state to a VCAP rule") Signed-off-by: Jinjie Ruan Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/microchip/vcap/vcap_api.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/microchip/vcap/vcap_api.c b/drivers/net/ethernet/microchip/vcap/vcap_api.c index 300fe1a93dce..ef980e4e5bc2 100644 --- a/drivers/net/ethernet/microchip/vcap/vcap_api.c +++ b/drivers/net/ethernet/microchip/vcap/vcap_api.c @@ -1021,18 +1021,32 @@ static struct vcap_rule_internal *vcap_dup_rule(struct vcap_rule_internal *ri, list_for_each_entry(ckf, &ri->data.keyfields, ctrl.list) { newckf = kmemdup(ckf, sizeof(*newckf), GFP_KERNEL); if (!newckf) - return ERR_PTR(-ENOMEM); + goto err; list_add_tail(&newckf->ctrl.list, &duprule->data.keyfields); } list_for_each_entry(caf, &ri->data.actionfields, ctrl.list) { newcaf = kmemdup(caf, sizeof(*newcaf), GFP_KERNEL); if (!newcaf) - return ERR_PTR(-ENOMEM); + goto err; list_add_tail(&newcaf->ctrl.list, &duprule->data.actionfields); } return duprule; + +err: + list_for_each_entry_safe(ckf, newckf, &duprule->data.keyfields, ctrl.list) { + list_del(&ckf->ctrl.list); + kfree(ckf); + } + + list_for_each_entry_safe(caf, newcaf, &duprule->data.actionfields, ctrl.list) { + list_del(&caf->ctrl.list); + kfree(caf); + } + + kfree(duprule); + return ERR_PTR(-ENOMEM); } static void vcap_apply_width(u8 *dst, int width, int bytes) -- cgit v1.2.3 From 88e69af061f2e061a68751ef9cad47a674527a1b Mon Sep 17 00:00:00 2001 From: Ratheesh Kannoth Date: Fri, 8 Sep 2023 08:23:09 +0530 Subject: octeontx2-pf: Fix page pool cache index corruption. The access to page pool `cache' array and the `count' variable is not locked. Page pool cache access is fine as long as there is only one consumer per pool. octeontx2 driver fills in rx buffers from page pool in NAPI context. If system is stressed and could not allocate buffers, refiiling work will be delegated to a delayed workqueue. This means that there are two cosumers to the page pool cache. Either workqueue or IRQ/NAPI can be run on other CPU. This will lead to lock less access, hence corruption of cache pool indexes. To fix this issue, NAPI is rescheduled from workqueue context to refill rx buffers. Fixes: b2e3406a38f0 ("octeontx2-pf: Add support for page pool") Signed-off-by: Ratheesh Kannoth Reported-by: Sebastian Andrzej Siewior Reviewed-by: Sebastian Andrzej Siewior Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c | 6 ++- drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h | 2 +- .../ethernet/marvell/octeontx2/nic/otx2_common.c | 43 +++------------------- .../ethernet/marvell/octeontx2/nic/otx2_common.h | 3 +- .../net/ethernet/marvell/octeontx2/nic/otx2_pf.c | 7 ++-- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.c | 30 ++++++++++++--- .../net/ethernet/marvell/octeontx2/nic/otx2_txrx.h | 4 +- 7 files changed, 44 insertions(+), 51 deletions(-) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c index 826f691de259..a4a258da8dd5 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.c @@ -107,12 +107,13 @@ int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura) } #define NPA_MAX_BURST 16 -void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) +int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) { struct otx2_nic *pfvf = dev; + int cnt = cq->pool_ptrs; u64 ptrs[NPA_MAX_BURST]; - int num_ptrs = 1; dma_addr_t bufptr; + int num_ptrs = 1; /* Refill pool with new buffers */ while (cq->pool_ptrs) { @@ -131,6 +132,7 @@ void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) num_ptrs = 1; } } + return cnt - cq->pool_ptrs; } void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h index 8ae96815865e..c1861f7de254 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/cn10k.h @@ -24,7 +24,7 @@ static inline int mtu_to_dwrr_weight(struct otx2_nic *pfvf, int mtu) return weight; } -void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); +int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx); int cn10k_sq_aq_init(void *dev, u16 qidx, u16 sqb_aura); int cn10k_lmtst_init(struct otx2_nic *pfvf); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c index 8511906cb4e2..997fedac3a98 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.c @@ -574,20 +574,8 @@ int otx2_alloc_rbuf(struct otx2_nic *pfvf, struct otx2_pool *pool, int otx2_alloc_buffer(struct otx2_nic *pfvf, struct otx2_cq_queue *cq, dma_addr_t *dma) { - if (unlikely(__otx2_alloc_rbuf(pfvf, cq->rbpool, dma))) { - struct refill_work *work; - struct delayed_work *dwork; - - work = &pfvf->refill_wrk[cq->cq_idx]; - dwork = &work->pool_refill_work; - /* Schedule a task if no other task is running */ - if (!cq->refill_task_sched) { - cq->refill_task_sched = true; - schedule_delayed_work(dwork, - msecs_to_jiffies(100)); - } + if (unlikely(__otx2_alloc_rbuf(pfvf, cq->rbpool, dma))) return -ENOMEM; - } return 0; } @@ -1082,39 +1070,20 @@ static int otx2_cq_init(struct otx2_nic *pfvf, u16 qidx) static void otx2_pool_refill_task(struct work_struct *work) { struct otx2_cq_queue *cq; - struct otx2_pool *rbpool; struct refill_work *wrk; - int qidx, free_ptrs = 0; struct otx2_nic *pfvf; - dma_addr_t bufptr; + int qidx; wrk = container_of(work, struct refill_work, pool_refill_work.work); pfvf = wrk->pf; qidx = wrk - pfvf->refill_wrk; cq = &pfvf->qset.cq[qidx]; - rbpool = cq->rbpool; - free_ptrs = cq->pool_ptrs; - while (cq->pool_ptrs) { - if (otx2_alloc_rbuf(pfvf, rbpool, &bufptr)) { - /* Schedule a WQ if we fails to free atleast half of the - * pointers else enable napi for this RQ. - */ - if (!((free_ptrs - cq->pool_ptrs) > free_ptrs / 2)) { - struct delayed_work *dwork; - - dwork = &wrk->pool_refill_work; - schedule_delayed_work(dwork, - msecs_to_jiffies(100)); - } else { - cq->refill_task_sched = false; - } - return; - } - pfvf->hw_ops->aura_freeptr(pfvf, qidx, bufptr + OTX2_HEAD_ROOM); - cq->pool_ptrs--; - } cq->refill_task_sched = false; + + local_bh_disable(); + napi_schedule(wrk->napi); + local_bh_enable(); } int otx2_config_nix_queues(struct otx2_nic *pfvf) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h index 4c6032ee7800..c04a8ee53a82 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_common.h @@ -302,6 +302,7 @@ struct flr_work { struct refill_work { struct delayed_work pool_refill_work; struct otx2_nic *pf; + struct napi_struct *napi; }; /* PTPv2 originTimestamp structure */ @@ -370,7 +371,7 @@ struct dev_hw_ops { int (*sq_aq_init)(void *dev, u16 qidx, u16 sqb_aura); void (*sqe_flush)(void *dev, struct otx2_snd_queue *sq, int size, int qidx); - void (*refill_pool_ptrs)(void *dev, struct otx2_cq_queue *cq); + int (*refill_pool_ptrs)(void *dev, struct otx2_cq_queue *cq); void (*aura_freeptr)(void *dev, int aura, u64 buf); }; diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c index 70b9065f7d10..6daf4d58c25d 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_pf.c @@ -1943,6 +1943,10 @@ int otx2_stop(struct net_device *netdev) netif_tx_disable(netdev); + for (wrk = 0; wrk < pf->qset.cq_cnt; wrk++) + cancel_delayed_work_sync(&pf->refill_wrk[wrk].pool_refill_work); + devm_kfree(pf->dev, pf->refill_wrk); + otx2_free_hw_resources(pf); otx2_free_cints(pf, pf->hw.cint_cnt); otx2_disable_napi(pf); @@ -1950,9 +1954,6 @@ int otx2_stop(struct net_device *netdev) for (qidx = 0; qidx < netdev->num_tx_queues; qidx++) netdev_tx_reset_queue(netdev_get_tx_queue(netdev, qidx)); - for (wrk = 0; wrk < pf->qset.cq_cnt; wrk++) - cancel_delayed_work_sync(&pf->refill_wrk[wrk].pool_refill_work); - devm_kfree(pf->dev, pf->refill_wrk); kfree(qset->sq); kfree(qset->cq); diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c index e369baf11530..e77d43848955 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.c @@ -424,9 +424,10 @@ process_cqe: return processed_cqe; } -void otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) +int otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) { struct otx2_nic *pfvf = dev; + int cnt = cq->pool_ptrs; dma_addr_t bufptr; while (cq->pool_ptrs) { @@ -435,6 +436,8 @@ void otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq) otx2_aura_freeptr(pfvf, cq->cq_idx, bufptr + OTX2_HEAD_ROOM); cq->pool_ptrs--; } + + return cnt - cq->pool_ptrs; } static int otx2_tx_napi_handler(struct otx2_nic *pfvf, @@ -521,6 +524,7 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) struct otx2_cq_queue *cq; struct otx2_qset *qset; struct otx2_nic *pfvf; + int filled_cnt = -1; cq_poll = container_of(napi, struct otx2_cq_poll, napi); pfvf = (struct otx2_nic *)cq_poll->dev; @@ -541,7 +545,7 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) } if (rx_cq && rx_cq->pool_ptrs) - pfvf->hw_ops->refill_pool_ptrs(pfvf, rx_cq); + filled_cnt = pfvf->hw_ops->refill_pool_ptrs(pfvf, rx_cq); /* Clear the IRQ */ otx2_write64(pfvf, NIX_LF_CINTX_INT(cq_poll->cint_idx), BIT_ULL(0)); @@ -561,9 +565,25 @@ int otx2_napi_handler(struct napi_struct *napi, int budget) otx2_config_irq_coalescing(pfvf, i); } - /* Re-enable interrupts */ - otx2_write64(pfvf, NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), - BIT_ULL(0)); + if (unlikely(!filled_cnt)) { + struct refill_work *work; + struct delayed_work *dwork; + + work = &pfvf->refill_wrk[cq->cq_idx]; + dwork = &work->pool_refill_work; + /* Schedule a task if no other task is running */ + if (!cq->refill_task_sched) { + work->napi = napi; + cq->refill_task_sched = true; + schedule_delayed_work(dwork, + msecs_to_jiffies(100)); + } + } else { + /* Re-enable interrupts */ + otx2_write64(pfvf, + NIX_LF_CINTX_ENA_W1S(cq_poll->cint_idx), + BIT_ULL(0)); + } } return workdone; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h index 9e3bfbe5c480..a82ffca8ce1b 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_txrx.h @@ -170,6 +170,6 @@ void cn10k_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx); void otx2_sqe_flush(void *dev, struct otx2_snd_queue *sq, int size, int qidx); -void otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); -void cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); +int otx2_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); +int cn10k_refill_pool_ptrs(void *dev, struct otx2_cq_queue *cq); #endif /* OTX2_TXRX_H */ -- cgit v1.2.3 From 6912e724832c47bb381eb1bd1e483ec8df0d0f0f Mon Sep 17 00:00:00 2001 From: Guangguan Wang Date: Fri, 8 Sep 2023 11:31:42 +0800 Subject: net/smc: bugfix for smcr v2 server connect success statistic In the macro SMC_STAT_SERV_SUCC_INC, the smcd_version is used to determin whether to increase the v1 statistic or the v2 statistic. It is correct for SMCD. But for SMCR, smcr_version should be used. Signed-off-by: Guangguan Wang Signed-off-by: David S. Miller --- net/smc/smc_stats.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/net/smc/smc_stats.h b/net/smc/smc_stats.h index b60fe1eb37ab..aa8928975cc6 100644 --- a/net/smc/smc_stats.h +++ b/net/smc/smc_stats.h @@ -243,8 +243,9 @@ while (0) #define SMC_STAT_SERV_SUCC_INC(net, _ini) \ do { \ typeof(_ini) i = (_ini); \ - bool is_v2 = (i->smcd_version & SMC_V2); \ bool is_smcd = (i->is_smcd); \ + u8 version = is_smcd ? i->smcd_version : i->smcr_version; \ + bool is_v2 = (version & SMC_V2); \ typeof(net->smc.smc_stats) smc_stats = (net)->smc.smc_stats; \ if (is_v2 && is_smcd) \ this_cpu_inc(smc_stats->smc[SMC_TYPE_D].srv_v2_succ_cnt); \ -- cgit v1.2.3 From f5146e3ef0a9eea405874b36178c19a4863b8989 Mon Sep 17 00:00:00 2001 From: Guangguan Wang Date: Fri, 8 Sep 2023 11:31:43 +0800 Subject: net/smc: use smc_lgr_list.lock to protect smc_lgr_list.list iterate in smcr_port_add While doing smcr_port_add, there maybe linkgroup add into or delete from smc_lgr_list.list at the same time, which may result kernel crash. So, use smc_lgr_list.lock to protect smc_lgr_list.list iterate in smcr_port_add. The crash calltrace show below: BUG: kernel NULL pointer dereference, address: 0000000000000000 PGD 0 P4D 0 Oops: 0000 [#1] SMP NOPTI CPU: 0 PID: 559726 Comm: kworker/0:92 Kdump: loaded Tainted: G Hardware name: Alibaba Cloud Alibaba Cloud ECS, BIOS 449e491 04/01/2014 Workqueue: events smc_ib_port_event_work [smc] RIP: 0010:smcr_port_add+0xa6/0xf0 [smc] RSP: 0000:ffffa5a2c8f67de0 EFLAGS: 00010297 RAX: 0000000000000001 RBX: ffff9935e0650000 RCX: 0000000000000000 RDX: 0000000000000010 RSI: ffff9935e0654290 RDI: ffff9935c8560000 RBP: 0000000000000000 R08: 0000000000000000 R09: ffff9934c0401918 R10: 0000000000000000 R11: ffffffffb4a5c278 R12: ffff99364029aae4 R13: ffff99364029aa00 R14: 00000000ffffffed R15: ffff99364029ab08 FS: 0000000000000000(0000) GS:ffff994380600000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000000 CR3: 0000000f06a10003 CR4: 0000000002770ef0 PKRU: 55555554 Call Trace: smc_ib_port_event_work+0x18f/0x380 [smc] process_one_work+0x19b/0x340 worker_thread+0x30/0x370 ? process_one_work+0x340/0x340 kthread+0x114/0x130 ? __kthread_cancel_work+0x50/0x50 ret_from_fork+0x1f/0x30 Fixes: 1f90a05d9ff9 ("net/smc: add smcr_port_add() and smcr_link_up() processing") Signed-off-by: Guangguan Wang Signed-off-by: David S. Miller --- net/smc/smc_core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index bd01dd31e4bd..d520ee62c8ec 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -1662,6 +1662,7 @@ void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport) { struct smc_link_group *lgr, *n; + spin_lock_bh(&smc_lgr_list.lock); list_for_each_entry_safe(lgr, n, &smc_lgr_list.list, list) { struct smc_link *link; @@ -1680,6 +1681,7 @@ void smcr_port_add(struct smc_ib_device *smcibdev, u8 ibport) if (link) smc_llc_add_link_local(link); } + spin_unlock_bh(&smc_lgr_list.lock); } /* link is down - switch connections to alternate link, -- cgit v1.2.3 From 24e0e61db3cb86a66824531989f1df80e0939f26 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Mon, 4 Sep 2023 22:42:56 +0200 Subject: ata: libata: disallow dev-initiated LPM transitions to unsupported states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In AHCI 1.3.1, the register description for CAP.SSC: "When cleared to ‘0’, software must not allow the HBA to initiate transitions to the Slumber state via agressive link power management nor the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port must be programmed to disallow device initiated Slumber requests." In AHCI 1.3.1, the register description for CAP.PSC: "When cleared to ‘0’, software must not allow the HBA to initiate transitions to the Partial state via agressive link power management nor the PxCMD.ICC field in each port, and the PxSCTL.IPM field in each port must be programmed to disallow device initiated Partial requests." Ensure that we always set the corresponding bits in PxSCTL.IPM, such that a device is not allowed to initiate transitions to power states which are unsupported by the HBA. DevSleep is always initiated by the HBA, however, for completeness, set the corresponding bit in PxSCTL.IPM such that agressive link power management cannot transition to DevSleep if DevSleep is not supported. sata_link_scr_lpm() is used by libahci, ata_piix and libata-pmp. However, only libahci has the ability to read the CAP/CAP2 register to see if these features are supported. Therefore, in order to not introduce any regressions on ata_piix or libata-pmp, create flags that indicate that the respective feature is NOT supported. This way, the behavior for ata_piix and libata-pmp should remain unchanged. This change is based on a patch originally submitted by Runa Guo-oc. Signed-off-by: Niklas Cassel Fixes: 1152b2617a6e ("libata: implement sata_link_scr_lpm() and make ata_dev_set_feature() global") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal --- drivers/ata/ahci.c | 9 +++++++++ drivers/ata/libata-sata.c | 19 ++++++++++++++++--- include/linux/libata.h | 4 ++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/drivers/ata/ahci.c b/drivers/ata/ahci.c index abb5911c9d09..08745e7db820 100644 --- a/drivers/ata/ahci.c +++ b/drivers/ata/ahci.c @@ -1883,6 +1883,15 @@ static int ahci_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) else dev_info(&pdev->dev, "SSS flag set, parallel bus scan disabled\n"); + if (!(hpriv->cap & HOST_CAP_PART)) + host->flags |= ATA_HOST_NO_PART; + + if (!(hpriv->cap & HOST_CAP_SSC)) + host->flags |= ATA_HOST_NO_SSC; + + if (!(hpriv->cap2 & HOST_CAP2_SDS)) + host->flags |= ATA_HOST_NO_DEVSLP; + if (pi.flags & ATA_FLAG_EM) ahci_reset_em(host); diff --git a/drivers/ata/libata-sata.c b/drivers/ata/libata-sata.c index 5d31c08be013..a701e1538482 100644 --- a/drivers/ata/libata-sata.c +++ b/drivers/ata/libata-sata.c @@ -396,10 +396,23 @@ int sata_link_scr_lpm(struct ata_link *link, enum ata_lpm_policy policy, case ATA_LPM_MED_POWER_WITH_DIPM: case ATA_LPM_MIN_POWER_WITH_PARTIAL: case ATA_LPM_MIN_POWER: - if (ata_link_nr_enabled(link) > 0) - /* no restrictions on LPM transitions */ + if (ata_link_nr_enabled(link) > 0) { + /* assume no restrictions on LPM transitions */ scontrol &= ~(0x7 << 8); - else { + + /* + * If the controller does not support partial, slumber, + * or devsleep, then disallow these transitions. + */ + if (link->ap->host->flags & ATA_HOST_NO_PART) + scontrol |= (0x1 << 8); + + if (link->ap->host->flags & ATA_HOST_NO_SSC) + scontrol |= (0x2 << 8); + + if (link->ap->host->flags & ATA_HOST_NO_DEVSLP) + scontrol |= (0x4 << 8); + } else { /* empty port, power off */ scontrol &= ~0xf; scontrol |= (0x1 << 2); diff --git a/include/linux/libata.h b/include/linux/libata.h index 52d58b13e5ee..bf4913f4d7ac 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -222,6 +222,10 @@ enum { ATA_HOST_PARALLEL_SCAN = (1 << 2), /* Ports on this host can be scanned in parallel */ ATA_HOST_IGNORE_ATA = (1 << 3), /* Ignore ATA devices on this host. */ + ATA_HOST_NO_PART = (1 << 4), /* Host does not support partial */ + ATA_HOST_NO_SSC = (1 << 5), /* Host does not support slumber */ + ATA_HOST_NO_DEVSLP = (1 << 6), /* Host does not support devslp */ + /* bits 24:31 of host->flags are reserved for LLD specific flags */ /* various lengths of time */ -- cgit v1.2.3 From e97eb65dd464e7f118a16a26337322d07eb653e2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Mon, 4 Sep 2023 21:54:36 +0200 Subject: ata: sata_mv: Fix incorrect string length computation in mv_dump_mem() snprintf() returns the "number of characters which *would* be generated for the given input", not the size *really* generated. In order to avoid too large values for 'o' (and potential negative values for "sizeof(linebuf) o") use scnprintf() instead of snprintf(). Note that given the "w < 4" in the for loop, the buffer can NOT overflow, but using the *right* function is always better. Signed-off-by: Christophe JAILLET Signed-off-by: Damien Le Moal --- drivers/ata/sata_mv.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index d105db5c7d81..45e48d653c60 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c @@ -1255,8 +1255,8 @@ static void mv_dump_mem(struct device *dev, void __iomem *start, unsigned bytes) for (b = 0; b < bytes; ) { for (w = 0, o = 0; b < bytes && w < 4; w++) { - o += snprintf(linebuf + o, sizeof(linebuf) - o, - "%08x ", readl(start + b)); + o += scnprintf(linebuf + o, sizeof(linebuf) - o, + "%08x ", readl(start + b)); b += sizeof(u32); } dev_dbg(dev, "%s: %p: %s\n", -- cgit v1.2.3 From 762f169f5d9b92f057ad2c27ec4e3849b743239a Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 7 Aug 2023 18:21:20 +0200 Subject: efi/x86: Move EFI runtime call setup/teardown helpers out of line Only the arch_efi_call_virt() macro that some architectures override needs to be a macro, given that it is variadic and encapsulates calls via function pointers that have different prototypes. The associated setup and teardown code are not special in this regard, and don't need to be instantiated at each call site. So turn them into ordinary C functions and move them out of line. Signed-off-by: Ard Biesheuvel --- arch/x86/include/asm/efi.h | 32 ++------------------------------ arch/x86/platform/efi/efi_32.c | 12 ++++++++++++ arch/x86/platform/efi/efi_64.c | 19 +++++++++++++++++-- 3 files changed, 31 insertions(+), 32 deletions(-) diff --git a/arch/x86/include/asm/efi.h b/arch/x86/include/asm/efi.h index b0994ae3bc23..c4555b269a1b 100644 --- a/arch/x86/include/asm/efi.h +++ b/arch/x86/include/asm/efi.h @@ -91,19 +91,6 @@ static inline void efi_fpu_end(void) #ifdef CONFIG_X86_32 #define EFI_X86_KERNEL_ALLOC_LIMIT (SZ_512M - 1) - -#define arch_efi_call_virt_setup() \ -({ \ - efi_fpu_begin(); \ - firmware_restrict_branch_speculation_start(); \ -}) - -#define arch_efi_call_virt_teardown() \ -({ \ - firmware_restrict_branch_speculation_end(); \ - efi_fpu_end(); \ -}) - #else /* !CONFIG_X86_32 */ #define EFI_X86_KERNEL_ALLOC_LIMIT EFI_ALLOC_LIMIT @@ -116,14 +103,6 @@ extern bool efi_disable_ibt_for_runtime; __efi_call(__VA_ARGS__); \ }) -#define arch_efi_call_virt_setup() \ -({ \ - efi_sync_low_kernel_mappings(); \ - efi_fpu_begin(); \ - firmware_restrict_branch_speculation_start(); \ - efi_enter_mm(); \ -}) - #undef arch_efi_call_virt #define arch_efi_call_virt(p, f, args...) ({ \ u64 ret, ibt = ibt_save(efi_disable_ibt_for_runtime); \ @@ -132,13 +111,6 @@ extern bool efi_disable_ibt_for_runtime; ret; \ }) -#define arch_efi_call_virt_teardown() \ -({ \ - efi_leave_mm(); \ - firmware_restrict_branch_speculation_end(); \ - efi_fpu_end(); \ -}) - #ifdef CONFIG_KASAN /* * CONFIG_KASAN may redefine memset to __memset. __memset function is present @@ -168,8 +140,8 @@ extern void efi_delete_dummy_variable(void); extern void efi_crash_gracefully_on_page_fault(unsigned long phys_addr); extern void efi_free_boot_services(void); -void efi_enter_mm(void); -void efi_leave_mm(void); +void arch_efi_call_virt_setup(void); +void arch_efi_call_virt_teardown(void); /* kexec external ABI */ struct efi_setup_data { diff --git a/arch/x86/platform/efi/efi_32.c b/arch/x86/platform/efi/efi_32.c index e06a199423c0..b2cc7b4552a1 100644 --- a/arch/x86/platform/efi/efi_32.c +++ b/arch/x86/platform/efi/efi_32.c @@ -140,3 +140,15 @@ void __init efi_runtime_update_mappings(void) } } } + +void arch_efi_call_virt_setup(void) +{ + efi_fpu_begin(); + firmware_restrict_branch_speculation_start(); +} + +void arch_efi_call_virt_teardown(void) +{ + firmware_restrict_branch_speculation_end(); + efi_fpu_end(); +} diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c index 77f7ac3668cb..91d31ac422d6 100644 --- a/arch/x86/platform/efi/efi_64.c +++ b/arch/x86/platform/efi/efi_64.c @@ -474,19 +474,34 @@ void __init efi_dump_pagetable(void) * can not change under us. * It should be ensured that there are no concurrent calls to this function. */ -void efi_enter_mm(void) +static void efi_enter_mm(void) { efi_prev_mm = current->active_mm; current->active_mm = &efi_mm; switch_mm(efi_prev_mm, &efi_mm, NULL); } -void efi_leave_mm(void) +static void efi_leave_mm(void) { current->active_mm = efi_prev_mm; switch_mm(&efi_mm, efi_prev_mm, NULL); } +void arch_efi_call_virt_setup(void) +{ + efi_sync_low_kernel_mappings(); + efi_fpu_begin(); + firmware_restrict_branch_speculation_start(); + efi_enter_mm(); +} + +void arch_efi_call_virt_teardown(void) +{ + efi_leave_mm(); + firmware_restrict_branch_speculation_end(); + efi_fpu_end(); +} + static DEFINE_SPINLOCK(efi_runtime_lock); /* -- cgit v1.2.3 From aba7e066c738d4b349413a271b2a236aa55bacbc Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 2 Aug 2023 17:17:04 +0200 Subject: efi/x86: Ensure that EFI_RUNTIME_MAP is enabled for kexec CONFIG_EFI_RUNTIME_MAP needs to be enabled in order for kexec to be able to provide the required information about the EFI runtime mappings to the incoming kernel, regardless of whether kexec_load() or kexec_file_load() is being used. Without this information, kexec boot in EFI mode is not possible. The CONFIG_EFI_RUNTIME_MAP option is currently directly configurable if CONFIG_EXPERT is enabled, so that it can be turned on for debugging purposes even if KEXEC is not enabled. However, the upshot of this is that it can also be disabled even when it shouldn't. So tweak the Kconfig declarations to avoid this situation. Reported-by: Kirill A. Shutemov Signed-off-by: Ard Biesheuvel --- arch/x86/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 982b777eadc7..66bfabae8814 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -1945,6 +1945,7 @@ config EFI select UCS2_STRING select EFI_RUNTIME_WRAPPERS select ARCH_USE_MEMREMAP_PROT + select EFI_RUNTIME_MAP if KEXEC_CORE help This enables the kernel to use EFI runtime services that are available (such as the EFI variable services). @@ -2020,7 +2021,6 @@ config EFI_MAX_FAKE_MEM config EFI_RUNTIME_MAP bool "Export EFI runtime maps to sysfs" if EXPERT depends on EFI - default KEXEC_CORE help Export EFI runtime memory regions to /sys/firmware/efi/runtime-map. That memory map is required by the 2nd kernel to set up EFI virtual -- cgit v1.2.3 From e7761d827e99919c32400056a884e481ef008ec4 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Wed, 16 Aug 2023 21:05:57 +0200 Subject: efi/unaccepted: Use ACPI reclaim memory for unaccepted memory table Kyril reports that crashkernels fail to work on confidential VMs that rely on the unaccepted memory table, and this appears to be caused by the fact that it is not considered part of the set of firmware tables that the crashkernel needs to map. This is an oversight, and a result of the use of the EFI_LOADER_DATA memory type for this table. The correct memory type to use for any firmware table is EFI_ACPI_RECLAIM_MEMORY (including ones created by the EFI stub), even though the name suggests that is it specific to ACPI. ACPI reclaim means that the memory is used by the firmware to expose information to the operating system, but that the memory region has no special significance to the firmware itself, and the OS is free to reclaim the memory and use it as ordinary memory if it is not interested in the contents, or if it has already consumed them. In Linux, this memory is never reclaimed, but it is always covered by the kernel direct map and generally made accessible as ordinary memory. On x86, ACPI reclaim memory is translated into E820_ACPI, which the kexec logic already recognizes as memory that the crashkernel may need to to access, and so it will be mapped and accessible to the booting crash kernel. Fixes: 745e3ed85f71 ("efi/libstub: Implement support for unaccepted memory") Reported-by: Kirill A. Shutemov Signed-off-by: Ard Biesheuvel --- drivers/firmware/efi/libstub/unaccepted_memory.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/efi/libstub/unaccepted_memory.c b/drivers/firmware/efi/libstub/unaccepted_memory.c index ca61f4733ea5..9a655f30ba47 100644 --- a/drivers/firmware/efi/libstub/unaccepted_memory.c +++ b/drivers/firmware/efi/libstub/unaccepted_memory.c @@ -62,7 +62,7 @@ efi_status_t allocate_unaccepted_bitmap(__u32 nr_desc, bitmap_size = DIV_ROUND_UP(unaccepted_end - unaccepted_start, EFI_UNACCEPTED_UNIT_SIZE * BITS_PER_BYTE); - status = efi_bs_call(allocate_pool, EFI_LOADER_DATA, + status = efi_bs_call(allocate_pool, EFI_ACPI_RECLAIM_MEMORY, sizeof(*unaccepted_table) + bitmap_size, (void **)&unaccepted_table); if (status != EFI_SUCCESS) { -- cgit v1.2.3 From fa60b8163816f194786f3ee334c9a458da7699c6 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 7 Sep 2023 12:46:31 +0200 Subject: net: stmmac: fix handling of zero coalescing tx-usecs Setting ethtool -C eth0 tx-usecs 0 is supposed to disable the use of the coalescing timer but currently it gets programmed with zero delay instead. Disable the use of the coalescing timer if tx-usecs is zero by preventing it from being restarted. Note that to keep things simple we don't start/stop the timer when the coalescing settings are changed, but just let that happen on the next transmit or timer expiry. Fixes: 8fce33317023 ("net: stmmac: Rework coalesce timer and fix multi-queue races") Signed-off-by: Vincent Whitchurch Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 9a3182b9e767..2206789802bf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -2704,9 +2704,7 @@ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) /* We still have pending packets, let's call for a new scheduling */ if (tx_q->dirty_tx != tx_q->cur_tx) - hrtimer_start(&tx_q->txtimer, - STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), - HRTIMER_MODE_REL); + stmmac_tx_timer_arm(priv, queue); flags = u64_stats_update_begin_irqsave(&tx_q->txq_stats.syncp); tx_q->txq_stats.tx_packets += tx_packets; @@ -2995,9 +2993,13 @@ static int stmmac_init_dma_engine(struct stmmac_priv *priv) static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) { struct stmmac_tx_queue *tx_q = &priv->dma_conf.tx_queue[queue]; + u32 tx_coal_timer = priv->tx_coal_timer[queue]; + + if (!tx_coal_timer) + return; hrtimer_start(&tx_q->txtimer, - STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), + STMMAC_COAL_TIMER(tx_coal_timer), HRTIMER_MODE_REL); } -- cgit v1.2.3 From 9b90aca97f6d5255ca41e716720d138b878cd034 Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Fri, 8 Sep 2023 14:19:48 +0800 Subject: net: ethernet: bcmasp: fix possible OOB write in bcmasp_netfilt_get_all_active() rule_locs is allocated in ethtool_get_rxnfc and the size is determined by rule_cnt from user space. So rule_cnt needs to be check before using rule_locs to avoid OOB writing or NULL pointer dereference. Fixes: c5d511c49587 ("net: bcmasp: Add support for wake on net filters") Signed-off-by: Hangyu Hua Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/broadcom/asp2/bcmasp.c | 9 +++++++-- drivers/net/ethernet/broadcom/asp2/bcmasp.h | 4 ++-- drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.c b/drivers/net/ethernet/broadcom/asp2/bcmasp.c index f048e3d45119..41a6098eb0c2 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.c @@ -528,13 +528,16 @@ void bcmasp_netfilt_suspend(struct bcmasp_intf *intf) ASP_RX_FILTER_BLK_CTRL); } -void bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, - u32 *rule_cnt) +int bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, + u32 *rule_cnt) { struct bcmasp_priv *priv = intf->parent; int j = 0, i; for (i = 0; i < NUM_NET_FILTERS; i++) { + if (j == *rule_cnt) + return -EMSGSIZE; + if (!priv->net_filters[i].claimed || priv->net_filters[i].port != intf->port) continue; @@ -548,6 +551,8 @@ void bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, } *rule_cnt = j; + + return 0; } int bcmasp_netfilt_get_active(struct bcmasp_intf *intf) diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp.h b/drivers/net/ethernet/broadcom/asp2/bcmasp.h index 5b512f7f5e94..ec90add6b03e 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp.h +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp.h @@ -577,8 +577,8 @@ void bcmasp_netfilt_release(struct bcmasp_intf *intf, int bcmasp_netfilt_get_active(struct bcmasp_intf *intf); -void bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, - u32 *rule_cnt); +int bcmasp_netfilt_get_all_active(struct bcmasp_intf *intf, u32 *rule_locs, + u32 *rule_cnt); void bcmasp_netfilt_suspend(struct bcmasp_intf *intf); diff --git a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c index c4f1604d5ab3..ce6a3d56fb23 100644 --- a/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c +++ b/drivers/net/ethernet/broadcom/asp2/bcmasp_ethtool.c @@ -335,7 +335,7 @@ static int bcmasp_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *cmd, err = bcmasp_flow_get(intf, cmd); break; case ETHTOOL_GRXCLSRLALL: - bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); + err = bcmasp_netfilt_get_all_active(intf, rule_locs, &cmd->rule_cnt); cmd->data = NUM_NET_FILTERS; break; default: -- cgit v1.2.3 From 51fe0a470543f345e3c62b6798929de3ddcedc1d Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Fri, 8 Sep 2023 14:19:49 +0800 Subject: net: ethernet: mvpp2_main: fix possible OOB write in mvpp2_ethtool_get_rxnfc() rules is allocated in ethtool_get_rxnfc and the size is determined by rule_cnt from user space. So rule_cnt needs to be check before using rules to avoid OOB writing or NULL pointer dereference. Fixes: 90b509b39ac9 ("net: mvpp2: cls: Add Classification offload support") Signed-off-by: Hangyu Hua Reviewed-by: Marcin Wojtas Reviewed-by: Russell King (Oracle) Signed-off-by: David S. Miller --- drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index eb74ccddb440..21c3f9b015c8 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5586,6 +5586,11 @@ static int mvpp2_ethtool_get_rxnfc(struct net_device *dev, break; case ETHTOOL_GRXCLSRLALL: for (i = 0; i < MVPP2_N_RFS_ENTRIES_PER_FLOW; i++) { + if (loc == info->rule_cnt) { + ret = -EMSGSIZE; + break; + } + if (port->rfs_rules[i]) rules[loc++] = i; } -- cgit v1.2.3 From e4c79810755f66c9a933ca810da2724133b1165a Mon Sep 17 00:00:00 2001 From: Hangyu Hua Date: Fri, 8 Sep 2023 14:19:50 +0800 Subject: net: ethernet: mtk_eth_soc: fix possible NULL pointer dereference in mtk_hwlro_get_fdir_all() rule_locs is allocated in ethtool_get_rxnfc and the size is determined by rule_cnt from user space. So rule_cnt needs to be check before using rule_locs to avoid NULL pointer dereference. Fixes: 7aab747e5563 ("net: ethernet: mediatek: add ethtool functions to configure RX flows of HW LRO") Signed-off-by: Hangyu Hua Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 6ad42e3b488f..2372ce8c2580 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -2994,6 +2994,9 @@ static int mtk_hwlro_get_fdir_all(struct net_device *dev, int i; for (i = 0; i < MTK_MAX_LRO_IP_CNT; i++) { + if (cnt == cmd->rule_cnt) + return -EMSGSIZE; + if (mac->hwlro_ip[i]) { rule_locs[cnt] = i; cnt++; -- cgit v1.2.3 From 484b4833c604c0adcf19eac1ca14b60b757355b5 Mon Sep 17 00:00:00 2001 From: Ziyang Xuan Date: Fri, 8 Sep 2023 18:17:52 +0800 Subject: hsr: Fix uninit-value access in fill_frame_info() Syzbot reports the following uninit-value access problem. ===================================================== BUG: KMSAN: uninit-value in fill_frame_info net/hsr/hsr_forward.c:601 [inline] BUG: KMSAN: uninit-value in hsr_forward_skb+0x9bd/0x30f0 net/hsr/hsr_forward.c:616 fill_frame_info net/hsr/hsr_forward.c:601 [inline] hsr_forward_skb+0x9bd/0x30f0 net/hsr/hsr_forward.c:616 hsr_dev_xmit+0x192/0x330 net/hsr/hsr_device.c:223 __netdev_start_xmit include/linux/netdevice.h:4889 [inline] netdev_start_xmit include/linux/netdevice.h:4903 [inline] xmit_one net/core/dev.c:3544 [inline] dev_hard_start_xmit+0x247/0xa10 net/core/dev.c:3560 __dev_queue_xmit+0x34d0/0x52a0 net/core/dev.c:4340 dev_queue_xmit include/linux/netdevice.h:3082 [inline] packet_xmit+0x9c/0x6b0 net/packet/af_packet.c:276 packet_snd net/packet/af_packet.c:3087 [inline] packet_sendmsg+0x8b1d/0x9f30 net/packet/af_packet.c:3119 sock_sendmsg_nosec net/socket.c:730 [inline] sock_sendmsg net/socket.c:753 [inline] __sys_sendto+0x781/0xa30 net/socket.c:2176 __do_sys_sendto net/socket.c:2188 [inline] __se_sys_sendto net/socket.c:2184 [inline] __ia32_sys_sendto+0x11f/0x1c0 net/socket.c:2184 do_syscall_32_irqs_on arch/x86/entry/common.c:112 [inline] __do_fast_syscall_32+0xa2/0x100 arch/x86/entry/common.c:178 do_fast_syscall_32+0x37/0x80 arch/x86/entry/common.c:203 do_SYSENTER_32+0x1f/0x30 arch/x86/entry/common.c:246 entry_SYSENTER_compat_after_hwframe+0x70/0x82 Uninit was created at: slab_post_alloc_hook+0x12f/0xb70 mm/slab.h:767 slab_alloc_node mm/slub.c:3478 [inline] kmem_cache_alloc_node+0x577/0xa80 mm/slub.c:3523 kmalloc_reserve+0x148/0x470 net/core/skbuff.c:559 __alloc_skb+0x318/0x740 net/core/skbuff.c:644 alloc_skb include/linux/skbuff.h:1286 [inline] alloc_skb_with_frags+0xc8/0xbd0 net/core/skbuff.c:6299 sock_alloc_send_pskb+0xa80/0xbf0 net/core/sock.c:2794 packet_alloc_skb net/packet/af_packet.c:2936 [inline] packet_snd net/packet/af_packet.c:3030 [inline] packet_sendmsg+0x70e8/0x9f30 net/packet/af_packet.c:3119 sock_sendmsg_nosec net/socket.c:730 [inline] sock_sendmsg net/socket.c:753 [inline] __sys_sendto+0x781/0xa30 net/socket.c:2176 __do_sys_sendto net/socket.c:2188 [inline] __se_sys_sendto net/socket.c:2184 [inline] __ia32_sys_sendto+0x11f/0x1c0 net/socket.c:2184 do_syscall_32_irqs_on arch/x86/entry/common.c:112 [inline] __do_fast_syscall_32+0xa2/0x100 arch/x86/entry/common.c:178 do_fast_syscall_32+0x37/0x80 arch/x86/entry/common.c:203 do_SYSENTER_32+0x1f/0x30 arch/x86/entry/common.c:246 entry_SYSENTER_compat_after_hwframe+0x70/0x82 It is because VLAN not yet supported in hsr driver. Return error when protocol is ETH_P_8021Q in fill_frame_info() now to fix it. Fixes: 451d8123f897 ("net: prp: add packet handling support") Reported-by: syzbot+bf7e6250c7ce248f3ec9@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=bf7e6250c7ce248f3ec9 Signed-off-by: Ziyang Xuan Signed-off-by: David S. Miller --- net/hsr/hsr_forward.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/hsr/hsr_forward.c b/net/hsr/hsr_forward.c index 629daacc9607..b71dab630a87 100644 --- a/net/hsr/hsr_forward.c +++ b/net/hsr/hsr_forward.c @@ -594,6 +594,7 @@ static int fill_frame_info(struct hsr_frame_info *frame, proto = vlan_hdr->vlanhdr.h_vlan_encapsulated_proto; /* FIXME: */ netdev_warn_once(skb->dev, "VLAN not yet supported"); + return -EINVAL; } frame->is_from_san = false; -- cgit v1.2.3 From 32530dba1bd48da4437d18d9a8dbc9d2826938a6 Mon Sep 17 00:00:00 2001 From: Ciprian Regus Date: Fri, 8 Sep 2023 15:58:08 +0300 Subject: net:ethernet:adi:adin1110: Fix forwarding offload Currently, when a new fdb entry is added (with both ports of the ADIN2111 bridged), the driver configures the MAC filters for the wrong port, which results in the forwarding being done by the host, and not actually hardware offloaded. The ADIN2111 offloads the forwarding by setting filters on the destination MAC address of incoming frames. Based on these, they may be routed to the other port. Thus, if a frame has to be forwarded from port 1 to port 2, the required configuration for the ADDR_FILT_UPRn register should set the APPLY2PORT1 bit (instead of APPLY2PORT2, as it's currently the case). Fixes: bc93e19d088b ("net: ethernet: adi: Add ADIN1110 support") Signed-off-by: Ciprian Regus Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/adi/adin1110.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/ethernet/adi/adin1110.c b/drivers/net/ethernet/adi/adin1110.c index 1c009b485188..ca66b747b7c5 100644 --- a/drivers/net/ethernet/adi/adin1110.c +++ b/drivers/net/ethernet/adi/adin1110.c @@ -1385,7 +1385,7 @@ static int adin1110_fdb_add(struct adin1110_port_priv *port_priv, return -ENOMEM; other_port = priv->ports[!port_priv->nr]; - port_rules = adin1110_port_rules(port_priv, false, true); + port_rules = adin1110_port_rules(other_port, false, true); eth_broadcast_addr(mask); return adin1110_write_mac_address(other_port, mac_nr, (u8 *)fdb->addr, -- cgit v1.2.3 From 02c652f5465011126152bbd93b6a582a1d0c32f1 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 8 Sep 2023 16:33:48 +0300 Subject: net: dsa: sja1105: hide all multicast addresses from "bridge fdb show" Commit 4d9423549501 ("net: dsa: sja1105: offload bridge port flags to device") has partially hidden some multicast entries from showing up in the "bridge fdb show" output, but it wasn't enough. Addresses which are added through "bridge mdb add" still show up. Hide them all. Fixes: 291d1e72b756 ("net: dsa: sja1105: Add support for FDB and MDB management") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_main.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index a23d980d28f5..11c917d5ce43 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1868,13 +1868,14 @@ static int sja1105_fdb_dump(struct dsa_switch *ds, int port, if (!(l2_lookup.destports & BIT(port))) continue; - /* We need to hide the FDB entry for unknown multicast */ - if (l2_lookup.macaddr == SJA1105_UNKNOWN_MULTICAST && - l2_lookup.mask_macaddr == SJA1105_UNKNOWN_MULTICAST) - continue; - u64_to_ether_addr(l2_lookup.macaddr, macaddr); + /* Hardware FDB is shared for fdb and mdb, "bridge fdb show" + * only wants to see unicast + */ + if (is_multicast_ether_addr(macaddr)) + continue; + /* We need to hide the dsa_8021q VLANs from the user. */ if (vid_is_dsa_8021q(l2_lookup.vlanid)) l2_lookup.vlanid = 0; -- cgit v1.2.3 From c956798062b5a308db96e75157747291197f0378 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 8 Sep 2023 16:33:49 +0300 Subject: net: dsa: sja1105: propagate exact error code from sja1105_dynamic_config_poll_valid() Currently, sja1105_dynamic_config_wait_complete() returns either 0 or -ETIMEDOUT, because it just looks at the read_poll_timeout() return code. There will be future changes which move some more checks to sja1105_dynamic_config_poll_valid(). It is important that we propagate their exact return code (-ENOENT, -EINVAL), because callers of sja1105_dynamic_config_read() depend on them. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_dynamic_config.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 7729d3f8b7f5..93d47dab8d3e 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -1211,13 +1211,14 @@ sja1105_dynamic_config_wait_complete(struct sja1105_private *priv, struct sja1105_dyn_cmd *cmd, const struct sja1105_dynamic_table_ops *ops) { - int rc; - - return read_poll_timeout(sja1105_dynamic_config_poll_valid, - rc, rc != -EAGAIN, - SJA1105_DYNAMIC_CONFIG_SLEEP_US, - SJA1105_DYNAMIC_CONFIG_TIMEOUT_US, - false, priv, cmd, ops); + int err, rc; + + err = read_poll_timeout(sja1105_dynamic_config_poll_valid, + rc, rc != -EAGAIN, + SJA1105_DYNAMIC_CONFIG_SLEEP_US, + SJA1105_DYNAMIC_CONFIG_TIMEOUT_US, + false, priv, cmd, ops); + return err < 0 ? err : rc; } /* Provides read access to the settings through the dynamic interface -- cgit v1.2.3 From 7cef293b9a634a05fcce9e1df4aee3aeed023345 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 8 Sep 2023 16:33:50 +0300 Subject: net: dsa: sja1105: fix multicast forwarding working only for last added mdb entry The commit cited in Fixes: did 2 things: it refactored the read-back polling from sja1105_dynamic_config_read() into a new function, sja1105_dynamic_config_wait_complete(), and it called that from sja1105_dynamic_config_write() too. What is problematic is the refactoring. The refactored code from sja1105_dynamic_config_poll_valid() works like the previous one, but the problem is that it uses another packed_buf[] SPI buffer, and there was code at the end of sja1105_dynamic_config_read() which was relying on the read-back packed_buf[]: /* Don't dereference possibly NULL pointer - maybe caller * only wanted to see whether the entry existed or not. */ if (entry) ops->entry_packing(packed_buf, entry, UNPACK); After the change, the packed_buf[] that this code sees is no longer the entry read back from hardware, but the original entry that the caller passed to the sja1105_dynamic_config_read(), packed into this buffer. This difference is the most notable with the SJA1105_SEARCH uses from sja1105pqrs_fdb_add() - used for both fdb and mdb. There, we have logic added by commit 728db843df88 ("net: dsa: sja1105: ignore the FDB entry for unknown multicast when adding a new address") to figure out whether the address we're trying to add matches on any existing hardware entry, with the exception of the catch-all multicast address. That logic was broken, because with sja1105_dynamic_config_read() not working properly, it doesn't return us the entry read back from hardware, but the entry that we passed to it. And, since for multicast, a match will always exist, it will tell us that any mdb entry already exists at index=0 L2 Address Lookup table. It is index=0 because the caller doesn't know the index - it wants to find it out, and sja1105_dynamic_config_read() does: if (index < 0) { // SJA1105_SEARCH /* Avoid copying a signed negative number to an u64 */ cmd.index = 0; // <- this cmd.search = true; } else { cmd.index = index; cmd.search = false; } So, to the caller of sja1105_dynamic_config_read(), the returned info looks entirely legit, and it will add all mdb entries to FDB index 0. There, they will always overwrite each other (not to mention, potentially they can also overwrite a pre-existing bridge fdb entry), and the user-visible impact will be that only the last mdb entry will be forwarded as it should. The others won't (will be flooded or dropped, depending on the egress flood settings). Fixing is a bit more complicated, and involves either passing the same packed_buf[] to sja1105_dynamic_config_wait_complete(), or moving all the extra processing on the packed_buf[] to sja1105_dynamic_config_wait_complete(). I've opted for the latter, because it makes sja1105_dynamic_config_wait_complete() a bit more self-contained. Fixes: df405910ab9f ("net: dsa: sja1105: wait for dynamic config command completion on writes too") Reported-by: Yanan Yang Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_dynamic_config.c | 80 +++++++++++------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c index 93d47dab8d3e..984c0e604e8d 100644 --- a/drivers/net/dsa/sja1105/sja1105_dynamic_config.c +++ b/drivers/net/dsa/sja1105/sja1105_dynamic_config.c @@ -1175,18 +1175,15 @@ const struct sja1105_dynamic_table_ops sja1110_dyn_ops[BLK_IDX_MAX_DYN] = { static int sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, - struct sja1105_dyn_cmd *cmd, - const struct sja1105_dynamic_table_ops *ops) + const struct sja1105_dynamic_table_ops *ops, + void *entry, bool check_valident, + bool check_errors) { u8 packed_buf[SJA1105_MAX_DYN_CMD_SIZE] = {}; + struct sja1105_dyn_cmd cmd = {}; int rc; - /* We don't _need_ to read the full entry, just the command area which - * is a fixed SJA1105_SIZE_DYN_CMD. But our cmd_packing() API expects a - * buffer that contains the full entry too. Additionally, our API - * doesn't really know how many bytes into the buffer does the command - * area really begin. So just read back the whole entry. - */ + /* Read back the whole entry + command structure. */ rc = sja1105_xfer_buf(priv, SPI_READ, ops->addr, packed_buf, ops->packed_size); if (rc) @@ -1195,11 +1192,25 @@ sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, /* Unpack the command structure, and return it to the caller in case it * needs to perform further checks on it (VALIDENT). */ - memset(cmd, 0, sizeof(*cmd)); - ops->cmd_packing(packed_buf, cmd, UNPACK); + ops->cmd_packing(packed_buf, &cmd, UNPACK); /* Hardware hasn't cleared VALID => still working on it */ - return cmd->valid ? -EAGAIN : 0; + if (cmd.valid) + return -EAGAIN; + + if (check_valident && !cmd.valident && !(ops->access & OP_VALID_ANYWAY)) + return -ENOENT; + + if (check_errors && cmd.errors) + return -EINVAL; + + /* Don't dereference possibly NULL pointer - maybe caller + * only wanted to see whether the entry existed or not. + */ + if (entry) + ops->entry_packing(packed_buf, entry, UNPACK); + + return 0; } /* Poll the dynamic config entry's control area until the hardware has @@ -1208,8 +1219,9 @@ sja1105_dynamic_config_poll_valid(struct sja1105_private *priv, */ static int sja1105_dynamic_config_wait_complete(struct sja1105_private *priv, - struct sja1105_dyn_cmd *cmd, - const struct sja1105_dynamic_table_ops *ops) + const struct sja1105_dynamic_table_ops *ops, + void *entry, bool check_valident, + bool check_errors) { int err, rc; @@ -1217,7 +1229,8 @@ sja1105_dynamic_config_wait_complete(struct sja1105_private *priv, rc, rc != -EAGAIN, SJA1105_DYNAMIC_CONFIG_SLEEP_US, SJA1105_DYNAMIC_CONFIG_TIMEOUT_US, - false, priv, cmd, ops); + false, priv, ops, entry, check_valident, + check_errors); return err < 0 ? err : rc; } @@ -1287,25 +1300,14 @@ int sja1105_dynamic_config_read(struct sja1105_private *priv, mutex_lock(&priv->dynamic_config_lock); rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf, ops->packed_size); - if (rc < 0) { - mutex_unlock(&priv->dynamic_config_lock); - return rc; - } - - rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops); - mutex_unlock(&priv->dynamic_config_lock); if (rc < 0) - return rc; + goto out; - if (!cmd.valident && !(ops->access & OP_VALID_ANYWAY)) - return -ENOENT; + rc = sja1105_dynamic_config_wait_complete(priv, ops, entry, true, false); +out: + mutex_unlock(&priv->dynamic_config_lock); - /* Don't dereference possibly NULL pointer - maybe caller - * only wanted to see whether the entry existed or not. - */ - if (entry) - ops->entry_packing(packed_buf, entry, UNPACK); - return 0; + return rc; } int sja1105_dynamic_config_write(struct sja1105_private *priv, @@ -1357,22 +1359,14 @@ int sja1105_dynamic_config_write(struct sja1105_private *priv, mutex_lock(&priv->dynamic_config_lock); rc = sja1105_xfer_buf(priv, SPI_WRITE, ops->addr, packed_buf, ops->packed_size); - if (rc < 0) { - mutex_unlock(&priv->dynamic_config_lock); - return rc; - } - - rc = sja1105_dynamic_config_wait_complete(priv, &cmd, ops); - mutex_unlock(&priv->dynamic_config_lock); if (rc < 0) - return rc; + goto out; - cmd = (struct sja1105_dyn_cmd) {0}; - ops->cmd_packing(packed_buf, &cmd, UNPACK); - if (cmd.errors) - return -EINVAL; + rc = sja1105_dynamic_config_wait_complete(priv, ops, NULL, false, true); +out: + mutex_unlock(&priv->dynamic_config_lock); - return 0; + return rc; } static u8 sja1105_crc8_add(u8 crc, u8 byte, u8 poly) -- cgit v1.2.3 From ea32690daf4fa525dc5a4d164bd00ed8c756e1c6 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 8 Sep 2023 16:33:51 +0300 Subject: net: dsa: sja1105: serialize sja1105_port_mcast_flood() with other FDB accesses sja1105_fdb_add() runs from the dsa_owq, and sja1105_port_mcast_flood() runs from switchdev_deferred_process_work(). Prior to the blamed commit, they used to be indirectly serialized through the rtnl_lock(), which no longer holds true because dsa_owq dropped that. So, it is now possible that we traverse the static config BLK_IDX_L2_LOOKUP elements concurrently compared to when we change them, in sja1105_static_fdb_change(). That is not ideal, since it might result in data corruption. Introduce a mutex which serializes accesses to the hardware FDB and to the static config elements for the L2 Address Lookup table. I can't find a good reason to add locking around sja1105_fdb_dump(). I'll add it later if needed. Fixes: 0faf890fc519 ("net: dsa: drop rtnl_lock from dsa_slave_switchdev_event_work") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105.h | 2 ++ drivers/net/dsa/sja1105/sja1105_main.c | 56 ++++++++++++++++++++++++++-------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/drivers/net/dsa/sja1105/sja1105.h b/drivers/net/dsa/sja1105/sja1105.h index 0617d5ccd3ff..8c66d3bf61f0 100644 --- a/drivers/net/dsa/sja1105/sja1105.h +++ b/drivers/net/dsa/sja1105/sja1105.h @@ -266,6 +266,8 @@ struct sja1105_private { * the switch doesn't confuse them with one another. */ struct mutex mgmt_lock; + /* Serializes accesses to the FDB */ + struct mutex fdb_lock; /* PTP two-step TX timestamp ID, and its serialization lock */ spinlock_t ts_id_lock; u8 ts_id; diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index 11c917d5ce43..cefd72617af4 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -1798,6 +1798,7 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, struct dsa_db db) { struct sja1105_private *priv = ds->priv; + int rc; if (!vid) { switch (db.type) { @@ -1812,12 +1813,16 @@ static int sja1105_fdb_add(struct dsa_switch *ds, int port, } } - return priv->info->fdb_add_cmd(ds, port, addr, vid); + mutex_lock(&priv->fdb_lock); + rc = priv->info->fdb_add_cmd(ds, port, addr, vid); + mutex_unlock(&priv->fdb_lock); + + return rc; } -static int sja1105_fdb_del(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid, - struct dsa_db db) +static int __sja1105_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) { struct sja1105_private *priv = ds->priv; @@ -1837,6 +1842,20 @@ static int sja1105_fdb_del(struct dsa_switch *ds, int port, return priv->info->fdb_del_cmd(ds, port, addr, vid); } +static int sja1105_fdb_del(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid, + struct dsa_db db) +{ + struct sja1105_private *priv = ds->priv; + int rc; + + mutex_lock(&priv->fdb_lock); + rc = __sja1105_fdb_del(ds, port, addr, vid, db); + mutex_unlock(&priv->fdb_lock); + + return rc; +} + static int sja1105_fdb_dump(struct dsa_switch *ds, int port, dsa_fdb_dump_cb_t *cb, void *data) { @@ -1899,6 +1918,8 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) }; int i; + mutex_lock(&priv->fdb_lock); + for (i = 0; i < SJA1105_MAX_L2_LOOKUP_COUNT; i++) { struct sja1105_l2_lookup_entry l2_lookup = {0}; u8 macaddr[ETH_ALEN]; @@ -1912,7 +1933,7 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) if (rc) { dev_err(ds->dev, "Failed to read FDB: %pe\n", ERR_PTR(rc)); - return; + break; } if (!(l2_lookup.destports & BIT(port))) @@ -1924,14 +1945,16 @@ static void sja1105_fast_age(struct dsa_switch *ds, int port) u64_to_ether_addr(l2_lookup.macaddr, macaddr); - rc = sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); + rc = __sja1105_fdb_del(ds, port, macaddr, l2_lookup.vlanid, db); if (rc) { dev_err(ds->dev, "Failed to delete FDB entry %pM vid %lld: %pe\n", macaddr, l2_lookup.vlanid, ERR_PTR(rc)); - return; + break; } } + + mutex_unlock(&priv->fdb_lock); } static int sja1105_mdb_add(struct dsa_switch *ds, int port, @@ -2955,7 +2978,9 @@ static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, { struct sja1105_l2_lookup_entry *l2_lookup; struct sja1105_table *table; - int match; + int match, rc; + + mutex_lock(&priv->fdb_lock); table = &priv->static_config.tables[BLK_IDX_L2_LOOKUP]; l2_lookup = table->entries; @@ -2968,7 +2993,8 @@ static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, if (match == table->entry_count) { NL_SET_ERR_MSG_MOD(extack, "Could not find FDB entry for unknown multicast"); - return -ENOSPC; + rc = -ENOSPC; + goto out; } if (flags.val & BR_MCAST_FLOOD) @@ -2976,10 +3002,13 @@ static int sja1105_port_mcast_flood(struct sja1105_private *priv, int to, else l2_lookup[match].destports &= ~BIT(to); - return sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, - l2_lookup[match].index, - &l2_lookup[match], - true); + rc = sja1105_dynamic_config_write(priv, BLK_IDX_L2_LOOKUP, + l2_lookup[match].index, + &l2_lookup[match], true); +out: + mutex_unlock(&priv->fdb_lock); + + return rc; } static int sja1105_port_pre_bridge_flags(struct dsa_switch *ds, int port, @@ -3349,6 +3378,7 @@ static int sja1105_probe(struct spi_device *spi) mutex_init(&priv->ptp_data.lock); mutex_init(&priv->dynamic_config_lock); mutex_init(&priv->mgmt_lock); + mutex_init(&priv->fdb_lock); spin_lock_init(&priv->ts_id_lock); rc = sja1105_parse_dt(priv); -- cgit v1.2.3 From 86899e9e1e29e854b5f6dcc24ba4f75f792c89aa Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Fri, 8 Sep 2023 16:33:52 +0300 Subject: net: dsa: sja1105: block FDB accesses that are concurrent with a switch reset Currently, when we add the first sja1105 port to a bridge with vlan_filtering 1, then we sometimes see this output: sja1105 spi2.2: port 4 failed to read back entry for be:79:b4:9e:9e:96 vid 3088: -ENOENT sja1105 spi2.2: Reset switch and programmed static config. Reason: VLAN filtering sja1105 spi2.2: port 0 failed to add be:79:b4:9e:9e:96 vid 0 to fdb: -2 It is because sja1105_fdb_add() runs from the dsa_owq which is no longer serialized with switch resets since it dropped the rtnl_lock() in the blamed commit. Either performing the FDB accesses before the reset, or after the reset, is equally fine, because sja1105_static_fdb_change() backs up those changes in the static config, but FDB access during reset isn't ok. Make sja1105_static_config_reload() take the fdb_lock to fix that. Fixes: 0faf890fc519 ("net: dsa: drop rtnl_lock from dsa_slave_switchdev_event_work") Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/dsa/sja1105/sja1105_main.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/dsa/sja1105/sja1105_main.c b/drivers/net/dsa/sja1105/sja1105_main.c index cefd72617af4..1a367e64bc3b 100644 --- a/drivers/net/dsa/sja1105/sja1105_main.c +++ b/drivers/net/dsa/sja1105/sja1105_main.c @@ -2297,6 +2297,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv, int rc, i; s64 now; + mutex_lock(&priv->fdb_lock); mutex_lock(&priv->mgmt_lock); mac = priv->static_config.tables[BLK_IDX_MAC_CONFIG].entries; @@ -2409,6 +2410,7 @@ int sja1105_static_config_reload(struct sja1105_private *priv, goto out; out: mutex_unlock(&priv->mgmt_lock); + mutex_unlock(&priv->fdb_lock); return rc; } -- cgit v1.2.3 From a7b8d60b37237680009dd0b025fe8c067aba0ee3 Mon Sep 17 00:00:00 2001 From: Hayes Wang Date: Fri, 8 Sep 2023 15:01:52 +0800 Subject: r8152: check budget for r8152_poll() According to the document of napi, there is no rx process when the budget is 0. Therefore, r8152_poll() has to return 0 directly when the budget is equal to 0. Fixes: d2187f8e4454 ("r8152: divide the tx and rx bottom functions") Signed-off-by: Hayes Wang Signed-off-by: David S. Miller --- drivers/net/usb/r8152.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index 332c853ca99b..0c13d9950cd8 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -2636,6 +2636,9 @@ static int r8152_poll(struct napi_struct *napi, int budget) struct r8152 *tp = container_of(napi, struct r8152, napi); int work_done; + if (!budget) + return 0; + work_done = rx_bottom(tp, budget); if (work_done < budget) { -- cgit v1.2.3 From c821a88bd720b0046433173185fd841a100d44ad Mon Sep 17 00:00:00 2001 From: Shigeru Yoshida Date: Sun, 10 Sep 2023 02:03:10 +0900 Subject: kcm: Fix memory leak in error path of kcm_sendmsg() syzbot reported a memory leak like below: BUG: memory leak unreferenced object 0xffff88810b088c00 (size 240): comm "syz-executor186", pid 5012, jiffies 4294943306 (age 13.680s) hex dump (first 32 bytes): 00 89 08 0b 81 88 ff ff 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] __alloc_skb+0x1ef/0x230 net/core/skbuff.c:634 [] alloc_skb include/linux/skbuff.h:1289 [inline] [] kcm_sendmsg+0x269/0x1050 net/kcm/kcmsock.c:815 [] sock_sendmsg_nosec net/socket.c:725 [inline] [] sock_sendmsg+0x56/0xb0 net/socket.c:748 [] ____sys_sendmsg+0x365/0x470 net/socket.c:2494 [] ___sys_sendmsg+0xc9/0x130 net/socket.c:2548 [] __sys_sendmsg+0xa6/0x120 net/socket.c:2577 [] do_syscall_x64 arch/x86/entry/common.c:50 [inline] [] do_syscall_64+0x38/0xb0 arch/x86/entry/common.c:80 [] entry_SYSCALL_64_after_hwframe+0x63/0xcd In kcm_sendmsg(), kcm_tx_msg(head)->last_skb is used as a cursor to append newly allocated skbs to 'head'. If some bytes are copied, an error occurred, and jumped to out_error label, 'last_skb' is left unmodified. A later kcm_sendmsg() will use an obsoleted 'last_skb' reference, corrupting the 'head' frag_list and causing the leak. This patch fixes this issue by properly updating the last allocated skb in 'last_skb'. Fixes: ab7ac4eb9832 ("kcm: Kernel Connection Multiplexor module") Reported-and-tested-by: syzbot+6f98de741f7dbbfc4ccb@syzkaller.appspotmail.com Closes: https://syzkaller.appspot.com/bug?extid=6f98de741f7dbbfc4ccb Signed-off-by: Shigeru Yoshida Signed-off-by: David S. Miller --- net/kcm/kcmsock.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 4580f61426bb..740539a218b7 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -939,6 +939,8 @@ out_error: if (head != kcm->seq_skb) kfree_skb(head); + else if (copied) + kcm_tx_msg(head)->last_skb = skb; err = sk_stream_error(sk, msg->msg_flags, err); -- cgit v1.2.3 From 79b83606abc778aa3cbee535b362ce905d0b9448 Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 10 Sep 2023 06:54:45 +0200 Subject: efivarfs: fix statfs() on efivarfs Some firmware (notably U-Boot) provides GetVariable() and GetNextVariableName() but not QueryVariableInfo(). With commit d86ff3333cb1 ("efivarfs: expose used and total size") the statfs syscall was broken for such firmware. If QueryVariableInfo() does not exist or returns EFI_UNSUPPORTED, just report the file system size as 0 as statfs_simple() previously did. Fixes: d86ff3333cb1 ("efivarfs: expose used and total size") Link: https://lore.kernel.org/all/20230910045445.41632-1-heinrich.schuchardt@canonical.com/ Signed-off-by: Heinrich Schuchardt [ardb: log warning on QueryVariableInfo() failure] Reviewed-by: Ilias Apalodimas Signed-off-by: Ard Biesheuvel --- fs/efivarfs/super.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index e028fafa04f3..996271473609 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c @@ -32,10 +32,16 @@ static int efivarfs_statfs(struct dentry *dentry, struct kstatfs *buf) u64 storage_space, remaining_space, max_variable_size; efi_status_t status; - status = efivar_query_variable_info(attr, &storage_space, &remaining_space, - &max_variable_size); - if (status != EFI_SUCCESS) - return efi_status_to_err(status); + /* Some UEFI firmware does not implement QueryVariableInfo() */ + storage_space = remaining_space = 0; + if (efi_rt_services_supported(EFI_RT_SUPPORTED_QUERY_VARIABLE_INFO)) { + status = efivar_query_variable_info(attr, &storage_space, + &remaining_space, + &max_variable_size); + if (status != EFI_SUCCESS && status != EFI_UNSUPPORTED) + pr_warn_ratelimited("query_variable_info() failed: 0x%lx\n", + status); + } /* * This is not a normal filesystem, so no point in pretending it has a block -- cgit v1.2.3 From e10a35abb3da12b812cfb6fc6137926a0c81e39a Mon Sep 17 00:00:00 2001 From: Daniel Golle Date: Sun, 10 Sep 2023 22:40:30 +0100 Subject: net: ethernet: mtk_eth_soc: fix uninitialized variable Variable dma_addr in function mtk_poll_rx can be uninitialized on some of the error paths. In practise this doesn't matter, even random data present in uninitialized stack memory can safely be used in the way it happens in the error path. However, in order to make Smatch happy make sure the variable is always initialized. Signed-off-by: Daniel Golle Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_eth_soc.c b/drivers/net/ethernet/mediatek/mtk_eth_soc.c index 2372ce8c2580..3cffd1bd3067 100644 --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c @@ -2005,11 +2005,11 @@ static int mtk_poll_rx(struct napi_struct *napi, int budget, u8 *data, *new_data; struct mtk_rx_dma_v2 *rxd, trxd; int done = 0, bytes = 0; + dma_addr_t dma_addr = DMA_MAPPING_ERROR; while (done < budget) { unsigned int pktlen, *rxdcsum; struct net_device *netdev; - dma_addr_t dma_addr; u32 hash, reason; int mac = 0; @@ -2186,7 +2186,8 @@ release_desc: else rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); - if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA)) + if (MTK_HAS_CAPS(eth->soc->caps, MTK_36BIT_DMA) && + likely(dma_addr != DMA_MAPPING_ERROR)) rxd->rxd2 |= RX_DMA_PREP_ADDR64(dma_addr); ring->calc_idx = idx; -- cgit v1.2.3 From 5a124b1fd3e6cb15a943f0cdfe96aa8f6d3d2f39 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 9 Sep 2023 20:41:56 +0200 Subject: net: ethernet: mtk_eth_soc: fix pse_port configuration for MT7988 MT7988 SoC support 3 NICs. Fix pse_port configuration in mtk_flow_set_output_device routine if the traffic is offloaded to eth2. Rely on mtk_pse_port definitions. Fixes: 88efedf517e6 ("net: ethernet: mtk_eth_soc: enable nft hw flowtable_offload for MT7988 SoC") Signed-off-by: Lorenzo Bianconi Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_ppe_offload.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c index a70a5417c173..a4efbeb16208 100644 --- a/drivers/net/ethernet/mediatek/mtk_ppe_offload.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe_offload.c @@ -214,9 +214,11 @@ mtk_flow_set_output_device(struct mtk_eth *eth, struct mtk_foe_entry *foe, dsa_port = mtk_flow_get_dsa_port(&dev); if (dev == eth->netdev[0]) - pse_port = 1; + pse_port = PSE_GDM1_PORT; else if (dev == eth->netdev[1]) - pse_port = 2; + pse_port = PSE_GDM2_PORT; + else if (dev == eth->netdev[2]) + pse_port = PSE_GDM3_PORT; else return -EOPNOTSUPP; -- cgit v1.2.3 From 78034cbece79c2d730ad0770b3b7f23eedbbecf5 Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 29 Aug 2023 13:42:59 -0400 Subject: platform/mellanox: mlxbf-tmfifo: Drop the Rx packet if no more descriptors This commit fixes tmfifo console stuck issue when the virtual networking interface is in down state. In such case, the network Rx descriptors runs out and causes the Rx network packet staying in the head of the tmfifo thus blocking the console packets. The fix is to drop the Rx network packet when no more Rx descriptors. Function name mlxbf_tmfifo_release_pending_pkt() is also renamed to mlxbf_tmfifo_release_pkt() to be more approperiate. Fixes: 1357dfd7261f ("platform/mellanox: Add TmFifo driver for Mellanox BlueField Soc") Signed-off-by: Liming Sun Reviewed-by: Vadim Pasternak Reviewed-by: David Thompson Link: https://lore.kernel.org/r/8c0177dc938ae03f52ff7e0b62dbeee74b7bec09.1693322547.git.limings@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-tmfifo.c | 66 ++++++++++++++++++++++++-------- 1 file changed, 49 insertions(+), 17 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index b600b77d91ef..5c1f859b682a 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -59,6 +59,7 @@ struct mlxbf_tmfifo; * @vq: pointer to the virtio virtqueue * @desc: current descriptor of the pending packet * @desc_head: head descriptor of the pending packet + * @drop_desc: dummy desc for packet dropping * @cur_len: processed length of the current descriptor * @rem_len: remaining length of the pending packet * @pkt_len: total length of the pending packet @@ -75,6 +76,7 @@ struct mlxbf_tmfifo_vring { struct virtqueue *vq; struct vring_desc *desc; struct vring_desc *desc_head; + struct vring_desc drop_desc; int cur_len; int rem_len; u32 pkt_len; @@ -86,6 +88,14 @@ struct mlxbf_tmfifo_vring { struct mlxbf_tmfifo *fifo; }; +/* Check whether vring is in drop mode. */ +#define IS_VRING_DROP(_r) ({ \ + typeof(_r) (r) = (_r); \ + (r->desc_head == &r->drop_desc ? true : false); }) + +/* A stub length to drop maximum length packet. */ +#define VRING_DROP_DESC_MAX_LEN GENMASK(15, 0) + /* Interrupt types. */ enum { MLXBF_TM_RX_LWM_IRQ, @@ -262,6 +272,7 @@ static int mlxbf_tmfifo_alloc_vrings(struct mlxbf_tmfifo *fifo, vring->align = SMP_CACHE_BYTES; vring->index = i; vring->vdev_id = tm_vdev->vdev.id.device; + vring->drop_desc.len = VRING_DROP_DESC_MAX_LEN; dev = &tm_vdev->vdev.dev; size = vring_size(vring->num, vring->align); @@ -367,7 +378,7 @@ static u32 mlxbf_tmfifo_get_pkt_len(struct mlxbf_tmfifo_vring *vring, return len; } -static void mlxbf_tmfifo_release_pending_pkt(struct mlxbf_tmfifo_vring *vring) +static void mlxbf_tmfifo_release_pkt(struct mlxbf_tmfifo_vring *vring) { struct vring_desc *desc_head; u32 len = 0; @@ -596,19 +607,25 @@ static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, if (vring->cur_len + sizeof(u64) <= len) { /* The whole word. */ - if (is_rx) - memcpy(addr + vring->cur_len, &data, sizeof(u64)); - else - memcpy(&data, addr + vring->cur_len, sizeof(u64)); + if (!IS_VRING_DROP(vring)) { + if (is_rx) + memcpy(addr + vring->cur_len, &data, + sizeof(u64)); + else + memcpy(&data, addr + vring->cur_len, + sizeof(u64)); + } vring->cur_len += sizeof(u64); } else { /* Leftover bytes. */ - if (is_rx) - memcpy(addr + vring->cur_len, &data, - len - vring->cur_len); - else - memcpy(&data, addr + vring->cur_len, - len - vring->cur_len); + if (!IS_VRING_DROP(vring)) { + if (is_rx) + memcpy(addr + vring->cur_len, &data, + len - vring->cur_len); + else + memcpy(&data, addr + vring->cur_len, + len - vring->cur_len); + } vring->cur_len = len; } @@ -709,8 +726,16 @@ static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring, /* Get the descriptor of the next packet. */ if (!vring->desc) { desc = mlxbf_tmfifo_get_next_pkt(vring, is_rx); - if (!desc) - return false; + if (!desc) { + /* Drop next Rx packet to avoid stuck. */ + if (is_rx) { + desc = &vring->drop_desc; + vring->desc_head = desc; + vring->desc = desc; + } else { + return false; + } + } } else { desc = vring->desc; } @@ -743,17 +768,24 @@ static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring, vring->rem_len -= len; /* Get the next desc on the chain. */ - if (vring->rem_len > 0 && + if (!IS_VRING_DROP(vring) && vring->rem_len > 0 && (virtio16_to_cpu(vdev, desc->flags) & VRING_DESC_F_NEXT)) { idx = virtio16_to_cpu(vdev, desc->next); desc = &vr->desc[idx]; goto mlxbf_tmfifo_desc_done; } - /* Done and release the pending packet. */ - mlxbf_tmfifo_release_pending_pkt(vring); + /* Done and release the packet. */ desc = NULL; fifo->vring[is_rx] = NULL; + if (!IS_VRING_DROP(vring)) { + mlxbf_tmfifo_release_pkt(vring); + } else { + vring->pkt_len = 0; + vring->desc_head = NULL; + vring->desc = NULL; + return false; + } /* * Make sure the load/store are in order before @@ -933,7 +965,7 @@ static void mlxbf_tmfifo_virtio_del_vqs(struct virtio_device *vdev) /* Release the pending packet. */ if (vring->desc) - mlxbf_tmfifo_release_pending_pkt(vring); + mlxbf_tmfifo_release_pkt(vring); vq = vring->vq; if (vq) { vring->vq = NULL; -- cgit v1.2.3 From fc4c655821546239abb3cf4274d66b9747aa87dd Mon Sep 17 00:00:00 2001 From: Liming Sun Date: Tue, 29 Aug 2023 13:43:00 -0400 Subject: platform/mellanox: mlxbf-tmfifo: Drop jumbo frames This commit drops over-sized network packets to avoid tmfifo queue stuck. Fixes: 1357dfd7261f ("platform/mellanox: Add TmFifo driver for Mellanox BlueField Soc") Signed-off-by: Liming Sun Reviewed-by: Vadim Pasternak Reviewed-by: David Thompson Link: https://lore.kernel.org/r/9318936c2447f76db475c985ca6d91f057efcd41.1693322547.git.limings@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-tmfifo.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-tmfifo.c b/drivers/platform/mellanox/mlxbf-tmfifo.c index 5c1f859b682a..f3696a54a2bd 100644 --- a/drivers/platform/mellanox/mlxbf-tmfifo.c +++ b/drivers/platform/mellanox/mlxbf-tmfifo.c @@ -224,7 +224,7 @@ static u8 mlxbf_tmfifo_net_default_mac[ETH_ALEN] = { static efi_char16_t mlxbf_tmfifo_efi_name[] = L"RshimMacAddr"; /* Maximum L2 header length. */ -#define MLXBF_TMFIFO_NET_L2_OVERHEAD 36 +#define MLXBF_TMFIFO_NET_L2_OVERHEAD (ETH_HLEN + VLAN_HLEN) /* Supported virtio-net features. */ #define MLXBF_TMFIFO_NET_FEATURES \ @@ -642,13 +642,14 @@ static void mlxbf_tmfifo_rxtx_word(struct mlxbf_tmfifo_vring *vring, * flag is set. */ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, - struct vring_desc *desc, + struct vring_desc **desc, bool is_rx, bool *vring_change) { struct mlxbf_tmfifo *fifo = vring->fifo; struct virtio_net_config *config; struct mlxbf_tmfifo_msg_hdr hdr; int vdev_id, hdr_len; + bool drop_rx = false; /* Read/Write packet header. */ if (is_rx) { @@ -668,8 +669,8 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, if (ntohs(hdr.len) > __virtio16_to_cpu(virtio_legacy_is_little_endian(), config->mtu) + - MLXBF_TMFIFO_NET_L2_OVERHEAD) - return; + MLXBF_TMFIFO_NET_L2_OVERHEAD) + drop_rx = true; } else { vdev_id = VIRTIO_ID_CONSOLE; hdr_len = 0; @@ -684,16 +685,25 @@ static void mlxbf_tmfifo_rxtx_header(struct mlxbf_tmfifo_vring *vring, if (!tm_dev2) return; - vring->desc = desc; + vring->desc = *desc; vring = &tm_dev2->vrings[MLXBF_TMFIFO_VRING_RX]; *vring_change = true; } + + if (drop_rx && !IS_VRING_DROP(vring)) { + if (vring->desc_head) + mlxbf_tmfifo_release_pkt(vring); + *desc = &vring->drop_desc; + vring->desc_head = *desc; + vring->desc = *desc; + } + vring->pkt_len = ntohs(hdr.len) + hdr_len; } else { /* Network virtio has an extra header. */ hdr_len = (vring->vdev_id == VIRTIO_ID_NET) ? sizeof(struct virtio_net_hdr) : 0; - vring->pkt_len = mlxbf_tmfifo_get_pkt_len(vring, desc); + vring->pkt_len = mlxbf_tmfifo_get_pkt_len(vring, *desc); hdr.type = (vring->vdev_id == VIRTIO_ID_NET) ? VIRTIO_ID_NET : VIRTIO_ID_CONSOLE; hdr.len = htons(vring->pkt_len - hdr_len); @@ -742,7 +752,7 @@ static bool mlxbf_tmfifo_rxtx_one_desc(struct mlxbf_tmfifo_vring *vring, /* Beginning of a packet. Start to Rx/Tx packet header. */ if (vring->pkt_len == 0) { - mlxbf_tmfifo_rxtx_header(vring, desc, is_rx, &vring_change); + mlxbf_tmfifo_rxtx_header(vring, &desc, is_rx, &vring_change); (*avail)--; /* Return if new packet is for another ring. */ -- cgit v1.2.3 From 80ccd40568bcd3655b0fd0be1e9b3379fd6e1056 Mon Sep 17 00:00:00 2001 From: Shravan Kumar Ramani Date: Tue, 5 Sep 2023 08:49:32 -0400 Subject: platform/mellanox: mlxbf-pmc: Fix potential buffer overflows Replace sprintf with sysfs_emit where possible. Size check in mlxbf_pmc_event_list_show should account for "\0". Fixes: 1a218d312e65 ("platform/mellanox: mlxbf-pmc: Add Mellanox BlueField PMC driver") Signed-off-by: Shravan Kumar Ramani Reviewed-by: Vadim Pasternak Reviewed-by: David Thompson Link: https://lore.kernel.org/r/bef39ef32319a31b32f999065911f61b0d3b17c3.1693917738.git.shravankr@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-pmc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index be967d797c28..95afcae7b9fa 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -1008,7 +1008,7 @@ static ssize_t mlxbf_pmc_counter_show(struct device *dev, } else return -EINVAL; - return sprintf(buf, "0x%llx\n", value); + return sysfs_emit(buf, "0x%llx\n", value); } /* Store function for "counter" sysfs files */ @@ -1078,13 +1078,13 @@ static ssize_t mlxbf_pmc_event_show(struct device *dev, err = mlxbf_pmc_read_event(blk_num, cnt_num, is_l3, &evt_num); if (err) - return sprintf(buf, "No event being monitored\n"); + return sysfs_emit(buf, "No event being monitored\n"); evt_name = mlxbf_pmc_get_event_name(pmc->block_name[blk_num], evt_num); if (!evt_name) return -EINVAL; - return sprintf(buf, "0x%llx: %s\n", evt_num, evt_name); + return sysfs_emit(buf, "0x%llx: %s\n", evt_num, evt_name); } /* Store function for "event" sysfs files */ @@ -1139,9 +1139,9 @@ static ssize_t mlxbf_pmc_event_list_show(struct device *dev, return -EINVAL; for (i = 0, buf[0] = '\0'; i < size; ++i) { - len += sprintf(e_info, "0x%x: %s\n", events[i].evt_num, - events[i].evt_name); - if (len > PAGE_SIZE) + len += snprintf(e_info, sizeof(e_info), "0x%x: %s\n", + events[i].evt_num, events[i].evt_name); + if (len >= PAGE_SIZE) break; strcat(buf, e_info); ret = len; @@ -1168,7 +1168,7 @@ static ssize_t mlxbf_pmc_enable_show(struct device *dev, value = FIELD_GET(MLXBF_PMC_L3C_PERF_CNT_CFG_EN, perfcnt_cfg); - return sprintf(buf, "%d\n", value); + return sysfs_emit(buf, "%d\n", value); } /* Store function for "enable" sysfs files - only for l3cache */ -- cgit v1.2.3 From 0f5969452e162efc50bdc98968fb62b424a9874b Mon Sep 17 00:00:00 2001 From: Shravan Kumar Ramani Date: Tue, 5 Sep 2023 08:49:33 -0400 Subject: platform/mellanox: mlxbf-pmc: Fix reading of unprogrammed events This fix involves 2 changes: - All event regs have a reset value of 0, which is not a valid event_number as per the event_list for most blocks and hence seen as an error. Add a "disable" event with event_number 0 for all blocks. - The enable bit for each counter need not be checked before reading the event info, and hence removed. Fixes: 1a218d312e65 ("platform/mellanox: mlxbf-pmc: Add Mellanox BlueField PMC driver") Signed-off-by: Shravan Kumar Ramani Reviewed-by: Vadim Pasternak Reviewed-by: David Thompson Link: https://lore.kernel.org/r/04d0213932d32681de1c716b54320ed894e52425.1693917738.git.shravankr@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/mlxbf-pmc.c | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/drivers/platform/mellanox/mlxbf-pmc.c b/drivers/platform/mellanox/mlxbf-pmc.c index 95afcae7b9fa..2d4bbe99959e 100644 --- a/drivers/platform/mellanox/mlxbf-pmc.c +++ b/drivers/platform/mellanox/mlxbf-pmc.c @@ -191,6 +191,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_smgen_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_1[] = { + { 0x0, "DISABLE" }, { 0xa0, "TPIO_DATA_BEAT" }, { 0xa1, "TDMA_DATA_BEAT" }, { 0xa2, "MAP_DATA_BEAT" }, @@ -214,6 +215,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_1[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_2[] = { + { 0x0, "DISABLE" }, { 0xa0, "TPIO_DATA_BEAT" }, { 0xa1, "TDMA_DATA_BEAT" }, { 0xa2, "MAP_DATA_BEAT" }, @@ -246,6 +248,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_trio_events_2[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_ecc_events[] = { + { 0x0, "DISABLE" }, { 0x100, "ECC_SINGLE_ERROR_CNT" }, { 0x104, "ECC_DOUBLE_ERROR_CNT" }, { 0x114, "SERR_INJ" }, @@ -258,6 +261,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_ecc_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { + { 0x0, "DISABLE" }, { 0xc0, "RXREQ_MSS" }, { 0xc1, "RXDAT_MSS" }, { 0xc2, "TXRSP_MSS" }, @@ -265,6 +269,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_mss_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_hnf_events[] = { + { 0x0, "DISABLE" }, { 0x45, "HNF_REQUESTS" }, { 0x46, "HNF_REJECTS" }, { 0x47, "ALL_BUSY" }, @@ -323,6 +328,7 @@ static const struct mlxbf_pmc_events mlxbf_pmc_hnf_events[] = { }; static const struct mlxbf_pmc_events mlxbf_pmc_hnfnet_events[] = { + { 0x0, "DISABLE" }, { 0x12, "CDN_REQ" }, { 0x13, "DDN_REQ" }, { 0x14, "NDN_REQ" }, @@ -892,7 +898,7 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, uint64_t *result) { uint32_t perfcfg_offset, perfval_offset; - uint64_t perfmon_cfg, perfevt, perfctl; + uint64_t perfmon_cfg, perfevt; if (cnt_num >= pmc->block[blk_num].counters) return -EINVAL; @@ -904,25 +910,6 @@ static int mlxbf_pmc_read_event(int blk_num, uint32_t cnt_num, bool is_l3, perfval_offset = perfcfg_offset + pmc->block[blk_num].counters * MLXBF_PMC_REG_SIZE; - /* Set counter in "read" mode */ - perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, - MLXBF_PMC_PERFCTL); - perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_STROBE, 1); - perfmon_cfg |= FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_WR_R_B, 0); - - if (mlxbf_pmc_write(pmc->block[blk_num].mmio_base + perfcfg_offset, - MLXBF_PMC_WRITE_REG_64, perfmon_cfg)) - return -EFAULT; - - /* Check if the counter is enabled */ - - if (mlxbf_pmc_read(pmc->block[blk_num].mmio_base + perfval_offset, - MLXBF_PMC_READ_REG_64, &perfctl)) - return -EFAULT; - - if (!FIELD_GET(MLXBF_PMC_PERFCTL_EN0, perfctl)) - return -EINVAL; - /* Set counter in "read" mode */ perfmon_cfg = FIELD_PREP(MLXBF_PMC_PERFMON_CONFIG_ADDR, MLXBF_PMC_PERFEVT); -- cgit v1.2.3 From c2dffda1d8f7511505bbbf16ba282f2079b30089 Mon Sep 17 00:00:00 2001 From: David Thompson Date: Tue, 5 Sep 2023 09:32:43 -0400 Subject: platform/mellanox: mlxbf-bootctl: add NET dependency into Kconfig The latest version of the mlxbf_bootctl driver utilizes "sysfs_format_mac", and this API is only available if NET is defined in the kernel configuration. This patch changes the mlxbf_bootctl Kconfig to depend on NET. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202309031058.JvwNDBKt-lkp@intel.com/ Reported-by: Randy Dunlap Signed-off-by: David Thompson Link: https://lore.kernel.org/r/20230905133243.31550-1-davthompson@nvidia.com Signed-off-by: Hans de Goede --- drivers/platform/mellanox/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index 382793e73a60..e52aea996ca5 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -60,6 +60,7 @@ config MLXBF_BOOTCTL tristate "Mellanox BlueField Firmware Boot Control driver" depends on ARM64 depends on ACPI + depends on NET help The Mellanox BlueField firmware implements functionality to request swapping the primary and alternate eMMC boot partition, -- cgit v1.2.3 From 0a138f1670bd1af13ba6949c48ea86ddd4bf557e Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 4 Sep 2023 14:00:35 +0200 Subject: platform/mellanox: NVSW_SN2201 should depend on ACPI The only probing method supported by the Nvidia SN2201 platform driver is probing through an ACPI match table. Hence add a dependency on ACPI, to prevent asking the user about this driver when configuring a kernel without ACPI support. Fixes: 662f24826f95 ("platform/mellanox: Add support for new SN2201 system") Signed-off-by: Geert Uytterhoeven Acked-by: Vadim Pasternak Acked-by: Andi Shyti Link: https://lore.kernel.org/r/ec5a4071691ab08d58771b7732a9988e89779268.1693828363.git.geert+renesas@glider.be Signed-off-by: Hans de Goede --- drivers/platform/mellanox/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/platform/mellanox/Kconfig b/drivers/platform/mellanox/Kconfig index e52aea996ca5..f7dfa0e785fd 100644 --- a/drivers/platform/mellanox/Kconfig +++ b/drivers/platform/mellanox/Kconfig @@ -81,8 +81,8 @@ config MLXBF_PMC config NVSW_SN2201 tristate "Nvidia SN2201 platform driver support" - depends on HWMON - depends on I2C + depends on HWMON && I2C + depends on ACPI || COMPILE_TEST select REGMAP_I2C help This driver provides support for the Nvidia SN2201 platform. -- cgit v1.2.3 From 4106a70ddad57ee6d8f98b81d6f036740c72762b Mon Sep 17 00:00:00 2001 From: "Luke D. Jones" Date: Tue, 5 Sep 2023 20:28:13 +1200 Subject: platform/x86: asus-wmi: Support 2023 ROG X16 tablet mode Add quirk for ASUS ROG X16 (GV601V, 2023 versions) Flow 2-in-1 to enable tablet mode with lid flip (all screen rotations). Signed-off-by: Luke D. Jones Link: https://lore.kernel.org/r/20230905082813.13470-1-luke@ljones.dev Reviewed-by: Hans de Goede Signed-off-by: Hans de Goede --- drivers/platform/x86/asus-nb-wmi.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/platform/x86/asus-nb-wmi.c b/drivers/platform/x86/asus-nb-wmi.c index fdf7da06af30..d85d895fee89 100644 --- a/drivers/platform/x86/asus-nb-wmi.c +++ b/drivers/platform/x86/asus-nb-wmi.c @@ -478,6 +478,15 @@ static const struct dmi_system_id asus_quirks[] = { }, .driver_data = &quirk_asus_tablet_mode, }, + { + .callback = dmi_matched, + .ident = "ASUS ROG FLOW X16", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "GV601V"), + }, + .driver_data = &quirk_asus_tablet_mode, + }, { .callback = dmi_matched, .ident = "ASUS VivoBook E410MA", -- cgit v1.2.3 From f26a679ed799deef9e2934a6b60b8f38bdbf4921 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Wed, 6 Sep 2023 11:48:42 +0300 Subject: usb: typec: ucsi: Fix NULL pointer dereference Making sure the UCSI debugfs entry actually exists before attempting to remove it. Fixes: df0383ffad64 ("usb: typec: ucsi: Add debugfs for ucsi commands") Reported-by: Dave Hansen Closes: https://lore.kernel.org/linux-usb/700df3c4-2f6c-85f9-6c61-065bc5b2db3a@intel.com/ Suggested-by: Dave Hansen Suggested-by: Mario Limonciello Cc: Saranya Gopal Signed-off-by: Heikki Krogerus Cc: Thorsten Leemhuis Link: https://lore.kernel.org/r/20230906084842.1922052-1-heikki.krogerus@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/ucsi/debugfs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/typec/ucsi/debugfs.c b/drivers/usb/typec/ucsi/debugfs.c index 0c7bf88d4a7f..f67733cecfdf 100644 --- a/drivers/usb/typec/ucsi/debugfs.c +++ b/drivers/usb/typec/ucsi/debugfs.c @@ -84,6 +84,9 @@ void ucsi_debugfs_register(struct ucsi *ucsi) void ucsi_debugfs_unregister(struct ucsi *ucsi) { + if (IS_ERR_OR_NULL(ucsi) || !ucsi->debugfs) + return; + debugfs_remove_recursive(ucsi->debugfs->dentry); kfree(ucsi->debugfs); } -- cgit v1.2.3 From 8a81cf96f5510aaf9a65d103f7405079a7b0fcc5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 7 Sep 2023 11:55:18 +0200 Subject: thermal/of: add missing of_node_put() for_each_child_of_node performs an of_node_get on each iteration, so a break out of the loop requires an of_node_put. This was done using the Coccinelle semantic patch iterators/for_each_child.cocci Signed-off-by: Julia Lawall Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_of.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_of.c b/drivers/thermal/thermal_of.c index 4ca905723429..1e0655b63259 100644 --- a/drivers/thermal/thermal_of.c +++ b/drivers/thermal/thermal_of.c @@ -37,8 +37,10 @@ static int of_find_trip_id(struct device_node *np, struct device_node *trip) */ for_each_child_of_node(trips, t) { - if (t == trip) + if (t == trip) { + of_node_put(t); goto out; + } i++; } @@ -401,8 +403,10 @@ static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz, for_each_child_of_node(cm_np, child) { ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action); - if (ret) + if (ret) { + of_node_put(child); break; + } } of_node_put(cm_np); -- cgit v1.2.3 From ebc7abb35b258152d4a424f89d7c03db1d7ce61c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 7 Sep 2023 20:18:56 +0200 Subject: thermal: Constify the trip argument of the .get_trend() zone callback Add 'const' to the definition of the 'trip' argument of the .get_trend() thermal zone callback to indicate that the trip point passed to it should not be modified by it and adjust the callback functions implementing it, thermal_get_trend() in the ACPI thermal driver and __ti_thermal_get_trend(), accordingly. No intentional functional impact. Signed-off-by: Rafael J. Wysocki Reviewed-by: Michal Wilczynski --- drivers/acpi/thermal.c | 2 +- drivers/thermal/ti-soc-thermal/ti-thermal-common.c | 3 ++- include/linux/thermal.h | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index f14e68266ccd..312730f8272e 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -492,7 +492,7 @@ static int thermal_get_temp(struct thermal_zone_device *thermal, int *temp) } static int thermal_get_trend(struct thermal_zone_device *thermal, - struct thermal_trip *trip, + const struct thermal_trip *trip, enum thermal_trend *trend) { struct acpi_thermal *tz = thermal_zone_device_priv(thermal); diff --git a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c index 6ba2613627e1..0cf0826b805a 100644 --- a/drivers/thermal/ti-soc-thermal/ti-thermal-common.c +++ b/drivers/thermal/ti-soc-thermal/ti-thermal-common.c @@ -110,7 +110,8 @@ static inline int __ti_thermal_get_temp(struct thermal_zone_device *tz, int *tem } static int __ti_thermal_get_trend(struct thermal_zone_device *tz, - struct thermal_trip *trip, enum thermal_trend *trend) + const struct thermal_trip *trip, + enum thermal_trend *trend) { struct ti_thermal_data *data = thermal_zone_device_priv(tz); struct ti_bandgap *bgp; diff --git a/include/linux/thermal.h b/include/linux/thermal.h index c99440aac1a1..a5ae4af955ff 100644 --- a/include/linux/thermal.h +++ b/include/linux/thermal.h @@ -80,8 +80,8 @@ struct thermal_zone_device_ops { int (*set_trip_hyst) (struct thermal_zone_device *, int, int); int (*get_crit_temp) (struct thermal_zone_device *, int *); int (*set_emul_temp) (struct thermal_zone_device *, int); - int (*get_trend) (struct thermal_zone_device *, struct thermal_trip *, - enum thermal_trend *); + int (*get_trend) (struct thermal_zone_device *, + const struct thermal_trip *, enum thermal_trend *); void (*hot)(struct thermal_zone_device *); void (*critical)(struct thermal_zone_device *); }; -- cgit v1.2.3 From 5290e88ba2c742ca77c5f5b690e5af549cfd8591 Mon Sep 17 00:00:00 2001 From: Steve Wahl Date: Mon, 7 Aug 2023 09:17:30 -0500 Subject: x86/platform/uv: Use alternate source for socket to node data The UV code attempts to build a set of tables to allow it to do bidirectional socket<=>node lookups. But when nr_cpus is set to a smaller number than actually present, the cpu_to_node() mapping information for unused CPUs is not available to build_socket_tables(). This results in skipping some nodes or sockets when creating the tables and leaving some -1's for later code to trip. over, causing oopses. The problem is that the socket<=>node lookups are created by doing a loop over all CPUs, then looking up the CPU's APICID and socket. But if a CPU is not present, there is no way to start this lookup. Instead of looping over all CPUs, take CPUs out of the equation entirely. Loop over all APICIDs which are mapped to a valid NUMA node. Then just extract the socket-id from the APICID. This avoid tripping over disabled CPUs. Fixes: 8a50c5851927 ("x86/platform/uv: UV support for sub-NUMA clustering") Signed-off-by: Steve Wahl Signed-off-by: Dave Hansen Cc: stable@vger.kernel.org Link: https://lore.kernel.org/all/20230807141730.1117278-1-steve.wahl%40hpe.com --- arch/x86/kernel/apic/x2apic_uv_x.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/arch/x86/kernel/apic/x2apic_uv_x.c b/arch/x86/kernel/apic/x2apic_uv_x.c index d9f5d7492f83..205cee567629 100644 --- a/arch/x86/kernel/apic/x2apic_uv_x.c +++ b/arch/x86/kernel/apic/x2apic_uv_x.c @@ -1533,7 +1533,7 @@ static void __init build_socket_tables(void) { struct uv_gam_range_entry *gre = uv_gre_table; int nums, numn, nump; - int cpu, i, lnid; + int i, lnid, apicid; int minsock = _min_socket; int maxsock = _max_socket; int minpnode = _min_pnode; @@ -1584,15 +1584,14 @@ static void __init build_socket_tables(void) /* Set socket -> node values: */ lnid = NUMA_NO_NODE; - for_each_possible_cpu(cpu) { - int nid = cpu_to_node(cpu); - int apicid, sockid; + for (apicid = 0; apicid < ARRAY_SIZE(__apicid_to_node); apicid++) { + int nid = __apicid_to_node[apicid]; + int sockid; - if (lnid == nid) + if ((nid == NUMA_NO_NODE) || (lnid == nid)) continue; lnid = nid; - apicid = per_cpu(x86_cpu_to_apicid, cpu); sockid = apicid >> uv_cpuid.socketid_shift; if (_socket_to_node[sockid - minsock] == SOCK_EMPTY) -- cgit v1.2.3 From df203da47f4428bc286fc99318936416253a321c Mon Sep 17 00:00:00 2001 From: Nigel Croxon Date: Mon, 11 Sep 2023 14:25:23 -0700 Subject: md/raid1: fix error: ISO C90 forbids mixed declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is a compile error when this commit is added: md: raid1: fix potential OOB in raid1_remove_disk() drivers/md/raid1.c: In function 'raid1_remove_disk': drivers/md/raid1.c:1844:9: error: ISO C90 forbids mixed declarations and code [-Werror=declaration-after-statement] 1844 |         struct raid1_info *p = conf->mirrors + number;     |         ^~~~~~ That's because the new code was inserted before the struct. The change is move the struct command above this commit. Fixes: 8b0472b50bcf ("md: raid1: fix potential OOB in raid1_remove_disk()") Signed-off-by: Nigel Croxon Signed-off-by: Song Liu Link: https://lore.kernel.org/r/46d929d0-2aab-4cf2-b2bf-338963e8ba5a@redhat.com --- drivers/md/raid1.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c index 4b30a1742162..2aabac773fe7 100644 --- a/drivers/md/raid1.c +++ b/drivers/md/raid1.c @@ -1837,12 +1837,11 @@ static int raid1_remove_disk(struct mddev *mddev, struct md_rdev *rdev) struct r1conf *conf = mddev->private; int err = 0; int number = rdev->raid_disk; + struct raid1_info *p = conf->mirrors + number; if (unlikely(number >= conf->raid_disks)) goto abort; - struct raid1_info *p = conf->mirrors + number; - if (rdev != p->rdev) p = conf->mirrors + conf->raid_disks + number; -- cgit v1.2.3 From 81faf9e0c3d39d47c6825469591d60a2cd0bbe10 Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Mon, 28 Aug 2023 14:18:23 -0400 Subject: drm/amdkfd: Fix reg offset for setting CWSR grace period This patch fixes the case where the code currently passes absolute register address and not the reg offset, which HWS expects, when sending the PM4 packet to set/update CWSR grace period. Additionally, cleanup the signature of build_grace_period_packet_info function as it no longer needs the inst parameter. Signed-off-by: Mukul Joshi Reviewed-by: Jonathan Kim Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c | 3 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h | 3 +-- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c | 6 ++---- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h | 3 +-- drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c | 3 +-- drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c | 3 +-- drivers/gpu/drm/amd/include/kgd_kfd_interface.h | 3 +-- 7 files changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c index f1f2c24de081..69810b3f1c63 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.c @@ -980,8 +980,7 @@ void kgd_gfx_v10_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst) + uint32_t *reg_data) { *reg_data = wait_times; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h index ecaead24e8c9..67bcaa3d4226 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v10.h @@ -55,5 +55,4 @@ void kgd_gfx_v10_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c index fa5ee96f8845..3c45a188b701 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.c @@ -1103,8 +1103,7 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst) + uint32_t *reg_data) { *reg_data = wait_times; @@ -1120,8 +1119,7 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, SCH_WAVE, grace_period); - *reg_offset = SOC15_REG_OFFSET(GC, GET_INST(GC, inst), - mmCP_IQ_WAIT_TIME2); + *reg_offset = SOC15_REG_OFFSET(GC, 0, mmCP_IQ_WAIT_TIME2); } void kgd_gfx_v9_program_trap_handler_settings(struct amdgpu_device *adev, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h index 936e501908ce..ce424615f59b 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd_gfx_v9.h @@ -100,5 +100,4 @@ void kgd_gfx_v9_build_grace_period_packet_info(struct amdgpu_device *adev, uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c index b166f30f083e..8a6cb41444a4 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device_queue_manager.c @@ -1677,8 +1677,7 @@ static int start_cpsch(struct device_queue_manager *dqm) dqm->dev->kfd2kgd->build_grace_period_packet_info( dqm->dev->adev, dqm->wait_times, grace_period, ®_offset, - &dqm->wait_times, - ffs(dqm->dev->xcc_mask) - 1); + &dqm->wait_times); } dqm_unlock(dqm); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c index 8ce6f5200905..1a03173e2313 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_packet_manager_v9.c @@ -299,8 +299,7 @@ static int pm_set_grace_period_v9(struct packet_manager *pm, pm->dqm->wait_times, grace_period, ®_offset, - ®_data, - 0); + ®_data); if (grace_period == USE_DEFAULT_GRACE_PERIOD) reg_data = pm->dqm->wait_times; diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index 8433f99f6667..f3f40dbb8ff7 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -326,8 +326,7 @@ struct kfd2kgd_calls { uint32_t wait_times, uint32_t grace_period, uint32_t *reg_offset, - uint32_t *reg_data, - uint32_t inst); + uint32_t *reg_data); void (*get_cu_occupancy)(struct amdgpu_device *adev, int pasid, int *wave_cnt, int *max_waves_per_cu, uint32_t inst); void (*program_trap_handler_settings)(struct amdgpu_device *adev, -- cgit v1.2.3 From 2f06b27444f928a79389b149247508bdad54252b Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Tue, 29 Aug 2023 12:06:09 -0400 Subject: drm/amdkfd: Fix unaligned 64-bit doorbell warning This patch fixes the following unaligned 64-bit doorbell warning seen when submitting packets on HIQ on GFX v9.4.3 by making the HIQ doorbell 64-bit aligned. The warning is seen when GPU is loaded in any mode other than SPX mode. [ +0.000301] ------------[ cut here ]------------ [ +0.000003] Unaligned 64-bit doorbell [ +0.000030] WARNING: /amdkfd/kfd_doorbell.c:339 write_kernel_doorbell64+0x72/0x80 [ +0.000003] RIP: 0010:write_kernel_doorbell64+0x72/0x80 [ +0.000004] RSP: 0018:ffffc90004287730 EFLAGS: 00010246 [ +0.000005] RAX: 0000000000000000 RBX: 0000000000000000 RCX: 0000000000000000 [ +0.000003] RDX: 0000000000000001 RSI: ffffffff82837c71 RDI: 00000000ffffffff [ +0.000003] RBP: ffffc90004287748 R08: 0000000000000003 R09: 0000000000000001 [ +0.000002] R10: 000000000000001a R11: ffff88a034008198 R12: ffffc900013bd004 [ +0.000003] R13: 0000000000000008 R14: ffffc900042877b0 R15: 000000000000007f [ +0.000003] FS: 00007fa8c7b62000(0000) GS:ffff889f88400000(0000) knlGS:0000000000000000 [ +0.000004] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ +0.000003] CR2: 000056111c45aaf0 CR3: 00000001414f2002 CR4: 0000000000770ee0 [ +0.000003] PKRU: 55555554 [ +0.000002] Call Trace: [ +0.000004] [ +0.000006] kq_submit_packet+0x45/0x50 [amdgpu] [ +0.000524] pm_send_set_resources+0x7f/0xc0 [amdgpu] [ +0.000500] set_sched_resources+0xe4/0x160 [amdgpu] [ +0.000503] start_cpsch+0x1c5/0x2a0 [amdgpu] [ +0.000497] kgd2kfd_device_init.cold+0x816/0xb42 [amdgpu] [ +0.000743] amdgpu_amdkfd_device_init+0x15f/0x1f0 [amdgpu] [ +0.000602] amdgpu_device_init.cold+0x1813/0x2176 [amdgpu] [ +0.000684] ? pci_bus_read_config_word+0x4a/0x80 [ +0.000012] ? do_pci_enable_device+0xdc/0x110 [ +0.000008] amdgpu_driver_load_kms+0x1a/0x110 [amdgpu] [ +0.000545] amdgpu_pci_probe+0x197/0x400 [amdgpu] Fixes: c31866651086 ("drm/amdgpu: use doorbell mgr for kfd kernel doorbells") Signed-off-by: Mukul Joshi Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c index c2e0b79dcc6d..7b38537c7c99 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_doorbell.c @@ -162,6 +162,7 @@ void __iomem *kfd_get_kernel_doorbell(struct kfd_dev *kfd, return NULL; *doorbell_off = amdgpu_doorbell_index_on_bar(kfd->adev, kfd->doorbells, inx); + inx *= 2; pr_debug("Get kernel queue doorbell\n" " doorbell offset == 0x%08X\n" @@ -176,6 +177,7 @@ void kfd_release_kernel_doorbell(struct kfd_dev *kfd, u32 __iomem *db_addr) unsigned int inx; inx = (unsigned int)(db_addr - kfd->doorbell_kernel_ptr); + inx /= 2; mutex_lock(&kfd->doorbell_mutex); __clear_bit(inx, kfd->doorbell_bitmap); -- cgit v1.2.3 From 97e3c6a853f2af9145daf0c6ca25bcdf55c759d4 Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Fri, 25 Aug 2023 11:59:09 -0400 Subject: drm/amdgpu: Store CU info from all XCCs for GFX v9.4.3 Currently, we store CU info only for a single XCC assuming that it is the same for all XCCs. However, that may not be true. As a result, store CU info for all XCCs. This info is later used for CU masking. Signed-off-by: Mukul Joshi Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c | 2 +- drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h | 3 +- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 2 +- drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c | 2 +- drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c | 4 +- drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c | 76 +++++++++++-------------- drivers/gpu/drm/amd/amdkfd/kfd_crat.c | 3 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c | 8 ++- drivers/gpu/drm/amd/amdkfd/kfd_topology.c | 11 ++-- drivers/gpu/drm/amd/include/kgd_kfd_interface.h | 6 +- 14 files changed, 60 insertions(+), 65 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c index cdf6087706aa..25d5fda5b243 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_amdkfd.c @@ -478,7 +478,7 @@ void amdgpu_amdkfd_get_cu_info(struct amdgpu_device *adev, struct kfd_cu_info *c cu_info->cu_active_number = acu_info.number; cu_info->cu_ao_mask = acu_info.ao_cu_mask; memcpy(&cu_info->cu_bitmap[0], &acu_info.bitmap[0], - sizeof(acu_info.bitmap)); + sizeof(cu_info->cu_bitmap)); cu_info->num_shader_engines = adev->gfx.config.max_shader_engines; cu_info->num_shader_arrays_per_engine = adev->gfx.config.max_sh_per_se; cu_info->num_cu_per_sh = adev->gfx.config.max_cu_per_sh; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index 395c1768b9fc..0ca95c4d4bfb 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -43,6 +43,7 @@ #define AMDGPU_GFX_LBPW_DISABLED_MODE 0x00000008L #define AMDGPU_MAX_GC_INSTANCES 8 +#define KGD_MAX_QUEUES 128 #define AMDGPU_MAX_GFX_QUEUES KGD_MAX_QUEUES #define AMDGPU_MAX_COMPUTE_QUEUES KGD_MAX_QUEUES @@ -257,7 +258,7 @@ struct amdgpu_cu_info { uint32_t number; uint32_t ao_cu_mask; uint32_t ao_cu_bitmap[4][4]; - uint32_t bitmap[4][4]; + uint32_t bitmap[AMDGPU_MAX_GC_INSTANCES][4][4]; }; struct amdgpu_gfx_ras { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 99f4df133ed3..2cd2ecebf465 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -839,7 +839,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) memcpy(&dev_info->cu_ao_bitmap[0], &adev->gfx.cu_info.ao_cu_bitmap[0], sizeof(adev->gfx.cu_info.ao_cu_bitmap)); memcpy(&dev_info->cu_bitmap[0], &adev->gfx.cu_info.bitmap[0], - sizeof(adev->gfx.cu_info.bitmap)); + sizeof(dev_info->cu_bitmap)); dev_info->vram_type = adev->gmc.vram_type; dev_info->vram_bit_width = adev->gmc.vram_width; dev_info->vce_harvest_config = adev->vce.harvest_config; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index 0aee9c8288a2..9032d7a24d7c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -9449,7 +9449,7 @@ static int gfx_v10_0_get_cu_info(struct amdgpu_device *adev, gfx_v10_0_set_user_wgp_inactive_bitmap_per_sh( adev, disable_masks[i * 2 + j]); bitmap = gfx_v10_0_get_cu_active_bitmap_per_sh(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 5c3db694afa8..762d7a19f1be 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -6368,7 +6368,7 @@ static int gfx_v11_0_get_cu_info(struct amdgpu_device *adev, * SE6: {SH0,SH1} --> {bitmap[2][2], bitmap[2][3]} * SE7: {SH0,SH1} --> {bitmap[3][2], bitmap[3][3]} */ - cu_info->bitmap[i % 4][j + (i / 4) * 2] = bitmap; + cu_info->bitmap[0][i % 4][j + (i / 4) * 2] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c index da6caff78c22..34f9211b2679 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v6_0.c @@ -3577,7 +3577,7 @@ static void gfx_v6_0_get_cu_info(struct amdgpu_device *adev) gfx_v6_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v6_0_get_cu_enabled(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c index 90b034b173c1..c2faf6b4c2fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v7_0.c @@ -5119,7 +5119,7 @@ static void gfx_v7_0_get_cu_info(struct amdgpu_device *adev) gfx_v7_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v7_0_get_cu_active_bitmap(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index 51c1745c8369..885ebd703260 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -7121,7 +7121,7 @@ static void gfx_v8_0_get_cu_info(struct amdgpu_device *adev) gfx_v8_0_set_user_cu_inactive_bitmap( adev, disable_masks[i * 2 + j]); bitmap = gfx_v8_0_get_cu_active_bitmap(adev); - cu_info->bitmap[i][j] = bitmap; + cu_info->bitmap[0][i][j] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c index 458faf657042..fd61574a737c 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_0.c @@ -1499,7 +1499,7 @@ static void gfx_v9_0_init_always_on_cu_mask(struct amdgpu_device *adev) amdgpu_gfx_select_se_sh(adev, i, j, 0xffffffff, 0); for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { - if (cu_info->bitmap[i][j] & mask) { + if (cu_info->bitmap[0][i][j] & mask) { if (counter == pg_always_on_cu_num) WREG32_SOC15(GC, 0, mmRLC_PG_ALWAYS_ON_CU_MASK, cu_bitmap); if (counter < always_on_cu_num) @@ -7233,7 +7233,7 @@ static int gfx_v9_0_get_cu_info(struct amdgpu_device *adev, * SE6,SH0 --> bitmap[2][1] * SE7,SH0 --> bitmap[3][1] */ - cu_info->bitmap[i % 4][j + i / 4] = bitmap; + cu_info->bitmap[0][i % 4][j + i / 4] = bitmap; for (k = 0; k < adev->gfx.config.max_cu_per_sh; k ++) { if (bitmap & mask) { diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index 0a26a00074a6..18ce5fe45f6f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -4259,7 +4259,7 @@ static void gfx_v9_4_3_set_gds_init(struct amdgpu_device *adev) } static void gfx_v9_4_3_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, - u32 bitmap) + u32 bitmap, int xcc_id) { u32 data; @@ -4269,15 +4269,15 @@ static void gfx_v9_4_3_set_user_cu_inactive_bitmap(struct amdgpu_device *adev, data = bitmap << GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; data &= GC_USER_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; - WREG32_SOC15(GC, GET_INST(GC, 0), regGC_USER_SHADER_ARRAY_CONFIG, data); + WREG32_SOC15(GC, GET_INST(GC, xcc_id), regGC_USER_SHADER_ARRAY_CONFIG, data); } -static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev) +static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev, int xcc_id) { u32 data, mask; - data = RREG32_SOC15(GC, GET_INST(GC, 0), regCC_GC_SHADER_ARRAY_CONFIG); - data |= RREG32_SOC15(GC, GET_INST(GC, 0), regGC_USER_SHADER_ARRAY_CONFIG); + data = RREG32_SOC15(GC, GET_INST(GC, xcc_id), regCC_GC_SHADER_ARRAY_CONFIG); + data |= RREG32_SOC15(GC, GET_INST(GC, xcc_id), regGC_USER_SHADER_ARRAY_CONFIG); data &= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS_MASK; data >>= CC_GC_SHADER_ARRAY_CONFIG__INACTIVE_CUS__SHIFT; @@ -4290,7 +4290,7 @@ static u32 gfx_v9_4_3_get_cu_active_bitmap(struct amdgpu_device *adev) static int gfx_v9_4_3_get_cu_info(struct amdgpu_device *adev, struct amdgpu_cu_info *cu_info) { - int i, j, k, counter, active_cu_number = 0; + int i, j, k, counter, xcc_id, active_cu_number = 0; u32 mask, bitmap, ao_bitmap, ao_cu_mask = 0; unsigned disable_masks[4 * 4]; @@ -4309,46 +4309,38 @@ static int gfx_v9_4_3_get_cu_info(struct amdgpu_device *adev, adev->gfx.config.max_sh_per_se); mutex_lock(&adev->grbm_idx_mutex); - for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { - for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { - mask = 1; - ao_bitmap = 0; - counter = 0; - gfx_v9_4_3_xcc_select_se_sh(adev, i, j, 0xffffffff, 0); - gfx_v9_4_3_set_user_cu_inactive_bitmap( - adev, disable_masks[i * adev->gfx.config.max_sh_per_se + j]); - bitmap = gfx_v9_4_3_get_cu_active_bitmap(adev); - - /* - * The bitmap(and ao_cu_bitmap) in cu_info structure is - * 4x4 size array, and it's usually suitable for Vega - * ASICs which has 4*2 SE/SH layout. - * But for Arcturus, SE/SH layout is changed to 8*1. - * To mostly reduce the impact, we make it compatible - * with current bitmap array as below: - * SE4,SH0 --> bitmap[0][1] - * SE5,SH0 --> bitmap[1][1] - * SE6,SH0 --> bitmap[2][1] - * SE7,SH0 --> bitmap[3][1] - */ - cu_info->bitmap[i % 4][j + i / 4] = bitmap; - - for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { - if (bitmap & mask) { - if (counter < adev->gfx.config.max_cu_per_sh) - ao_bitmap |= mask; - counter++; + for (xcc_id = 0; xcc_id < NUM_XCC(adev->gfx.xcc_mask); xcc_id++) { + for (i = 0; i < adev->gfx.config.max_shader_engines; i++) { + for (j = 0; j < adev->gfx.config.max_sh_per_se; j++) { + mask = 1; + ao_bitmap = 0; + counter = 0; + gfx_v9_4_3_xcc_select_se_sh(adev, i, j, 0xffffffff, xcc_id); + gfx_v9_4_3_set_user_cu_inactive_bitmap( + adev, + disable_masks[i * adev->gfx.config.max_sh_per_se + j], + xcc_id); + bitmap = gfx_v9_4_3_get_cu_active_bitmap(adev, xcc_id); + + cu_info->bitmap[xcc_id][i][j] = bitmap; + + for (k = 0; k < adev->gfx.config.max_cu_per_sh; k++) { + if (bitmap & mask) { + if (counter < adev->gfx.config.max_cu_per_sh) + ao_bitmap |= mask; + counter++; + } + mask <<= 1; } - mask <<= 1; + active_cu_number += counter; + if (i < 2 && j < 2) + ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8)); + cu_info->ao_cu_bitmap[i][j] = ao_bitmap; } - active_cu_number += counter; - if (i < 2 && j < 2) - ao_cu_mask |= (ao_bitmap << (i * 16 + j * 8)); - cu_info->ao_cu_bitmap[i % 4][j + i / 4] = ao_bitmap; } + gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, + xcc_id); } - gfx_v9_4_3_xcc_select_se_sh(adev, 0xffffffff, 0xffffffff, 0xffffffff, - 0); mutex_unlock(&adev->grbm_idx_mutex); cu_info->number = active_cu_number; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c index 86fb7ac7982a..f76b7aee5c0a 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.c @@ -2087,7 +2087,8 @@ static int kfd_create_vcrat_image_gpu(void *pcrat_image, amdgpu_amdkfd_get_cu_info(kdev->adev, &cu_info); cu->num_simd_per_cu = cu_info.simd_per_cu; - cu->num_simd_cores = cu_info.simd_per_cu * cu_info.cu_active_number; + cu->num_simd_cores = cu_info.simd_per_cu * + (cu_info.cu_active_number / kdev->kfd->num_nodes); cu->max_waves_simd = cu_info.max_waves_per_simd; cu->wave_front_size = cu_info.wave_front_size; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c index d01bb57733b3..763966236658 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c @@ -104,11 +104,13 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, bool wgp_mode_req = KFD_GC_VERSION(mm->dev) >= IP_VERSION(10, 0, 0); uint32_t en_mask = wgp_mode_req ? 0x3 : 0x1; int i, se, sh, cu, cu_bitmap_sh_mul, inc = wgp_mode_req ? 2 : 1; + uint32_t cu_active_per_node; amdgpu_amdkfd_get_cu_info(mm->dev->adev, &cu_info); - if (cu_mask_count > cu_info.cu_active_number) - cu_mask_count = cu_info.cu_active_number; + cu_active_per_node = cu_info.cu_active_number / mm->dev->kfd->num_nodes; + if (cu_mask_count > cu_active_per_node) + cu_mask_count = cu_active_per_node; /* Exceeding these bounds corrupts the stack and indicates a coding error. * Returning with no CU's enabled will hang the queue, which should be @@ -141,7 +143,7 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, for (se = 0; se < cu_info.num_shader_engines; se++) for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) cu_per_sh[se][sh] = hweight32( - cu_info.cu_bitmap[se % 4][sh + (se / 4) * cu_bitmap_sh_mul]); + cu_info.cu_bitmap[0][se % 4][sh + (se / 4) * cu_bitmap_sh_mul]); /* Symmetrically map cu_mask to all SEs & SHs: * se_mask programs up to 2 SH in the upper and lower 16 bits. diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index ff98fded9534..c54795682dfb 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -450,8 +450,7 @@ static ssize_t node_show(struct kobject *kobj, struct attribute *attr, sysfs_show_32bit_prop(buffer, offs, "cpu_cores_count", dev->node_props.cpu_cores_count); sysfs_show_32bit_prop(buffer, offs, "simd_count", - dev->gpu ? (dev->node_props.simd_count * - NUM_XCC(dev->gpu->xcc_mask)) : 0); + dev->gpu ? dev->node_props.simd_count : 0); sysfs_show_32bit_prop(buffer, offs, "mem_banks_count", dev->node_props.mem_banks_count); sysfs_show_32bit_prop(buffer, offs, "caches_count", @@ -1604,7 +1603,7 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, int i, j, k; struct kfd_cache_properties *pcache = NULL; - cu_sibling_map_mask = cu_info->cu_bitmap[0][0]; + cu_sibling_map_mask = cu_info->cu_bitmap[0][0][0]; cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); first_active_cu = ffs(cu_sibling_map_mask); @@ -1647,7 +1646,7 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); k += 4; - cu_sibling_map_mask = cu_info->cu_bitmap[i % 4][j + i / 4]; + cu_sibling_map_mask = cu_info->cu_bitmap[0][i % 4][j + i / 4]; cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); } } @@ -1708,8 +1707,8 @@ static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, - pcu_info->cu_bitmap[i % 4][j + i / 4], ct, - cu_processor_id, k); + pcu_info->cu_bitmap[0][i % 4][j + i / 4], ct, + cu_processor_id, k); if (ret < 0) break; diff --git a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h index f3f40dbb8ff7..3b5a56585c4b 100644 --- a/drivers/gpu/drm/amd/include/kgd_kfd_interface.h +++ b/drivers/gpu/drm/amd/include/kgd_kfd_interface.h @@ -31,12 +31,12 @@ #include #include #include +#include "amdgpu_irq.h" +#include "amdgpu_gfx.h" struct pci_dev; struct amdgpu_device; -#define KGD_MAX_QUEUES 128 - struct kfd_dev; struct kgd_mem; @@ -68,7 +68,7 @@ struct kfd_cu_info { uint32_t wave_front_size; uint32_t max_scratch_slots_per_cu; uint32_t lds_size; - uint32_t cu_bitmap[4][4]; + uint32_t cu_bitmap[AMDGPU_MAX_GC_INSTANCES][4][4]; }; /* For getting GPU local memory information from KGD */ -- cgit v1.2.3 From 0752e66e91fa86fa5481b04b22053363833ffb85 Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Fri, 25 Aug 2023 12:18:06 -0400 Subject: drm/amdkfd: Update cache info reporting for GFX v9.4.3 Update cache info reporting in sysfs to report the correct number of CUs and associated cache information based on different spatial partitioning modes. Signed-off-by: Mukul Joshi Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_crat.h | 4 ++ drivers/gpu/drm/amd/amdkfd/kfd_topology.c | 82 +++++++++++++++++-------------- drivers/gpu/drm/amd/amdkfd/kfd_topology.h | 2 +- 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_crat.h b/drivers/gpu/drm/amd/amdkfd/kfd_crat.h index 387a8ef49385..74c2d7a0d628 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_crat.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_crat.h @@ -79,6 +79,10 @@ struct crat_header { #define CRAT_SUBTYPE_IOLINK_AFFINITY 5 #define CRAT_SUBTYPE_MAX 6 +/* + * Do not change the value of CRAT_SIBLINGMAP_SIZE from 32 + * as it breaks the ABI. + */ #define CRAT_SIBLINGMAP_SIZE 32 /* diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index c54795682dfb..c8c75ff7cea8 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -1596,14 +1596,17 @@ static int fill_in_l1_pcache(struct kfd_cache_properties **props_ext, static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, struct kfd_gpu_cache_info *pcache_info, struct kfd_cu_info *cu_info, - int cache_type, unsigned int cu_processor_id) + int cache_type, unsigned int cu_processor_id, + struct kfd_node *knode) { unsigned int cu_sibling_map_mask; int first_active_cu; - int i, j, k; + int i, j, k, xcc, start, end; struct kfd_cache_properties *pcache = NULL; - cu_sibling_map_mask = cu_info->cu_bitmap[0][0][0]; + start = ffs(knode->xcc_mask) - 1; + end = start + NUM_XCC(knode->xcc_mask); + cu_sibling_map_mask = cu_info->cu_bitmap[start][0][0]; cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); first_active_cu = ffs(cu_sibling_map_mask); @@ -1638,16 +1641,18 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, cu_sibling_map_mask = cu_sibling_map_mask >> (first_active_cu - 1); k = 0; - for (i = 0; i < cu_info->num_shader_engines; i++) { - for (j = 0; j < cu_info->num_shader_arrays_per_engine; j++) { - pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF); - pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF); - pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF); - pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); - k += 4; - - cu_sibling_map_mask = cu_info->cu_bitmap[0][i % 4][j + i / 4]; - cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); + for (xcc = start; xcc < end; xcc++) { + for (i = 0; i < cu_info->num_shader_engines; i++) { + for (j = 0; j < cu_info->num_shader_arrays_per_engine; j++) { + pcache->sibling_map[k] = (uint8_t)(cu_sibling_map_mask & 0xFF); + pcache->sibling_map[k+1] = (uint8_t)((cu_sibling_map_mask >> 8) & 0xFF); + pcache->sibling_map[k+2] = (uint8_t)((cu_sibling_map_mask >> 16) & 0xFF); + pcache->sibling_map[k+3] = (uint8_t)((cu_sibling_map_mask >> 24) & 0xFF); + k += 4; + + cu_sibling_map_mask = cu_info->cu_bitmap[xcc][i % 4][j + i / 4]; + cu_sibling_map_mask &= ((1 << pcache_info[cache_type].num_cu_shared) - 1); + } } } pcache->sibling_map_size = k; @@ -1665,7 +1670,7 @@ static int fill_in_l2_l3_pcache(struct kfd_cache_properties **props_ext, static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct kfd_node *kdev) { struct kfd_gpu_cache_info *pcache_info = NULL; - int i, j, k; + int i, j, k, xcc, start, end; int ct = 0; unsigned int cu_processor_id; int ret; @@ -1699,37 +1704,42 @@ static void kfd_fill_cache_non_crat_info(struct kfd_topology_device *dev, struct * then it will consider only one CU from * the shared unit */ + start = ffs(kdev->xcc_mask) - 1; + end = start + NUM_XCC(kdev->xcc_mask); + for (ct = 0; ct < num_of_cache_types; ct++) { cu_processor_id = gpu_processor_id; if (pcache_info[ct].cache_level == 1) { - for (i = 0; i < pcu_info->num_shader_engines; i++) { - for (j = 0; j < pcu_info->num_shader_arrays_per_engine; j++) { - for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { - - ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, - pcu_info->cu_bitmap[0][i % 4][j + i / 4], ct, - cu_processor_id, k); - - if (ret < 0) - break; - - if (!ret) { - num_of_entries++; - list_add_tail(&props_ext->list, &dev->cache_props); + for (xcc = start; xcc < end; xcc++) { + for (i = 0; i < pcu_info->num_shader_engines; i++) { + for (j = 0; j < pcu_info->num_shader_arrays_per_engine; j++) { + for (k = 0; k < pcu_info->num_cu_per_sh; k += pcache_info[ct].num_cu_shared) { + + ret = fill_in_l1_pcache(&props_ext, pcache_info, pcu_info, + pcu_info->cu_bitmap[xcc][i % 4][j + i / 4], ct, + cu_processor_id, k); + + if (ret < 0) + break; + + if (!ret) { + num_of_entries++; + list_add_tail(&props_ext->list, &dev->cache_props); + } + + /* Move to next CU block */ + num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <= + pcu_info->num_cu_per_sh) ? + pcache_info[ct].num_cu_shared : + (pcu_info->num_cu_per_sh - k); + cu_processor_id += num_cu_shared; } - - /* Move to next CU block */ - num_cu_shared = ((k + pcache_info[ct].num_cu_shared) <= - pcu_info->num_cu_per_sh) ? - pcache_info[ct].num_cu_shared : - (pcu_info->num_cu_per_sh - k); - cu_processor_id += num_cu_shared; } } } } else { ret = fill_in_l2_l3_pcache(&props_ext, pcache_info, - pcu_info, ct, cu_processor_id); + pcu_info, ct, cu_processor_id, kdev); if (ret < 0) break; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.h b/drivers/gpu/drm/amd/amdkfd/kfd_topology.h index dea32a9e5506..27386ce9a021 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.h @@ -89,7 +89,7 @@ struct kfd_mem_properties { struct attribute attr; }; -#define CACHE_SIBLINGMAP_SIZE 64 +#define CACHE_SIBLINGMAP_SIZE 128 struct kfd_cache_properties { struct list_head list; -- cgit v1.2.3 From fc6efed2c728c9c10b058512fc9c1613f870a8e8 Mon Sep 17 00:00:00 2001 From: Mukul Joshi Date: Tue, 22 Aug 2023 11:35:25 -0400 Subject: drm/amdkfd: Update CU masking for GFX 9.4.3 The CU mask passed from user-space will change based on different spatial partitioning mode. As a result, update CU masking code for GFX9.4.3 to work for all partitioning modes. Signed-off-by: Mukul Joshi Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c | 28 +++++++++++---- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c | 2 +- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c | 46 +++++++++++++++--------- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c | 2 +- 7 files changed, 56 insertions(+), 28 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c index 763966236658..447829c22295 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.c @@ -97,14 +97,16 @@ void free_mqd_hiq_sdma(struct mqd_manager *mm, void *mqd, void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, const uint32_t *cu_mask, uint32_t cu_mask_count, - uint32_t *se_mask) + uint32_t *se_mask, uint32_t inst) { struct kfd_cu_info cu_info; uint32_t cu_per_sh[KFD_MAX_NUM_SE][KFD_MAX_NUM_SH_PER_SE] = {0}; bool wgp_mode_req = KFD_GC_VERSION(mm->dev) >= IP_VERSION(10, 0, 0); uint32_t en_mask = wgp_mode_req ? 0x3 : 0x1; - int i, se, sh, cu, cu_bitmap_sh_mul, inc = wgp_mode_req ? 2 : 1; + int i, se, sh, cu, cu_bitmap_sh_mul, cu_inc = wgp_mode_req ? 2 : 1; uint32_t cu_active_per_node; + int inc = cu_inc * NUM_XCC(mm->dev->xcc_mask); + int xcc_inst = inst + ffs(mm->dev->xcc_mask) - 1; amdgpu_amdkfd_get_cu_info(mm->dev->adev, &cu_info); @@ -143,7 +145,8 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, for (se = 0; se < cu_info.num_shader_engines; se++) for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) cu_per_sh[se][sh] = hweight32( - cu_info.cu_bitmap[0][se % 4][sh + (se / 4) * cu_bitmap_sh_mul]); + cu_info.cu_bitmap[xcc_inst][se % 4][sh + (se / 4) * + cu_bitmap_sh_mul]); /* Symmetrically map cu_mask to all SEs & SHs: * se_mask programs up to 2 SH in the upper and lower 16 bits. @@ -166,20 +169,33 @@ void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, * cu_mask[0] bit8 -> se_mask[0] bit1 (SE0,SH0,CU1) * ... * + * For GFX 9.4.3, the following code only looks at a + * subset of the cu_mask corresponding to the inst parameter. + * If we have n XCCs under one GPU node + * cu_mask[0] bit0 -> XCC0 se_mask[0] bit0 (XCC0,SE0,SH0,CU0) + * cu_mask[0] bit1 -> XCC1 se_mask[0] bit0 (XCC1,SE0,SH0,CU0) + * .. + * cu_mask[0] bitn -> XCCn se_mask[0] bit0 (XCCn,SE0,SH0,CU0) + * cu_mask[0] bit n+1 -> XCC0 se_mask[1] bit0 (XCC0,SE1,SH0,CU0) + * + * For example, if there are 6 XCCs under 1 KFD node, this code + * running for each inst, will look at the bits as: + * inst, inst + 6, inst + 12... + * * First ensure all CUs are disabled, then enable user specified CUs. */ for (i = 0; i < cu_info.num_shader_engines; i++) se_mask[i] = 0; - i = 0; - for (cu = 0; cu < 16; cu += inc) { + i = inst; + for (cu = 0; cu < 16; cu += cu_inc) { for (sh = 0; sh < cu_info.num_shader_arrays_per_engine; sh++) { for (se = 0; se < cu_info.num_shader_engines; se++) { if (cu_per_sh[se][sh] > cu) { if (cu_mask[i / 32] & (en_mask << (i % 32))) se_mask[se] |= en_mask << (cu + sh * 16); i += inc; - if (i == cu_mask_count) + if (i >= cu_mask_count) return; } } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h index 23158db7da03..57bf5e513f4d 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager.h @@ -138,7 +138,7 @@ void free_mqd_hiq_sdma(struct mqd_manager *mm, void *mqd, void mqd_symmetrically_map_cu_mask(struct mqd_manager *mm, const uint32_t *cu_mask, uint32_t cu_mask_count, - uint32_t *se_mask); + uint32_t *se_mask, uint32_t inst); int kfd_hiq_load_mqd_kiq(struct mqd_manager *mm, void *mqd, uint32_t pipe_id, uint32_t queue_id, diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c index ee1d32d957f2..1a4a69943c71 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_cik.c @@ -52,7 +52,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c index 83699392c808..8b7fed913526 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v10.c @@ -52,7 +52,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index 0bbf0edbabd4..964b5d50a77e 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -71,7 +71,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, } mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m->compute_static_thread_mgmt_se0 = se_mask[0]; m->compute_static_thread_mgmt_se1 = se_mask[1]; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c index e23d32f35607..42d881809dc7 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v9.c @@ -60,7 +60,7 @@ static inline struct v9_sdma_mqd *get_sdma_mqd(void *mqd) } static void update_cu_mask(struct mqd_manager *mm, void *mqd, - struct mqd_update_info *minfo) + struct mqd_update_info *minfo, uint32_t inst) { struct v9_mqd *m; uint32_t se_mask[KFD_MAX_NUM_SE] = {0}; @@ -69,27 +69,36 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, inst); m = get_mqd(mqd); + m->compute_static_thread_mgmt_se0 = se_mask[0]; m->compute_static_thread_mgmt_se1 = se_mask[1]; m->compute_static_thread_mgmt_se2 = se_mask[2]; m->compute_static_thread_mgmt_se3 = se_mask[3]; - m->compute_static_thread_mgmt_se4 = se_mask[4]; - m->compute_static_thread_mgmt_se5 = se_mask[5]; - m->compute_static_thread_mgmt_se6 = se_mask[6]; - m->compute_static_thread_mgmt_se7 = se_mask[7]; - - pr_debug("update cu mask to %#x %#x %#x %#x %#x %#x %#x %#x\n", - m->compute_static_thread_mgmt_se0, - m->compute_static_thread_mgmt_se1, - m->compute_static_thread_mgmt_se2, - m->compute_static_thread_mgmt_se3, - m->compute_static_thread_mgmt_se4, - m->compute_static_thread_mgmt_se5, - m->compute_static_thread_mgmt_se6, - m->compute_static_thread_mgmt_se7); + if (KFD_GC_VERSION(mm->dev) != IP_VERSION(9, 4, 3)) { + m->compute_static_thread_mgmt_se4 = se_mask[4]; + m->compute_static_thread_mgmt_se5 = se_mask[5]; + m->compute_static_thread_mgmt_se6 = se_mask[6]; + m->compute_static_thread_mgmt_se7 = se_mask[7]; + + pr_debug("update cu mask to %#x %#x %#x %#x %#x %#x %#x %#x\n", + m->compute_static_thread_mgmt_se0, + m->compute_static_thread_mgmt_se1, + m->compute_static_thread_mgmt_se2, + m->compute_static_thread_mgmt_se3, + m->compute_static_thread_mgmt_se4, + m->compute_static_thread_mgmt_se5, + m->compute_static_thread_mgmt_se6, + m->compute_static_thread_mgmt_se7); + } else { + pr_debug("inst: %u, update cu mask to %#x %#x %#x %#x\n", + inst, m->compute_static_thread_mgmt_se0, + m->compute_static_thread_mgmt_se1, + m->compute_static_thread_mgmt_se2, + m->compute_static_thread_mgmt_se3); + } } static void set_priority(struct v9_mqd *m, struct queue_properties *q) @@ -290,7 +299,8 @@ static void update_mqd(struct mqd_manager *mm, void *mqd, if (mm->dev->kfd->cwsr_enabled && q->ctx_save_restore_area_address) m->cp_hqd_ctx_save_control = 0; - update_cu_mask(mm, mqd, minfo); + if (KFD_GC_VERSION(mm->dev) != IP_VERSION(9, 4, 3)) + update_cu_mask(mm, mqd, minfo, 0); set_priority(m, q); q->is_active = QUEUE_IS_ACTIVE(*q); @@ -676,6 +686,8 @@ static void update_mqd_v9_4_3(struct mqd_manager *mm, void *mqd, m = get_mqd(mqd + size * xcc); update_mqd(mm, m, q, minfo); + update_cu_mask(mm, mqd, minfo, xcc); + if (q->format == KFD_QUEUE_FORMAT_AQL) { switch (xcc) { case 0: diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c index 657c37822980..3e1a574d4ea6 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_vi.c @@ -55,7 +55,7 @@ static void update_cu_mask(struct mqd_manager *mm, void *mqd, return; mqd_symmetrically_map_cu_mask(mm, - minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask); + minfo->cu_mask.ptr, minfo->cu_mask.count, se_mask, 0); m = get_mqd(mqd); m->compute_static_thread_mgmt_se0 = se_mask[0]; -- cgit v1.2.3 From 6be6d112419713334ddd9c01f219ca16adaa4c76 Mon Sep 17 00:00:00 2001 From: Chengming Zhou Date: Fri, 8 Sep 2023 08:57:02 +0800 Subject: blk-mq: fix tags UAF when shrinking q->nr_hw_queues When nr_hw_queues shrink, we free the excess tags before realloc'ing hw_ctxs for each queue. During that resize, we may need to access those tags, like blk_mq_tag_idle(hctx) will access queue shared tags. This can cause a slab use-after-free, as reported by KASAN. Fix it by moving the releasing of excess tags to the end. Fixes: e1dd7bc93029 ("blk-mq: fix tags leak when shrink nr_hw_queues") Reported-by: Yi Zhang Closes: https://lore.kernel.org/all/CAHj4cs_CK63uoDpGBGZ6DN4OCTpzkR3UaVgK=LX8Owr8ej2ieQ@mail.gmail.com/ Cc: Ming Lei Signed-off-by: Chengming Zhou Reviewed-by: Hannes Reinecke Link: https://lore.kernel.org/r/20230908005702.2183908-1-chengming.zhou@linux.dev Signed-off-by: Jens Axboe --- block/blk-mq.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/block/blk-mq.c b/block/blk-mq.c index ec922c6bccbe..1fafd54dce3c 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -4405,11 +4405,8 @@ static int blk_mq_realloc_tag_set_tags(struct blk_mq_tag_set *set, struct blk_mq_tags **new_tags; int i; - if (set->nr_hw_queues >= new_nr_hw_queues) { - for (i = new_nr_hw_queues; i < set->nr_hw_queues; i++) - __blk_mq_free_map_and_rqs(set, i); + if (set->nr_hw_queues >= new_nr_hw_queues) goto done; - } new_tags = kcalloc_node(new_nr_hw_queues, sizeof(struct blk_mq_tags *), GFP_KERNEL, set->numa_node); @@ -4719,7 +4716,8 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, { struct request_queue *q; LIST_HEAD(head); - int prev_nr_hw_queues; + int prev_nr_hw_queues = set->nr_hw_queues; + int i; lockdep_assert_held(&set->tag_list_lock); @@ -4746,7 +4744,6 @@ static void __blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, blk_mq_sysfs_unregister_hctxs(q); } - prev_nr_hw_queues = set->nr_hw_queues; if (blk_mq_realloc_tag_set_tags(set, nr_hw_queues) < 0) goto reregister; @@ -4781,6 +4778,10 @@ switch_back: list_for_each_entry(q, &set->tag_list, tag_set_list) blk_mq_unfreeze_queue(q); + + /* Free the excess tags when nr_hw_queues shrink. */ + for (i = set->nr_hw_queues; i < prev_nr_hw_queues; i++) + __blk_mq_free_map_and_rqs(set, i); } void blk_mq_update_nr_hw_queues(struct blk_mq_tag_set *set, int nr_hw_queues) -- cgit v1.2.3 From ef064187a9709393a981a56cce1e31880fd97107 Mon Sep 17 00:00:00 2001 From: Yifan Zhang Date: Fri, 8 Sep 2023 16:46:39 +0800 Subject: drm/amd/display: fix the white screen issue when >= 64GB DRAM Dropping bit 31:4 of page table base is wrong, it makes page table base points to wrong address if phys addr is beyond 64GB; dropping page_table_start/end bit 31:4 is unnecessary since dcn20_vmid_setup will do that. Also, while we are at it, cleanup the assignments using upper_32_bits()/lower_32_bits() and AMDGPU_GPU_PAGE_SHIFT. Cc: stable@vger.kernel.org Link: https://gitlab.freedesktop.org/drm/amd/-/issues/2354 Fixes: 81d0bcf99009 ("drm/amdgpu: make display pinning more flexible (v2)") Acked-by: Harry Wentland Reviewed-by: Alex Deucher Signed-off-by: Yifan Zhang Co-developed-by: Hamza Mahfooz Signed-off-by: Hamza Mahfooz Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 88ba8b66de1f..6a0ea15936ae 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1274,11 +1274,15 @@ static void mmhub_read_system_context(struct amdgpu_device *adev, struct dc_phy_ pt_base = amdgpu_gmc_pd_addr(adev->gart.bo); - page_table_start.high_part = (u32)(adev->gmc.gart_start >> 44) & 0xF; - page_table_start.low_part = (u32)(adev->gmc.gart_start >> 12); - page_table_end.high_part = (u32)(adev->gmc.gart_end >> 44) & 0xF; - page_table_end.low_part = (u32)(adev->gmc.gart_end >> 12); - page_table_base.high_part = upper_32_bits(pt_base) & 0xF; + page_table_start.high_part = upper_32_bits(adev->gmc.gart_start >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_start.low_part = lower_32_bits(adev->gmc.gart_start >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_end.high_part = upper_32_bits(adev->gmc.gart_end >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_end.low_part = lower_32_bits(adev->gmc.gart_end >> + AMDGPU_GPU_PAGE_SHIFT); + page_table_base.high_part = upper_32_bits(pt_base); page_table_base.low_part = lower_32_bits(pt_base); pa_config->system_aperture.start_addr = (uint64_t)logical_addr_low << 18; -- cgit v1.2.3 From 169ed4ece8373f02f10642eae5240e3d1ef5c038 Mon Sep 17 00:00:00 2001 From: Hamza Mahfooz Date: Fri, 8 Sep 2023 10:36:44 -0400 Subject: Revert "drm/amd: Disable S/G for APUs when 64GB or more host memory" This reverts commit 70e64c4d522b732e31c6475a3be2349de337d321. Since, we now have an actual fix for this issue, we can get rid of this workaround as it can cause pin failures if enough VRAM isn't carved out by the BIOS. Cc: stable@vger.kernel.org # 6.1+ Acked-by: Harry Wentland Reviewed-by: Alex Deucher Signed-off-by: Hamza Mahfooz Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu.h | 1 - drivers/gpu/drm/amd/amdgpu/amdgpu_device.c | 26 ----------------------- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 5 +++-- 3 files changed, 3 insertions(+), 29 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index dc2d53081e80..a79d53bdbe13 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -1293,7 +1293,6 @@ int amdgpu_device_gpu_recover(struct amdgpu_device *adev, void amdgpu_device_pci_config_reset(struct amdgpu_device *adev); int amdgpu_device_pci_reset(struct amdgpu_device *adev); bool amdgpu_device_need_post(struct amdgpu_device *adev); -bool amdgpu_sg_display_supported(struct amdgpu_device *adev); bool amdgpu_device_pcie_dynamic_switching_supported(void); bool amdgpu_device_should_use_aspm(struct amdgpu_device *adev); bool amdgpu_device_aspm_support_quirk(void); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index 3f001a50b34a..30c4f5cca02c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -1244,32 +1244,6 @@ bool amdgpu_device_need_post(struct amdgpu_device *adev) return true; } -/* - * On APUs with >= 64GB white flickering has been observed w/ SG enabled. - * Disable S/G on such systems until we have a proper fix. - * https://gitlab.freedesktop.org/drm/amd/-/issues/2354 - * https://gitlab.freedesktop.org/drm/amd/-/issues/2735 - */ -bool amdgpu_sg_display_supported(struct amdgpu_device *adev) -{ - switch (amdgpu_sg_display) { - case -1: - break; - case 0: - return false; - case 1: - return true; - default: - return false; - } - if ((totalram_pages() << (PAGE_SHIFT - 10)) + - (adev->gmc.real_vram_size / 1024) >= 64000000) { - DRM_WARN("Disabling S/G due to >=64GB RAM\n"); - return false; - } - return true; -} - /* * Intel hosts such as Raptor Lake and Sapphire Rapids don't support dynamic * speed switching. Until we have confirmation from Intel that a specific host diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 6a0ea15936ae..954906c515aa 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -1644,8 +1644,9 @@ static int amdgpu_dm_init(struct amdgpu_device *adev) } break; } - if (init_data.flags.gpu_vm_support) - init_data.flags.gpu_vm_support = amdgpu_sg_display_supported(adev); + if (init_data.flags.gpu_vm_support && + (amdgpu_sg_display == 0)) + init_data.flags.gpu_vm_support = false; if (init_data.flags.gpu_vm_support) adev->mode_info.gpu_vm_support = true; -- cgit v1.2.3 From 679fc891bf11845730b572fc44f8a0eb846aba29 Mon Sep 17 00:00:00 2001 From: Bhawanpreet Lakha Date: Tue, 22 Aug 2023 10:02:46 -0400 Subject: drm/amd/display: Add dirty rect support for Replay Dirty rect can be used with replay, so enable them to allow for more powersaving. Reviewed-by: Sun peng Li Acked-by: Stylon Wang Signed-off-by: Bhawanpreet Lakha Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 954906c515aa..ca129983a08b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -8078,7 +8078,8 @@ static void amdgpu_dm_commit_planes(struct drm_atomic_state *state, bundle->surface_updates[planes_count].plane_info = &bundle->plane_infos[planes_count]; - if (acrtc_state->stream->link->psr_settings.psr_feature_enabled) { + if (acrtc_state->stream->link->psr_settings.psr_feature_enabled || + acrtc_state->stream->link->replay_settings.replay_feature_enabled) { fill_dc_dirty_rects(plane, old_plane_state, new_plane_state, new_crtc_state, &bundle->flip_addrs[planes_count], -- cgit v1.2.3 From 81cc8779cf46d6323c83475706b61d9552230274 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 6 Sep 2023 13:54:38 +0300 Subject: drm/amdgpu: fix retry loop test This loop will exit with "retry" set to -1 if it fails but the code checks for if "retry" is zero. Fix this by changing post-op to a pre-op. --retry vs retry--. Fixes: e01eeffc3f86 ("drm/amd/pm: avoid driver getting empty metrics table for the first time") Reviewed-by: Evan Quan Signed-off-by: Dan Carpenter Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index 199a673b8120..de80e191a92c 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -336,7 +336,7 @@ static int smu_v13_0_6_setup_driver_pptable(struct smu_context *smu) /* Store one-time values in driver PPTable */ if (!pptable->Init) { - while (retry--) { + while (--retry) { ret = smu_v13_0_6_get_metrics_table(smu, NULL, true); if (ret) return ret; -- cgit v1.2.3 From f5b2c10b57615828b531bb0ae56bd6325a41167e Mon Sep 17 00:00:00 2001 From: Swapnil Patel Date: Thu, 17 Aug 2023 14:04:26 -0400 Subject: drm/amd/display: Don't check registers, if using AUX BL control [Why] Currently the driver looks DCN registers to access if BL is on or not. This check is not valid if we are using AUX based brightness control. This causes driver to not send out "backlight off" command during power off sequence as it already thinks it is off. [How] Only check DCN registers if we aren't using AUX based brightness control. Reviewed-by: Wenjing Liu Acked-by: Stylon Wang Signed-off-by: Swapnil Patel Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c index ad967b58d7be..478281f2a5ba 100644 --- a/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dce110/dce110_hw_sequencer.c @@ -964,7 +964,9 @@ void dce110_edp_backlight_control( return; } - if (link->panel_cntl) { + if (link->panel_cntl && !(link->dpcd_sink_ext_caps.bits.oled || + link->dpcd_sink_ext_caps.bits.hdr_aux_backlight_control == 1 || + link->dpcd_sink_ext_caps.bits.sdr_aux_backlight_control == 1)) { bool is_backlight_on = link->panel_cntl->funcs->is_panel_backlight_on(link->panel_cntl); if ((enable && is_backlight_on) || (!enable && !is_backlight_on)) { -- cgit v1.2.3 From 1832403cd41ca6b19b24e9d64f79cb08d920ca44 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Wed, 6 Sep 2023 11:35:04 -0400 Subject: drm/amdgpu/soc21: don't remap HDP registers for SR-IOV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This matches the behavior for soc15 and nv. Acked-by: Christian König Reviewed-by: Timmy Tsai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/soc21.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/amdgpu/soc21.c b/drivers/gpu/drm/amd/amdgpu/soc21.c index 40d23738ee4e..8b2ff2b281b0 100644 --- a/drivers/gpu/drm/amd/amdgpu/soc21.c +++ b/drivers/gpu/drm/amd/amdgpu/soc21.c @@ -766,7 +766,7 @@ static int soc21_common_hw_init(void *handle) * for the purpose of expose those registers * to process space */ - if (adev->nbio.funcs->remap_hdp_registers) + if (adev->nbio.funcs->remap_hdp_registers && !amdgpu_sriov_vf(adev)) adev->nbio.funcs->remap_hdp_registers(adev); /* enable the doorbell aperture */ adev->nbio.funcs->enable_doorbell_aperture(adev, true); -- cgit v1.2.3 From ab43213e7afd08ac68d4282060bacf309e70fd14 Mon Sep 17 00:00:00 2001 From: Alex Deucher Date: Thu, 7 Sep 2023 15:44:54 -0400 Subject: drm/amdgpu/nbio4.3: set proper rmmio_remap.reg_offset for SR-IOV Needed for HDP flush to work correctly. Reviewed-by: Timmy Tsai Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c index d5ed9e0e1a5f..e5b5b0f4940f 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v4_3.c @@ -345,6 +345,9 @@ static void nbio_v4_3_init_registers(struct amdgpu_device *adev) data &= ~RCC_DEV0_EPF2_STRAP2__STRAP_NO_SOFT_RESET_DEV0_F2_MASK; WREG32_SOC15(NBIO, 0, regRCC_DEV0_EPF2_STRAP2, data); } + if (amdgpu_sriov_vf(adev)) + adev->rmmio_remap.reg_offset = SOC15_REG_OFFSET(NBIO, 0, + regBIF_BX_DEV0_EPF0_VF0_HDP_MEM_COHERENCY_FLUSH_CNTL) << 2; } static u32 nbio_v4_3_get_rom_offset(struct amdgpu_device *adev) -- cgit v1.2.3 From ffd6bde302061aeee405ab364403af30210f0b99 Mon Sep 17 00:00:00 2001 From: Hawking Zhang Date: Fri, 8 Sep 2023 21:21:55 +0800 Subject: drm/amdgpu: fallback to old RAS error message for aqua_vanjaram So driver doesn't generate incorrect message until the new format is settled down for aqua_vanjaram Signed-off-by: Hawking Zhang Reviewed-by: Yang Wang Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 3c4600e15b86..937c54fc7174 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -1052,7 +1052,8 @@ int amdgpu_ras_query_error_status(struct amdgpu_device *adev, info->ce_count = obj->err_data.ce_count; if (err_data.ce_count) { - if (adev->smuio.funcs && + if (!adev->aid_mask && + adev->smuio.funcs && adev->smuio.funcs->get_socket_id && adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " @@ -1072,7 +1073,8 @@ int amdgpu_ras_query_error_status(struct amdgpu_device *adev, } } if (err_data.ue_count) { - if (adev->smuio.funcs && + if (!adev->aid_mask && + adev->smuio.funcs && adev->smuio.funcs->get_socket_id && adev->smuio.funcs->get_die_id) { dev_info(adev->dev, "socket: %d, die: %d " -- cgit v1.2.3 From ec5fa9fcdeca69edf7dab5ca3b2e0ceb1c08fe9a Mon Sep 17 00:00:00 2001 From: Wayne Lin Date: Tue, 22 Aug 2023 16:03:17 +0800 Subject: drm/amd/display: Adjust the MST resume flow [Why] In drm_dp_mst_topology_mgr_resume() today, it will resume the mst branch to be ready handling mst mode and also consecutively do the mst topology probing. Which will cause the dirver have chance to fire hotplug event before restoring the old state. Then Userspace will react to the hotplug event based on a wrong state. [How] Adjust the mst resume flow as: 1. set dpcd to resume mst branch status 2. restore source old state 3. Do mst resume topology probing For drm_dp_mst_topology_mgr_resume(), it's better to adjust it to pull out topology probing work into a 2nd part procedure of the mst resume. Will have a follow up patch in drm. Reviewed-by: Chao-kai Wang Cc: Mario Limonciello Cc: Alex Deucher Cc: stable@vger.kernel.org Acked-by: Stylon Wang Signed-off-by: Wayne Lin Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c | 93 +++++++++++++++++++---- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index ca129983a08b..c6fd34bab358 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -2340,14 +2340,62 @@ static int dm_late_init(void *handle) return detect_mst_link_for_all_connectors(adev_to_drm(adev)); } +static void resume_mst_branch_status(struct drm_dp_mst_topology_mgr *mgr) +{ + int ret; + u8 guid[16]; + u64 tmp64; + + mutex_lock(&mgr->lock); + if (!mgr->mst_primary) + goto out_fail; + + if (drm_dp_read_dpcd_caps(mgr->aux, mgr->dpcd) < 0) { + drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); + goto out_fail; + } + + ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, + DP_MST_EN | + DP_UP_REQ_EN | + DP_UPSTREAM_IS_SRC); + if (ret < 0) { + drm_dbg_kms(mgr->dev, "mst write failed - undocked during suspend?\n"); + goto out_fail; + } + + /* Some hubs forget their guids after they resume */ + ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16); + if (ret != 16) { + drm_dbg_kms(mgr->dev, "dpcd read failed - undocked during suspend?\n"); + goto out_fail; + } + + if (memchr_inv(guid, 0, 16) == NULL) { + tmp64 = get_jiffies_64(); + memcpy(&guid[0], &tmp64, sizeof(u64)); + memcpy(&guid[8], &tmp64, sizeof(u64)); + + ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, guid, 16); + + if (ret != 16) { + drm_dbg_kms(mgr->dev, "check mstb guid failed - undocked during suspend?\n"); + goto out_fail; + } + } + + memcpy(mgr->mst_primary->guid, guid, 16); + +out_fail: + mutex_unlock(&mgr->lock); +} + static void s3_handle_mst(struct drm_device *dev, bool suspend) { struct amdgpu_dm_connector *aconnector; struct drm_connector *connector; struct drm_connector_list_iter iter; struct drm_dp_mst_topology_mgr *mgr; - int ret; - bool need_hotplug = false; drm_connector_list_iter_begin(dev, &iter); drm_for_each_connector_iter(connector, &iter) { @@ -2369,18 +2417,15 @@ static void s3_handle_mst(struct drm_device *dev, bool suspend) if (!dp_is_lttpr_present(aconnector->dc_link)) try_to_configure_aux_timeout(aconnector->dc_link->ddc, LINK_AUX_DEFAULT_TIMEOUT_PERIOD); - ret = drm_dp_mst_topology_mgr_resume(mgr, true); - if (ret < 0) { - dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, - aconnector->dc_link); - need_hotplug = true; - } + /* TODO: move resume_mst_branch_status() into drm mst resume again + * once topology probing work is pulled out from mst resume into mst + * resume 2nd step. mst resume 2nd step should be called after old + * state getting restored (i.e. drm_atomic_helper_resume()). + */ + resume_mst_branch_status(mgr); } } drm_connector_list_iter_end(&iter); - - if (need_hotplug) - drm_kms_helper_hotplug_event(dev); } static int amdgpu_dm_smu_write_watermarks_table(struct amdgpu_device *adev) @@ -2774,7 +2819,8 @@ static int dm_resume(void *handle) struct dm_atomic_state *dm_state = to_dm_atomic_state(dm->atomic_obj.state); enum dc_connection_type new_connection_type = dc_connection_none; struct dc_state *dc_state; - int i, r, j; + int i, r, j, ret; + bool need_hotplug = false; if (amdgpu_in_reset(adev)) { dc_state = dm->cached_dc_state; @@ -2872,7 +2918,7 @@ static int dm_resume(void *handle) continue; /* - * this is the case when traversing through already created + * this is the case when traversing through already created end sink * MST connectors, should be skipped */ if (aconnector && aconnector->mst_root) @@ -2932,6 +2978,27 @@ static int dm_resume(void *handle) dm->cached_state = NULL; + /* Do mst topology probing after resuming cached state*/ + drm_connector_list_iter_begin(ddev, &iter); + drm_for_each_connector_iter(connector, &iter) { + aconnector = to_amdgpu_dm_connector(connector); + if (aconnector->dc_link->type != dc_connection_mst_branch || + aconnector->mst_root) + continue; + + ret = drm_dp_mst_topology_mgr_resume(&aconnector->mst_mgr, true); + + if (ret < 0) { + dm_helpers_dp_mst_stop_top_mgr(aconnector->dc_link->ctx, + aconnector->dc_link); + need_hotplug = true; + } + } + drm_connector_list_iter_end(&iter); + + if (need_hotplug) + drm_kms_helper_hotplug_event(ddev); + amdgpu_dm_irq_resume_late(adev); amdgpu_dm_smu_write_watermarks_table(adev); -- cgit v1.2.3 From fc52a64416b010c8324e2cb50070faae868521c1 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Fri, 8 Sep 2023 16:39:29 -0400 Subject: tracing/synthetic: Fix order of struct trace_dynamic_info To make handling BIG and LITTLE endian better the offset/len of dynamic fields of the synthetic events was changed into a structure of: struct trace_dynamic_info { #ifdef CONFIG_CPU_BIG_ENDIAN u16 offset; u16 len; #else u16 len; u16 offset; #endif }; to replace the manual changes of: data_offset = offset & 0xffff; data_offest = len << 16; But if you look closely, the above is: << 16 | offset Which in little endian would be in memory: offset_lo offset_hi len_lo len_hi and in big endian: len_hi len_lo offset_hi offset_lo Which if broken into a structure would be: struct trace_dynamic_info { #ifdef CONFIG_CPU_BIG_ENDIAN u16 len; u16 offset; #else u16 offset; u16 len; #endif }; Which is the opposite of what was defined. Fix this and just to be safe also add "__packed". Link: https://lore.kernel.org/all/20230908154417.5172e343@gandalf.local.home/ Link: https://lore.kernel.org/linux-trace-kernel/20230908163929.2c25f3dc@gandalf.local.home Cc: stable@vger.kernel.org Cc: Mark Rutland Tested-by: Sven Schnelle Acked-by: Masami Hiramatsu (Google) Fixes: ddeea494a16f3 ("tracing/synthetic: Use union instead of casts") Signed-off-by: Steven Rostedt (Google) --- include/linux/trace_events.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 12f875e9e69a..21ae37e49319 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -62,13 +62,13 @@ void trace_event_printf(struct trace_iterator *iter, const char *fmt, ...); /* Used to find the offset and length of dynamic fields in trace events */ struct trace_dynamic_info { #ifdef CONFIG_CPU_BIG_ENDIAN - u16 offset; u16 len; + u16 offset; #else - u16 len; u16 offset; + u16 len; #endif -}; +} __packed; /* * The trace entry - the most basic unit of tracing. This is what -- cgit v1.2.3 From 9296da8c40900b4dae3d973aa22be306e2a77671 Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 22 Nov 2022 15:14:32 -0500 Subject: drm/amdkfd: Checkpoint and restore queues on GFX11 The code in kfd_mqd_manager_v11.c to support criu dump and restore of queue state was missing. Added it; should be equivalent to kfd_mqd_manager_v10.c. CC: Felix Kuehling Reviewed-by: Harish Kasiviswanathan Acked-by: Alex Deucher Signed-off-by: David Francis Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c | 41 ++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c index 964b5d50a77e..15277f1d5cf0 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_mqd_manager_v11.c @@ -321,6 +321,43 @@ static int get_wave_state(struct mqd_manager *mm, void *mqd, return 0; } +static void checkpoint_mqd(struct mqd_manager *mm, void *mqd, void *mqd_dst, void *ctl_stack_dst) +{ + struct v11_compute_mqd *m; + + m = get_mqd(mqd); + + memcpy(mqd_dst, m, sizeof(struct v11_compute_mqd)); +} + +static void restore_mqd(struct mqd_manager *mm, void **mqd, + struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, + struct queue_properties *qp, + const void *mqd_src, + const void *ctl_stack_src, const u32 ctl_stack_size) +{ + uint64_t addr; + struct v11_compute_mqd *m; + + m = (struct v11_compute_mqd *) mqd_mem_obj->cpu_ptr; + addr = mqd_mem_obj->gpu_addr; + + memcpy(m, mqd_src, sizeof(*m)); + + *mqd = m; + if (gart_addr) + *gart_addr = addr; + + m->cp_hqd_pq_doorbell_control = + qp->doorbell_off << + CP_HQD_PQ_DOORBELL_CONTROL__DOORBELL_OFFSET__SHIFT; + pr_debug("cp_hqd_pq_doorbell_control 0x%x\n", + m->cp_hqd_pq_doorbell_control); + + qp->is_active = 0; +} + + static void init_mqd_hiq(struct mqd_manager *mm, void **mqd, struct kfd_mem_obj *mqd_mem_obj, uint64_t *gart_addr, struct queue_properties *q) @@ -458,6 +495,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->mqd_size = sizeof(struct v11_compute_mqd); mqd->get_wave_state = get_wave_state; mqd->mqd_stride = kfd_mqd_stride; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; #if defined(CONFIG_DEBUG_FS) mqd->debugfs_show_mqd = debugfs_show_mqd; #endif @@ -502,6 +541,8 @@ struct mqd_manager *mqd_manager_init_v11(enum KFD_MQD_TYPE type, mqd->update_mqd = update_mqd_sdma; mqd->destroy_mqd = kfd_destroy_mqd_sdma; mqd->is_occupied = kfd_is_occupied_sdma; + mqd->checkpoint_mqd = checkpoint_mqd; + mqd->restore_mqd = restore_mqd; mqd->mqd_size = sizeof(struct v11_sdma_mqd); mqd->mqd_stride = kfd_mqd_stride; #if defined(CONFIG_DEBUG_FS) -- cgit v1.2.3 From 62663b849662c1a5126b6274d91671b90566ef13 Mon Sep 17 00:00:00 2001 From: Tero Kristo Date: Mon, 11 Sep 2023 17:17:04 +0300 Subject: tracing/synthetic: Print out u64 values properly The synth traces incorrectly print pointer to the synthetic event values instead of the actual value when using u64 type. Fix by addressing the contents of the union properly. Link: https://lore.kernel.org/linux-trace-kernel/20230911141704.3585965-1-tero.kristo@linux.intel.com Fixes: ddeea494a16f ("tracing/synthetic: Use union instead of casts") Cc: stable@vger.kernel.org Signed-off-by: Tero Kristo Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_events_synth.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/trace/trace_events_synth.c b/kernel/trace/trace_events_synth.c index 9897d0bfcab7..14cb275a0bab 100644 --- a/kernel/trace/trace_events_synth.c +++ b/kernel/trace/trace_events_synth.c @@ -337,7 +337,7 @@ static void print_synth_event_num_val(struct trace_seq *s, break; default: - trace_seq_printf(s, print_fmt, name, val, space); + trace_seq_printf(s, print_fmt, name, val->as_u64, space); break; } } -- cgit v1.2.3 From 5e7e82254270c8cf8b107451c5de01cee2f135ae Mon Sep 17 00:00:00 2001 From: David Francis Date: Tue, 5 Sep 2023 10:13:51 -0400 Subject: drm/amdgpu: Handle null atom context in VBIOS info ioctl On some APU systems, there is no atom context and so the atom_context struct is null. Add a check to the VBIOS_INFO branch of amdgpu_info_ioctl to handle this case, returning all zeroes. Reviewed-by: Alex Deucher Signed-off-by: David Francis Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index 2cd2ecebf465..d30dc0b718c7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -940,12 +940,17 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) struct atom_context *atom_context; atom_context = adev->mode_info.atom_context; - memcpy(vbios_info.name, atom_context->name, sizeof(atom_context->name)); - memcpy(vbios_info.vbios_pn, atom_context->vbios_pn, sizeof(atom_context->vbios_pn)); - vbios_info.version = atom_context->version; - memcpy(vbios_info.vbios_ver_str, atom_context->vbios_ver_str, - sizeof(atom_context->vbios_ver_str)); - memcpy(vbios_info.date, atom_context->date, sizeof(atom_context->date)); + if (atom_context) { + memcpy(vbios_info.name, atom_context->name, + sizeof(atom_context->name)); + memcpy(vbios_info.vbios_pn, atom_context->vbios_pn, + sizeof(atom_context->vbios_pn)); + vbios_info.version = atom_context->version; + memcpy(vbios_info.vbios_ver_str, atom_context->vbios_ver_str, + sizeof(atom_context->vbios_ver_str)); + memcpy(vbios_info.date, atom_context->date, + sizeof(atom_context->date)); + } return copy_to_user(out, &vbios_info, min((size_t)size, sizeof(vbios_info))) ? -EFAULT : 0; -- cgit v1.2.3 From db5494a85294f057e0bb41bdb5372c2dbf46fb79 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 10 Sep 2023 16:44:50 -0700 Subject: drm/amd/display: fix replay_mode kernel-doc warning Fix the typo in the kernel-doc for @replay_mode to prevent kernel-doc warnings: drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:623: warning: Incorrect use of kernel-doc format: * @replay mode: Replay supported drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h:626: warning: Function parameter or member 'replay_mode' not described in 'amdgpu_hdmi_vsdb_info' Fixes: ec8e59cb4e0c ("drm/amd/display: Get replay info from VSDB") Signed-off-by: Randy Dunlap Reported-by: kernel test robot Cc: Bhawanpreet Lakha Cc: Harry Wentland Cc: Alex Deucher Cc: Leo Li Cc: Rodrigo Siqueira Cc: amd-gfx@lists.freedesktop.org Cc: dri-devel@lists.freedesktop.org Signed-off-by: Hamza Mahfooz Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h index a2d34be82613..9e4cc5eeda76 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.h @@ -620,7 +620,7 @@ struct amdgpu_hdmi_vsdb_info { unsigned int max_refresh_rate_hz; /** - * @replay mode: Replay supported + * @replay_mode: Replay supported */ bool replay_mode; }; -- cgit v1.2.3 From 64be47ba286117ee4e3dd9d064c88ea2913e3269 Mon Sep 17 00:00:00 2001 From: Mustapha Ghaddar Date: Thu, 10 Aug 2023 16:20:23 -0400 Subject: drm/amd/display: Add DPIA Link Encoder Assignment Fix For DPIA we should have preferred DIG assignment based on DPIA selected as per the ASIC design. Reviewed-by: George Shen Acked-by: Hamza Mahfooz Signed-off-by: Mustapha Ghaddar Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- .../gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c | 35 ++++++++++++++++++---- drivers/gpu/drm/amd/display/dc/dc.h | 1 + .../drm/amd/display/dc/dcn314/dcn314_resource.c | 23 ++++++++++++++ drivers/gpu/drm/amd/display/dc/inc/core_types.h | 1 + drivers/gpu/drm/amd/display/dc/link/link_factory.c | 4 +++ 5 files changed, 58 insertions(+), 6 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c index 30c0644d4418..b66eeac4d3d2 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c @@ -169,11 +169,23 @@ static void add_link_enc_assignment( /* Return first available DIG link encoder. */ static enum engine_id find_first_avail_link_enc( const struct dc_context *ctx, - const struct dc_state *state) + const struct dc_state *state, + enum engine_id eng_id_requested) { enum engine_id eng_id = ENGINE_ID_UNKNOWN; int i; + if (eng_id_requested != ENGINE_ID_UNKNOWN) { + + for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { + eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i]; + if (eng_id == eng_id_requested) + return eng_id; + } + } + + eng_id = ENGINE_ID_UNKNOWN; + for (i = 0; i < ctx->dc->res_pool->res_cap->num_dig_link_enc; i++) { eng_id = state->res_ctx.link_enc_cfg_ctx.link_enc_avail[i]; if (eng_id != ENGINE_ID_UNKNOWN) @@ -287,7 +299,7 @@ void link_enc_cfg_link_encs_assign( struct dc_stream_state *streams[], uint8_t stream_count) { - enum engine_id eng_id = ENGINE_ID_UNKNOWN; + enum engine_id eng_id = ENGINE_ID_UNKNOWN, eng_id_req = ENGINE_ID_UNKNOWN; int i; int j; @@ -377,8 +389,15 @@ void link_enc_cfg_link_encs_assign( * assigned to that endpoint. */ link_enc = get_link_enc_used_by_link(state, stream->link); - if (link_enc == NULL) - eng_id = find_first_avail_link_enc(stream->ctx, state); + if (link_enc == NULL) { + + if (stream->link->ep_type == DISPLAY_ENDPOINT_USB4_DPIA && + stream->link->dpia_preferred_eng_id != ENGINE_ID_UNKNOWN) + eng_id_req = stream->link->dpia_preferred_eng_id; + + if (eng_id == ENGINE_ID_UNKNOWN) + eng_id = find_first_avail_link_enc(stream->ctx, state, eng_id_req); + } else eng_id = link_enc->preferred_engine; @@ -402,7 +421,9 @@ void link_enc_cfg_link_encs_assign( DC_LOG_DEBUG("%s: CUR %s(%d) - enc_id(%d)\n", __func__, assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA", - assignment.ep_id.link_id.enum_id - 1, + assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? + assignment.ep_id.link_id.enum_id : + assignment.ep_id.link_id.enum_id - 1, assignment.eng_id); } for (i = 0; i < MAX_PIPES; i++) { @@ -413,7 +434,9 @@ void link_enc_cfg_link_encs_assign( DC_LOG_DEBUG("%s: NEW %s(%d) - enc_id(%d)\n", __func__, assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? "PHY" : "DPIA", - assignment.ep_id.link_id.enum_id - 1, + assignment.ep_id.ep_type == DISPLAY_ENDPOINT_PHY ? + assignment.ep_id.link_id.enum_id : + assignment.ep_id.link_id.enum_id - 1, assignment.eng_id); } diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 0d0bef8eb331..31e3183497a7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -1496,6 +1496,7 @@ struct dc_link { * object creation. */ enum engine_id eng_id; + enum engine_id dpia_preferred_eng_id; bool test_pattern_enabled; enum dp_test_pattern current_test_pattern; diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c index 1c1fb2fa0822..004beed9bd44 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c @@ -1032,6 +1032,28 @@ static const struct dce_i2c_mask i2c_masks = { I2C_COMMON_MASK_SH_LIST_DCN30(_MASK) }; +/* ========================================================== */ + +/* + * DPIA index | Preferred Encoder | Host Router + * 0 | C | 0 + * 1 | First Available | 0 + * 2 | D | 1 + * 3 | First Available | 1 + */ +/* ========================================================== */ +static const enum engine_id dpia_to_preferred_enc_id_table[] = { + ENGINE_ID_DIGC, + ENGINE_ID_DIGC, + ENGINE_ID_DIGD, + ENGINE_ID_DIGD +}; + +static enum engine_id dcn314_get_preferred_eng_id_dpia(unsigned int dpia_index) +{ + return dpia_to_preferred_enc_id_table[dpia_index]; +} + static struct dce_i2c_hw *dcn31_i2c_hw_create( struct dc_context *ctx, uint32_t inst) @@ -1785,6 +1807,7 @@ static struct resource_funcs dcn314_res_pool_funcs = { .update_bw_bounding_box = dcn314_update_bw_bounding_box, .patch_unknown_plane_state = dcn20_patch_unknown_plane_state, .get_panel_config_defaults = dcn314_get_panel_config_defaults, + .get_preferred_eng_id_dpia = dcn314_get_preferred_eng_id_dpia, }; static struct clock_source *dcn30_clock_source_create( diff --git a/drivers/gpu/drm/amd/display/dc/inc/core_types.h b/drivers/gpu/drm/amd/display/dc/inc/core_types.h index 027aec70c070..eaad1260bfd1 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/core_types.h +++ b/drivers/gpu/drm/amd/display/dc/inc/core_types.h @@ -65,6 +65,7 @@ struct resource_context; struct clk_bw_params; struct resource_funcs { + enum engine_id (*get_preferred_eng_id_dpia)(unsigned int dpia_index); void (*destroy)(struct resource_pool **pool); void (*link_init)(struct dc_link *link); struct panel_cntl*(*panel_cntl_create)( diff --git a/drivers/gpu/drm/amd/display/dc/link/link_factory.c b/drivers/gpu/drm/amd/display/dc/link/link_factory.c index 195ca9e52eda..0895742a3102 100644 --- a/drivers/gpu/drm/amd/display/dc/link/link_factory.c +++ b/drivers/gpu/drm/amd/display/dc/link/link_factory.c @@ -791,6 +791,10 @@ static bool construct_dpia(struct dc_link *link, /* Set dpia port index : 0 to number of dpia ports */ link->ddc_hw_inst = init_params->connector_index; + // Assign Dpia preferred eng_id + if (link->dc->res_pool->funcs->get_preferred_eng_id_dpia) + link->dpia_preferred_eng_id = link->dc->res_pool->funcs->get_preferred_eng_id_dpia(link->ddc_hw_inst); + /* TODO: Create link encoder */ link->psr_settings.psr_version = DC_PSR_VERSION_UNSUPPORTED; -- cgit v1.2.3 From 29319378449035c6fc6391b31a3c2cbaf75be221 Mon Sep 17 00:00:00 2001 From: Mustapha Ghaddar Date: Tue, 22 Aug 2023 16:18:03 -0400 Subject: drm/amd/display: Fix 2nd DPIA encoder Assignment [HOW & Why] There seems to be an issue with 2nd DPIA acquiring link encoder for tiled displays. Solution is to remove check for eng_id before we get first dynamic encoder for it Reviewed-by: Cruise Hung Reviewed-by: Meenakshikumar Somasundaram Cc: Mario Limonciello Cc: Alex Deucher Cc: stable@vger.kernel.org Acked-by: Stylon Wang Signed-off-by: Mustapha Ghaddar Tested-by: Daniel Wheeler Signed-off-by: Alex Deucher --- drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c index b66eeac4d3d2..be5a6d008b29 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_link_enc_cfg.c @@ -395,8 +395,7 @@ void link_enc_cfg_link_encs_assign( stream->link->dpia_preferred_eng_id != ENGINE_ID_UNKNOWN) eng_id_req = stream->link->dpia_preferred_eng_id; - if (eng_id == ENGINE_ID_UNKNOWN) - eng_id = find_first_avail_link_enc(stream->ctx, state, eng_id_req); + eng_id = find_first_avail_link_enc(stream->ctx, state, eng_id_req); } else eng_id = link_enc->preferred_engine; @@ -501,7 +500,6 @@ struct dc_link *link_enc_cfg_get_link_using_link_enc( if (stream) link = stream->link; - // dm_output_to_console("%s: No link using DIG(%d).\n", __func__, eng_id); return link; } -- cgit v1.2.3 From a06023a8f78d3e9e73ca4363ccf3871a06e16ecc Mon Sep 17 00:00:00 2001 From: Beau Belgrave Date: Fri, 8 Sep 2023 20:19:16 +0000 Subject: selftests/user_events: Fix failures when user_events is not installed When user_events is not installed the self tests currently fail. Now that these self tests run by default we need to ensure they don't fail when user_events was not enabled for the kernel being tested. Add common methods to detect if tracefs and user_events is enabled. If either is not enabled skip the test. If tracefs is enabled, but is not mounted, mount tracefs and fail if there were any errors. Fail if not run as root. Fixes: 68b4d2d58389 ("selftests/user_events: Reenable build") Reported-by: Naresh Kamboju Link: https://lore.kernel.org/all/CA+G9fYuugZ0OMeS6HvpSS4nuf_A3s455ecipGBvER0LJHojKZg@mail.gmail.com/ Signed-off-by: Beau Belgrave Signed-off-by: Shuah Khan --- tools/testing/selftests/user_events/abi_test.c | 3 + tools/testing/selftests/user_events/dyn_test.c | 2 + tools/testing/selftests/user_events/ftrace_test.c | 3 + tools/testing/selftests/user_events/perf_test.c | 3 + .../selftests/user_events/user_events_selftests.h | 100 +++++++++++++++++++++ 5 files changed, 111 insertions(+) create mode 100644 tools/testing/selftests/user_events/user_events_selftests.h diff --git a/tools/testing/selftests/user_events/abi_test.c b/tools/testing/selftests/user_events/abi_test.c index 5125c42efe65..22374d29ffdd 100644 --- a/tools/testing/selftests/user_events/abi_test.c +++ b/tools/testing/selftests/user_events/abi_test.c @@ -19,6 +19,7 @@ #include #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *data_file = "/sys/kernel/tracing/user_events_data"; const char *enable_file = "/sys/kernel/tracing/events/user_events/__abi_event/enable"; @@ -93,6 +94,8 @@ FIXTURE(user) { }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return); + change_event(false); self->check = 0; } diff --git a/tools/testing/selftests/user_events/dyn_test.c b/tools/testing/selftests/user_events/dyn_test.c index 91a4444ad42b..32c827a52d7d 100644 --- a/tools/testing/selftests/user_events/dyn_test.c +++ b/tools/testing/selftests/user_events/dyn_test.c @@ -15,6 +15,7 @@ #include #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *abi_file = "/sys/kernel/tracing/user_events_data"; const char *enable_file = "/sys/kernel/tracing/events/user_events/__test_event/enable"; @@ -146,6 +147,7 @@ FIXTURE(user) { }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return); } FIXTURE_TEARDOWN(user) { diff --git a/tools/testing/selftests/user_events/ftrace_test.c b/tools/testing/selftests/user_events/ftrace_test.c index 5beb0aef1d81..6a260caeeddc 100644 --- a/tools/testing/selftests/user_events/ftrace_test.c +++ b/tools/testing/selftests/user_events/ftrace_test.c @@ -16,6 +16,7 @@ #include #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *data_file = "/sys/kernel/tracing/user_events_data"; const char *status_file = "/sys/kernel/tracing/user_events_status"; @@ -206,6 +207,8 @@ FIXTURE(user) { }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return); + self->status_fd = open(status_file, O_RDONLY); ASSERT_NE(-1, self->status_fd); diff --git a/tools/testing/selftests/user_events/perf_test.c b/tools/testing/selftests/user_events/perf_test.c index 8b09be566fa2..f893398cda05 100644 --- a/tools/testing/selftests/user_events/perf_test.c +++ b/tools/testing/selftests/user_events/perf_test.c @@ -17,6 +17,7 @@ #include #include "../kselftest_harness.h" +#include "user_events_selftests.h" const char *data_file = "/sys/kernel/tracing/user_events_data"; const char *id_file = "/sys/kernel/tracing/events/user_events/__test_event/id"; @@ -113,6 +114,8 @@ FIXTURE(user) { }; FIXTURE_SETUP(user) { + USER_EVENT_FIXTURE_SETUP(return); + self->data_fd = open(data_file, O_RDWR); ASSERT_NE(-1, self->data_fd); } diff --git a/tools/testing/selftests/user_events/user_events_selftests.h b/tools/testing/selftests/user_events/user_events_selftests.h new file mode 100644 index 000000000000..690378942f82 --- /dev/null +++ b/tools/testing/selftests/user_events/user_events_selftests.h @@ -0,0 +1,100 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _USER_EVENTS_SELFTESTS_H +#define _USER_EVENTS_SELFTESTS_H + +#include +#include +#include +#include +#include + +#include "../kselftest.h" + +static inline bool tracefs_enabled(char **message, bool *fail) +{ + struct stat buf; + int ret; + + *message = ""; + *fail = false; + + /* Ensure tracefs is installed */ + ret = stat("/sys/kernel/tracing", &buf); + + if (ret == -1) { + *message = "Tracefs is not installed"; + return false; + } + + /* Ensure mounted tracefs */ + ret = stat("/sys/kernel/tracing/README", &buf); + + if (ret == -1 && errno == ENOENT) { + if (mount(NULL, "/sys/kernel/tracing", "tracefs", 0, NULL) != 0) { + *message = "Cannot mount tracefs"; + *fail = true; + return false; + } + + ret = stat("/sys/kernel/tracing/README", &buf); + } + + if (ret == -1) { + *message = "Cannot access tracefs"; + *fail = true; + return false; + } + + return true; +} + +static inline bool user_events_enabled(char **message, bool *fail) +{ + struct stat buf; + int ret; + + *message = ""; + *fail = false; + + if (getuid() != 0) { + *message = "Must be run as root"; + *fail = true; + return false; + } + + if (!tracefs_enabled(message, fail)) + return false; + + /* Ensure user_events is installed */ + ret = stat("/sys/kernel/tracing/user_events_data", &buf); + + if (ret == -1) { + switch (errno) { + case ENOENT: + *message = "user_events is not installed"; + return false; + + default: + *message = "Cannot access user_events_data"; + *fail = true; + return false; + } + } + + return true; +} + +#define USER_EVENT_FIXTURE_SETUP(statement) do { \ + char *message; \ + bool fail; \ + if (!user_events_enabled(&message, &fail)) { \ + if (fail) { \ + TH_LOG("Setup failed due to: %s", message); \ + ASSERT_FALSE(fail); \ + } \ + SKIP(statement, "Skipping due to: %s", message); \ + } \ +} while (0) + +#endif /* _USER_EVENTS_SELFTESTS_H */ -- cgit v1.2.3 From 7dc1e125f07aeeb8b6eacfd9a05ef3ef6fe539c7 Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Fri, 8 Sep 2023 18:17:21 -0400 Subject: ftrace/selftests: Add softlink to latest log directory When I'm debugging something with the ftrace selftests and need to look at the logs, it becomes tedious that I need to do the following: ls -ltr logs [ copy the last directory ] ls logs/ to see where the logs are. Instead, do the common practice of having a "latest" softlink to the last run selftest. This way after running the selftest I only need to do: ls logs/latest/ and it will always give me the directory of the last run selftest logs! Signed-off-by: Steven Rostedt (Google) Acked-by: Masami Hiramatsu (Google) Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/ftracetest | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index cb5f18c06593..7df8baa0f98f 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -124,6 +124,7 @@ parse_opts() { # opts ;; --logdir|-l) LOG_DIR=$2 + LINK_PTR= shift 2 ;; *.tc) @@ -181,7 +182,10 @@ fi TOP_DIR=`absdir $0` TEST_DIR=$TOP_DIR/test.d TEST_CASES=`find_testcases $TEST_DIR` -LOG_DIR=$TOP_DIR/logs/`date +%Y%m%d-%H%M%S`/ +LOG_TOP_DIR=$TOP_DIR/logs +LOG_DATE=`date +%Y%m%d-%H%M%S` +LOG_DIR=$LOG_TOP_DIR/$LOG_DATE/ +LINK_PTR=$LOG_TOP_DIR/latest KEEP_LOG=0 KTAP=0 DEBUG=0 @@ -207,6 +211,10 @@ else LOG_FILE=$LOG_DIR/ftracetest.log mkdir -p $LOG_DIR || errexit "Failed to make a log directory: $LOG_DIR" date > $LOG_FILE + if [ "x-$LINK_PTR" != "x-" ]; then + unlink $LINK_PTR + ln -fs $LOG_DATE $LINK_PTR + fi fi # Define text colors -- cgit v1.2.3 From 7ab6fe6625c9bdcb8fa5f61c8f8e30e13f689284 Mon Sep 17 00:00:00 2001 From: Naresh Kamboju Date: Thu, 7 Sep 2023 13:32:09 +0530 Subject: selftests: user_events: create test-specific Kconfig fragments Create the config file in user_events directory of testcase which need more kernel configuration than the default defconfig. User could use these configs with merge_config.sh script: The Kconfig CONFIG_USER_EVENTS=y is needed for the test to read data from the following files, - "/sys/kernel/tracing/user_events_data" - "/sys/kernel/tracing/user_events_status" - "/sys/kernel/tracing/events/user_events/*" Enable config for specific testcase: (export ARCH=xxx #for cross compiling) ./scripts/kconfig/merge_config.sh .config \ tools/testing/selftests/user_events/config Enable configs for all testcases: (export ARCH=xxx #for cross compiling) ./scripts/kconfig/merge_config.sh .config \ tools/testing/selftests/*/config Cc: Beau Belgrave Cc: Shuah Khan Cc: linux-kselftest@vger.kernel.org Signed-off-by: Naresh Kamboju Signed-off-by: Shuah Khan --- tools/testing/selftests/user_events/config | 1 + 1 file changed, 1 insertion(+) create mode 100644 tools/testing/selftests/user_events/config diff --git a/tools/testing/selftests/user_events/config b/tools/testing/selftests/user_events/config new file mode 100644 index 000000000000..64f7a9a90cec --- /dev/null +++ b/tools/testing/selftests/user_events/config @@ -0,0 +1 @@ +CONFIG_USER_EVENTS=y -- cgit v1.2.3 From 9243e5430995498f14f92be56da995ded107d71e Mon Sep 17 00:00:00 2001 From: "Steven Rostedt (Google)" Date: Mon, 11 Sep 2023 20:06:54 -0400 Subject: tracefs/eventfs: Use list_for_each_srcu() in dcache_dir_open_wrapper() The eventfs files list is protected by SRCU. In earlier iterations it was protected with just RCU, but because it needed to also call sleepable code, it had to be switch to SRCU. The dcache_dir_open_wrapper() list_for_each_rcu() was missed and did not get converted over to list_for_each_srcu(). That needs to be fixed. Link: https://lore.kernel.org/linux-trace-kernel/20230911120053.ca82f545e7f46ea753deda18@kernel.org/ Link: https://lore.kernel.org/linux-trace-kernel/20230911200654.71ce927c@gandalf.local.home Cc: Mark Rutland Cc: Ajay Kaher Cc: "Paul E. McKenney" Reported-by: Masami Hiramatsu (Google) Fixes: 63940449555e7 ("eventfs: Implement eventfs lookup, read, open functions") Signed-off-by: Steven Rostedt (Google) --- fs/tracefs/event_inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/tracefs/event_inode.c b/fs/tracefs/event_inode.c index f168aca45458..9f64e7332796 100644 --- a/fs/tracefs/event_inode.c +++ b/fs/tracefs/event_inode.c @@ -452,7 +452,8 @@ static int dcache_dir_open_wrapper(struct inode *inode, struct file *file) ei = ti->private; idx = srcu_read_lock(&eventfs_srcu); - list_for_each_entry_rcu(ef, &ei->e_top_files, list) { + list_for_each_entry_srcu(ef, &ei->e_top_files, list, + srcu_read_lock_held(&eventfs_srcu)) { create_dentry(ef, dentry, false); } srcu_read_unlock(&eventfs_srcu, idx); -- cgit v1.2.3 From 72178d5d1a38dd185d1db15f177f2d122ef10d9b Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 11 Sep 2023 16:56:13 -0700 Subject: objtool: Fix _THIS_IP_ detection for cold functions Cold functions and their non-cold counterparts can use _THIS_IP_ to reference each other. Don't warn about !ENDBR in that case. Note that for GCC this is currently irrelevant in light of the following commit c27cd083cfb9 ("Compiler attributes: GCC cold function alignment workarounds") which disabled cold functions in the kernel. However this may still be possible with Clang. Fixes several warnings like the following: drivers/scsi/bnx2i/bnx2i.prelink.o: warning: objtool: bnx2i_hw_ep_disconnect+0x19d: relocation to !ENDBR: bnx2i_hw_ep_disconnect.cold+0x0 drivers/net/ipvlan/ipvlan.prelink.o: warning: objtool: ipvlan_addr4_event.cold+0x28: relocation to !ENDBR: ipvlan_addr4_event+0xda drivers/net/ipvlan/ipvlan.prelink.o: warning: objtool: ipvlan_addr6_event.cold+0x26: relocation to !ENDBR: ipvlan_addr6_event+0xb7 drivers/net/ethernet/broadcom/tg3.prelink.o: warning: objtool: tg3_set_ringparam.cold+0x17: relocation to !ENDBR: tg3_set_ringparam+0x115 drivers/net/ethernet/broadcom/tg3.prelink.o: warning: objtool: tg3_self_test.cold+0x17: relocation to !ENDBR: tg3_self_test+0x2e1 drivers/target/iscsi/cxgbit/cxgbit.prelink.o: warning: objtool: __cxgbit_free_conn.cold+0x24: relocation to !ENDBR: __cxgbit_free_conn+0xfb net/can/can.prelink.o: warning: objtool: can_rx_unregister.cold+0x2c: relocation to !ENDBR: can_rx_unregister+0x11b drivers/net/ethernet/qlogic/qed/qed.prelink.o: warning: objtool: qed_spq_post+0xc0: relocation to !ENDBR: qed_spq_post.cold+0x9a drivers/net/ethernet/qlogic/qed/qed.prelink.o: warning: objtool: qed_iwarp_ll2_comp_syn_pkt.cold+0x12f: relocation to !ENDBR: qed_iwarp_ll2_comp_syn_pkt+0x34b net/tipc/tipc.prelink.o: warning: objtool: tipc_nametbl_publish.cold+0x21: relocation to !ENDBR: tipc_nametbl_publish+0xa6 Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/d8f1ab6a23a6105bc023c132b105f245c7976be6.1694476559.git.jpoimboe@kernel.org --- tools/objtool/check.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 1384090530db..e308d1ba664e 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4333,7 +4333,8 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn continue; } - if (insn_func(dest) && insn_func(dest) == insn_func(insn)) { + if (insn_func(dest) && insn_func(insn) && + insn_func(dest)->pfunc == insn_func(insn)->pfunc) { /* * Anything from->to self is either _THIS_IP_ or * IRET-to-self. -- cgit v1.2.3 From cfaa80c91f6f99b9342b6557f0f0e1143e434066 Mon Sep 17 00:00:00 2001 From: Liu Jian Date: Sat, 9 Sep 2023 16:14:34 +0800 Subject: net/tls: do not free tls_rec on async operation in bpf_exec_tx_verdict() I got the below warning when do fuzzing test: BUG: KASAN: null-ptr-deref in scatterwalk_copychunks+0x320/0x470 Read of size 4 at addr 0000000000000008 by task kworker/u8:1/9 CPU: 0 PID: 9 Comm: kworker/u8:1 Tainted: G OE Hardware name: linux,dummy-virt (DT) Workqueue: pencrypt_parallel padata_parallel_worker Call trace: dump_backtrace+0x0/0x420 show_stack+0x34/0x44 dump_stack+0x1d0/0x248 __kasan_report+0x138/0x140 kasan_report+0x44/0x6c __asan_load4+0x94/0xd0 scatterwalk_copychunks+0x320/0x470 skcipher_next_slow+0x14c/0x290 skcipher_walk_next+0x2fc/0x480 skcipher_walk_first+0x9c/0x110 skcipher_walk_aead_common+0x380/0x440 skcipher_walk_aead_encrypt+0x54/0x70 ccm_encrypt+0x13c/0x4d0 crypto_aead_encrypt+0x7c/0xfc pcrypt_aead_enc+0x28/0x84 padata_parallel_worker+0xd0/0x2dc process_one_work+0x49c/0xbdc worker_thread+0x124/0x880 kthread+0x210/0x260 ret_from_fork+0x10/0x18 This is because the value of rec_seq of tls_crypto_info configured by the user program is too large, for example, 0xffffffffffffff. In addition, TLS is asynchronously accelerated. When tls_do_encryption() returns -EINPROGRESS and sk->sk_err is set to EBADMSG due to rec_seq overflow, skmsg is released before the asynchronous encryption process ends. As a result, the UAF problem occurs during the asynchronous processing of the encryption module. If the operation is asynchronous and the encryption module returns EINPROGRESS, do not free the record information. Fixes: 635d93981786 ("net/tls: free record only on encryption error") Signed-off-by: Liu Jian Reviewed-by: Sabrina Dubroca Link: https://lore.kernel.org/r/20230909081434.2324940-1-liujian56@huawei.com Signed-off-by: Paolo Abeni --- net/tls/tls_sw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tls/tls_sw.c b/net/tls/tls_sw.c index 1ed4a611631f..d1fc295b83b5 100644 --- a/net/tls/tls_sw.c +++ b/net/tls/tls_sw.c @@ -817,7 +817,7 @@ static int bpf_exec_tx_verdict(struct sk_msg *msg, struct sock *sk, psock = sk_psock_get(sk); if (!psock || !policy) { err = tls_push_record(sk, flags, record_type); - if (err && sk->sk_err == EBADMSG) { + if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { *copied -= sk_msg_free(sk, msg); tls_free_open_rec(sk); err = -sk->sk_err; @@ -846,7 +846,7 @@ more_data: switch (psock->eval) { case __SK_PASS: err = tls_push_record(sk, flags, record_type); - if (err && sk->sk_err == EBADMSG) { + if (err && err != -EINPROGRESS && sk->sk_err == EBADMSG) { *copied -= sk_msg_free(sk, msg); tls_free_open_rec(sk); err = -sk->sk_err; -- cgit v1.2.3 From 40d84e198b0ae64df71ac0e70675b16900b90bde Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 6 Sep 2023 12:18:41 +0800 Subject: PM: hibernate: Rename function parameter from snapshot_test to exclusive Several functions reply on snapshot_test to decide whether to open the resume device exclusively. However there is no strict connection between the snapshot_test and the open mode. Rename the 'snapshot_test' input parameter to 'exclusive' to better reflect the use case. No functional change is expected. Signed-off-by: Chen Yu Signed-off-by: Rafael J. Wysocki --- kernel/power/power.h | 4 ++-- kernel/power/swap.c | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/kernel/power/power.h b/kernel/power/power.h index 46eb14dc50c3..a98f95e309a3 100644 --- a/kernel/power/power.h +++ b/kernel/power/power.h @@ -168,11 +168,11 @@ extern int swsusp_swap_in_use(void); #define SF_HW_SIG 8 /* kernel/power/hibernate.c */ -int swsusp_check(bool snapshot_test); +int swsusp_check(bool exclusive); extern void swsusp_free(void); extern int swsusp_read(unsigned int *flags_p); extern int swsusp_write(unsigned int flags); -void swsusp_close(bool snapshot_test); +void swsusp_close(bool exclusive); #ifdef CONFIG_SUSPEND extern int swsusp_unmark(void); #endif diff --git a/kernel/power/swap.c b/kernel/power/swap.c index f6ebcd00c410..74edbce2320b 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -1513,12 +1513,13 @@ end: static void *swsusp_holder; /** - * swsusp_check - Check for swsusp signature in the resume device + * swsusp_check - Check for swsusp signature in the resume device + * @exclusive: Open the resume device exclusively. */ -int swsusp_check(bool snapshot_test) +int swsusp_check(bool exclusive) { - void *holder = snapshot_test ? &swsusp_holder : NULL; + void *holder = exclusive ? &swsusp_holder : NULL; int error; hib_resume_bdev = blkdev_get_by_dev(swsusp_resume_device, BLK_OPEN_READ, @@ -1563,17 +1564,18 @@ put: } /** - * swsusp_close - close swap device. + * swsusp_close - close swap device. + * @exclusive: Close the resume device which is exclusively opened. */ -void swsusp_close(bool snapshot_test) +void swsusp_close(bool exclusive) { if (IS_ERR(hib_resume_bdev)) { pr_debug("Image device not initialised\n"); return; } - blkdev_put(hib_resume_bdev, snapshot_test ? &swsusp_holder : NULL); + blkdev_put(hib_resume_bdev, exclusive ? &swsusp_holder : NULL); } /** -- cgit v1.2.3 From 148b6f4cc3920e563094540fe1a12d00d3bbccae Mon Sep 17 00:00:00 2001 From: Chen Yu Date: Wed, 6 Sep 2023 12:18:52 +0800 Subject: PM: hibernate: Fix the exclusive get block device in test_resume mode Commit 5904de0d735b ("PM: hibernate: Do not get block device exclusively in test_resume mode") fixes a hibernation issue under test_resume mode. That commit is supposed to open the block device in non-exclusive mode when in test_resume. However the code does the opposite, which is against its description. In summary, the swap device is only opened exclusively by swsusp_check() with its corresponding *close(), and must be in non test_resume mode. This is to avoid the race condition that different processes scribble the device at the same time. All the other cases should use non-exclusive mode. Fix it by really disabling exclusive mode under test_resume. Fixes: 5904de0d735b ("PM: hibernate: Do not get block device exclusively in test_resume mode") Closes: https://lore.kernel.org/lkml/000000000000761f5f0603324129@google.com/ Reported-by: Pengfei Xu Signed-off-by: Chen Yu Tested-by: Chenzhou Feng Signed-off-by: Rafael J. Wysocki --- kernel/power/hibernate.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 2b4a946a6ff5..8d35b9f9aaa3 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -786,9 +786,9 @@ int hibernate(void) unlock_device_hotplug(); if (snapshot_test) { pm_pr_dbg("Checking hibernation image\n"); - error = swsusp_check(snapshot_test); + error = swsusp_check(false); if (!error) - error = load_image_and_restore(snapshot_test); + error = load_image_and_restore(false); } thaw_processes(); @@ -945,14 +945,14 @@ static int software_resume(void) pm_pr_dbg("Looking for hibernation image.\n"); mutex_lock(&system_transition_mutex); - error = swsusp_check(false); + error = swsusp_check(true); if (error) goto Unlock; /* The snapshot device should not be opened while we're running */ if (!hibernate_acquire()) { error = -EBUSY; - swsusp_close(false); + swsusp_close(true); goto Unlock; } @@ -973,7 +973,7 @@ static int software_resume(void) goto Close_Finish; } - error = load_image_and_restore(false); + error = load_image_and_restore(true); thaw_processes(); Finish: pm_notifier_call_chain(PM_POST_RESTORE); @@ -987,7 +987,7 @@ static int software_resume(void) pm_pr_dbg("Hibernation image not present or could not be loaded.\n"); return error; Close_Finish: - swsusp_close(false); + swsusp_close(true); goto Finish; } -- cgit v1.2.3 From 7c95ec3b59479bb24093918bbfc801c9f31826f2 Mon Sep 17 00:00:00 2001 From: Ville Syrjälä Date: Fri, 8 Sep 2023 08:25:27 +0300 Subject: drm/i915: Only check eDP HPD when AUX CH is shared MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently Acer Chromebook C740 (BDW-ULT) doesn't have the eDP HPD line properly connected, and thus fails the new HPD check during eDP probe. The result is that we lose the eDP output. I suspect all such machines would be Chromebooks or other Linux exclusive systems as the Windows driver likely wouldn't work either. I did check a few other BDW machines here and those do have eDP HPD connected, one of them even is a different Chromebook (Samus). To account for these funky machines let's skip the HPD check when it looks like the eDP port is the only one using that specific AUX channel. In case of multiple ports sharing the same AUX CH (eg. on Asrock B250M-HDV) we still do the check and thus should correctly ignore the eDP port in favor of the other DP port (usually a DP->VGA converter). v2: Don't oops during list iteration Cc: stable@vger.kernel.org Closes: https://gitlab.freedesktop.org/drm/intel/-/issues/9264 Fixes: cfe5bdfb27fa ("drm/i915: Check HPD live state during eDP probe") Signed-off-by: Ville Syrjälä Link: https://patchwork.freedesktop.org/patch/msgid/20230908052527.685-1-ville.syrjala@linux.intel.com Reviewed-by: Luca Coelho (cherry picked from commit 70052100fabec5d8c1b09c9959817a2f4517e6b5) Signed-off-by: Rodrigo Vivi --- drivers/gpu/drm/i915/display/intel_bios.c | 21 +++++++++++++++++++++ drivers/gpu/drm/i915/display/intel_bios.h | 1 + drivers/gpu/drm/i915/display/intel_dp.c | 7 ++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/gpu/drm/i915/display/intel_bios.c b/drivers/gpu/drm/i915/display/intel_bios.c index 858c959f7bab..f735b035436c 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.c +++ b/drivers/gpu/drm/i915/display/intel_bios.c @@ -3540,6 +3540,27 @@ enum aux_ch intel_bios_dp_aux_ch(const struct intel_bios_encoder_data *devdata) return map_aux_ch(devdata->i915, devdata->child.aux_channel); } +bool intel_bios_dp_has_shared_aux_ch(const struct intel_bios_encoder_data *devdata) +{ + struct drm_i915_private *i915; + u8 aux_channel; + int count = 0; + + if (!devdata || !devdata->child.aux_channel) + return false; + + i915 = devdata->i915; + aux_channel = devdata->child.aux_channel; + + list_for_each_entry(devdata, &i915->display.vbt.display_devices, node) { + if (intel_bios_encoder_supports_dp(devdata) && + aux_channel == devdata->child.aux_channel) + count++; + } + + return count > 1; +} + int intel_bios_dp_boost_level(const struct intel_bios_encoder_data *devdata) { if (!devdata || devdata->i915->display.vbt.version < 196 || !devdata->child.iboost) diff --git a/drivers/gpu/drm/i915/display/intel_bios.h b/drivers/gpu/drm/i915/display/intel_bios.h index 9680e3e92bb5..49e24b7cf675 100644 --- a/drivers/gpu/drm/i915/display/intel_bios.h +++ b/drivers/gpu/drm/i915/display/intel_bios.h @@ -273,6 +273,7 @@ enum aux_ch intel_bios_dp_aux_ch(const struct intel_bios_encoder_data *devdata); int intel_bios_dp_boost_level(const struct intel_bios_encoder_data *devdata); int intel_bios_dp_max_lane_count(const struct intel_bios_encoder_data *devdata); int intel_bios_dp_max_link_rate(const struct intel_bios_encoder_data *devdata); +bool intel_bios_dp_has_shared_aux_ch(const struct intel_bios_encoder_data *devdata); int intel_bios_hdmi_boost_level(const struct intel_bios_encoder_data *devdata); int intel_bios_hdmi_ddc_pin(const struct intel_bios_encoder_data *devdata); int intel_bios_hdmi_level_shift(const struct intel_bios_encoder_data *devdata); diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c index 12bd2f322e62..e0e4cb529284 100644 --- a/drivers/gpu/drm/i915/display/intel_dp.c +++ b/drivers/gpu/drm/i915/display/intel_dp.c @@ -5512,8 +5512,13 @@ static bool intel_edp_init_connector(struct intel_dp *intel_dp, /* * VBT and straps are liars. Also check HPD as that seems * to be the most reliable piece of information available. + * + * ... expect on devices that forgot to hook HPD up for eDP + * (eg. Acer Chromebook C710), so we'll check it only if multiple + * ports are attempting to use the same AUX CH, according to VBT. */ - if (!intel_digital_port_connected(encoder)) { + if (intel_bios_dp_has_shared_aux_ch(encoder->devdata) && + !intel_digital_port_connected(encoder)) { /* * If this fails, presume the DPCD answer came * from some other port using the same AUX CH. -- cgit v1.2.3 From 403f0e771457e2b8811dc280719d11b9bacf10f4 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 8 Sep 2023 13:29:13 +0200 Subject: net: macb: fix sleep inside spinlock macb_set_tx_clk() is called under a spinlock but itself calls clk_set_rate() which can sleep. This results in: | BUG: sleeping function called from invalid context at kernel/locking/mutex.c:580 | pps pps1: new PPS source ptp1 | in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 40, name: kworker/u4:3 | preempt_count: 1, expected: 0 | RCU nest depth: 0, expected: 0 | 4 locks held by kworker/u4:3/40: | #0: ffff000003409148 | macb ff0c0000.ethernet: gem-ptp-timer ptp clock registered. | ((wq_completion)events_power_efficient){+.+.}-{0:0}, at: process_one_work+0x14c/0x51c | #1: ffff8000833cbdd8 ((work_completion)(&pl->resolve)){+.+.}-{0:0}, at: process_one_work+0x14c/0x51c | #2: ffff000004f01578 (&pl->state_mutex){+.+.}-{4:4}, at: phylink_resolve+0x44/0x4e8 | #3: ffff000004f06f50 (&bp->lock){....}-{3:3}, at: macb_mac_link_up+0x40/0x2ac | irq event stamp: 113998 | hardirqs last enabled at (113997): [] _raw_spin_unlock_irq+0x30/0x64 | hardirqs last disabled at (113998): [] _raw_spin_lock_irqsave+0xac/0xc8 | softirqs last enabled at (113608): [] __do_softirq+0x430/0x4e4 | softirqs last disabled at (113597): [] ____do_softirq+0x10/0x1c | CPU: 0 PID: 40 Comm: kworker/u4:3 Not tainted 6.5.0-11717-g9355ce8b2f50-dirty #368 | Hardware name: ... ZynqMP ... (DT) | Workqueue: events_power_efficient phylink_resolve | Call trace: | dump_backtrace+0x98/0xf0 | show_stack+0x18/0x24 | dump_stack_lvl+0x60/0xac | dump_stack+0x18/0x24 | __might_resched+0x144/0x24c | __might_sleep+0x48/0x98 | __mutex_lock+0x58/0x7b0 | mutex_lock_nested+0x24/0x30 | clk_prepare_lock+0x4c/0xa8 | clk_set_rate+0x24/0x8c | macb_mac_link_up+0x25c/0x2ac | phylink_resolve+0x178/0x4e8 | process_one_work+0x1ec/0x51c | worker_thread+0x1ec/0x3e4 | kthread+0x120/0x124 | ret_from_fork+0x10/0x20 The obvious fix is to move the call to macb_set_tx_clk() out of the protected area. This seems safe as rx and tx are both disabled anyway at this point. It is however not entirely clear what the spinlock shall protect. It could be the read-modify-write access to the NCFGR register, but this is accessed in macb_set_rx_mode() and macb_set_rxcsum_feature() as well without holding the spinlock. It could also be the register accesses done in mog_init_rings() or macb_init_buffers(), but again these functions are called without holding the spinlock in macb_hresp_error_task(). The locking seems fishy in this driver and it might deserve another look before this patch is applied. Fixes: 633e98a711ac0 ("net: macb: use resolved link config in mac_link_up()") Signed-off-by: Sascha Hauer Link: https://lore.kernel.org/r/20230908112913.1701766-1-s.hauer@pengutronix.de Signed-off-by: Paolo Abeni --- drivers/net/ethernet/cadence/macb_main.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 31f664ee4d77..b940dcd3ace6 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -756,8 +756,6 @@ static void macb_mac_link_up(struct phylink_config *config, if (rx_pause) ctrl |= MACB_BIT(PAE); - macb_set_tx_clk(bp, speed); - /* Initialize rings & buffers as clearing MACB_BIT(TE) in link down * cleared the pipeline and control registers. */ @@ -777,6 +775,9 @@ static void macb_mac_link_up(struct phylink_config *config, spin_unlock_irqrestore(&bp->lock, flags); + if (!(bp->caps & MACB_CAPS_MACB_IS_EMAC)) + macb_set_tx_clk(bp, speed); + /* Enable Rx and Tx; Enable PTP unicast */ ctrl = macb_readl(bp, NCR); if (gem_has_ptp(bp)) -- cgit v1.2.3 From 88956eabfdea7d01d550535af120d4ef265b1d02 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Tue, 12 Sep 2023 11:25:00 +1000 Subject: NFSD: fix possible oops when nfsd/pool_stats is closed. If /proc/fs/nfsd/pool_stats is open when the last nfsd thread exits, then when the file is closed a NULL pointer is dereferenced. This is because nfsd_pool_stats_release() assumes that the pointer to the svc_serv cannot become NULL while a reference is held. This used to be the case but a recent patch split nfsd_last_thread() out from nfsd_put(), and clearing the pointer is done in nfsd_last_thread(). This is easily reproduced by running rpc.nfsd 8 ; ( rpc.nfsd 0;true) < /proc/fs/nfsd/pool_stats Fortunately nfsd_pool_stats_release() has easy access to the svc_serv pointer, and so can call svc_put() on it directly. Fixes: 9f28a971ee9f ("nfsd: separate nfsd_last_thread() from nfsd_put()") Signed-off-by: NeilBrown Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- fs/nfsd/nfssvc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 1582af33e204..c7af1095f6b5 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c @@ -1082,11 +1082,12 @@ int nfsd_pool_stats_open(struct inode *inode, struct file *file) int nfsd_pool_stats_release(struct inode *inode, struct file *file) { + struct seq_file *seq = file->private_data; + struct svc_serv *serv = seq->private; int ret = seq_release(inode, file); - struct net *net = inode->i_sb->s_fs_info; mutex_lock(&nfsd_mutex); - nfsd_put(net); + svc_put(serv); mutex_unlock(&nfsd_mutex); return ret; } -- cgit v1.2.3 From 98a15816636044f25be4644db2a3e09fad68aaf7 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Tue, 5 Sep 2023 10:09:22 +0100 Subject: Revert "comedi: add HAS_IOPORT dependencies" This reverts commit b5c75b68b7ded84d4c82118974ce3975a4dcaa74. The commit makes it impossible to select configuration options that depend on COMEDI_8254, COMEDI_DAS08, COMEDI_NI_LABPC, or COMEDI_AMPLC_DIO200 options due to changing 'select' directives to 'depends on' directives and there being no other way to select those codependent configuration options. Fixes: b5c75b68b7de ("comedi: add HAS_IOPORT dependencies") Cc: Niklas Schnelle Cc: Arnd Bergmann Cc: # v6.5+ Acked-by: Arnd Bergmann Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20230905090922.3314-1-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/Kconfig | 103 +++++++++++++++++-------------------------------- 1 file changed, 35 insertions(+), 68 deletions(-) diff --git a/drivers/comedi/Kconfig b/drivers/comedi/Kconfig index 7a8d402f05be..9af280735cba 100644 --- a/drivers/comedi/Kconfig +++ b/drivers/comedi/Kconfig @@ -67,7 +67,6 @@ config COMEDI_TEST config COMEDI_PARPORT tristate "Parallel port support" - depends on HAS_IOPORT help Enable support for the standard parallel port. A cheap and easy way to get a few more digital I/O lines. Steal @@ -80,7 +79,6 @@ config COMEDI_PARPORT config COMEDI_SSV_DNP tristate "SSV Embedded Systems DIL/Net-PC support" depends on X86_32 || COMPILE_TEST - depends on HAS_IOPORT help Enable support for SSV Embedded Systems DIL/Net-PC @@ -91,7 +89,6 @@ endif # COMEDI_MISC_DRIVERS menuconfig COMEDI_ISA_DRIVERS bool "Comedi ISA and PC/104 drivers" - depends on ISA help Enable comedi ISA and PC/104 drivers to be built @@ -103,8 +100,7 @@ if COMEDI_ISA_DRIVERS config COMEDI_PCL711 tristate "Advantech PCL-711/711b and ADlink ACL-8112 ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-711 and 711b, ADlink ACL-8112 @@ -165,9 +161,8 @@ config COMEDI_PCL730 config COMEDI_PCL812 tristate "Advantech PCL-812/813 and ADlink ACL-8112/8113/8113/8216" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-812/PG, PCL-813/B, ADLink ACL-8112DG/HG/PG, ACL-8113, ACL-8216, ICP DAS A-821PGH/PGL/PGL-NDA, @@ -178,9 +173,8 @@ config COMEDI_PCL812 config COMEDI_PCL816 tristate "Advantech PCL-814 and PCL-816 ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-814 and PCL-816 ISA cards @@ -189,9 +183,8 @@ config COMEDI_PCL816 config COMEDI_PCL818 tristate "Advantech PCL-718 and PCL-818 ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCL-818 ISA cards PCL-818L, PCL-818H, PCL-818HD, PCL-818HG, PCL-818 and PCL-718 @@ -210,7 +203,7 @@ config COMEDI_PCM3724 config COMEDI_AMPLC_DIO200_ISA tristate "Amplicon PC212E/PC214E/PC215E/PC218E/PC272E" - depends on COMEDI_AMPLC_DIO200 + select COMEDI_AMPLC_DIO200 help Enable support for Amplicon PC212E, PC214E, PC215E, PC218E and PC272E ISA DIO boards @@ -262,8 +255,7 @@ config COMEDI_DAC02 config COMEDI_DAS16M1 tristate "MeasurementComputing CIO-DAS16/M1DAS-16 ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Measurement Computing CIO-DAS16/M1 ISA cards. @@ -273,7 +265,7 @@ config COMEDI_DAS16M1 config COMEDI_DAS08_ISA tristate "DAS-08 compatible ISA and PC/104 card support" - depends on COMEDI_DAS08 + select COMEDI_DAS08 help Enable support for Keithley Metrabyte/ComputerBoards DAS08 and compatible ISA and PC/104 cards: @@ -286,9 +278,8 @@ config COMEDI_DAS08_ISA config COMEDI_DAS16 tristate "DAS-16 compatible ISA and PC/104 card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Keithley Metrabyte/ComputerBoards DAS16 @@ -305,8 +296,7 @@ config COMEDI_DAS16 config COMEDI_DAS800 tristate "DAS800 and compatible ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Keithley Metrabyte DAS800 and compatible ISA cards Keithley Metrabyte DAS-800, DAS-801, DAS-802 @@ -318,9 +308,8 @@ config COMEDI_DAS800 config COMEDI_DAS1800 tristate "DAS1800 and compatible ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for DAS1800 and compatible ISA cards Keithley Metrabyte DAS-1701ST, DAS-1701ST-DA, DAS-1701/AO, @@ -334,8 +323,7 @@ config COMEDI_DAS1800 config COMEDI_DAS6402 tristate "DAS6402 and compatible ISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for DAS6402 and compatible ISA cards Computerboards, Keithley Metrabyte DAS6402 and compatibles @@ -414,8 +402,7 @@ config COMEDI_FL512 config COMEDI_AIO_AIO12_8 tristate "I/O Products PC/104 AIO12-8 Analog I/O Board support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for I/O Products PC/104 AIO12-8 Analog I/O Board @@ -469,9 +456,8 @@ config COMEDI_ADQ12B config COMEDI_NI_AT_A2150 tristate "NI AT-A2150 ISA card support" - depends on HAS_IOPORT select COMEDI_ISADMA if ISA_DMA_API - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for National Instruments AT-A2150 cards @@ -480,8 +466,7 @@ config COMEDI_NI_AT_A2150 config COMEDI_NI_AT_AO tristate "NI AT-AO-6/10 EISA card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for National Instruments AT-AO-6/10 cards @@ -512,7 +497,7 @@ config COMEDI_NI_ATMIO16D config COMEDI_NI_LABPC_ISA tristate "NI Lab-PC and compatibles ISA support" - depends on COMEDI_NI_LABPC + select COMEDI_NI_LABPC help Enable support for National Instruments Lab-PC and compatibles Lab-PC-1200, Lab-PC-1200AI, Lab-PC+. @@ -576,7 +561,7 @@ endif # COMEDI_ISA_DRIVERS menuconfig COMEDI_PCI_DRIVERS tristate "Comedi PCI drivers" - depends on PCI && HAS_IOPORT + depends on PCI help Enable support for comedi PCI drivers. @@ -725,8 +710,7 @@ config COMEDI_ADL_PCI8164 config COMEDI_ADL_PCI9111 tristate "ADLink PCI-9111HR support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for ADlink PCI9111 cards @@ -736,7 +720,7 @@ config COMEDI_ADL_PCI9111 config COMEDI_ADL_PCI9118 tristate "ADLink PCI-9118DG, PCI-9118HG, PCI-9118HR support" depends on HAS_DMA - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for ADlink PCI-9118DG, PCI-9118HG, PCI-9118HR cards @@ -745,8 +729,7 @@ config COMEDI_ADL_PCI9118 config COMEDI_ADV_PCI1710 tristate "Advantech PCI-171x and PCI-1731 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Advantech PCI-1710, PCI-1710HG, PCI-1711, PCI-1713 and PCI-1731 @@ -790,8 +773,7 @@ config COMEDI_ADV_PCI1760 config COMEDI_ADV_PCI_DIO tristate "Advantech PCI DIO card support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Advantech PCI DIO cards @@ -804,7 +786,7 @@ config COMEDI_ADV_PCI_DIO config COMEDI_AMPLC_DIO200_PCI tristate "Amplicon PCI215/PCI272/PCIe215/PCIe236/PCIe296 DIO support" - depends on COMEDI_AMPLC_DIO200 + select COMEDI_AMPLC_DIO200 help Enable support for Amplicon PCI215, PCI272, PCIe215, PCIe236 and PCIe296 DIO boards. @@ -832,8 +814,7 @@ config COMEDI_AMPLC_PC263_PCI config COMEDI_AMPLC_PCI224 tristate "Amplicon PCI224 and PCI234 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Amplicon PCI224 and PCI234 AO boards @@ -842,8 +823,7 @@ config COMEDI_AMPLC_PCI224 config COMEDI_AMPLC_PCI230 tristate "Amplicon PCI230 and PCI260 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for Amplicon PCI230 and PCI260 Multifunction I/O @@ -862,7 +842,7 @@ config COMEDI_CONTEC_PCI_DIO config COMEDI_DAS08_PCI tristate "DAS-08 PCI support" - depends on COMEDI_DAS08 + select COMEDI_DAS08 help Enable support for PCI DAS-08 cards. @@ -949,8 +929,7 @@ config COMEDI_CB_PCIDAS64 config COMEDI_CB_PCIDAS tristate "MeasurementComputing PCI-DAS support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for ComputerBoards/MeasurementComputing PCI-DAS with @@ -974,8 +953,7 @@ config COMEDI_CB_PCIDDA config COMEDI_CB_PCIMDAS tristate "MeasurementComputing PCIM-DAS1602/16, PCIe-DAS1602/16 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 help Enable support for ComputerBoards/MeasurementComputing PCI Migration @@ -995,8 +973,7 @@ config COMEDI_CB_PCIMDDA config COMEDI_ME4000 tristate "Meilhaus ME-4000 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Meilhaus PCI data acquisition cards ME-4650, ME-4670i, ME-4680, ME-4680i and ME-4680is @@ -1054,7 +1031,7 @@ config COMEDI_NI_670X config COMEDI_NI_LABPC_PCI tristate "NI Lab-PC PCI-1200 support" - depends on COMEDI_NI_LABPC + select COMEDI_NI_LABPC help Enable support for National Instruments Lab-PC PCI-1200. @@ -1076,7 +1053,6 @@ config COMEDI_NI_PCIDIO config COMEDI_NI_PCIMIO tristate "NI PCI-MIO-E series and M series support" depends on HAS_DMA - depends on HAS_IOPORT select COMEDI_NI_TIOCMD select COMEDI_8255 help @@ -1098,8 +1074,7 @@ config COMEDI_NI_PCIMIO config COMEDI_RTD520 tristate "Real Time Devices PCI4520/DM7520 support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for Real Time Devices PCI4520/DM7520 @@ -1139,8 +1114,7 @@ if COMEDI_PCMCIA_DRIVERS config COMEDI_CB_DAS16_CS tristate "CB DAS16 series PCMCIA support" - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 help Enable support for the ComputerBoards/MeasurementComputing PCMCIA cards DAS16/16, PCM-DAS16D/12 and PCM-DAS16s/16 @@ -1150,7 +1124,7 @@ config COMEDI_CB_DAS16_CS config COMEDI_DAS08_CS tristate "CB DAS08 PCMCIA support" - depends on COMEDI_DAS08 + select COMEDI_DAS08 help Enable support for the ComputerBoards/MeasurementComputing DAS-08 PCMCIA card @@ -1160,7 +1134,6 @@ config COMEDI_DAS08_CS config COMEDI_NI_DAQ_700_CS tristate "NI DAQCard-700 PCMCIA support" - depends on HAS_IOPORT help Enable support for the National Instruments PCMCIA DAQCard-700 DIO @@ -1169,7 +1142,6 @@ config COMEDI_NI_DAQ_700_CS config COMEDI_NI_DAQ_DIO24_CS tristate "NI DAQ-Card DIO-24 PCMCIA support" - depends on HAS_IOPORT select COMEDI_8255 help Enable support for the National Instruments PCMCIA DAQ-Card DIO-24 @@ -1179,7 +1151,7 @@ config COMEDI_NI_DAQ_DIO24_CS config COMEDI_NI_LABPC_CS tristate "NI DAQCard-1200 PCMCIA support" - depends on COMEDI_NI_LABPC + select COMEDI_NI_LABPC help Enable support for the National Instruments PCMCIA DAQCard-1200 @@ -1188,7 +1160,6 @@ config COMEDI_NI_LABPC_CS config COMEDI_NI_MIO_CS tristate "NI DAQCard E series PCMCIA support" - depends on HAS_IOPORT select COMEDI_NI_TIO select COMEDI_8255 help @@ -1201,7 +1172,6 @@ config COMEDI_NI_MIO_CS config COMEDI_QUATECH_DAQP_CS tristate "Quatech DAQP PCMCIA data capture card support" - depends on HAS_IOPORT help Enable support for the Quatech DAQP PCMCIA data capture cards DAQP-208 and DAQP-308 @@ -1278,14 +1248,12 @@ endif # COMEDI_USB_DRIVERS config COMEDI_8254 tristate - depends on HAS_IOPORT config COMEDI_8255 tristate config COMEDI_8255_SA tristate "Standalone 8255 support" - depends on HAS_IOPORT select COMEDI_8255 help Enable support for 8255 digital I/O as a standalone driver. @@ -1317,7 +1285,7 @@ config COMEDI_KCOMEDILIB called kcomedilib. config COMEDI_AMPLC_DIO200 - depends on COMEDI_8254 + select COMEDI_8254 tristate config COMEDI_AMPLC_PC236 @@ -1326,7 +1294,7 @@ config COMEDI_AMPLC_PC236 config COMEDI_DAS08 tristate - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 config COMEDI_ISADMA @@ -1334,8 +1302,7 @@ config COMEDI_ISADMA config COMEDI_NI_LABPC tristate - depends on HAS_IOPORT - depends on COMEDI_8254 + select COMEDI_8254 select COMEDI_8255 config COMEDI_NI_LABPC_ISADMA -- cgit v1.2.3 From fd6f7ad2fd4d53fa14f4fd190f9b05d043973892 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 28 Aug 2023 17:58:24 +0300 Subject: driver core: return an error when dev_set_name() hasn't happened The commit d21fdd07cea4 ("driver core: Return proper error code when dev_set_name() fails") rewrote the logic of handling the dev_set_name() error codes, but missed the point that initially set error value to -EINVAL might be rewritten and hence the error path can't be triggered at some circumstances. To fix this, make sure that error variable is set to -EINVAL when other conditionals are false. Reported-by: syzbot+bdfb03b1ec8b342c12cb@syzkaller.appspotmail.com Fixes: d21fdd07cea4 ("driver core: Return proper error code when dev_set_name() fails") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20230828145824.3895288-1-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index b7d7f410c256..4d8b315c48a1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -3537,6 +3537,8 @@ int device_add(struct device *dev) /* subsystems can specify simple device enumeration */ else if (dev->bus && dev->bus->dev_name) error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id); + else + error = -EINVAL; if (error) goto name_error; -- cgit v1.2.3 From c8414dab164a74bd3bb859a2d836cb537d6b9298 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Tue, 12 Sep 2023 21:47:52 +0800 Subject: eventfs: Fix the NULL pointer dereference bug in eventfs_remove_rec() Inject fault while probing btrfs.ko, if kstrdup() fails in eventfs_prepare_ef() in eventfs_add_dir(), it will return ERR_PTR to assign file->ef. But the eventfs_remove() check NULL in trace_module_remove_events(), which causes the below NULL pointer dereference. As both Masami and Steven suggest, allocater side should handle the error carefully and remove it, so fix the places where it failed. Could not create tracefs 'raid56_write' directory Btrfs loaded, zoned=no, fsverity=no Unable to handle kernel NULL pointer dereference at virtual address 000000000000001c Mem abort info: ESR = 0x0000000096000004 EC = 0x25: DABT (current EL), IL = 32 bits SET = 0, FnV = 0 EA = 0, S1PTW = 0 FSC = 0x04: level 0 translation fault Data abort info: ISV = 0, ISS = 0x00000004, ISS2 = 0x00000000 CM = 0, WnR = 0, TnD = 0, TagAccess = 0 GCS = 0, Overlay = 0, DirtyBit = 0, Xs = 0 user pgtable: 4k pages, 48-bit VAs, pgdp=0000000102544000 [000000000000001c] pgd=0000000000000000, p4d=0000000000000000 Internal error: Oops: 0000000096000004 [#1] PREEMPT SMP Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: btrfs(-) libcrc32c xor xor_neon raid6_pq cfg80211 rfkill 8021q garp mrp stp llc ipv6 [last unloaded: btrfs] CPU: 15 PID: 1343 Comm: rmmod Tainted: G N 6.5.0+ #40 Hardware name: linux,dummy-virt (DT) pstate: 80000005 (Nzcv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : eventfs_remove_rec+0x24/0xc0 lr : eventfs_remove+0x68/0x1d8 sp : ffff800082d63b60 x29: ffff800082d63b60 x28: ffffb84b80ddd00c x27: ffffb84b3054ba40 x26: 0000000000000002 x25: ffff800082d63bf8 x24: ffffb84b8398e440 x23: ffffb84b82af3000 x22: dead000000000100 x21: dead000000000122 x20: ffff800082d63bf8 x19: fffffffffffffff4 x18: ffffb84b82508820 x17: 0000000000000000 x16: 0000000000000000 x15: 000083bc876a3166 x14: 000000000000006d x13: 000000000000006d x12: 0000000000000000 x11: 0000000000000001 x10: 00000000000017e0 x9 : 0000000000000001 x8 : 0000000000000000 x7 : 0000000000000000 x6 : ffffb84b84289804 x5 : 0000000000000000 x4 : 9696969696969697 x3 : ffff33a5b7601f38 x2 : 0000000000000000 x1 : ffff800082d63bf8 x0 : fffffffffffffff4 Call trace: eventfs_remove_rec+0x24/0xc0 eventfs_remove+0x68/0x1d8 remove_event_file_dir+0x88/0x100 event_remove+0x140/0x15c trace_module_notify+0x1fc/0x230 notifier_call_chain+0x98/0x17c blocking_notifier_call_chain+0x4c/0x74 __arm64_sys_delete_module+0x1a4/0x298 invoke_syscall+0x44/0x100 el0_svc_common.constprop.1+0x68/0xe0 do_el0_svc+0x1c/0x28 el0_svc+0x3c/0xc4 el0t_64_sync_handler+0xa0/0xc4 el0t_64_sync+0x174/0x178 Code: 5400052c a90153b3 aa0003f3 aa0103f4 (f9401400) ---[ end trace 0000000000000000 ]--- Kernel panic - not syncing: Oops: Fatal exception SMP: stopping secondary CPUs Dumping ftrace buffer: (ftrace buffer empty) Kernel Offset: 0x384b00c00000 from 0xffff800080000000 PHYS_OFFSET: 0xffffcc5b80000000 CPU features: 0x88000203,3c020000,1000421b Memory Limit: none Rebooting in 1 seconds.. Link: https://lore.kernel.org/linux-trace-kernel/20230912134752.1838524-1-ruanjinjie@huawei.com Link: https://lore.kernel.org/all/20230912025808.668187-1-ruanjinjie@huawei.com/ Link: https://lore.kernel.org/all/20230911052818.1020547-1-ruanjinjie@huawei.com/ Link: https://lore.kernel.org/all/20230909072817.182846-1-ruanjinjie@huawei.com/ Link: https://lore.kernel.org/all/20230908074816.3724716-1-ruanjinjie@huawei.com/ Cc: Ajay Kaher Fixes: 5bdcd5f5331a ("eventfs: Implement removal of meta data from eventfs") Signed-off-by: Jinjie Ruan Suggested-by: Masami Hiramatsu (Google) Suggested-by: Steven Rostedt Signed-off-by: Steven Rostedt (Google) --- kernel/trace/trace_events.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/kernel/trace/trace_events.c b/kernel/trace/trace_events.c index 065c63991858..91951d038ba4 100644 --- a/kernel/trace/trace_events.c +++ b/kernel/trace/trace_events.c @@ -2286,6 +2286,7 @@ event_subsystem_dir(struct trace_array *tr, const char *name, { struct event_subsystem *system, *iter; struct trace_subsystem_dir *dir; + struct eventfs_file *ef; int res; /* First see if we did not already create this dir */ @@ -2318,13 +2319,14 @@ event_subsystem_dir(struct trace_array *tr, const char *name, } else __get_system(system); - dir->ef = eventfs_add_subsystem_dir(name, parent); - if (IS_ERR(dir->ef)) { + ef = eventfs_add_subsystem_dir(name, parent); + if (IS_ERR(ef)) { pr_warn("Failed to create system directory %s\n", name); __put_system(system); goto out_free; } + dir->ef = ef; dir->tr = tr; dir->ref_count = 1; dir->nr_events = 1; @@ -2404,6 +2406,7 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) struct trace_event_call *call = file->event_call; struct eventfs_file *ef_subsystem = NULL; struct trace_array *tr = file->tr; + struct eventfs_file *ef; const char *name; int ret; @@ -2420,12 +2423,14 @@ event_create_dir(struct dentry *parent, struct trace_event_file *file) return -ENOMEM; name = trace_event_name(call); - file->ef = eventfs_add_dir(name, ef_subsystem); - if (IS_ERR(file->ef)) { + ef = eventfs_add_dir(name, ef_subsystem); + if (IS_ERR(ef)) { pr_warn("Could not create tracefs '%s' directory\n", name); return -1; } + file->ef = ef; + if (call->class->reg && !(call->flags & TRACE_EVENT_FL_IGNORE_ENABLE)) eventfs_add_file("enable", TRACE_MODE_WRITE, file->ef, file, &ftrace_enable_fops); -- cgit v1.2.3 From 7a6102aa6df0d5d032b4cbc51935d1d4cda17254 Mon Sep 17 00:00:00 2001 From: Toke Høiland-Jørgensen Date: Mon, 11 Sep 2023 15:58:25 +0200 Subject: veth: Update XDP feature set when bringing up device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's an early return in veth_set_features() if the device is in a down state, which leads to the XDP feature flags not being updated when enabling GRO while the device is down. Which in turn leads to XDP_REDIRECT not working, because the redirect code now checks the flags. Fix this by updating the feature flags after bringing the device up. Before this patch: NETDEV_XDP_ACT_BASIC: yes NETDEV_XDP_ACT_REDIRECT: yes NETDEV_XDP_ACT_NDO_XMIT: no NETDEV_XDP_ACT_XSK_ZEROCOPY: no NETDEV_XDP_ACT_HW_OFFLOAD: no NETDEV_XDP_ACT_RX_SG: yes NETDEV_XDP_ACT_NDO_XMIT_SG: no After this patch: NETDEV_XDP_ACT_BASIC: yes NETDEV_XDP_ACT_REDIRECT: yes NETDEV_XDP_ACT_NDO_XMIT: yes NETDEV_XDP_ACT_XSK_ZEROCOPY: no NETDEV_XDP_ACT_HW_OFFLOAD: no NETDEV_XDP_ACT_RX_SG: yes NETDEV_XDP_ACT_NDO_XMIT_SG: yes Fixes: fccca038f300 ("veth: take into account device reconfiguration for xdp_features flag") Fixes: 66c0e13ad236 ("drivers: net: turn on XDP features") Signed-off-by: Toke Høiland-Jørgensen Link: https://lore.kernel.org/r/20230911135826.722295-1-toke@redhat.com Signed-off-by: Paolo Abeni --- drivers/net/veth.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/net/veth.c b/drivers/net/veth.c index 9c6f4f83f22b..0deefd1573cf 100644 --- a/drivers/net/veth.c +++ b/drivers/net/veth.c @@ -1446,6 +1446,8 @@ static int veth_open(struct net_device *dev) netif_carrier_on(peer); } + veth_set_xdp_features(dev); + return 0; } -- cgit v1.2.3 From 7e021da80f48582171029714f8a487347f29dddb Mon Sep 17 00:00:00 2001 From: "Masami Hiramatsu (Google)" Date: Tue, 12 Sep 2023 10:10:39 +0900 Subject: selftests: tracing: Fix to unmount tracefs for recovering environment Fix to unmount the tracefs if the ftracetest mounted it for recovering system environment. If the tracefs is already mounted, this does nothing. Suggested-by: Mark Brown Link: https://lore.kernel.org/all/29fce076-746c-4650-8358-b4e0fa215cf7@sirena.org.uk/ Fixes: cbd965bde74c ("ftrace/selftests: Return the skip code when tracing directory not configured in kernel") Signed-off-by: Masami Hiramatsu (Google) Reviewed-by: Steven Rostedt (Google) Reviewed-by: Mark Brown Signed-off-by: Shuah Khan --- tools/testing/selftests/ftrace/ftracetest | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/testing/selftests/ftrace/ftracetest b/tools/testing/selftests/ftrace/ftracetest index 7df8baa0f98f..c778d4dcc17e 100755 --- a/tools/testing/selftests/ftrace/ftracetest +++ b/tools/testing/selftests/ftrace/ftracetest @@ -31,6 +31,9 @@ err_ret=1 # kselftest skip code is 4 err_skip=4 +# umount required +UMOUNT_DIR="" + # cgroup RT scheduling prevents chrt commands from succeeding, which # induces failures in test wakeup tests. Disable for the duration of # the tests. @@ -45,6 +48,9 @@ setup() { cleanup() { echo $sched_rt_runtime_orig > $sched_rt_runtime + if [ -n "${UMOUNT_DIR}" ]; then + umount ${UMOUNT_DIR} ||: + fi } errexit() { # message @@ -161,11 +167,13 @@ if [ -z "$TRACING_DIR" ]; then mount -t tracefs nodev /sys/kernel/tracing || errexit "Failed to mount /sys/kernel/tracing" TRACING_DIR="/sys/kernel/tracing" + UMOUNT_DIR=${TRACING_DIR} # If debugfs exists, then so does /sys/kernel/debug elif [ -d "/sys/kernel/debug" ]; then mount -t debugfs nodev /sys/kernel/debug || errexit "Failed to mount /sys/kernel/debug" TRACING_DIR="/sys/kernel/debug/tracing" + UMOUNT_DIR=${TRACING_DIR} else err_ret=$err_skip errexit "debugfs and tracefs are not configured in this kernel" -- cgit v1.2.3 From 08700ec705043eb0cee01b35cf5b9d63f0230d12 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 6 Sep 2023 03:46:57 +0900 Subject: linux/export: fix reference to exported functions for parisc64 John David Anglin reported parisc has been broken since commit ddb5cdbafaaa ("kbuild: generate KSYMTAB entries by modpost"). Like ia64, parisc64 uses a function descriptor. The function references must be prefixed with P%. Also, symbols prefixed $$ from the library have the symbol type STT_LOPROC instead of STT_FUNC. They should be handled as functions too. Fixes: ddb5cdbafaaa ("kbuild: generate KSYMTAB entries by modpost") Reported-by: John David Anglin Tested-by: John David Anglin Tested-by: Helge Deller Closes: https://lore.kernel.org/linux-parisc/1901598a-e11d-f7dd-a5d9-9a69d06e6b6e@bell.net/T/#u Signed-off-by: Masahiro Yamada Signed-off-by: Helge Deller --- include/linux/export-internal.h | 2 ++ scripts/mod/modpost.c | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/include/linux/export-internal.h b/include/linux/export-internal.h index 1c849db953a5..45fca09b2319 100644 --- a/include/linux/export-internal.h +++ b/include/linux/export-internal.h @@ -52,6 +52,8 @@ #ifdef CONFIG_IA64 #define KSYM_FUNC(name) @fptr(name) +#elif defined(CONFIG_PARISC) && defined(CONFIG_64BIT) +#define KSYM_FUNC(name) P%name #else #define KSYM_FUNC(name) name #endif diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c index b29b29707f10..ba981f22908a 100644 --- a/scripts/mod/modpost.c +++ b/scripts/mod/modpost.c @@ -1226,6 +1226,15 @@ static void check_export_symbol(struct module *mod, struct elf_info *elf, */ s->is_func = (ELF_ST_TYPE(sym->st_info) == STT_FUNC); + /* + * For parisc64, symbols prefixed $$ from the library have the symbol type + * STT_LOPROC. They should be handled as functions too. + */ + if (elf->hdr->e_ident[EI_CLASS] == ELFCLASS64 && + elf->hdr->e_machine == EM_PARISC && + ELF_ST_TYPE(sym->st_info) == STT_LOPROC) + s->is_func = true; + if (match(secname, PATTERNS(INIT_SECTIONS))) warn("%s: %s: EXPORT_SYMBOL used for init symbol. Remove __init or EXPORT_SYMBOL.\n", mod->name, name); -- cgit v1.2.3 From 25e73b7e3f72a25aa30cbb2eecb49036e0acf066 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Aug 2023 12:55:46 +0200 Subject: x86/ibt: Suppress spurious ENDBR It was reported that under certain circumstances GCC emits ENDBR instructions for _THIS_IP_ usage. Specifically, when it appears at the start of a basic block -- but not elsewhere. Since _THIS_IP_ is never used for control flow, these ENDBR instructions are completely superfluous. Override the _THIS_IP_ definition for x86_64 to avoid this. Less ENDBR instructions is better. Fixes: 156ff4a544ae ("x86/ibt: Base IBT bits") Reported-by: David Kaplan Reviewed-by: Andrew Cooper Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/20230802110323.016197440@infradead.org --- arch/x86/include/asm/linkage.h | 8 ++++++++ include/linux/instruction_pointer.h | 5 +++++ 2 files changed, 13 insertions(+) diff --git a/arch/x86/include/asm/linkage.h b/arch/x86/include/asm/linkage.h index 97a3de7892d3..5ff49fd67732 100644 --- a/arch/x86/include/asm/linkage.h +++ b/arch/x86/include/asm/linkage.h @@ -8,6 +8,14 @@ #undef notrace #define notrace __attribute__((no_instrument_function)) +#ifdef CONFIG_64BIT +/* + * The generic version tends to create spurious ENDBR instructions under + * certain conditions. + */ +#define _THIS_IP_ ({ unsigned long __here; asm ("lea 0(%%rip), %0" : "=r" (__here)); __here; }) +#endif + #ifdef CONFIG_X86_32 #define asmlinkage CPP_ASMLINKAGE __attribute__((regparm(0))) #endif /* CONFIG_X86_32 */ diff --git a/include/linux/instruction_pointer.h b/include/linux/instruction_pointer.h index cda1f706eaeb..aa0b3ffea935 100644 --- a/include/linux/instruction_pointer.h +++ b/include/linux/instruction_pointer.h @@ -2,7 +2,12 @@ #ifndef _LINUX_INSTRUCTION_POINTER_H #define _LINUX_INSTRUCTION_POINTER_H +#include + #define _RET_IP_ (unsigned long)__builtin_return_address(0) + +#ifndef _THIS_IP_ #define _THIS_IP_ ({ __label__ __here; __here: (unsigned long)&&__here; }) +#endif #endif /* _LINUX_INSTRUCTION_POINTER_H */ -- cgit v1.2.3 From 7575e5a35267983dcbeb1e0d3a49d21ae3cf0b82 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 2 Aug 2023 12:55:47 +0200 Subject: x86/ibt: Avoid duplicate ENDBR in __put_user_nocheck*() Commit cb855971d717 ("x86/putuser: Provide room for padding") changed __put_user_nocheck_*() into proper functions but failed to note that SYM_FUNC_START() already provides ENDBR, rendering the explicit ENDBR superfluous. Fixes: cb855971d717 ("x86/putuser: Provide room for padding") Reported-by: David Kaplan Reviewed-by: Andrew Cooper Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/20230802110323.086971726@infradead.org --- arch/x86/lib/putuser.S | 4 ---- 1 file changed, 4 deletions(-) diff --git a/arch/x86/lib/putuser.S b/arch/x86/lib/putuser.S index 1451e0c4ae22..235bbda6fc82 100644 --- a/arch/x86/lib/putuser.S +++ b/arch/x86/lib/putuser.S @@ -56,7 +56,6 @@ SYM_FUNC_END(__put_user_1) EXPORT_SYMBOL(__put_user_1) SYM_FUNC_START(__put_user_nocheck_1) - ENDBR ASM_STAC 2: movb %al,(%_ASM_CX) xor %ecx,%ecx @@ -76,7 +75,6 @@ SYM_FUNC_END(__put_user_2) EXPORT_SYMBOL(__put_user_2) SYM_FUNC_START(__put_user_nocheck_2) - ENDBR ASM_STAC 4: movw %ax,(%_ASM_CX) xor %ecx,%ecx @@ -96,7 +94,6 @@ SYM_FUNC_END(__put_user_4) EXPORT_SYMBOL(__put_user_4) SYM_FUNC_START(__put_user_nocheck_4) - ENDBR ASM_STAC 6: movl %eax,(%_ASM_CX) xor %ecx,%ecx @@ -119,7 +116,6 @@ SYM_FUNC_END(__put_user_8) EXPORT_SYMBOL(__put_user_8) SYM_FUNC_START(__put_user_nocheck_8) - ENDBR ASM_STAC 9: mov %_ASM_AX,(%_ASM_CX) #ifdef CONFIG_X86_32 -- cgit v1.2.3 From dad651b2a44eb6b201738f810254279dca29d30d Mon Sep 17 00:00:00 2001 From: Pratyush Yadav Date: Tue, 12 Sep 2023 17:52:49 +0200 Subject: nvme-pci: do not set the NUMA node of device if it has none If a device has no NUMA node information associated with it, the driver puts the device in node first_memory_node (say node 0). Not having a NUMA node and being associated with node 0 are completely different things and it makes little sense to mix the two. Signed-off-by: Pratyush Yadav Signed-off-by: Keith Busch --- drivers/nvme/host/pci.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index baf69af7ea78..f5ba2d7102ea 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -2916,9 +2916,6 @@ static struct nvme_dev *nvme_pci_alloc_dev(struct pci_dev *pdev, struct nvme_dev *dev; int ret = -ENOMEM; - if (node == NUMA_NO_NODE) - set_dev_node(&pdev->dev, first_memory_node); - dev = kzalloc_node(sizeof(*dev), GFP_KERNEL, node); if (!dev) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From 8cdd9f1aaedf823006449faa4e540026c692ac43 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 11 Sep 2023 15:42:13 +0000 Subject: ipv6: fix ip6_sock_set_addr_preferences() typo ip6_sock_set_addr_preferences() second argument should be an integer. SUNRPC attempts to set IPV6_PREFER_SRC_PUBLIC were translated to IPV6_PREFER_SRC_TMP Fixes: 18d5ad623275 ("ipv6: add ip6_sock_set_addr_preferences") Signed-off-by: Eric Dumazet Cc: Christoph Hellwig Cc: Chuck Lever Reviewed-by: Simon Horman Link: https://lore.kernel.org/r/20230911154213.713941-1-edumazet@google.com Signed-off-by: Paolo Abeni --- include/net/ipv6.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index 0675be0f3fa0..fe274c122a56 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -1360,7 +1360,7 @@ static inline int __ip6_sock_set_addr_preferences(struct sock *sk, int val) return 0; } -static inline int ip6_sock_set_addr_preferences(struct sock *sk, bool val) +static inline int ip6_sock_set_addr_preferences(struct sock *sk, int val) { int ret; -- cgit v1.2.3 From 05d0f8f55ad60854cb706798da94276a33590445 Mon Sep 17 00:00:00 2001 From: Steve French Date: Tue, 12 Sep 2023 14:08:36 -0500 Subject: smb3: move server check earlier when setting channel sequence number Smatch warning pointed out by Dan Carpenter: fs/smb/client/smb2pdu.c:105 smb2_hdr_assemble() warn: variable dereferenced before check 'server' (see line 95) Fixes: 09ee7a3bf866 ("[SMB3] send channel sequence number in SMB3 requests after reconnects") Reported-by: Dan Carpenter Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 092b0087c9dc..3403188e3100 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -92,17 +92,22 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, shdr->ProtocolId = SMB2_PROTO_NUMBER; shdr->StructureSize = cpu_to_le16(64); shdr->Command = smb2_cmd; - if (server->dialect >= SMB30_PROT_ID) { - /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ - smb3_hdr = (struct smb3_hdr_req *)shdr; - /* if primary channel is not set yet, use default channel for chan sequence num */ - if (SERVER_IS_CHAN(server)) - smb3_hdr->ChannelSequence = - cpu_to_le16(server->primary_server->channel_sequence_num); - else - smb3_hdr->ChannelSequence = cpu_to_le16(server->channel_sequence_num); - } + if (server) { + /* After reconnect SMB3 must set ChannelSequence on subsequent reqs */ + if (server->dialect >= SMB30_PROT_ID) { + smb3_hdr = (struct smb3_hdr_req *)shdr; + /* + * if primary channel is not set yet, use default + * channel for chan sequence num + */ + if (SERVER_IS_CHAN(server)) + smb3_hdr->ChannelSequence = + cpu_to_le16(server->primary_server->channel_sequence_num); + else + smb3_hdr->ChannelSequence = + cpu_to_le16(server->channel_sequence_num); + } spin_lock(&server->req_lock); /* Request up to 10 credits but don't go over the limit. */ if (server->credits >= server->max_credits) -- cgit v1.2.3 From ea72883a3bf11fb09dd1ad4f8328cc040263881a Mon Sep 17 00:00:00 2001 From: "Justin M. Forbes" Date: Tue, 12 Sep 2023 12:02:47 -0500 Subject: tpm: Fix typo in tpmrm class definition Commit d2e8071bed0be ("tpm: make all 'class' structures const") unfortunately had a typo for the name on tpmrm. Fixes: d2e8071bed0b ("tpm: make all 'class' structures const") Signed-off-by: Justin M. Forbes Signed-off-by: Jarkko Sakkinen --- drivers/char/tpm/tpm-chip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 23f6f2eda84c..42b1062e33cd 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -33,7 +33,7 @@ const struct class tpm_class = { .shutdown_pre = tpm_class_shutdown, }; const struct class tpmrm_class = { - .name = "tmprm", + .name = "tpmrm", }; dev_t tpm_devt; -- cgit v1.2.3 From 1bfb2b618d52e59a4ef1896b46c4698ad2be66b7 Mon Sep 17 00:00:00 2001 From: Song Shuai Date: Wed, 6 Sep 2023 17:58:17 +0800 Subject: riscv: kexec: Align the kexeced kernel entry The current riscv boot protocol requires 2MB alignment for RV64 and 4MB alignment for RV32. In KEXEC_FILE path, the elf_find_pbase() function should align the kexeced kernel entry according to the requirement, otherwise the kexeced kernel would silently BUG at the setup_vm(). Fixes: 8acea455fafa ("RISC-V: Support for kexec_file on panic") Signed-off-by: Song Shuai Link: https://lore.kernel.org/r/20230906095817.364390-1-songshuaishuai@tinylab.org Signed-off-by: Palmer Dabbelt --- arch/riscv/kernel/elf_kexec.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kernel/elf_kexec.c b/arch/riscv/kernel/elf_kexec.c index f4099059ed8f..e60fbd8660c4 100644 --- a/arch/riscv/kernel/elf_kexec.c +++ b/arch/riscv/kernel/elf_kexec.c @@ -98,7 +98,13 @@ static int elf_find_pbase(struct kimage *image, unsigned long kernel_len, kbuf.image = image; kbuf.buf_min = lowest_paddr; kbuf.buf_max = ULONG_MAX; - kbuf.buf_align = PAGE_SIZE; + + /* + * Current riscv boot protocol requires 2MB alignment for + * RV64 and 4MB alignment for RV32 + * + */ + kbuf.buf_align = PMD_SIZE; kbuf.mem = KEXEC_BUF_MEM_UNKNOWN; kbuf.memsz = ALIGN(kernel_len, PAGE_SIZE); kbuf.top_down = false; -- cgit v1.2.3 From 8eb8fe67e2c84324398f5983c41b4f831d0705b3 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Tue, 12 Sep 2023 15:24:10 +0800 Subject: riscv: errata: fix T-Head dcache.cva encoding The dcache.cva encoding shown in the comments are wrong, it's for dcache.cval1 (which is restricted to L1) instead. Fix this in the comment and in the hardcoded instruction. Signed-off-by: Icenowy Zheng Tested-by: Sergey Matyukevich Reviewed-by: Heiko Stuebner Reviewed-by: Guo Ren Tested-by: Drew Fustini Link: https://lore.kernel.org/r/20230912072410.2481-1-jszhang@kernel.org Signed-off-by: Palmer Dabbelt --- arch/riscv/include/asm/errata_list.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/riscv/include/asm/errata_list.h b/arch/riscv/include/asm/errata_list.h index e2ecd01bfac7..b55b434f0059 100644 --- a/arch/riscv/include/asm/errata_list.h +++ b/arch/riscv/include/asm/errata_list.h @@ -105,7 +105,7 @@ asm volatile(ALTERNATIVE( \ * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | * 0000001 01001 rs1 000 00000 0001011 * dcache.cva rs1 (clean, virtual address) - * 0000001 00100 rs1 000 00000 0001011 + * 0000001 00101 rs1 000 00000 0001011 * * dcache.cipa rs1 (clean then invalidate, physical address) * | 31 - 25 | 24 - 20 | 19 - 15 | 14 - 12 | 11 - 7 | 6 - 0 | @@ -118,7 +118,7 @@ asm volatile(ALTERNATIVE( \ * 0000000 11001 00000 000 00000 0001011 */ #define THEAD_inval_A0 ".long 0x0265000b" -#define THEAD_clean_A0 ".long 0x0245000b" +#define THEAD_clean_A0 ".long 0x0255000b" #define THEAD_flush_A0 ".long 0x0275000b" #define THEAD_SYNC_S ".long 0x0190000b" -- cgit v1.2.3 From ccf1dab96be4caed7c5235b1cfdb606ac161b996 Mon Sep 17 00:00:00 2001 From: Ondrej Mosnacek Date: Mon, 11 Sep 2023 16:23:58 +0200 Subject: selinux: fix handling of empty opts in selinux_fs_context_submount() selinux_set_mnt_opts() relies on the fact that the mount options pointer is always NULL when all options are unset (specifically in its !selinux_initialized() branch. However, the new selinux_fs_context_submount() hook breaks this rule by allocating a new structure even if no options are set. That causes any submount created before a SELinux policy is loaded to be rejected in selinux_set_mnt_opts(). Fix this by making selinux_fs_context_submount() leave fc->security set to NULL when there are no options to be copied from the reference superblock. Cc: Reported-by: Adam Williamson Link: https://bugzilla.redhat.com/show_bug.cgi?id=2236345 Fixes: d80a8f1b58c2 ("vfs, security: Fix automount superblock LSM init problem, preventing NFS sb sharing") Signed-off-by: Ondrej Mosnacek Reviewed-by: Jeff Layton Signed-off-by: Paul Moore --- security/selinux/hooks.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 10350534de6d..2aa0e219d721 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2775,14 +2775,20 @@ static int selinux_umount(struct vfsmount *mnt, int flags) static int selinux_fs_context_submount(struct fs_context *fc, struct super_block *reference) { - const struct superblock_security_struct *sbsec; + const struct superblock_security_struct *sbsec = selinux_superblock(reference); struct selinux_mnt_opts *opts; + /* + * Ensure that fc->security remains NULL when no options are set + * as expected by selinux_set_mnt_opts(). + */ + if (!(sbsec->flags & (FSCONTEXT_MNT|CONTEXT_MNT|DEFCONTEXT_MNT))) + return 0; + opts = kzalloc(sizeof(*opts), GFP_KERNEL); if (!opts) return -ENOMEM; - sbsec = selinux_superblock(reference); if (sbsec->flags & FSCONTEXT_MNT) opts->fscontext_sid = sbsec->sid; if (sbsec->flags & CONTEXT_MNT) -- cgit v1.2.3 From edcfe22985d09ee8e2346c9217f5a52ab150099f Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Mon, 11 Sep 2023 14:49:06 -0400 Subject: drm/amdkfd: Insert missing TLB flush on GFX10 and later Heavy-weight TLB flush is required after unmap on all GPUs for correctness and security. Signed-off-by: Harish Kasiviswanathan Reviewed-by: Felix Kuehling Signed-off-by: Alex Deucher Cc: stable@vger.kernel.org --- drivers/gpu/drm/amd/amdkfd/kfd_priv.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 3d9ce44d88da..fa24e1852493 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -1466,8 +1466,7 @@ void kfd_flush_tlb(struct kfd_process_device *pdd, enum TLB_FLUSH_TYPE type); static inline bool kfd_flush_tlb_after_unmap(struct kfd_dev *dev) { - return KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 3) || - KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 2) || + return KFD_GC_VERSION(dev) > IP_VERSION(9, 4, 2) || (KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 1) && dev->sdma_fw_version >= 18) || KFD_GC_VERSION(dev) == IP_VERSION(9, 4, 0); } -- cgit v1.2.3 From c6d277064b1da7f9015b575a562734de87a7e463 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 11:36:55 -0700 Subject: tcp: Factorise sk_family-independent comparison in inet_bind2_bucket_match(_addr_any). This is a prep patch to make the following patches cleaner that touch inet_bind2_bucket_match() and inet_bind2_bucket_match_addr_any(). Both functions have duplicated comparison for netns, port, and l3mdev. Let's factorise them. Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/inet_hashtables.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 7876b7d703cb..5c54f2804174 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -815,41 +815,39 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, const struct net *net, unsigned short port, int l3mdev, const struct sock *sk) { + if (!net_eq(ib2_net(tb), net) || tb->port != port || + tb->l3mdev != l3mdev) + return false; + #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family != tb->family) return false; if (sk->sk_family == AF_INET6) - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && - ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr); - else + return ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr); #endif - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && tb->rcv_saddr == sk->sk_rcv_saddr; + return tb->rcv_saddr == sk->sk_rcv_saddr; } bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const struct net *net, unsigned short port, int l3mdev, const struct sock *sk) { + if (!net_eq(ib2_net(tb), net) || tb->port != port || + tb->l3mdev != l3mdev) + return false; + #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family != tb->family) { if (sk->sk_family == AF_INET) - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && - ipv6_addr_any(&tb->v6_rcv_saddr); + return ipv6_addr_any(&tb->v6_rcv_saddr); return false; } if (sk->sk_family == AF_INET6) - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && - ipv6_addr_any(&tb->v6_rcv_saddr); - else + return ipv6_addr_any(&tb->v6_rcv_saddr); #endif - return net_eq(ib2_net(tb), net) && tb->port == port && - tb->l3mdev == l3mdev && tb->rcv_saddr == 0; + return tb->rcv_saddr == 0; } /* The socket's bhash2 hashbucket spinlock must be held when this is called */ -- cgit v1.2.3 From aa99e5f87bd54db55dd37cb130bd5eb55933027f Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 11:36:56 -0700 Subject: tcp: Fix bind() regression for v4-mapped-v6 wildcard address. Andrei Vagin reported bind() regression with strace logs. If we bind() a TCPv6 socket to ::FFFF:0.0.0.0 and then bind() a TCPv4 socket to 127.0.0.1, the 2nd bind() should fail but now succeeds. from socket import * s1 = socket(AF_INET6, SOCK_STREAM) s1.bind(('::ffff:0.0.0.0', 0)) s2 = socket(AF_INET, SOCK_STREAM) s2.bind(('127.0.0.1', s1.getsockname()[1])) During the 2nd bind(), if tb->family is AF_INET6 and sk->sk_family is AF_INET in inet_bind2_bucket_match_addr_any(), we still need to check if tb has the v4-mapped-v6 wildcard address. The example above does not work after commit 5456262d2baa ("net: Fix incorrect address comparison when searching for a bind2 bucket"), but the blamed change is not the commit. Before the commit, the leading zeros of ::FFFF:0.0.0.0 were treated as 0.0.0.0, and the sequence above worked by chance. Technically, this case has been broken since bhash2 was introduced. Note that if we bind() two sockets to 127.0.0.1 and then ::FFFF:0.0.0.0, the 2nd bind() fails properly because we fall back to using bhash to detect conflicts for the v4-mapped-v6 address. Fixes: 28044fc1d495 ("net: Add a bhash2 table hashed by port and address") Reported-by: Andrei Vagin Closes: https://lore.kernel.org/netdev/ZPuYBOFC8zsK6r9T@google.com/ Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/ipv6.h | 5 +++++ net/ipv4/inet_hashtables.c | 3 ++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/net/ipv6.h b/include/net/ipv6.h index fe274c122a56..c6932d1a3fa8 100644 --- a/include/net/ipv6.h +++ b/include/net/ipv6.h @@ -784,6 +784,11 @@ static inline bool ipv6_addr_v4mapped(const struct in6_addr *a) cpu_to_be32(0x0000ffff))) == 0UL; } +static inline bool ipv6_addr_v4mapped_any(const struct in6_addr *a) +{ + return ipv6_addr_v4mapped(a) && ipv4_is_zeronet(a->s6_addr32[3]); +} + static inline bool ipv6_addr_v4mapped_loopback(const struct in6_addr *a) { return ipv6_addr_v4mapped(a) && ipv4_is_loopback(a->s6_addr32[3]); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 5c54f2804174..a58b04052ca6 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -839,7 +839,8 @@ bool inet_bind2_bucket_match_addr_any(const struct inet_bind2_bucket *tb, const #if IS_ENABLED(CONFIG_IPV6) if (sk->sk_family != tb->family) { if (sk->sk_family == AF_INET) - return ipv6_addr_any(&tb->v6_rcv_saddr); + return ipv6_addr_any(&tb->v6_rcv_saddr) || + ipv6_addr_v4mapped_any(&tb->v6_rcv_saddr); return false; } -- cgit v1.2.3 From c48ef9c4aed3632566b57ba66cec6ec78624d4cb Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 11:36:57 -0700 Subject: tcp: Fix bind() regression for v4-mapped-v6 non-wildcard address. Since bhash2 was introduced, the example below does not work as expected. These two bind() should conflict, but the 2nd bind() now succeeds. from socket import * s1 = socket(AF_INET6, SOCK_STREAM) s1.bind(('::ffff:127.0.0.1', 0)) s2 = socket(AF_INET, SOCK_STREAM) s2.bind(('127.0.0.1', s1.getsockname()[1])) During the 2nd bind() in inet_csk_get_port(), inet_bind2_bucket_find() fails to find the 1st socket's tb2, so inet_bind2_bucket_create() allocates a new tb2 for the 2nd socket. Then, we call inet_csk_bind_conflict() that checks conflicts in the new tb2 by inet_bhash2_conflict(). However, the new tb2 does not include the 1st socket, thus the bind() finally succeeds. In this case, inet_bind2_bucket_match() must check if AF_INET6 tb2 has the conflicting v4-mapped-v6 address so that inet_bind2_bucket_find() returns the 1st socket's tb2. Note that if we bind two sockets to 127.0.0.1 and then ::FFFF:127.0.0.1, the 2nd bind() fails properly for the same reason mentinoed in the previous commit. Fixes: 28044fc1d495 ("net: Add a bhash2 table hashed by port and address") Signed-off-by: Kuniyuki Iwashima Reviewed-by: Eric Dumazet Acked-by: Andrei Vagin Signed-off-by: David S. Miller --- net/ipv4/inet_hashtables.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index a58b04052ca6..c32f5e28758b 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -820,8 +820,13 @@ static bool inet_bind2_bucket_match(const struct inet_bind2_bucket *tb, return false; #if IS_ENABLED(CONFIG_IPV6) - if (sk->sk_family != tb->family) + if (sk->sk_family != tb->family) { + if (sk->sk_family == AF_INET) + return ipv6_addr_v4mapped(&tb->v6_rcv_saddr) && + tb->v6_rcv_saddr.s6_addr32[3] == sk->sk_rcv_saddr; + return false; + } if (sk->sk_family == AF_INET6) return ipv6_addr_equal(&tb->v6_rcv_saddr, &sk->sk_v6_rcv_saddr); -- cgit v1.2.3 From 0071d15517b4a3d265abc00395beb1138e7236c7 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 11:36:58 -0700 Subject: selftest: tcp: Fix address length in bind_wildcard.c. The selftest passes the IPv6 address length for an IPv4 address. We should pass the correct length. Note inet_bind_sk() does not check if the size is larger than sizeof(struct sockaddr_in), so there is no real bug in this selftest. Fixes: 13715acf8ab5 ("selftest: Add test for bind() conflicts.") Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- tools/testing/selftests/net/bind_wildcard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/net/bind_wildcard.c b/tools/testing/selftests/net/bind_wildcard.c index 58edfc15d28b..e7ebe72e879d 100644 --- a/tools/testing/selftests/net/bind_wildcard.c +++ b/tools/testing/selftests/net/bind_wildcard.c @@ -100,7 +100,7 @@ void bind_sockets(struct __test_metadata *_metadata, TEST_F(bind_wildcard, v4_v6) { bind_sockets(_metadata, self, - (struct sockaddr *)&self->addr4, sizeof(self->addr6), + (struct sockaddr *)&self->addr4, sizeof(self->addr4), (struct sockaddr *)&self->addr6, sizeof(self->addr6)); } -- cgit v1.2.3 From 2895d879dd41a588d80acde1aa832deb38d67823 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 11:36:59 -0700 Subject: selftest: tcp: Move expected_errno into each test case in bind_wildcard.c. This is a preparation patch for the following patch. Let's define expected_errno in each test case so that we can add other test cases easily. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- tools/testing/selftests/net/bind_wildcard.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/testing/selftests/net/bind_wildcard.c b/tools/testing/selftests/net/bind_wildcard.c index e7ebe72e879d..81f694536099 100644 --- a/tools/testing/selftests/net/bind_wildcard.c +++ b/tools/testing/selftests/net/bind_wildcard.c @@ -10,37 +10,41 @@ FIXTURE(bind_wildcard) { struct sockaddr_in addr4; struct sockaddr_in6 addr6; - int expected_errno; }; FIXTURE_VARIANT(bind_wildcard) { const __u32 addr4_const; const struct in6_addr *addr6_const; + int expected_errno; }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_any) { .addr4_const = INADDR_ANY, .addr6_const = &in6addr_any, + .expected_errno = EADDRINUSE, }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local) { .addr4_const = INADDR_ANY, .addr6_const = &in6addr_loopback, + .expected_errno = 0, }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_any) { .addr4_const = INADDR_LOOPBACK, .addr6_const = &in6addr_any, + .expected_errno = EADDRINUSE, }; FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local) { .addr4_const = INADDR_LOOPBACK, .addr6_const = &in6addr_loopback, + .expected_errno = 0, }; FIXTURE_SETUP(bind_wildcard) @@ -52,11 +56,6 @@ FIXTURE_SETUP(bind_wildcard) self->addr6.sin6_family = AF_INET6; self->addr6.sin6_port = htons(0); self->addr6.sin6_addr = *variant->addr6_const; - - if (variant->addr6_const == &in6addr_any) - self->expected_errno = EADDRINUSE; - else - self->expected_errno = 0; } FIXTURE_TEARDOWN(bind_wildcard) @@ -65,6 +64,7 @@ FIXTURE_TEARDOWN(bind_wildcard) void bind_sockets(struct __test_metadata *_metadata, FIXTURE_DATA(bind_wildcard) *self, + int expected_errno, struct sockaddr *addr1, socklen_t addrlen1, struct sockaddr *addr2, socklen_t addrlen2) { @@ -86,9 +86,9 @@ void bind_sockets(struct __test_metadata *_metadata, ASSERT_GT(fd[1], 0); ret = bind(fd[1], addr2, addrlen2); - if (self->expected_errno) { + if (expected_errno) { ASSERT_EQ(ret, -1); - ASSERT_EQ(errno, self->expected_errno); + ASSERT_EQ(errno, expected_errno); } else { ASSERT_EQ(ret, 0); } @@ -99,14 +99,14 @@ void bind_sockets(struct __test_metadata *_metadata, TEST_F(bind_wildcard, v4_v6) { - bind_sockets(_metadata, self, + bind_sockets(_metadata, self, variant->expected_errno, (struct sockaddr *)&self->addr4, sizeof(self->addr4), (struct sockaddr *)&self->addr6, sizeof(self->addr6)); } TEST_F(bind_wildcard, v6_v4) { - bind_sockets(_metadata, self, + bind_sockets(_metadata, self, variant->expected_errno, (struct sockaddr *)&self->addr6, sizeof(self->addr6), (struct sockaddr *)&self->addr4, sizeof(self->addr4)); } -- cgit v1.2.3 From 8637d8e8b653f4c8b6fd277b434b118f844d1d77 Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 11:37:00 -0700 Subject: selftest: tcp: Add v4-mapped-v6 cases in bind_wildcard.c. We add these 8 test cases in bind_wildcard.c to check bind() conflicts. 1st bind() 2nd bind() --------- --------- 0.0.0.0 ::FFFF:0.0.0.0 ::FFFF:0.0.0.0 0.0.0.0 0.0.0.0 ::FFFF:127.0.0.1 ::FFFF:127.0.0.1 0.0.0.0 127.0.0.1 ::FFFF:0.0.0.0 ::FFFF:0.0.0.0 127.0.0.1 127.0.0.1 ::FFFF:127.0.0.1 ::FFFF:127.0.0.1 127.0.0.1 All test passed without bhash2 and with bhash2 and this series. Before bhash2: $ uname -r 6.0.0-rc1-00393-g0bf73255d3a3 $ ./bind_wildcard ... # PASSED: 16 / 16 tests passed. Just after bhash2: $ uname -r 6.0.0-rc1-00394-g28044fc1d495 $ ./bind_wildcard ... ok 15 bind_wildcard.v4_local_v6_v4mapped_local.v4_v6 not ok 16 bind_wildcard.v4_local_v6_v4mapped_local.v6_v4 # FAILED: 15 / 16 tests passed. On net.git: $ ./bind_wildcard ... not ok 14 bind_wildcard.v4_local_v6_v4mapped_any.v6_v4 not ok 16 bind_wildcard.v4_local_v6_v4mapped_local.v6_v4 # FAILED: 13 / 16 tests passed. With this series: $ ./bind_wildcard ... # PASSED: 16 / 16 tests passed. Signed-off-by: Kuniyuki Iwashima Signed-off-by: David S. Miller --- tools/testing/selftests/net/bind_wildcard.c | 46 +++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tools/testing/selftests/net/bind_wildcard.c b/tools/testing/selftests/net/bind_wildcard.c index 81f694536099..a2662348cdb1 100644 --- a/tools/testing/selftests/net/bind_wildcard.c +++ b/tools/testing/selftests/net/bind_wildcard.c @@ -6,6 +6,24 @@ #include "../kselftest_harness.h" +struct in6_addr in6addr_v4mapped_any = { + .s6_addr = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 255, 255, + 0, 0, 0, 0 + } +}; + +struct in6_addr in6addr_v4mapped_loopback = { + .s6_addr = { + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 255, 255, + 127, 0, 0, 1 + } +}; + FIXTURE(bind_wildcard) { struct sockaddr_in addr4; @@ -33,6 +51,20 @@ FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_local) .expected_errno = 0, }; +FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_v4mapped_any) +{ + .addr4_const = INADDR_ANY, + .addr6_const = &in6addr_v4mapped_any, + .expected_errno = EADDRINUSE, +}; + +FIXTURE_VARIANT_ADD(bind_wildcard, v4_any_v6_v4mapped_local) +{ + .addr4_const = INADDR_ANY, + .addr6_const = &in6addr_v4mapped_loopback, + .expected_errno = EADDRINUSE, +}; + FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_any) { .addr4_const = INADDR_LOOPBACK, @@ -47,6 +79,20 @@ FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_local) .expected_errno = 0, }; +FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_v4mapped_any) +{ + .addr4_const = INADDR_LOOPBACK, + .addr6_const = &in6addr_v4mapped_any, + .expected_errno = EADDRINUSE, +}; + +FIXTURE_VARIANT_ADD(bind_wildcard, v4_local_v6_v4mapped_local) +{ + .addr4_const = INADDR_LOOPBACK, + .addr6_const = &in6addr_v4mapped_loopback, + .expected_errno = EADDRINUSE, +}; + FIXTURE_SETUP(bind_wildcard) { self->addr4.sin_family = AF_INET; -- cgit v1.2.3 From 139e08188babf7a4c5f0df54b605105852fc347a Mon Sep 17 00:00:00 2001 From: Palmer Dabbelt Date: Tue, 12 Sep 2023 11:06:56 -0700 Subject: Documentation: embargoed-hardware-issues.rst: Add myself for RISC-V I'm not sure exactly how RISC-V fits into the story here, but I'm happy to voluteer a sort of catch-all for vendors who aren't otherwise represented. Signed-off-by: Palmer Dabbelt Link: https://lore.kernel.org/r/20230912180657.31841-1-palmer@rivosinc.com Signed-off-by: Greg Kroah-Hartman --- Documentation/process/embargoed-hardware-issues.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/process/embargoed-hardware-issues.rst b/Documentation/process/embargoed-hardware-issues.rst index cb686238f21d..ac7c52f130c9 100644 --- a/Documentation/process/embargoed-hardware-issues.rst +++ b/Documentation/process/embargoed-hardware-issues.rst @@ -251,6 +251,7 @@ an involved disclosed party. The current ambassadors list: IBM Z Christian Borntraeger Intel Tony Luck Qualcomm Trilok Soni + RISC-V Palmer Dabbelt Samsung Javier González Microsoft James Morris -- cgit v1.2.3 From 3a7d263aea9d505e6272a913d6cfece00b800b4d Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 5 Sep 2023 21:42:52 +0200 Subject: w1: ds2482: Switch back to use struct i2c_driver's .probe() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After commit b8a1a4cd5a98 ("i2c: Provide a temporary .probe_new() call-back type"), all drivers being converted to .probe_new() and then commit 03c835f498b5 ("i2c: Switch .probe() to not take an id parameter") convert back to (the new) .probe() to be able to eventually drop .probe_new() from struct i2c_driver. Reviewed-by: Krzysztof Kozlowski Link: https://lore.kernel.org/lkml/20230612072807.839689-1-u.kleine-koenig@pengutronix.de/ Signed-off-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- drivers/w1/masters/ds2482.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index c1de8a92e144..b2d76c1784bd 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -551,7 +551,7 @@ static struct i2c_driver ds2482_driver = { .driver = { .name = "ds2482", }, - .probe_new = ds2482_probe, + .probe = ds2482_probe, .remove = ds2482_remove, .id_table = ds2482_id, }; -- cgit v1.2.3 From 5eb1e6e459cfa025f79c43014f66ff62a55542f1 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 5 Sep 2023 21:42:53 +0200 Subject: i2c: Drop legacy callback .probe_new() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all drivers are converted to the (new) .probe() callback, the temporary .probe_new() can go away. \o/ Link: https://lore.kernel.org/linux-i2c/20230626094548.559542-1-u.kleine-koenig@pengutronix.de Reviewed-by: Javier Martinez Canillas Reviewed-by: Jean Delvare Signed-off-by: Uwe Kleine-König Signed-off-by: Wolfram Sang --- include/linux/i2c.h | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/include/linux/i2c.h b/include/linux/i2c.h index 3430cc2b05a6..0dae9db27538 100644 --- a/include/linux/i2c.h +++ b/include/linux/i2c.h @@ -237,7 +237,6 @@ enum i2c_driver_flags { * struct i2c_driver - represent an I2C device driver * @class: What kind of i2c device we instantiate (for detect) * @probe: Callback for device binding - * @probe_new: Transitional callback for device binding - do not use * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown * @alert: Alert callback, for example for the SMBus alert protocol @@ -272,16 +271,8 @@ enum i2c_driver_flags { struct i2c_driver { unsigned int class; - union { /* Standard driver model interfaces */ - int (*probe)(struct i2c_client *client); - /* - * Legacy callback that was part of a conversion of .probe(). - * Today it has the same semantic as .probe(). Don't use for new - * code. - */ - int (*probe_new)(struct i2c_client *client); - }; + int (*probe)(struct i2c_client *client); void (*remove)(struct i2c_client *client); -- cgit v1.2.3 From 24dc13f94367edb314b13923818d98dd565edc44 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 15 Aug 2023 17:29:11 +0200 Subject: i2c: Make I2C_ATR invisible I2C Address Translator (ATR) support is not a stand-alone driver, but a library. All of its users select I2C_ATR. Hence there is no need for the user to enable this symbol manually, except when compile-testing. Signed-off-by: Geert Uytterhoeven Reviewed-by: Luca Ceresoli Reviewed-by: Tomi Valkeinen Signed-off-by: Wolfram Sang --- drivers/i2c/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index c6d1a345ea6d..9388823bb0bb 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -72,7 +72,7 @@ config I2C_MUX source "drivers/i2c/muxes/Kconfig" config I2C_ATR - tristate "I2C Address Translator (ATR) support" + tristate "I2C Address Translator (ATR) support" if COMPILE_TEST help Enable support for I2C Address Translator (ATR) chips. -- cgit v1.2.3 From b2cacc2e818717545e6d0cc453b72f98249398bf Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 4 Sep 2023 14:00:36 +0200 Subject: i2c: I2C_MLXCPLD on ARM64 should depend on ACPI The "i2c_mlxcpld" platform device is only instantiated on X86 systems (through drivers/platform/x86/mlx-platform.c), or on ARM64 systems with ACPI (through drivers/platform/mellanox/nvsw-sn2201.c). Hence further restrict the dependency on ARM64 to ACPI, to prevent asking the user about this driver when configuring an ARM64 kernel without ACPI support. While at it, document in the Kconfig help text that the driver supports ARM64/ACPI based systems, too. Signed-off-by: Geert Uytterhoeven Acked-by: Vadim Pasternak Acked-by: Andi Shyti Signed-off-by: Wolfram Sang --- drivers/i2c/busses/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 169607e80331..6644eebedaf3 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -1384,10 +1384,10 @@ config I2C_ICY config I2C_MLXCPLD tristate "Mellanox I2C driver" - depends on X86_64 || ARM64 || COMPILE_TEST + depends on X86_64 || (ARM64 && ACPI) || COMPILE_TEST help This exposes the Mellanox platform I2C busses to the linux I2C layer - for X86 based systems. + for X86 and ARM64/ACPI based systems. Controller is implemented as CPLD logic. This driver can also be built as a module. If so, the module will be -- cgit v1.2.3 From fee465150b458351b6d9b9f66084f3cc3022b88b Mon Sep 17 00:00:00 2001 From: Tommy Huang Date: Wed, 6 Sep 2023 08:49:10 +0800 Subject: i2c: aspeed: Reset the i2c controller when timeout occurs Reset the i2c controller when an i2c transfer timeout occurs. The remaining interrupts and device should be reset to avoid unpredictable controller behavior. Fixes: 2e57b7cebb98 ("i2c: aspeed: Add multi-master use case support") Cc: # v5.1+ Signed-off-by: Tommy Huang Reviewed-by: Andi Shyti Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-aspeed.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c index 2e5acfeb76c8..5a416b39b818 100644 --- a/drivers/i2c/busses/i2c-aspeed.c +++ b/drivers/i2c/busses/i2c-aspeed.c @@ -698,13 +698,16 @@ static int aspeed_i2c_master_xfer(struct i2c_adapter *adap, if (time_left == 0) { /* - * If timed out and bus is still busy in a multi master - * environment, attempt recovery at here. + * In a multi-master setup, if a timeout occurs, attempt + * recovery. But if the bus is idle, we still need to reset the + * i2c controller to clear the remaining interrupts. */ if (bus->multi_master && (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS)) aspeed_i2c_recover_bus(bus); + else + aspeed_i2c_reset(bus); /* * If timed out and the state is still pending, drop the pending -- cgit v1.2.3 From e2ad626f8f409899baf1bf192d0533a851128b19 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Wed, 13 Sep 2023 00:11:27 +0200 Subject: pmdomain: Rename the genpd subsystem to pmdomain It has been pointed out that naming a subsystem "genpd" isn't very self-explanatory and the acronym itself that means Generic PM Domain, is known only by a limited group of people. In a way to improve the situation, let's rename the subsystem to pmdomain, which ideally should indicate that this is about so called Power Domains or "PM domains" as we often also use within the Linux Kernel terminology. Suggested-by: Rafael J. Wysocki Signed-off-by: Ulf Hansson Reviewed-by: Linus Walleij Acked-by: Arnd Bergmann Acked-by: Heiko Stuebner Acked-by: Rafael J. Wysocki Acked-by: Geert Uytterhoeven Link: https://lore.kernel.org/r/20230912221127.487327-1-ulf.hansson@linaro.org --- MAINTAINERS | 22 +- drivers/Makefile | 2 +- drivers/genpd/Makefile | 17 - drivers/genpd/actions/Makefile | 3 - drivers/genpd/actions/owl-sps-helper.c | 48 - drivers/genpd/actions/owl-sps.c | 320 ----- drivers/genpd/amlogic/Makefile | 4 - drivers/genpd/amlogic/meson-ee-pwrc.c | 635 --------- drivers/genpd/amlogic/meson-gx-pwrc-vpu.c | 379 ------ drivers/genpd/amlogic/meson-secure-pwrc.c | 257 ---- drivers/genpd/apple/Makefile | 2 - drivers/genpd/apple/pmgr-pwrstate.c | 326 ----- drivers/genpd/bcm/Makefile | 5 - drivers/genpd/bcm/bcm-pmb.c | 363 ----- drivers/genpd/bcm/bcm2835-power.c | 713 ---------- drivers/genpd/bcm/bcm63xx-power.c | 375 ------ drivers/genpd/bcm/raspberrypi-power.c | 245 ---- drivers/genpd/imx/Makefile | 8 - drivers/genpd/imx/gpc.c | 554 -------- drivers/genpd/imx/gpcv2.c | 1550 ---------------------- drivers/genpd/imx/imx8m-blk-ctrl.c | 899 ------------- drivers/genpd/imx/imx8mp-blk-ctrl.c | 867 ------------ drivers/genpd/imx/imx93-blk-ctrl.c | 451 ------- drivers/genpd/imx/imx93-pd.c | 176 --- drivers/genpd/imx/scu-pd.c | 550 -------- drivers/genpd/mediatek/Makefile | 3 - drivers/genpd/mediatek/mt6795-pm-domains.h | 112 -- drivers/genpd/mediatek/mt8167-pm-domains.h | 105 -- drivers/genpd/mediatek/mt8173-pm-domains.h | 123 -- drivers/genpd/mediatek/mt8183-pm-domains.h | 266 ---- drivers/genpd/mediatek/mt8186-pm-domains.h | 342 ----- drivers/genpd/mediatek/mt8188-pm-domains.h | 623 --------- drivers/genpd/mediatek/mt8192-pm-domains.h | 355 ----- drivers/genpd/mediatek/mt8195-pm-domains.h | 613 --------- drivers/genpd/mediatek/mtk-pm-domains.c | 688 ---------- drivers/genpd/mediatek/mtk-pm-domains.h | 111 -- drivers/genpd/mediatek/mtk-scpsys.c | 1147 ---------------- drivers/genpd/qcom/Makefile | 4 - drivers/genpd/qcom/cpr.c | 1756 ------------------------- drivers/genpd/qcom/rpmhpd.c | 886 ------------- drivers/genpd/qcom/rpmpd.c | 1023 -------------- drivers/genpd/renesas/Makefile | 30 - drivers/genpd/renesas/r8a7742-sysc.c | 42 - drivers/genpd/renesas/r8a7743-sysc.c | 28 - drivers/genpd/renesas/r8a7745-sysc.c | 28 - drivers/genpd/renesas/r8a77470-sysc.c | 28 - drivers/genpd/renesas/r8a774a1-sysc.c | 44 - drivers/genpd/renesas/r8a774b1-sysc.c | 37 - drivers/genpd/renesas/r8a774c0-sysc.c | 55 - drivers/genpd/renesas/r8a774e1-sysc.c | 43 - drivers/genpd/renesas/r8a7779-sysc.c | 30 - drivers/genpd/renesas/r8a7790-sysc.c | 44 - drivers/genpd/renesas/r8a7791-sysc.c | 29 - drivers/genpd/renesas/r8a7792-sysc.c | 30 - drivers/genpd/renesas/r8a7794-sysc.c | 29 - drivers/genpd/renesas/r8a7795-sysc.c | 86 -- drivers/genpd/renesas/r8a7796-sysc.c | 67 - drivers/genpd/renesas/r8a77965-sysc.c | 38 - drivers/genpd/renesas/r8a77970-sysc.c | 37 - drivers/genpd/renesas/r8a77980-sysc.c | 54 - drivers/genpd/renesas/r8a77990-sysc.c | 55 - drivers/genpd/renesas/r8a77995-sysc.c | 26 - drivers/genpd/renesas/r8a779a0-sysc.c | 76 -- drivers/genpd/renesas/r8a779f0-sysc.c | 47 - drivers/genpd/renesas/r8a779g0-sysc.c | 63 - drivers/genpd/renesas/rcar-gen4-sysc.c | 379 ------ drivers/genpd/renesas/rcar-gen4-sysc.h | 44 - drivers/genpd/renesas/rcar-sysc.c | 494 ------- drivers/genpd/renesas/rcar-sysc.h | 82 -- drivers/genpd/renesas/rmobile-sysc.c | 343 ----- drivers/genpd/rockchip/Makefile | 2 - drivers/genpd/rockchip/pm-domains.c | 1396 -------------------- drivers/genpd/samsung/Makefile | 2 - drivers/genpd/samsung/exynos-pm-domains.c | 167 --- drivers/genpd/st/Makefile | 2 - drivers/genpd/st/ste-ux500-pm-domain.c | 94 -- drivers/genpd/starfive/Makefile | 2 - drivers/genpd/starfive/jh71xx-pmu.c | 383 ------ drivers/genpd/sunxi/Makefile | 2 - drivers/genpd/sunxi/sun20i-ppu.c | 207 --- drivers/genpd/tegra/Makefile | 2 - drivers/genpd/tegra/powergate-bpmp.c | 361 ----- drivers/genpd/ti/Makefile | 3 - drivers/genpd/ti/omap_prm.c | 989 -------------- drivers/genpd/ti/ti_sci_pm_domains.c | 204 --- drivers/genpd/xilinx/Makefile | 2 - drivers/genpd/xilinx/zynqmp-pm-domains.c | 322 ----- drivers/pmdomain/Makefile | 17 + drivers/pmdomain/actions/Makefile | 3 + drivers/pmdomain/actions/owl-sps-helper.c | 48 + drivers/pmdomain/actions/owl-sps.c | 320 +++++ drivers/pmdomain/amlogic/Makefile | 4 + drivers/pmdomain/amlogic/meson-ee-pwrc.c | 635 +++++++++ drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c | 379 ++++++ drivers/pmdomain/amlogic/meson-secure-pwrc.c | 257 ++++ drivers/pmdomain/apple/Makefile | 2 + drivers/pmdomain/apple/pmgr-pwrstate.c | 326 +++++ drivers/pmdomain/bcm/Makefile | 5 + drivers/pmdomain/bcm/bcm-pmb.c | 363 +++++ drivers/pmdomain/bcm/bcm2835-power.c | 713 ++++++++++ drivers/pmdomain/bcm/bcm63xx-power.c | 375 ++++++ drivers/pmdomain/bcm/raspberrypi-power.c | 245 ++++ drivers/pmdomain/imx/Makefile | 8 + drivers/pmdomain/imx/gpc.c | 554 ++++++++ drivers/pmdomain/imx/gpcv2.c | 1550 ++++++++++++++++++++++ drivers/pmdomain/imx/imx8m-blk-ctrl.c | 899 +++++++++++++ drivers/pmdomain/imx/imx8mp-blk-ctrl.c | 867 ++++++++++++ drivers/pmdomain/imx/imx93-blk-ctrl.c | 451 +++++++ drivers/pmdomain/imx/imx93-pd.c | 176 +++ drivers/pmdomain/imx/scu-pd.c | 550 ++++++++ drivers/pmdomain/mediatek/Makefile | 3 + drivers/pmdomain/mediatek/mt6795-pm-domains.h | 112 ++ drivers/pmdomain/mediatek/mt8167-pm-domains.h | 105 ++ drivers/pmdomain/mediatek/mt8173-pm-domains.h | 123 ++ drivers/pmdomain/mediatek/mt8183-pm-domains.h | 266 ++++ drivers/pmdomain/mediatek/mt8186-pm-domains.h | 342 +++++ drivers/pmdomain/mediatek/mt8188-pm-domains.h | 623 +++++++++ drivers/pmdomain/mediatek/mt8192-pm-domains.h | 355 +++++ drivers/pmdomain/mediatek/mt8195-pm-domains.h | 613 +++++++++ drivers/pmdomain/mediatek/mtk-pm-domains.c | 688 ++++++++++ drivers/pmdomain/mediatek/mtk-pm-domains.h | 111 ++ drivers/pmdomain/mediatek/mtk-scpsys.c | 1147 ++++++++++++++++ drivers/pmdomain/qcom/Makefile | 4 + drivers/pmdomain/qcom/cpr.c | 1756 +++++++++++++++++++++++++ drivers/pmdomain/qcom/rpmhpd.c | 886 +++++++++++++ drivers/pmdomain/qcom/rpmpd.c | 1023 ++++++++++++++ drivers/pmdomain/renesas/Makefile | 30 + drivers/pmdomain/renesas/r8a7742-sysc.c | 42 + drivers/pmdomain/renesas/r8a7743-sysc.c | 28 + drivers/pmdomain/renesas/r8a7745-sysc.c | 28 + drivers/pmdomain/renesas/r8a77470-sysc.c | 28 + drivers/pmdomain/renesas/r8a774a1-sysc.c | 44 + drivers/pmdomain/renesas/r8a774b1-sysc.c | 37 + drivers/pmdomain/renesas/r8a774c0-sysc.c | 55 + drivers/pmdomain/renesas/r8a774e1-sysc.c | 43 + drivers/pmdomain/renesas/r8a7779-sysc.c | 30 + drivers/pmdomain/renesas/r8a7790-sysc.c | 44 + drivers/pmdomain/renesas/r8a7791-sysc.c | 29 + drivers/pmdomain/renesas/r8a7792-sysc.c | 30 + drivers/pmdomain/renesas/r8a7794-sysc.c | 29 + drivers/pmdomain/renesas/r8a7795-sysc.c | 86 ++ drivers/pmdomain/renesas/r8a7796-sysc.c | 67 + drivers/pmdomain/renesas/r8a77965-sysc.c | 38 + drivers/pmdomain/renesas/r8a77970-sysc.c | 37 + drivers/pmdomain/renesas/r8a77980-sysc.c | 54 + drivers/pmdomain/renesas/r8a77990-sysc.c | 55 + drivers/pmdomain/renesas/r8a77995-sysc.c | 26 + drivers/pmdomain/renesas/r8a779a0-sysc.c | 76 ++ drivers/pmdomain/renesas/r8a779f0-sysc.c | 47 + drivers/pmdomain/renesas/r8a779g0-sysc.c | 63 + drivers/pmdomain/renesas/rcar-gen4-sysc.c | 379 ++++++ drivers/pmdomain/renesas/rcar-gen4-sysc.h | 44 + drivers/pmdomain/renesas/rcar-sysc.c | 494 +++++++ drivers/pmdomain/renesas/rcar-sysc.h | 82 ++ drivers/pmdomain/renesas/rmobile-sysc.c | 343 +++++ drivers/pmdomain/rockchip/Makefile | 2 + drivers/pmdomain/rockchip/pm-domains.c | 1396 ++++++++++++++++++++ drivers/pmdomain/samsung/Makefile | 2 + drivers/pmdomain/samsung/exynos-pm-domains.c | 167 +++ drivers/pmdomain/st/Makefile | 2 + drivers/pmdomain/st/ste-ux500-pm-domain.c | 94 ++ drivers/pmdomain/starfive/Makefile | 2 + drivers/pmdomain/starfive/jh71xx-pmu.c | 383 ++++++ drivers/pmdomain/sunxi/Makefile | 2 + drivers/pmdomain/sunxi/sun20i-ppu.c | 207 +++ drivers/pmdomain/tegra/Makefile | 2 + drivers/pmdomain/tegra/powergate-bpmp.c | 361 +++++ drivers/pmdomain/ti/Makefile | 3 + drivers/pmdomain/ti/omap_prm.c | 989 ++++++++++++++ drivers/pmdomain/ti/ti_sci_pm_domains.c | 204 +++ drivers/pmdomain/xilinx/Makefile | 2 + drivers/pmdomain/xilinx/zynqmp-pm-domains.c | 322 +++++ 172 files changed, 23444 insertions(+), 23444 deletions(-) delete mode 100644 drivers/genpd/Makefile delete mode 100644 drivers/genpd/actions/Makefile delete mode 100644 drivers/genpd/actions/owl-sps-helper.c delete mode 100644 drivers/genpd/actions/owl-sps.c delete mode 100644 drivers/genpd/amlogic/Makefile delete mode 100644 drivers/genpd/amlogic/meson-ee-pwrc.c delete mode 100644 drivers/genpd/amlogic/meson-gx-pwrc-vpu.c delete mode 100644 drivers/genpd/amlogic/meson-secure-pwrc.c delete mode 100644 drivers/genpd/apple/Makefile delete mode 100644 drivers/genpd/apple/pmgr-pwrstate.c delete mode 100644 drivers/genpd/bcm/Makefile delete mode 100644 drivers/genpd/bcm/bcm-pmb.c delete mode 100644 drivers/genpd/bcm/bcm2835-power.c delete mode 100644 drivers/genpd/bcm/bcm63xx-power.c delete mode 100644 drivers/genpd/bcm/raspberrypi-power.c delete mode 100644 drivers/genpd/imx/Makefile delete mode 100644 drivers/genpd/imx/gpc.c delete mode 100644 drivers/genpd/imx/gpcv2.c delete mode 100644 drivers/genpd/imx/imx8m-blk-ctrl.c delete mode 100644 drivers/genpd/imx/imx8mp-blk-ctrl.c delete mode 100644 drivers/genpd/imx/imx93-blk-ctrl.c delete mode 100644 drivers/genpd/imx/imx93-pd.c delete mode 100644 drivers/genpd/imx/scu-pd.c delete mode 100644 drivers/genpd/mediatek/Makefile delete mode 100644 drivers/genpd/mediatek/mt6795-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8167-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8173-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8183-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8186-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8188-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8192-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mt8195-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mtk-pm-domains.c delete mode 100644 drivers/genpd/mediatek/mtk-pm-domains.h delete mode 100644 drivers/genpd/mediatek/mtk-scpsys.c delete mode 100644 drivers/genpd/qcom/Makefile delete mode 100644 drivers/genpd/qcom/cpr.c delete mode 100644 drivers/genpd/qcom/rpmhpd.c delete mode 100644 drivers/genpd/qcom/rpmpd.c delete mode 100644 drivers/genpd/renesas/Makefile delete mode 100644 drivers/genpd/renesas/r8a7742-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7743-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7745-sysc.c delete mode 100644 drivers/genpd/renesas/r8a77470-sysc.c delete mode 100644 drivers/genpd/renesas/r8a774a1-sysc.c delete mode 100644 drivers/genpd/renesas/r8a774b1-sysc.c delete mode 100644 drivers/genpd/renesas/r8a774c0-sysc.c delete mode 100644 drivers/genpd/renesas/r8a774e1-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7779-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7790-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7791-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7792-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7794-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7795-sysc.c delete mode 100644 drivers/genpd/renesas/r8a7796-sysc.c delete mode 100644 drivers/genpd/renesas/r8a77965-sysc.c delete mode 100644 drivers/genpd/renesas/r8a77970-sysc.c delete mode 100644 drivers/genpd/renesas/r8a77980-sysc.c delete mode 100644 drivers/genpd/renesas/r8a77990-sysc.c delete mode 100644 drivers/genpd/renesas/r8a77995-sysc.c delete mode 100644 drivers/genpd/renesas/r8a779a0-sysc.c delete mode 100644 drivers/genpd/renesas/r8a779f0-sysc.c delete mode 100644 drivers/genpd/renesas/r8a779g0-sysc.c delete mode 100644 drivers/genpd/renesas/rcar-gen4-sysc.c delete mode 100644 drivers/genpd/renesas/rcar-gen4-sysc.h delete mode 100644 drivers/genpd/renesas/rcar-sysc.c delete mode 100644 drivers/genpd/renesas/rcar-sysc.h delete mode 100644 drivers/genpd/renesas/rmobile-sysc.c delete mode 100644 drivers/genpd/rockchip/Makefile delete mode 100644 drivers/genpd/rockchip/pm-domains.c delete mode 100644 drivers/genpd/samsung/Makefile delete mode 100644 drivers/genpd/samsung/exynos-pm-domains.c delete mode 100644 drivers/genpd/st/Makefile delete mode 100644 drivers/genpd/st/ste-ux500-pm-domain.c delete mode 100644 drivers/genpd/starfive/Makefile delete mode 100644 drivers/genpd/starfive/jh71xx-pmu.c delete mode 100644 drivers/genpd/sunxi/Makefile delete mode 100644 drivers/genpd/sunxi/sun20i-ppu.c delete mode 100644 drivers/genpd/tegra/Makefile delete mode 100644 drivers/genpd/tegra/powergate-bpmp.c delete mode 100644 drivers/genpd/ti/Makefile delete mode 100644 drivers/genpd/ti/omap_prm.c delete mode 100644 drivers/genpd/ti/ti_sci_pm_domains.c delete mode 100644 drivers/genpd/xilinx/Makefile delete mode 100644 drivers/genpd/xilinx/zynqmp-pm-domains.c create mode 100644 drivers/pmdomain/Makefile create mode 100644 drivers/pmdomain/actions/Makefile create mode 100644 drivers/pmdomain/actions/owl-sps-helper.c create mode 100644 drivers/pmdomain/actions/owl-sps.c create mode 100644 drivers/pmdomain/amlogic/Makefile create mode 100644 drivers/pmdomain/amlogic/meson-ee-pwrc.c create mode 100644 drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c create mode 100644 drivers/pmdomain/amlogic/meson-secure-pwrc.c create mode 100644 drivers/pmdomain/apple/Makefile create mode 100644 drivers/pmdomain/apple/pmgr-pwrstate.c create mode 100644 drivers/pmdomain/bcm/Makefile create mode 100644 drivers/pmdomain/bcm/bcm-pmb.c create mode 100644 drivers/pmdomain/bcm/bcm2835-power.c create mode 100644 drivers/pmdomain/bcm/bcm63xx-power.c create mode 100644 drivers/pmdomain/bcm/raspberrypi-power.c create mode 100644 drivers/pmdomain/imx/Makefile create mode 100644 drivers/pmdomain/imx/gpc.c create mode 100644 drivers/pmdomain/imx/gpcv2.c create mode 100644 drivers/pmdomain/imx/imx8m-blk-ctrl.c create mode 100644 drivers/pmdomain/imx/imx8mp-blk-ctrl.c create mode 100644 drivers/pmdomain/imx/imx93-blk-ctrl.c create mode 100644 drivers/pmdomain/imx/imx93-pd.c create mode 100644 drivers/pmdomain/imx/scu-pd.c create mode 100644 drivers/pmdomain/mediatek/Makefile create mode 100644 drivers/pmdomain/mediatek/mt6795-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8167-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8173-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8183-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8186-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8188-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8192-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mt8195-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mtk-pm-domains.c create mode 100644 drivers/pmdomain/mediatek/mtk-pm-domains.h create mode 100644 drivers/pmdomain/mediatek/mtk-scpsys.c create mode 100644 drivers/pmdomain/qcom/Makefile create mode 100644 drivers/pmdomain/qcom/cpr.c create mode 100644 drivers/pmdomain/qcom/rpmhpd.c create mode 100644 drivers/pmdomain/qcom/rpmpd.c create mode 100644 drivers/pmdomain/renesas/Makefile create mode 100644 drivers/pmdomain/renesas/r8a7742-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7743-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7745-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a77470-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a774a1-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a774b1-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a774c0-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a774e1-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7779-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7790-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7791-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7792-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7794-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7795-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a7796-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a77965-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a77970-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a77980-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a77990-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a77995-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a779a0-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a779f0-sysc.c create mode 100644 drivers/pmdomain/renesas/r8a779g0-sysc.c create mode 100644 drivers/pmdomain/renesas/rcar-gen4-sysc.c create mode 100644 drivers/pmdomain/renesas/rcar-gen4-sysc.h create mode 100644 drivers/pmdomain/renesas/rcar-sysc.c create mode 100644 drivers/pmdomain/renesas/rcar-sysc.h create mode 100644 drivers/pmdomain/renesas/rmobile-sysc.c create mode 100644 drivers/pmdomain/rockchip/Makefile create mode 100644 drivers/pmdomain/rockchip/pm-domains.c create mode 100644 drivers/pmdomain/samsung/Makefile create mode 100644 drivers/pmdomain/samsung/exynos-pm-domains.c create mode 100644 drivers/pmdomain/st/Makefile create mode 100644 drivers/pmdomain/st/ste-ux500-pm-domain.c create mode 100644 drivers/pmdomain/starfive/Makefile create mode 100644 drivers/pmdomain/starfive/jh71xx-pmu.c create mode 100644 drivers/pmdomain/sunxi/Makefile create mode 100644 drivers/pmdomain/sunxi/sun20i-ppu.c create mode 100644 drivers/pmdomain/tegra/Makefile create mode 100644 drivers/pmdomain/tegra/powergate-bpmp.c create mode 100644 drivers/pmdomain/ti/Makefile create mode 100644 drivers/pmdomain/ti/omap_prm.c create mode 100644 drivers/pmdomain/ti/ti_sci_pm_domains.c create mode 100644 drivers/pmdomain/xilinx/Makefile create mode 100644 drivers/pmdomain/xilinx/zynqmp-pm-domains.c diff --git a/MAINTAINERS b/MAINTAINERS index 90f13281d297..4d9e7d42412f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1855,7 +1855,7 @@ F: Documentation/devicetree/bindings/phy/amlogic* F: arch/arm/boot/dts/amlogic/ F: arch/arm/mach-meson/ F: arch/arm64/boot/dts/amlogic/ -F: drivers/genpd/amlogic/ +F: drivers/pmdomain/amlogic/ F: drivers/mmc/host/meson* F: drivers/phy/amlogic/ F: drivers/pinctrl/meson/ @@ -1918,7 +1918,7 @@ F: drivers/bluetooth/hci_bcm4377.c F: drivers/clk/clk-apple-nco.c F: drivers/cpufreq/apple-soc-cpufreq.c F: drivers/dma/apple-admac.c -F: drivers/genpd/apple/ +F: drivers/pmdomain/apple/ F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/iommu/apple-dart.c @@ -2435,7 +2435,7 @@ F: arch/arm/mach-ux500/ F: drivers/clk/clk-nomadik.c F: drivers/clocksource/clksrc-dbx500-prcmu.c F: drivers/dma/ste_dma40* -F: drivers/genpd/st/ste-ux500-pm-domain.c +F: drivers/pmdomain/st/ste-ux500-pm-domain.c F: drivers/hwspinlock/u8500_hsem.c F: drivers/i2c/busses/i2c-nomadik.c F: drivers/iio/adc/ab8500-gpadc.c @@ -2598,7 +2598,7 @@ F: arch/arm/include/debug/renesas-scif.S F: arch/arm/mach-shmobile/ F: arch/arm64/boot/dts/renesas/ F: arch/riscv/boot/dts/renesas/ -F: drivers/genpd/renesas/ +F: drivers/pmdomain/renesas/ F: drivers/soc/renesas/ F: include/linux/soc/renesas/ K: \brenesas, @@ -4026,7 +4026,7 @@ F: arch/mips/kernel/*bmips* F: drivers/irqchip/irq-bcm63* F: drivers/irqchip/irq-bcm7* F: drivers/irqchip/irq-brcmstb* -F: drivers/genpd/bcm/bcm63xx-power.c +F: drivers/pmdomain/bcm/bcm63xx-power.c F: include/linux/bcm963xx_nvram.h F: include/linux/bcm963xx_tag.h @@ -4248,7 +4248,7 @@ R: Broadcom internal kernel review list L: linux-pm@vger.kernel.org S: Maintained T: git https://github.com/broadcom/stblinux.git -F: drivers/genpd/bcm/bcm-pmb.c +F: drivers/pmdomain/bcm/bcm-pmb.c F: include/dt-bindings/soc/bcm-pmb.h BROADCOM SPECIFIC AMBA DRIVER (BCMA) @@ -8729,7 +8729,7 @@ M: Ulf Hansson L: linux-pm@vger.kernel.org S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/ulfh/linux-pm.git -F: drivers/genpd/ +F: drivers/pmdomain/ GENERIC RESISTIVE TOUCHSCREEN ADC DRIVER M: Eugen Hristev @@ -17680,7 +17680,7 @@ L: linux-pm@vger.kernel.org L: linux-arm-msm@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/power/avs/qcom,cpr.yaml -F: drivers/genpd/qcom/cpr.c +F: drivers/pmdomain/qcom/cpr.c QUALCOMM CPUFREQ DRIVER MSM8996/APQ8096 M: Ilia Lin @@ -20514,7 +20514,7 @@ STARFIVE JH71XX PMU CONTROLLER DRIVER M: Walker Chen S: Supported F: Documentation/devicetree/bindings/power/starfive* -F: drivers/genpd/starfive/jh71xx-pmu.c +F: drivers/pmdomain/starfive/jh71xx-pmu.c F: include/dt-bindings/power/starfive,jh7110-pmu.h STARFIVE SOC DRIVERS @@ -21339,7 +21339,7 @@ F: drivers/irqchip/irq-ti-sci-inta.c F: drivers/irqchip/irq-ti-sci-intr.c F: drivers/reset/reset-ti-sci.c F: drivers/soc/ti/ti_sci_inta_msi.c -F: drivers/genpd/ti/ti_sci_pm_domains.c +F: drivers/pmdomain/ti/ti_sci_pm_domains.c F: include/dt-bindings/soc/ti,sci_pm_domain.h F: include/linux/soc/ti/ti_sci_inta_msi.h F: include/linux/soc/ti/ti_sci_protocol.h @@ -21581,7 +21581,7 @@ L: linux-kernel@vger.kernel.org L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/ti/linux.git -F: drivers/genpd/ti/omap_prm.c +F: drivers/pmdomain/ti/omap_prm.c F: drivers/soc/ti/* TI LM49xxx FAMILY ASoC CODEC DRIVERS diff --git a/drivers/Makefile b/drivers/Makefile index cb0afca2e4a0..1bec7819a837 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -46,7 +46,7 @@ obj-$(CONFIG_DMADEVICES) += dma/ # SOC specific infrastructure drivers. obj-y += soc/ -obj-$(CONFIG_PM_GENERIC_DOMAINS) += genpd/ +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pmdomain/ obj-y += virtio/ obj-$(CONFIG_VDPA) += vdpa/ diff --git a/drivers/genpd/Makefile b/drivers/genpd/Makefile deleted file mode 100644 index 666753676e5c..000000000000 --- a/drivers/genpd/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-y += actions/ -obj-y += amlogic/ -obj-y += apple/ -obj-y += bcm/ -obj-y += imx/ -obj-y += mediatek/ -obj-y += qcom/ -obj-y += renesas/ -obj-y += rockchip/ -obj-y += samsung/ -obj-y += st/ -obj-y += starfive/ -obj-y += sunxi/ -obj-y += tegra/ -obj-y += ti/ -obj-y += xilinx/ diff --git a/drivers/genpd/actions/Makefile b/drivers/genpd/actions/Makefile deleted file mode 100644 index 7e8aa473d12d..000000000000 --- a/drivers/genpd/actions/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0+ -obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o -obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o diff --git a/drivers/genpd/actions/owl-sps-helper.c b/drivers/genpd/actions/owl-sps-helper.c deleted file mode 100644 index e3f36603dd53..000000000000 --- a/drivers/genpd/actions/owl-sps-helper.c +++ /dev/null @@ -1,48 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Actions Semi Owl Smart Power System (SPS) shared helpers - * - * Copyright 2012 Actions Semi Inc. - * Author: Actions Semi, Inc. - * - * Copyright (c) 2017 Andreas Färber - */ - -#include -#include -#include - -#define OWL_SPS_PG_CTL 0x0 - -int owl_sps_set_pg(void __iomem *base, u32 pwr_mask, u32 ack_mask, bool enable) -{ - u32 val; - bool ack; - int timeout; - - val = readl(base + OWL_SPS_PG_CTL); - ack = val & ack_mask; - if (ack == enable) - return 0; - - if (enable) - val |= pwr_mask; - else - val &= ~pwr_mask; - - writel(val, base + OWL_SPS_PG_CTL); - - for (timeout = 5000; timeout > 0; timeout -= 50) { - val = readl(base + OWL_SPS_PG_CTL); - if ((val & ack_mask) == (enable ? ack_mask : 0)) - break; - udelay(50); - } - if (timeout <= 0) - return -ETIMEDOUT; - - udelay(10); - - return 0; -} -EXPORT_SYMBOL_GPL(owl_sps_set_pg); diff --git a/drivers/genpd/actions/owl-sps.c b/drivers/genpd/actions/owl-sps.c deleted file mode 100644 index 73a9e0bb7e8e..000000000000 --- a/drivers/genpd/actions/owl-sps.c +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Actions Semi Owl Smart Power System (SPS) - * - * Copyright 2012 Actions Semi Inc. - * Author: Actions Semi, Inc. - * - * Copyright (c) 2017 Andreas Färber - */ - -#include -#include -#include -#include -#include -#include -#include - -struct owl_sps_domain_info { - const char *name; - int pwr_bit; - int ack_bit; - unsigned int genpd_flags; -}; - -struct owl_sps_info { - unsigned num_domains; - const struct owl_sps_domain_info *domains; -}; - -struct owl_sps { - struct device *dev; - const struct owl_sps_info *info; - void __iomem *base; - struct genpd_onecell_data genpd_data; - struct generic_pm_domain *domains[]; -}; - -#define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) - -struct owl_sps_domain { - struct generic_pm_domain genpd; - const struct owl_sps_domain_info *info; - struct owl_sps *sps; -}; - -static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) -{ - u32 pwr_mask, ack_mask; - - ack_mask = BIT(pd->info->ack_bit); - pwr_mask = BIT(pd->info->pwr_bit); - - return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable); -} - -static int owl_sps_power_on(struct generic_pm_domain *domain) -{ - struct owl_sps_domain *pd = to_owl_pd(domain); - - dev_dbg(pd->sps->dev, "%s power on", pd->info->name); - - return owl_sps_set_power(pd, true); -} - -static int owl_sps_power_off(struct generic_pm_domain *domain) -{ - struct owl_sps_domain *pd = to_owl_pd(domain); - - dev_dbg(pd->sps->dev, "%s power off", pd->info->name); - - return owl_sps_set_power(pd, false); -} - -static int owl_sps_init_domain(struct owl_sps *sps, int index) -{ - struct owl_sps_domain *pd; - - pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - pd->info = &sps->info->domains[index]; - pd->sps = sps; - - pd->genpd.name = pd->info->name; - pd->genpd.power_on = owl_sps_power_on; - pd->genpd.power_off = owl_sps_power_off; - pd->genpd.flags = pd->info->genpd_flags; - pm_genpd_init(&pd->genpd, NULL, false); - - sps->genpd_data.domains[index] = &pd->genpd; - - return 0; -} - -static int owl_sps_probe(struct platform_device *pdev) -{ - const struct of_device_id *match; - const struct owl_sps_info *sps_info; - struct owl_sps *sps; - int i, ret; - - if (!pdev->dev.of_node) { - dev_err(&pdev->dev, "no device node\n"); - return -ENODEV; - } - - match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); - if (!match || !match->data) { - dev_err(&pdev->dev, "unknown compatible or missing data\n"); - return -EINVAL; - } - - sps_info = match->data; - - sps = devm_kzalloc(&pdev->dev, - struct_size(sps, domains, sps_info->num_domains), - GFP_KERNEL); - if (!sps) - return -ENOMEM; - - sps->base = of_io_request_and_map(pdev->dev.of_node, 0, "owl-sps"); - if (IS_ERR(sps->base)) { - dev_err(&pdev->dev, "failed to map sps registers\n"); - return PTR_ERR(sps->base); - } - - sps->dev = &pdev->dev; - sps->info = sps_info; - sps->genpd_data.domains = sps->domains; - sps->genpd_data.num_domains = sps_info->num_domains; - - for (i = 0; i < sps_info->num_domains; i++) { - ret = owl_sps_init_domain(sps, i); - if (ret) - return ret; - } - - ret = of_genpd_add_provider_onecell(pdev->dev.of_node, &sps->genpd_data); - if (ret) { - dev_err(&pdev->dev, "failed to add provider (%d)", ret); - return ret; - } - - return 0; -} - -static const struct owl_sps_domain_info s500_sps_domains[] = { - [S500_PD_VDE] = { - .name = "VDE", - .pwr_bit = 0, - .ack_bit = 16, - }, - [S500_PD_VCE_SI] = { - .name = "VCE_SI", - .pwr_bit = 1, - .ack_bit = 17, - }, - [S500_PD_USB2_1] = { - .name = "USB2_1", - .pwr_bit = 2, - .ack_bit = 18, - }, - [S500_PD_CPU2] = { - .name = "CPU2", - .pwr_bit = 5, - .ack_bit = 21, - .genpd_flags = GENPD_FLAG_ALWAYS_ON, - }, - [S500_PD_CPU3] = { - .name = "CPU3", - .pwr_bit = 6, - .ack_bit = 22, - .genpd_flags = GENPD_FLAG_ALWAYS_ON, - }, - [S500_PD_DMA] = { - .name = "DMA", - .pwr_bit = 8, - .ack_bit = 12, - }, - [S500_PD_DS] = { - .name = "DS", - .pwr_bit = 9, - .ack_bit = 13, - }, - [S500_PD_USB3] = { - .name = "USB3", - .pwr_bit = 10, - .ack_bit = 14, - }, - [S500_PD_USB2_0] = { - .name = "USB2_0", - .pwr_bit = 11, - .ack_bit = 15, - }, -}; - -static const struct owl_sps_info s500_sps_info = { - .num_domains = ARRAY_SIZE(s500_sps_domains), - .domains = s500_sps_domains, -}; - -static const struct owl_sps_domain_info s700_sps_domains[] = { - [S700_PD_VDE] = { - .name = "VDE", - .pwr_bit = 0, - }, - [S700_PD_VCE_SI] = { - .name = "VCE_SI", - .pwr_bit = 1, - }, - [S700_PD_USB2_1] = { - .name = "USB2_1", - .pwr_bit = 2, - }, - [S700_PD_HDE] = { - .name = "HDE", - .pwr_bit = 7, - }, - [S700_PD_DMA] = { - .name = "DMA", - .pwr_bit = 8, - }, - [S700_PD_DS] = { - .name = "DS", - .pwr_bit = 9, - }, - [S700_PD_USB3] = { - .name = "USB3", - .pwr_bit = 10, - }, - [S700_PD_USB2_0] = { - .name = "USB2_0", - .pwr_bit = 11, - }, -}; - -static const struct owl_sps_info s700_sps_info = { - .num_domains = ARRAY_SIZE(s700_sps_domains), - .domains = s700_sps_domains, -}; - -static const struct owl_sps_domain_info s900_sps_domains[] = { - [S900_PD_GPU_B] = { - .name = "GPU_B", - .pwr_bit = 3, - }, - [S900_PD_VCE] = { - .name = "VCE", - .pwr_bit = 4, - }, - [S900_PD_SENSOR] = { - .name = "SENSOR", - .pwr_bit = 5, - }, - [S900_PD_VDE] = { - .name = "VDE", - .pwr_bit = 6, - }, - [S900_PD_HDE] = { - .name = "HDE", - .pwr_bit = 7, - }, - [S900_PD_USB3] = { - .name = "USB3", - .pwr_bit = 8, - }, - [S900_PD_DDR0] = { - .name = "DDR0", - .pwr_bit = 9, - }, - [S900_PD_DDR1] = { - .name = "DDR1", - .pwr_bit = 10, - }, - [S900_PD_DE] = { - .name = "DE", - .pwr_bit = 13, - }, - [S900_PD_NAND] = { - .name = "NAND", - .pwr_bit = 14, - }, - [S900_PD_USB2_H0] = { - .name = "USB2_H0", - .pwr_bit = 15, - }, - [S900_PD_USB2_H1] = { - .name = "USB2_H1", - .pwr_bit = 16, - }, -}; - -static const struct owl_sps_info s900_sps_info = { - .num_domains = ARRAY_SIZE(s900_sps_domains), - .domains = s900_sps_domains, -}; - -static const struct of_device_id owl_sps_of_matches[] = { - { .compatible = "actions,s500-sps", .data = &s500_sps_info }, - { .compatible = "actions,s700-sps", .data = &s700_sps_info }, - { .compatible = "actions,s900-sps", .data = &s900_sps_info }, - { } -}; - -static struct platform_driver owl_sps_platform_driver = { - .probe = owl_sps_probe, - .driver = { - .name = "owl-sps", - .of_match_table = owl_sps_of_matches, - .suppress_bind_attrs = true, - }, -}; - -static int __init owl_sps_init(void) -{ - return platform_driver_register(&owl_sps_platform_driver); -} -postcore_initcall(owl_sps_init); diff --git a/drivers/genpd/amlogic/Makefile b/drivers/genpd/amlogic/Makefile deleted file mode 100644 index 3d58abd574f9..000000000000 --- a/drivers/genpd/amlogic/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o -obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o -obj-$(CONFIG_MESON_SECURE_PM_DOMAINS) += meson-secure-pwrc.o diff --git a/drivers/genpd/amlogic/meson-ee-pwrc.c b/drivers/genpd/amlogic/meson-ee-pwrc.c deleted file mode 100644 index cfb796d40d9d..000000000000 --- a/drivers/genpd/amlogic/meson-ee-pwrc.c +++ /dev/null @@ -1,635 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (c) 2019 BayLibre, SAS - * Author: Neil Armstrong - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AO Offsets */ - -#define GX_AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) -#define GX_AO_RTI_GEN_PWR_ISO0 (0x3b << 2) - -/* - * Meson8/Meson8b/Meson8m2 only expose the power management registers of the - * AO-bus as syscon. 0x3a from GX translates to 0x02, 0x3b translates to 0x03 - * and so on. - */ -#define MESON8_AO_RTI_GEN_PWR_SLEEP0 (0x02 << 2) -#define MESON8_AO_RTI_GEN_PWR_ISO0 (0x03 << 2) - -/* HHI Offsets */ - -#define HHI_MEM_PD_REG0 (0x40 << 2) -#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) -#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) -#define HHI_VPU_MEM_PD_REG3 (0x43 << 2) -#define HHI_VPU_MEM_PD_REG4 (0x44 << 2) -#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2) -#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2) -#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2) -#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) - -#define G12A_HHI_NANOQ_MEM_PD_REG0 (0x43 << 2) -#define G12A_HHI_NANOQ_MEM_PD_REG1 (0x44 << 2) - -struct meson_ee_pwrc; -struct meson_ee_pwrc_domain; - -struct meson_ee_pwrc_mem_domain { - unsigned int reg; - unsigned int mask; -}; - -struct meson_ee_pwrc_top_domain { - unsigned int sleep_reg; - unsigned int sleep_mask; - unsigned int iso_reg; - unsigned int iso_mask; -}; - -struct meson_ee_pwrc_domain_desc { - char *name; - unsigned int reset_names_count; - unsigned int clk_names_count; - struct meson_ee_pwrc_top_domain *top_pd; - unsigned int mem_pd_count; - struct meson_ee_pwrc_mem_domain *mem_pd; - bool (*is_powered_off)(struct meson_ee_pwrc_domain *pwrc_domain); -}; - -struct meson_ee_pwrc_domain_data { - unsigned int count; - struct meson_ee_pwrc_domain_desc *domains; -}; - -/* TOP Power Domains */ - -static struct meson_ee_pwrc_top_domain gx_pwrc_vpu = { - .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, - .sleep_mask = BIT(8), - .iso_reg = GX_AO_RTI_GEN_PWR_SLEEP0, - .iso_mask = BIT(9), -}; - -static struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = { - .sleep_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, - .sleep_mask = BIT(8), - .iso_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, - .iso_mask = BIT(9), -}; - -#define SM1_EE_PD(__bit) \ - { \ - .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, \ - .sleep_mask = BIT(__bit), \ - .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, \ - .iso_mask = BIT(__bit), \ - } - -static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); -static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); -static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); -static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); -static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); - -static struct meson_ee_pwrc_top_domain g12a_pwrc_nna = { - .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, - .sleep_mask = BIT(16) | BIT(17), - .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, - .iso_mask = BIT(16) | BIT(17), -}; - -/* Memory PD Domains */ - -#define VPU_MEMPD(__reg) \ - { __reg, GENMASK(1, 0) }, \ - { __reg, GENMASK(3, 2) }, \ - { __reg, GENMASK(5, 4) }, \ - { __reg, GENMASK(7, 6) }, \ - { __reg, GENMASK(9, 8) }, \ - { __reg, GENMASK(11, 10) }, \ - { __reg, GENMASK(13, 12) }, \ - { __reg, GENMASK(15, 14) }, \ - { __reg, GENMASK(17, 16) }, \ - { __reg, GENMASK(19, 18) }, \ - { __reg, GENMASK(21, 20) }, \ - { __reg, GENMASK(23, 22) }, \ - { __reg, GENMASK(25, 24) }, \ - { __reg, GENMASK(27, 26) }, \ - { __reg, GENMASK(29, 28) }, \ - { __reg, GENMASK(31, 30) } - -#define VPU_HHI_MEMPD(__reg) \ - { __reg, BIT(8) }, \ - { __reg, BIT(9) }, \ - { __reg, BIT(10) }, \ - { __reg, BIT(11) }, \ - { __reg, BIT(12) }, \ - { __reg, BIT(13) }, \ - { __reg, BIT(14) }, \ - { __reg, BIT(15) } - -static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_vpu[] = { - VPU_MEMPD(HHI_VPU_MEM_PD_REG0), - VPU_HHI_MEMPD(HHI_MEM_PD_REG0), -}; - -static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { - VPU_MEMPD(HHI_VPU_MEM_PD_REG0), - VPU_MEMPD(HHI_VPU_MEM_PD_REG1), - VPU_MEMPD(HHI_VPU_MEM_PD_REG2), - VPU_HHI_MEMPD(HHI_MEM_PD_REG0), -}; - -static struct meson_ee_pwrc_mem_domain gxbb_pwrc_mem_vpu[] = { - VPU_MEMPD(HHI_VPU_MEM_PD_REG0), - VPU_MEMPD(HHI_VPU_MEM_PD_REG1), - VPU_HHI_MEMPD(HHI_MEM_PD_REG0), -}; - -static struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = { - { HHI_MEM_PD_REG0, GENMASK(3, 2) }, -}; - -static struct meson_ee_pwrc_mem_domain meson8_pwrc_audio_dsp_mem[] = { - { HHI_MEM_PD_REG0, GENMASK(1, 0) }, -}; - -static struct meson_ee_pwrc_mem_domain meson8_pwrc_mem_vpu[] = { - VPU_MEMPD(HHI_VPU_MEM_PD_REG0), - VPU_MEMPD(HHI_VPU_MEM_PD_REG1), - VPU_HHI_MEMPD(HHI_MEM_PD_REG0), -}; - -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { - VPU_MEMPD(HHI_VPU_MEM_PD_REG0), - VPU_MEMPD(HHI_VPU_MEM_PD_REG1), - VPU_MEMPD(HHI_VPU_MEM_PD_REG2), - VPU_MEMPD(HHI_VPU_MEM_PD_REG3), - { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) }, - { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) }, - { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) }, - { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) }, - VPU_HHI_MEMPD(HHI_MEM_PD_REG0), -}; - -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { - { HHI_NANOQ_MEM_PD_REG0, 0xff }, - { HHI_NANOQ_MEM_PD_REG1, 0xff }, -}; - -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { - { HHI_MEM_PD_REG0, GENMASK(31, 30) }, -}; - -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { - { HHI_MEM_PD_REG0, GENMASK(29, 26) }, -}; - -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { - { HHI_MEM_PD_REG0, GENMASK(25, 18) }, -}; - -static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_audio[] = { - { HHI_MEM_PD_REG0, GENMASK(5, 4) }, -}; - -static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { - { HHI_MEM_PD_REG0, GENMASK(5, 4) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) }, - { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, -}; - -static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_nna[] = { - { G12A_HHI_NANOQ_MEM_PD_REG0, GENMASK(31, 0) }, - { G12A_HHI_NANOQ_MEM_PD_REG1, GENMASK(23, 0) }, -}; - -#define VPU_PD(__name, __top_pd, __mem, __is_pwr_off, __resets, __clks) \ - { \ - .name = __name, \ - .reset_names_count = __resets, \ - .clk_names_count = __clks, \ - .top_pd = __top_pd, \ - .mem_pd_count = ARRAY_SIZE(__mem), \ - .mem_pd = __mem, \ - .is_powered_off = __is_pwr_off, \ - } - -#define TOP_PD(__name, __top_pd, __mem, __is_pwr_off) \ - { \ - .name = __name, \ - .top_pd = __top_pd, \ - .mem_pd_count = ARRAY_SIZE(__mem), \ - .mem_pd = __mem, \ - .is_powered_off = __is_pwr_off, \ - } - -#define MEM_PD(__name, __mem) \ - TOP_PD(__name, NULL, __mem, NULL) - -static bool pwrc_ee_is_powered_off(struct meson_ee_pwrc_domain *pwrc_domain); - -static struct meson_ee_pwrc_domain_desc axg_pwrc_domains[] = { - [PWRC_AXG_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, axg_pwrc_mem_vpu, - pwrc_ee_is_powered_off, 5, 2), - [PWRC_AXG_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), - [PWRC_AXG_AUDIO_ID] = MEM_PD("AUDIO", axg_pwrc_mem_audio), -}; - -static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { - [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, g12a_pwrc_mem_vpu, - pwrc_ee_is_powered_off, 11, 2), - [PWRC_G12A_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), - [PWRC_G12A_NNA_ID] = TOP_PD("NNA", &g12a_pwrc_nna, g12a_pwrc_mem_nna, - pwrc_ee_is_powered_off), -}; - -static struct meson_ee_pwrc_domain_desc gxbb_pwrc_domains[] = { - [PWRC_GXBB_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, gxbb_pwrc_mem_vpu, - pwrc_ee_is_powered_off, 12, 2), - [PWRC_GXBB_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), -}; - -static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { - [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, - meson8_pwrc_mem_vpu, - pwrc_ee_is_powered_off, 0, 1), - [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM", - meson_pwrc_mem_eth), - [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM", - meson8_pwrc_audio_dsp_mem), -}; - -static struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = { - [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, - meson8_pwrc_mem_vpu, - pwrc_ee_is_powered_off, 11, 1), - [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM", - meson_pwrc_mem_eth), - [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM", - meson8_pwrc_audio_dsp_mem), -}; - -static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { - [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, - pwrc_ee_is_powered_off, 11, 2), - [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, - pwrc_ee_is_powered_off), - [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb, - pwrc_ee_is_powered_off), - [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie, - pwrc_ee_is_powered_off), - [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, - pwrc_ee_is_powered_off), - [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), - [PWRC_SM1_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), -}; - -struct meson_ee_pwrc_domain { - struct generic_pm_domain base; - bool enabled; - struct meson_ee_pwrc *pwrc; - struct meson_ee_pwrc_domain_desc desc; - struct clk_bulk_data *clks; - int num_clks; - struct reset_control *rstc; - int num_rstc; -}; - -struct meson_ee_pwrc { - struct regmap *regmap_ao; - struct regmap *regmap_hhi; - struct meson_ee_pwrc_domain *domains; - struct genpd_onecell_data xlate; -}; - -static bool pwrc_ee_is_powered_off(struct meson_ee_pwrc_domain *pwrc_domain) -{ - u32 reg; - - regmap_read(pwrc_domain->pwrc->regmap_ao, - pwrc_domain->desc.top_pd->sleep_reg, ®); - - return (reg & pwrc_domain->desc.top_pd->sleep_mask); -} - -static int meson_ee_pwrc_off(struct generic_pm_domain *domain) -{ - struct meson_ee_pwrc_domain *pwrc_domain = - container_of(domain, struct meson_ee_pwrc_domain, base); - int i; - - if (pwrc_domain->desc.top_pd) - regmap_update_bits(pwrc_domain->pwrc->regmap_ao, - pwrc_domain->desc.top_pd->sleep_reg, - pwrc_domain->desc.top_pd->sleep_mask, - pwrc_domain->desc.top_pd->sleep_mask); - udelay(20); - - for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) - regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, - pwrc_domain->desc.mem_pd[i].reg, - pwrc_domain->desc.mem_pd[i].mask, - pwrc_domain->desc.mem_pd[i].mask); - - udelay(20); - - if (pwrc_domain->desc.top_pd) - regmap_update_bits(pwrc_domain->pwrc->regmap_ao, - pwrc_domain->desc.top_pd->iso_reg, - pwrc_domain->desc.top_pd->iso_mask, - pwrc_domain->desc.top_pd->iso_mask); - - if (pwrc_domain->num_clks) { - msleep(20); - clk_bulk_disable_unprepare(pwrc_domain->num_clks, - pwrc_domain->clks); - } - - return 0; -} - -static int meson_ee_pwrc_on(struct generic_pm_domain *domain) -{ - struct meson_ee_pwrc_domain *pwrc_domain = - container_of(domain, struct meson_ee_pwrc_domain, base); - int i, ret; - - if (pwrc_domain->desc.top_pd) - regmap_update_bits(pwrc_domain->pwrc->regmap_ao, - pwrc_domain->desc.top_pd->sleep_reg, - pwrc_domain->desc.top_pd->sleep_mask, 0); - udelay(20); - - for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) - regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, - pwrc_domain->desc.mem_pd[i].reg, - pwrc_domain->desc.mem_pd[i].mask, 0); - - udelay(20); - - ret = reset_control_assert(pwrc_domain->rstc); - if (ret) - return ret; - - if (pwrc_domain->desc.top_pd) - regmap_update_bits(pwrc_domain->pwrc->regmap_ao, - pwrc_domain->desc.top_pd->iso_reg, - pwrc_domain->desc.top_pd->iso_mask, 0); - - ret = reset_control_deassert(pwrc_domain->rstc); - if (ret) - return ret; - - return clk_bulk_prepare_enable(pwrc_domain->num_clks, - pwrc_domain->clks); -} - -static int meson_ee_pwrc_init_domain(struct platform_device *pdev, - struct meson_ee_pwrc *pwrc, - struct meson_ee_pwrc_domain *dom) -{ - int ret; - - dom->pwrc = pwrc; - dom->num_rstc = dom->desc.reset_names_count; - dom->num_clks = dom->desc.clk_names_count; - - if (dom->num_rstc) { - int count = reset_control_get_count(&pdev->dev); - - if (count != dom->num_rstc) - dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n", - count, dom->desc.name); - - dom->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(dom->rstc)) - return PTR_ERR(dom->rstc); - } - - if (dom->num_clks) { - int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks); - if (ret < 0) - return ret; - - if (dom->num_clks != ret) { - dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n", - ret, dom->desc.name); - dom->num_clks = ret; - } - } - - dom->base.name = dom->desc.name; - dom->base.power_on = meson_ee_pwrc_on; - dom->base.power_off = meson_ee_pwrc_off; - - /* - * TOFIX: This is a special case for the VPU power domain, which can - * be enabled previously by the bootloader. In this case the VPU - * pipeline may be functional but no driver maybe never attach - * to this power domain, and if the domain is disabled it could - * cause system errors. This is why the pm_domain_always_on_gov - * is used here. - * For the same reason, the clocks should be enabled in case - * we need to power the domain off, otherwise the internal clocks - * prepare/enable counters won't be in sync. - */ - if (dom->num_clks && dom->desc.is_powered_off && !dom->desc.is_powered_off(dom)) { - ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks); - if (ret) - return ret; - - dom->base.flags = GENPD_FLAG_ALWAYS_ON; - ret = pm_genpd_init(&dom->base, NULL, false); - if (ret) - return ret; - } else { - ret = pm_genpd_init(&dom->base, NULL, - (dom->desc.is_powered_off ? - dom->desc.is_powered_off(dom) : true)); - if (ret) - return ret; - } - - return 0; -} - -static int meson_ee_pwrc_probe(struct platform_device *pdev) -{ - const struct meson_ee_pwrc_domain_data *match; - struct regmap *regmap_ao, *regmap_hhi; - struct device_node *parent_np; - struct meson_ee_pwrc *pwrc; - int i, ret; - - match = of_device_get_match_data(&pdev->dev); - if (!match) { - dev_err(&pdev->dev, "failed to get match data\n"); - return -ENODEV; - } - - pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); - if (!pwrc) - return -ENOMEM; - - pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, - sizeof(*pwrc->xlate.domains), - GFP_KERNEL); - if (!pwrc->xlate.domains) - return -ENOMEM; - - pwrc->domains = devm_kcalloc(&pdev->dev, match->count, - sizeof(*pwrc->domains), GFP_KERNEL); - if (!pwrc->domains) - return -ENOMEM; - - pwrc->xlate.num_domains = match->count; - - parent_np = of_get_parent(pdev->dev.of_node); - regmap_hhi = syscon_node_to_regmap(parent_np); - of_node_put(parent_np); - if (IS_ERR(regmap_hhi)) { - dev_err(&pdev->dev, "failed to get HHI regmap\n"); - return PTR_ERR(regmap_hhi); - } - - regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "amlogic,ao-sysctrl"); - if (IS_ERR(regmap_ao)) { - dev_err(&pdev->dev, "failed to get AO regmap\n"); - return PTR_ERR(regmap_ao); - } - - pwrc->regmap_ao = regmap_ao; - pwrc->regmap_hhi = regmap_hhi; - - platform_set_drvdata(pdev, pwrc); - - for (i = 0 ; i < match->count ; ++i) { - struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; - - memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc)); - - ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom); - if (ret) - return ret; - - pwrc->xlate.domains[i] = &dom->base; - } - - return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); -} - -static void meson_ee_pwrc_shutdown(struct platform_device *pdev) -{ - struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev); - int i; - - for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) { - struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; - - if (dom->desc.is_powered_off && !dom->desc.is_powered_off(dom)) - meson_ee_pwrc_off(&dom->base); - } -} - -static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { - .count = ARRAY_SIZE(g12a_pwrc_domains), - .domains = g12a_pwrc_domains, -}; - -static struct meson_ee_pwrc_domain_data meson_ee_axg_pwrc_data = { - .count = ARRAY_SIZE(axg_pwrc_domains), - .domains = axg_pwrc_domains, -}; - -static struct meson_ee_pwrc_domain_data meson_ee_gxbb_pwrc_data = { - .count = ARRAY_SIZE(gxbb_pwrc_domains), - .domains = gxbb_pwrc_domains, -}; - -static struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = { - .count = ARRAY_SIZE(meson8_pwrc_domains), - .domains = meson8_pwrc_domains, -}; - -static struct meson_ee_pwrc_domain_data meson_ee_m8b_pwrc_data = { - .count = ARRAY_SIZE(meson8b_pwrc_domains), - .domains = meson8b_pwrc_domains, -}; - -static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { - .count = ARRAY_SIZE(sm1_pwrc_domains), - .domains = sm1_pwrc_domains, -}; - -static const struct of_device_id meson_ee_pwrc_match_table[] = { - { - .compatible = "amlogic,meson8-pwrc", - .data = &meson_ee_m8_pwrc_data, - }, - { - .compatible = "amlogic,meson8b-pwrc", - .data = &meson_ee_m8b_pwrc_data, - }, - { - .compatible = "amlogic,meson8m2-pwrc", - .data = &meson_ee_m8b_pwrc_data, - }, - { - .compatible = "amlogic,meson-axg-pwrc", - .data = &meson_ee_axg_pwrc_data, - }, - { - .compatible = "amlogic,meson-gxbb-pwrc", - .data = &meson_ee_gxbb_pwrc_data, - }, - { - .compatible = "amlogic,meson-g12a-pwrc", - .data = &meson_ee_g12a_pwrc_data, - }, - { - .compatible = "amlogic,meson-sm1-pwrc", - .data = &meson_ee_sm1_pwrc_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, meson_ee_pwrc_match_table); - -static struct platform_driver meson_ee_pwrc_driver = { - .probe = meson_ee_pwrc_probe, - .shutdown = meson_ee_pwrc_shutdown, - .driver = { - .name = "meson_ee_pwrc", - .of_match_table = meson_ee_pwrc_match_table, - }, -}; -module_platform_driver(meson_ee_pwrc_driver); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/genpd/amlogic/meson-gx-pwrc-vpu.c b/drivers/genpd/amlogic/meson-gx-pwrc-vpu.c deleted file mode 100644 index 33df520eab95..000000000000 --- a/drivers/genpd/amlogic/meson-gx-pwrc-vpu.c +++ /dev/null @@ -1,379 +0,0 @@ -/* - * Copyright (c) 2017 BayLibre, SAS - * Author: Neil Armstrong - * - * SPDX-License-Identifier: GPL-2.0+ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* AO Offsets */ - -#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) - -#define GEN_PWR_VPU_HDMI BIT(8) -#define GEN_PWR_VPU_HDMI_ISO BIT(9) - -/* HHI Offsets */ - -#define HHI_MEM_PD_REG0 (0x40 << 2) -#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) -#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) -#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) - -struct meson_gx_pwrc_vpu { - struct generic_pm_domain genpd; - struct regmap *regmap_ao; - struct regmap *regmap_hhi; - struct reset_control *rstc; - struct clk *vpu_clk; - struct clk *vapb_clk; -}; - -static inline -struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) -{ - return container_of(d, struct meson_gx_pwrc_vpu, genpd); -} - -static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); - udelay(20); - - /* Power Down Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), BIT(i)); - udelay(5); - } - udelay(20); - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); - - msleep(20); - - clk_disable_unprepare(pd->vpu_clk); - clk_disable_unprepare(pd->vapb_clk); - - return 0; -} - -static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); - udelay(20); - - /* Power Down Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, - 0x3 << i, 0x3 << i); - udelay(5); - } - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), BIT(i)); - udelay(5); - } - udelay(20); - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); - - msleep(20); - - clk_disable_unprepare(pd->vpu_clk); - clk_disable_unprepare(pd->vapb_clk); - - return 0; -} - -static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) -{ - int ret; - - ret = clk_prepare_enable(pd->vpu_clk); - if (ret) - return ret; - - ret = clk_prepare_enable(pd->vapb_clk); - if (ret) - clk_disable_unprepare(pd->vpu_clk); - - return ret; -} - -static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int ret; - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, 0); - udelay(20); - - /* Power Up Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0); - udelay(5); - } - - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0); - udelay(5); - } - - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), 0); - udelay(5); - } - udelay(20); - - ret = reset_control_assert(pd->rstc); - if (ret) - return ret; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, 0); - - ret = reset_control_deassert(pd->rstc); - if (ret) - return ret; - - ret = meson_gx_pwrc_vpu_setup_clk(pd); - if (ret) - return ret; - - return 0; -} - -static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd) -{ - struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); - int ret; - int i; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI, 0); - udelay(20); - - /* Power Up Memories */ - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x3 << i, 0); - udelay(5); - } - - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x3 << i, 0); - udelay(5); - } - - for (i = 0; i < 32; i += 2) { - regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, - 0x3 << i, 0); - udelay(5); - } - - for (i = 8; i < 16; i++) { - regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, - BIT(i), 0); - udelay(5); - } - udelay(20); - - ret = reset_control_assert(pd->rstc); - if (ret) - return ret; - - regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, - GEN_PWR_VPU_HDMI_ISO, 0); - - ret = reset_control_deassert(pd->rstc); - if (ret) - return ret; - - ret = meson_gx_pwrc_vpu_setup_clk(pd); - if (ret) - return ret; - - return 0; -} - -static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) -{ - u32 reg; - - regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, ®); - - return (reg & GEN_PWR_VPU_HDMI); -} - -static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { - .genpd = { - .name = "vpu_hdmi", - .power_off = meson_gx_pwrc_vpu_power_off, - .power_on = meson_gx_pwrc_vpu_power_on, - }, -}; - -static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = { - .genpd = { - .name = "vpu_hdmi", - .power_off = meson_g12a_pwrc_vpu_power_off, - .power_on = meson_g12a_pwrc_vpu_power_on, - }, -}; - -static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) -{ - const struct meson_gx_pwrc_vpu *vpu_pd_match; - struct regmap *regmap_ao, *regmap_hhi; - struct meson_gx_pwrc_vpu *vpu_pd; - struct device_node *parent_np; - struct reset_control *rstc; - struct clk *vpu_clk; - struct clk *vapb_clk; - bool powered_off; - int ret; - - vpu_pd_match = of_device_get_match_data(&pdev->dev); - if (!vpu_pd_match) { - dev_err(&pdev->dev, "failed to get match data\n"); - return -ENODEV; - } - - vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); - if (!vpu_pd) - return -ENOMEM; - - memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); - - parent_np = of_get_parent(pdev->dev.of_node); - regmap_ao = syscon_node_to_regmap(parent_np); - of_node_put(parent_np); - if (IS_ERR(regmap_ao)) { - dev_err(&pdev->dev, "failed to get regmap\n"); - return PTR_ERR(regmap_ao); - } - - regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "amlogic,hhi-sysctrl"); - if (IS_ERR(regmap_hhi)) { - dev_err(&pdev->dev, "failed to get HHI regmap\n"); - return PTR_ERR(regmap_hhi); - } - - rstc = devm_reset_control_array_get_exclusive(&pdev->dev); - if (IS_ERR(rstc)) - return dev_err_probe(&pdev->dev, PTR_ERR(rstc), - "failed to get reset lines\n"); - - vpu_clk = devm_clk_get(&pdev->dev, "vpu"); - if (IS_ERR(vpu_clk)) { - dev_err(&pdev->dev, "vpu clock request failed\n"); - return PTR_ERR(vpu_clk); - } - - vapb_clk = devm_clk_get(&pdev->dev, "vapb"); - if (IS_ERR(vapb_clk)) { - dev_err(&pdev->dev, "vapb clock request failed\n"); - return PTR_ERR(vapb_clk); - } - - vpu_pd->regmap_ao = regmap_ao; - vpu_pd->regmap_hhi = regmap_hhi; - vpu_pd->rstc = rstc; - vpu_pd->vpu_clk = vpu_clk; - vpu_pd->vapb_clk = vapb_clk; - - platform_set_drvdata(pdev, vpu_pd); - - powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); - - /* If already powered, sync the clock states */ - if (!powered_off) { - ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); - if (ret) - return ret; - } - - vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; - pm_genpd_init(&vpu_pd->genpd, NULL, powered_off); - - return of_genpd_add_provider_simple(pdev->dev.of_node, - &vpu_pd->genpd); -} - -static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) -{ - struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); - bool powered_off; - - powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); - if (!powered_off) - vpu_pd->genpd.power_off(&vpu_pd->genpd); -} - -static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { - { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, - { - .compatible = "amlogic,meson-g12a-pwrc-vpu", - .data = &vpu_hdmi_pd_g12a - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table); - -static struct platform_driver meson_gx_pwrc_vpu_driver = { - .probe = meson_gx_pwrc_vpu_probe, - .shutdown = meson_gx_pwrc_vpu_shutdown, - .driver = { - .name = "meson_gx_pwrc_vpu", - .of_match_table = meson_gx_pwrc_vpu_match_table, - }, -}; -module_platform_driver(meson_gx_pwrc_vpu_driver); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/genpd/amlogic/meson-secure-pwrc.c b/drivers/genpd/amlogic/meson-secure-pwrc.c deleted file mode 100644 index 89c881c56cd7..000000000000 --- a/drivers/genpd/amlogic/meson-secure-pwrc.c +++ /dev/null @@ -1,257 +0,0 @@ -// SPDX-License-Identifier: (GPL-2.0+ OR MIT) -/* - * Copyright (c) 2019 Amlogic, Inc. - * Author: Jianxin Pan - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PWRC_ON 1 -#define PWRC_OFF 0 - -struct meson_secure_pwrc_domain { - struct generic_pm_domain base; - unsigned int index; - struct meson_secure_pwrc *pwrc; -}; - -struct meson_secure_pwrc { - struct meson_secure_pwrc_domain *domains; - struct genpd_onecell_data xlate; - struct meson_sm_firmware *fw; -}; - -struct meson_secure_pwrc_domain_desc { - unsigned int index; - unsigned int flags; - char *name; - bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain); -}; - -struct meson_secure_pwrc_domain_data { - unsigned int count; - struct meson_secure_pwrc_domain_desc *domains; -}; - -static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain) -{ - int is_off = 1; - - if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off, - pwrc_domain->index, 0, 0, 0, 0) < 0) - pr_err("failed to get power domain status\n"); - - return is_off; -} - -static int meson_secure_pwrc_off(struct generic_pm_domain *domain) -{ - int ret = 0; - struct meson_secure_pwrc_domain *pwrc_domain = - container_of(domain, struct meson_secure_pwrc_domain, base); - - if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, - pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) { - pr_err("failed to set power domain off\n"); - ret = -EINVAL; - } - - return ret; -} - -static int meson_secure_pwrc_on(struct generic_pm_domain *domain) -{ - int ret = 0; - struct meson_secure_pwrc_domain *pwrc_domain = - container_of(domain, struct meson_secure_pwrc_domain, base); - - if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, - pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) { - pr_err("failed to set power domain on\n"); - ret = -EINVAL; - } - - return ret; -} - -#define SEC_PD(__name, __flag) \ -[PWRC_##__name##_ID] = \ -{ \ - .name = #__name, \ - .index = PWRC_##__name##_ID, \ - .is_off = pwrc_secure_is_off, \ - .flags = __flag, \ -} - -static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { - SEC_PD(DSPA, 0), - SEC_PD(DSPB, 0), - /* UART should keep working in ATF after suspend and before resume */ - SEC_PD(UART, GENPD_FLAG_ALWAYS_ON), - /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ - SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON), - SEC_PD(I2C, 0), - SEC_PD(PSRAM, 0), - SEC_PD(ACODEC, 0), - SEC_PD(AUDIO, 0), - SEC_PD(OTP, 0), - SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE), - SEC_PD(SD_EMMC, 0), - SEC_PD(RAMA, 0), - /* SRAMB is used as ATF runtime memory, and should be always on */ - SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON), - SEC_PD(IR, 0), - SEC_PD(SPICC, 0), - SEC_PD(SPIFC, 0), - SEC_PD(USB, 0), - /* NIC is for the Arm NIC-400 interconnect, and should be always on */ - SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON), - SEC_PD(PDMIN, 0), - SEC_PD(RSA, 0), -}; - -static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { - SEC_PD(C3_NNA, 0), - SEC_PD(C3_AUDIO, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_SDIOA, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_EMMC, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_USB_COMB, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_SDCARD, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_ETH, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_GE2D, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_CVE, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_GDC_WRAP, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_ISP_TOP, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_MIPI_ISP_WRAP, GENPD_FLAG_ALWAYS_ON), - SEC_PD(C3_VCODEC, 0), -}; - -static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = { - SEC_PD(S4_DOS_HEVC, 0), - SEC_PD(S4_DOS_VDEC, 0), - SEC_PD(S4_VPU_HDMI, 0), - SEC_PD(S4_USB_COMB, 0), - SEC_PD(S4_GE2D, 0), - /* ETH is for ethernet online wakeup, and should be always on */ - SEC_PD(S4_ETH, GENPD_FLAG_ALWAYS_ON), - SEC_PD(S4_DEMOD, 0), - SEC_PD(S4_AUDIO, 0), -}; - -static int meson_secure_pwrc_probe(struct platform_device *pdev) -{ - int i; - struct device_node *sm_np; - struct meson_secure_pwrc *pwrc; - const struct meson_secure_pwrc_domain_data *match; - - match = of_device_get_match_data(&pdev->dev); - if (!match) { - dev_err(&pdev->dev, "failed to get match data\n"); - return -ENODEV; - } - - sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm"); - if (!sm_np) { - dev_err(&pdev->dev, "no secure-monitor node\n"); - return -ENODEV; - } - - pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); - if (!pwrc) { - of_node_put(sm_np); - return -ENOMEM; - } - - pwrc->fw = meson_sm_get(sm_np); - of_node_put(sm_np); - if (!pwrc->fw) - return -EPROBE_DEFER; - - pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, - sizeof(*pwrc->xlate.domains), - GFP_KERNEL); - if (!pwrc->xlate.domains) - return -ENOMEM; - - pwrc->domains = devm_kcalloc(&pdev->dev, match->count, - sizeof(*pwrc->domains), GFP_KERNEL); - if (!pwrc->domains) - return -ENOMEM; - - pwrc->xlate.num_domains = match->count; - platform_set_drvdata(pdev, pwrc); - - for (i = 0 ; i < match->count ; ++i) { - struct meson_secure_pwrc_domain *dom = &pwrc->domains[i]; - - if (!match->domains[i].name) - continue; - - dom->pwrc = pwrc; - dom->index = match->domains[i].index; - dom->base.name = match->domains[i].name; - dom->base.flags = match->domains[i].flags; - dom->base.power_on = meson_secure_pwrc_on; - dom->base.power_off = meson_secure_pwrc_off; - - pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom)); - - pwrc->xlate.domains[i] = &dom->base; - } - - return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); -} - -static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { - .domains = a1_pwrc_domains, - .count = ARRAY_SIZE(a1_pwrc_domains), -}; - -static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { - .domains = c3_pwrc_domains, - .count = ARRAY_SIZE(c3_pwrc_domains), -}; - -static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = { - .domains = s4_pwrc_domains, - .count = ARRAY_SIZE(s4_pwrc_domains), -}; - -static const struct of_device_id meson_secure_pwrc_match_table[] = { - { - .compatible = "amlogic,meson-a1-pwrc", - .data = &meson_secure_a1_pwrc_data, - }, - { - .compatible = "amlogic,c3-pwrc", - .data = &amlogic_secure_c3_pwrc_data, - }, - { - .compatible = "amlogic,meson-s4-pwrc", - .data = &meson_secure_s4_pwrc_data, - }, - { /* sentinel */ } -}; -MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table); - -static struct platform_driver meson_secure_pwrc_driver = { - .probe = meson_secure_pwrc_probe, - .driver = { - .name = "meson_secure_pwrc", - .of_match_table = meson_secure_pwrc_match_table, - }, -}; -module_platform_driver(meson_secure_pwrc_driver); -MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/genpd/apple/Makefile b/drivers/genpd/apple/Makefile deleted file mode 100644 index 53665af630be..000000000000 --- a/drivers/genpd/apple/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += pmgr-pwrstate.o diff --git a/drivers/genpd/apple/pmgr-pwrstate.c b/drivers/genpd/apple/pmgr-pwrstate.c deleted file mode 100644 index d62a776c89a1..000000000000 --- a/drivers/genpd/apple/pmgr-pwrstate.c +++ /dev/null @@ -1,326 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only OR MIT -/* - * Apple SoC PMGR device power state driver - * - * Copyright The Asahi Linux Contributors - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define APPLE_PMGR_RESET BIT(31) -#define APPLE_PMGR_AUTO_ENABLE BIT(28) -#define APPLE_PMGR_PS_AUTO GENMASK(27, 24) -#define APPLE_PMGR_PS_MIN GENMASK(19, 16) -#define APPLE_PMGR_PARENT_OFF BIT(11) -#define APPLE_PMGR_DEV_DISABLE BIT(10) -#define APPLE_PMGR_WAS_CLKGATED BIT(9) -#define APPLE_PMGR_WAS_PWRGATED BIT(8) -#define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4) -#define APPLE_PMGR_PS_TARGET GENMASK(3, 0) - -#define APPLE_PMGR_FLAGS (APPLE_PMGR_WAS_CLKGATED | APPLE_PMGR_WAS_PWRGATED) - -#define APPLE_PMGR_PS_ACTIVE 0xf -#define APPLE_PMGR_PS_CLKGATE 0x4 -#define APPLE_PMGR_PS_PWRGATE 0x0 - -#define APPLE_PMGR_PS_SET_TIMEOUT 100 -#define APPLE_PMGR_RESET_TIME 1 - -struct apple_pmgr_ps { - struct device *dev; - struct generic_pm_domain genpd; - struct reset_controller_dev rcdev; - struct regmap *regmap; - u32 offset; - u32 min_state; -}; - -#define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) -#define rcdev_to_apple_pmgr_ps(_rcdev) container_of(_rcdev, struct apple_pmgr_ps, rcdev) - -static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool auto_enable) -{ - int ret; - struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd); - u32 reg; - - ret = regmap_read(ps->regmap, ps->offset, ®); - if (ret < 0) - return ret; - - /* Resets are synchronous, and only work if the device is powered and clocked. */ - if (reg & APPLE_PMGR_RESET && pstate != APPLE_PMGR_PS_ACTIVE) - dev_err(ps->dev, "PS %s: powering off with RESET active\n", - genpd->name); - - reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET); - reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate); - - dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg); - - regmap_write(ps->regmap, ps->offset, reg); - - ret = regmap_read_poll_timeout_atomic( - ps->regmap, ps->offset, reg, - (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1, - APPLE_PMGR_PS_SET_TIMEOUT); - if (ret < 0) - dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n", - genpd->name, pstate, reg); - - if (auto_enable) { - /* Not all devices implement this; this is a no-op where not implemented. */ - reg &= ~APPLE_PMGR_FLAGS; - reg |= APPLE_PMGR_AUTO_ENABLE; - regmap_write(ps->regmap, ps->offset, reg); - } - - return ret; -} - -static bool apple_pmgr_ps_is_active(struct apple_pmgr_ps *ps) -{ - u32 reg = 0; - - regmap_read(ps->regmap, ps->offset, ®); - /* - * We consider domains as active if they are actually on, or if they have auto-PM - * enabled and the intended target is on. - */ - return (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == APPLE_PMGR_PS_ACTIVE || - (FIELD_GET(APPLE_PMGR_PS_TARGET, reg) == APPLE_PMGR_PS_ACTIVE && - reg & APPLE_PMGR_AUTO_ENABLE)); -} - -static int apple_pmgr_ps_power_on(struct generic_pm_domain *genpd) -{ - return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_ACTIVE, true); -} - -static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd) -{ - return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, false); -} - -static int apple_pmgr_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) -{ - struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); - unsigned long flags; - - spin_lock_irqsave(&ps->genpd.slock, flags); - - if (ps->genpd.status == GENPD_STATE_OFF) - dev_err(ps->dev, "PS 0x%x: asserting RESET while powered down\n", ps->offset); - - dev_dbg(ps->dev, "PS 0x%x: assert reset\n", ps->offset); - /* Quiesce device before asserting reset */ - regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, - APPLE_PMGR_DEV_DISABLE); - regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, - APPLE_PMGR_RESET); - - spin_unlock_irqrestore(&ps->genpd.slock, flags); - - return 0; -} - -static int apple_pmgr_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) -{ - struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); - unsigned long flags; - - spin_lock_irqsave(&ps->genpd.slock, flags); - - dev_dbg(ps->dev, "PS 0x%x: deassert reset\n", ps->offset); - regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, 0); - regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, 0); - - if (ps->genpd.status == GENPD_STATE_OFF) - dev_err(ps->dev, "PS 0x%x: RESET was deasserted while powered down\n", ps->offset); - - spin_unlock_irqrestore(&ps->genpd.slock, flags); - - return 0; -} - -static int apple_pmgr_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) -{ - int ret; - - ret = apple_pmgr_reset_assert(rcdev, id); - if (ret) - return ret; - - usleep_range(APPLE_PMGR_RESET_TIME, 2 * APPLE_PMGR_RESET_TIME); - - return apple_pmgr_reset_deassert(rcdev, id); -} - -static int apple_pmgr_reset_status(struct reset_controller_dev *rcdev, unsigned long id) -{ - struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); - u32 reg = 0; - - regmap_read(ps->regmap, ps->offset, ®); - - return !!(reg & APPLE_PMGR_RESET); -} - -const struct reset_control_ops apple_pmgr_reset_ops = { - .assert = apple_pmgr_reset_assert, - .deassert = apple_pmgr_reset_deassert, - .reset = apple_pmgr_reset_reset, - .status = apple_pmgr_reset_status, -}; - -static int apple_pmgr_reset_xlate(struct reset_controller_dev *rcdev, - const struct of_phandle_args *reset_spec) -{ - return 0; -} - -static int apple_pmgr_ps_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *node = dev->of_node; - struct apple_pmgr_ps *ps; - struct regmap *regmap; - struct of_phandle_iterator it; - int ret; - const char *name; - bool active; - - regmap = syscon_node_to_regmap(node->parent); - if (IS_ERR(regmap)) - return PTR_ERR(regmap); - - ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL); - if (!ps) - return -ENOMEM; - - ps->dev = dev; - ps->regmap = regmap; - - ret = of_property_read_string(node, "label", &name); - if (ret < 0) { - dev_err(dev, "missing label property\n"); - return ret; - } - - ret = of_property_read_u32(node, "reg", &ps->offset); - if (ret < 0) { - dev_err(dev, "missing reg property\n"); - return ret; - } - - ps->genpd.flags |= GENPD_FLAG_IRQ_SAFE; - ps->genpd.name = name; - ps->genpd.power_on = apple_pmgr_ps_power_on; - ps->genpd.power_off = apple_pmgr_ps_power_off; - - ret = of_property_read_u32(node, "apple,min-state", &ps->min_state); - if (ret == 0 && ps->min_state <= APPLE_PMGR_PS_ACTIVE) - regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN, - FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state)); - - active = apple_pmgr_ps_is_active(ps); - if (of_property_read_bool(node, "apple,always-on")) { - ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON; - if (!active) { - dev_warn(dev, "always-on domain %s is not on at boot\n", name); - /* Turn it on so pm_genpd_init does not fail */ - active = apple_pmgr_ps_power_on(&ps->genpd) == 0; - } - } - - /* Turn on auto-PM if the domain is already on */ - if (active) - regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, - APPLE_PMGR_AUTO_ENABLE); - - ret = pm_genpd_init(&ps->genpd, NULL, !active); - if (ret < 0) { - dev_err(dev, "pm_genpd_init failed\n"); - return ret; - } - - ret = of_genpd_add_provider_simple(node, &ps->genpd); - if (ret < 0) { - dev_err(dev, "of_genpd_add_provider_simple failed\n"); - return ret; - } - - of_for_each_phandle(&it, ret, node, "power-domains", "#power-domain-cells", -1) { - struct of_phandle_args parent, child; - - parent.np = it.node; - parent.args_count = of_phandle_iterator_args(&it, parent.args, MAX_PHANDLE_ARGS); - child.np = node; - child.args_count = 0; - ret = of_genpd_add_subdomain(&parent, &child); - - if (ret == -EPROBE_DEFER) { - of_node_put(parent.np); - goto err_remove; - } else if (ret < 0) { - dev_err(dev, "failed to add to parent domain: %d (%s -> %s)\n", - ret, it.node->name, node->name); - of_node_put(parent.np); - goto err_remove; - } - } - - /* - * Do not participate in regular PM; parent power domains are handled via the - * genpd hierarchy. - */ - pm_genpd_remove_device(dev); - - ps->rcdev.owner = THIS_MODULE; - ps->rcdev.nr_resets = 1; - ps->rcdev.ops = &apple_pmgr_reset_ops; - ps->rcdev.of_node = dev->of_node; - ps->rcdev.of_reset_n_cells = 0; - ps->rcdev.of_xlate = apple_pmgr_reset_xlate; - - ret = devm_reset_controller_register(dev, &ps->rcdev); - if (ret < 0) - goto err_remove; - - return 0; -err_remove: - of_genpd_del_provider(node); - pm_genpd_remove(&ps->genpd); - return ret; -} - -static const struct of_device_id apple_pmgr_ps_of_match[] = { - { .compatible = "apple,pmgr-pwrstate" }, - {} -}; - -MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match); - -static struct platform_driver apple_pmgr_ps_driver = { - .probe = apple_pmgr_ps_probe, - .driver = { - .name = "apple-pmgr-pwrstate", - .of_match_table = apple_pmgr_ps_of_match, - }, -}; - -MODULE_AUTHOR("Hector Martin "); -MODULE_DESCRIPTION("PMGR power state driver for Apple SoCs"); - -module_platform_driver(apple_pmgr_ps_driver); diff --git a/drivers/genpd/bcm/Makefile b/drivers/genpd/bcm/Makefile deleted file mode 100644 index 6bfbe4e4db13..000000000000 --- a/drivers/genpd/bcm/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_BCM_PMB) += bcm-pmb.o -obj-$(CONFIG_BCM2835_POWER) += bcm2835-power.o -obj-$(CONFIG_BCM63XX_POWER) += bcm63xx-power.o -obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o diff --git a/drivers/genpd/bcm/bcm-pmb.c b/drivers/genpd/bcm/bcm-pmb.c deleted file mode 100644 index a72ba26ecf9d..000000000000 --- a/drivers/genpd/bcm/bcm-pmb.c +++ /dev/null @@ -1,363 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Copyright (c) 2013 Broadcom - * Copyright (C) 2020 Rafał Miłecki - */ - -#include -#include -#include -#include -#include -#include -#include - -#define BPCM_ID_REG 0x00 -#define BPCM_CAPABILITIES 0x04 -#define BPCM_CAP_NUM_ZONES 0x000000ff -#define BPCM_CAP_SR_REG_BITS 0x0000ff00 -#define BPCM_CAP_PLLTYPE 0x00030000 -#define BPCM_CAP_UBUS 0x00080000 -#define BPCM_CONTROL 0x08 -#define BPCM_STATUS 0x0c -#define BPCM_ROSC_CONTROL 0x10 -#define BPCM_ROSC_THRESH_H 0x14 -#define BPCM_ROSC_THRESHOLD_BCM6838 0x14 -#define BPCM_ROSC_THRESH_S 0x18 -#define BPCM_ROSC_COUNT_BCM6838 0x18 -#define BPCM_ROSC_COUNT 0x1c -#define BPCM_PWD_CONTROL_BCM6838 0x1c -#define BPCM_PWD_CONTROL 0x20 -#define BPCM_SR_CONTROL_BCM6838 0x20 -#define BPCM_PWD_ACCUM_CONTROL 0x24 -#define BPCM_SR_CONTROL 0x28 -#define BPCM_GLOBAL_CONTROL 0x2c -#define BPCM_MISC_CONTROL 0x30 -#define BPCM_MISC_CONTROL2 0x34 -#define BPCM_SGPHY_CNTL 0x38 -#define BPCM_SGPHY_STATUS 0x3c -#define BPCM_ZONE0 0x40 -#define BPCM_ZONE_CONTROL 0x00 -#define BPCM_ZONE_CONTROL_MANUAL_CLK_EN 0x00000001 -#define BPCM_ZONE_CONTROL_MANUAL_RESET_CTL 0x00000002 -#define BPCM_ZONE_CONTROL_FREQ_SCALE_USED 0x00000004 /* R/O */ -#define BPCM_ZONE_CONTROL_DPG_CAPABLE 0x00000008 /* R/O */ -#define BPCM_ZONE_CONTROL_MANUAL_MEM_PWR 0x00000030 -#define BPCM_ZONE_CONTROL_MANUAL_ISO_CTL 0x00000040 -#define BPCM_ZONE_CONTROL_MANUAL_CTL 0x00000080 -#define BPCM_ZONE_CONTROL_DPG_CTL_EN 0x00000100 -#define BPCM_ZONE_CONTROL_PWR_DN_REQ 0x00000200 -#define BPCM_ZONE_CONTROL_PWR_UP_REQ 0x00000400 -#define BPCM_ZONE_CONTROL_MEM_PWR_CTL_EN 0x00000800 -#define BPCM_ZONE_CONTROL_BLK_RESET_ASSERT 0x00001000 -#define BPCM_ZONE_CONTROL_MEM_STBY 0x00002000 -#define BPCM_ZONE_CONTROL_RESERVED 0x0007c000 -#define BPCM_ZONE_CONTROL_PWR_CNTL_STATE 0x00f80000 -#define BPCM_ZONE_CONTROL_FREQ_SCALAR_DYN_SEL 0x01000000 /* R/O */ -#define BPCM_ZONE_CONTROL_PWR_OFF_STATE 0x02000000 /* R/O */ -#define BPCM_ZONE_CONTROL_PWR_ON_STATE 0x04000000 /* R/O */ -#define BPCM_ZONE_CONTROL_PWR_GOOD 0x08000000 /* R/O */ -#define BPCM_ZONE_CONTROL_DPG_PWR_STATE 0x10000000 /* R/O */ -#define BPCM_ZONE_CONTROL_MEM_PWR_STATE 0x20000000 /* R/O */ -#define BPCM_ZONE_CONTROL_ISO_STATE 0x40000000 /* R/O */ -#define BPCM_ZONE_CONTROL_RESET_STATE 0x80000000 /* R/O */ -#define BPCM_ZONE_CONFIG1 0x04 -#define BPCM_ZONE_CONFIG2 0x08 -#define BPCM_ZONE_FREQ_SCALAR_CONTROL 0x0c -#define BPCM_ZONE_SIZE 0x10 - -struct bcm_pmb { - struct device *dev; - void __iomem *base; - spinlock_t lock; - bool little_endian; - struct genpd_onecell_data genpd_onecell_data; -}; - -struct bcm_pmb_pd_data { - const char * const name; - int id; - u8 bus; - u8 device; -}; - -struct bcm_pmb_pm_domain { - struct bcm_pmb *pmb; - const struct bcm_pmb_pd_data *data; - struct generic_pm_domain genpd; -}; - -static int bcm_pmb_bpcm_read(struct bcm_pmb *pmb, int bus, u8 device, - int offset, u32 *val) -{ - void __iomem *base = pmb->base + bus * 0x20; - unsigned long flags; - int err; - - spin_lock_irqsave(&pmb->lock, flags); - err = bpcm_rd(base, device, offset, val); - spin_unlock_irqrestore(&pmb->lock, flags); - - if (!err) - *val = pmb->little_endian ? le32_to_cpu(*val) : be32_to_cpu(*val); - - return err; -} - -static int bcm_pmb_bpcm_write(struct bcm_pmb *pmb, int bus, u8 device, - int offset, u32 val) -{ - void __iomem *base = pmb->base + bus * 0x20; - unsigned long flags; - int err; - - val = pmb->little_endian ? cpu_to_le32(val) : cpu_to_be32(val); - - spin_lock_irqsave(&pmb->lock, flags); - err = bpcm_wr(base, device, offset, val); - spin_unlock_irqrestore(&pmb->lock, flags); - - return err; -} - -static int bcm_pmb_power_off_zone(struct bcm_pmb *pmb, int bus, u8 device, - int zone) -{ - int offset; - u32 val; - int err; - - offset = BPCM_ZONE0 + zone * BPCM_ZONE_SIZE + BPCM_ZONE_CONTROL; - - err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val); - if (err) - return err; - - val |= BPCM_ZONE_CONTROL_PWR_DN_REQ; - val &= ~BPCM_ZONE_CONTROL_PWR_UP_REQ; - - err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val); - - return err; -} - -static int bcm_pmb_power_on_zone(struct bcm_pmb *pmb, int bus, u8 device, - int zone) -{ - int offset; - u32 val; - int err; - - offset = BPCM_ZONE0 + zone * BPCM_ZONE_SIZE + BPCM_ZONE_CONTROL; - - err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val); - if (err) - return err; - - if (!(val & BPCM_ZONE_CONTROL_PWR_ON_STATE)) { - val &= ~BPCM_ZONE_CONTROL_PWR_DN_REQ; - val |= BPCM_ZONE_CONTROL_DPG_CTL_EN; - val |= BPCM_ZONE_CONTROL_PWR_UP_REQ; - val |= BPCM_ZONE_CONTROL_MEM_PWR_CTL_EN; - val |= BPCM_ZONE_CONTROL_BLK_RESET_ASSERT; - - err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val); - } - - return err; -} - -static int bcm_pmb_power_off_device(struct bcm_pmb *pmb, int bus, u8 device) -{ - int offset; - u32 val; - int err; - - /* Entire device can be powered off by powering off the 0th zone */ - offset = BPCM_ZONE0 + BPCM_ZONE_CONTROL; - - err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val); - if (err) - return err; - - if (!(val & BPCM_ZONE_CONTROL_PWR_OFF_STATE)) { - val = BPCM_ZONE_CONTROL_PWR_DN_REQ; - - err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val); - } - - return err; -} - -static int bcm_pmb_power_on_device(struct bcm_pmb *pmb, int bus, u8 device) -{ - u32 val; - int err; - int i; - - err = bcm_pmb_bpcm_read(pmb, bus, device, BPCM_CAPABILITIES, &val); - if (err) - return err; - - for (i = 0; i < (val & BPCM_CAP_NUM_ZONES); i++) { - err = bcm_pmb_power_on_zone(pmb, bus, device, i); - if (err) - return err; - } - - return err; -} - -static int bcm_pmb_power_on_sata(struct bcm_pmb *pmb, int bus, u8 device) -{ - int err; - - err = bcm_pmb_power_on_zone(pmb, bus, device, 0); - if (err) - return err; - - /* Does not apply to the BCM963158 */ - err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_MISC_CONTROL, 0); - if (err) - return err; - - err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_SR_CONTROL, 0xffffffff); - if (err) - return err; - - err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_SR_CONTROL, 0); - - return err; -} - -static int bcm_pmb_power_on(struct generic_pm_domain *genpd) -{ - struct bcm_pmb_pm_domain *pd = container_of(genpd, struct bcm_pmb_pm_domain, genpd); - const struct bcm_pmb_pd_data *data = pd->data; - struct bcm_pmb *pmb = pd->pmb; - - switch (data->id) { - case BCM_PMB_PCIE0: - case BCM_PMB_PCIE1: - case BCM_PMB_PCIE2: - return bcm_pmb_power_on_zone(pmb, data->bus, data->device, 0); - case BCM_PMB_HOST_USB: - return bcm_pmb_power_on_device(pmb, data->bus, data->device); - case BCM_PMB_SATA: - return bcm_pmb_power_on_sata(pmb, data->bus, data->device); - default: - dev_err(pmb->dev, "unsupported device id: %d\n", data->id); - return -EINVAL; - } -} - -static int bcm_pmb_power_off(struct generic_pm_domain *genpd) -{ - struct bcm_pmb_pm_domain *pd = container_of(genpd, struct bcm_pmb_pm_domain, genpd); - const struct bcm_pmb_pd_data *data = pd->data; - struct bcm_pmb *pmb = pd->pmb; - - switch (data->id) { - case BCM_PMB_PCIE0: - case BCM_PMB_PCIE1: - case BCM_PMB_PCIE2: - return bcm_pmb_power_off_zone(pmb, data->bus, data->device, 0); - case BCM_PMB_HOST_USB: - return bcm_pmb_power_off_device(pmb, data->bus, data->device); - default: - dev_err(pmb->dev, "unsupported device id: %d\n", data->id); - return -EINVAL; - } -} - -static int bcm_pmb_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct bcm_pmb_pd_data *table; - const struct bcm_pmb_pd_data *e; - struct bcm_pmb *pmb; - int max_id; - int err; - - pmb = devm_kzalloc(dev, sizeof(*pmb), GFP_KERNEL); - if (!pmb) - return -ENOMEM; - - pmb->dev = dev; - - pmb->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(pmb->base)) - return PTR_ERR(pmb->base); - - spin_lock_init(&pmb->lock); - - pmb->little_endian = !of_device_is_big_endian(dev->of_node); - - table = of_device_get_match_data(dev); - if (!table) - return -EINVAL; - - max_id = 0; - for (e = table; e->name; e++) - max_id = max(max_id, e->id); - - pmb->genpd_onecell_data.num_domains = max_id + 1; - pmb->genpd_onecell_data.domains = - devm_kcalloc(dev, pmb->genpd_onecell_data.num_domains, - sizeof(struct generic_pm_domain *), GFP_KERNEL); - if (!pmb->genpd_onecell_data.domains) - return -ENOMEM; - - for (e = table; e->name; e++) { - struct bcm_pmb_pm_domain *pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - - if (!pd) - return -ENOMEM; - - pd->pmb = pmb; - pd->data = e; - pd->genpd.name = e->name; - pd->genpd.power_on = bcm_pmb_power_on; - pd->genpd.power_off = bcm_pmb_power_off; - - pm_genpd_init(&pd->genpd, NULL, true); - pmb->genpd_onecell_data.domains[e->id] = &pd->genpd; - } - - err = of_genpd_add_provider_onecell(dev->of_node, &pmb->genpd_onecell_data); - if (err) { - dev_err(dev, "failed to add genpd provider: %d\n", err); - return err; - } - - return 0; -} - -static const struct bcm_pmb_pd_data bcm_pmb_bcm4908_data[] = { - { .name = "pcie2", .id = BCM_PMB_PCIE2, .bus = 0, .device = 2, }, - { .name = "pcie0", .id = BCM_PMB_PCIE0, .bus = 1, .device = 14, }, - { .name = "pcie1", .id = BCM_PMB_PCIE1, .bus = 1, .device = 15, }, - { .name = "usb", .id = BCM_PMB_HOST_USB, .bus = 1, .device = 17, }, - { }, -}; - -static const struct bcm_pmb_pd_data bcm_pmb_bcm63138_data[] = { - { .name = "sata", .id = BCM_PMB_SATA, .bus = 0, .device = 3, }, - { }, -}; - -static const struct of_device_id bcm_pmb_of_match[] = { - { .compatible = "brcm,bcm4908-pmb", .data = &bcm_pmb_bcm4908_data, }, - { .compatible = "brcm,bcm63138-pmb", .data = &bcm_pmb_bcm63138_data, }, - { }, -}; - -static struct platform_driver bcm_pmb_driver = { - .driver = { - .name = "bcm-pmb", - .of_match_table = bcm_pmb_of_match, - }, - .probe = bcm_pmb_probe, -}; - -builtin_platform_driver(bcm_pmb_driver); diff --git a/drivers/genpd/bcm/bcm2835-power.c b/drivers/genpd/bcm/bcm2835-power.c deleted file mode 100644 index 1a179d4e011c..000000000000 --- a/drivers/genpd/bcm/bcm2835-power.c +++ /dev/null @@ -1,713 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Power domain driver for Broadcom BCM2835 - * - * Copyright (C) 2018 Broadcom - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PM_GNRIC 0x00 -#define PM_AUDIO 0x04 -#define PM_STATUS 0x18 -#define PM_RSTC 0x1c -#define PM_RSTS 0x20 -#define PM_WDOG 0x24 -#define PM_PADS0 0x28 -#define PM_PADS2 0x2c -#define PM_PADS3 0x30 -#define PM_PADS4 0x34 -#define PM_PADS5 0x38 -#define PM_PADS6 0x3c -#define PM_CAM0 0x44 -#define PM_CAM0_LDOHPEN BIT(2) -#define PM_CAM0_LDOLPEN BIT(1) -#define PM_CAM0_CTRLEN BIT(0) - -#define PM_CAM1 0x48 -#define PM_CAM1_LDOHPEN BIT(2) -#define PM_CAM1_LDOLPEN BIT(1) -#define PM_CAM1_CTRLEN BIT(0) - -#define PM_CCP2TX 0x4c -#define PM_CCP2TX_LDOEN BIT(1) -#define PM_CCP2TX_CTRLEN BIT(0) - -#define PM_DSI0 0x50 -#define PM_DSI0_LDOHPEN BIT(2) -#define PM_DSI0_LDOLPEN BIT(1) -#define PM_DSI0_CTRLEN BIT(0) - -#define PM_DSI1 0x54 -#define PM_DSI1_LDOHPEN BIT(2) -#define PM_DSI1_LDOLPEN BIT(1) -#define PM_DSI1_CTRLEN BIT(0) - -#define PM_HDMI 0x58 -#define PM_HDMI_RSTDR BIT(19) -#define PM_HDMI_LDOPD BIT(1) -#define PM_HDMI_CTRLEN BIT(0) - -#define PM_USB 0x5c -/* The power gates must be enabled with this bit before enabling the LDO in the - * USB block. - */ -#define PM_USB_CTRLEN BIT(0) - -#define PM_PXLDO 0x60 -#define PM_PXBG 0x64 -#define PM_DFT 0x68 -#define PM_SMPS 0x6c -#define PM_XOSC 0x70 -#define PM_SPAREW 0x74 -#define PM_SPARER 0x78 -#define PM_AVS_RSTDR 0x7c -#define PM_AVS_STAT 0x80 -#define PM_AVS_EVENT 0x84 -#define PM_AVS_INTEN 0x88 -#define PM_DUMMY 0xfc - -#define PM_IMAGE 0x108 -#define PM_GRAFX 0x10c -#define PM_PROC 0x110 -#define PM_ENAB BIT(12) -#define PM_ISPRSTN BIT(8) -#define PM_H264RSTN BIT(7) -#define PM_PERIRSTN BIT(6) -#define PM_V3DRSTN BIT(6) -#define PM_ISFUNC BIT(5) -#define PM_MRDONE BIT(4) -#define PM_MEMREP BIT(3) -#define PM_ISPOW BIT(2) -#define PM_POWOK BIT(1) -#define PM_POWUP BIT(0) -#define PM_INRUSH_SHIFT 13 -#define PM_INRUSH_3_5_MA 0 -#define PM_INRUSH_5_MA 1 -#define PM_INRUSH_10_MA 2 -#define PM_INRUSH_20_MA 3 -#define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT) - -#define PM_PASSWORD 0x5a000000 - -#define PM_WDOG_TIME_SET 0x000fffff -#define PM_RSTC_WRCFG_CLR 0xffffffcf -#define PM_RSTS_HADWRH_SET 0x00000040 -#define PM_RSTC_WRCFG_SET 0x00000030 -#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 -#define PM_RSTC_RESET 0x00000102 - -#define PM_READ(reg) readl(power->base + (reg)) -#define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg)) - -#define ASB_BRDG_VERSION 0x00 -#define ASB_CPR_CTRL 0x04 - -#define ASB_V3D_S_CTRL 0x08 -#define ASB_V3D_M_CTRL 0x0c -#define ASB_ISP_S_CTRL 0x10 -#define ASB_ISP_M_CTRL 0x14 -#define ASB_H264_S_CTRL 0x18 -#define ASB_H264_M_CTRL 0x1c - -#define ASB_REQ_STOP BIT(0) -#define ASB_ACK BIT(1) -#define ASB_EMPTY BIT(2) -#define ASB_FULL BIT(3) - -#define ASB_AXI_BRDG_ID 0x20 - -#define BCM2835_BRDG_ID 0x62726467 - -struct bcm2835_power_domain { - struct generic_pm_domain base; - struct bcm2835_power *power; - u32 domain; - struct clk *clk; -}; - -struct bcm2835_power { - struct device *dev; - /* PM registers. */ - void __iomem *base; - /* AXI Async bridge registers. */ - void __iomem *asb; - /* RPiVid bridge registers. */ - void __iomem *rpivid_asb; - - struct genpd_onecell_data pd_xlate; - struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; - struct reset_controller_dev reset; -}; - -static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable) -{ - void __iomem *base = power->asb; - u64 start; - u32 val; - - switch (reg) { - case 0: - return 0; - case ASB_V3D_S_CTRL: - case ASB_V3D_M_CTRL: - if (power->rpivid_asb) - base = power->rpivid_asb; - break; - } - - start = ktime_get_ns(); - - /* Enable the module's async AXI bridges. */ - if (enable) { - val = readl(base + reg) & ~ASB_REQ_STOP; - } else { - val = readl(base + reg) | ASB_REQ_STOP; - } - writel(PM_PASSWORD | val, base + reg); - - while (readl(base + reg) & ASB_ACK) { - cpu_relax(); - if (ktime_get_ns() - start >= 1000) - return -ETIMEDOUT; - } - - return 0; -} - -static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) -{ - return bcm2835_asb_control(power, reg, true); -} - -static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) -{ - return bcm2835_asb_control(power, reg, false); -} - -static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) -{ - struct bcm2835_power *power = pd->power; - - /* We don't run this on BCM2711 */ - if (power->rpivid_asb) - return 0; - - /* Enable functional isolation */ - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); - - /* Enable electrical isolation */ - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); - - /* Open the power switches. */ - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP); - - return 0; -} - -static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) -{ - struct bcm2835_power *power = pd->power; - struct device *dev = power->dev; - u64 start; - int ret; - int inrush; - bool powok; - - /* We don't run this on BCM2711 */ - if (power->rpivid_asb) - return 0; - - /* If it was already powered on by the fw, leave it that way. */ - if (PM_READ(pm_reg) & PM_POWUP) - return 0; - - /* Enable power. Allowing too much current at once may result - * in POWOK never getting set, so start low and ramp it up as - * necessary to succeed. - */ - powok = false; - for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) { - PM_WRITE(pm_reg, - (PM_READ(pm_reg) & ~PM_INRUSH_MASK) | - (inrush << PM_INRUSH_SHIFT) | - PM_POWUP); - - start = ktime_get_ns(); - while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) { - cpu_relax(); - if (ktime_get_ns() - start >= 3000) - break; - } - } - if (!powok) { - dev_err(dev, "Timeout waiting for %s power OK\n", - pd->base.name); - ret = -ETIMEDOUT; - goto err_disable_powup; - } - - /* Disable electrical isolation */ - PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW); - - /* Repair memory */ - PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP); - start = ktime_get_ns(); - while (!(PM_READ(pm_reg) & PM_MRDONE)) { - cpu_relax(); - if (ktime_get_ns() - start >= 1000) { - dev_err(dev, "Timeout waiting for %s memory repair\n", - pd->base.name); - ret = -ETIMEDOUT; - goto err_disable_ispow; - } - } - - /* Disable functional isolation */ - PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC); - - return 0; - -err_disable_ispow: - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); -err_disable_powup: - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK)); - return ret; -} - -static int bcm2835_asb_power_on(struct bcm2835_power_domain *pd, - u32 pm_reg, - u32 asb_m_reg, - u32 asb_s_reg, - u32 reset_flags) -{ - struct bcm2835_power *power = pd->power; - int ret; - - ret = clk_prepare_enable(pd->clk); - if (ret) { - dev_err(power->dev, "Failed to enable clock for %s\n", - pd->base.name); - return ret; - } - - /* Wait 32 clocks for reset to propagate, 1 us will be enough */ - udelay(1); - - clk_disable_unprepare(pd->clk); - - /* Deassert the resets. */ - PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags); - - ret = clk_prepare_enable(pd->clk); - if (ret) { - dev_err(power->dev, "Failed to enable clock for %s\n", - pd->base.name); - goto err_enable_resets; - } - - ret = bcm2835_asb_enable(power, asb_m_reg); - if (ret) { - dev_err(power->dev, "Failed to enable ASB master for %s\n", - pd->base.name); - goto err_disable_clk; - } - ret = bcm2835_asb_enable(power, asb_s_reg); - if (ret) { - dev_err(power->dev, "Failed to enable ASB slave for %s\n", - pd->base.name); - goto err_disable_asb_master; - } - - return 0; - -err_disable_asb_master: - bcm2835_asb_disable(power, asb_m_reg); -err_disable_clk: - clk_disable_unprepare(pd->clk); -err_enable_resets: - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); - return ret; -} - -static int bcm2835_asb_power_off(struct bcm2835_power_domain *pd, - u32 pm_reg, - u32 asb_m_reg, - u32 asb_s_reg, - u32 reset_flags) -{ - struct bcm2835_power *power = pd->power; - int ret; - - ret = bcm2835_asb_disable(power, asb_s_reg); - if (ret) { - dev_warn(power->dev, "Failed to disable ASB slave for %s\n", - pd->base.name); - return ret; - } - ret = bcm2835_asb_disable(power, asb_m_reg); - if (ret) { - dev_warn(power->dev, "Failed to disable ASB master for %s\n", - pd->base.name); - bcm2835_asb_enable(power, asb_s_reg); - return ret; - } - - clk_disable_unprepare(pd->clk); - - /* Assert the resets. */ - PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); - - return 0; -} - -static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain) -{ - struct bcm2835_power_domain *pd = - container_of(domain, struct bcm2835_power_domain, base); - struct bcm2835_power *power = pd->power; - - switch (pd->domain) { - case BCM2835_POWER_DOMAIN_GRAFX: - return bcm2835_power_power_on(pd, PM_GRAFX); - - case BCM2835_POWER_DOMAIN_GRAFX_V3D: - return bcm2835_asb_power_on(pd, PM_GRAFX, - ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, - PM_V3DRSTN); - - case BCM2835_POWER_DOMAIN_IMAGE: - return bcm2835_power_power_on(pd, PM_IMAGE); - - case BCM2835_POWER_DOMAIN_IMAGE_PERI: - return bcm2835_asb_power_on(pd, PM_IMAGE, - 0, 0, - PM_PERIRSTN); - - case BCM2835_POWER_DOMAIN_IMAGE_ISP: - return bcm2835_asb_power_on(pd, PM_IMAGE, - ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, - PM_ISPRSTN); - - case BCM2835_POWER_DOMAIN_IMAGE_H264: - return bcm2835_asb_power_on(pd, PM_IMAGE, - ASB_H264_M_CTRL, ASB_H264_S_CTRL, - PM_H264RSTN); - - case BCM2835_POWER_DOMAIN_USB: - PM_WRITE(PM_USB, PM_USB_CTRLEN); - return 0; - - case BCM2835_POWER_DOMAIN_DSI0: - PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); - PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN); - return 0; - - case BCM2835_POWER_DOMAIN_DSI1: - PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); - PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN); - return 0; - - case BCM2835_POWER_DOMAIN_CCP2TX: - PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); - PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN); - return 0; - - case BCM2835_POWER_DOMAIN_HDMI: - PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR); - PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN); - PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD); - usleep_range(100, 200); - PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR); - return 0; - - default: - dev_err(power->dev, "Invalid domain %d\n", pd->domain); - return -EINVAL; - } -} - -static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain) -{ - struct bcm2835_power_domain *pd = - container_of(domain, struct bcm2835_power_domain, base); - struct bcm2835_power *power = pd->power; - - switch (pd->domain) { - case BCM2835_POWER_DOMAIN_GRAFX: - return bcm2835_power_power_off(pd, PM_GRAFX); - - case BCM2835_POWER_DOMAIN_GRAFX_V3D: - return bcm2835_asb_power_off(pd, PM_GRAFX, - ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, - PM_V3DRSTN); - - case BCM2835_POWER_DOMAIN_IMAGE: - return bcm2835_power_power_off(pd, PM_IMAGE); - - case BCM2835_POWER_DOMAIN_IMAGE_PERI: - return bcm2835_asb_power_off(pd, PM_IMAGE, - 0, 0, - PM_PERIRSTN); - - case BCM2835_POWER_DOMAIN_IMAGE_ISP: - return bcm2835_asb_power_off(pd, PM_IMAGE, - ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, - PM_ISPRSTN); - - case BCM2835_POWER_DOMAIN_IMAGE_H264: - return bcm2835_asb_power_off(pd, PM_IMAGE, - ASB_H264_M_CTRL, ASB_H264_S_CTRL, - PM_H264RSTN); - - case BCM2835_POWER_DOMAIN_USB: - PM_WRITE(PM_USB, 0); - return 0; - - case BCM2835_POWER_DOMAIN_DSI0: - PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); - PM_WRITE(PM_DSI0, 0); - return 0; - - case BCM2835_POWER_DOMAIN_DSI1: - PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); - PM_WRITE(PM_DSI1, 0); - return 0; - - case BCM2835_POWER_DOMAIN_CCP2TX: - PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); - PM_WRITE(PM_CCP2TX, 0); - return 0; - - case BCM2835_POWER_DOMAIN_HDMI: - PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD); - PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN); - return 0; - - default: - dev_err(power->dev, "Invalid domain %d\n", pd->domain); - return -EINVAL; - } -} - -static int -bcm2835_init_power_domain(struct bcm2835_power *power, - int pd_xlate_index, const char *name) -{ - struct device *dev = power->dev; - struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index]; - - dom->clk = devm_clk_get(dev->parent, name); - if (IS_ERR(dom->clk)) { - int ret = PTR_ERR(dom->clk); - - if (ret == -EPROBE_DEFER) - return ret; - - /* Some domains don't have a clk, so make sure that we - * don't deref an error pointer later. - */ - dom->clk = NULL; - } - - dom->base.name = name; - dom->base.power_on = bcm2835_power_pd_power_on; - dom->base.power_off = bcm2835_power_pd_power_off; - - dom->domain = pd_xlate_index; - dom->power = power; - - /* XXX: on/off at boot? */ - pm_genpd_init(&dom->base, NULL, true); - - power->pd_xlate.domains[pd_xlate_index] = &dom->base; - - return 0; -} - -/** bcm2835_reset_reset - Resets a block that has a reset line in the - * PM block. - * - * The consumer of the reset controller must have the power domain up - * -- there's no reset ability with the power domain down. To reset - * the sub-block, we just disable its access to memory through the - * ASB, reset, and re-enable. - */ -static int bcm2835_reset_reset(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, - reset); - struct bcm2835_power_domain *pd; - int ret; - - switch (id) { - case BCM2835_RESET_V3D: - pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D]; - break; - case BCM2835_RESET_H264: - pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264]; - break; - case BCM2835_RESET_ISP: - pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP]; - break; - default: - dev_err(power->dev, "Bad reset id %ld\n", id); - return -EINVAL; - } - - ret = bcm2835_power_pd_power_off(&pd->base); - if (ret) - return ret; - - return bcm2835_power_pd_power_on(&pd->base); -} - -static int bcm2835_reset_status(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, - reset); - - switch (id) { - case BCM2835_RESET_V3D: - return !PM_READ(PM_GRAFX & PM_V3DRSTN); - case BCM2835_RESET_H264: - return !PM_READ(PM_IMAGE & PM_H264RSTN); - case BCM2835_RESET_ISP: - return !PM_READ(PM_IMAGE & PM_ISPRSTN); - default: - return -EINVAL; - } -} - -static const struct reset_control_ops bcm2835_reset_ops = { - .reset = bcm2835_reset_reset, - .status = bcm2835_reset_status, -}; - -static const char *const power_domain_names[] = { - [BCM2835_POWER_DOMAIN_GRAFX] = "grafx", - [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d", - - [BCM2835_POWER_DOMAIN_IMAGE] = "image", - [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image", - [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264", - [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp", - - [BCM2835_POWER_DOMAIN_USB] = "usb", - [BCM2835_POWER_DOMAIN_DSI0] = "dsi0", - [BCM2835_POWER_DOMAIN_DSI1] = "dsi1", - [BCM2835_POWER_DOMAIN_CAM0] = "cam0", - [BCM2835_POWER_DOMAIN_CAM1] = "cam1", - [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx", - [BCM2835_POWER_DOMAIN_HDMI] = "hdmi", -}; - -static int bcm2835_power_probe(struct platform_device *pdev) -{ - struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); - struct device *dev = &pdev->dev; - struct bcm2835_power *power; - static const struct { - int parent, child; - } domain_deps[] = { - { BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D }, - { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI }, - { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 }, - { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP }, - { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB }, - { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 }, - { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 }, - }; - int ret = 0, i; - u32 id; - - power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); - if (!power) - return -ENOMEM; - platform_set_drvdata(pdev, power); - - power->dev = dev; - power->base = pm->base; - power->asb = pm->asb; - power->rpivid_asb = pm->rpivid_asb; - - id = readl(power->asb + ASB_AXI_BRDG_ID); - if (id != BCM2835_BRDG_ID /* "BRDG" */) { - dev_err(dev, "ASB register ID returned 0x%08x\n", id); - return -ENODEV; - } - - if (power->rpivid_asb) { - id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID); - if (id != BCM2835_BRDG_ID /* "BRDG" */) { - dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n", - id); - return -ENODEV; - } - } - - power->pd_xlate.domains = devm_kcalloc(dev, - ARRAY_SIZE(power_domain_names), - sizeof(*power->pd_xlate.domains), - GFP_KERNEL); - if (!power->pd_xlate.domains) - return -ENOMEM; - - power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names); - - for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { - ret = bcm2835_init_power_domain(power, i, power_domain_names[i]); - if (ret) - goto fail; - } - - for (i = 0; i < ARRAY_SIZE(domain_deps); i++) { - pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base, - &power->domains[domain_deps[i].child].base); - } - - power->reset.owner = THIS_MODULE; - power->reset.nr_resets = BCM2835_RESET_COUNT; - power->reset.ops = &bcm2835_reset_ops; - power->reset.of_node = dev->parent->of_node; - - ret = devm_reset_controller_register(dev, &power->reset); - if (ret) - goto fail; - - of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate); - - dev_info(dev, "Broadcom BCM2835 power domains driver"); - return 0; - -fail: - for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { - struct generic_pm_domain *dom = &power->domains[i].base; - - if (dom->name) - pm_genpd_remove(dom); - } - return ret; -} - -static struct platform_driver bcm2835_power_driver = { - .probe = bcm2835_power_probe, - .driver = { - .name = "bcm2835-power", - }, -}; -module_platform_driver(bcm2835_power_driver); - -MODULE_AUTHOR("Eric Anholt "); -MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset"); diff --git a/drivers/genpd/bcm/bcm63xx-power.c b/drivers/genpd/bcm/bcm63xx-power.c deleted file mode 100644 index 98b0c2430dbc..000000000000 --- a/drivers/genpd/bcm/bcm63xx-power.c +++ /dev/null @@ -1,375 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * BCM63xx Power Domain Controller Driver - * - * Copyright (C) 2020 Álvaro Fernández Rojas - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct bcm63xx_power_dev { - struct generic_pm_domain genpd; - struct bcm63xx_power *power; - uint32_t mask; -}; - -struct bcm63xx_power { - void __iomem *base; - spinlock_t lock; - struct bcm63xx_power_dev *dev; - struct genpd_onecell_data genpd_data; - struct generic_pm_domain **genpd; -}; - -struct bcm63xx_power_data { - const char * const name; - uint8_t bit; - unsigned int flags; -}; - -static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on) -{ - struct bcm63xx_power *power = pmd->power; - - if (!pmd->mask) { - *is_on = false; - return -EINVAL; - } - - *is_on = !(__raw_readl(power->base) & pmd->mask); - - return 0; -} - -static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on) -{ - struct bcm63xx_power *power = pmd->power; - unsigned long flags; - uint32_t val; - - if (!pmd->mask) - return -EINVAL; - - spin_lock_irqsave(&power->lock, flags); - val = __raw_readl(power->base); - if (on) - val &= ~pmd->mask; - else - val |= pmd->mask; - __raw_writel(val, power->base); - spin_unlock_irqrestore(&power->lock, flags); - - return 0; -} - -static int bcm63xx_power_on(struct generic_pm_domain *genpd) -{ - struct bcm63xx_power_dev *pmd = container_of(genpd, - struct bcm63xx_power_dev, genpd); - - return bcm63xx_power_set_state(pmd, true); -} - -static int bcm63xx_power_off(struct generic_pm_domain *genpd) -{ - struct bcm63xx_power_dev *pmd = container_of(genpd, - struct bcm63xx_power_dev, genpd); - - return bcm63xx_power_set_state(pmd, false); -} - -static int bcm63xx_power_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - const struct bcm63xx_power_data *entry, *table; - struct bcm63xx_power *power; - unsigned int ndom; - uint8_t max_bit = 0; - int ret; - - power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); - if (!power) - return -ENOMEM; - - power->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(power->base)) - return PTR_ERR(power->base); - - table = of_device_get_match_data(dev); - if (!table) - return -EINVAL; - - power->genpd_data.num_domains = 0; - ndom = 0; - for (entry = table; entry->name; entry++) { - max_bit = max(max_bit, entry->bit); - ndom++; - } - - if (!ndom) - return -ENODEV; - - power->genpd_data.num_domains = max_bit + 1; - - power->dev = devm_kcalloc(dev, power->genpd_data.num_domains, - sizeof(struct bcm63xx_power_dev), - GFP_KERNEL); - if (!power->dev) - return -ENOMEM; - - power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains, - sizeof(struct generic_pm_domain *), - GFP_KERNEL); - if (!power->genpd) - return -ENOMEM; - - power->genpd_data.domains = power->genpd; - - ndom = 0; - for (entry = table; entry->name; entry++) { - struct bcm63xx_power_dev *pmd = &power->dev[ndom]; - bool is_on; - - pmd->power = power; - pmd->mask = BIT(entry->bit); - pmd->genpd.name = entry->name; - pmd->genpd.flags = entry->flags; - - ret = bcm63xx_power_get_state(pmd, &is_on); - if (ret) - dev_warn(dev, "unable to get current state for %s\n", - pmd->genpd.name); - - pmd->genpd.power_on = bcm63xx_power_on; - pmd->genpd.power_off = bcm63xx_power_off; - - pm_genpd_init(&pmd->genpd, NULL, !is_on); - power->genpd[entry->bit] = &pmd->genpd; - - ndom++; - } - - spin_lock_init(&power->lock); - - ret = of_genpd_add_provider_onecell(np, &power->genpd_data); - if (ret) { - dev_err(dev, "failed to register genpd driver: %d\n", ret); - return ret; - } - - dev_info(dev, "registered %u power domains\n", ndom); - - return 0; -} - -static const struct bcm63xx_power_data bcm6318_power_domains[] = { - { - .name = "pcie", - .bit = BCM6318_POWER_DOMAIN_PCIE, - }, { - .name = "usb", - .bit = BCM6318_POWER_DOMAIN_USB, - }, { - .name = "ephy0", - .bit = BCM6318_POWER_DOMAIN_EPHY0, - }, { - .name = "ephy1", - .bit = BCM6318_POWER_DOMAIN_EPHY1, - }, { - .name = "ephy2", - .bit = BCM6318_POWER_DOMAIN_EPHY2, - }, { - .name = "ephy3", - .bit = BCM6318_POWER_DOMAIN_EPHY3, - }, { - .name = "ldo2p5", - .bit = BCM6318_POWER_DOMAIN_LDO2P5, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "ldo2p9", - .bit = BCM6318_POWER_DOMAIN_LDO2P9, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "sw1p0", - .bit = BCM6318_POWER_DOMAIN_SW1P0, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "pad", - .bit = BCM6318_POWER_DOMAIN_PAD, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - /* sentinel */ - }, -}; - -static const struct bcm63xx_power_data bcm6328_power_domains[] = { - { - .name = "adsl2-mips", - .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS, - }, { - .name = "adsl2-phy", - .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY, - }, { - .name = "adsl2-afe", - .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE, - }, { - .name = "sar", - .bit = BCM6328_POWER_DOMAIN_SAR, - }, { - .name = "pcm", - .bit = BCM6328_POWER_DOMAIN_PCM, - }, { - .name = "usbd", - .bit = BCM6328_POWER_DOMAIN_USBD, - }, { - .name = "usbh", - .bit = BCM6328_POWER_DOMAIN_USBH, - }, { - .name = "pcie", - .bit = BCM6328_POWER_DOMAIN_PCIE, - }, { - .name = "robosw", - .bit = BCM6328_POWER_DOMAIN_ROBOSW, - }, { - .name = "ephy", - .bit = BCM6328_POWER_DOMAIN_EPHY, - }, { - /* sentinel */ - }, -}; - -static const struct bcm63xx_power_data bcm6362_power_domains[] = { - { - .name = "sar", - .bit = BCM6362_POWER_DOMAIN_SAR, - }, { - .name = "ipsec", - .bit = BCM6362_POWER_DOMAIN_IPSEC, - }, { - .name = "mips", - .bit = BCM6362_POWER_DOMAIN_MIPS, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "dect", - .bit = BCM6362_POWER_DOMAIN_DECT, - }, { - .name = "usbh", - .bit = BCM6362_POWER_DOMAIN_USBH, - }, { - .name = "usbd", - .bit = BCM6362_POWER_DOMAIN_USBD, - }, { - .name = "robosw", - .bit = BCM6362_POWER_DOMAIN_ROBOSW, - }, { - .name = "pcm", - .bit = BCM6362_POWER_DOMAIN_PCM, - }, { - .name = "periph", - .bit = BCM6362_POWER_DOMAIN_PERIPH, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "adsl-phy", - .bit = BCM6362_POWER_DOMAIN_ADSL_PHY, - }, { - .name = "gmii-pads", - .bit = BCM6362_POWER_DOMAIN_GMII_PADS, - }, { - .name = "fap", - .bit = BCM6362_POWER_DOMAIN_FAP, - }, { - .name = "pcie", - .bit = BCM6362_POWER_DOMAIN_PCIE, - }, { - .name = "wlan-pads", - .bit = BCM6362_POWER_DOMAIN_WLAN_PADS, - }, { - /* sentinel */ - }, -}; - -static const struct bcm63xx_power_data bcm63268_power_domains[] = { - { - .name = "sar", - .bit = BCM63268_POWER_DOMAIN_SAR, - }, { - .name = "ipsec", - .bit = BCM63268_POWER_DOMAIN_IPSEC, - }, { - .name = "mips", - .bit = BCM63268_POWER_DOMAIN_MIPS, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "dect", - .bit = BCM63268_POWER_DOMAIN_DECT, - }, { - .name = "usbh", - .bit = BCM63268_POWER_DOMAIN_USBH, - }, { - .name = "usbd", - .bit = BCM63268_POWER_DOMAIN_USBD, - }, { - .name = "robosw", - .bit = BCM63268_POWER_DOMAIN_ROBOSW, - }, { - .name = "pcm", - .bit = BCM63268_POWER_DOMAIN_PCM, - }, { - .name = "periph", - .bit = BCM63268_POWER_DOMAIN_PERIPH, - .flags = GENPD_FLAG_ALWAYS_ON, - }, { - .name = "vdsl-phy", - .bit = BCM63268_POWER_DOMAIN_VDSL_PHY, - }, { - .name = "vdsl-mips", - .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS, - }, { - .name = "fap", - .bit = BCM63268_POWER_DOMAIN_FAP, - }, { - .name = "pcie", - .bit = BCM63268_POWER_DOMAIN_PCIE, - }, { - .name = "wlan-pads", - .bit = BCM63268_POWER_DOMAIN_WLAN_PADS, - }, { - /* sentinel */ - }, -}; - -static const struct of_device_id bcm63xx_power_of_match[] = { - { - .compatible = "brcm,bcm6318-power-controller", - .data = &bcm6318_power_domains, - }, { - .compatible = "brcm,bcm6328-power-controller", - .data = &bcm6328_power_domains, - }, { - .compatible = "brcm,bcm6362-power-controller", - .data = &bcm6362_power_domains, - }, { - .compatible = "brcm,bcm63268-power-controller", - .data = &bcm63268_power_domains, - }, { - /* sentinel */ - } -}; - -static struct platform_driver bcm63xx_power_driver = { - .driver = { - .name = "bcm63xx-power-controller", - .of_match_table = bcm63xx_power_of_match, - }, - .probe = bcm63xx_power_probe, -}; -builtin_platform_driver(bcm63xx_power_driver); diff --git a/drivers/genpd/bcm/raspberrypi-power.c b/drivers/genpd/bcm/raspberrypi-power.c deleted file mode 100644 index 06196ebfe03b..000000000000 --- a/drivers/genpd/bcm/raspberrypi-power.c +++ /dev/null @@ -1,245 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* (C) 2015 Pengutronix, Alexander Aring - * - * Authors: - * Alexander Aring - * Eric Anholt - */ - -#include -#include -#include -#include -#include -#include - -/* - * Firmware indices for the old power domains interface. Only a few - * of them were actually implemented. - */ -#define RPI_OLD_POWER_DOMAIN_USB 3 -#define RPI_OLD_POWER_DOMAIN_V3D 10 - -struct rpi_power_domain { - u32 domain; - bool enabled; - bool old_interface; - struct generic_pm_domain base; - struct rpi_firmware *fw; -}; - -struct rpi_power_domains { - bool has_new_interface; - struct genpd_onecell_data xlate; - struct rpi_firmware *fw; - struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT]; -}; - -/* - * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and - * RPI_FIRMWARE_SET_DOMAIN_STATE - */ -struct rpi_power_domain_packet { - u32 domain; - u32 on; -}; - -/* - * Asks the firmware to enable or disable power on a specific power - * domain. - */ -static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on) -{ - struct rpi_power_domain_packet packet; - - packet.domain = rpi_domain->domain; - packet.on = on; - return rpi_firmware_property(rpi_domain->fw, - rpi_domain->old_interface ? - RPI_FIRMWARE_SET_POWER_STATE : - RPI_FIRMWARE_SET_DOMAIN_STATE, - &packet, sizeof(packet)); -} - -static int rpi_domain_off(struct generic_pm_domain *domain) -{ - struct rpi_power_domain *rpi_domain = - container_of(domain, struct rpi_power_domain, base); - - return rpi_firmware_set_power(rpi_domain, false); -} - -static int rpi_domain_on(struct generic_pm_domain *domain) -{ - struct rpi_power_domain *rpi_domain = - container_of(domain, struct rpi_power_domain, base); - - return rpi_firmware_set_power(rpi_domain, true); -} - -static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, - int xlate_index, const char *name) -{ - struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; - - dom->fw = rpi_domains->fw; - - dom->base.name = name; - dom->base.power_on = rpi_domain_on; - dom->base.power_off = rpi_domain_off; - - /* - * Treat all power domains as off at boot. - * - * The firmware itself may be keeping some domains on, but - * from Linux's perspective all we control is the refcounts - * that we give to the firmware, and we can't ask the firmware - * to turn off something that we haven't ourselves turned on. - */ - pm_genpd_init(&dom->base, NULL, true); - - rpi_domains->xlate.domains[xlate_index] = &dom->base; -} - -static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains, - int xlate_index, const char *name) -{ - struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; - - if (!rpi_domains->has_new_interface) - return; - - /* The DT binding index is the firmware's domain index minus one. */ - dom->domain = xlate_index + 1; - - rpi_common_init_power_domain(rpi_domains, xlate_index, name); -} - -static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains, - int xlate_index, int domain, - const char *name) -{ - struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; - - dom->old_interface = true; - dom->domain = domain; - - rpi_common_init_power_domain(rpi_domains, xlate_index, name); -} - -/* - * Detects whether the firmware supports the new power domains interface. - * - * The firmware doesn't actually return an error on an unknown tag, - * and just skips over it, so we do the detection by putting an - * unexpected value in the return field and checking if it was - * unchanged. - */ -static bool -rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains) -{ - struct rpi_power_domain_packet packet; - int ret; - - packet.domain = RPI_POWER_DOMAIN_ARM; - packet.on = ~0; - - ret = rpi_firmware_property(rpi_domains->fw, - RPI_FIRMWARE_GET_DOMAIN_STATE, - &packet, sizeof(packet)); - - return ret == 0 && packet.on != ~0; -} - -static int rpi_power_probe(struct platform_device *pdev) -{ - struct device_node *fw_np; - struct device *dev = &pdev->dev; - struct rpi_power_domains *rpi_domains; - - rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL); - if (!rpi_domains) - return -ENOMEM; - - rpi_domains->xlate.domains = - devm_kcalloc(dev, - RPI_POWER_DOMAIN_COUNT, - sizeof(*rpi_domains->xlate.domains), - GFP_KERNEL); - if (!rpi_domains->xlate.domains) - return -ENOMEM; - - rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT; - - fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0); - if (!fw_np) { - dev_err(&pdev->dev, "no firmware node\n"); - return -ENODEV; - } - - rpi_domains->fw = devm_rpi_firmware_get(&pdev->dev, fw_np); - of_node_put(fw_np); - if (!rpi_domains->fw) - return -EPROBE_DEFER; - - rpi_domains->has_new_interface = - rpi_has_new_domain_support(rpi_domains); - - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER, - "VIDEO_SCALER"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI"); - - /* - * Use the old firmware interface for USB power, so that we - * can turn it on even if the firmware hasn't been updated. - */ - rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB, - RPI_OLD_POWER_DOMAIN_USB, "USB"); - - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER, - "TRANSPOSER"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP"); - rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM"); - - of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate); - - platform_set_drvdata(pdev, rpi_domains); - - return 0; -} - -static const struct of_device_id rpi_power_of_match[] = { - { .compatible = "raspberrypi,bcm2835-power", }, - {}, -}; -MODULE_DEVICE_TABLE(of, rpi_power_of_match); - -static struct platform_driver rpi_power_driver = { - .driver = { - .name = "raspberrypi-power", - .of_match_table = rpi_power_of_match, - }, - .probe = rpi_power_probe, -}; -builtin_platform_driver(rpi_power_driver); - -MODULE_AUTHOR("Alexander Aring "); -MODULE_AUTHOR("Eric Anholt "); -MODULE_DESCRIPTION("Raspberry Pi power domain driver"); diff --git a/drivers/genpd/imx/Makefile b/drivers/genpd/imx/Makefile deleted file mode 100644 index 52d2629014a7..000000000000 --- a/drivers/genpd/imx/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o -obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o -obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o -obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o -obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8mp-blk-ctrl.o -obj-$(CONFIG_SOC_IMX9) += imx93-pd.o -obj-$(CONFIG_IMX9_BLK_CTRL) += imx93-blk-ctrl.o diff --git a/drivers/genpd/imx/gpc.c b/drivers/genpd/imx/gpc.c deleted file mode 100644 index 90a8b2c0676f..000000000000 --- a/drivers/genpd/imx/gpc.c +++ /dev/null @@ -1,554 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2015-2017 Pengutronix, Lucas Stach - * Copyright 2011-2013 Freescale Semiconductor, Inc. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define GPC_CNTR 0x000 - -#define GPC_PGC_CTRL_OFFS 0x0 -#define GPC_PGC_PUPSCR_OFFS 0x4 -#define GPC_PGC_PDNSCR_OFFS 0x8 -#define GPC_PGC_SW2ISO_SHIFT 0x8 -#define GPC_PGC_SW_SHIFT 0x0 - -#define GPC_PGC_PCI_PDN 0x200 -#define GPC_PGC_PCI_SR 0x20c - -#define GPC_PGC_GPU_PDN 0x260 -#define GPC_PGC_GPU_PUPSCR 0x264 -#define GPC_PGC_GPU_PDNSCR 0x268 -#define GPC_PGC_GPU_SR 0x26c - -#define GPC_PGC_DISP_PDN 0x240 -#define GPC_PGC_DISP_SR 0x24c - -#define GPU_VPU_PUP_REQ BIT(1) -#define GPU_VPU_PDN_REQ BIT(0) - -#define GPC_CLK_MAX 7 - -#define PGC_DOMAIN_FLAG_NO_PD BIT(0) - -struct imx_pm_domain { - struct generic_pm_domain base; - struct regmap *regmap; - struct regulator *supply; - struct clk *clk[GPC_CLK_MAX]; - int num_clks; - unsigned int reg_offs; - signed char cntr_pdn_bit; - unsigned int ipg_rate_mhz; -}; - -static inline struct imx_pm_domain * -to_imx_pm_domain(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx_pm_domain, base); -} - -static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd) -{ - struct imx_pm_domain *pd = to_imx_pm_domain(genpd); - int iso, iso2sw; - u32 val; - - /* Read ISO and ISO2SW power down delays */ - regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val); - iso = val & 0x3f; - iso2sw = (val >> 8) & 0x3f; - - /* Gate off domain when powered down */ - regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, - 0x1, 0x1); - - /* Request GPC to power down domain */ - val = BIT(pd->cntr_pdn_bit); - regmap_update_bits(pd->regmap, GPC_CNTR, val, val); - - /* Wait ISO + ISO2SW IPG clock cycles */ - udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz)); - - if (pd->supply) - regulator_disable(pd->supply); - - return 0; -} - -static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) -{ - struct imx_pm_domain *pd = to_imx_pm_domain(genpd); - int i, ret; - u32 val, req; - - if (pd->supply) { - ret = regulator_enable(pd->supply); - if (ret) { - pr_err("%s: failed to enable regulator: %d\n", - __func__, ret); - return ret; - } - } - - /* Enable reset clocks for all devices in the domain */ - for (i = 0; i < pd->num_clks; i++) - clk_prepare_enable(pd->clk[i]); - - /* Gate off domain when powered down */ - regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, - 0x1, 0x1); - - /* Request GPC to power up domain */ - req = BIT(pd->cntr_pdn_bit + 1); - regmap_update_bits(pd->regmap, GPC_CNTR, req, req); - - /* Wait for the PGC to handle the request */ - ret = regmap_read_poll_timeout(pd->regmap, GPC_CNTR, val, !(val & req), - 1, 50); - if (ret) - pr_err("powerup request on domain %s timed out\n", genpd->name); - - /* Wait for reset to propagate through peripherals */ - usleep_range(5, 10); - - /* Disable reset clocks for all devices in the domain */ - for (i = 0; i < pd->num_clks; i++) - clk_disable_unprepare(pd->clk[i]); - - return 0; -} - -static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain) -{ - int i, ret; - - for (i = 0; ; i++) { - struct clk *clk = of_clk_get(dev->of_node, i); - if (IS_ERR(clk)) - break; - if (i >= GPC_CLK_MAX) { - dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX); - ret = -EINVAL; - goto clk_err; - } - domain->clk[i] = clk; - } - domain->num_clks = i; - - return 0; - -clk_err: - while (i--) - clk_put(domain->clk[i]); - - return ret; -} - -static void imx_pgc_put_clocks(struct imx_pm_domain *domain) -{ - int i; - - for (i = domain->num_clks - 1; i >= 0; i--) - clk_put(domain->clk[i]); -} - -static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain) -{ - /* try to get the domain supply regulator */ - domain->supply = devm_regulator_get_optional(dev, "power"); - if (IS_ERR(domain->supply)) { - if (PTR_ERR(domain->supply) == -ENODEV) - domain->supply = NULL; - else - return PTR_ERR(domain->supply); - } - - /* try to get all clocks needed for reset propagation */ - return imx_pgc_get_clocks(dev, domain); -} - -static int imx_pgc_power_domain_probe(struct platform_device *pdev) -{ - struct imx_pm_domain *domain = pdev->dev.platform_data; - struct device *dev = &pdev->dev; - int ret; - - /* if this PD is associated with a DT node try to parse it */ - if (dev->of_node) { - ret = imx_pgc_parse_dt(dev, domain); - if (ret) - return ret; - } - - /* initially power on the domain */ - if (domain->base.power_on) - domain->base.power_on(&domain->base); - - if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { - pm_genpd_init(&domain->base, NULL, false); - ret = of_genpd_add_provider_simple(dev->of_node, &domain->base); - if (ret) - goto genpd_err; - } - - device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER); - - return 0; - -genpd_err: - pm_genpd_remove(&domain->base); - imx_pgc_put_clocks(domain); - - return ret; -} - -static int imx_pgc_power_domain_remove(struct platform_device *pdev) -{ - struct imx_pm_domain *domain = pdev->dev.platform_data; - - if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { - of_genpd_del_provider(pdev->dev.of_node); - pm_genpd_remove(&domain->base); - imx_pgc_put_clocks(domain); - } - - return 0; -} - -static const struct platform_device_id imx_pgc_power_domain_id[] = { - { "imx-pgc-power-domain"}, - { }, -}; - -static struct platform_driver imx_pgc_power_domain_driver = { - .driver = { - .name = "imx-pgc-pd", - }, - .probe = imx_pgc_power_domain_probe, - .remove = imx_pgc_power_domain_remove, - .id_table = imx_pgc_power_domain_id, -}; -builtin_platform_driver(imx_pgc_power_domain_driver) - -#define GPC_PGC_DOMAIN_ARM 0 -#define GPC_PGC_DOMAIN_PU 1 -#define GPC_PGC_DOMAIN_DISPLAY 2 -#define GPC_PGC_DOMAIN_PCI 3 - -static struct genpd_power_state imx6_pm_domain_pu_state = { - .power_off_latency_ns = 25000, - .power_on_latency_ns = 2000000, -}; - -static struct imx_pm_domain imx_gpc_domains[] = { - [GPC_PGC_DOMAIN_ARM] = { - .base = { - .name = "ARM", - .flags = GENPD_FLAG_ALWAYS_ON, - }, - }, - [GPC_PGC_DOMAIN_PU] = { - .base = { - .name = "PU", - .power_off = imx6_pm_domain_power_off, - .power_on = imx6_pm_domain_power_on, - .states = &imx6_pm_domain_pu_state, - .state_count = 1, - }, - .reg_offs = 0x260, - .cntr_pdn_bit = 0, - }, - [GPC_PGC_DOMAIN_DISPLAY] = { - .base = { - .name = "DISPLAY", - .power_off = imx6_pm_domain_power_off, - .power_on = imx6_pm_domain_power_on, - }, - .reg_offs = 0x240, - .cntr_pdn_bit = 4, - }, - [GPC_PGC_DOMAIN_PCI] = { - .base = { - .name = "PCI", - .power_off = imx6_pm_domain_power_off, - .power_on = imx6_pm_domain_power_on, - }, - .reg_offs = 0x200, - .cntr_pdn_bit = 6, - }, -}; - -struct imx_gpc_dt_data { - int num_domains; - bool err009619_present; - bool err006287_present; -}; - -static const struct imx_gpc_dt_data imx6q_dt_data = { - .num_domains = 2, - .err009619_present = false, - .err006287_present = false, -}; - -static const struct imx_gpc_dt_data imx6qp_dt_data = { - .num_domains = 2, - .err009619_present = true, - .err006287_present = false, -}; - -static const struct imx_gpc_dt_data imx6sl_dt_data = { - .num_domains = 3, - .err009619_present = false, - .err006287_present = true, -}; - -static const struct imx_gpc_dt_data imx6sx_dt_data = { - .num_domains = 4, - .err009619_present = false, - .err006287_present = false, -}; - -static const struct of_device_id imx_gpc_dt_ids[] = { - { .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data }, - { .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data }, - { .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data }, - { .compatible = "fsl,imx6sx-gpc", .data = &imx6sx_dt_data }, - { } -}; - -static const struct regmap_range yes_ranges[] = { - regmap_reg_range(GPC_CNTR, GPC_CNTR), - regmap_reg_range(GPC_PGC_PCI_PDN, GPC_PGC_PCI_SR), - regmap_reg_range(GPC_PGC_GPU_PDN, GPC_PGC_GPU_SR), - regmap_reg_range(GPC_PGC_DISP_PDN, GPC_PGC_DISP_SR), -}; - -static const struct regmap_access_table access_table = { - .yes_ranges = yes_ranges, - .n_yes_ranges = ARRAY_SIZE(yes_ranges), -}; - -static const struct regmap_config imx_gpc_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .rd_table = &access_table, - .wr_table = &access_table, - .max_register = 0x2ac, - .fast_io = true, -}; - -static struct generic_pm_domain *imx_gpc_onecell_domains[] = { - &imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base, - &imx_gpc_domains[GPC_PGC_DOMAIN_PU].base, -}; - -static struct genpd_onecell_data imx_gpc_onecell_data = { - .domains = imx_gpc_onecell_domains, - .num_domains = 2, -}; - -static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap, - unsigned int num_domains) -{ - struct imx_pm_domain *domain; - int i, ret; - - for (i = 0; i < num_domains; i++) { - domain = &imx_gpc_domains[i]; - domain->regmap = regmap; - domain->ipg_rate_mhz = 66; - - if (i == 1) { - domain->supply = devm_regulator_get(dev, "pu"); - if (IS_ERR(domain->supply)) - return PTR_ERR(domain->supply); - - ret = imx_pgc_get_clocks(dev, domain); - if (ret) - goto clk_err; - - domain->base.power_on(&domain->base); - } - } - - for (i = 0; i < num_domains; i++) - pm_genpd_init(&imx_gpc_domains[i].base, NULL, false); - - if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { - ret = of_genpd_add_provider_onecell(dev->of_node, - &imx_gpc_onecell_data); - if (ret) - goto genpd_err; - } - - return 0; - -genpd_err: - for (i = 0; i < num_domains; i++) - pm_genpd_remove(&imx_gpc_domains[i].base); - imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); -clk_err: - return ret; -} - -static int imx_gpc_probe(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(imx_gpc_dt_ids, &pdev->dev); - const struct imx_gpc_dt_data *of_id_data = of_id->data; - struct device_node *pgc_node; - struct regmap *regmap; - void __iomem *base; - int ret; - - pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); - - /* bail out if DT too old and doesn't provide the necessary info */ - if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && - !pgc_node) - return 0; - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, - &imx_gpc_regmap_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - dev_err(&pdev->dev, "failed to init regmap: %d\n", - ret); - return ret; - } - - /* - * Disable PU power down by runtime PM if ERR009619 is present. - * - * The PRE clock will be paused for several cycles when turning on the - * PU domain LDO from power down state. If PRE is in use at that time, - * the IPU/PRG cannot get the correct display data from the PRE. - * - * This is not a concern when the whole system enters suspend state, so - * it's safe to power down PU in this case. - */ - if (of_id_data->err009619_present) - imx_gpc_domains[GPC_PGC_DOMAIN_PU].base.flags |= - GENPD_FLAG_RPM_ALWAYS_ON; - - /* Keep DISP always on if ERR006287 is present */ - if (of_id_data->err006287_present) - imx_gpc_domains[GPC_PGC_DOMAIN_DISPLAY].base.flags |= - GENPD_FLAG_ALWAYS_ON; - - if (!pgc_node) { - ret = imx_gpc_old_dt_init(&pdev->dev, regmap, - of_id_data->num_domains); - if (ret) - return ret; - } else { - struct imx_pm_domain *domain; - struct platform_device *pd_pdev; - struct device_node *np; - struct clk *ipg_clk; - unsigned int ipg_rate_mhz; - int domain_index; - - ipg_clk = devm_clk_get(&pdev->dev, "ipg"); - if (IS_ERR(ipg_clk)) - return PTR_ERR(ipg_clk); - ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000; - - for_each_child_of_node(pgc_node, np) { - ret = of_property_read_u32(np, "reg", &domain_index); - if (ret) { - of_node_put(np); - return ret; - } - if (domain_index >= of_id_data->num_domains) - continue; - - pd_pdev = platform_device_alloc("imx-pgc-power-domain", - domain_index); - if (!pd_pdev) { - of_node_put(np); - return -ENOMEM; - } - - ret = platform_device_add_data(pd_pdev, - &imx_gpc_domains[domain_index], - sizeof(imx_gpc_domains[domain_index])); - if (ret) { - platform_device_put(pd_pdev); - of_node_put(np); - return ret; - } - domain = pd_pdev->dev.platform_data; - domain->regmap = regmap; - domain->ipg_rate_mhz = ipg_rate_mhz; - - pd_pdev->dev.parent = &pdev->dev; - pd_pdev->dev.of_node = np; - - ret = platform_device_add(pd_pdev); - if (ret) { - platform_device_put(pd_pdev); - of_node_put(np); - return ret; - } - } - } - - return 0; -} - -static int imx_gpc_remove(struct platform_device *pdev) -{ - struct device_node *pgc_node; - int ret; - - pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); - - /* bail out if DT too old and doesn't provide the necessary info */ - if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && - !pgc_node) - return 0; - - /* - * If the old DT binding is used the toplevel driver needs to - * de-register the power domains - */ - if (!pgc_node) { - of_genpd_del_provider(pdev->dev.of_node); - - ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base); - if (ret) - return ret; - imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); - - ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base); - if (ret) - return ret; - } - - return 0; -} - -static struct platform_driver imx_gpc_driver = { - .driver = { - .name = "imx-gpc", - .of_match_table = imx_gpc_dt_ids, - }, - .probe = imx_gpc_probe, - .remove = imx_gpc_remove, -}; -builtin_platform_driver(imx_gpc_driver) diff --git a/drivers/genpd/imx/gpcv2.c b/drivers/genpd/imx/gpcv2.c deleted file mode 100644 index fbd3d92f8cd8..000000000000 --- a/drivers/genpd/imx/gpcv2.c +++ /dev/null @@ -1,1550 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright 2017 Impinj, Inc - * Author: Andrey Smirnov - * - * Based on the code of analogus driver: - * - * Copyright 2015-2017 Pengutronix, Lucas Stach - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define GPC_LPCR_A_CORE_BSC 0x000 - -#define GPC_PGC_CPU_MAPPING 0x0ec -#define IMX8MP_GPC_PGC_CPU_MAPPING 0x1cc - -#define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6) -#define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5) -#define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4) -#define IMX7_PCIE_PHY_A_CORE_DOMAIN BIT(3) -#define IMX7_MIPI_PHY_A_CORE_DOMAIN BIT(2) - -#define IMX8M_PCIE2_A53_DOMAIN BIT(15) -#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14) -#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13) -#define IMX8M_DISP_A53_DOMAIN BIT(12) -#define IMX8M_HDMI_A53_DOMAIN BIT(11) -#define IMX8M_VPU_A53_DOMAIN BIT(10) -#define IMX8M_GPU_A53_DOMAIN BIT(9) -#define IMX8M_DDR2_A53_DOMAIN BIT(8) -#define IMX8M_DDR1_A53_DOMAIN BIT(7) -#define IMX8M_OTG2_A53_DOMAIN BIT(5) -#define IMX8M_OTG1_A53_DOMAIN BIT(4) -#define IMX8M_PCIE1_A53_DOMAIN BIT(3) -#define IMX8M_MIPI_A53_DOMAIN BIT(2) - -#define IMX8MM_VPUH1_A53_DOMAIN BIT(15) -#define IMX8MM_VPUG2_A53_DOMAIN BIT(14) -#define IMX8MM_VPUG1_A53_DOMAIN BIT(13) -#define IMX8MM_DISPMIX_A53_DOMAIN BIT(12) -#define IMX8MM_VPUMIX_A53_DOMAIN BIT(10) -#define IMX8MM_GPUMIX_A53_DOMAIN BIT(9) -#define IMX8MM_GPU_A53_DOMAIN (BIT(8) | BIT(11)) -#define IMX8MM_DDR1_A53_DOMAIN BIT(7) -#define IMX8MM_OTG2_A53_DOMAIN BIT(5) -#define IMX8MM_OTG1_A53_DOMAIN BIT(4) -#define IMX8MM_PCIE_A53_DOMAIN BIT(3) -#define IMX8MM_MIPI_A53_DOMAIN BIT(2) - -#define IMX8MN_DISPMIX_A53_DOMAIN BIT(12) -#define IMX8MN_GPUMIX_A53_DOMAIN BIT(9) -#define IMX8MN_DDR1_A53_DOMAIN BIT(7) -#define IMX8MN_OTG1_A53_DOMAIN BIT(4) -#define IMX8MN_MIPI_A53_DOMAIN BIT(2) - -#define IMX8MP_MEDIA_ISPDWP_A53_DOMAIN BIT(20) -#define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19) -#define IMX8MP_MIPI_PHY2_A53_DOMAIN BIT(18) -#define IMX8MP_HDMI_PHY_A53_DOMAIN BIT(17) -#define IMX8MP_HDMIMIX_A53_DOMAIN BIT(16) -#define IMX8MP_VPU_VC8000E_A53_DOMAIN BIT(15) -#define IMX8MP_VPU_G2_A53_DOMAIN BIT(14) -#define IMX8MP_VPU_G1_A53_DOMAIN BIT(13) -#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12) -#define IMX8MP_GPU3D_A53_DOMAIN BIT(11) -#define IMX8MP_VPUMIX_A53_DOMAIN BIT(10) -#define IMX8MP_GPUMIX_A53_DOMAIN BIT(9) -#define IMX8MP_GPU2D_A53_DOMAIN BIT(8) -#define IMX8MP_AUDIOMIX_A53_DOMAIN BIT(7) -#define IMX8MP_MLMIX_A53_DOMAIN BIT(6) -#define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5) -#define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4) -#define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3) -#define IMX8MP_MIPI_PHY1_A53_DOMAIN BIT(2) - -#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ 0x0d8 -#define IMX8MP_GPC_PU_PGC_SW_PDN_REQ 0x0e4 - -#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 -#define GPC_PU_PGC_SW_PDN_REQ 0x104 - -#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4) -#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3) -#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2) -#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1) -#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0) - -#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) -#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) -#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) -#define IMX8M_DISP_SW_Pxx_REQ BIT(10) -#define IMX8M_HDMI_SW_Pxx_REQ BIT(9) -#define IMX8M_VPU_SW_Pxx_REQ BIT(8) -#define IMX8M_GPU_SW_Pxx_REQ BIT(7) -#define IMX8M_DDR2_SW_Pxx_REQ BIT(6) -#define IMX8M_DDR1_SW_Pxx_REQ BIT(5) -#define IMX8M_OTG2_SW_Pxx_REQ BIT(3) -#define IMX8M_OTG1_SW_Pxx_REQ BIT(2) -#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1) -#define IMX8M_MIPI_SW_Pxx_REQ BIT(0) - -#define IMX8MM_VPUH1_SW_Pxx_REQ BIT(13) -#define IMX8MM_VPUG2_SW_Pxx_REQ BIT(12) -#define IMX8MM_VPUG1_SW_Pxx_REQ BIT(11) -#define IMX8MM_DISPMIX_SW_Pxx_REQ BIT(10) -#define IMX8MM_VPUMIX_SW_Pxx_REQ BIT(8) -#define IMX8MM_GPUMIX_SW_Pxx_REQ BIT(7) -#define IMX8MM_GPU_SW_Pxx_REQ (BIT(6) | BIT(9)) -#define IMX8MM_DDR1_SW_Pxx_REQ BIT(5) -#define IMX8MM_OTG2_SW_Pxx_REQ BIT(3) -#define IMX8MM_OTG1_SW_Pxx_REQ BIT(2) -#define IMX8MM_PCIE_SW_Pxx_REQ BIT(1) -#define IMX8MM_MIPI_SW_Pxx_REQ BIT(0) - -#define IMX8MN_DISPMIX_SW_Pxx_REQ BIT(10) -#define IMX8MN_GPUMIX_SW_Pxx_REQ BIT(7) -#define IMX8MN_DDR1_SW_Pxx_REQ BIT(5) -#define IMX8MN_OTG1_SW_Pxx_REQ BIT(2) -#define IMX8MN_MIPI_SW_Pxx_REQ BIT(0) - -#define IMX8MP_DDRMIX_Pxx_REQ BIT(19) -#define IMX8MP_MEDIA_ISP_DWP_Pxx_REQ BIT(18) -#define IMX8MP_HSIOMIX_Pxx_REQ BIT(17) -#define IMX8MP_MIPI_PHY2_Pxx_REQ BIT(16) -#define IMX8MP_HDMI_PHY_Pxx_REQ BIT(15) -#define IMX8MP_HDMIMIX_Pxx_REQ BIT(14) -#define IMX8MP_VPU_VC8K_Pxx_REQ BIT(13) -#define IMX8MP_VPU_G2_Pxx_REQ BIT(12) -#define IMX8MP_VPU_G1_Pxx_REQ BIT(11) -#define IMX8MP_MEDIMIX_Pxx_REQ BIT(10) -#define IMX8MP_GPU_3D_Pxx_REQ BIT(9) -#define IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ BIT(8) -#define IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ BIT(7) -#define IMX8MP_GPU_2D_Pxx_REQ BIT(6) -#define IMX8MP_AUDIOMIX_Pxx_REQ BIT(5) -#define IMX8MP_MLMIX_Pxx_REQ BIT(4) -#define IMX8MP_USB2_PHY_Pxx_REQ BIT(3) -#define IMX8MP_USB1_PHY_Pxx_REQ BIT(2) -#define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1) -#define IMX8MP_MIPI_PHY1_SW_Pxx_REQ BIT(0) - -#define GPC_M4_PU_PDN_FLG 0x1bc - -#define IMX8MP_GPC_PU_PWRHSK 0x190 -#define GPC_PU_PWRHSK 0x1fc - -#define IMX8M_GPU_HSK_PWRDNACKN BIT(26) -#define IMX8M_VPU_HSK_PWRDNACKN BIT(25) -#define IMX8M_DISP_HSK_PWRDNACKN BIT(24) -#define IMX8M_GPU_HSK_PWRDNREQN BIT(6) -#define IMX8M_VPU_HSK_PWRDNREQN BIT(5) -#define IMX8M_DISP_HSK_PWRDNREQN BIT(4) - -#define IMX8MM_GPUMIX_HSK_PWRDNACKN BIT(29) -#define IMX8MM_GPU_HSK_PWRDNACKN (BIT(27) | BIT(28)) -#define IMX8MM_VPUMIX_HSK_PWRDNACKN BIT(26) -#define IMX8MM_DISPMIX_HSK_PWRDNACKN BIT(25) -#define IMX8MM_HSIO_HSK_PWRDNACKN (BIT(23) | BIT(24)) -#define IMX8MM_GPUMIX_HSK_PWRDNREQN BIT(11) -#define IMX8MM_GPU_HSK_PWRDNREQN (BIT(9) | BIT(10)) -#define IMX8MM_VPUMIX_HSK_PWRDNREQN BIT(8) -#define IMX8MM_DISPMIX_HSK_PWRDNREQN BIT(7) -#define IMX8MM_HSIO_HSK_PWRDNREQN (BIT(5) | BIT(6)) - -#define IMX8MN_GPUMIX_HSK_PWRDNACKN (BIT(29) | BIT(27)) -#define IMX8MN_DISPMIX_HSK_PWRDNACKN BIT(25) -#define IMX8MN_HSIO_HSK_PWRDNACKN BIT(23) -#define IMX8MN_GPUMIX_HSK_PWRDNREQN (BIT(11) | BIT(9)) -#define IMX8MN_DISPMIX_HSK_PWRDNREQN BIT(7) -#define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5) - -#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30) -#define IMX8MP_HDMIMIX_PWRDNACKN BIT(29) -#define IMX8MP_HSIOMIX_PWRDNACKN BIT(28) -#define IMX8MP_VPUMIX_PWRDNACKN BIT(26) -#define IMX8MP_GPUMIX_PWRDNACKN BIT(25) -#define IMX8MP_MLMIX_PWRDNACKN (BIT(23) | BIT(24)) -#define IMX8MP_AUDIOMIX_PWRDNACKN (BIT(20) | BIT(31)) -#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14) -#define IMX8MP_HDMIMIX_PWRDNREQN BIT(13) -#define IMX8MP_HSIOMIX_PWRDNREQN BIT(12) -#define IMX8MP_VPUMIX_PWRDNREQN BIT(10) -#define IMX8MP_GPUMIX_PWRDNREQN BIT(9) -#define IMX8MP_MLMIX_PWRDNREQN (BIT(7) | BIT(8)) -#define IMX8MP_AUDIOMIX_PWRDNREQN (BIT(4) | BIT(15)) - -/* - * The PGC offset values in Reference Manual - * (Rev. 1, 01/2018 and the older ones) GPC chapter's - * GPC_PGC memory map are incorrect, below offset - * values are from design RTL. - */ -#define IMX7_PGC_MIPI 16 -#define IMX7_PGC_PCIE 17 -#define IMX7_PGC_USB_HSIC 20 - -#define IMX8M_PGC_MIPI 16 -#define IMX8M_PGC_PCIE1 17 -#define IMX8M_PGC_OTG1 18 -#define IMX8M_PGC_OTG2 19 -#define IMX8M_PGC_DDR1 21 -#define IMX8M_PGC_GPU 23 -#define IMX8M_PGC_VPU 24 -#define IMX8M_PGC_DISP 26 -#define IMX8M_PGC_MIPI_CSI1 27 -#define IMX8M_PGC_MIPI_CSI2 28 -#define IMX8M_PGC_PCIE2 29 - -#define IMX8MM_PGC_MIPI 16 -#define IMX8MM_PGC_PCIE 17 -#define IMX8MM_PGC_OTG1 18 -#define IMX8MM_PGC_OTG2 19 -#define IMX8MM_PGC_DDR1 21 -#define IMX8MM_PGC_GPU2D 22 -#define IMX8MM_PGC_GPUMIX 23 -#define IMX8MM_PGC_VPUMIX 24 -#define IMX8MM_PGC_GPU3D 25 -#define IMX8MM_PGC_DISPMIX 26 -#define IMX8MM_PGC_VPUG1 27 -#define IMX8MM_PGC_VPUG2 28 -#define IMX8MM_PGC_VPUH1 29 - -#define IMX8MN_PGC_MIPI 16 -#define IMX8MN_PGC_OTG1 18 -#define IMX8MN_PGC_DDR1 21 -#define IMX8MN_PGC_GPUMIX 23 -#define IMX8MN_PGC_DISPMIX 26 - -#define IMX8MP_PGC_NOC 9 -#define IMX8MP_PGC_MIPI1 12 -#define IMX8MP_PGC_PCIE 13 -#define IMX8MP_PGC_USB1 14 -#define IMX8MP_PGC_USB2 15 -#define IMX8MP_PGC_MLMIX 16 -#define IMX8MP_PGC_AUDIOMIX 17 -#define IMX8MP_PGC_GPU2D 18 -#define IMX8MP_PGC_GPUMIX 19 -#define IMX8MP_PGC_VPUMIX 20 -#define IMX8MP_PGC_GPU3D 21 -#define IMX8MP_PGC_MEDIAMIX 22 -#define IMX8MP_PGC_VPU_G1 23 -#define IMX8MP_PGC_VPU_G2 24 -#define IMX8MP_PGC_VPU_VC8000E 25 -#define IMX8MP_PGC_HDMIMIX 26 -#define IMX8MP_PGC_HDMI 27 -#define IMX8MP_PGC_MIPI2 28 -#define IMX8MP_PGC_HSIOMIX 29 -#define IMX8MP_PGC_MEDIA_ISP_DWP 30 -#define IMX8MP_PGC_DDRMIX 31 - -#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) -#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) - -#define GPC_PGC_CTRL_PCR BIT(0) - -struct imx_pgc_regs { - u16 map; - u16 pup; - u16 pdn; - u16 hsk; -}; - -struct imx_pgc_domain { - struct generic_pm_domain genpd; - struct regmap *regmap; - const struct imx_pgc_regs *regs; - struct regulator *regulator; - struct reset_control *reset; - struct clk_bulk_data *clks; - int num_clks; - - unsigned long pgc; - - const struct { - u32 pxx; - u32 map; - u32 hskreq; - u32 hskack; - } bits; - - const int voltage; - const bool keep_clocks; - struct device *dev; - - unsigned int pgc_sw_pup_reg; - unsigned int pgc_sw_pdn_reg; -}; - -struct imx_pgc_domain_data { - const struct imx_pgc_domain *domains; - size_t domains_num; - const struct regmap_access_table *reg_access_table; - const struct imx_pgc_regs *pgc_regs; -}; - -static inline struct imx_pgc_domain * -to_imx_pgc_domain(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx_pgc_domain, genpd); -} - -static int imx_pgc_power_up(struct generic_pm_domain *genpd) -{ - struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd); - u32 reg_val, pgc; - int ret; - - ret = pm_runtime_get_sync(domain->dev); - if (ret < 0) { - pm_runtime_put_noidle(domain->dev); - return ret; - } - - if (!IS_ERR(domain->regulator)) { - ret = regulator_enable(domain->regulator); - if (ret) { - dev_err(domain->dev, - "failed to enable regulator: %pe\n", - ERR_PTR(ret)); - goto out_put_pm; - } - } - - reset_control_assert(domain->reset); - - /* Enable reset clocks for all devices in the domain */ - ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); - if (ret) { - dev_err(domain->dev, "failed to enable reset clocks\n"); - goto out_regulator_disable; - } - - /* delays for reset to propagate */ - udelay(5); - - if (domain->bits.pxx) { - /* request the domain to power up */ - regmap_update_bits(domain->regmap, domain->regs->pup, - domain->bits.pxx, domain->bits.pxx); - /* - * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait - * for PUP_REQ/PDN_REQ bit to be cleared - */ - ret = regmap_read_poll_timeout(domain->regmap, - domain->regs->pup, reg_val, - !(reg_val & domain->bits.pxx), - 0, USEC_PER_MSEC); - if (ret) { - dev_err(domain->dev, "failed to command PGC\n"); - goto out_clk_disable; - } - - /* disable power control */ - for_each_set_bit(pgc, &domain->pgc, 32) { - regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc), - GPC_PGC_CTRL_PCR); - } - } - - /* delay for reset to propagate */ - udelay(5); - - reset_control_deassert(domain->reset); - - /* request the ADB400 to power up */ - if (domain->bits.hskreq) { - regmap_update_bits(domain->regmap, domain->regs->hsk, - domain->bits.hskreq, domain->bits.hskreq); - - /* - * ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, reg_val, - * (reg_val & domain->bits.hskack), 0, - * USEC_PER_MSEC); - * Technically we need the commented code to wait handshake. But that needs - * the BLK-CTL module BUS clk-en bit being set. - * - * There is a separate BLK-CTL module and we will have such a driver for it, - * that driver will set the BUS clk-en bit and handshake will be triggered - * automatically there. Just add a delay and suppose the handshake finish - * after that. - */ - } - - /* Disable reset clocks for all devices in the domain */ - if (!domain->keep_clocks) - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - - return 0; - -out_clk_disable: - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); -out_regulator_disable: - if (!IS_ERR(domain->regulator)) - regulator_disable(domain->regulator); -out_put_pm: - pm_runtime_put(domain->dev); - - return ret; -} - -static int imx_pgc_power_down(struct generic_pm_domain *genpd) -{ - struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd); - u32 reg_val, pgc; - int ret; - - /* Enable reset clocks for all devices in the domain */ - if (!domain->keep_clocks) { - ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); - if (ret) { - dev_err(domain->dev, "failed to enable reset clocks\n"); - return ret; - } - } - - /* request the ADB400 to power down */ - if (domain->bits.hskreq) { - regmap_clear_bits(domain->regmap, domain->regs->hsk, - domain->bits.hskreq); - - ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, - reg_val, - !(reg_val & domain->bits.hskack), - 0, USEC_PER_MSEC); - if (ret) { - dev_err(domain->dev, "failed to power down ADB400\n"); - goto out_clk_disable; - } - } - - if (domain->bits.pxx) { - /* enable power control */ - for_each_set_bit(pgc, &domain->pgc, 32) { - regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc), - GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); - } - - /* request the domain to power down */ - regmap_update_bits(domain->regmap, domain->regs->pdn, - domain->bits.pxx, domain->bits.pxx); - /* - * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait - * for PUP_REQ/PDN_REQ bit to be cleared - */ - ret = regmap_read_poll_timeout(domain->regmap, - domain->regs->pdn, reg_val, - !(reg_val & domain->bits.pxx), - 0, USEC_PER_MSEC); - if (ret) { - dev_err(domain->dev, "failed to command PGC\n"); - goto out_clk_disable; - } - } - - /* Disable reset clocks for all devices in the domain */ - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - - if (!IS_ERR(domain->regulator)) { - ret = regulator_disable(domain->regulator); - if (ret) { - dev_err(domain->dev, - "failed to disable regulator: %pe\n", - ERR_PTR(ret)); - return ret; - } - } - - pm_runtime_put_sync_suspend(domain->dev); - - return 0; - -out_clk_disable: - if (!domain->keep_clocks) - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - - return ret; -} - -static const struct imx_pgc_domain imx7_pgc_domains[] = { - [IMX7_POWER_DOMAIN_MIPI_PHY] = { - .genpd = { - .name = "mipi-phy", - }, - .bits = { - .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ, - .map = IMX7_MIPI_PHY_A_CORE_DOMAIN, - }, - .voltage = 1000000, - .pgc = BIT(IMX7_PGC_MIPI), - }, - - [IMX7_POWER_DOMAIN_PCIE_PHY] = { - .genpd = { - .name = "pcie-phy", - }, - .bits = { - .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ, - .map = IMX7_PCIE_PHY_A_CORE_DOMAIN, - }, - .voltage = 1000000, - .pgc = BIT(IMX7_PGC_PCIE), - }, - - [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { - .genpd = { - .name = "usb-hsic-phy", - }, - .bits = { - .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ, - .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN, - }, - .voltage = 1200000, - .pgc = BIT(IMX7_PGC_USB_HSIC), - }, -}; - -static const struct regmap_range imx7_yes_ranges[] = { - regmap_reg_range(GPC_LPCR_A_CORE_BSC, - GPC_M4_PU_PDN_FLG), - regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_MIPI), - GPC_PGC_SR(IMX7_PGC_MIPI)), - regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_PCIE), - GPC_PGC_SR(IMX7_PGC_PCIE)), - regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_USB_HSIC), - GPC_PGC_SR(IMX7_PGC_USB_HSIC)), -}; - -static const struct regmap_access_table imx7_access_table = { - .yes_ranges = imx7_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(imx7_yes_ranges), -}; - -static const struct imx_pgc_regs imx7_pgc_regs = { - .map = GPC_PGC_CPU_MAPPING, - .pup = GPC_PU_PGC_SW_PUP_REQ, - .pdn = GPC_PU_PGC_SW_PDN_REQ, - .hsk = GPC_PU_PWRHSK, -}; - -static const struct imx_pgc_domain_data imx7_pgc_domain_data = { - .domains = imx7_pgc_domains, - .domains_num = ARRAY_SIZE(imx7_pgc_domains), - .reg_access_table = &imx7_access_table, - .pgc_regs = &imx7_pgc_regs, -}; - -static const struct imx_pgc_domain imx8m_pgc_domains[] = { - [IMX8M_POWER_DOMAIN_MIPI] = { - .genpd = { - .name = "mipi", - }, - .bits = { - .pxx = IMX8M_MIPI_SW_Pxx_REQ, - .map = IMX8M_MIPI_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_MIPI), - }, - - [IMX8M_POWER_DOMAIN_PCIE1] = { - .genpd = { - .name = "pcie1", - }, - .bits = { - .pxx = IMX8M_PCIE1_SW_Pxx_REQ, - .map = IMX8M_PCIE1_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_PCIE1), - }, - - [IMX8M_POWER_DOMAIN_USB_OTG1] = { - .genpd = { - .name = "usb-otg1", - }, - .bits = { - .pxx = IMX8M_OTG1_SW_Pxx_REQ, - .map = IMX8M_OTG1_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_OTG1), - }, - - [IMX8M_POWER_DOMAIN_USB_OTG2] = { - .genpd = { - .name = "usb-otg2", - }, - .bits = { - .pxx = IMX8M_OTG2_SW_Pxx_REQ, - .map = IMX8M_OTG2_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_OTG2), - }, - - [IMX8M_POWER_DOMAIN_DDR1] = { - .genpd = { - .name = "ddr1", - }, - .bits = { - .pxx = IMX8M_DDR1_SW_Pxx_REQ, - .map = IMX8M_DDR2_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_DDR1), - }, - - [IMX8M_POWER_DOMAIN_GPU] = { - .genpd = { - .name = "gpu", - }, - .bits = { - .pxx = IMX8M_GPU_SW_Pxx_REQ, - .map = IMX8M_GPU_A53_DOMAIN, - .hskreq = IMX8M_GPU_HSK_PWRDNREQN, - .hskack = IMX8M_GPU_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8M_PGC_GPU), - }, - - [IMX8M_POWER_DOMAIN_VPU] = { - .genpd = { - .name = "vpu", - }, - .bits = { - .pxx = IMX8M_VPU_SW_Pxx_REQ, - .map = IMX8M_VPU_A53_DOMAIN, - .hskreq = IMX8M_VPU_HSK_PWRDNREQN, - .hskack = IMX8M_VPU_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8M_PGC_VPU), - .keep_clocks = true, - }, - - [IMX8M_POWER_DOMAIN_DISP] = { - .genpd = { - .name = "disp", - }, - .bits = { - .pxx = IMX8M_DISP_SW_Pxx_REQ, - .map = IMX8M_DISP_A53_DOMAIN, - .hskreq = IMX8M_DISP_HSK_PWRDNREQN, - .hskack = IMX8M_DISP_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8M_PGC_DISP), - }, - - [IMX8M_POWER_DOMAIN_MIPI_CSI1] = { - .genpd = { - .name = "mipi-csi1", - }, - .bits = { - .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ, - .map = IMX8M_MIPI_CSI1_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_MIPI_CSI1), - }, - - [IMX8M_POWER_DOMAIN_MIPI_CSI2] = { - .genpd = { - .name = "mipi-csi2", - }, - .bits = { - .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ, - .map = IMX8M_MIPI_CSI2_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_MIPI_CSI2), - }, - - [IMX8M_POWER_DOMAIN_PCIE2] = { - .genpd = { - .name = "pcie2", - }, - .bits = { - .pxx = IMX8M_PCIE2_SW_Pxx_REQ, - .map = IMX8M_PCIE2_A53_DOMAIN, - }, - .pgc = BIT(IMX8M_PGC_PCIE2), - }, -}; - -static const struct regmap_range imx8m_yes_ranges[] = { - regmap_reg_range(GPC_LPCR_A_CORE_BSC, - GPC_PU_PWRHSK), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), - GPC_PGC_SR(IMX8M_PGC_MIPI)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), - GPC_PGC_SR(IMX8M_PGC_PCIE1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG1), - GPC_PGC_SR(IMX8M_PGC_OTG1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG2), - GPC_PGC_SR(IMX8M_PGC_OTG2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DDR1), - GPC_PGC_SR(IMX8M_PGC_DDR1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_GPU), - GPC_PGC_SR(IMX8M_PGC_GPU)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_VPU), - GPC_PGC_SR(IMX8M_PGC_VPU)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DISP), - GPC_PGC_SR(IMX8M_PGC_DISP)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI1), - GPC_PGC_SR(IMX8M_PGC_MIPI_CSI1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI2), - GPC_PGC_SR(IMX8M_PGC_MIPI_CSI2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE2), - GPC_PGC_SR(IMX8M_PGC_PCIE2)), -}; - -static const struct regmap_access_table imx8m_access_table = { - .yes_ranges = imx8m_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(imx8m_yes_ranges), -}; - -static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { - .domains = imx8m_pgc_domains, - .domains_num = ARRAY_SIZE(imx8m_pgc_domains), - .reg_access_table = &imx8m_access_table, - .pgc_regs = &imx7_pgc_regs, -}; - -static const struct imx_pgc_domain imx8mm_pgc_domains[] = { - [IMX8MM_POWER_DOMAIN_HSIOMIX] = { - .genpd = { - .name = "hsiomix", - }, - .bits = { - .pxx = 0, /* no power sequence control */ - .map = 0, /* no power sequence control */ - .hskreq = IMX8MM_HSIO_HSK_PWRDNREQN, - .hskack = IMX8MM_HSIO_HSK_PWRDNACKN, - }, - .keep_clocks = true, - }, - - [IMX8MM_POWER_DOMAIN_PCIE] = { - .genpd = { - .name = "pcie", - }, - .bits = { - .pxx = IMX8MM_PCIE_SW_Pxx_REQ, - .map = IMX8MM_PCIE_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_PCIE), - }, - - [IMX8MM_POWER_DOMAIN_OTG1] = { - .genpd = { - .name = "usb-otg1", - .flags = GENPD_FLAG_ACTIVE_WAKEUP, - }, - .bits = { - .pxx = IMX8MM_OTG1_SW_Pxx_REQ, - .map = IMX8MM_OTG1_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_OTG1), - }, - - [IMX8MM_POWER_DOMAIN_OTG2] = { - .genpd = { - .name = "usb-otg2", - .flags = GENPD_FLAG_ACTIVE_WAKEUP, - }, - .bits = { - .pxx = IMX8MM_OTG2_SW_Pxx_REQ, - .map = IMX8MM_OTG2_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_OTG2), - }, - - [IMX8MM_POWER_DOMAIN_GPUMIX] = { - .genpd = { - .name = "gpumix", - }, - .bits = { - .pxx = IMX8MM_GPUMIX_SW_Pxx_REQ, - .map = IMX8MM_GPUMIX_A53_DOMAIN, - .hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN, - .hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8MM_PGC_GPUMIX), - .keep_clocks = true, - }, - - [IMX8MM_POWER_DOMAIN_GPU] = { - .genpd = { - .name = "gpu", - }, - .bits = { - .pxx = IMX8MM_GPU_SW_Pxx_REQ, - .map = IMX8MM_GPU_A53_DOMAIN, - .hskreq = IMX8MM_GPU_HSK_PWRDNREQN, - .hskack = IMX8MM_GPU_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D), - }, - - [IMX8MM_POWER_DOMAIN_VPUMIX] = { - .genpd = { - .name = "vpumix", - }, - .bits = { - .pxx = IMX8MM_VPUMIX_SW_Pxx_REQ, - .map = IMX8MM_VPUMIX_A53_DOMAIN, - .hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN, - .hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8MM_PGC_VPUMIX), - .keep_clocks = true, - }, - - [IMX8MM_POWER_DOMAIN_VPUG1] = { - .genpd = { - .name = "vpu-g1", - }, - .bits = { - .pxx = IMX8MM_VPUG1_SW_Pxx_REQ, - .map = IMX8MM_VPUG1_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_VPUG1), - }, - - [IMX8MM_POWER_DOMAIN_VPUG2] = { - .genpd = { - .name = "vpu-g2", - }, - .bits = { - .pxx = IMX8MM_VPUG2_SW_Pxx_REQ, - .map = IMX8MM_VPUG2_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_VPUG2), - }, - - [IMX8MM_POWER_DOMAIN_VPUH1] = { - .genpd = { - .name = "vpu-h1", - }, - .bits = { - .pxx = IMX8MM_VPUH1_SW_Pxx_REQ, - .map = IMX8MM_VPUH1_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_VPUH1), - .keep_clocks = true, - }, - - [IMX8MM_POWER_DOMAIN_DISPMIX] = { - .genpd = { - .name = "dispmix", - }, - .bits = { - .pxx = IMX8MM_DISPMIX_SW_Pxx_REQ, - .map = IMX8MM_DISPMIX_A53_DOMAIN, - .hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN, - .hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8MM_PGC_DISPMIX), - .keep_clocks = true, - }, - - [IMX8MM_POWER_DOMAIN_MIPI] = { - .genpd = { - .name = "mipi", - }, - .bits = { - .pxx = IMX8MM_MIPI_SW_Pxx_REQ, - .map = IMX8MM_MIPI_A53_DOMAIN, - }, - .pgc = BIT(IMX8MM_PGC_MIPI), - }, -}; - -static const struct regmap_range imx8mm_yes_ranges[] = { - regmap_reg_range(GPC_LPCR_A_CORE_BSC, - GPC_PU_PWRHSK), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_MIPI), - GPC_PGC_SR(IMX8MM_PGC_MIPI)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_PCIE), - GPC_PGC_SR(IMX8MM_PGC_PCIE)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_OTG1), - GPC_PGC_SR(IMX8MM_PGC_OTG1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_OTG2), - GPC_PGC_SR(IMX8MM_PGC_OTG2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_DDR1), - GPC_PGC_SR(IMX8MM_PGC_DDR1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPU2D), - GPC_PGC_SR(IMX8MM_PGC_GPU2D)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPUMIX), - GPC_PGC_SR(IMX8MM_PGC_GPUMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUMIX), - GPC_PGC_SR(IMX8MM_PGC_VPUMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPU3D), - GPC_PGC_SR(IMX8MM_PGC_GPU3D)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_DISPMIX), - GPC_PGC_SR(IMX8MM_PGC_DISPMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUG1), - GPC_PGC_SR(IMX8MM_PGC_VPUG1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUG2), - GPC_PGC_SR(IMX8MM_PGC_VPUG2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUH1), - GPC_PGC_SR(IMX8MM_PGC_VPUH1)), -}; - -static const struct regmap_access_table imx8mm_access_table = { - .yes_ranges = imx8mm_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(imx8mm_yes_ranges), -}; - -static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = { - .domains = imx8mm_pgc_domains, - .domains_num = ARRAY_SIZE(imx8mm_pgc_domains), - .reg_access_table = &imx8mm_access_table, - .pgc_regs = &imx7_pgc_regs, -}; - -static const struct imx_pgc_domain imx8mp_pgc_domains[] = { - [IMX8MP_POWER_DOMAIN_MIPI_PHY1] = { - .genpd = { - .name = "mipi-phy1", - }, - .bits = { - .pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ, - .map = IMX8MP_MIPI_PHY1_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_MIPI1), - }, - - [IMX8MP_POWER_DOMAIN_PCIE_PHY] = { - .genpd = { - .name = "pcie-phy1", - }, - .bits = { - .pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ, - .map = IMX8MP_PCIE_PHY_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_PCIE), - }, - - [IMX8MP_POWER_DOMAIN_USB1_PHY] = { - .genpd = { - .name = "usb-otg1", - }, - .bits = { - .pxx = IMX8MP_USB1_PHY_Pxx_REQ, - .map = IMX8MP_USB1_PHY_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_USB1), - }, - - [IMX8MP_POWER_DOMAIN_USB2_PHY] = { - .genpd = { - .name = "usb-otg2", - }, - .bits = { - .pxx = IMX8MP_USB2_PHY_Pxx_REQ, - .map = IMX8MP_USB2_PHY_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_USB2), - }, - - [IMX8MP_POWER_DOMAIN_MLMIX] = { - .genpd = { - .name = "mlmix", - }, - .bits = { - .pxx = IMX8MP_MLMIX_Pxx_REQ, - .map = IMX8MP_MLMIX_A53_DOMAIN, - .hskreq = IMX8MP_MLMIX_PWRDNREQN, - .hskack = IMX8MP_MLMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_MLMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_AUDIOMIX] = { - .genpd = { - .name = "audiomix", - }, - .bits = { - .pxx = IMX8MP_AUDIOMIX_Pxx_REQ, - .map = IMX8MP_AUDIOMIX_A53_DOMAIN, - .hskreq = IMX8MP_AUDIOMIX_PWRDNREQN, - .hskack = IMX8MP_AUDIOMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_AUDIOMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_GPU2D] = { - .genpd = { - .name = "gpu2d", - }, - .bits = { - .pxx = IMX8MP_GPU_2D_Pxx_REQ, - .map = IMX8MP_GPU2D_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_GPU2D), - }, - - [IMX8MP_POWER_DOMAIN_GPUMIX] = { - .genpd = { - .name = "gpumix", - }, - .bits = { - .pxx = IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ, - .map = IMX8MP_GPUMIX_A53_DOMAIN, - .hskreq = IMX8MP_GPUMIX_PWRDNREQN, - .hskack = IMX8MP_GPUMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_GPUMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_VPUMIX] = { - .genpd = { - .name = "vpumix", - }, - .bits = { - .pxx = IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ, - .map = IMX8MP_VPUMIX_A53_DOMAIN, - .hskreq = IMX8MP_VPUMIX_PWRDNREQN, - .hskack = IMX8MP_VPUMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_VPUMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_GPU3D] = { - .genpd = { - .name = "gpu3d", - }, - .bits = { - .pxx = IMX8MP_GPU_3D_Pxx_REQ, - .map = IMX8MP_GPU3D_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_GPU3D), - }, - - [IMX8MP_POWER_DOMAIN_MEDIAMIX] = { - .genpd = { - .name = "mediamix", - }, - .bits = { - .pxx = IMX8MP_MEDIMIX_Pxx_REQ, - .map = IMX8MP_MEDIAMIX_A53_DOMAIN, - .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN, - .hskack = IMX8MP_MEDIAMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_MEDIAMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_VPU_G1] = { - .genpd = { - .name = "vpu-g1", - }, - .bits = { - .pxx = IMX8MP_VPU_G1_Pxx_REQ, - .map = IMX8MP_VPU_G1_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_VPU_G1), - }, - - [IMX8MP_POWER_DOMAIN_VPU_G2] = { - .genpd = { - .name = "vpu-g2", - }, - .bits = { - .pxx = IMX8MP_VPU_G2_Pxx_REQ, - .map = IMX8MP_VPU_G2_A53_DOMAIN - }, - .pgc = BIT(IMX8MP_PGC_VPU_G2), - }, - - [IMX8MP_POWER_DOMAIN_VPU_VC8000E] = { - .genpd = { - .name = "vpu-h1", - }, - .bits = { - .pxx = IMX8MP_VPU_VC8K_Pxx_REQ, - .map = IMX8MP_VPU_VC8000E_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_VPU_VC8000E), - }, - - [IMX8MP_POWER_DOMAIN_HDMIMIX] = { - .genpd = { - .name = "hdmimix", - }, - .bits = { - .pxx = IMX8MP_HDMIMIX_Pxx_REQ, - .map = IMX8MP_HDMIMIX_A53_DOMAIN, - .hskreq = IMX8MP_HDMIMIX_PWRDNREQN, - .hskack = IMX8MP_HDMIMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_HDMIMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_HDMI_PHY] = { - .genpd = { - .name = "hdmi-phy", - }, - .bits = { - .pxx = IMX8MP_HDMI_PHY_Pxx_REQ, - .map = IMX8MP_HDMI_PHY_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_HDMI), - }, - - [IMX8MP_POWER_DOMAIN_MIPI_PHY2] = { - .genpd = { - .name = "mipi-phy2", - }, - .bits = { - .pxx = IMX8MP_MIPI_PHY2_Pxx_REQ, - .map = IMX8MP_MIPI_PHY2_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_MIPI2), - }, - - [IMX8MP_POWER_DOMAIN_HSIOMIX] = { - .genpd = { - .name = "hsiomix", - }, - .bits = { - .pxx = IMX8MP_HSIOMIX_Pxx_REQ, - .map = IMX8MP_HSIOMIX_A53_DOMAIN, - .hskreq = IMX8MP_HSIOMIX_PWRDNREQN, - .hskack = IMX8MP_HSIOMIX_PWRDNACKN, - }, - .pgc = BIT(IMX8MP_PGC_HSIOMIX), - .keep_clocks = true, - }, - - [IMX8MP_POWER_DOMAIN_MEDIAMIX_ISPDWP] = { - .genpd = { - .name = "mediamix-isp-dwp", - }, - .bits = { - .pxx = IMX8MP_MEDIA_ISP_DWP_Pxx_REQ, - .map = IMX8MP_MEDIA_ISPDWP_A53_DOMAIN, - }, - .pgc = BIT(IMX8MP_PGC_MEDIA_ISP_DWP), - }, -}; - -static const struct regmap_range imx8mp_yes_ranges[] = { - regmap_reg_range(GPC_LPCR_A_CORE_BSC, - IMX8MP_GPC_PGC_CPU_MAPPING), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_NOC), - GPC_PGC_SR(IMX8MP_PGC_NOC)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI1), - GPC_PGC_SR(IMX8MP_PGC_MIPI1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_PCIE), - GPC_PGC_SR(IMX8MP_PGC_PCIE)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB1), - GPC_PGC_SR(IMX8MP_PGC_USB1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB2), - GPC_PGC_SR(IMX8MP_PGC_USB2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MLMIX), - GPC_PGC_SR(IMX8MP_PGC_MLMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_AUDIOMIX), - GPC_PGC_SR(IMX8MP_PGC_AUDIOMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU2D), - GPC_PGC_SR(IMX8MP_PGC_GPU2D)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPUMIX), - GPC_PGC_SR(IMX8MP_PGC_GPUMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPUMIX), - GPC_PGC_SR(IMX8MP_PGC_VPUMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU3D), - GPC_PGC_SR(IMX8MP_PGC_GPU3D)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIAMIX), - GPC_PGC_SR(IMX8MP_PGC_MEDIAMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G1), - GPC_PGC_SR(IMX8MP_PGC_VPU_G1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G2), - GPC_PGC_SR(IMX8MP_PGC_VPU_G2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_VC8000E), - GPC_PGC_SR(IMX8MP_PGC_VPU_VC8000E)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMIMIX), - GPC_PGC_SR(IMX8MP_PGC_HDMIMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMI), - GPC_PGC_SR(IMX8MP_PGC_HDMI)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI2), - GPC_PGC_SR(IMX8MP_PGC_MIPI2)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HSIOMIX), - GPC_PGC_SR(IMX8MP_PGC_HSIOMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIA_ISP_DWP), - GPC_PGC_SR(IMX8MP_PGC_MEDIA_ISP_DWP)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_DDRMIX), - GPC_PGC_SR(IMX8MP_PGC_DDRMIX)), -}; - -static const struct regmap_access_table imx8mp_access_table = { - .yes_ranges = imx8mp_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(imx8mp_yes_ranges), -}; - -static const struct imx_pgc_regs imx8mp_pgc_regs = { - .map = IMX8MP_GPC_PGC_CPU_MAPPING, - .pup = IMX8MP_GPC_PU_PGC_SW_PUP_REQ, - .pdn = IMX8MP_GPC_PU_PGC_SW_PDN_REQ, - .hsk = IMX8MP_GPC_PU_PWRHSK, -}; -static const struct imx_pgc_domain_data imx8mp_pgc_domain_data = { - .domains = imx8mp_pgc_domains, - .domains_num = ARRAY_SIZE(imx8mp_pgc_domains), - .reg_access_table = &imx8mp_access_table, - .pgc_regs = &imx8mp_pgc_regs, -}; - -static const struct imx_pgc_domain imx8mn_pgc_domains[] = { - [IMX8MN_POWER_DOMAIN_HSIOMIX] = { - .genpd = { - .name = "hsiomix", - }, - .bits = { - .pxx = 0, /* no power sequence control */ - .map = 0, /* no power sequence control */ - .hskreq = IMX8MN_HSIO_HSK_PWRDNREQN, - .hskack = IMX8MN_HSIO_HSK_PWRDNACKN, - }, - .keep_clocks = true, - }, - - [IMX8MN_POWER_DOMAIN_OTG1] = { - .genpd = { - .name = "usb-otg1", - .flags = GENPD_FLAG_ACTIVE_WAKEUP, - }, - .bits = { - .pxx = IMX8MN_OTG1_SW_Pxx_REQ, - .map = IMX8MN_OTG1_A53_DOMAIN, - }, - .pgc = BIT(IMX8MN_PGC_OTG1), - }, - - [IMX8MN_POWER_DOMAIN_GPUMIX] = { - .genpd = { - .name = "gpumix", - }, - .bits = { - .pxx = IMX8MN_GPUMIX_SW_Pxx_REQ, - .map = IMX8MN_GPUMIX_A53_DOMAIN, - .hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN, - .hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8MN_PGC_GPUMIX), - .keep_clocks = true, - }, - - [IMX8MN_POWER_DOMAIN_DISPMIX] = { - .genpd = { - .name = "dispmix", - }, - .bits = { - .pxx = IMX8MN_DISPMIX_SW_Pxx_REQ, - .map = IMX8MN_DISPMIX_A53_DOMAIN, - .hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN, - .hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN, - }, - .pgc = BIT(IMX8MN_PGC_DISPMIX), - .keep_clocks = true, - }, - - [IMX8MN_POWER_DOMAIN_MIPI] = { - .genpd = { - .name = "mipi", - }, - .bits = { - .pxx = IMX8MN_MIPI_SW_Pxx_REQ, - .map = IMX8MN_MIPI_A53_DOMAIN, - }, - .pgc = BIT(IMX8MN_PGC_MIPI), - }, -}; - -static const struct regmap_range imx8mn_yes_ranges[] = { - regmap_reg_range(GPC_LPCR_A_CORE_BSC, - GPC_PU_PWRHSK), - regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_MIPI), - GPC_PGC_SR(IMX8MN_PGC_MIPI)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_OTG1), - GPC_PGC_SR(IMX8MN_PGC_OTG1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_DDR1), - GPC_PGC_SR(IMX8MN_PGC_DDR1)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_GPUMIX), - GPC_PGC_SR(IMX8MN_PGC_GPUMIX)), - regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_DISPMIX), - GPC_PGC_SR(IMX8MN_PGC_DISPMIX)), -}; - -static const struct regmap_access_table imx8mn_access_table = { - .yes_ranges = imx8mn_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(imx8mn_yes_ranges), -}; - -static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = { - .domains = imx8mn_pgc_domains, - .domains_num = ARRAY_SIZE(imx8mn_pgc_domains), - .reg_access_table = &imx8mn_access_table, - .pgc_regs = &imx7_pgc_regs, -}; - -static int imx_pgc_domain_probe(struct platform_device *pdev) -{ - struct imx_pgc_domain *domain = pdev->dev.platform_data; - int ret; - - domain->dev = &pdev->dev; - - domain->regulator = devm_regulator_get_optional(domain->dev, "power"); - if (IS_ERR(domain->regulator)) { - if (PTR_ERR(domain->regulator) != -ENODEV) - return dev_err_probe(domain->dev, PTR_ERR(domain->regulator), - "Failed to get domain's regulator\n"); - } else if (domain->voltage) { - regulator_set_voltage(domain->regulator, - domain->voltage, domain->voltage); - } - - domain->num_clks = devm_clk_bulk_get_all(domain->dev, &domain->clks); - if (domain->num_clks < 0) - return dev_err_probe(domain->dev, domain->num_clks, - "Failed to get domain's clocks\n"); - - domain->reset = devm_reset_control_array_get_optional_exclusive(domain->dev); - if (IS_ERR(domain->reset)) - return dev_err_probe(domain->dev, PTR_ERR(domain->reset), - "Failed to get domain's resets\n"); - - pm_runtime_enable(domain->dev); - - if (domain->bits.map) - regmap_update_bits(domain->regmap, domain->regs->map, - domain->bits.map, domain->bits.map); - - ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err(domain->dev, "Failed to init power domain\n"); - goto out_domain_unmap; - } - - if (IS_ENABLED(CONFIG_LOCKDEP) && - of_property_read_bool(domain->dev->of_node, "power-domains")) - lockdep_set_subclass(&domain->genpd.mlock, 1); - - ret = of_genpd_add_provider_simple(domain->dev->of_node, - &domain->genpd); - if (ret) { - dev_err(domain->dev, "Failed to add genpd provider\n"); - goto out_genpd_remove; - } - - return 0; - -out_genpd_remove: - pm_genpd_remove(&domain->genpd); -out_domain_unmap: - if (domain->bits.map) - regmap_update_bits(domain->regmap, domain->regs->map, - domain->bits.map, 0); - pm_runtime_disable(domain->dev); - - return ret; -} - -static int imx_pgc_domain_remove(struct platform_device *pdev) -{ - struct imx_pgc_domain *domain = pdev->dev.platform_data; - - of_genpd_del_provider(domain->dev->of_node); - pm_genpd_remove(&domain->genpd); - - if (domain->bits.map) - regmap_update_bits(domain->regmap, domain->regs->map, - domain->bits.map, 0); - - pm_runtime_disable(domain->dev); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int imx_pgc_domain_suspend(struct device *dev) -{ - int ret; - - /* - * This may look strange, but is done so the generic PM_SLEEP code - * can power down our domain and more importantly power it up again - * after resume, without tripping over our usage of runtime PM to - * power up/down the nested domains. - */ - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); - return ret; - } - - return 0; -} - -static int imx_pgc_domain_resume(struct device *dev) -{ - return pm_runtime_put(dev); -} -#endif - -static const struct dev_pm_ops imx_pgc_domain_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx_pgc_domain_suspend, imx_pgc_domain_resume) -}; - -static const struct platform_device_id imx_pgc_domain_id[] = { - { "imx-pgc-domain", }, - { }, -}; - -static struct platform_driver imx_pgc_domain_driver = { - .driver = { - .name = "imx-pgc", - .pm = &imx_pgc_domain_pm_ops, - }, - .probe = imx_pgc_domain_probe, - .remove = imx_pgc_domain_remove, - .id_table = imx_pgc_domain_id, -}; -builtin_platform_driver(imx_pgc_domain_driver) - -static int imx_gpcv2_probe(struct platform_device *pdev) -{ - const struct imx_pgc_domain_data *domain_data = - of_device_get_match_data(&pdev->dev); - - struct regmap_config regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .rd_table = domain_data->reg_access_table, - .wr_table = domain_data->reg_access_table, - .max_register = SZ_4K, - }; - struct device *dev = &pdev->dev; - struct device_node *pgc_np, *np; - struct regmap *regmap; - void __iomem *base; - int ret; - - pgc_np = of_get_child_by_name(dev->of_node, "pgc"); - if (!pgc_np) { - dev_err(dev, "No power domains specified in DT\n"); - return -EINVAL; - } - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap = devm_regmap_init_mmio(dev, base, ®map_config); - if (IS_ERR(regmap)) { - ret = PTR_ERR(regmap); - dev_err(dev, "failed to init regmap (%d)\n", ret); - return ret; - } - - for_each_child_of_node(pgc_np, np) { - struct platform_device *pd_pdev; - struct imx_pgc_domain *domain; - u32 domain_index; - - if (!of_device_is_available(np)) - continue; - - ret = of_property_read_u32(np, "reg", &domain_index); - if (ret) { - dev_err(dev, "Failed to read 'reg' property\n"); - of_node_put(np); - return ret; - } - - if (domain_index >= domain_data->domains_num) { - dev_warn(dev, - "Domain index %d is out of bounds\n", - domain_index); - continue; - } - - pd_pdev = platform_device_alloc("imx-pgc-domain", - domain_index); - if (!pd_pdev) { - dev_err(dev, "Failed to allocate platform device\n"); - of_node_put(np); - return -ENOMEM; - } - - ret = platform_device_add_data(pd_pdev, - &domain_data->domains[domain_index], - sizeof(domain_data->domains[domain_index])); - if (ret) { - platform_device_put(pd_pdev); - of_node_put(np); - return ret; - } - - domain = pd_pdev->dev.platform_data; - domain->regmap = regmap; - domain->regs = domain_data->pgc_regs; - - domain->genpd.power_on = imx_pgc_power_up; - domain->genpd.power_off = imx_pgc_power_down; - - pd_pdev->dev.parent = dev; - device_set_node(&pd_pdev->dev, of_fwnode_handle(np)); - - ret = platform_device_add(pd_pdev); - if (ret) { - platform_device_put(pd_pdev); - of_node_put(np); - return ret; - } - } - - return 0; -} - -static const struct of_device_id imx_gpcv2_dt_ids[] = { - { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, }, - { .compatible = "fsl,imx8mm-gpc", .data = &imx8mm_pgc_domain_data, }, - { .compatible = "fsl,imx8mn-gpc", .data = &imx8mn_pgc_domain_data, }, - { .compatible = "fsl,imx8mp-gpc", .data = &imx8mp_pgc_domain_data, }, - { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, - { } -}; - -static struct platform_driver imx_gpc_driver = { - .driver = { - .name = "imx-gpcv2", - .of_match_table = imx_gpcv2_dt_ids, - }, - .probe = imx_gpcv2_probe, -}; -builtin_platform_driver(imx_gpc_driver) diff --git a/drivers/genpd/imx/imx8m-blk-ctrl.c b/drivers/genpd/imx/imx8m-blk-ctrl.c deleted file mode 100644 index cc5ef6e2f0a8..000000000000 --- a/drivers/genpd/imx/imx8m-blk-ctrl.c +++ /dev/null @@ -1,899 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -/* - * Copyright 2021 Pengutronix, Lucas Stach - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#define BLK_SFT_RSTN 0x0 -#define BLK_CLK_EN 0x4 -#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */ - -struct imx8m_blk_ctrl_domain; - -struct imx8m_blk_ctrl { - struct device *dev; - struct notifier_block power_nb; - struct device *bus_power_dev; - struct regmap *regmap; - struct imx8m_blk_ctrl_domain *domains; - struct genpd_onecell_data onecell_data; -}; - -struct imx8m_blk_ctrl_domain_data { - const char *name; - const char * const *clk_names; - const char * const *path_names; - const char *gpc_name; - int num_clks; - int num_paths; - u32 rst_mask; - u32 clk_mask; - - /* - * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register - * which is used to control the reset for the MIPI Phy. - * Since it's only present in certain circumstances, - * an if-statement should be used before setting and clearing this - * register. - */ - u32 mipi_phy_rst_mask; -}; - -#define DOMAIN_MAX_CLKS 4 -#define DOMAIN_MAX_PATHS 4 - -struct imx8m_blk_ctrl_domain { - struct generic_pm_domain genpd; - const struct imx8m_blk_ctrl_domain_data *data; - struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; - struct icc_bulk_data paths[DOMAIN_MAX_PATHS]; - struct device *power_dev; - struct imx8m_blk_ctrl *bc; - int num_paths; -}; - -struct imx8m_blk_ctrl_data { - int max_reg; - notifier_fn_t power_notifier_fn; - const struct imx8m_blk_ctrl_domain_data *domains; - int num_domains; -}; - -static inline struct imx8m_blk_ctrl_domain * -to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd); -} - -static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) -{ - struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd); - const struct imx8m_blk_ctrl_domain_data *data = domain->data; - struct imx8m_blk_ctrl *bc = domain->bc; - int ret; - - /* make sure bus domain is awake */ - ret = pm_runtime_get_sync(bc->bus_power_dev); - if (ret < 0) { - pm_runtime_put_noidle(bc->bus_power_dev); - dev_err(bc->dev, "failed to power up bus domain\n"); - return ret; - } - - /* put devices into reset */ - regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); - if (data->mipi_phy_rst_mask) - regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); - - /* enable upstream and blk-ctrl clocks to allow reset to propagate */ - ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); - if (ret) { - dev_err(bc->dev, "failed to enable clocks\n"); - goto bus_put; - } - regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); - - /* power up upstream GPC domain */ - ret = pm_runtime_get_sync(domain->power_dev); - if (ret < 0) { - dev_err(bc->dev, "failed to power up peripheral domain\n"); - goto clk_disable; - } - - /* wait for reset to propagate */ - udelay(5); - - /* release reset */ - regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); - if (data->mipi_phy_rst_mask) - regmap_set_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); - - ret = icc_bulk_set_bw(domain->num_paths, domain->paths); - if (ret) - dev_err(bc->dev, "failed to set icc bw\n"); - - /* disable upstream clocks */ - clk_bulk_disable_unprepare(data->num_clks, domain->clks); - - return 0; - -clk_disable: - clk_bulk_disable_unprepare(data->num_clks, domain->clks); -bus_put: - pm_runtime_put(bc->bus_power_dev); - - return ret; -} - -static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd) -{ - struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd); - const struct imx8m_blk_ctrl_domain_data *data = domain->data; - struct imx8m_blk_ctrl *bc = domain->bc; - - /* put devices into reset and disable clocks */ - if (data->mipi_phy_rst_mask) - regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); - - regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); - regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); - - /* power down upstream GPC domain */ - pm_runtime_put(domain->power_dev); - - /* allow bus domain to suspend */ - pm_runtime_put(bc->bus_power_dev); - - return 0; -} - -static struct lock_class_key blk_ctrl_genpd_lock_class; - -static int imx8m_blk_ctrl_probe(struct platform_device *pdev) -{ - const struct imx8m_blk_ctrl_data *bc_data; - struct device *dev = &pdev->dev; - struct imx8m_blk_ctrl *bc; - void __iomem *base; - int i, ret; - - struct regmap_config regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - }; - - bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); - if (!bc) - return -ENOMEM; - - bc->dev = dev; - - bc_data = of_device_get_match_data(dev); - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap_config.max_register = bc_data->max_reg; - bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); - if (IS_ERR(bc->regmap)) - return dev_err_probe(dev, PTR_ERR(bc->regmap), - "failed to init regmap\n"); - - bc->domains = devm_kcalloc(dev, bc_data->num_domains, - sizeof(struct imx8m_blk_ctrl_domain), - GFP_KERNEL); - if (!bc->domains) - return -ENOMEM; - - bc->onecell_data.num_domains = bc_data->num_domains; - bc->onecell_data.domains = - devm_kcalloc(dev, bc_data->num_domains, - sizeof(struct generic_pm_domain *), GFP_KERNEL); - if (!bc->onecell_data.domains) - return -ENOMEM; - - bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); - if (IS_ERR(bc->bus_power_dev)) { - if (PTR_ERR(bc->bus_power_dev) == -ENODEV) - return dev_err_probe(dev, -EPROBE_DEFER, - "failed to attach power domain \"bus\"\n"); - else - return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), - "failed to attach power domain \"bus\"\n"); - } - - for (i = 0; i < bc_data->num_domains; i++) { - const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i]; - struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; - int j; - - domain->data = data; - domain->num_paths = data->num_paths; - - for (j = 0; j < data->num_clks; j++) - domain->clks[j].id = data->clk_names[j]; - - for (j = 0; j < data->num_paths; j++) { - domain->paths[j].name = data->path_names[j]; - /* Fake value for now, just let ICC could configure NoC mode/priority */ - domain->paths[j].avg_bw = 1; - domain->paths[j].peak_bw = 1; - } - - ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths); - if (ret) { - if (ret != -EPROBE_DEFER) { - dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n"); - domain->num_paths = 0; - } else { - dev_err_probe(dev, ret, "failed to get noc entries\n"); - goto cleanup_pds; - } - } - - ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); - if (ret) { - dev_err_probe(dev, ret, "failed to get clock\n"); - goto cleanup_pds; - } - - domain->power_dev = - dev_pm_domain_attach_by_name(dev, data->gpc_name); - if (IS_ERR(domain->power_dev)) { - dev_err_probe(dev, PTR_ERR(domain->power_dev), - "failed to attach power domain \"%s\"\n", - data->gpc_name); - ret = PTR_ERR(domain->power_dev); - goto cleanup_pds; - } - - domain->genpd.name = data->name; - domain->genpd.power_on = imx8m_blk_ctrl_power_on; - domain->genpd.power_off = imx8m_blk_ctrl_power_off; - domain->bc = bc; - - ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err_probe(dev, ret, - "failed to init power domain \"%s\"\n", - data->gpc_name); - dev_pm_domain_detach(domain->power_dev, true); - goto cleanup_pds; - } - - /* - * We use runtime PM to trigger power on/off of the upstream GPC - * domain, as a strict hierarchical parent/child power domain - * setup doesn't allow us to meet the sequencing requirements. - * This means we have nested locking of genpd locks, without the - * nesting being visible at the genpd level, so we need a - * separate lock class to make lockdep aware of the fact that - * this are separate domain locks that can be nested without a - * self-deadlock. - */ - lockdep_set_class(&domain->genpd.mlock, - &blk_ctrl_genpd_lock_class); - - bc->onecell_data.domains[i] = &domain->genpd; - } - - ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); - if (ret) { - dev_err_probe(dev, ret, "failed to add power domain provider\n"); - goto cleanup_pds; - } - - bc->power_nb.notifier_call = bc_data->power_notifier_fn; - ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); - if (ret) { - dev_err_probe(dev, ret, "failed to add power notifier\n"); - goto cleanup_provider; - } - - dev_set_drvdata(dev, bc); - - ret = devm_of_platform_populate(dev); - if (ret) - goto cleanup_provider; - - return 0; - -cleanup_provider: - of_genpd_del_provider(dev->of_node); -cleanup_pds: - for (i--; i >= 0; i--) { - pm_genpd_remove(&bc->domains[i].genpd); - dev_pm_domain_detach(bc->domains[i].power_dev, true); - } - - dev_pm_domain_detach(bc->bus_power_dev, true); - - return ret; -} - -static int imx8m_blk_ctrl_remove(struct platform_device *pdev) -{ - struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); - int i; - - of_genpd_del_provider(pdev->dev.of_node); - - for (i = 0; bc->onecell_data.num_domains; i++) { - struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; - - pm_genpd_remove(&domain->genpd); - dev_pm_domain_detach(domain->power_dev, true); - } - - dev_pm_genpd_remove_notifier(bc->bus_power_dev); - - dev_pm_domain_detach(bc->bus_power_dev, true); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int imx8m_blk_ctrl_suspend(struct device *dev) -{ - struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev); - int ret, i; - - /* - * This may look strange, but is done so the generic PM_SLEEP code - * can power down our domains and more importantly power them up again - * after resume, without tripping over our usage of runtime PM to - * control the upstream GPC domains. Things happen in the right order - * in the system suspend/resume paths due to the device parent/child - * hierarchy. - */ - ret = pm_runtime_get_sync(bc->bus_power_dev); - if (ret < 0) { - pm_runtime_put_noidle(bc->bus_power_dev); - return ret; - } - - for (i = 0; i < bc->onecell_data.num_domains; i++) { - struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; - - ret = pm_runtime_get_sync(domain->power_dev); - if (ret < 0) { - pm_runtime_put_noidle(domain->power_dev); - goto out_fail; - } - } - - return 0; - -out_fail: - for (i--; i >= 0; i--) - pm_runtime_put(bc->domains[i].power_dev); - - pm_runtime_put(bc->bus_power_dev); - - return ret; -} - -static int imx8m_blk_ctrl_resume(struct device *dev) -{ - struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev); - int i; - - for (i = 0; i < bc->onecell_data.num_domains; i++) - pm_runtime_put(bc->domains[i].power_dev); - - pm_runtime_put(bc->bus_power_dev); - - return 0; -} -#endif - -static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume) -}; - -static int imx8mm_vpu_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, - power_nb); - - if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) - return NOTIFY_OK; - - /* - * The ADB in the VPUMIX domain has no separate reset and clock - * enable bits, but is ungated together with the VPU clocks. To - * allow the handshake with the GPC to progress we put the VPUs - * in reset and ungate the clocks. - */ - regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2)); - regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2)); - - if (action == GENPD_NOTIFY_ON) { - /* - * On power up we have no software backchannel to the GPC to - * wait for the ADB handshake to happen, so we just delay for a - * bit. On power down the GPC driver waits for the handshake. - */ - udelay(5); - - /* set "fuse" bits to enable the VPUs */ - regmap_set_bits(bc->regmap, 0x8, 0xffffffff); - regmap_set_bits(bc->regmap, 0xc, 0xffffffff); - regmap_set_bits(bc->regmap, 0x10, 0xffffffff); - regmap_set_bits(bc->regmap, 0x14, 0xffffffff); - } - - return NOTIFY_OK; -} - -static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = { - [IMX8MM_VPUBLK_PD_G1] = { - .name = "vpublk-g1", - .clk_names = (const char *[]){ "g1", }, - .num_clks = 1, - .gpc_name = "g1", - .rst_mask = BIT(1), - .clk_mask = BIT(1), - }, - [IMX8MM_VPUBLK_PD_G2] = { - .name = "vpublk-g2", - .clk_names = (const char *[]){ "g2", }, - .num_clks = 1, - .gpc_name = "g2", - .rst_mask = BIT(0), - .clk_mask = BIT(0), - }, - [IMX8MM_VPUBLK_PD_H1] = { - .name = "vpublk-h1", - .clk_names = (const char *[]){ "h1", }, - .num_clks = 1, - .gpc_name = "h1", - .rst_mask = BIT(2), - .clk_mask = BIT(2), - }, -}; - -static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = { - .max_reg = 0x18, - .power_notifier_fn = imx8mm_vpu_power_notifier, - .domains = imx8mm_vpu_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data), -}; - -static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] = { - [IMX8MP_VPUBLK_PD_G1] = { - .name = "vpublk-g1", - .clk_names = (const char *[]){ "g1", }, - .num_clks = 1, - .gpc_name = "g1", - .rst_mask = BIT(1), - .clk_mask = BIT(1), - .path_names = (const char *[]){"g1"}, - .num_paths = 1, - }, - [IMX8MP_VPUBLK_PD_G2] = { - .name = "vpublk-g2", - .clk_names = (const char *[]){ "g2", }, - .num_clks = 1, - .gpc_name = "g2", - .rst_mask = BIT(0), - .clk_mask = BIT(0), - .path_names = (const char *[]){"g2"}, - .num_paths = 1, - }, - [IMX8MP_VPUBLK_PD_VC8000E] = { - .name = "vpublk-vc8000e", - .clk_names = (const char *[]){ "vc8000e", }, - .num_clks = 1, - .gpc_name = "vc8000e", - .rst_mask = BIT(2), - .clk_mask = BIT(2), - .path_names = (const char *[]){"vc8000e"}, - .num_paths = 1, - }, -}; - -static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = { - .max_reg = 0x18, - .power_notifier_fn = imx8mm_vpu_power_notifier, - .domains = imx8mp_vpu_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data), -}; - -static int imx8mm_disp_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, - power_nb); - - if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) - return NOTIFY_OK; - - /* Enable bus clock and deassert bus reset */ - regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12)); - regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6)); - - /* - * On power up we have no software backchannel to the GPC to - * wait for the ADB handshake to happen, so we just delay for a - * bit. On power down the GPC driver waits for the handshake. - */ - if (action == GENPD_NOTIFY_ON) - udelay(5); - - - return NOTIFY_OK; -} - -static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = { - [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = { - .name = "dispblk-csi-bridge", - .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb", - "csi-bridge-core", }, - .num_clks = 3, - .gpc_name = "csi-bridge", - .rst_mask = BIT(0) | BIT(1) | BIT(2), - .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5), - }, - [IMX8MM_DISPBLK_PD_LCDIF] = { - .name = "dispblk-lcdif", - .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, - .num_clks = 3, - .gpc_name = "lcdif", - .clk_mask = BIT(6) | BIT(7), - }, - [IMX8MM_DISPBLK_PD_MIPI_DSI] = { - .name = "dispblk-mipi-dsi", - .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, - .num_clks = 2, - .gpc_name = "mipi-dsi", - .rst_mask = BIT(5), - .clk_mask = BIT(8) | BIT(9), - .mipi_phy_rst_mask = BIT(17), - }, - [IMX8MM_DISPBLK_PD_MIPI_CSI] = { - .name = "dispblk-mipi-csi", - .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, - .num_clks = 2, - .gpc_name = "mipi-csi", - .rst_mask = BIT(3) | BIT(4), - .clk_mask = BIT(10) | BIT(11), - .mipi_phy_rst_mask = BIT(16), - }, -}; - -static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = { - .max_reg = 0x2c, - .power_notifier_fn = imx8mm_disp_power_notifier, - .domains = imx8mm_disp_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data), -}; - - -static int imx8mn_disp_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, - power_nb); - - if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) - return NOTIFY_OK; - - /* Enable bus clock and deassert bus reset */ - regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); - regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); - - /* - * On power up we have no software backchannel to the GPC to - * wait for the ADB handshake to happen, so we just delay for a - * bit. On power down the GPC driver waits for the handshake. - */ - if (action == GENPD_NOTIFY_ON) - udelay(5); - - - return NOTIFY_OK; -} - -static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = { - [IMX8MN_DISPBLK_PD_MIPI_DSI] = { - .name = "dispblk-mipi-dsi", - .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, - .num_clks = 2, - .gpc_name = "mipi-dsi", - .rst_mask = BIT(0) | BIT(1), - .clk_mask = BIT(0) | BIT(1), - .mipi_phy_rst_mask = BIT(17), - }, - [IMX8MN_DISPBLK_PD_MIPI_CSI] = { - .name = "dispblk-mipi-csi", - .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, - .num_clks = 2, - .gpc_name = "mipi-csi", - .rst_mask = BIT(2) | BIT(3), - .clk_mask = BIT(2) | BIT(3), - .mipi_phy_rst_mask = BIT(16), - }, - [IMX8MN_DISPBLK_PD_LCDIF] = { - .name = "dispblk-lcdif", - .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, - .num_clks = 3, - .gpc_name = "lcdif", - .rst_mask = BIT(4) | BIT(5), - .clk_mask = BIT(4) | BIT(5), - }, - [IMX8MN_DISPBLK_PD_ISI] = { - .name = "dispblk-isi", - .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root", - "disp_apb_root"}, - .num_clks = 4, - .gpc_name = "isi", - .rst_mask = BIT(6) | BIT(7), - .clk_mask = BIT(6) | BIT(7), - }, -}; - -static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { - .max_reg = 0x84, - .power_notifier_fn = imx8mn_disp_power_notifier, - .domains = imx8mn_disp_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data), -}; - -#define LCDIF_ARCACHE_CTRL 0x4c -#define LCDIF_1_RD_HURRY GENMASK(15, 13) -#define LCDIF_0_RD_HURRY GENMASK(12, 10) - -static int imx8mp_media_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, - power_nb); - - if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) - return NOTIFY_OK; - - /* Enable bus clock and deassert bus reset */ - regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); - regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); - - if (action == GENPD_NOTIFY_ON) { - /* - * On power up we have no software backchannel to the GPC to - * wait for the ADB handshake to happen, so we just delay for a - * bit. On power down the GPC driver waits for the handshake. - */ - udelay(5); - - /* - * Set panic read hurry level for both LCDIF interfaces to - * maximum priority to minimize chances of display FIFO - * underflow. - */ - regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL, - FIELD_PREP(LCDIF_1_RD_HURRY, 7) | - FIELD_PREP(LCDIF_0_RD_HURRY, 7)); - } - - return NOTIFY_OK; -} - -/* - * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1, - * section 13.2.2, 13.2.3 - * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks - */ -static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = { - [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = { - .name = "mediablk-mipi-dsi-1", - .clk_names = (const char *[]){ "apb", "phy", }, - .num_clks = 2, - .gpc_name = "mipi-dsi1", - .rst_mask = BIT(0) | BIT(1), - .clk_mask = BIT(0) | BIT(1), - .mipi_phy_rst_mask = BIT(17), - }, - [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = { - .name = "mediablk-mipi-csi2-1", - .clk_names = (const char *[]){ "apb", "cam1" }, - .num_clks = 2, - .gpc_name = "mipi-csi1", - .rst_mask = BIT(2) | BIT(3), - .clk_mask = BIT(2) | BIT(3), - .mipi_phy_rst_mask = BIT(16), - }, - [IMX8MP_MEDIABLK_PD_LCDIF_1] = { - .name = "mediablk-lcdif-1", - .clk_names = (const char *[]){ "disp1", "apb", "axi", }, - .num_clks = 3, - .gpc_name = "lcdif1", - .rst_mask = BIT(4) | BIT(5) | BIT(23), - .clk_mask = BIT(4) | BIT(5) | BIT(23), - .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"}, - .num_paths = 2, - }, - [IMX8MP_MEDIABLK_PD_ISI] = { - .name = "mediablk-isi", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, - .gpc_name = "isi", - .rst_mask = BIT(6) | BIT(7), - .clk_mask = BIT(6) | BIT(7), - .path_names = (const char *[]){"isi0", "isi1", "isi2"}, - .num_paths = 3, - }, - [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = { - .name = "mediablk-mipi-csi2-2", - .clk_names = (const char *[]){ "apb", "cam2" }, - .num_clks = 2, - .gpc_name = "mipi-csi2", - .rst_mask = BIT(9) | BIT(10), - .clk_mask = BIT(9) | BIT(10), - .mipi_phy_rst_mask = BIT(30), - }, - [IMX8MP_MEDIABLK_PD_LCDIF_2] = { - .name = "mediablk-lcdif-2", - .clk_names = (const char *[]){ "disp2", "apb", "axi", }, - .num_clks = 3, - .gpc_name = "lcdif2", - .rst_mask = BIT(11) | BIT(12) | BIT(24), - .clk_mask = BIT(11) | BIT(12) | BIT(24), - .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"}, - .num_paths = 2, - }, - [IMX8MP_MEDIABLK_PD_ISP] = { - .name = "mediablk-isp", - .clk_names = (const char *[]){ "isp", "axi", "apb" }, - .num_clks = 3, - .gpc_name = "isp", - .rst_mask = BIT(16) | BIT(17) | BIT(18), - .clk_mask = BIT(16) | BIT(17) | BIT(18), - .path_names = (const char *[]){"isp0", "isp1"}, - .num_paths = 2, - }, - [IMX8MP_MEDIABLK_PD_DWE] = { - .name = "mediablk-dwe", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, - .gpc_name = "dwe", - .rst_mask = BIT(19) | BIT(20) | BIT(21), - .clk_mask = BIT(19) | BIT(20) | BIT(21), - .path_names = (const char *[]){"dwe"}, - .num_paths = 1, - }, - [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = { - .name = "mediablk-mipi-dsi-2", - .clk_names = (const char *[]){ "phy", }, - .num_clks = 1, - .gpc_name = "mipi-dsi2", - .rst_mask = BIT(22), - .clk_mask = BIT(22), - .mipi_phy_rst_mask = BIT(29), - }, -}; - -static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = { - .max_reg = 0x138, - .power_notifier_fn = imx8mp_media_power_notifier, - .domains = imx8mp_media_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data), -}; - -static int imx8mq_vpu_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, - power_nb); - - if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) - return NOTIFY_OK; - - /* - * The ADB in the VPUMIX domain has no separate reset and clock - * enable bits, but is ungated and reset together with the VPUs. The - * reset and clock enable inputs to the ADB is a logical OR of the - * VPU bits. In order to set the G2 fuse bits, the G2 clock must - * also be enabled. - */ - regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1)); - regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1)); - - if (action == GENPD_NOTIFY_ON) { - /* - * On power up we have no software backchannel to the GPC to - * wait for the ADB handshake to happen, so we just delay for a - * bit. On power down the GPC driver waits for the handshake. - */ - udelay(5); - - /* set "fuse" bits to enable the VPUs */ - regmap_set_bits(bc->regmap, 0x8, 0xffffffff); - regmap_set_bits(bc->regmap, 0xc, 0xffffffff); - regmap_set_bits(bc->regmap, 0x10, 0xffffffff); - } - - return NOTIFY_OK; -} - -static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = { - [IMX8MQ_VPUBLK_PD_G1] = { - .name = "vpublk-g1", - .clk_names = (const char *[]){ "g1", }, - .num_clks = 1, - .gpc_name = "g1", - .rst_mask = BIT(1), - .clk_mask = BIT(1), - }, - [IMX8MQ_VPUBLK_PD_G2] = { - .name = "vpublk-g2", - .clk_names = (const char *[]){ "g2", }, - .num_clks = 1, - .gpc_name = "g2", - .rst_mask = BIT(0), - .clk_mask = BIT(0), - }, -}; - -static const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = { - .max_reg = 0x14, - .power_notifier_fn = imx8mq_vpu_power_notifier, - .domains = imx8mq_vpu_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data), -}; - -static const struct of_device_id imx8m_blk_ctrl_of_match[] = { - { - .compatible = "fsl,imx8mm-vpu-blk-ctrl", - .data = &imx8mm_vpu_blk_ctl_dev_data - }, { - .compatible = "fsl,imx8mm-disp-blk-ctrl", - .data = &imx8mm_disp_blk_ctl_dev_data - }, { - .compatible = "fsl,imx8mn-disp-blk-ctrl", - .data = &imx8mn_disp_blk_ctl_dev_data - }, { - .compatible = "fsl,imx8mp-media-blk-ctrl", - .data = &imx8mp_media_blk_ctl_dev_data - }, { - .compatible = "fsl,imx8mq-vpu-blk-ctrl", - .data = &imx8mq_vpu_blk_ctl_dev_data - }, { - .compatible = "fsl,imx8mp-vpu-blk-ctrl", - .data = &imx8mp_vpu_blk_ctl_dev_data - }, { - /* Sentinel */ - } -}; -MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match); - -static struct platform_driver imx8m_blk_ctrl_driver = { - .probe = imx8m_blk_ctrl_probe, - .remove = imx8m_blk_ctrl_remove, - .driver = { - .name = "imx8m-blk-ctrl", - .pm = &imx8m_blk_ctrl_pm_ops, - .of_match_table = imx8m_blk_ctrl_of_match, - }, -}; -module_platform_driver(imx8m_blk_ctrl_driver); -MODULE_LICENSE("GPL"); diff --git a/drivers/genpd/imx/imx8mp-blk-ctrl.c b/drivers/genpd/imx/imx8mp-blk-ctrl.c deleted file mode 100644 index c6ac32c1a8c1..000000000000 --- a/drivers/genpd/imx/imx8mp-blk-ctrl.c +++ /dev/null @@ -1,867 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ - -/* - * Copyright 2022 Pengutronix, Lucas Stach - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define GPR_REG0 0x0 -#define PCIE_CLOCK_MODULE_EN BIT(0) -#define USB_CLOCK_MODULE_EN BIT(1) -#define PCIE_PHY_APB_RST BIT(4) -#define PCIE_PHY_INIT_RST BIT(5) -#define GPR_REG1 0x4 -#define PLL_LOCK BIT(13) -#define GPR_REG2 0x8 -#define P_PLL_MASK GENMASK(5, 0) -#define M_PLL_MASK GENMASK(15, 6) -#define S_PLL_MASK GENMASK(18, 16) -#define GPR_REG3 0xc -#define PLL_CKE BIT(17) -#define PLL_RST BIT(31) - -struct imx8mp_blk_ctrl_domain; - -struct imx8mp_blk_ctrl { - struct device *dev; - struct notifier_block power_nb; - struct device *bus_power_dev; - struct regmap *regmap; - struct imx8mp_blk_ctrl_domain *domains; - struct genpd_onecell_data onecell_data; - void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); - void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); -}; - -struct imx8mp_blk_ctrl_domain_data { - const char *name; - const char * const *clk_names; - int num_clks; - const char * const *path_names; - int num_paths; - const char *gpc_name; -}; - -#define DOMAIN_MAX_CLKS 2 -#define DOMAIN_MAX_PATHS 3 - -struct imx8mp_blk_ctrl_domain { - struct generic_pm_domain genpd; - const struct imx8mp_blk_ctrl_domain_data *data; - struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; - struct icc_bulk_data paths[DOMAIN_MAX_PATHS]; - struct device *power_dev; - struct imx8mp_blk_ctrl *bc; - int num_paths; - int id; -}; - -struct imx8mp_blk_ctrl_data { - int max_reg; - int (*probe) (struct imx8mp_blk_ctrl *bc); - notifier_fn_t power_notifier_fn; - void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); - void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); - const struct imx8mp_blk_ctrl_domain_data *domains; - int num_domains; -}; - -static inline struct imx8mp_blk_ctrl_domain * -to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd); -} - -struct clk_hsio_pll { - struct clk_hw hw; - struct regmap *regmap; -}; - -static inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw) -{ - return container_of(hw, struct clk_hsio_pll, hw); -} - -static int clk_hsio_pll_prepare(struct clk_hw *hw) -{ - struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); - u32 val; - - /* set the PLL configuration */ - regmap_update_bits(clk->regmap, GPR_REG2, - P_PLL_MASK | M_PLL_MASK | S_PLL_MASK, - FIELD_PREP(P_PLL_MASK, 12) | - FIELD_PREP(M_PLL_MASK, 800) | - FIELD_PREP(S_PLL_MASK, 4)); - - /* de-assert PLL reset */ - regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST); - - /* enable PLL */ - regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE); - - return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val, - val & PLL_LOCK, 10, 100); -} - -static void clk_hsio_pll_unprepare(struct clk_hw *hw) -{ - struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); - - regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0); -} - -static int clk_hsio_pll_is_prepared(struct clk_hw *hw) -{ - struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); - - return regmap_test_bits(clk->regmap, GPR_REG1, PLL_LOCK); -} - -static unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw, - unsigned long parent_rate) -{ - return 100000000; -} - -static const struct clk_ops clk_hsio_pll_ops = { - .prepare = clk_hsio_pll_prepare, - .unprepare = clk_hsio_pll_unprepare, - .is_prepared = clk_hsio_pll_is_prepared, - .recalc_rate = clk_hsio_pll_recalc_rate, -}; - -static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc) -{ - struct clk_hsio_pll *clk_hsio_pll; - struct clk_hw *hw; - struct clk_init_data init = {}; - int ret; - - clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL); - if (!clk_hsio_pll) - return -ENOMEM; - - init.name = "hsio_pll"; - init.ops = &clk_hsio_pll_ops; - init.parent_names = (const char *[]){"osc_24m"}; - init.num_parents = 1; - - clk_hsio_pll->regmap = bc->regmap; - clk_hsio_pll->hw.init = &init; - - hw = &clk_hsio_pll->hw; - ret = devm_clk_hw_register(bc->bus_power_dev, hw); - if (ret) - return ret; - - return devm_of_clk_add_hw_provider(bc->dev, of_clk_hw_simple_get, hw); -} - -static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, - struct imx8mp_blk_ctrl_domain *domain) -{ - switch (domain->id) { - case IMX8MP_HSIOBLK_PD_USB: - regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); - break; - case IMX8MP_HSIOBLK_PD_PCIE: - regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); - break; - case IMX8MP_HSIOBLK_PD_PCIE_PHY: - regmap_set_bits(bc->regmap, GPR_REG0, - PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST); - break; - default: - break; - } -} - -static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, - struct imx8mp_blk_ctrl_domain *domain) -{ - switch (domain->id) { - case IMX8MP_HSIOBLK_PD_USB: - regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); - break; - case IMX8MP_HSIOBLK_PD_PCIE: - regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); - break; - case IMX8MP_HSIOBLK_PD_PCIE_PHY: - regmap_clear_bits(bc->regmap, GPR_REG0, - PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST); - break; - default: - break; - } -} - -static int imx8mp_hsio_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, - power_nb); - struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks; - int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks; - int ret; - - switch (action) { - case GENPD_NOTIFY_ON: - /* - * enable USB clock for a moment for the power-on ADB handshake - * to proceed - */ - ret = clk_bulk_prepare_enable(num_clks, usb_clk); - if (ret) - return NOTIFY_BAD; - regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); - - udelay(5); - - regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); - clk_bulk_disable_unprepare(num_clks, usb_clk); - break; - case GENPD_NOTIFY_PRE_OFF: - /* enable USB clock for the power-down ADB handshake to work */ - ret = clk_bulk_prepare_enable(num_clks, usb_clk); - if (ret) - return NOTIFY_BAD; - - regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); - break; - case GENPD_NOTIFY_OFF: - clk_bulk_disable_unprepare(num_clks, usb_clk); - break; - default: - break; - } - - return NOTIFY_OK; -} - -static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = { - [IMX8MP_HSIOBLK_PD_USB] = { - .name = "hsioblk-usb", - .clk_names = (const char *[]){ "usb" }, - .num_clks = 1, - .gpc_name = "usb", - .path_names = (const char *[]){"usb1", "usb2"}, - .num_paths = 2, - }, - [IMX8MP_HSIOBLK_PD_USB_PHY1] = { - .name = "hsioblk-usb-phy1", - .gpc_name = "usb-phy1", - }, - [IMX8MP_HSIOBLK_PD_USB_PHY2] = { - .name = "hsioblk-usb-phy2", - .gpc_name = "usb-phy2", - }, - [IMX8MP_HSIOBLK_PD_PCIE] = { - .name = "hsioblk-pcie", - .clk_names = (const char *[]){ "pcie" }, - .num_clks = 1, - .gpc_name = "pcie", - .path_names = (const char *[]){"noc-pcie", "pcie"}, - .num_paths = 2, - }, - [IMX8MP_HSIOBLK_PD_PCIE_PHY] = { - .name = "hsioblk-pcie-phy", - .gpc_name = "pcie-phy", - }, -}; - -static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = { - .max_reg = 0x24, - .probe = imx8mp_hsio_blk_ctrl_probe, - .power_on = imx8mp_hsio_blk_ctrl_power_on, - .power_off = imx8mp_hsio_blk_ctrl_power_off, - .power_notifier_fn = imx8mp_hsio_power_notifier, - .domains = imx8mp_hsio_domain_data, - .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data), -}; - -#define HDMI_RTX_RESET_CTL0 0x20 -#define HDMI_RTX_CLK_CTL0 0x40 -#define HDMI_RTX_CLK_CTL1 0x50 -#define HDMI_RTX_CLK_CTL2 0x60 -#define HDMI_RTX_CLK_CTL3 0x70 -#define HDMI_RTX_CLK_CTL4 0x80 -#define HDMI_TX_CONTROL0 0x200 -#define HDMI_LCDIF_NOC_HURRY_MASK GENMASK(14, 12) - -static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, - struct imx8mp_blk_ctrl_domain *domain) -{ - switch (domain->id) { - case IMX8MP_HDMIBLK_PD_IRQSTEER: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); - break; - case IMX8MP_HDMIBLK_PD_LCDIF: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, - BIT(16) | BIT(17) | BIT(18) | - BIT(19) | BIT(20)); - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, - BIT(4) | BIT(5) | BIT(6)); - regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, - FIELD_PREP(HDMI_LCDIF_NOC_HURRY_MASK, 7)); - break; - case IMX8MP_HDMIBLK_PD_PAI: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); - break; - case IMX8MP_HDMIBLK_PD_PVI: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); - break; - case IMX8MP_HDMIBLK_PD_TRNG: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); - break; - case IMX8MP_HDMIBLK_PD_HDMI_TX: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, - BIT(2) | BIT(4) | BIT(5)); - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, - BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | - BIT(18) | BIT(19) | BIT(20) | BIT(21)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, - BIT(7) | BIT(10) | BIT(11)); - regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); - break; - case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); - regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); - break; - case IMX8MP_HDMIBLK_PD_HDCP: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); - break; - case IMX8MP_HDMIBLK_PD_HRV: - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); - break; - default: - break; - } -} - -static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, - struct imx8mp_blk_ctrl_domain *domain) -{ - switch (domain->id) { - case IMX8MP_HDMIBLK_PD_IRQSTEER: - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); - break; - case IMX8MP_HDMIBLK_PD_LCDIF: - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, - BIT(4) | BIT(5) | BIT(6)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, - BIT(16) | BIT(17) | BIT(18) | - BIT(19) | BIT(20)); - break; - case IMX8MP_HDMIBLK_PD_PAI: - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); - break; - case IMX8MP_HDMIBLK_PD_PVI: - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); - break; - case IMX8MP_HDMIBLK_PD_TRNG: - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); - break; - case IMX8MP_HDMIBLK_PD_HDMI_TX: - regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, - BIT(7) | BIT(10) | BIT(11)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, - BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | - BIT(18) | BIT(19) | BIT(20) | BIT(21)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, - BIT(2) | BIT(4) | BIT(5)); - break; - case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: - regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); - break; - case IMX8MP_HDMIBLK_PD_HDCP: - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); - break; - case IMX8MP_HDMIBLK_PD_HRV: - regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); - regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); - break; - default: - break; - } -} - -static int imx8mp_hdmi_power_notifier(struct notifier_block *nb, - unsigned long action, void *data) -{ - struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, - power_nb); - - if (action != GENPD_NOTIFY_ON) - return NOTIFY_OK; - - /* - * Contrary to other blk-ctrls the reset and clock don't clear when the - * power domain is powered down. To ensure the proper reset pulsing, - * first clear them all to asserted state, then enable the bus clocks - * and then release the ADB reset. - */ - regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0); - regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0); - regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0); - regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, - BIT(0) | BIT(1) | BIT(10)); - regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0)); - - /* - * On power up we have no software backchannel to the GPC to - * wait for the ADB handshake to happen, so we just delay for a - * bit. On power down the GPC driver waits for the handshake. - */ - udelay(5); - - return NOTIFY_OK; -} - -static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { - [IMX8MP_HDMIBLK_PD_IRQSTEER] = { - .name = "hdmiblk-irqsteer", - .clk_names = (const char *[]){ "apb" }, - .num_clks = 1, - .gpc_name = "irqsteer", - }, - [IMX8MP_HDMIBLK_PD_LCDIF] = { - .name = "hdmiblk-lcdif", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, - .gpc_name = "lcdif", - .path_names = (const char *[]){"lcdif-hdmi"}, - .num_paths = 1, - }, - [IMX8MP_HDMIBLK_PD_PAI] = { - .name = "hdmiblk-pai", - .clk_names = (const char *[]){ "apb" }, - .num_clks = 1, - .gpc_name = "pai", - }, - [IMX8MP_HDMIBLK_PD_PVI] = { - .name = "hdmiblk-pvi", - .clk_names = (const char *[]){ "apb" }, - .num_clks = 1, - .gpc_name = "pvi", - }, - [IMX8MP_HDMIBLK_PD_TRNG] = { - .name = "hdmiblk-trng", - .clk_names = (const char *[]){ "apb" }, - .num_clks = 1, - .gpc_name = "trng", - }, - [IMX8MP_HDMIBLK_PD_HDMI_TX] = { - .name = "hdmiblk-hdmi-tx", - .clk_names = (const char *[]){ "apb", "ref_266m" }, - .num_clks = 2, - .gpc_name = "hdmi-tx", - }, - [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = { - .name = "hdmiblk-hdmi-tx-phy", - .clk_names = (const char *[]){ "apb", "ref_24m" }, - .num_clks = 2, - .gpc_name = "hdmi-tx-phy", - }, - [IMX8MP_HDMIBLK_PD_HRV] = { - .name = "hdmiblk-hrv", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, - .gpc_name = "hrv", - .path_names = (const char *[]){"hrv"}, - .num_paths = 1, - }, - [IMX8MP_HDMIBLK_PD_HDCP] = { - .name = "hdmiblk-hdcp", - .clk_names = (const char *[]){ "axi", "apb" }, - .num_clks = 2, - .gpc_name = "hdcp", - .path_names = (const char *[]){"hdcp"}, - .num_paths = 1, - }, -}; - -static const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = { - .max_reg = 0x23c, - .power_on = imx8mp_hdmi_blk_ctrl_power_on, - .power_off = imx8mp_hdmi_blk_ctrl_power_off, - .power_notifier_fn = imx8mp_hdmi_power_notifier, - .domains = imx8mp_hdmi_domain_data, - .num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data), -}; - -static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) -{ - struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); - const struct imx8mp_blk_ctrl_domain_data *data = domain->data; - struct imx8mp_blk_ctrl *bc = domain->bc; - int ret; - - /* make sure bus domain is awake */ - ret = pm_runtime_resume_and_get(bc->bus_power_dev); - if (ret < 0) { - dev_err(bc->dev, "failed to power up bus domain\n"); - return ret; - } - - /* enable upstream clocks */ - ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); - if (ret) { - dev_err(bc->dev, "failed to enable clocks\n"); - goto bus_put; - } - - /* domain specific blk-ctrl manipulation */ - bc->power_on(bc, domain); - - /* power up upstream GPC domain */ - ret = pm_runtime_resume_and_get(domain->power_dev); - if (ret < 0) { - dev_err(bc->dev, "failed to power up peripheral domain\n"); - goto clk_disable; - } - - ret = icc_bulk_set_bw(domain->num_paths, domain->paths); - if (ret) - dev_err(bc->dev, "failed to set icc bw\n"); - - clk_bulk_disable_unprepare(data->num_clks, domain->clks); - - return 0; - -clk_disable: - clk_bulk_disable_unprepare(data->num_clks, domain->clks); -bus_put: - pm_runtime_put(bc->bus_power_dev); - - return ret; -} - -static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd) -{ - struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); - const struct imx8mp_blk_ctrl_domain_data *data = domain->data; - struct imx8mp_blk_ctrl *bc = domain->bc; - int ret; - - ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); - if (ret) { - dev_err(bc->dev, "failed to enable clocks\n"); - return ret; - } - - /* domain specific blk-ctrl manipulation */ - bc->power_off(bc, domain); - - clk_bulk_disable_unprepare(data->num_clks, domain->clks); - - /* power down upstream GPC domain */ - pm_runtime_put(domain->power_dev); - - /* allow bus domain to suspend */ - pm_runtime_put(bc->bus_power_dev); - - return 0; -} - -static struct lock_class_key blk_ctrl_genpd_lock_class; - -static int imx8mp_blk_ctrl_probe(struct platform_device *pdev) -{ - const struct imx8mp_blk_ctrl_data *bc_data; - struct device *dev = &pdev->dev; - struct imx8mp_blk_ctrl *bc; - void __iomem *base; - int num_domains, i, ret; - - struct regmap_config regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - }; - - bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); - if (!bc) - return -ENOMEM; - - bc->dev = dev; - - bc_data = of_device_get_match_data(dev); - num_domains = bc_data->num_domains; - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - regmap_config.max_register = bc_data->max_reg; - bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); - if (IS_ERR(bc->regmap)) - return dev_err_probe(dev, PTR_ERR(bc->regmap), - "failed to init regmap\n"); - - bc->domains = devm_kcalloc(dev, num_domains, - sizeof(struct imx8mp_blk_ctrl_domain), - GFP_KERNEL); - if (!bc->domains) - return -ENOMEM; - - bc->onecell_data.num_domains = num_domains; - bc->onecell_data.domains = - devm_kcalloc(dev, num_domains, - sizeof(struct generic_pm_domain *), GFP_KERNEL); - if (!bc->onecell_data.domains) - return -ENOMEM; - - bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); - if (IS_ERR(bc->bus_power_dev)) - return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), - "failed to attach bus power domain\n"); - - bc->power_off = bc_data->power_off; - bc->power_on = bc_data->power_on; - - for (i = 0; i < num_domains; i++) { - const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i]; - struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; - int j; - - domain->data = data; - domain->num_paths = data->num_paths; - - for (j = 0; j < data->num_clks; j++) - domain->clks[j].id = data->clk_names[j]; - - for (j = 0; j < data->num_paths; j++) { - domain->paths[j].name = data->path_names[j]; - /* Fake value for now, just let ICC could configure NoC mode/priority */ - domain->paths[j].avg_bw = 1; - domain->paths[j].peak_bw = 1; - } - - ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths); - if (ret) { - if (ret != -EPROBE_DEFER) { - dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n"); - domain->num_paths = 0; - } else { - dev_err_probe(dev, ret, "failed to get noc entries\n"); - goto cleanup_pds; - } - } - - ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); - if (ret) { - dev_err_probe(dev, ret, "failed to get clock\n"); - goto cleanup_pds; - } - - domain->power_dev = - dev_pm_domain_attach_by_name(dev, data->gpc_name); - if (IS_ERR(domain->power_dev)) { - dev_err_probe(dev, PTR_ERR(domain->power_dev), - "failed to attach power domain %s\n", - data->gpc_name); - ret = PTR_ERR(domain->power_dev); - goto cleanup_pds; - } - - domain->genpd.name = data->name; - domain->genpd.power_on = imx8mp_blk_ctrl_power_on; - domain->genpd.power_off = imx8mp_blk_ctrl_power_off; - domain->bc = bc; - domain->id = i; - - ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err_probe(dev, ret, "failed to init power domain\n"); - dev_pm_domain_detach(domain->power_dev, true); - goto cleanup_pds; - } - - /* - * We use runtime PM to trigger power on/off of the upstream GPC - * domain, as a strict hierarchical parent/child power domain - * setup doesn't allow us to meet the sequencing requirements. - * This means we have nested locking of genpd locks, without the - * nesting being visible at the genpd level, so we need a - * separate lock class to make lockdep aware of the fact that - * this are separate domain locks that can be nested without a - * self-deadlock. - */ - lockdep_set_class(&domain->genpd.mlock, - &blk_ctrl_genpd_lock_class); - - bc->onecell_data.domains[i] = &domain->genpd; - } - - ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); - if (ret) { - dev_err_probe(dev, ret, "failed to add power domain provider\n"); - goto cleanup_pds; - } - - bc->power_nb.notifier_call = bc_data->power_notifier_fn; - ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); - if (ret) { - dev_err_probe(dev, ret, "failed to add power notifier\n"); - goto cleanup_provider; - } - - if (bc_data->probe) { - ret = bc_data->probe(bc); - if (ret) - goto cleanup_provider; - } - - dev_set_drvdata(dev, bc); - - return 0; - -cleanup_provider: - of_genpd_del_provider(dev->of_node); -cleanup_pds: - for (i--; i >= 0; i--) { - pm_genpd_remove(&bc->domains[i].genpd); - dev_pm_domain_detach(bc->domains[i].power_dev, true); - } - - dev_pm_domain_detach(bc->bus_power_dev, true); - - return ret; -} - -static int imx8mp_blk_ctrl_remove(struct platform_device *pdev) -{ - struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); - int i; - - of_genpd_del_provider(pdev->dev.of_node); - - for (i = 0; bc->onecell_data.num_domains; i++) { - struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; - - pm_genpd_remove(&domain->genpd); - dev_pm_domain_detach(domain->power_dev, true); - } - - dev_pm_genpd_remove_notifier(bc->bus_power_dev); - - dev_pm_domain_detach(bc->bus_power_dev, true); - - return 0; -} - -#ifdef CONFIG_PM_SLEEP -static int imx8mp_blk_ctrl_suspend(struct device *dev) -{ - struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); - int ret, i; - - /* - * This may look strange, but is done so the generic PM_SLEEP code - * can power down our domains and more importantly power them up again - * after resume, without tripping over our usage of runtime PM to - * control the upstream GPC domains. Things happen in the right order - * in the system suspend/resume paths due to the device parent/child - * hierarchy. - */ - ret = pm_runtime_get_sync(bc->bus_power_dev); - if (ret < 0) { - pm_runtime_put_noidle(bc->bus_power_dev); - return ret; - } - - for (i = 0; i < bc->onecell_data.num_domains; i++) { - struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; - - ret = pm_runtime_get_sync(domain->power_dev); - if (ret < 0) { - pm_runtime_put_noidle(domain->power_dev); - goto out_fail; - } - } - - return 0; - -out_fail: - for (i--; i >= 0; i--) - pm_runtime_put(bc->domains[i].power_dev); - - pm_runtime_put(bc->bus_power_dev); - - return ret; -} - -static int imx8mp_blk_ctrl_resume(struct device *dev) -{ - struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); - int i; - - for (i = 0; i < bc->onecell_data.num_domains; i++) - pm_runtime_put(bc->domains[i].power_dev); - - pm_runtime_put(bc->bus_power_dev); - - return 0; -} -#endif - -static const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend, - imx8mp_blk_ctrl_resume) -}; - -static const struct of_device_id imx8mp_blk_ctrl_of_match[] = { - { - .compatible = "fsl,imx8mp-hsio-blk-ctrl", - .data = &imx8mp_hsio_blk_ctl_dev_data, - }, { - .compatible = "fsl,imx8mp-hdmi-blk-ctrl", - .data = &imx8mp_hdmi_blk_ctl_dev_data, - }, { - /* Sentinel */ - } -}; -MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match); - -static struct platform_driver imx8mp_blk_ctrl_driver = { - .probe = imx8mp_blk_ctrl_probe, - .remove = imx8mp_blk_ctrl_remove, - .driver = { - .name = "imx8mp-blk-ctrl", - .pm = &imx8mp_blk_ctrl_pm_ops, - .of_match_table = imx8mp_blk_ctrl_of_match, - }, -}; -module_platform_driver(imx8mp_blk_ctrl_driver); -MODULE_LICENSE("GPL"); diff --git a/drivers/genpd/imx/imx93-blk-ctrl.c b/drivers/genpd/imx/imx93-blk-ctrl.c deleted file mode 100644 index 40bd90f8b977..000000000000 --- a/drivers/genpd/imx/imx93-blk-ctrl.c +++ /dev/null @@ -1,451 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright 2022 NXP, Peng Fan - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define BLK_SFT_RSTN 0x0 -#define BLK_CLK_EN 0x4 -#define BLK_MAX_CLKS 4 - -#define DOMAIN_MAX_CLKS 4 - -#define LCDIF_QOS_REG 0xC -#define LCDIF_DEFAULT_QOS_OFF 12 -#define LCDIF_CFG_QOS_OFF 8 - -#define PXP_QOS_REG 0x10 -#define PXP_R_DEFAULT_QOS_OFF 28 -#define PXP_R_CFG_QOS_OFF 24 -#define PXP_W_DEFAULT_QOS_OFF 20 -#define PXP_W_CFG_QOS_OFF 16 - -#define ISI_CACHE_REG 0x14 - -#define ISI_QOS_REG 0x1C -#define ISI_V_DEFAULT_QOS_OFF 28 -#define ISI_V_CFG_QOS_OFF 24 -#define ISI_U_DEFAULT_QOS_OFF 20 -#define ISI_U_CFG_QOS_OFF 16 -#define ISI_Y_R_DEFAULT_QOS_OFF 12 -#define ISI_Y_R_CFG_QOS_OFF 8 -#define ISI_Y_W_DEFAULT_QOS_OFF 4 -#define ISI_Y_W_CFG_QOS_OFF 0 - -#define PRIO_MASK 0xF - -#define PRIO(X) (X) - -struct imx93_blk_ctrl_domain; - -struct imx93_blk_ctrl { - struct device *dev; - struct regmap *regmap; - int num_clks; - struct clk_bulk_data clks[BLK_MAX_CLKS]; - struct imx93_blk_ctrl_domain *domains; - struct genpd_onecell_data onecell_data; -}; - -#define DOMAIN_MAX_QOS 4 - -struct imx93_blk_ctrl_qos { - u32 reg; - u32 cfg_off; - u32 default_prio; - u32 cfg_prio; -}; - -struct imx93_blk_ctrl_domain_data { - const char *name; - const char * const *clk_names; - int num_clks; - u32 rst_mask; - u32 clk_mask; - int num_qos; - struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; -}; - -struct imx93_blk_ctrl_domain { - struct generic_pm_domain genpd; - const struct imx93_blk_ctrl_domain_data *data; - struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; - struct imx93_blk_ctrl *bc; -}; - -struct imx93_blk_ctrl_data { - const struct imx93_blk_ctrl_domain_data *domains; - int num_domains; - const char * const *clk_names; - int num_clks; - const struct regmap_access_table *reg_access_table; -}; - -static inline struct imx93_blk_ctrl_domain * -to_imx93_blk_ctrl_domain(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx93_blk_ctrl_domain, genpd); -} - -static int imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain *domain) -{ - const struct imx93_blk_ctrl_domain_data *data = domain->data; - struct imx93_blk_ctrl *bc = domain->bc; - const struct imx93_blk_ctrl_qos *qos; - u32 val, mask; - int i; - - for (i = 0; i < data->num_qos; i++) { - qos = &data->qos[i]; - - mask = PRIO_MASK << qos->cfg_off; - mask |= PRIO_MASK << (qos->cfg_off + 4); - val = qos->cfg_prio << qos->cfg_off; - val |= qos->default_prio << (qos->cfg_off + 4); - - regmap_write_bits(bc->regmap, qos->reg, mask, val); - - dev_dbg(bc->dev, "data->qos[i].reg 0x%x 0x%x\n", qos->reg, val); - } - - return 0; -} - -static int imx93_blk_ctrl_power_on(struct generic_pm_domain *genpd) -{ - struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd); - const struct imx93_blk_ctrl_domain_data *data = domain->data; - struct imx93_blk_ctrl *bc = domain->bc; - int ret; - - ret = clk_bulk_prepare_enable(bc->num_clks, bc->clks); - if (ret) { - dev_err(bc->dev, "failed to enable bus clocks\n"); - return ret; - } - - ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); - if (ret) { - clk_bulk_disable_unprepare(bc->num_clks, bc->clks); - dev_err(bc->dev, "failed to enable clocks\n"); - return ret; - } - - ret = pm_runtime_get_sync(bc->dev); - if (ret < 0) { - pm_runtime_put_noidle(bc->dev); - dev_err(bc->dev, "failed to power up domain\n"); - goto disable_clk; - } - - /* ungate clk */ - regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); - - /* release reset */ - regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); - - dev_dbg(bc->dev, "pd_on: name: %s\n", genpd->name); - - return imx93_blk_ctrl_set_qos(domain); - -disable_clk: - clk_bulk_disable_unprepare(data->num_clks, domain->clks); - - clk_bulk_disable_unprepare(bc->num_clks, bc->clks); - - return ret; -} - -static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd) -{ - struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd); - const struct imx93_blk_ctrl_domain_data *data = domain->data; - struct imx93_blk_ctrl *bc = domain->bc; - - dev_dbg(bc->dev, "pd_off: name: %s\n", genpd->name); - - regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); - regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); - - pm_runtime_put(bc->dev); - - clk_bulk_disable_unprepare(data->num_clks, domain->clks); - - clk_bulk_disable_unprepare(bc->num_clks, bc->clks); - - return 0; -} - -static struct lock_class_key blk_ctrl_genpd_lock_class; - -static int imx93_blk_ctrl_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - const struct imx93_blk_ctrl_data *bc_data = of_device_get_match_data(dev); - struct imx93_blk_ctrl *bc; - void __iomem *base; - int i, ret; - - struct regmap_config regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, - .rd_table = bc_data->reg_access_table, - .wr_table = bc_data->reg_access_table, - .max_register = SZ_4K, - }; - - bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); - if (!bc) - return -ENOMEM; - - bc->dev = dev; - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); - if (IS_ERR(bc->regmap)) - return dev_err_probe(dev, PTR_ERR(bc->regmap), - "failed to init regmap\n"); - - bc->domains = devm_kcalloc(dev, bc_data->num_domains, - sizeof(struct imx93_blk_ctrl_domain), - GFP_KERNEL); - if (!bc->domains) - return -ENOMEM; - - bc->onecell_data.num_domains = bc_data->num_domains; - bc->onecell_data.domains = - devm_kcalloc(dev, bc_data->num_domains, - sizeof(struct generic_pm_domain *), GFP_KERNEL); - if (!bc->onecell_data.domains) - return -ENOMEM; - - for (i = 0; i < bc_data->num_clks; i++) - bc->clks[i].id = bc_data->clk_names[i]; - bc->num_clks = bc_data->num_clks; - - ret = devm_clk_bulk_get(dev, bc->num_clks, bc->clks); - if (ret) { - dev_err_probe(dev, ret, "failed to get bus clock\n"); - return ret; - } - - for (i = 0; i < bc_data->num_domains; i++) { - const struct imx93_blk_ctrl_domain_data *data = &bc_data->domains[i]; - struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; - int j; - - domain->data = data; - - for (j = 0; j < data->num_clks; j++) - domain->clks[j].id = data->clk_names[j]; - - ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); - if (ret) { - dev_err_probe(dev, ret, "failed to get clock\n"); - goto cleanup_pds; - } - - domain->genpd.name = data->name; - domain->genpd.power_on = imx93_blk_ctrl_power_on; - domain->genpd.power_off = imx93_blk_ctrl_power_off; - domain->bc = bc; - - ret = pm_genpd_init(&domain->genpd, NULL, true); - if (ret) { - dev_err_probe(dev, ret, "failed to init power domain\n"); - goto cleanup_pds; - } - - /* - * We use runtime PM to trigger power on/off of the upstream GPC - * domain, as a strict hierarchical parent/child power domain - * setup doesn't allow us to meet the sequencing requirements. - * This means we have nested locking of genpd locks, without the - * nesting being visible at the genpd level, so we need a - * separate lock class to make lockdep aware of the fact that - * this are separate domain locks that can be nested without a - * self-deadlock. - */ - lockdep_set_class(&domain->genpd.mlock, - &blk_ctrl_genpd_lock_class); - - bc->onecell_data.domains[i] = &domain->genpd; - } - - pm_runtime_enable(dev); - - ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); - if (ret) { - dev_err_probe(dev, ret, "failed to add power domain provider\n"); - goto cleanup_pds; - } - - dev_set_drvdata(dev, bc); - - return 0; - -cleanup_pds: - for (i--; i >= 0; i--) - pm_genpd_remove(&bc->domains[i].genpd); - - return ret; -} - -static int imx93_blk_ctrl_remove(struct platform_device *pdev) -{ - struct imx93_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); - int i; - - of_genpd_del_provider(pdev->dev.of_node); - - for (i = 0; bc->onecell_data.num_domains; i++) { - struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; - - pm_genpd_remove(&domain->genpd); - } - - return 0; -} - -static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = { - [IMX93_MEDIABLK_PD_MIPI_DSI] = { - .name = "mediablk-mipi-dsi", - .clk_names = (const char *[]){ "dsi" }, - .num_clks = 1, - .rst_mask = BIT(11) | BIT(12), - .clk_mask = BIT(11) | BIT(12), - }, - [IMX93_MEDIABLK_PD_MIPI_CSI] = { - .name = "mediablk-mipi-csi", - .clk_names = (const char *[]){ "cam", "csi" }, - .num_clks = 2, - .rst_mask = BIT(9) | BIT(10), - .clk_mask = BIT(9) | BIT(10), - }, - [IMX93_MEDIABLK_PD_PXP] = { - .name = "mediablk-pxp", - .clk_names = (const char *[]){ "pxp" }, - .num_clks = 1, - .rst_mask = BIT(7) | BIT(8), - .clk_mask = BIT(7) | BIT(8), - .num_qos = 2, - .qos = { - { - .reg = PXP_QOS_REG, - .cfg_off = PXP_R_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(6), - }, { - .reg = PXP_QOS_REG, - .cfg_off = PXP_W_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(6), - } - } - }, - [IMX93_MEDIABLK_PD_LCDIF] = { - .name = "mediablk-lcdif", - .clk_names = (const char *[]){ "disp", "lcdif" }, - .num_clks = 2, - .rst_mask = BIT(4) | BIT(5) | BIT(6), - .clk_mask = BIT(4) | BIT(5) | BIT(6), - .num_qos = 1, - .qos = { - { - .reg = LCDIF_QOS_REG, - .cfg_off = LCDIF_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(7), - } - } - }, - [IMX93_MEDIABLK_PD_ISI] = { - .name = "mediablk-isi", - .clk_names = (const char *[]){ "isi" }, - .num_clks = 1, - .rst_mask = BIT(2) | BIT(3), - .clk_mask = BIT(2) | BIT(3), - .num_qos = 4, - .qos = { - { - .reg = ISI_QOS_REG, - .cfg_off = ISI_Y_W_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(7), - }, { - .reg = ISI_QOS_REG, - .cfg_off = ISI_Y_R_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(7), - }, { - .reg = ISI_QOS_REG, - .cfg_off = ISI_U_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(7), - }, { - .reg = ISI_QOS_REG, - .cfg_off = ISI_V_CFG_QOS_OFF, - .default_prio = PRIO(3), - .cfg_prio = PRIO(7), - } - } - }, -}; - -static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { - regmap_reg_range(BLK_SFT_RSTN, BLK_CLK_EN), - regmap_reg_range(LCDIF_QOS_REG, ISI_CACHE_REG), - regmap_reg_range(ISI_QOS_REG, ISI_QOS_REG), -}; - -static const struct regmap_access_table imx93_media_blk_ctl_access_table = { - .yes_ranges = imx93_media_blk_ctl_yes_ranges, - .n_yes_ranges = ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges), -}; - -static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data = { - .domains = imx93_media_blk_ctl_domain_data, - .num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data), - .clk_names = (const char *[]){ "axi", "apb", "nic", }, - .num_clks = 3, - .reg_access_table = &imx93_media_blk_ctl_access_table, -}; - -static const struct of_device_id imx93_blk_ctrl_of_match[] = { - { - .compatible = "fsl,imx93-media-blk-ctrl", - .data = &imx93_media_blk_ctl_dev_data - }, { - /* Sentinel */ - } -}; -MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match); - -static struct platform_driver imx93_blk_ctrl_driver = { - .probe = imx93_blk_ctrl_probe, - .remove = imx93_blk_ctrl_remove, - .driver = { - .name = "imx93-blk-ctrl", - .of_match_table = imx93_blk_ctrl_of_match, - }, -}; -module_platform_driver(imx93_blk_ctrl_driver); - -MODULE_AUTHOR("Peng Fan "); -MODULE_DESCRIPTION("i.MX93 BLK CTRL driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/genpd/imx/imx93-pd.c b/drivers/genpd/imx/imx93-pd.c deleted file mode 100644 index b9e60d136875..000000000000 --- a/drivers/genpd/imx/imx93-pd.c +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright 2022 NXP - */ - -#include -#include -#include -#include -#include -#include -#include - -#define MIX_SLICE_SW_CTRL_OFF 0x20 -#define SLICE_SW_CTRL_PSW_CTRL_OFF_MASK BIT(4) -#define SLICE_SW_CTRL_PDN_SOFT_MASK BIT(31) - -#define MIX_FUNC_STAT_OFF 0xB4 - -#define FUNC_STAT_PSW_STAT_MASK BIT(0) -#define FUNC_STAT_RST_STAT_MASK BIT(2) -#define FUNC_STAT_ISO_STAT_MASK BIT(4) - -struct imx93_power_domain { - struct generic_pm_domain genpd; - struct device *dev; - void __iomem *addr; - struct clk_bulk_data *clks; - int num_clks; - bool init_off; -}; - -#define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd) - -static int imx93_pd_on(struct generic_pm_domain *genpd) -{ - struct imx93_power_domain *domain = to_imx93_pd(genpd); - void __iomem *addr = domain->addr; - u32 val; - int ret; - - ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); - if (ret) { - dev_err(domain->dev, "failed to enable clocks for domain: %s\n", genpd->name); - return ret; - } - - val = readl(addr + MIX_SLICE_SW_CTRL_OFF); - val &= ~SLICE_SW_CTRL_PDN_SOFT_MASK; - writel(val, addr + MIX_SLICE_SW_CTRL_OFF); - - ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, - !(val & FUNC_STAT_ISO_STAT_MASK), 1, 10000); - if (ret) { - dev_err(domain->dev, "pd_on timeout: name: %s, stat: %x\n", genpd->name, val); - return ret; - } - - return 0; -} - -static int imx93_pd_off(struct generic_pm_domain *genpd) -{ - struct imx93_power_domain *domain = to_imx93_pd(genpd); - void __iomem *addr = domain->addr; - int ret; - u32 val; - - /* Power off MIX */ - val = readl(addr + MIX_SLICE_SW_CTRL_OFF); - val |= SLICE_SW_CTRL_PDN_SOFT_MASK; - writel(val, addr + MIX_SLICE_SW_CTRL_OFF); - - ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, - val & FUNC_STAT_PSW_STAT_MASK, 1, 1000); - if (ret) { - dev_err(domain->dev, "pd_off timeout: name: %s, stat: %x\n", genpd->name, val); - return ret; - } - - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - - return 0; -}; - -static int imx93_pd_remove(struct platform_device *pdev) -{ - struct imx93_power_domain *domain = platform_get_drvdata(pdev); - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - - if (!domain->init_off) - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - - of_genpd_del_provider(np); - pm_genpd_remove(&domain->genpd); - - return 0; -} - -static int imx93_pd_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct imx93_power_domain *domain; - int ret; - - domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); - if (!domain) - return -ENOMEM; - - domain->addr = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(domain->addr)) - return PTR_ERR(domain->addr); - - domain->num_clks = devm_clk_bulk_get_all(dev, &domain->clks); - if (domain->num_clks < 0) - return dev_err_probe(dev, domain->num_clks, "Failed to get domain's clocks\n"); - - domain->genpd.name = dev_name(dev); - domain->genpd.power_off = imx93_pd_off; - domain->genpd.power_on = imx93_pd_on; - domain->dev = dev; - - domain->init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK; - /* Just to sync the status of hardware */ - if (!domain->init_off) { - ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); - if (ret) { - dev_err(domain->dev, "failed to enable clocks for domain: %s\n", - domain->genpd.name); - return ret; - } - } - - ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); - if (ret) - goto err_clk_unprepare; - - platform_set_drvdata(pdev, domain); - - ret = of_genpd_add_provider_simple(np, &domain->genpd); - if (ret) - goto err_genpd_remove; - - return 0; - -err_genpd_remove: - pm_genpd_remove(&domain->genpd); - -err_clk_unprepare: - if (!domain->init_off) - clk_bulk_disable_unprepare(domain->num_clks, domain->clks); - - return ret; -} - -static const struct of_device_id imx93_pd_ids[] = { - { .compatible = "fsl,imx93-src-slice" }, - { } -}; -MODULE_DEVICE_TABLE(of, imx93_pd_ids); - -static struct platform_driver imx93_power_domain_driver = { - .driver = { - .name = "imx93_power_domain", - .of_match_table = imx93_pd_ids, - }, - .probe = imx93_pd_probe, - .remove = imx93_pd_remove, -}; -module_platform_driver(imx93_power_domain_driver); - -MODULE_AUTHOR("Peng Fan "); -MODULE_DESCRIPTION("NXP i.MX93 power domain driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/genpd/imx/scu-pd.c b/drivers/genpd/imx/scu-pd.c deleted file mode 100644 index 2f693b67ddb4..000000000000 --- a/drivers/genpd/imx/scu-pd.c +++ /dev/null @@ -1,550 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2016 Freescale Semiconductor, Inc. - * Copyright 2017-2018 NXP - * Dong Aisheng - * - * Implementation of the SCU based Power Domains - * - * NOTE: a better implementation suggested by Ulf Hansson is using a - * single global power domain and implement the ->attach|detach_dev() - * callback for the genpd and use the regular of_genpd_add_provider_simple(). - * From within the ->attach_dev(), we could get the OF node for - * the device that is being attached and then parse the power-domain - * cell containing the "resource id" and store that in the per device - * struct generic_pm_domain_data (we have void pointer there for - * storing these kind of things). - * - * Additionally, we need to implement the ->stop() and ->start() - * callbacks of genpd, which is where you "power on/off" devices, - * rather than using the above ->power_on|off() callbacks. - * - * However, there're two known issues: - * 1. The ->attach_dev() of power domain infrastructure still does - * not support multi domains case as the struct device *dev passed - * in is a virtual PD device, it does not help for parsing the real - * device resource id from device tree, so it's unware of which - * real sub power domain of device should be attached. - * - * The framework needs some proper extension to support multi power - * domain cases. - * - * Update: Genpd assigns the ->of_node for the virtual device before it - * invokes ->attach_dev() callback, hence parsing for device resources via - * DT should work fine. - * - * 2. It also breaks most of current drivers as the driver probe sequence - * behavior changed if removing ->power_on|off() callback and use - * ->start() and ->stop() instead. genpd_dev_pm_attach will only power - * up the domain and attach device, but will not call .start() which - * relies on device runtime pm. That means the device power is still - * not up before running driver probe function. For SCU enabled - * platforms, all device drivers accessing registers/clock without power - * domain enabled will trigger a HW access error. That means we need fix - * most drivers probe sequence with proper runtime pm. - * - * Update: Runtime PM support isn't necessary. Instead, this can easily be - * fixed in drivers by adding a call to dev_pm_domain_start() during probe. - * - * In summary, the second part needs to be addressed via minor updates to the - * relevant drivers, before the "single global power domain" model can be used. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* SCU Power Mode Protocol definition */ -struct imx_sc_msg_req_set_resource_power_mode { - struct imx_sc_rpc_msg hdr; - u16 resource; - u8 mode; -} __packed __aligned(4); - -struct req_get_resource_mode { - u16 resource; -}; - -struct resp_get_resource_mode { - u8 mode; -}; - -struct imx_sc_msg_req_get_resource_power_mode { - struct imx_sc_rpc_msg hdr; - union { - struct req_get_resource_mode req; - struct resp_get_resource_mode resp; - } data; -} __packed __aligned(4); - -#define IMX_SCU_PD_NAME_SIZE 20 -struct imx_sc_pm_domain { - struct generic_pm_domain pd; - char name[IMX_SCU_PD_NAME_SIZE]; - u32 rsrc; -}; - -struct imx_sc_pd_range { - char *name; - u32 rsrc; - u8 num; - - /* add domain index */ - bool postfix; - u8 start_from; -}; - -struct imx_sc_pd_soc { - const struct imx_sc_pd_range *pd_ranges; - u8 num_ranges; -}; - -static int imx_con_rsrc; - -/* Align with the IMX_SC_PM_PW_MODE_[OFF,STBY,LP,ON] macros */ -static const char * const imx_sc_pm_mode[] = { - "IMX_SC_PM_PW_MODE_OFF", - "IMX_SC_PM_PW_MODE_STBY", - "IMX_SC_PM_PW_MODE_LP", - "IMX_SC_PM_PW_MODE_ON" -}; - -static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { - /* LSIO SS */ - { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, - { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, - { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, - { "kpp", IMX_SC_R_KPP, 1, false, 0 }, - { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, - { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, - { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, - - /* CONN SS */ - { "usb", IMX_SC_R_USB_0, 2, true, 0 }, - { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, - { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, - { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, - { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, - { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, - { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, - { "nand", IMX_SC_R_NAND, 1, false, 0 }, - { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, - - /* AUDIO SS */ - { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, - { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, - { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, - { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, - { "mclk-out-0", IMX_SC_R_MCLK_OUT_0, 1, false, 0 }, - { "mclk-out-1", IMX_SC_R_MCLK_OUT_1, 1, false, 0 }, - { "dma0-ch", IMX_SC_R_DMA_0_CH0, 32, true, 0 }, - { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, - { "dma2-ch", IMX_SC_R_DMA_2_CH0, 32, true, 0 }, - { "dma3-ch", IMX_SC_R_DMA_3_CH0, 32, true, 0 }, - { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, - { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, - { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, - { "esai1", IMX_SC_R_ESAI_1, 1, false, 0 }, - { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, - { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, - { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, - { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, - { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, - { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, - { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, - { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, - { "amix", IMX_SC_R_AMIX, 1, false, 0 }, - { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, - { "dsp", IMX_SC_R_DSP, 1, false, 0 }, - { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, - - /* DMA SS */ - { "can", IMX_SC_R_CAN_0, 3, true, 0 }, - { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, - { "lpi2c", IMX_SC_R_I2C_0, 5, true, 0 }, - { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, - { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, - { "lcd-pll", IMX_SC_R_ELCDIF_PLL, 1, true, 0 }, - { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, - { "lpuart", IMX_SC_R_UART_0, 5, true, 0 }, - { "sim", IMX_SC_R_EMVSIM_0, 2, true, 0 }, - { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, - { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, - - /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, false, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, - { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 }, - { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 }, - { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 }, - { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 }, - - /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, - { "gpu1-pid", IMX_SC_R_GPU_1_PID0, 4, true, 0 }, - - - /* HSIO SS */ - { "pcie-a", IMX_SC_R_PCIE_A, 1, false, 0 }, - { "serdes-0", IMX_SC_R_SERDES_0, 1, false, 0 }, - { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, - { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, - { "sata-0", IMX_SC_R_SATA_0, 1, false, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, - - /* MIPI SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, - - { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 }, - { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 }, - { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 }, - - /* LVDS SS */ - { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, - { "lvds0-pwm", IMX_SC_R_LVDS_0_PWM_0, 1, false, 0 }, - { "lvds0-lpi2c", IMX_SC_R_LVDS_0_I2C_0, 2, true, 0 }, - { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 }, - { "lvds1-pwm", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 }, - { "lvds1-lpi2c", IMX_SC_R_LVDS_1_I2C_0, 2, true, 0 }, - - { "mipi1", IMX_SC_R_MIPI_1, 1, 0 }, - { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 }, - { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, 1 }, - { "lvds1", IMX_SC_R_LVDS_1, 1, 0 }, - - /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, - { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 }, - - { "dc1", IMX_SC_R_DC_1, 1, false, 0 }, - { "dc1-pll", IMX_SC_R_DC_1_PLL_0, 2, true, 0 }, - { "dc1-video", IMX_SC_R_DC_1_VIDEO0, 2, true, 0 }, - - /* CM40 SS */ - { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, - { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, - { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, - { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, - { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, - - /* CM41 SS */ - { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, - { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, - { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, - { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, - { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, - - /* CM41 SS */ - { "cm41_i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, - { "cm41_intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, - - /* DB SS */ - { "perf", IMX_SC_R_PERF, 1, false, 0}, - - /* IMAGE SS */ - { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 }, - { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, - { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, - { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, - - /* SECO SS */ - { "seco_mu", IMX_SC_R_SECO_MU_2, 3, true, 2}, - - /* V2X SS */ - { "v2x_mu", IMX_SC_R_V2X_MU_0, 2, true, 0}, - { "v2x_mu", IMX_SC_R_V2X_MU_2, 1, true, 2}, - { "v2x_mu", IMX_SC_R_V2X_MU_3, 2, true, 3}, - { "img-pdma", IMX_SC_R_ISI_CH0, 8, true, 0 }, - { "img-csi0", IMX_SC_R_CSI_0, 1, false, 0 }, - { "img-csi0-i2c0", IMX_SC_R_CSI_0_I2C_0, 1, false, 0 }, - { "img-csi0-pwm0", IMX_SC_R_CSI_0_PWM_0, 1, false, 0 }, - { "img-csi1", IMX_SC_R_CSI_1, 1, false, 0 }, - { "img-csi1-i2c0", IMX_SC_R_CSI_1_I2C_0, 1, false, 0 }, - { "img-csi1-pwm0", IMX_SC_R_CSI_1_PWM_0, 1, false, 0 }, - { "img-parallel", IMX_SC_R_PI_0, 1, false, 0 }, - { "img-parallel-i2c0", IMX_SC_R_PI_0_I2C_0, 1, false, 0 }, - { "img-parallel-pwm0", IMX_SC_R_PI_0_PWM_0, 2, true, 0 }, - { "img-parallel-pll", IMX_SC_R_PI_0_PLL, 1, false, 0 }, - - /* HDMI TX SS */ - { "hdmi-tx", IMX_SC_R_HDMI, 1, false, 0}, - { "hdmi-tx-i2s", IMX_SC_R_HDMI_I2S, 1, false, 0}, - { "hdmi-tx-i2c0", IMX_SC_R_HDMI_I2C_0, 1, false, 0}, - { "hdmi-tx-pll0", IMX_SC_R_HDMI_PLL_0, 1, false, 0}, - { "hdmi-tx-pll1", IMX_SC_R_HDMI_PLL_1, 1, false, 0}, - - /* HDMI RX SS */ - { "hdmi-rx", IMX_SC_R_HDMI_RX, 1, false, 0}, - { "hdmi-rx-pwm", IMX_SC_R_HDMI_RX_PWM_0, 1, false, 0}, - { "hdmi-rx-i2c0", IMX_SC_R_HDMI_RX_I2C_0, 1, false, 0}, - { "hdmi-rx-bypass", IMX_SC_R_HDMI_RX_BYPASS, 1, false, 0}, - - /* SECURITY SS */ - { "sec-jr", IMX_SC_R_CAAM_JR2, 2, true, 2}, - - /* BOARD SS */ - { "board", IMX_SC_R_BOARD_R0, 8, true, 0}, -}; - -static const struct imx_sc_pd_soc imx8qxp_scu_pd = { - .pd_ranges = imx8qxp_scu_pd_ranges, - .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), -}; - -static struct imx_sc_ipc *pm_ipc_handle; - -static inline struct imx_sc_pm_domain * -to_imx_sc_pd(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct imx_sc_pm_domain, pd); -} - -static void imx_sc_pd_get_console_rsrc(void) -{ - struct of_phandle_args specs; - int ret; - - if (!of_stdout) - return; - - ret = of_parse_phandle_with_args(of_stdout, "power-domains", - "#power-domain-cells", - 0, &specs); - if (ret) - return; - - imx_con_rsrc = specs.args[0]; -} - -static int imx_sc_get_pd_power(struct device *dev, u32 rsrc) -{ - struct imx_sc_msg_req_get_resource_power_mode msg; - struct imx_sc_rpc_msg *hdr = &msg.hdr; - int ret; - - hdr->ver = IMX_SC_RPC_VERSION; - hdr->svc = IMX_SC_RPC_SVC_PM; - hdr->func = IMX_SC_PM_FUNC_GET_RESOURCE_POWER_MODE; - hdr->size = 2; - - msg.data.req.resource = rsrc; - - ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); - if (ret) - dev_err(dev, "failed to get power resource %d mode, ret %d\n", - rsrc, ret); - - return msg.data.resp.mode; -} - -static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) -{ - struct imx_sc_msg_req_set_resource_power_mode msg; - struct imx_sc_rpc_msg *hdr = &msg.hdr; - struct imx_sc_pm_domain *pd; - int ret; - - pd = to_imx_sc_pd(domain); - - hdr->ver = IMX_SC_RPC_VERSION; - hdr->svc = IMX_SC_RPC_SVC_PM; - hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; - hdr->size = 2; - - msg.resource = pd->rsrc; - msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; - - /* keep uart console power on for no_console_suspend */ - if (imx_con_rsrc == pd->rsrc && !console_suspend_enabled && !power_on) - return -EBUSY; - - ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); - if (ret) - dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", - power_on ? "up" : "off", pd->rsrc, ret); - - return ret; -} - -static int imx_sc_pd_power_on(struct generic_pm_domain *domain) -{ - return imx_sc_pd_power(domain, true); -} - -static int imx_sc_pd_power_off(struct generic_pm_domain *domain) -{ - return imx_sc_pd_power(domain, false); -} - -static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, - void *data) -{ - struct generic_pm_domain *domain = ERR_PTR(-ENOENT); - struct genpd_onecell_data *pd_data = data; - unsigned int i; - - for (i = 0; i < pd_data->num_domains; i++) { - struct imx_sc_pm_domain *sc_pd; - - sc_pd = to_imx_sc_pd(pd_data->domains[i]); - if (sc_pd->rsrc == spec->args[0]) { - domain = &sc_pd->pd; - break; - } - } - - return domain; -} - -static struct imx_sc_pm_domain * -imx_scu_add_pm_domain(struct device *dev, int idx, - const struct imx_sc_pd_range *pd_ranges) -{ - struct imx_sc_pm_domain *sc_pd; - bool is_off; - int mode, ret; - - if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) - return NULL; - - sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); - if (!sc_pd) - return ERR_PTR(-ENOMEM); - - sc_pd->rsrc = pd_ranges->rsrc + idx; - sc_pd->pd.power_off = imx_sc_pd_power_off; - sc_pd->pd.power_on = imx_sc_pd_power_on; - - if (pd_ranges->postfix) - snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, pd_ranges->start_from + idx); - else - snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s", pd_ranges->name); - - sc_pd->pd.name = sc_pd->name; - if (imx_con_rsrc == sc_pd->rsrc) - sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; - - mode = imx_sc_get_pd_power(dev, pd_ranges->rsrc + idx); - if (mode == IMX_SC_PM_PW_MODE_ON) - is_off = false; - else - is_off = true; - - dev_dbg(dev, "%s : %s\n", sc_pd->name, imx_sc_pm_mode[mode]); - - if (sc_pd->rsrc >= IMX_SC_R_LAST) { - dev_warn(dev, "invalid pd %s rsrc id %d found", - sc_pd->name, sc_pd->rsrc); - - devm_kfree(dev, sc_pd); - return NULL; - } - - ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); - if (ret) { - dev_warn(dev, "failed to init pd %s rsrc id %d", - sc_pd->name, sc_pd->rsrc); - devm_kfree(dev, sc_pd); - return NULL; - } - - return sc_pd; -} - -static int imx_scu_init_pm_domains(struct device *dev, - const struct imx_sc_pd_soc *pd_soc) -{ - const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; - struct generic_pm_domain **domains; - struct genpd_onecell_data *pd_data; - struct imx_sc_pm_domain *sc_pd; - u32 count = 0; - int i, j; - - for (i = 0; i < pd_soc->num_ranges; i++) - count += pd_ranges[i].num; - - domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); - if (!domains) - return -ENOMEM; - - pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); - if (!pd_data) - return -ENOMEM; - - count = 0; - for (i = 0; i < pd_soc->num_ranges; i++) { - for (j = 0; j < pd_ranges[i].num; j++) { - sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); - if (IS_ERR_OR_NULL(sc_pd)) - continue; - - domains[count++] = &sc_pd->pd; - dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); - } - } - - pd_data->domains = domains; - pd_data->num_domains = count; - pd_data->xlate = imx_scu_pd_xlate; - - of_genpd_add_provider_onecell(dev->of_node, pd_data); - - return 0; -} - -static int imx_sc_pd_probe(struct platform_device *pdev) -{ - const struct imx_sc_pd_soc *pd_soc; - int ret; - - ret = imx_scu_get_handle(&pm_ipc_handle); - if (ret) - return ret; - - pd_soc = of_device_get_match_data(&pdev->dev); - if (!pd_soc) - return -ENODEV; - - imx_sc_pd_get_console_rsrc(); - - return imx_scu_init_pm_domains(&pdev->dev, pd_soc); -} - -static const struct of_device_id imx_sc_pd_match[] = { - { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, - { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, - { /* sentinel */ } -}; - -static struct platform_driver imx_sc_pd_driver = { - .driver = { - .name = "imx-scu-pd", - .of_match_table = imx_sc_pd_match, - .suppress_bind_attrs = true, - }, - .probe = imx_sc_pd_probe, -}; -builtin_platform_driver(imx_sc_pd_driver); - -MODULE_AUTHOR("Dong Aisheng "); -MODULE_DESCRIPTION("IMX SCU Power Domain driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/genpd/mediatek/Makefile b/drivers/genpd/mediatek/Makefile deleted file mode 100644 index 8cde09e654b3..000000000000 --- a/drivers/genpd/mediatek/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o -obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o diff --git a/drivers/genpd/mediatek/mt6795-pm-domains.h b/drivers/genpd/mediatek/mt6795-pm-domains.h deleted file mode 100644 index ef07c9dfdd9b..000000000000 --- a/drivers/genpd/mediatek/mt6795-pm-domains.h +++ /dev/null @@ -1,112 +0,0 @@ -/* 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 - -/* - * 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 */ diff --git a/drivers/genpd/mediatek/mt8167-pm-domains.h b/drivers/genpd/mediatek/mt8167-pm-domains.h deleted file mode 100644 index 4d6c32759606..000000000000 --- a/drivers/genpd/mediatek/mt8167-pm-domains.h +++ /dev/null @@ -1,105 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __SOC_MEDIATEK_MT8167_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8167_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -#define MT8167_PWR_STATUS_MFG_2D BIT(24) -#define MT8167_PWR_STATUS_MFG_ASYNC BIT(25) - -/* - * MT8167 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8167[] = { - [MT8167_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(MT8167_TOP_AXI_PROT_EN_MM_EMI | - MT8167_TOP_AXI_PROT_EN_MCU_MM), - }, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8167_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(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8167_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), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8167_POWER_DOMAIN_MFG_ASYNC] = { - .name = "mfg_async", - .sta_mask = MT8167_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 = 0, - .sram_pdn_ack_bits = 0, - .bp_infracfg = { - BUS_PROT_UPDATE_TOPAXI(MT8167_TOP_AXI_PROT_EN_MCU_MFG | - MT8167_TOP_AXI_PROT_EN_MFG_EMI), - }, - }, - [MT8167_POWER_DOMAIN_MFG_2D] = { - .name = "mfg_2d", - .sta_mask = MT8167_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(15, 12), - }, - [MT8167_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(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - }, - [MT8167_POWER_DOMAIN_CONN] = { - .name = "conn", - .sta_mask = PWR_STATUS_CONN, - .ctl_offs = SPM_CONN_PWR_CON, - .pwr_sta_offs = SPM_PWR_STATUS, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = 0, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - .bp_infracfg = { - BUS_PROT_UPDATE_TOPAXI(MT8167_TOP_AXI_PROT_EN_CONN_EMI | - MT8167_TOP_AXI_PROT_EN_CONN_MCU | - MT8167_TOP_AXI_PROT_EN_MCU_CONN), - }, - }, -}; - -static const struct scpsys_soc_data mt8167_scpsys_data = { - .domains_data = scpsys_domain_data_mt8167, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8167), -}; - -#endif /* __SOC_MEDIATEK_MT8167_PM_DOMAINS_H */ - diff --git a/drivers/genpd/mediatek/mt8173-pm-domains.h b/drivers/genpd/mediatek/mt8173-pm-domains.h deleted file mode 100644 index 1a5dc63b7357..000000000000 --- a/drivers/genpd/mediatek/mt8173-pm-domains.h +++ /dev/null @@ -1,123 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __SOC_MEDIATEK_MT8173_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8173_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -/* - * MT8173 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8173[] = { - [MT8173_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), - }, - [MT8173_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), - }, - [MT8173_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), - }, - [MT8173_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), - }, - }, - [MT8173_POWER_DOMAIN_VENC_LT] = { - .name = "venc_lt", - .sta_mask = PWR_STATUS_VENC_LT, - .ctl_offs = SPM_VEN2_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), - }, - [MT8173_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), - }, - [MT8173_POWER_DOMAIN_USB] = { - .name = "usb", - .sta_mask = PWR_STATUS_USB, - .ctl_offs = SPM_USB_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), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8173_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, - .caps = MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8173_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), - }, - [MT8173_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 mt8173_scpsys_data = { - .domains_data = scpsys_domain_data_mt8173, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8173), -}; - -#endif /* __SOC_MEDIATEK_MT8173_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mt8183-pm-domains.h b/drivers/genpd/mediatek/mt8183-pm-domains.h deleted file mode 100644 index 99de67fe5de8..000000000000 --- a/drivers/genpd/mediatek/mt8183-pm-domains.h +++ /dev/null @@ -1,266 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __SOC_MEDIATEK_MT8183_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8183_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -/* - * MT8183 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { - [MT8183_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = PWR_STATUS_AUDIO, - .ctl_offs = 0x0314, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - }, - [MT8183_POWER_DOMAIN_CONN] = { - .name = "conn", - .sta_mask = PWR_STATUS_CONN, - .ctl_offs = 0x032c, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = 0, - .sram_pdn_ack_bits = 0, - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_CONN, MT8183_TOP_AXI_PROT_EN_SET, - MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), - }, - }, - [MT8183_POWER_DOMAIN_MFG_ASYNC] = { - .name = "mfg_async", - .sta_mask = PWR_STATUS_MFG_ASYNC, - .ctl_offs = 0x0334, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = 0, - .sram_pdn_ack_bits = 0, - .caps = MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8183_POWER_DOMAIN_MFG] = { - .name = "mfg", - .sta_mask = PWR_STATUS_MFG, - .ctl_offs = 0x0338, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8183_POWER_DOMAIN_MFG_CORE0] = { - .name = "mfg_core0", - .sta_mask = BIT(7), - .ctl_offs = 0x034c, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8183_POWER_DOMAIN_MFG_CORE1] = { - .name = "mfg_core1", - .sta_mask = BIT(20), - .ctl_offs = 0x0310, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8183_POWER_DOMAIN_MFG_2D] = { - .name = "mfg_2d", - .sta_mask = PWR_STATUS_MFG_2D, - .ctl_offs = 0x0348, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_1_MFG, MT8183_TOP_AXI_PROT_EN_1_SET, - MT8183_TOP_AXI_PROT_EN_1_CLR, MT8183_TOP_AXI_PROT_EN_STA1_1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MFG, MT8183_TOP_AXI_PROT_EN_SET, - MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), - }, - }, - [MT8183_POWER_DOMAIN_DISP] = { - .name = "disp", - .sta_mask = PWR_STATUS_DISP, - .ctl_offs = 0x030c, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_1_DISP, MT8183_TOP_AXI_PROT_EN_1_SET, - MT8183_TOP_AXI_PROT_EN_1_CLR, MT8183_TOP_AXI_PROT_EN_STA1_1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_DISP, MT8183_TOP_AXI_PROT_EN_SET, - MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), - }, - .bp_smi = { - BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_DISP, - MT8183_SMI_COMMON_CLAMP_EN_SET, - MT8183_SMI_COMMON_CLAMP_EN_CLR, - MT8183_SMI_COMMON_CLAMP_EN), - }, - }, - [MT8183_POWER_DOMAIN_CAM] = { - .name = "cam", - .sta_mask = BIT(25), - .ctl_offs = 0x0344, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(9, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_CAM, MT8183_TOP_AXI_PROT_EN_MM_SET, - MT8183_TOP_AXI_PROT_EN_MM_CLR, MT8183_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_CAM, MT8183_TOP_AXI_PROT_EN_SET, - MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR_IGN(MT8183_TOP_AXI_PROT_EN_MM_CAM_2ND, - MT8183_TOP_AXI_PROT_EN_MM_SET, - MT8183_TOP_AXI_PROT_EN_MM_CLR, - MT8183_TOP_AXI_PROT_EN_MM_STA1), - }, - .bp_smi = { - BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_CAM, - MT8183_SMI_COMMON_CLAMP_EN_SET, - MT8183_SMI_COMMON_CLAMP_EN_CLR, - MT8183_SMI_COMMON_CLAMP_EN), - }, - }, - [MT8183_POWER_DOMAIN_ISP] = { - .name = "isp", - .sta_mask = PWR_STATUS_ISP, - .ctl_offs = 0x0308, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(9, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_ISP, - MT8183_TOP_AXI_PROT_EN_MM_SET, - MT8183_TOP_AXI_PROT_EN_MM_CLR, - MT8183_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR_IGN(MT8183_TOP_AXI_PROT_EN_MM_ISP_2ND, - MT8183_TOP_AXI_PROT_EN_MM_SET, - MT8183_TOP_AXI_PROT_EN_MM_CLR, - MT8183_TOP_AXI_PROT_EN_MM_STA1), - }, - .bp_smi = { - BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_ISP, - MT8183_SMI_COMMON_CLAMP_EN_SET, - MT8183_SMI_COMMON_CLAMP_EN_CLR, - MT8183_SMI_COMMON_CLAMP_EN), - }, - }, - [MT8183_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = BIT(31), - .ctl_offs = 0x0300, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_smi = { - BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_VDEC, - MT8183_SMI_COMMON_CLAMP_EN_SET, - MT8183_SMI_COMMON_CLAMP_EN_CLR, - MT8183_SMI_COMMON_CLAMP_EN), - }, - }, - [MT8183_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = PWR_STATUS_VENC, - .ctl_offs = 0x0304, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .bp_smi = { - BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_VENC, - MT8183_SMI_COMMON_CLAMP_EN_SET, - MT8183_SMI_COMMON_CLAMP_EN_CLR, - MT8183_SMI_COMMON_CLAMP_EN), - }, - }, - [MT8183_POWER_DOMAIN_VPU_TOP] = { - .name = "vpu_top", - .sta_mask = BIT(26), - .ctl_offs = 0x0324, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_VPU_TOP, - MT8183_TOP_AXI_PROT_EN_MM_SET, - MT8183_TOP_AXI_PROT_EN_MM_CLR, - MT8183_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_VPU_TOP, - MT8183_TOP_AXI_PROT_EN_SET, - MT8183_TOP_AXI_PROT_EN_CLR, - MT8183_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_VPU_TOP_2ND, - MT8183_TOP_AXI_PROT_EN_MM_SET, - MT8183_TOP_AXI_PROT_EN_MM_CLR, - MT8183_TOP_AXI_PROT_EN_MM_STA1), - }, - .bp_smi = { - BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_VPU_TOP, - MT8183_SMI_COMMON_CLAMP_EN_SET, - MT8183_SMI_COMMON_CLAMP_EN_CLR, - MT8183_SMI_COMMON_CLAMP_EN), - }, - }, - [MT8183_POWER_DOMAIN_VPU_CORE0] = { - .name = "vpu_core0", - .sta_mask = BIT(27), - .ctl_offs = 0x33c, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE0, - MT8183_TOP_AXI_PROT_EN_MCU_SET, - MT8183_TOP_AXI_PROT_EN_MCU_CLR, - MT8183_TOP_AXI_PROT_EN_MCU_STA1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE0_2ND, - MT8183_TOP_AXI_PROT_EN_MCU_SET, - MT8183_TOP_AXI_PROT_EN_MCU_CLR, - MT8183_TOP_AXI_PROT_EN_MCU_STA1), - }, - .caps = MTK_SCPD_SRAM_ISO, - }, - [MT8183_POWER_DOMAIN_VPU_CORE1] = { - .name = "vpu_core1", - .sta_mask = BIT(28), - .ctl_offs = 0x0340, - .pwr_sta_offs = 0x0180, - .pwr_sta2nd_offs = 0x0184, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE1, - MT8183_TOP_AXI_PROT_EN_MCU_SET, - MT8183_TOP_AXI_PROT_EN_MCU_CLR, - MT8183_TOP_AXI_PROT_EN_MCU_STA1), - BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE1_2ND, - MT8183_TOP_AXI_PROT_EN_MCU_SET, - MT8183_TOP_AXI_PROT_EN_MCU_CLR, - MT8183_TOP_AXI_PROT_EN_MCU_STA1), - }, - .caps = MTK_SCPD_SRAM_ISO, - }, -}; - -static const struct scpsys_soc_data mt8183_scpsys_data = { - .domains_data = scpsys_domain_data_mt8183, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8183), -}; - -#endif /* __SOC_MEDIATEK_MT8183_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mt8186-pm-domains.h b/drivers/genpd/mediatek/mt8186-pm-domains.h deleted file mode 100644 index fce86f79c505..000000000000 --- a/drivers/genpd/mediatek/mt8186-pm-domains.h +++ /dev/null @@ -1,342 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2022 MediaTek Inc. - * Author: Chun-Jie Chen - */ - -#ifndef __SOC_MEDIATEK_MT8186_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8186_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -/* - * MT8186 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8186[] = { - [MT8186_POWER_DOMAIN_MFG0] = { - .name = "mfg0", - .sta_mask = BIT(2), - .ctl_offs = 0x308, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8186_POWER_DOMAIN_MFG1] = { - .name = "mfg1", - .sta_mask = BIT(3), - .ctl_offs = 0x30c, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_MFG1_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_MFG1_STEP2, - MT8186_TOP_AXI_PROT_EN_SET, - MT8186_TOP_AXI_PROT_EN_CLR, - MT8186_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_MFG1_STEP3, - MT8186_TOP_AXI_PROT_EN_SET, - MT8186_TOP_AXI_PROT_EN_CLR, - MT8186_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_MFG1_STEP4, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8186_POWER_DOMAIN_MFG2] = { - .name = "mfg2", - .sta_mask = BIT(4), - .ctl_offs = 0x310, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_MFG3] = { - .name = "mfg3", - .sta_mask = BIT(5), - .ctl_offs = 0x314, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_SSUSB] = { - .name = "ssusb", - .sta_mask = BIT(20), - .ctl_offs = 0x9F0, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8186_POWER_DOMAIN_SSUSB_P1] = { - .name = "ssusb_p1", - .sta_mask = BIT(19), - .ctl_offs = 0x9F4, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8186_POWER_DOMAIN_DIS] = { - .name = "dis", - .sta_mask = BIT(21), - .ctl_offs = 0x354, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_DIS_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_DIS_STEP2, - MT8186_TOP_AXI_PROT_EN_SET, - MT8186_TOP_AXI_PROT_EN_CLR, - MT8186_TOP_AXI_PROT_EN_STA), - }, - }, - [MT8186_POWER_DOMAIN_IMG] = { - .name = "img", - .sta_mask = BIT(13), - .ctl_offs = 0x334, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IMG_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IMG_STEP2, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_IMG2] = { - .name = "img2", - .sta_mask = BIT(14), - .ctl_offs = 0x338, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_IPE] = { - .name = "ipe", - .sta_mask = BIT(15), - .ctl_offs = 0x33C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IPE_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IPE_STEP2, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_CAM] = { - .name = "cam", - .sta_mask = BIT(23), - .ctl_offs = 0x35C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_CAM_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_CAM_STEP2, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_CAM_RAWA] = { - .name = "cam_rawa", - .sta_mask = BIT(24), - .ctl_offs = 0x360, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_CAM_RAWB] = { - .name = "cam_rawb", - .sta_mask = BIT(25), - .ctl_offs = 0x364, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = BIT(18), - .ctl_offs = 0x348, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VENC_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VENC_STEP2, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = BIT(16), - .ctl_offs = 0x340, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VDEC_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VDEC_STEP2, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_WPE] = { - .name = "wpe", - .sta_mask = BIT(0), - .ctl_offs = 0x3F8, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_2_WPE_STEP1, - MT8186_TOP_AXI_PROT_EN_2_SET, - MT8186_TOP_AXI_PROT_EN_2_CLR, - MT8186_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_2_WPE_STEP2, - MT8186_TOP_AXI_PROT_EN_2_SET, - MT8186_TOP_AXI_PROT_EN_2_CLR, - MT8186_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_CONN_ON] = { - .name = "conn_on", - .sta_mask = BIT(1), - .ctl_offs = 0x304, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_CONN_ON_STEP1, - MT8186_TOP_AXI_PROT_EN_1_SET, - MT8186_TOP_AXI_PROT_EN_1_CLR, - MT8186_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_CONN_ON_STEP2, - MT8186_TOP_AXI_PROT_EN_SET, - MT8186_TOP_AXI_PROT_EN_CLR, - MT8186_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_CONN_ON_STEP3, - MT8186_TOP_AXI_PROT_EN_SET, - MT8186_TOP_AXI_PROT_EN_CLR, - MT8186_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_CONN_ON_STEP4, - MT8186_TOP_AXI_PROT_EN_SET, - MT8186_TOP_AXI_PROT_EN_CLR, - MT8186_TOP_AXI_PROT_EN_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8186_POWER_DOMAIN_CSIRX_TOP] = { - .name = "csirx_top", - .sta_mask = BIT(6), - .ctl_offs = 0x318, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8186_POWER_DOMAIN_ADSP_AO] = { - .name = "adsp_ao", - .sta_mask = BIT(17), - .ctl_offs = 0x9FC, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - }, - [MT8186_POWER_DOMAIN_ADSP_INFRA] = { - .name = "adsp_infra", - .sta_mask = BIT(10), - .ctl_offs = 0x9F8, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - }, - [MT8186_POWER_DOMAIN_ADSP_TOP] = { - .name = "adsp_top", - .sta_mask = BIT(31), - .ctl_offs = 0x3E4, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_3_ADSP_TOP_STEP1, - MT8186_TOP_AXI_PROT_EN_3_SET, - MT8186_TOP_AXI_PROT_EN_3_CLR, - MT8186_TOP_AXI_PROT_EN_3_STA), - BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_3_ADSP_TOP_STEP2, - MT8186_TOP_AXI_PROT_EN_3_SET, - MT8186_TOP_AXI_PROT_EN_3_CLR, - MT8186_TOP_AXI_PROT_EN_3_STA), - }, - .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_ACTIVE_WAKEUP, - }, -}; - -static const struct scpsys_soc_data mt8186_scpsys_data = { - .domains_data = scpsys_domain_data_mt8186, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8186), -}; - -#endif /* __SOC_MEDIATEK_MT8186_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mt8188-pm-domains.h b/drivers/genpd/mediatek/mt8188-pm-domains.h deleted file mode 100644 index 0692cb444ed0..000000000000 --- a/drivers/genpd/mediatek/mt8188-pm-domains.h +++ /dev/null @@ -1,623 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2022 MediaTek Inc. - * Author: Garmin Chang - */ - -#ifndef __SOC_MEDIATEK_MT8188_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8188_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -/* - * MT8188 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { - [MT8188_POWER_DOMAIN_MFG0] = { - .name = "mfg0", - .sta_mask = BIT(1), - .ctl_offs = 0x300, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8188_POWER_DOMAIN_MFG1] = { - .name = "mfg1", - .sta_mask = BIT(2), - .ctl_offs = 0x304, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MFG1_STEP1, - MT8188_TOP_AXI_PROT_EN_SET, - MT8188_TOP_AXI_PROT_EN_CLR, - MT8188_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_MFG1_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_1_MFG1_STEP3, - MT8188_TOP_AXI_PROT_EN_1_SET, - MT8188_TOP_AXI_PROT_EN_1_CLR, - MT8188_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_MFG1_STEP4, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MFG1_STEP5, - MT8188_TOP_AXI_PROT_EN_SET, - MT8188_TOP_AXI_PROT_EN_CLR, - MT8188_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MFG1_STEP6, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8188_POWER_DOMAIN_MFG2] = { - .name = "mfg2", - .sta_mask = BIT(3), - .ctl_offs = 0x308, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_MFG3] = { - .name = "mfg3", - .sta_mask = BIT(4), - .ctl_offs = 0x30C, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_MFG4] = { - .name = "mfg4", - .sta_mask = BIT(5), - .ctl_offs = 0x310, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_PEXTP_MAC_P0] = { - .name = "pextp_mac_p0", - .sta_mask = BIT(10), - .ctl_offs = 0x324, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_PEXTP_MAC_P0_STEP1, - MT8188_TOP_AXI_PROT_EN_SET, - MT8188_TOP_AXI_PROT_EN_CLR, - MT8188_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_PEXTP_MAC_P0_STEP2, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_PEXTP_PHY_TOP] = { - .name = "pextp_phy_top", - .sta_mask = BIT(12), - .ctl_offs = 0x328, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_CSIRX_TOP] = { - .name = "pextp_csirx_top", - .sta_mask = BIT(17), - .ctl_offs = 0x3C4, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_ETHER] = { - .name = "ether", - .sta_mask = BIT(1), - .ctl_offs = 0x338, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_ETHER_STEP1, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8188_POWER_DOMAIN_HDMI_TX] = { - .name = "hdmi_tx", - .sta_mask = BIT(18), - .ctl_offs = 0x37C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_HDMI_TX_STEP1, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8188_POWER_DOMAIN_ADSP_AO] = { - .name = "adsp_ao", - .sta_mask = BIT(10), - .ctl_offs = 0x35C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_AO_STEP1, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_AO_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_ALWAYS_ON, - }, - [MT8188_POWER_DOMAIN_ADSP_INFRA] = { - .name = "adsp_infra", - .sta_mask = BIT(9), - .ctl_offs = 0x358, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_INFRA_STEP1, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_INFRA_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_ALWAYS_ON, - }, - [MT8188_POWER_DOMAIN_ADSP] = { - .name = "adsp", - .sta_mask = BIT(8), - .ctl_offs = 0x354, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_STEP1, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_SRAM_ISO | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8188_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = BIT(6), - .ctl_offs = 0x34C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8188_POWER_DOMAIN_AUDIO_ASRC] = { - .name = "audio_asrc", - .sta_mask = BIT(7), - .ctl_offs = 0x350, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_ASRC_STEP1, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_ASRC_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_VPPSYS0] = { - .name = "vppsys0", - .sta_mask = BIT(11), - .ctl_offs = 0x360, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_VPPSYS0_STEP1, - MT8188_TOP_AXI_PROT_EN_SET, - MT8188_TOP_AXI_PROT_EN_CLR, - MT8188_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VPPSYS0_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_VPPSYS0_STEP3, - MT8188_TOP_AXI_PROT_EN_SET, - MT8188_TOP_AXI_PROT_EN_CLR, - MT8188_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VPPSYS0_STEP4, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VPPSYS0_STEP5, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA), - }, - }, - [MT8188_POWER_DOMAIN_VDOSYS0] = { - .name = "vdosys0", - .sta_mask = BIT(13), - .ctl_offs = 0x368, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDOSYS0_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_VDOSYS0_STEP2, - MT8188_TOP_AXI_PROT_EN_SET, - MT8188_TOP_AXI_PROT_EN_CLR, - MT8188_TOP_AXI_PROT_EN_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VDOSYS0_STEP3, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA), - }, - }, - [MT8188_POWER_DOMAIN_VDOSYS1] = { - .name = "vdosys1", - .sta_mask = BIT(14), - .ctl_offs = 0x36C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDOSYS1_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDOSYS1_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VDOSYS1_STEP3, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - }, - [MT8188_POWER_DOMAIN_DP_TX] = { - .name = "dp_tx", - .sta_mask = BIT(16), - .ctl_offs = 0x374, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_DP_TX_STEP1, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_EDP_TX] = { - .name = "edp_tx", - .sta_mask = BIT(17), - .ctl_offs = 0x378, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_EDP_TX_STEP1, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, - MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_VPPSYS1] = { - .name = "vppsys1", - .sta_mask = BIT(12), - .ctl_offs = 0x364, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VPPSYS1_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VPPSYS1_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VPPSYS1_STEP3, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - }, - [MT8188_POWER_DOMAIN_WPE] = { - .name = "wpe", - .sta_mask = BIT(15), - .ctl_offs = 0x370, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_WPE_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_WPE_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_VDEC0] = { - .name = "vdec0", - .sta_mask = BIT(19), - .ctl_offs = 0x380, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDEC0_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VDEC0_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_VDEC1] = { - .name = "vdec1", - .sta_mask = BIT(20), - .ctl_offs = 0x384, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDEC1_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDEC1_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = BIT(22), - .ctl_offs = 0x38C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VENC_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VENC_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VENC_STEP3, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_IMG_VCORE] = { - .name = "vcore", - .sta_mask = BIT(28), - .ctl_offs = 0x3A4, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_IMG_VCORE_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_IMG_VCORE_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_IMG_VCORE_STEP3, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8188_POWER_DOMAIN_IMG_MAIN] = { - .name = "img_main", - .sta_mask = BIT(29), - .ctl_offs = 0x3A8, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_IMG_MAIN_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_IMG_MAIN_STEP2, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_DIP] = { - .name = "dip", - .sta_mask = BIT(30), - .ctl_offs = 0x3AC, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_IPE] = { - .name = "ipe", - .sta_mask = BIT(31), - .ctl_offs = 0x3B0, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_CAM_VCORE] = { - .name = "cam_vcore", - .sta_mask = BIT(27), - .ctl_offs = 0x3A0, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_CAM_VCORE_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_CAM_VCORE_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_1_CAM_VCORE_STEP3, - MT8188_TOP_AXI_PROT_EN_1_SET, - MT8188_TOP_AXI_PROT_EN_1_CLR, - MT8188_TOP_AXI_PROT_EN_1_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_CAM_VCORE_STEP4, - MT8188_TOP_AXI_PROT_EN_MM_SET, - MT8188_TOP_AXI_PROT_EN_MM_CLR, - MT8188_TOP_AXI_PROT_EN_MM_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_CAM_VCORE_STEP5, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8188_POWER_DOMAIN_CAM_MAIN] = { - .name = "cam_main", - .sta_mask = BIT(24), - .ctl_offs = 0x394, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .bp_infracfg = { - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_CAM_MAIN_STEP1, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_CAM_MAIN_STEP2, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_CAM_MAIN_STEP3, - MT8188_TOP_AXI_PROT_EN_MM_2_SET, - MT8188_TOP_AXI_PROT_EN_MM_2_CLR, - MT8188_TOP_AXI_PROT_EN_MM_2_STA), - BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_CAM_MAIN_STEP4, - MT8188_TOP_AXI_PROT_EN_2_SET, - MT8188_TOP_AXI_PROT_EN_2_CLR, - MT8188_TOP_AXI_PROT_EN_2_STA), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_CAM_SUBA] = { - .name = "cam_suba", - .sta_mask = BIT(25), - .ctl_offs = 0x398, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8188_POWER_DOMAIN_CAM_SUBB] = { - .name = "cam_subb", - .sta_mask = BIT(26), - .ctl_offs = 0x39C, - .pwr_sta_offs = 0x16C, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = BIT(8), - .sram_pdn_ack_bits = BIT(12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, -}; - -static const struct scpsys_soc_data mt8188_scpsys_data = { - .domains_data = scpsys_domain_data_mt8188, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8188), -}; - -#endif /* __SOC_MEDIATEK_MT8188_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mt8192-pm-domains.h b/drivers/genpd/mediatek/mt8192-pm-domains.h deleted file mode 100644 index b97b2051920f..000000000000 --- a/drivers/genpd/mediatek/mt8192-pm-domains.h +++ /dev/null @@ -1,355 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __SOC_MEDIATEK_MT8192_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8192_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -/* - * MT8192 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = { - [MT8192_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = BIT(21), - .ctl_offs = 0x0354, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_AUDIO, - MT8192_TOP_AXI_PROT_EN_2_SET, - MT8192_TOP_AXI_PROT_EN_2_CLR, - MT8192_TOP_AXI_PROT_EN_2_STA1), - }, - }, - [MT8192_POWER_DOMAIN_CONN] = { - .name = "conn", - .sta_mask = PWR_STATUS_CONN, - .ctl_offs = 0x0304, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = 0, - .sram_pdn_ack_bits = 0, - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_CONN, - MT8192_TOP_AXI_PROT_EN_SET, - MT8192_TOP_AXI_PROT_EN_CLR, - MT8192_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_CONN_2ND, - MT8192_TOP_AXI_PROT_EN_SET, - MT8192_TOP_AXI_PROT_EN_CLR, - MT8192_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_1_CONN, - MT8192_TOP_AXI_PROT_EN_1_SET, - MT8192_TOP_AXI_PROT_EN_1_CLR, - MT8192_TOP_AXI_PROT_EN_1_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8192_POWER_DOMAIN_MFG0] = { - .name = "mfg0", - .sta_mask = BIT(2), - .ctl_offs = 0x0308, - .pwr_sta_offs = 0x016c, - .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", - .sta_mask = BIT(3), - .ctl_offs = 0x030c, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_1_MFG1, - MT8192_TOP_AXI_PROT_EN_1_SET, - MT8192_TOP_AXI_PROT_EN_1_CLR, - MT8192_TOP_AXI_PROT_EN_1_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_MFG1, - MT8192_TOP_AXI_PROT_EN_2_SET, - MT8192_TOP_AXI_PROT_EN_2_CLR, - MT8192_TOP_AXI_PROT_EN_2_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MFG1, - MT8192_TOP_AXI_PROT_EN_SET, - MT8192_TOP_AXI_PROT_EN_CLR, - MT8192_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_MFG1_2ND, - MT8192_TOP_AXI_PROT_EN_2_SET, - 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", - .sta_mask = BIT(4), - .ctl_offs = 0x0310, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_MFG3] = { - .name = "mfg3", - .sta_mask = BIT(5), - .ctl_offs = 0x0314, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_MFG4] = { - .name = "mfg4", - .sta_mask = BIT(6), - .ctl_offs = 0x0318, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_MFG5] = { - .name = "mfg5", - .sta_mask = BIT(7), - .ctl_offs = 0x031c, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_MFG6] = { - .name = "mfg6", - .sta_mask = BIT(8), - .ctl_offs = 0x0320, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_DISP] = { - .name = "disp", - .sta_mask = BIT(20), - .ctl_offs = 0x0350, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR_IGN(MT8192_TOP_AXI_PROT_EN_MM_DISP, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR_IGN(MT8192_TOP_AXI_PROT_EN_MM_2_DISP, - MT8192_TOP_AXI_PROT_EN_MM_2_SET, - MT8192_TOP_AXI_PROT_EN_MM_2_CLR, - MT8192_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_DISP, - MT8192_TOP_AXI_PROT_EN_SET, - MT8192_TOP_AXI_PROT_EN_CLR, - MT8192_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_DISP_2ND, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_DISP_2ND, - MT8192_TOP_AXI_PROT_EN_MM_2_SET, - MT8192_TOP_AXI_PROT_EN_MM_2_CLR, - MT8192_TOP_AXI_PROT_EN_MM_2_STA1), - }, - }, - [MT8192_POWER_DOMAIN_IPE] = { - .name = "ipe", - .sta_mask = BIT(14), - .ctl_offs = 0x0338, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_IPE, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_IPE_2ND, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - }, - }, - [MT8192_POWER_DOMAIN_ISP] = { - .name = "isp", - .sta_mask = BIT(12), - .ctl_offs = 0x0330, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_ISP, - MT8192_TOP_AXI_PROT_EN_MM_2_SET, - MT8192_TOP_AXI_PROT_EN_MM_2_CLR, - MT8192_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_ISP_2ND, - MT8192_TOP_AXI_PROT_EN_MM_2_SET, - MT8192_TOP_AXI_PROT_EN_MM_2_CLR, - MT8192_TOP_AXI_PROT_EN_MM_2_STA1), - }, - }, - [MT8192_POWER_DOMAIN_ISP2] = { - .name = "isp2", - .sta_mask = BIT(13), - .ctl_offs = 0x0334, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_ISP2, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_ISP2_2ND, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - }, - }, - [MT8192_POWER_DOMAIN_MDP] = { - .name = "mdp", - .sta_mask = BIT(19), - .ctl_offs = 0x034c, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_MDP, - MT8192_TOP_AXI_PROT_EN_MM_2_SET, - MT8192_TOP_AXI_PROT_EN_MM_2_CLR, - MT8192_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_MDP_2ND, - MT8192_TOP_AXI_PROT_EN_MM_2_SET, - MT8192_TOP_AXI_PROT_EN_MM_2_CLR, - MT8192_TOP_AXI_PROT_EN_MM_2_STA1), - }, - }, - [MT8192_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = BIT(17), - .ctl_offs = 0x0344, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VENC, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VENC_2ND, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - }, - }, - [MT8192_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = BIT(15), - .ctl_offs = 0x033c, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VDEC, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VDEC_2ND, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - }, - }, - [MT8192_POWER_DOMAIN_VDEC2] = { - .name = "vdec2", - .sta_mask = BIT(16), - .ctl_offs = 0x0340, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_CAM] = { - .name = "cam", - .sta_mask = BIT(23), - .ctl_offs = 0x035c, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_CAM, - MT8192_TOP_AXI_PROT_EN_2_SET, - MT8192_TOP_AXI_PROT_EN_2_CLR, - MT8192_TOP_AXI_PROT_EN_2_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_CAM, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_1_CAM, - MT8192_TOP_AXI_PROT_EN_1_SET, - MT8192_TOP_AXI_PROT_EN_1_CLR, - MT8192_TOP_AXI_PROT_EN_1_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_CAM_2ND, - MT8192_TOP_AXI_PROT_EN_MM_SET, - MT8192_TOP_AXI_PROT_EN_MM_CLR, - MT8192_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_VDNR_CAM, - MT8192_TOP_AXI_PROT_EN_VDNR_SET, - MT8192_TOP_AXI_PROT_EN_VDNR_CLR, - MT8192_TOP_AXI_PROT_EN_VDNR_STA1), - }, - }, - [MT8192_POWER_DOMAIN_CAM_RAWA] = { - .name = "cam_rawa", - .sta_mask = BIT(24), - .ctl_offs = 0x0360, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_CAM_RAWB] = { - .name = "cam_rawb", - .sta_mask = BIT(25), - .ctl_offs = 0x0364, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, - [MT8192_POWER_DOMAIN_CAM_RAWC] = { - .name = "cam_rawc", - .sta_mask = BIT(26), - .ctl_offs = 0x0368, - .pwr_sta_offs = 0x016c, - .pwr_sta2nd_offs = 0x0170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - }, -}; - -static const struct scpsys_soc_data mt8192_scpsys_data = { - .domains_data = scpsys_domain_data_mt8192, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8192), -}; - -#endif /* __SOC_MEDIATEK_MT8192_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mt8195-pm-domains.h b/drivers/genpd/mediatek/mt8195-pm-domains.h deleted file mode 100644 index d7387ea1b9c9..000000000000 --- a/drivers/genpd/mediatek/mt8195-pm-domains.h +++ /dev/null @@ -1,613 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright (c) 2021 MediaTek Inc. - * Author: Chun-Jie Chen - */ - -#ifndef __SOC_MEDIATEK_MT8195_PM_DOMAINS_H -#define __SOC_MEDIATEK_MT8195_PM_DOMAINS_H - -#include "mtk-pm-domains.h" -#include - -/* - * MT8195 power domain support - */ - -static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = { - [MT8195_POWER_DOMAIN_PCIE_MAC_P0] = { - .name = "pcie_mac_p0", - .sta_mask = BIT(11), - .ctl_offs = 0x328, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_PCIE_MAC_P0, - MT8195_TOP_AXI_PROT_EN_VDNR_SET, - MT8195_TOP_AXI_PROT_EN_VDNR_CLR, - MT8195_TOP_AXI_PROT_EN_VDNR_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_PCIE_MAC_P0, - MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, - MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, - MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), - }, - }, - [MT8195_POWER_DOMAIN_PCIE_MAC_P1] = { - .name = "pcie_mac_p1", - .sta_mask = BIT(12), - .ctl_offs = 0x32C, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_PCIE_MAC_P1, - MT8195_TOP_AXI_PROT_EN_VDNR_SET, - MT8195_TOP_AXI_PROT_EN_VDNR_CLR, - MT8195_TOP_AXI_PROT_EN_VDNR_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_PCIE_MAC_P1, - MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, - MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, - MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), - }, - }, - [MT8195_POWER_DOMAIN_PCIE_PHY] = { - .name = "pcie_phy", - .sta_mask = BIT(13), - .ctl_offs = 0x330, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8195_POWER_DOMAIN_SSUSB_PCIE_PHY] = { - .name = "ssusb_pcie_phy", - .sta_mask = BIT(14), - .ctl_offs = 0x334, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_ALWAYS_ON, - }, - [MT8195_POWER_DOMAIN_CSI_RX_TOP] = { - .name = "csi_rx_top", - .sta_mask = BIT(18), - .ctl_offs = 0x3C4, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_ETHER] = { - .name = "ether", - .sta_mask = BIT(3), - .ctl_offs = 0x344, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8195_POWER_DOMAIN_ADSP] = { - .name = "adsp", - .sta_mask = BIT(10), - .ctl_offs = 0x360, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_ADSP, - MT8195_TOP_AXI_PROT_EN_2_SET, - MT8195_TOP_AXI_PROT_EN_2_CLR, - MT8195_TOP_AXI_PROT_EN_2_STA1), - }, - .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8195_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = BIT(8), - .ctl_offs = 0x358, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_AUDIO, - MT8195_TOP_AXI_PROT_EN_2_SET, - MT8195_TOP_AXI_PROT_EN_2_CLR, - MT8195_TOP_AXI_PROT_EN_2_STA1), - }, - }, - [MT8195_POWER_DOMAIN_MFG0] = { - .name = "mfg0", - .sta_mask = BIT(1), - .ctl_offs = 0x300, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8195_POWER_DOMAIN_MFG1] = { - .name = "mfg1", - .sta_mask = BIT(2), - .ctl_offs = 0x304, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MFG1, - MT8195_TOP_AXI_PROT_EN_SET, - MT8195_TOP_AXI_PROT_EN_CLR, - MT8195_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_MFG1, - MT8195_TOP_AXI_PROT_EN_2_SET, - MT8195_TOP_AXI_PROT_EN_2_CLR, - MT8195_TOP_AXI_PROT_EN_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_1_MFG1, - MT8195_TOP_AXI_PROT_EN_1_SET, - MT8195_TOP_AXI_PROT_EN_1_CLR, - MT8195_TOP_AXI_PROT_EN_1_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_MFG1_2ND, - MT8195_TOP_AXI_PROT_EN_2_SET, - MT8195_TOP_AXI_PROT_EN_2_CLR, - MT8195_TOP_AXI_PROT_EN_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MFG1_2ND, - MT8195_TOP_AXI_PROT_EN_SET, - MT8195_TOP_AXI_PROT_EN_CLR, - MT8195_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MFG1, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, - }, - [MT8195_POWER_DOMAIN_MFG2] = { - .name = "mfg2", - .sta_mask = BIT(3), - .ctl_offs = 0x308, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_MFG3] = { - .name = "mfg3", - .sta_mask = BIT(4), - .ctl_offs = 0x30C, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_MFG4] = { - .name = "mfg4", - .sta_mask = BIT(5), - .ctl_offs = 0x310, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_MFG5] = { - .name = "mfg5", - .sta_mask = BIT(6), - .ctl_offs = 0x314, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_MFG6] = { - .name = "mfg6", - .sta_mask = BIT(7), - .ctl_offs = 0x318, - .pwr_sta_offs = 0x174, - .pwr_sta2nd_offs = 0x178, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_VPPSYS0] = { - .name = "vppsys0", - .sta_mask = BIT(11), - .ctl_offs = 0x364, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VPPSYS0, - MT8195_TOP_AXI_PROT_EN_SET, - MT8195_TOP_AXI_PROT_EN_CLR, - MT8195_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VPPSYS0, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VPPSYS0_2ND, - MT8195_TOP_AXI_PROT_EN_SET, - MT8195_TOP_AXI_PROT_EN_CLR, - MT8195_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VPPSYS0_2ND, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VPPSYS0, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), - }, - }, - [MT8195_POWER_DOMAIN_VDOSYS0] = { - .name = "vdosys0", - .sta_mask = BIT(13), - .ctl_offs = 0x36C, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDOSYS0, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDOSYS0, - MT8195_TOP_AXI_PROT_EN_SET, - MT8195_TOP_AXI_PROT_EN_CLR, - MT8195_TOP_AXI_PROT_EN_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VDOSYS0, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, - MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), - }, - }, - [MT8195_POWER_DOMAIN_VPPSYS1] = { - .name = "vppsys1", - .sta_mask = BIT(12), - .ctl_offs = 0x368, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VPPSYS1, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VPPSYS1_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VPPSYS1, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - }, - [MT8195_POWER_DOMAIN_VDOSYS1] = { - .name = "vdosys1", - .sta_mask = BIT(14), - .ctl_offs = 0x370, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDOSYS1, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDOSYS1_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDOSYS1, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - }, - [MT8195_POWER_DOMAIN_DP_TX] = { - .name = "dp_tx", - .sta_mask = BIT(16), - .ctl_offs = 0x378, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_DP_TX, - MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, - MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, - MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_EPD_TX] = { - .name = "epd_tx", - .sta_mask = BIT(17), - .ctl_offs = 0x37C, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_EPD_TX, - MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, - MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, - MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_HDMI_TX] = { - .name = "hdmi_tx", - .sta_mask = BIT(18), - .ctl_offs = 0x380, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8195_POWER_DOMAIN_WPESYS] = { - .name = "wpesys", - .sta_mask = BIT(15), - .ctl_offs = 0x374, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_WPESYS, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_WPESYS, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_WPESYS_2ND, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - }, - [MT8195_POWER_DOMAIN_VDEC0] = { - .name = "vdec0", - .sta_mask = BIT(20), - .ctl_offs = 0x388, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC0, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC0, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC0_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC0_2ND, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_VDEC1] = { - .name = "vdec1", - .sta_mask = BIT(21), - .ctl_offs = 0x38C, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC1, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC1_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_VDEC2] = { - .name = "vdec2", - .sta_mask = BIT(22), - .ctl_offs = 0x390, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC2, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC2_2ND, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = BIT(23), - .ctl_offs = 0x394, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VENC, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VENC_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VENC, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_VENC_CORE1] = { - .name = "venc_core1", - .sta_mask = BIT(24), - .ctl_offs = 0x398, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VENC_CORE1, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VENC_CORE1, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_IMG] = { - .name = "img", - .sta_mask = BIT(29), - .ctl_offs = 0x3AC, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_IMG, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_IMG_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_DIP] = { - .name = "dip", - .sta_mask = BIT(30), - .ctl_offs = 0x3B0, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_IPE] = { - .name = "ipe", - .sta_mask = BIT(31), - .ctl_offs = 0x3B4, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_IPE, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_IPE, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_CAM] = { - .name = "cam", - .sta_mask = BIT(25), - .ctl_offs = 0x39C, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .bp_infracfg = { - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_CAM, - MT8195_TOP_AXI_PROT_EN_2_SET, - MT8195_TOP_AXI_PROT_EN_2_CLR, - MT8195_TOP_AXI_PROT_EN_2_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_CAM, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_1_CAM, - MT8195_TOP_AXI_PROT_EN_1_SET, - MT8195_TOP_AXI_PROT_EN_1_CLR, - MT8195_TOP_AXI_PROT_EN_1_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_CAM_2ND, - MT8195_TOP_AXI_PROT_EN_MM_SET, - MT8195_TOP_AXI_PROT_EN_MM_CLR, - MT8195_TOP_AXI_PROT_EN_MM_STA1), - BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_CAM, - MT8195_TOP_AXI_PROT_EN_MM_2_SET, - MT8195_TOP_AXI_PROT_EN_MM_2_CLR, - MT8195_TOP_AXI_PROT_EN_MM_2_STA1), - }, - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_CAM_RAWA] = { - .name = "cam_rawa", - .sta_mask = BIT(26), - .ctl_offs = 0x3A0, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_CAM_RAWB] = { - .name = "cam_rawb", - .sta_mask = BIT(27), - .ctl_offs = 0x3A4, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, - [MT8195_POWER_DOMAIN_CAM_MRAW] = { - .name = "cam_mraw", - .sta_mask = BIT(28), - .ctl_offs = 0x3A8, - .pwr_sta_offs = 0x16c, - .pwr_sta2nd_offs = 0x170, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .caps = MTK_SCPD_KEEP_DEFAULT_OFF, - }, -}; - -static const struct scpsys_soc_data mt8195_scpsys_data = { - .domains_data = scpsys_domain_data_mt8195, - .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8195), -}; - -#endif /* __SOC_MEDIATEK_MT8195_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mtk-pm-domains.c b/drivers/genpd/mediatek/mtk-pm-domains.c deleted file mode 100644 index ee962804b830..000000000000 --- a/drivers/genpd/mediatek/mtk-pm-domains.c +++ /dev/null @@ -1,688 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2020 Collabora Ltd. - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "mt6795-pm-domains.h" -#include "mt8167-pm-domains.h" -#include "mt8173-pm-domains.h" -#include "mt8183-pm-domains.h" -#include "mt8186-pm-domains.h" -#include "mt8188-pm-domains.h" -#include "mt8192-pm-domains.h" -#include "mt8195-pm-domains.h" - -#define MTK_POLL_DELAY_US 10 -#define MTK_POLL_TIMEOUT USEC_PER_SEC - -#define PWR_RST_B_BIT BIT(0) -#define PWR_ISO_BIT BIT(1) -#define PWR_ON_BIT BIT(2) -#define PWR_ON_2ND_BIT BIT(3) -#define PWR_CLK_DIS_BIT BIT(4) -#define PWR_SRAM_CLKISO_BIT BIT(5) -#define PWR_SRAM_ISOINT_B_BIT BIT(6) - -struct scpsys_domain { - struct generic_pm_domain genpd; - const struct scpsys_domain_data *data; - struct scpsys *scpsys; - int num_clks; - struct clk_bulk_data *clks; - int num_subsys_clks; - struct clk_bulk_data *subsys_clks; - struct regmap *infracfg; - struct regmap *smi; - struct regulator *supply; -}; - -struct scpsys { - struct device *dev; - struct regmap *base; - const struct scpsys_soc_data *soc_data; - struct genpd_onecell_data pd_data; - struct generic_pm_domain *domains[]; -}; - -#define to_scpsys_domain(gpd) container_of(gpd, struct scpsys_domain, genpd) - -static bool scpsys_domain_is_on(struct scpsys_domain *pd) -{ - struct scpsys *scpsys = pd->scpsys; - u32 status, status2; - - regmap_read(scpsys->base, pd->data->pwr_sta_offs, &status); - status &= pd->data->sta_mask; - - regmap_read(scpsys->base, pd->data->pwr_sta2nd_offs, &status2); - status2 &= pd->data->sta_mask; - - /* A domain is on when both status bits are set. */ - return status && status2; -} - -static int scpsys_sram_enable(struct scpsys_domain *pd) -{ - u32 pdn_ack = pd->data->sram_pdn_ack_bits; - struct scpsys *scpsys = pd->scpsys; - unsigned int tmp; - int ret; - - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, pd->data->sram_pdn_bits); - - /* Either wait until SRAM_PDN_ACK all 1 or 0 */ - ret = regmap_read_poll_timeout(scpsys->base, pd->data->ctl_offs, tmp, - (tmp & pdn_ack) == 0, MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret < 0) - return ret; - - if (MTK_SCPD_CAPS(pd, MTK_SCPD_SRAM_ISO)) { - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_ISOINT_B_BIT); - udelay(1); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_CLKISO_BIT); - } - - return 0; -} - -static int scpsys_sram_disable(struct scpsys_domain *pd) -{ - u32 pdn_ack = pd->data->sram_pdn_ack_bits; - struct scpsys *scpsys = pd->scpsys; - unsigned int tmp; - - if (MTK_SCPD_CAPS(pd, MTK_SCPD_SRAM_ISO)) { - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_CLKISO_BIT); - udelay(1); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_ISOINT_B_BIT); - } - - regmap_set_bits(scpsys->base, pd->data->ctl_offs, pd->data->sram_pdn_bits); - - /* Either wait until SRAM_PDN_ACK all 1 or 0 */ - return regmap_read_poll_timeout(scpsys->base, pd->data->ctl_offs, tmp, - (tmp & pdn_ack) == pdn_ack, MTK_POLL_DELAY_US, - MTK_POLL_TIMEOUT); -} - -static int _scpsys_bus_protect_enable(const struct scpsys_bus_prot_data *bpd, struct regmap *regmap) -{ - int i, ret; - - for (i = 0; i < SPM_MAX_BUS_PROT_DATA; i++) { - u32 val, mask = bpd[i].bus_prot_mask; - - if (!mask) - break; - - if (bpd[i].bus_prot_reg_update) - regmap_set_bits(regmap, bpd[i].bus_prot_set, mask); - else - regmap_write(regmap, bpd[i].bus_prot_set, mask); - - ret = regmap_read_poll_timeout(regmap, bpd[i].bus_prot_sta, - val, (val & mask) == mask, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret) - return ret; - } - - return 0; -} - -static int scpsys_bus_protect_enable(struct scpsys_domain *pd) -{ - int ret; - - ret = _scpsys_bus_protect_enable(pd->data->bp_infracfg, pd->infracfg); - if (ret) - return ret; - - return _scpsys_bus_protect_enable(pd->data->bp_smi, pd->smi); -} - -static int _scpsys_bus_protect_disable(const struct scpsys_bus_prot_data *bpd, - struct regmap *regmap) -{ - int i, ret; - - for (i = SPM_MAX_BUS_PROT_DATA - 1; i >= 0; i--) { - u32 val, mask = bpd[i].bus_prot_mask; - - if (!mask) - continue; - - if (bpd[i].bus_prot_reg_update) - regmap_clear_bits(regmap, bpd[i].bus_prot_clr, mask); - else - regmap_write(regmap, bpd[i].bus_prot_clr, mask); - - if (bpd[i].ignore_clr_ack) - continue; - - ret = regmap_read_poll_timeout(regmap, bpd[i].bus_prot_sta, - val, !(val & mask), - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret) - return ret; - } - - return 0; -} - -static int scpsys_bus_protect_disable(struct scpsys_domain *pd) -{ - int ret; - - ret = _scpsys_bus_protect_disable(pd->data->bp_smi, pd->smi); - if (ret) - return ret; - - return _scpsys_bus_protect_disable(pd->data->bp_infracfg, pd->infracfg); -} - -static int scpsys_regulator_enable(struct regulator *supply) -{ - return supply ? regulator_enable(supply) : 0; -} - -static int scpsys_regulator_disable(struct regulator *supply) -{ - return supply ? regulator_disable(supply) : 0; -} - -static int scpsys_power_on(struct generic_pm_domain *genpd) -{ - struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); - struct scpsys *scpsys = pd->scpsys; - bool tmp; - int ret; - - ret = scpsys_regulator_enable(pd->supply); - if (ret) - return ret; - - ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks); - if (ret) - goto err_reg; - - if (pd->data->ext_buck_iso_offs && MTK_SCPD_CAPS(pd, MTK_SCPD_EXT_BUCK_ISO)) - regmap_clear_bits(scpsys->base, pd->data->ext_buck_iso_offs, - pd->data->ext_buck_iso_mask); - - /* subsys power on */ - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT); - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT); - - /* wait until PWR_ACK = 1 */ - ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, tmp, MTK_POLL_DELAY_US, - MTK_POLL_TIMEOUT); - if (ret < 0) - goto err_pwr_ack; - - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT); - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); - - ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks); - if (ret) - goto err_pwr_ack; - - ret = scpsys_sram_enable(pd); - if (ret < 0) - goto err_disable_subsys_clks; - - ret = scpsys_bus_protect_disable(pd); - if (ret < 0) - goto err_disable_sram; - - return 0; - -err_disable_sram: - scpsys_sram_disable(pd); -err_disable_subsys_clks: - clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); -err_pwr_ack: - clk_bulk_disable_unprepare(pd->num_clks, pd->clks); -err_reg: - scpsys_regulator_disable(pd->supply); - return ret; -} - -static int scpsys_power_off(struct generic_pm_domain *genpd) -{ - struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); - struct scpsys *scpsys = pd->scpsys; - bool tmp; - int ret; - - ret = scpsys_bus_protect_enable(pd); - if (ret < 0) - return ret; - - ret = scpsys_sram_disable(pd); - if (ret < 0) - return ret; - - if (pd->data->ext_buck_iso_offs && MTK_SCPD_CAPS(pd, MTK_SCPD_EXT_BUCK_ISO)) - regmap_set_bits(scpsys->base, pd->data->ext_buck_iso_offs, - pd->data->ext_buck_iso_mask); - - clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); - - /* subsys power off */ - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT); - regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT); - regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT); - - /* wait until PWR_ACK = 0 */ - ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, !tmp, MTK_POLL_DELAY_US, - MTK_POLL_TIMEOUT); - if (ret < 0) - return ret; - - clk_bulk_disable_unprepare(pd->num_clks, pd->clks); - - scpsys_regulator_disable(pd->supply); - - return 0; -} - -static struct -generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) -{ - const struct scpsys_domain_data *domain_data; - struct scpsys_domain *pd; - struct device_node *root_node = scpsys->dev->of_node; - struct device_node *smi_node; - struct property *prop; - const char *clk_name; - int i, ret, num_clks; - struct clk *clk; - int clk_ind = 0; - u32 id; - - ret = of_property_read_u32(node, "reg", &id); - if (ret) { - dev_err(scpsys->dev, "%pOF: failed to retrieve domain id from reg: %d\n", - node, ret); - return ERR_PTR(-EINVAL); - } - - if (id >= scpsys->soc_data->num_domains) { - dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id); - return ERR_PTR(-EINVAL); - } - - domain_data = &scpsys->soc_data->domains_data[id]; - if (domain_data->sta_mask == 0) { - dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id); - return ERR_PTR(-EINVAL); - } - - pd = devm_kzalloc(scpsys->dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return ERR_PTR(-ENOMEM); - - pd->data = domain_data; - pd->scpsys = scpsys; - - if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) { - /* - * Find regulator in current power domain node. - * devm_regulator_get() finds regulator in a node and its child - * node, so set of_node to current power domain node then change - * back to original node after regulator is found for current - * power domain node. - */ - scpsys->dev->of_node = node; - pd->supply = devm_regulator_get(scpsys->dev, "domain"); - scpsys->dev->of_node = root_node; - if (IS_ERR(pd->supply)) { - dev_err_probe(scpsys->dev, PTR_ERR(pd->supply), - "%pOF: failed to get power supply.\n", - node); - return ERR_CAST(pd->supply); - } - } - - pd->infracfg = syscon_regmap_lookup_by_phandle_optional(node, "mediatek,infracfg"); - if (IS_ERR(pd->infracfg)) - return ERR_CAST(pd->infracfg); - - smi_node = of_parse_phandle(node, "mediatek,smi", 0); - if (smi_node) { - pd->smi = device_node_to_regmap(smi_node); - of_node_put(smi_node); - if (IS_ERR(pd->smi)) - return ERR_CAST(pd->smi); - } - - num_clks = of_clk_get_parent_count(node); - if (num_clks > 0) { - /* Calculate number of subsys_clks */ - of_property_for_each_string(node, "clock-names", prop, clk_name) { - char *subsys; - - subsys = strchr(clk_name, '-'); - if (subsys) - pd->num_subsys_clks++; - else - pd->num_clks++; - } - - pd->clks = devm_kcalloc(scpsys->dev, pd->num_clks, sizeof(*pd->clks), GFP_KERNEL); - if (!pd->clks) - return ERR_PTR(-ENOMEM); - - pd->subsys_clks = devm_kcalloc(scpsys->dev, pd->num_subsys_clks, - sizeof(*pd->subsys_clks), GFP_KERNEL); - if (!pd->subsys_clks) - return ERR_PTR(-ENOMEM); - - } - - for (i = 0; i < pd->num_clks; i++) { - clk = of_clk_get(node, i); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err_probe(scpsys->dev, ret, - "%pOF: failed to get clk at index %d\n", node, i); - goto err_put_clocks; - } - - pd->clks[clk_ind++].clk = clk; - } - - for (i = 0; i < pd->num_subsys_clks; i++) { - clk = of_clk_get(node, i + clk_ind); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err_probe(scpsys->dev, ret, - "%pOF: failed to get clk at index %d\n", node, - i + clk_ind); - goto err_put_subsys_clocks; - } - - pd->subsys_clks[i].clk = clk; - } - - /* - * Initially turn on all domains to make the domains usable - * with !CONFIG_PM and to get the hardware in sync with the - * software. The unused domains will be switched off during - * late_init time. - */ - if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) { - if (scpsys_domain_is_on(pd)) - dev_warn(scpsys->dev, - "%pOF: A default off power domain has been ON\n", node); - } else { - ret = scpsys_power_on(&pd->genpd); - if (ret < 0) { - 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]) { - ret = -EINVAL; - dev_err(scpsys->dev, - "power domain with id %d already exists, check your device-tree\n", id); - goto err_put_subsys_clocks; - } - - if (!pd->data->name) - pd->genpd.name = node->name; - else - pd->genpd.name = pd->data->name; - - pd->genpd.power_off = scpsys_power_off; - pd->genpd.power_on = scpsys_power_on; - - if (MTK_SCPD_CAPS(pd, MTK_SCPD_ACTIVE_WAKEUP)) - pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; - - if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) - pm_genpd_init(&pd->genpd, NULL, true); - else - pm_genpd_init(&pd->genpd, NULL, false); - - scpsys->domains[id] = &pd->genpd; - - return scpsys->pd_data.domains[id]; - -err_put_subsys_clocks: - clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); -err_put_clocks: - clk_bulk_put(pd->num_clks, pd->clks); - return ERR_PTR(ret); -} - -static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent) -{ - struct generic_pm_domain *child_pd, *parent_pd; - struct device_node *child; - int ret; - - for_each_child_of_node(parent, child) { - u32 id; - - ret = of_property_read_u32(parent, "reg", &id); - if (ret) { - dev_err(scpsys->dev, "%pOF: failed to get parent domain id\n", child); - goto err_put_node; - } - - if (!scpsys->pd_data.domains[id]) { - ret = -EINVAL; - dev_err(scpsys->dev, "power domain with id %d does not exist\n", id); - goto err_put_node; - } - - parent_pd = scpsys->pd_data.domains[id]; - - child_pd = scpsys_add_one_domain(scpsys, child); - if (IS_ERR(child_pd)) { - ret = PTR_ERR(child_pd); - dev_err_probe(scpsys->dev, ret, "%pOF: failed to get child domain id\n", - child); - goto err_put_node; - } - - ret = pm_genpd_add_subdomain(parent_pd, child_pd); - if (ret) { - dev_err(scpsys->dev, "failed to add %s subdomain to parent %s\n", - child_pd->name, parent_pd->name); - goto err_put_node; - } else { - dev_dbg(scpsys->dev, "%s add subdomain: %s\n", parent_pd->name, - child_pd->name); - } - - /* recursive call to add all subdomains */ - ret = scpsys_add_subdomain(scpsys, child); - if (ret) - goto err_put_node; - } - - return 0; - -err_put_node: - of_node_put(child); - return ret; -} - -static void scpsys_remove_one_domain(struct scpsys_domain *pd) -{ - int ret; - - if (scpsys_domain_is_on(pd)) - scpsys_power_off(&pd->genpd); - - /* - * We're in the error cleanup already, so we only complain, - * but won't emit another error on top of the original one. - */ - ret = pm_genpd_remove(&pd->genpd); - if (ret < 0) - dev_err(pd->scpsys->dev, - "failed to remove domain '%s' : %d - state may be inconsistent\n", - pd->genpd.name, ret); - - clk_bulk_put(pd->num_clks, pd->clks); - clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); -} - -static void scpsys_domain_cleanup(struct scpsys *scpsys) -{ - struct generic_pm_domain *genpd; - struct scpsys_domain *pd; - int i; - - for (i = scpsys->pd_data.num_domains - 1; i >= 0; i--) { - genpd = scpsys->pd_data.domains[i]; - if (genpd) { - pd = to_scpsys_domain(genpd); - scpsys_remove_one_domain(pd); - } - } -} - -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, - }, - { - .compatible = "mediatek,mt8173-power-controller", - .data = &mt8173_scpsys_data, - }, - { - .compatible = "mediatek,mt8183-power-controller", - .data = &mt8183_scpsys_data, - }, - { - .compatible = "mediatek,mt8186-power-controller", - .data = &mt8186_scpsys_data, - }, - { - .compatible = "mediatek,mt8188-power-controller", - .data = &mt8188_scpsys_data, - }, - { - .compatible = "mediatek,mt8192-power-controller", - .data = &mt8192_scpsys_data, - }, - { - .compatible = "mediatek,mt8195-power-controller", - .data = &mt8195_scpsys_data, - }, - { } -}; - -static int scpsys_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - const struct scpsys_soc_data *soc; - struct device_node *node; - struct device *parent; - struct scpsys *scpsys; - int ret; - - soc = of_device_get_match_data(&pdev->dev); - if (!soc) { - dev_err(&pdev->dev, "no power controller data\n"); - return -EINVAL; - } - - scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, soc->num_domains), GFP_KERNEL); - if (!scpsys) - return -ENOMEM; - - scpsys->dev = dev; - scpsys->soc_data = soc; - - scpsys->pd_data.domains = scpsys->domains; - scpsys->pd_data.num_domains = soc->num_domains; - - parent = dev->parent; - if (!parent) { - dev_err(dev, "no parent for syscon devices\n"); - return -ENODEV; - } - - scpsys->base = syscon_node_to_regmap(parent->of_node); - if (IS_ERR(scpsys->base)) { - dev_err(dev, "no regmap available\n"); - return PTR_ERR(scpsys->base); - } - - ret = -ENODEV; - for_each_available_child_of_node(np, node) { - struct generic_pm_domain *domain; - - domain = scpsys_add_one_domain(scpsys, node); - if (IS_ERR(domain)) { - ret = PTR_ERR(domain); - of_node_put(node); - goto err_cleanup_domains; - } - - ret = scpsys_add_subdomain(scpsys, node); - if (ret) { - of_node_put(node); - goto err_cleanup_domains; - } - } - - if (ret) { - dev_dbg(dev, "no power domains present\n"); - return ret; - } - - ret = of_genpd_add_provider_onecell(np, &scpsys->pd_data); - if (ret) { - dev_err(dev, "failed to add provider: %d\n", ret); - goto err_cleanup_domains; - } - - return 0; - -err_cleanup_domains: - scpsys_domain_cleanup(scpsys); - return ret; -} - -static struct platform_driver scpsys_pm_domain_driver = { - .probe = scpsys_probe, - .driver = { - .name = "mtk-power-controller", - .suppress_bind_attrs = true, - .of_match_table = scpsys_of_match, - }, -}; -builtin_platform_driver(scpsys_pm_domain_driver); diff --git a/drivers/genpd/mediatek/mtk-pm-domains.h b/drivers/genpd/mediatek/mtk-pm-domains.h deleted file mode 100644 index 5ec53ee073c4..000000000000 --- a/drivers/genpd/mediatek/mtk-pm-domains.h +++ /dev/null @@ -1,111 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ - -#ifndef __SOC_MEDIATEK_MTK_PM_DOMAINS_H -#define __SOC_MEDIATEK_MTK_PM_DOMAINS_H - -#define MTK_SCPD_ACTIVE_WAKEUP BIT(0) -#define MTK_SCPD_FWAIT_SRAM BIT(1) -#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_EXT_BUCK_ISO BIT(6) -#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) - -#define SPM_VDE_PWR_CON 0x0210 -#define SPM_MFG_PWR_CON 0x0214 -#define SPM_VEN_PWR_CON 0x0230 -#define SPM_ISP_PWR_CON 0x0238 -#define SPM_DIS_PWR_CON 0x023c -#define SPM_CONN_PWR_CON 0x0280 -#define SPM_VEN2_PWR_CON 0x0298 -#define SPM_AUDIO_PWR_CON 0x029c -#define SPM_MFG_2D_PWR_CON 0x02c0 -#define SPM_MFG_ASYNC_PWR_CON 0x02c4 -#define SPM_USB_PWR_CON 0x02cc - -#define SPM_PWR_STATUS 0x060c -#define SPM_PWR_STATUS_2ND 0x0610 - -#define PWR_STATUS_CONN BIT(1) -#define PWR_STATUS_DISP BIT(3) -#define PWR_STATUS_MFG BIT(4) -#define PWR_STATUS_ISP BIT(5) -#define PWR_STATUS_VDEC BIT(7) -#define PWR_STATUS_VENC_LT BIT(20) -#define PWR_STATUS_VENC BIT(21) -#define PWR_STATUS_MFG_2D BIT(22) -#define PWR_STATUS_MFG_ASYNC BIT(23) -#define PWR_STATUS_AUDIO BIT(24) -#define PWR_STATUS_USB BIT(25) - -#define SPM_MAX_BUS_PROT_DATA 6 - -#define _BUS_PROT(_mask, _set, _clr, _sta, _update, _ignore) { \ - .bus_prot_mask = (_mask), \ - .bus_prot_set = _set, \ - .bus_prot_clr = _clr, \ - .bus_prot_sta = _sta, \ - .bus_prot_reg_update = _update, \ - .ignore_clr_ack = _ignore, \ - } - -#define BUS_PROT_WR(_mask, _set, _clr, _sta) \ - _BUS_PROT(_mask, _set, _clr, _sta, false, false) - -#define BUS_PROT_WR_IGN(_mask, _set, _clr, _sta) \ - _BUS_PROT(_mask, _set, _clr, _sta, false, true) - -#define BUS_PROT_UPDATE(_mask, _set, _clr, _sta) \ - _BUS_PROT(_mask, _set, _clr, _sta, true, false) - -#define BUS_PROT_UPDATE_TOPAXI(_mask) \ - BUS_PROT_UPDATE(_mask, \ - INFRA_TOPAXI_PROTECTEN, \ - INFRA_TOPAXI_PROTECTEN, \ - INFRA_TOPAXI_PROTECTSTA1) - -struct scpsys_bus_prot_data { - u32 bus_prot_mask; - u32 bus_prot_set; - u32 bus_prot_clr; - u32 bus_prot_sta; - bool bus_prot_reg_update; - bool ignore_clr_ack; -}; - -/** - * struct scpsys_domain_data - scp domain data for power on/off flow - * @name: The name of the power domain. - * @sta_mask: The mask for power on/off status bit. - * @ctl_offs: The offset for main power control register. - * @sram_pdn_bits: The mask for sram power control bits. - * @sram_pdn_ack_bits: The mask for sram power control acked bits. - * @ext_buck_iso_offs: The offset for external buck isolation - * @ext_buck_iso_mask: The mask for external buck isolation - * @caps: The flag for active wake-up action. - * @bp_infracfg: bus protection for infracfg subsystem - * @bp_smi: bus protection for smi subsystem - */ -struct scpsys_domain_data { - const char *name; - u32 sta_mask; - int ctl_offs; - u32 sram_pdn_bits; - u32 sram_pdn_ack_bits; - int ext_buck_iso_offs; - u32 ext_buck_iso_mask; - u8 caps; - const struct scpsys_bus_prot_data bp_infracfg[SPM_MAX_BUS_PROT_DATA]; - const struct scpsys_bus_prot_data bp_smi[SPM_MAX_BUS_PROT_DATA]; - int pwr_sta_offs; - int pwr_sta2nd_offs; -}; - -struct scpsys_soc_data { - const struct scpsys_domain_data *domains_data; - int num_domains; -}; - -#endif /* __SOC_MEDIATEK_MTK_PM_DOMAINS_H */ diff --git a/drivers/genpd/mediatek/mtk-scpsys.c b/drivers/genpd/mediatek/mtk-scpsys.c deleted file mode 100644 index b374d01fdac7..000000000000 --- a/drivers/genpd/mediatek/mtk-scpsys.c +++ /dev/null @@ -1,1147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2015 Pengutronix, Sascha Hauer - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#define MTK_POLL_DELAY_US 10 -#define MTK_POLL_TIMEOUT USEC_PER_SEC - -#define MTK_SCPD_ACTIVE_WAKEUP BIT(0) -#define MTK_SCPD_FWAIT_SRAM BIT(1) -#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) - -#define SPM_VDE_PWR_CON 0x0210 -#define SPM_MFG_PWR_CON 0x0214 -#define SPM_VEN_PWR_CON 0x0230 -#define SPM_ISP_PWR_CON 0x0238 -#define SPM_DIS_PWR_CON 0x023c -#define SPM_CONN_PWR_CON 0x0280 -#define SPM_VEN2_PWR_CON 0x0298 -#define SPM_AUDIO_PWR_CON 0x029c /* MT8173, MT2712 */ -#define SPM_BDP_PWR_CON 0x029c /* MT2701 */ -#define SPM_ETH_PWR_CON 0x02a0 -#define SPM_HIF_PWR_CON 0x02a4 -#define SPM_IFR_MSC_PWR_CON 0x02a8 -#define SPM_MFG_2D_PWR_CON 0x02c0 -#define SPM_MFG_ASYNC_PWR_CON 0x02c4 -#define SPM_USB_PWR_CON 0x02cc -#define SPM_USB2_PWR_CON 0x02d4 /* MT2712 */ -#define SPM_ETHSYS_PWR_CON 0x02e0 /* MT7622 */ -#define SPM_HIF0_PWR_CON 0x02e4 /* MT7622 */ -#define SPM_HIF1_PWR_CON 0x02e8 /* MT7622 */ -#define SPM_WB_PWR_CON 0x02ec /* MT7622 */ - -#define SPM_PWR_STATUS 0x060c -#define SPM_PWR_STATUS_2ND 0x0610 - -#define PWR_RST_B_BIT BIT(0) -#define PWR_ISO_BIT BIT(1) -#define PWR_ON_BIT BIT(2) -#define PWR_ON_2ND_BIT BIT(3) -#define PWR_CLK_DIS_BIT BIT(4) - -#define PWR_STATUS_CONN BIT(1) -#define PWR_STATUS_DISP BIT(3) -#define PWR_STATUS_MFG BIT(4) -#define PWR_STATUS_ISP BIT(5) -#define PWR_STATUS_VDEC BIT(7) -#define PWR_STATUS_BDP BIT(14) -#define PWR_STATUS_ETH BIT(15) -#define PWR_STATUS_HIF BIT(16) -#define PWR_STATUS_IFR_MSC BIT(17) -#define PWR_STATUS_USB2 BIT(19) /* MT2712 */ -#define PWR_STATUS_VENC_LT BIT(20) -#define PWR_STATUS_VENC BIT(21) -#define PWR_STATUS_MFG_2D BIT(22) /* MT8173 */ -#define PWR_STATUS_MFG_ASYNC BIT(23) /* MT8173 */ -#define PWR_STATUS_AUDIO BIT(24) /* MT8173, MT2712 */ -#define PWR_STATUS_USB BIT(25) /* MT8173, MT2712 */ -#define PWR_STATUS_ETHSYS BIT(24) /* MT7622 */ -#define PWR_STATUS_HIF0 BIT(25) /* MT7622 */ -#define PWR_STATUS_HIF1 BIT(26) /* MT7622 */ -#define PWR_STATUS_WB BIT(27) /* MT7622 */ - -enum clk_id { - CLK_NONE, - CLK_MM, - CLK_MFG, - CLK_VENC, - CLK_VENC_LT, - CLK_ETHIF, - CLK_VDEC, - CLK_HIFSEL, - CLK_JPGDEC, - CLK_AUDIO, - CLK_MAX, -}; - -static const char * const clk_names[] = { - NULL, - "mm", - "mfg", - "venc", - "venc_lt", - "ethif", - "vdec", - "hif_sel", - "jpgdec", - "audio", - NULL, -}; - -#define MAX_CLKS 3 - -/** - * struct scp_domain_data - scp domain data for power on/off flow - * @name: The domain name. - * @sta_mask: The mask for power on/off status bit. - * @ctl_offs: The offset for main power control register. - * @sram_pdn_bits: The mask for sram power control bits. - * @sram_pdn_ack_bits: The mask for sram power control acked bits. - * @bus_prot_mask: The mask for single step bus protection. - * @clk_id: The basic clocks required by this power domain. - * @caps: The flag for active wake-up action. - */ -struct scp_domain_data { - const char *name; - u32 sta_mask; - int ctl_offs; - u32 sram_pdn_bits; - u32 sram_pdn_ack_bits; - u32 bus_prot_mask; - enum clk_id clk_id[MAX_CLKS]; - u8 caps; -}; - -struct scp; - -struct scp_domain { - struct generic_pm_domain genpd; - struct scp *scp; - struct clk *clk[MAX_CLKS]; - const struct scp_domain_data *data; - struct regulator *supply; -}; - -struct scp_ctrl_reg { - int pwr_sta_offs; - int pwr_sta2nd_offs; -}; - -struct scp { - struct scp_domain *domains; - struct genpd_onecell_data pd_data; - struct device *dev; - void __iomem *base; - struct regmap *infracfg; - struct scp_ctrl_reg ctrl_reg; - bool bus_prot_reg_update; -}; - -struct scp_subdomain { - int origin; - int subdomain; -}; - -struct scp_soc_data { - const struct scp_domain_data *domains; - int num_domains; - const struct scp_subdomain *subdomains; - int num_subdomains; - const struct scp_ctrl_reg regs; - bool bus_prot_reg_update; -}; - -static int scpsys_domain_is_on(struct scp_domain *scpd) -{ - struct scp *scp = scpd->scp; - - u32 status = readl(scp->base + scp->ctrl_reg.pwr_sta_offs) & - scpd->data->sta_mask; - u32 status2 = readl(scp->base + scp->ctrl_reg.pwr_sta2nd_offs) & - scpd->data->sta_mask; - - /* - * A domain is on when both status bits are set. If only one is set - * return an error. This happens while powering up a domain - */ - - if (status && status2) - return true; - if (!status && !status2) - return false; - - return -EINVAL; -} - -static int scpsys_regulator_enable(struct scp_domain *scpd) -{ - if (!scpd->supply) - return 0; - - return regulator_enable(scpd->supply); -} - -static int scpsys_regulator_disable(struct scp_domain *scpd) -{ - if (!scpd->supply) - return 0; - - return regulator_disable(scpd->supply); -} - -static void scpsys_clk_disable(struct clk *clk[], int max_num) -{ - int i; - - for (i = max_num - 1; i >= 0; i--) - clk_disable_unprepare(clk[i]); -} - -static int scpsys_clk_enable(struct clk *clk[], int max_num) -{ - int i, ret = 0; - - for (i = 0; i < max_num && clk[i]; i++) { - ret = clk_prepare_enable(clk[i]); - if (ret) { - scpsys_clk_disable(clk, i); - break; - } - } - - return ret; -} - -static int scpsys_sram_enable(struct scp_domain *scpd, void __iomem *ctl_addr) -{ - u32 val; - u32 pdn_ack = scpd->data->sram_pdn_ack_bits; - int tmp; - - val = readl(ctl_addr); - val &= ~scpd->data->sram_pdn_bits; - writel(val, ctl_addr); - - /* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ - if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { - /* - * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for - * MT7622_POWER_DOMAIN_WB and thus just a trivial setup - * is applied here. - */ - usleep_range(12000, 12100); - } else { - /* Either wait until SRAM_PDN_ACK all 1 or 0 */ - int ret = readl_poll_timeout(ctl_addr, tmp, - (tmp & pdn_ack) == 0, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret < 0) - return ret; - } - - return 0; -} - -static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr) -{ - u32 val; - u32 pdn_ack = scpd->data->sram_pdn_ack_bits; - int tmp; - - val = readl(ctl_addr); - val |= scpd->data->sram_pdn_bits; - writel(val, ctl_addr); - - /* Either wait until SRAM_PDN_ACK all 1 or 0 */ - return readl_poll_timeout(ctl_addr, tmp, - (tmp & pdn_ack) == pdn_ack, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); -} - -static int scpsys_bus_protect_enable(struct scp_domain *scpd) -{ - struct scp *scp = scpd->scp; - - if (!scpd->data->bus_prot_mask) - return 0; - - return mtk_infracfg_set_bus_protection(scp->infracfg, - scpd->data->bus_prot_mask, - scp->bus_prot_reg_update); -} - -static int scpsys_bus_protect_disable(struct scp_domain *scpd) -{ - struct scp *scp = scpd->scp; - - if (!scpd->data->bus_prot_mask) - return 0; - - return mtk_infracfg_clear_bus_protection(scp->infracfg, - scpd->data->bus_prot_mask, - scp->bus_prot_reg_update); -} - -static int scpsys_power_on(struct generic_pm_domain *genpd) -{ - struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); - struct scp *scp = scpd->scp; - void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; - u32 val; - int ret, tmp; - - ret = scpsys_regulator_enable(scpd); - if (ret < 0) - return ret; - - ret = scpsys_clk_enable(scpd->clk, MAX_CLKS); - if (ret) - goto err_clk; - - /* subsys power on */ - val = readl(ctl_addr); - val |= PWR_ON_BIT; - writel(val, ctl_addr); - val |= PWR_ON_2ND_BIT; - writel(val, ctl_addr); - - /* wait until PWR_ACK = 1 */ - ret = readx_poll_timeout(scpsys_domain_is_on, scpd, tmp, tmp > 0, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret < 0) - goto err_pwr_ack; - - val &= ~PWR_CLK_DIS_BIT; - writel(val, ctl_addr); - - val &= ~PWR_ISO_BIT; - writel(val, ctl_addr); - - val |= PWR_RST_B_BIT; - writel(val, ctl_addr); - - ret = scpsys_sram_enable(scpd, ctl_addr); - if (ret < 0) - goto err_pwr_ack; - - ret = scpsys_bus_protect_disable(scpd); - if (ret < 0) - goto err_pwr_ack; - - return 0; - -err_pwr_ack: - scpsys_clk_disable(scpd->clk, MAX_CLKS); -err_clk: - scpsys_regulator_disable(scpd); - - dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); - - return ret; -} - -static int scpsys_power_off(struct generic_pm_domain *genpd) -{ - struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); - struct scp *scp = scpd->scp; - void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; - u32 val; - int ret, tmp; - - ret = scpsys_bus_protect_enable(scpd); - if (ret < 0) - goto out; - - ret = scpsys_sram_disable(scpd, ctl_addr); - if (ret < 0) - goto out; - - /* subsys power off */ - val = readl(ctl_addr); - val |= PWR_ISO_BIT; - writel(val, ctl_addr); - - val &= ~PWR_RST_B_BIT; - writel(val, ctl_addr); - - val |= PWR_CLK_DIS_BIT; - writel(val, ctl_addr); - - val &= ~PWR_ON_BIT; - writel(val, ctl_addr); - - val &= ~PWR_ON_2ND_BIT; - writel(val, ctl_addr); - - /* wait until PWR_ACK = 0 */ - ret = readx_poll_timeout(scpsys_domain_is_on, scpd, tmp, tmp == 0, - MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); - if (ret < 0) - goto out; - - scpsys_clk_disable(scpd->clk, MAX_CLKS); - - ret = scpsys_regulator_disable(scpd); - if (ret < 0) - goto out; - - return 0; - -out: - dev_err(scp->dev, "Failed to power off domain %s\n", genpd->name); - - return ret; -} - -static void init_clks(struct platform_device *pdev, struct clk **clk) -{ - int i; - - for (i = CLK_NONE + 1; i < CLK_MAX; i++) - clk[i] = devm_clk_get(&pdev->dev, clk_names[i]); -} - -static struct scp *init_scp(struct platform_device *pdev, - const struct scp_domain_data *scp_domain_data, int num, - const struct scp_ctrl_reg *scp_ctrl_reg, - bool bus_prot_reg_update) -{ - struct genpd_onecell_data *pd_data; - struct resource *res; - int i, j; - struct scp *scp; - struct clk *clk[CLK_MAX]; - - scp = devm_kzalloc(&pdev->dev, sizeof(*scp), GFP_KERNEL); - if (!scp) - return ERR_PTR(-ENOMEM); - - scp->ctrl_reg.pwr_sta_offs = scp_ctrl_reg->pwr_sta_offs; - scp->ctrl_reg.pwr_sta2nd_offs = scp_ctrl_reg->pwr_sta2nd_offs; - - scp->bus_prot_reg_update = bus_prot_reg_update; - - scp->dev = &pdev->dev; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - scp->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(scp->base)) - return ERR_CAST(scp->base); - - scp->domains = devm_kcalloc(&pdev->dev, - num, sizeof(*scp->domains), GFP_KERNEL); - if (!scp->domains) - return ERR_PTR(-ENOMEM); - - pd_data = &scp->pd_data; - - pd_data->domains = devm_kcalloc(&pdev->dev, - num, sizeof(*pd_data->domains), GFP_KERNEL); - if (!pd_data->domains) - return ERR_PTR(-ENOMEM); - - scp->infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, - "infracfg"); - if (IS_ERR(scp->infracfg)) { - dev_err(&pdev->dev, "Cannot find infracfg controller: %ld\n", - PTR_ERR(scp->infracfg)); - return ERR_CAST(scp->infracfg); - } - - for (i = 0; i < num; i++) { - struct scp_domain *scpd = &scp->domains[i]; - const struct scp_domain_data *data = &scp_domain_data[i]; - - scpd->supply = devm_regulator_get_optional(&pdev->dev, data->name); - if (IS_ERR(scpd->supply)) { - if (PTR_ERR(scpd->supply) == -ENODEV) - scpd->supply = NULL; - else - return ERR_CAST(scpd->supply); - } - } - - pd_data->num_domains = num; - - init_clks(pdev, clk); - - for (i = 0; i < num; i++) { - struct scp_domain *scpd = &scp->domains[i]; - struct generic_pm_domain *genpd = &scpd->genpd; - const struct scp_domain_data *data = &scp_domain_data[i]; - - pd_data->domains[i] = genpd; - scpd->scp = scp; - - scpd->data = data; - - for (j = 0; j < MAX_CLKS && data->clk_id[j]; j++) { - struct clk *c = clk[data->clk_id[j]]; - - if (IS_ERR(c)) { - dev_err(&pdev->dev, "%s: clk unavailable\n", - data->name); - return ERR_CAST(c); - } - - scpd->clk[j] = c; - } - - genpd->name = data->name; - genpd->power_off = scpsys_power_off; - genpd->power_on = scpsys_power_on; - if (MTK_SCPD_CAPS(scpd, MTK_SCPD_ACTIVE_WAKEUP)) - genpd->flags |= GENPD_FLAG_ACTIVE_WAKEUP; - } - - return scp; -} - -static void mtk_register_power_domains(struct platform_device *pdev, - struct scp *scp, int num) -{ - struct genpd_onecell_data *pd_data; - int i, ret; - - for (i = 0; i < num; i++) { - struct scp_domain *scpd = &scp->domains[i]; - struct generic_pm_domain *genpd = &scpd->genpd; - bool on; - - /* - * Initially turn on all domains to make the domains usable - * with !CONFIG_PM and to get the hardware in sync with the - * software. The unused domains will be switched off during - * late_init time. - */ - on = !WARN_ON(genpd->power_on(genpd) < 0); - - pm_genpd_init(genpd, NULL, !on); - } - - /* - * We are not allowed to fail here since there is no way to unregister - * a power domain. Once registered above we have to keep the domains - * valid. - */ - - pd_data = &scp->pd_data; - - ret = of_genpd_add_provider_onecell(pdev->dev.of_node, pd_data); - if (ret) - dev_err(&pdev->dev, "Failed to add OF provider: %d\n", ret); -} - -/* - * MT2701 power domain support - */ - -static const struct scp_domain_data scp_domain_data_mt2701[] = { - [MT2701_POWER_DOMAIN_CONN] = { - .name = "conn", - .sta_mask = PWR_STATUS_CONN, - .ctl_offs = SPM_CONN_PWR_CON, - .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_CONN_M | - MT2701_TOP_AXI_PROT_EN_CONN_S, - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_DISP] = { - .name = "disp", - .sta_mask = PWR_STATUS_DISP, - .ctl_offs = SPM_DIS_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .clk_id = {CLK_MM}, - .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_MM_M0, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_MFG] = { - .name = "mfg", - .sta_mask = PWR_STATUS_MFG, - .ctl_offs = SPM_MFG_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MFG}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = PWR_STATUS_VDEC, - .ctl_offs = SPM_VDE_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MM}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_ISP] = { - .name = "isp", - .sta_mask = PWR_STATUS_ISP, - .ctl_offs = SPM_ISP_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = {CLK_MM}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_BDP] = { - .name = "bdp", - .sta_mask = PWR_STATUS_BDP, - .ctl_offs = SPM_BDP_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_ETH] = { - .name = "eth", - .sta_mask = PWR_STATUS_ETH, - .ctl_offs = SPM_ETH_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_ETHIF}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_HIF] = { - .name = "hif", - .sta_mask = PWR_STATUS_HIF, - .ctl_offs = SPM_HIF_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_ETHIF}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2701_POWER_DOMAIN_IFR_MSC] = { - .name = "ifr_msc", - .sta_mask = PWR_STATUS_IFR_MSC, - .ctl_offs = SPM_IFR_MSC_PWR_CON, - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, -}; - -/* - * MT2712 power domain support - */ -static const struct scp_domain_data scp_domain_data_mt2712[] = { - [MT2712_POWER_DOMAIN_MM] = { - .name = "mm", - .sta_mask = PWR_STATUS_DISP, - .ctl_offs = SPM_DIS_PWR_CON, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MM}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = PWR_STATUS_VDEC, - .ctl_offs = SPM_VDE_PWR_CON, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MM, CLK_VDEC}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = PWR_STATUS_VENC, - .ctl_offs = SPM_VEN_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_MM, CLK_VENC, CLK_JPGDEC}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_ISP] = { - .name = "isp", - .sta_mask = PWR_STATUS_ISP, - .ctl_offs = SPM_ISP_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = {CLK_MM}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = PWR_STATUS_AUDIO, - .ctl_offs = SPM_AUDIO_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_AUDIO}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_USB] = { - .name = "usb", - .sta_mask = PWR_STATUS_USB, - .ctl_offs = SPM_USB_PWR_CON, - .sram_pdn_bits = GENMASK(10, 8), - .sram_pdn_ack_bits = GENMASK(14, 12), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_USB2] = { - .name = "usb2", - .sta_mask = PWR_STATUS_USB2, - .ctl_offs = SPM_USB2_PWR_CON, - .sram_pdn_bits = GENMASK(10, 8), - .sram_pdn_ack_bits = GENMASK(14, 12), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_MFG] = { - .name = "mfg", - .sta_mask = PWR_STATUS_MFG, - .ctl_offs = SPM_MFG_PWR_CON, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(16, 16), - .clk_id = {CLK_MFG}, - .bus_prot_mask = BIT(14) | BIT(21) | BIT(23), - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_MFG_SC1] = { - .name = "mfg_sc1", - .sta_mask = BIT(22), - .ctl_offs = 0x02c0, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(16, 16), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_MFG_SC2] = { - .name = "mfg_sc2", - .sta_mask = BIT(23), - .ctl_offs = 0x02c4, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(16, 16), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT2712_POWER_DOMAIN_MFG_SC3] = { - .name = "mfg_sc3", - .sta_mask = BIT(30), - .ctl_offs = 0x01f8, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(16, 16), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, -}; - -static const struct scp_subdomain scp_subdomain_mt2712[] = { - {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_VDEC}, - {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_VENC}, - {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_ISP}, - {MT2712_POWER_DOMAIN_MFG, MT2712_POWER_DOMAIN_MFG_SC1}, - {MT2712_POWER_DOMAIN_MFG_SC1, MT2712_POWER_DOMAIN_MFG_SC2}, - {MT2712_POWER_DOMAIN_MFG_SC2, MT2712_POWER_DOMAIN_MFG_SC3}, -}; - -/* - * MT6797 power domain support - */ - -static const struct scp_domain_data scp_domain_data_mt6797[] = { - [MT6797_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = BIT(7), - .ctl_offs = 0x300, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_VDEC}, - }, - [MT6797_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = BIT(21), - .ctl_offs = 0x304, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_NONE}, - }, - [MT6797_POWER_DOMAIN_ISP] = { - .name = "isp", - .sta_mask = BIT(5), - .ctl_offs = 0x308, - .sram_pdn_bits = GENMASK(9, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = {CLK_NONE}, - }, - [MT6797_POWER_DOMAIN_MM] = { - .name = "mm", - .sta_mask = BIT(3), - .ctl_offs = 0x30C, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MM}, - .bus_prot_mask = (BIT(1) | BIT(2)), - }, - [MT6797_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = BIT(24), - .ctl_offs = 0x314, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_NONE}, - }, - [MT6797_POWER_DOMAIN_MFG_ASYNC] = { - .name = "mfg_async", - .sta_mask = BIT(13), - .ctl_offs = 0x334, - .sram_pdn_bits = 0, - .sram_pdn_ack_bits = 0, - .clk_id = {CLK_MFG}, - }, - [MT6797_POWER_DOMAIN_MJC] = { - .name = "mjc", - .sta_mask = BIT(20), - .ctl_offs = 0x310, - .sram_pdn_bits = GENMASK(8, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_NONE}, - }, -}; - -#define SPM_PWR_STATUS_MT6797 0x0180 -#define SPM_PWR_STATUS_2ND_MT6797 0x0184 - -static const struct scp_subdomain scp_subdomain_mt6797[] = { - {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_VDEC}, - {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_ISP}, - {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_VENC}, - {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_MJC}, -}; - -/* - * MT7622 power domain support - */ - -static const struct scp_domain_data scp_domain_data_mt7622[] = { - [MT7622_POWER_DOMAIN_ETHSYS] = { - .name = "ethsys", - .sta_mask = PWR_STATUS_ETHSYS, - .ctl_offs = SPM_ETHSYS_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_NONE}, - .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_ETHSYS, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT7622_POWER_DOMAIN_HIF0] = { - .name = "hif0", - .sta_mask = PWR_STATUS_HIF0, - .ctl_offs = SPM_HIF0_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_HIFSEL}, - .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_HIF0, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT7622_POWER_DOMAIN_HIF1] = { - .name = "hif1", - .sta_mask = PWR_STATUS_HIF1, - .ctl_offs = SPM_HIF1_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_HIFSEL}, - .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_HIF1, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT7622_POWER_DOMAIN_WB] = { - .name = "wb", - .sta_mask = PWR_STATUS_WB, - .ctl_offs = SPM_WB_PWR_CON, - .sram_pdn_bits = 0, - .sram_pdn_ack_bits = 0, - .clk_id = {CLK_NONE}, - .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_WB, - .caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_FWAIT_SRAM, - }, -}; - -/* - * MT7623A power domain support - */ - -static const struct scp_domain_data scp_domain_data_mt7623a[] = { - [MT7623A_POWER_DOMAIN_CONN] = { - .name = "conn", - .sta_mask = PWR_STATUS_CONN, - .ctl_offs = SPM_CONN_PWR_CON, - .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_CONN_M | - MT2701_TOP_AXI_PROT_EN_CONN_S, - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT7623A_POWER_DOMAIN_ETH] = { - .name = "eth", - .sta_mask = PWR_STATUS_ETH, - .ctl_offs = SPM_ETH_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_ETHIF}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT7623A_POWER_DOMAIN_HIF] = { - .name = "hif", - .sta_mask = PWR_STATUS_HIF, - .ctl_offs = SPM_HIF_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_ETHIF}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT7623A_POWER_DOMAIN_IFR_MSC] = { - .name = "ifr_msc", - .sta_mask = PWR_STATUS_IFR_MSC, - .ctl_offs = SPM_IFR_MSC_PWR_CON, - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, -}; - -/* - * MT8173 power domain support - */ - -static const struct scp_domain_data scp_domain_data_mt8173[] = { - [MT8173_POWER_DOMAIN_VDEC] = { - .name = "vdec", - .sta_mask = PWR_STATUS_VDEC, - .ctl_offs = SPM_VDE_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MM}, - }, - [MT8173_POWER_DOMAIN_VENC] = { - .name = "venc", - .sta_mask = PWR_STATUS_VENC, - .ctl_offs = SPM_VEN_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_MM, CLK_VENC}, - }, - [MT8173_POWER_DOMAIN_ISP] = { - .name = "isp", - .sta_mask = PWR_STATUS_ISP, - .ctl_offs = SPM_ISP_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = {CLK_MM}, - }, - [MT8173_POWER_DOMAIN_MM] = { - .name = "mm", - .sta_mask = PWR_STATUS_DISP, - .ctl_offs = SPM_DIS_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(12, 12), - .clk_id = {CLK_MM}, - .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MM_M0 | - MT8173_TOP_AXI_PROT_EN_MM_M1, - }, - [MT8173_POWER_DOMAIN_VENC_LT] = { - .name = "venc_lt", - .sta_mask = PWR_STATUS_VENC_LT, - .ctl_offs = SPM_VEN2_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_MM, CLK_VENC_LT}, - }, - [MT8173_POWER_DOMAIN_AUDIO] = { - .name = "audio", - .sta_mask = PWR_STATUS_AUDIO, - .ctl_offs = SPM_AUDIO_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_NONE}, - }, - [MT8173_POWER_DOMAIN_USB] = { - .name = "usb", - .sta_mask = PWR_STATUS_USB, - .ctl_offs = SPM_USB_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(15, 12), - .clk_id = {CLK_NONE}, - .caps = MTK_SCPD_ACTIVE_WAKEUP, - }, - [MT8173_POWER_DOMAIN_MFG_ASYNC] = { - .name = "mfg_async", - .sta_mask = PWR_STATUS_MFG_ASYNC, - .ctl_offs = SPM_MFG_ASYNC_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = 0, - .clk_id = {CLK_MFG}, - }, - [MT8173_POWER_DOMAIN_MFG_2D] = { - .name = "mfg_2d", - .sta_mask = PWR_STATUS_MFG_2D, - .ctl_offs = SPM_MFG_2D_PWR_CON, - .sram_pdn_bits = GENMASK(11, 8), - .sram_pdn_ack_bits = GENMASK(13, 12), - .clk_id = {CLK_NONE}, - }, - [MT8173_POWER_DOMAIN_MFG] = { - .name = "mfg", - .sta_mask = PWR_STATUS_MFG, - .ctl_offs = SPM_MFG_PWR_CON, - .sram_pdn_bits = GENMASK(13, 8), - .sram_pdn_ack_bits = GENMASK(21, 16), - .clk_id = {CLK_NONE}, - .bus_prot_mask = 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 scp_subdomain scp_subdomain_mt8173[] = { - {MT8173_POWER_DOMAIN_MFG_ASYNC, MT8173_POWER_DOMAIN_MFG_2D}, - {MT8173_POWER_DOMAIN_MFG_2D, MT8173_POWER_DOMAIN_MFG}, -}; - -static const struct scp_soc_data mt2701_data = { - .domains = scp_domain_data_mt2701, - .num_domains = ARRAY_SIZE(scp_domain_data_mt2701), - .regs = { - .pwr_sta_offs = SPM_PWR_STATUS, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND - }, - .bus_prot_reg_update = true, -}; - -static const struct scp_soc_data mt2712_data = { - .domains = scp_domain_data_mt2712, - .num_domains = ARRAY_SIZE(scp_domain_data_mt2712), - .subdomains = scp_subdomain_mt2712, - .num_subdomains = ARRAY_SIZE(scp_subdomain_mt2712), - .regs = { - .pwr_sta_offs = SPM_PWR_STATUS, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND - }, - .bus_prot_reg_update = false, -}; - -static const struct scp_soc_data mt6797_data = { - .domains = scp_domain_data_mt6797, - .num_domains = ARRAY_SIZE(scp_domain_data_mt6797), - .subdomains = scp_subdomain_mt6797, - .num_subdomains = ARRAY_SIZE(scp_subdomain_mt6797), - .regs = { - .pwr_sta_offs = SPM_PWR_STATUS_MT6797, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797 - }, - .bus_prot_reg_update = true, -}; - -static const struct scp_soc_data mt7622_data = { - .domains = scp_domain_data_mt7622, - .num_domains = ARRAY_SIZE(scp_domain_data_mt7622), - .regs = { - .pwr_sta_offs = SPM_PWR_STATUS, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND - }, - .bus_prot_reg_update = true, -}; - -static const struct scp_soc_data mt7623a_data = { - .domains = scp_domain_data_mt7623a, - .num_domains = ARRAY_SIZE(scp_domain_data_mt7623a), - .regs = { - .pwr_sta_offs = SPM_PWR_STATUS, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND - }, - .bus_prot_reg_update = true, -}; - -static const struct scp_soc_data mt8173_data = { - .domains = scp_domain_data_mt8173, - .num_domains = ARRAY_SIZE(scp_domain_data_mt8173), - .subdomains = scp_subdomain_mt8173, - .num_subdomains = ARRAY_SIZE(scp_subdomain_mt8173), - .regs = { - .pwr_sta_offs = SPM_PWR_STATUS, - .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND - }, - .bus_prot_reg_update = true, -}; - -/* - * scpsys driver init - */ - -static const struct of_device_id of_scpsys_match_tbl[] = { - { - .compatible = "mediatek,mt2701-scpsys", - .data = &mt2701_data, - }, { - .compatible = "mediatek,mt2712-scpsys", - .data = &mt2712_data, - }, { - .compatible = "mediatek,mt6797-scpsys", - .data = &mt6797_data, - }, { - .compatible = "mediatek,mt7622-scpsys", - .data = &mt7622_data, - }, { - .compatible = "mediatek,mt7623a-scpsys", - .data = &mt7623a_data, - }, { - .compatible = "mediatek,mt8173-scpsys", - .data = &mt8173_data, - }, { - /* sentinel */ - } -}; - -static int scpsys_probe(struct platform_device *pdev) -{ - const struct scp_subdomain *sd; - const struct scp_soc_data *soc; - struct scp *scp; - struct genpd_onecell_data *pd_data; - int i, ret; - - soc = of_device_get_match_data(&pdev->dev); - - scp = init_scp(pdev, soc->domains, soc->num_domains, &soc->regs, - soc->bus_prot_reg_update); - if (IS_ERR(scp)) - return PTR_ERR(scp); - - mtk_register_power_domains(pdev, scp, soc->num_domains); - - pd_data = &scp->pd_data; - - for (i = 0, sd = soc->subdomains; i < soc->num_subdomains; i++, sd++) { - ret = pm_genpd_add_subdomain(pd_data->domains[sd->origin], - pd_data->domains[sd->subdomain]); - if (ret && IS_ENABLED(CONFIG_PM)) - dev_err(&pdev->dev, "Failed to add subdomain: %d\n", - ret); - } - - return 0; -} - -static struct platform_driver scpsys_drv = { - .probe = scpsys_probe, - .driver = { - .name = "mtk-scpsys", - .suppress_bind_attrs = true, - .owner = THIS_MODULE, - .of_match_table = of_scpsys_match_tbl, - }, -}; -builtin_platform_driver(scpsys_drv); diff --git a/drivers/genpd/qcom/Makefile b/drivers/genpd/qcom/Makefile deleted file mode 100644 index 403dfc5af095..000000000000 --- a/drivers/genpd/qcom/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_QCOM_CPR) += cpr.o -obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o -obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o diff --git a/drivers/genpd/qcom/cpr.c b/drivers/genpd/qcom/cpr.c deleted file mode 100644 index 94a3f0977212..000000000000 --- a/drivers/genpd/qcom/cpr.c +++ /dev/null @@ -1,1756 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. - * Copyright (c) 2019, Linaro Limited - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* Register Offsets for RB-CPR and Bit Definitions */ - -/* RBCPR Version Register */ -#define REG_RBCPR_VERSION 0 -#define RBCPR_VER_2 0x02 -#define FLAGS_IGNORE_1ST_IRQ_STATUS BIT(0) - -/* RBCPR Gate Count and Target Registers */ -#define REG_RBCPR_GCNT_TARGET(n) (0x60 + 4 * (n)) - -#define RBCPR_GCNT_TARGET_TARGET_SHIFT 0 -#define RBCPR_GCNT_TARGET_TARGET_MASK GENMASK(11, 0) -#define RBCPR_GCNT_TARGET_GCNT_SHIFT 12 -#define RBCPR_GCNT_TARGET_GCNT_MASK GENMASK(9, 0) - -/* RBCPR Timer Control */ -#define REG_RBCPR_TIMER_INTERVAL 0x44 -#define REG_RBIF_TIMER_ADJUST 0x4c - -#define RBIF_TIMER_ADJ_CONS_UP_MASK GENMASK(3, 0) -#define RBIF_TIMER_ADJ_CONS_UP_SHIFT 0 -#define RBIF_TIMER_ADJ_CONS_DOWN_MASK GENMASK(3, 0) -#define RBIF_TIMER_ADJ_CONS_DOWN_SHIFT 4 -#define RBIF_TIMER_ADJ_CLAMP_INT_MASK GENMASK(7, 0) -#define RBIF_TIMER_ADJ_CLAMP_INT_SHIFT 8 - -/* RBCPR Config Register */ -#define REG_RBIF_LIMIT 0x48 -#define RBIF_LIMIT_CEILING_MASK GENMASK(5, 0) -#define RBIF_LIMIT_CEILING_SHIFT 6 -#define RBIF_LIMIT_FLOOR_BITS 6 -#define RBIF_LIMIT_FLOOR_MASK GENMASK(5, 0) - -#define RBIF_LIMIT_CEILING_DEFAULT RBIF_LIMIT_CEILING_MASK -#define RBIF_LIMIT_FLOOR_DEFAULT 0 - -#define REG_RBIF_SW_VLEVEL 0x94 -#define RBIF_SW_VLEVEL_DEFAULT 0x20 - -#define REG_RBCPR_STEP_QUOT 0x80 -#define RBCPR_STEP_QUOT_STEPQUOT_MASK GENMASK(7, 0) -#define RBCPR_STEP_QUOT_IDLE_CLK_MASK GENMASK(3, 0) -#define RBCPR_STEP_QUOT_IDLE_CLK_SHIFT 8 - -/* RBCPR Control Register */ -#define REG_RBCPR_CTL 0x90 - -#define RBCPR_CTL_LOOP_EN BIT(0) -#define RBCPR_CTL_TIMER_EN BIT(3) -#define RBCPR_CTL_SW_AUTO_CONT_ACK_EN BIT(5) -#define RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN BIT(6) -#define RBCPR_CTL_COUNT_MODE BIT(10) -#define RBCPR_CTL_UP_THRESHOLD_MASK GENMASK(3, 0) -#define RBCPR_CTL_UP_THRESHOLD_SHIFT 24 -#define RBCPR_CTL_DN_THRESHOLD_MASK GENMASK(3, 0) -#define RBCPR_CTL_DN_THRESHOLD_SHIFT 28 - -/* RBCPR Ack/Nack Response */ -#define REG_RBIF_CONT_ACK_CMD 0x98 -#define REG_RBIF_CONT_NACK_CMD 0x9c - -/* RBCPR Result status Register */ -#define REG_RBCPR_RESULT_0 0xa0 - -#define RBCPR_RESULT0_BUSY_SHIFT 19 -#define RBCPR_RESULT0_BUSY_MASK BIT(RBCPR_RESULT0_BUSY_SHIFT) -#define RBCPR_RESULT0_ERROR_LT0_SHIFT 18 -#define RBCPR_RESULT0_ERROR_SHIFT 6 -#define RBCPR_RESULT0_ERROR_MASK GENMASK(11, 0) -#define RBCPR_RESULT0_ERROR_STEPS_SHIFT 2 -#define RBCPR_RESULT0_ERROR_STEPS_MASK GENMASK(3, 0) -#define RBCPR_RESULT0_STEP_UP_SHIFT 1 - -/* RBCPR Interrupt Control Register */ -#define REG_RBIF_IRQ_EN(n) (0x100 + 4 * (n)) -#define REG_RBIF_IRQ_CLEAR 0x110 -#define REG_RBIF_IRQ_STATUS 0x114 - -#define CPR_INT_DONE BIT(0) -#define CPR_INT_MIN BIT(1) -#define CPR_INT_DOWN BIT(2) -#define CPR_INT_MID BIT(3) -#define CPR_INT_UP BIT(4) -#define CPR_INT_MAX BIT(5) -#define CPR_INT_CLAMP BIT(6) -#define CPR_INT_ALL (CPR_INT_DONE | CPR_INT_MIN | CPR_INT_DOWN | \ - CPR_INT_MID | CPR_INT_UP | CPR_INT_MAX | CPR_INT_CLAMP) -#define CPR_INT_DEFAULT (CPR_INT_UP | CPR_INT_DOWN) - -#define CPR_NUM_RING_OSC 8 - -/* CPR eFuse parameters */ -#define CPR_FUSE_TARGET_QUOT_BITS_MASK GENMASK(11, 0) - -#define CPR_FUSE_MIN_QUOT_DIFF 50 - -#define FUSE_REVISION_UNKNOWN (-1) - -enum voltage_change_dir { - NO_CHANGE, - DOWN, - UP, -}; - -struct cpr_fuse { - char *ring_osc; - char *init_voltage; - char *quotient; - char *quotient_offset; -}; - -struct fuse_corner_data { - int ref_uV; - int max_uV; - int min_uV; - int max_volt_scale; - int max_quot_scale; - /* fuse quot */ - int quot_offset; - int quot_scale; - int quot_adjust; - /* fuse quot_offset */ - int quot_offset_scale; - int quot_offset_adjust; -}; - -struct cpr_fuses { - int init_voltage_step; - int init_voltage_width; - struct fuse_corner_data *fuse_corner_data; -}; - -struct corner_data { - unsigned int fuse_corner; - unsigned long freq; -}; - -struct cpr_desc { - unsigned int num_fuse_corners; - int min_diff_quot; - int *step_quot; - - unsigned int timer_delay_us; - unsigned int timer_cons_up; - unsigned int timer_cons_down; - unsigned int up_threshold; - unsigned int down_threshold; - unsigned int idle_clocks; - unsigned int gcnt_us; - unsigned int vdd_apc_step_up_limit; - unsigned int vdd_apc_step_down_limit; - unsigned int clamp_timer_interval; - - struct cpr_fuses cpr_fuses; - bool reduce_to_fuse_uV; - bool reduce_to_corner_uV; -}; - -struct acc_desc { - unsigned int enable_reg; - u32 enable_mask; - - struct reg_sequence *config; - struct reg_sequence *settings; - int num_regs_per_fuse; -}; - -struct cpr_acc_desc { - const struct cpr_desc *cpr_desc; - const struct acc_desc *acc_desc; -}; - -struct fuse_corner { - int min_uV; - int max_uV; - int uV; - int quot; - int step_quot; - const struct reg_sequence *accs; - int num_accs; - unsigned long max_freq; - u8 ring_osc_idx; -}; - -struct corner { - int min_uV; - int max_uV; - int uV; - int last_uV; - int quot_adjust; - u32 save_ctl; - u32 save_irq; - unsigned long freq; - struct fuse_corner *fuse_corner; -}; - -struct cpr_drv { - unsigned int num_corners; - unsigned int ref_clk_khz; - - struct generic_pm_domain pd; - struct device *dev; - struct device *attached_cpu_dev; - struct mutex lock; - void __iomem *base; - struct corner *corner; - struct regulator *vdd_apc; - struct clk *cpu_clk; - struct regmap *tcsr; - bool loop_disabled; - u32 gcnt; - unsigned long flags; - - struct fuse_corner *fuse_corners; - struct corner *corners; - - const struct cpr_desc *desc; - const struct acc_desc *acc_desc; - const struct cpr_fuse *cpr_fuses; - - struct dentry *debugfs; -}; - -static bool cpr_is_allowed(struct cpr_drv *drv) -{ - return !drv->loop_disabled; -} - -static void cpr_write(struct cpr_drv *drv, u32 offset, u32 value) -{ - writel_relaxed(value, drv->base + offset); -} - -static u32 cpr_read(struct cpr_drv *drv, u32 offset) -{ - return readl_relaxed(drv->base + offset); -} - -static void -cpr_masked_write(struct cpr_drv *drv, u32 offset, u32 mask, u32 value) -{ - u32 val; - - val = readl_relaxed(drv->base + offset); - val &= ~mask; - val |= value & mask; - writel_relaxed(val, drv->base + offset); -} - -static void cpr_irq_clr(struct cpr_drv *drv) -{ - cpr_write(drv, REG_RBIF_IRQ_CLEAR, CPR_INT_ALL); -} - -static void cpr_irq_clr_nack(struct cpr_drv *drv) -{ - cpr_irq_clr(drv); - cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1); -} - -static void cpr_irq_clr_ack(struct cpr_drv *drv) -{ - cpr_irq_clr(drv); - cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1); -} - -static void cpr_irq_set(struct cpr_drv *drv, u32 int_bits) -{ - cpr_write(drv, REG_RBIF_IRQ_EN(0), int_bits); -} - -static void cpr_ctl_modify(struct cpr_drv *drv, u32 mask, u32 value) -{ - cpr_masked_write(drv, REG_RBCPR_CTL, mask, value); -} - -static void cpr_ctl_enable(struct cpr_drv *drv, struct corner *corner) -{ - u32 val, mask; - const struct cpr_desc *desc = drv->desc; - - /* Program Consecutive Up & Down */ - val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT; - val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT; - mask = RBIF_TIMER_ADJ_CONS_UP_MASK | RBIF_TIMER_ADJ_CONS_DOWN_MASK; - cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST, mask, val); - cpr_masked_write(drv, REG_RBCPR_CTL, - RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN | - RBCPR_CTL_SW_AUTO_CONT_ACK_EN, - corner->save_ctl); - cpr_irq_set(drv, corner->save_irq); - - if (cpr_is_allowed(drv) && corner->max_uV > corner->min_uV) - val = RBCPR_CTL_LOOP_EN; - else - val = 0; - cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, val); -} - -static void cpr_ctl_disable(struct cpr_drv *drv) -{ - cpr_irq_set(drv, 0); - cpr_ctl_modify(drv, RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN | - RBCPR_CTL_SW_AUTO_CONT_ACK_EN, 0); - cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST, - RBIF_TIMER_ADJ_CONS_UP_MASK | - RBIF_TIMER_ADJ_CONS_DOWN_MASK, 0); - cpr_irq_clr(drv); - cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1); - cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1); - cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, 0); -} - -static bool cpr_ctl_is_enabled(struct cpr_drv *drv) -{ - u32 reg_val; - - reg_val = cpr_read(drv, REG_RBCPR_CTL); - return reg_val & RBCPR_CTL_LOOP_EN; -} - -static bool cpr_ctl_is_busy(struct cpr_drv *drv) -{ - u32 reg_val; - - reg_val = cpr_read(drv, REG_RBCPR_RESULT_0); - return reg_val & RBCPR_RESULT0_BUSY_MASK; -} - -static void cpr_corner_save(struct cpr_drv *drv, struct corner *corner) -{ - corner->save_ctl = cpr_read(drv, REG_RBCPR_CTL); - corner->save_irq = cpr_read(drv, REG_RBIF_IRQ_EN(0)); -} - -static void cpr_corner_restore(struct cpr_drv *drv, struct corner *corner) -{ - u32 gcnt, ctl, irq, ro_sel, step_quot; - struct fuse_corner *fuse = corner->fuse_corner; - const struct cpr_desc *desc = drv->desc; - int i; - - ro_sel = fuse->ring_osc_idx; - gcnt = drv->gcnt; - gcnt |= fuse->quot - corner->quot_adjust; - - /* Program the step quotient and idle clocks */ - step_quot = desc->idle_clocks << RBCPR_STEP_QUOT_IDLE_CLK_SHIFT; - step_quot |= fuse->step_quot & RBCPR_STEP_QUOT_STEPQUOT_MASK; - cpr_write(drv, REG_RBCPR_STEP_QUOT, step_quot); - - /* Clear the target quotient value and gate count of all ROs */ - for (i = 0; i < CPR_NUM_RING_OSC; i++) - cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0); - - cpr_write(drv, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt); - ctl = corner->save_ctl; - cpr_write(drv, REG_RBCPR_CTL, ctl); - irq = corner->save_irq; - cpr_irq_set(drv, irq); - dev_dbg(drv->dev, "gcnt = %#08x, ctl = %#08x, irq = %#08x\n", gcnt, - ctl, irq); -} - -static void cpr_set_acc(struct regmap *tcsr, struct fuse_corner *f, - struct fuse_corner *end) -{ - if (f == end) - return; - - if (f < end) { - for (f += 1; f <= end; f++) - regmap_multi_reg_write(tcsr, f->accs, f->num_accs); - } else { - for (f -= 1; f >= end; f--) - regmap_multi_reg_write(tcsr, f->accs, f->num_accs); - } -} - -static int cpr_pre_voltage(struct cpr_drv *drv, - struct fuse_corner *fuse_corner, - enum voltage_change_dir dir) -{ - struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner; - - if (drv->tcsr && dir == DOWN) - cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner); - - return 0; -} - -static int cpr_post_voltage(struct cpr_drv *drv, - struct fuse_corner *fuse_corner, - enum voltage_change_dir dir) -{ - struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner; - - if (drv->tcsr && dir == UP) - cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner); - - return 0; -} - -static int cpr_scale_voltage(struct cpr_drv *drv, struct corner *corner, - int new_uV, enum voltage_change_dir dir) -{ - int ret; - struct fuse_corner *fuse_corner = corner->fuse_corner; - - ret = cpr_pre_voltage(drv, fuse_corner, dir); - if (ret) - return ret; - - ret = regulator_set_voltage(drv->vdd_apc, new_uV, new_uV); - if (ret) { - dev_err_ratelimited(drv->dev, "failed to set apc voltage %d\n", - new_uV); - return ret; - } - - ret = cpr_post_voltage(drv, fuse_corner, dir); - if (ret) - return ret; - - return 0; -} - -static unsigned int cpr_get_cur_perf_state(struct cpr_drv *drv) -{ - return drv->corner ? drv->corner - drv->corners + 1 : 0; -} - -static int cpr_scale(struct cpr_drv *drv, enum voltage_change_dir dir) -{ - u32 val, error_steps, reg_mask; - int last_uV, new_uV, step_uV, ret; - struct corner *corner; - const struct cpr_desc *desc = drv->desc; - - if (dir != UP && dir != DOWN) - return 0; - - step_uV = regulator_get_linear_step(drv->vdd_apc); - if (!step_uV) - return -EINVAL; - - corner = drv->corner; - - val = cpr_read(drv, REG_RBCPR_RESULT_0); - - error_steps = val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT; - error_steps &= RBCPR_RESULT0_ERROR_STEPS_MASK; - last_uV = corner->last_uV; - - if (dir == UP) { - if (desc->clamp_timer_interval && - error_steps < desc->up_threshold) { - /* - * Handle the case where another measurement started - * after the interrupt was triggered due to a core - * exiting from power collapse. - */ - error_steps = max(desc->up_threshold, - desc->vdd_apc_step_up_limit); - } - - if (last_uV >= corner->max_uV) { - cpr_irq_clr_nack(drv); - - /* Maximize the UP threshold */ - reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK; - reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT; - val = reg_mask; - cpr_ctl_modify(drv, reg_mask, val); - - /* Disable UP interrupt */ - cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_UP); - - return 0; - } - - if (error_steps > desc->vdd_apc_step_up_limit) - error_steps = desc->vdd_apc_step_up_limit; - - /* Calculate new voltage */ - new_uV = last_uV + error_steps * step_uV; - new_uV = min(new_uV, corner->max_uV); - - dev_dbg(drv->dev, - "UP: -> new_uV: %d last_uV: %d perf state: %u\n", - new_uV, last_uV, cpr_get_cur_perf_state(drv)); - } else { - if (desc->clamp_timer_interval && - error_steps < desc->down_threshold) { - /* - * Handle the case where another measurement started - * after the interrupt was triggered due to a core - * exiting from power collapse. - */ - error_steps = max(desc->down_threshold, - desc->vdd_apc_step_down_limit); - } - - if (last_uV <= corner->min_uV) { - cpr_irq_clr_nack(drv); - - /* Enable auto nack down */ - reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN; - val = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN; - - cpr_ctl_modify(drv, reg_mask, val); - - /* Disable DOWN interrupt */ - cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_DOWN); - - return 0; - } - - if (error_steps > desc->vdd_apc_step_down_limit) - error_steps = desc->vdd_apc_step_down_limit; - - /* Calculate new voltage */ - new_uV = last_uV - error_steps * step_uV; - new_uV = max(new_uV, corner->min_uV); - - dev_dbg(drv->dev, - "DOWN: -> new_uV: %d last_uV: %d perf state: %u\n", - new_uV, last_uV, cpr_get_cur_perf_state(drv)); - } - - ret = cpr_scale_voltage(drv, corner, new_uV, dir); - if (ret) { - cpr_irq_clr_nack(drv); - return ret; - } - drv->corner->last_uV = new_uV; - - if (dir == UP) { - /* Disable auto nack down */ - reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN; - val = 0; - } else { - /* Restore default threshold for UP */ - reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK; - reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT; - val = desc->up_threshold; - val <<= RBCPR_CTL_UP_THRESHOLD_SHIFT; - } - - cpr_ctl_modify(drv, reg_mask, val); - - /* Re-enable default interrupts */ - cpr_irq_set(drv, CPR_INT_DEFAULT); - - /* Ack */ - cpr_irq_clr_ack(drv); - - return 0; -} - -static irqreturn_t cpr_irq_handler(int irq, void *dev) -{ - struct cpr_drv *drv = dev; - const struct cpr_desc *desc = drv->desc; - irqreturn_t ret = IRQ_HANDLED; - u32 val; - - mutex_lock(&drv->lock); - - val = cpr_read(drv, REG_RBIF_IRQ_STATUS); - if (drv->flags & FLAGS_IGNORE_1ST_IRQ_STATUS) - val = cpr_read(drv, REG_RBIF_IRQ_STATUS); - - dev_dbg(drv->dev, "IRQ_STATUS = %#02x\n", val); - - if (!cpr_ctl_is_enabled(drv)) { - dev_dbg(drv->dev, "CPR is disabled\n"); - ret = IRQ_NONE; - } else if (cpr_ctl_is_busy(drv) && !desc->clamp_timer_interval) { - dev_dbg(drv->dev, "CPR measurement is not ready\n"); - } else if (!cpr_is_allowed(drv)) { - val = cpr_read(drv, REG_RBCPR_CTL); - dev_err_ratelimited(drv->dev, - "Interrupt broken? RBCPR_CTL = %#02x\n", - val); - ret = IRQ_NONE; - } else { - /* - * Following sequence of handling is as per each IRQ's - * priority - */ - if (val & CPR_INT_UP) { - cpr_scale(drv, UP); - } else if (val & CPR_INT_DOWN) { - cpr_scale(drv, DOWN); - } else if (val & CPR_INT_MIN) { - cpr_irq_clr_nack(drv); - } else if (val & CPR_INT_MAX) { - cpr_irq_clr_nack(drv); - } else if (val & CPR_INT_MID) { - /* RBCPR_CTL_SW_AUTO_CONT_ACK_EN is enabled */ - dev_dbg(drv->dev, "IRQ occurred for Mid Flag\n"); - } else { - dev_dbg(drv->dev, - "IRQ occurred for unknown flag (%#08x)\n", val); - } - - /* Save register values for the corner */ - cpr_corner_save(drv, drv->corner); - } - - mutex_unlock(&drv->lock); - - return ret; -} - -static int cpr_enable(struct cpr_drv *drv) -{ - int ret; - - ret = regulator_enable(drv->vdd_apc); - if (ret) - return ret; - - mutex_lock(&drv->lock); - - if (cpr_is_allowed(drv) && drv->corner) { - cpr_irq_clr(drv); - cpr_corner_restore(drv, drv->corner); - cpr_ctl_enable(drv, drv->corner); - } - - mutex_unlock(&drv->lock); - - return 0; -} - -static int cpr_disable(struct cpr_drv *drv) -{ - mutex_lock(&drv->lock); - - if (cpr_is_allowed(drv)) { - cpr_ctl_disable(drv); - cpr_irq_clr(drv); - } - - mutex_unlock(&drv->lock); - - return regulator_disable(drv->vdd_apc); -} - -static int cpr_config(struct cpr_drv *drv) -{ - int i; - u32 val, gcnt; - struct corner *corner; - const struct cpr_desc *desc = drv->desc; - - /* Disable interrupt and CPR */ - cpr_write(drv, REG_RBIF_IRQ_EN(0), 0); - cpr_write(drv, REG_RBCPR_CTL, 0); - - /* Program the default HW ceiling, floor and vlevel */ - val = (RBIF_LIMIT_CEILING_DEFAULT & RBIF_LIMIT_CEILING_MASK) - << RBIF_LIMIT_CEILING_SHIFT; - val |= RBIF_LIMIT_FLOOR_DEFAULT & RBIF_LIMIT_FLOOR_MASK; - cpr_write(drv, REG_RBIF_LIMIT, val); - cpr_write(drv, REG_RBIF_SW_VLEVEL, RBIF_SW_VLEVEL_DEFAULT); - - /* - * Clear the target quotient value and gate count of all - * ring oscillators - */ - for (i = 0; i < CPR_NUM_RING_OSC; i++) - cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0); - - /* Init and save gcnt */ - gcnt = (drv->ref_clk_khz * desc->gcnt_us) / 1000; - gcnt = gcnt & RBCPR_GCNT_TARGET_GCNT_MASK; - gcnt <<= RBCPR_GCNT_TARGET_GCNT_SHIFT; - drv->gcnt = gcnt; - - /* Program the delay count for the timer */ - val = (drv->ref_clk_khz * desc->timer_delay_us) / 1000; - cpr_write(drv, REG_RBCPR_TIMER_INTERVAL, val); - dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val, - desc->timer_delay_us); - - /* Program Consecutive Up & Down */ - val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT; - val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT; - val |= desc->clamp_timer_interval << RBIF_TIMER_ADJ_CLAMP_INT_SHIFT; - cpr_write(drv, REG_RBIF_TIMER_ADJUST, val); - - /* Program the control register */ - val = desc->up_threshold << RBCPR_CTL_UP_THRESHOLD_SHIFT; - val |= desc->down_threshold << RBCPR_CTL_DN_THRESHOLD_SHIFT; - val |= RBCPR_CTL_TIMER_EN | RBCPR_CTL_COUNT_MODE; - val |= RBCPR_CTL_SW_AUTO_CONT_ACK_EN; - cpr_write(drv, REG_RBCPR_CTL, val); - - for (i = 0; i < drv->num_corners; i++) { - corner = &drv->corners[i]; - corner->save_ctl = val; - corner->save_irq = CPR_INT_DEFAULT; - } - - cpr_irq_set(drv, CPR_INT_DEFAULT); - - val = cpr_read(drv, REG_RBCPR_VERSION); - if (val <= RBCPR_VER_2) - drv->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS; - - return 0; -} - -static int cpr_set_performance_state(struct generic_pm_domain *domain, - unsigned int state) -{ - struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); - struct corner *corner, *end; - enum voltage_change_dir dir; - int ret = 0, new_uV; - - mutex_lock(&drv->lock); - - dev_dbg(drv->dev, "%s: setting perf state: %u (prev state: %u)\n", - __func__, state, cpr_get_cur_perf_state(drv)); - - /* - * Determine new corner we're going to. - * Remove one since lowest performance state is 1. - */ - corner = drv->corners + state - 1; - end = &drv->corners[drv->num_corners - 1]; - if (corner > end || corner < drv->corners) { - ret = -EINVAL; - goto unlock; - } - - /* Determine direction */ - if (drv->corner > corner) - dir = DOWN; - else if (drv->corner < corner) - dir = UP; - else - dir = NO_CHANGE; - - if (cpr_is_allowed(drv)) - new_uV = corner->last_uV; - else - new_uV = corner->uV; - - if (cpr_is_allowed(drv)) - cpr_ctl_disable(drv); - - ret = cpr_scale_voltage(drv, corner, new_uV, dir); - if (ret) - goto unlock; - - if (cpr_is_allowed(drv)) { - cpr_irq_clr(drv); - if (drv->corner != corner) - cpr_corner_restore(drv, corner); - cpr_ctl_enable(drv, corner); - } - - drv->corner = corner; - -unlock: - mutex_unlock(&drv->lock); - - return ret; -} - -static int -cpr_populate_ring_osc_idx(struct cpr_drv *drv) -{ - struct fuse_corner *fuse = drv->fuse_corners; - struct fuse_corner *end = fuse + drv->desc->num_fuse_corners; - const struct cpr_fuse *fuses = drv->cpr_fuses; - u32 data; - int ret; - - for (; fuse < end; fuse++, fuses++) { - ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->ring_osc, &data); - if (ret) - return ret; - fuse->ring_osc_idx = data; - } - - return 0; -} - -static int cpr_read_fuse_uV(const struct cpr_desc *desc, - const struct fuse_corner_data *fdata, - const char *init_v_efuse, - int step_volt, - struct cpr_drv *drv) -{ - int step_size_uV, steps, uV; - u32 bits = 0; - int ret; - - ret = nvmem_cell_read_variable_le_u32(drv->dev, init_v_efuse, &bits); - if (ret) - return ret; - - steps = bits & ~BIT(desc->cpr_fuses.init_voltage_width - 1); - /* Not two's complement.. instead highest bit is sign bit */ - if (bits & BIT(desc->cpr_fuses.init_voltage_width - 1)) - steps = -steps; - - step_size_uV = desc->cpr_fuses.init_voltage_step; - - uV = fdata->ref_uV + steps * step_size_uV; - return DIV_ROUND_UP(uV, step_volt) * step_volt; -} - -static int cpr_fuse_corner_init(struct cpr_drv *drv) -{ - const struct cpr_desc *desc = drv->desc; - const struct cpr_fuse *fuses = drv->cpr_fuses; - const struct acc_desc *acc_desc = drv->acc_desc; - int i; - unsigned int step_volt; - struct fuse_corner_data *fdata; - struct fuse_corner *fuse, *end; - int uV; - const struct reg_sequence *accs; - int ret; - - accs = acc_desc->settings; - - step_volt = regulator_get_linear_step(drv->vdd_apc); - if (!step_volt) - return -EINVAL; - - /* Populate fuse_corner members */ - fuse = drv->fuse_corners; - end = &fuse[desc->num_fuse_corners - 1]; - fdata = desc->cpr_fuses.fuse_corner_data; - - for (i = 0; fuse <= end; fuse++, fuses++, i++, fdata++) { - /* - * Update SoC voltages: platforms might choose a different - * regulators than the one used to characterize the algorithms - * (ie, init_voltage_step). - */ - fdata->min_uV = roundup(fdata->min_uV, step_volt); - fdata->max_uV = roundup(fdata->max_uV, step_volt); - - /* Populate uV */ - uV = cpr_read_fuse_uV(desc, fdata, fuses->init_voltage, - step_volt, drv); - if (uV < 0) - return uV; - - fuse->min_uV = fdata->min_uV; - fuse->max_uV = fdata->max_uV; - fuse->uV = clamp(uV, fuse->min_uV, fuse->max_uV); - - if (fuse == end) { - /* - * Allow the highest fuse corner's PVS voltage to - * define the ceiling voltage for that corner in order - * to support SoC's in which variable ceiling values - * are required. - */ - end->max_uV = max(end->max_uV, end->uV); - } - - /* Populate target quotient by scaling */ - ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->quotient, &fuse->quot); - if (ret) - return ret; - - fuse->quot *= fdata->quot_scale; - fuse->quot += fdata->quot_offset; - fuse->quot += fdata->quot_adjust; - fuse->step_quot = desc->step_quot[fuse->ring_osc_idx]; - - /* Populate acc settings */ - fuse->accs = accs; - fuse->num_accs = acc_desc->num_regs_per_fuse; - accs += acc_desc->num_regs_per_fuse; - } - - /* - * Restrict all fuse corner PVS voltages based upon per corner - * ceiling and floor voltages. - */ - for (fuse = drv->fuse_corners, i = 0; fuse <= end; fuse++, i++) { - if (fuse->uV > fuse->max_uV) - fuse->uV = fuse->max_uV; - else if (fuse->uV < fuse->min_uV) - fuse->uV = fuse->min_uV; - - ret = regulator_is_supported_voltage(drv->vdd_apc, - fuse->min_uV, - fuse->min_uV); - if (!ret) { - dev_err(drv->dev, - "min uV: %d (fuse corner: %d) not supported by regulator\n", - fuse->min_uV, i); - return -EINVAL; - } - - ret = regulator_is_supported_voltage(drv->vdd_apc, - fuse->max_uV, - fuse->max_uV); - if (!ret) { - dev_err(drv->dev, - "max uV: %d (fuse corner: %d) not supported by regulator\n", - fuse->max_uV, i); - return -EINVAL; - } - - dev_dbg(drv->dev, - "fuse corner %d: [%d %d %d] RO%hhu quot %d squot %d\n", - i, fuse->min_uV, fuse->uV, fuse->max_uV, - fuse->ring_osc_idx, fuse->quot, fuse->step_quot); - } - - return 0; -} - -static int cpr_calculate_scaling(const char *quot_offset, - struct cpr_drv *drv, - const struct fuse_corner_data *fdata, - const struct corner *corner) -{ - u32 quot_diff = 0; - unsigned long freq_diff; - int scaling; - const struct fuse_corner *fuse, *prev_fuse; - int ret; - - fuse = corner->fuse_corner; - prev_fuse = fuse - 1; - - if (quot_offset) { - ret = nvmem_cell_read_variable_le_u32(drv->dev, quot_offset, "_diff); - if (ret) - return ret; - - quot_diff *= fdata->quot_offset_scale; - quot_diff += fdata->quot_offset_adjust; - } else { - quot_diff = fuse->quot - prev_fuse->quot; - } - - freq_diff = fuse->max_freq - prev_fuse->max_freq; - freq_diff /= 1000000; /* Convert to MHz */ - scaling = 1000 * quot_diff / freq_diff; - return min(scaling, fdata->max_quot_scale); -} - -static int cpr_interpolate(const struct corner *corner, int step_volt, - const struct fuse_corner_data *fdata) -{ - unsigned long f_high, f_low, f_diff; - int uV_high, uV_low, uV; - u64 temp, temp_limit; - const struct fuse_corner *fuse, *prev_fuse; - - fuse = corner->fuse_corner; - prev_fuse = fuse - 1; - - f_high = fuse->max_freq; - f_low = prev_fuse->max_freq; - uV_high = fuse->uV; - uV_low = prev_fuse->uV; - f_diff = fuse->max_freq - corner->freq; - - /* - * Don't interpolate in the wrong direction. This could happen - * if the adjusted fuse voltage overlaps with the previous fuse's - * adjusted voltage. - */ - if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq) - return corner->uV; - - temp = f_diff * (uV_high - uV_low); - temp = div64_ul(temp, f_high - f_low); - - /* - * max_volt_scale has units of uV/MHz while freq values - * have units of Hz. Divide by 1000000 to convert to. - */ - temp_limit = f_diff * fdata->max_volt_scale; - do_div(temp_limit, 1000000); - - uV = uV_high - min(temp, temp_limit); - return roundup(uV, step_volt); -} - -static unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp) -{ - struct device_node *np; - unsigned int fuse_corner = 0; - - np = dev_pm_opp_get_of_node(opp); - if (of_property_read_u32(np, "qcom,opp-fuse-level", &fuse_corner)) - pr_err("%s: missing 'qcom,opp-fuse-level' property\n", - __func__); - - of_node_put(np); - - return fuse_corner; -} - -static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, - struct device *cpu_dev) -{ - u64 rate = 0; - struct device_node *ref_np; - struct device_node *desc_np; - struct device_node *child_np = NULL; - struct device_node *child_req_np = NULL; - - desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); - if (!desc_np) - return 0; - - ref_np = dev_pm_opp_get_of_node(ref); - if (!ref_np) - goto out_ref; - - do { - of_node_put(child_req_np); - child_np = of_get_next_available_child(desc_np, child_np); - child_req_np = of_parse_phandle(child_np, "required-opps", 0); - } while (child_np && child_req_np != ref_np); - - if (child_np && child_req_np == ref_np) - of_property_read_u64(child_np, "opp-hz", &rate); - - of_node_put(child_req_np); - of_node_put(child_np); - of_node_put(ref_np); -out_ref: - of_node_put(desc_np); - - return (unsigned long) rate; -} - -static int cpr_corner_init(struct cpr_drv *drv) -{ - const struct cpr_desc *desc = drv->desc; - const struct cpr_fuse *fuses = drv->cpr_fuses; - int i, level, scaling = 0; - unsigned int fnum, fc; - const char *quot_offset; - struct fuse_corner *fuse, *prev_fuse; - struct corner *corner, *end; - struct corner_data *cdata; - const struct fuse_corner_data *fdata; - bool apply_scaling; - unsigned long freq_diff, freq_diff_mhz; - unsigned long freq; - int step_volt = regulator_get_linear_step(drv->vdd_apc); - struct dev_pm_opp *opp; - - if (!step_volt) - return -EINVAL; - - corner = drv->corners; - end = &corner[drv->num_corners - 1]; - - cdata = devm_kcalloc(drv->dev, drv->num_corners, - sizeof(struct corner_data), - GFP_KERNEL); - if (!cdata) - return -ENOMEM; - - /* - * Store maximum frequency for each fuse corner based on the frequency - * plan - */ - for (level = 1; level <= drv->num_corners; level++) { - opp = dev_pm_opp_find_level_exact(&drv->pd.dev, level); - if (IS_ERR(opp)) - return -EINVAL; - fc = cpr_get_fuse_corner(opp); - if (!fc) { - dev_pm_opp_put(opp); - return -EINVAL; - } - fnum = fc - 1; - freq = cpr_get_opp_hz_for_req(opp, drv->attached_cpu_dev); - if (!freq) { - dev_pm_opp_put(opp); - return -EINVAL; - } - cdata[level - 1].fuse_corner = fnum; - cdata[level - 1].freq = freq; - - fuse = &drv->fuse_corners[fnum]; - dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n", - freq, dev_pm_opp_get_level(opp) - 1, fnum); - if (freq > fuse->max_freq) - fuse->max_freq = freq; - dev_pm_opp_put(opp); - } - - /* - * Get the quotient adjustment scaling factor, according to: - * - * scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1)) - * / (freq(corner_N) - freq(corner_N-1)), max_factor) - * - * QUOT(corner_N): quotient read from fuse for fuse corner N - * QUOT(corner_N-1): quotient read from fuse for fuse corner (N - 1) - * freq(corner_N): max frequency in MHz supported by fuse corner N - * freq(corner_N-1): max frequency in MHz supported by fuse corner - * (N - 1) - * - * Then walk through the corners mapped to each fuse corner - * and calculate the quotient adjustment for each one using the - * following formula: - * - * quot_adjust = (freq_max - freq_corner) * scaling / 1000 - * - * freq_max: max frequency in MHz supported by the fuse corner - * freq_corner: frequency in MHz corresponding to the corner - * scaling: calculated from above equation - * - * - * + + - * | v | - * q | f c o | f c - * u | c l | c - * o | f t | f - * t | c a | c - * | c f g | c f - * | e | - * +--------------- +---------------- - * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 - * corner corner - * - * c = corner - * f = fuse corner - * - */ - for (apply_scaling = false, i = 0; corner <= end; corner++, i++) { - fnum = cdata[i].fuse_corner; - fdata = &desc->cpr_fuses.fuse_corner_data[fnum]; - quot_offset = fuses[fnum].quotient_offset; - fuse = &drv->fuse_corners[fnum]; - if (fnum) - prev_fuse = &drv->fuse_corners[fnum - 1]; - else - prev_fuse = NULL; - - corner->fuse_corner = fuse; - corner->freq = cdata[i].freq; - corner->uV = fuse->uV; - - if (prev_fuse && cdata[i - 1].freq == prev_fuse->max_freq) { - scaling = cpr_calculate_scaling(quot_offset, drv, - fdata, corner); - if (scaling < 0) - return scaling; - - apply_scaling = true; - } else if (corner->freq == fuse->max_freq) { - /* This is a fuse corner; don't scale anything */ - apply_scaling = false; - } - - if (apply_scaling) { - freq_diff = fuse->max_freq - corner->freq; - freq_diff_mhz = freq_diff / 1000000; - corner->quot_adjust = scaling * freq_diff_mhz / 1000; - - corner->uV = cpr_interpolate(corner, step_volt, fdata); - } - - corner->max_uV = fuse->max_uV; - corner->min_uV = fuse->min_uV; - corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV); - corner->last_uV = corner->uV; - - /* Reduce the ceiling voltage if needed */ - if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV) - corner->max_uV = corner->uV; - else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV) - corner->max_uV = max(corner->min_uV, fuse->uV); - - dev_dbg(drv->dev, "corner %d: [%d %d %d] quot %d\n", i, - corner->min_uV, corner->uV, corner->max_uV, - fuse->quot - corner->quot_adjust); - } - - return 0; -} - -static const struct cpr_fuse *cpr_get_fuses(struct cpr_drv *drv) -{ - const struct cpr_desc *desc = drv->desc; - struct cpr_fuse *fuses; - int i; - - fuses = devm_kcalloc(drv->dev, desc->num_fuse_corners, - sizeof(struct cpr_fuse), - GFP_KERNEL); - if (!fuses) - return ERR_PTR(-ENOMEM); - - for (i = 0; i < desc->num_fuse_corners; i++) { - char tbuf[32]; - - snprintf(tbuf, 32, "cpr_ring_osc%d", i + 1); - fuses[i].ring_osc = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); - if (!fuses[i].ring_osc) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_init_voltage%d", i + 1); - fuses[i].init_voltage = devm_kstrdup(drv->dev, tbuf, - GFP_KERNEL); - if (!fuses[i].init_voltage) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_quotient%d", i + 1); - fuses[i].quotient = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); - if (!fuses[i].quotient) - return ERR_PTR(-ENOMEM); - - snprintf(tbuf, 32, "cpr_quotient_offset%d", i + 1); - fuses[i].quotient_offset = devm_kstrdup(drv->dev, tbuf, - GFP_KERNEL); - if (!fuses[i].quotient_offset) - return ERR_PTR(-ENOMEM); - } - - return fuses; -} - -static void cpr_set_loop_allowed(struct cpr_drv *drv) -{ - drv->loop_disabled = false; -} - -static int cpr_init_parameters(struct cpr_drv *drv) -{ - const struct cpr_desc *desc = drv->desc; - struct clk *clk; - - clk = clk_get(drv->dev, "ref"); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - drv->ref_clk_khz = clk_get_rate(clk) / 1000; - clk_put(clk); - - if (desc->timer_cons_up > RBIF_TIMER_ADJ_CONS_UP_MASK || - desc->timer_cons_down > RBIF_TIMER_ADJ_CONS_DOWN_MASK || - desc->up_threshold > RBCPR_CTL_UP_THRESHOLD_MASK || - desc->down_threshold > RBCPR_CTL_DN_THRESHOLD_MASK || - desc->idle_clocks > RBCPR_STEP_QUOT_IDLE_CLK_MASK || - desc->clamp_timer_interval > RBIF_TIMER_ADJ_CLAMP_INT_MASK) - return -EINVAL; - - dev_dbg(drv->dev, "up threshold = %u, down threshold = %u\n", - desc->up_threshold, desc->down_threshold); - - return 0; -} - -static int cpr_find_initial_corner(struct cpr_drv *drv) -{ - unsigned long rate; - const struct corner *end; - struct corner *iter; - unsigned int i = 0; - - if (!drv->cpu_clk) { - dev_err(drv->dev, "cannot get rate from NULL clk\n"); - return -EINVAL; - } - - end = &drv->corners[drv->num_corners - 1]; - rate = clk_get_rate(drv->cpu_clk); - - /* - * Some bootloaders set a CPU clock frequency that is not defined - * in the OPP table. When running at an unlisted frequency, - * cpufreq_online() will change to the OPP which has the lowest - * frequency, at or above the unlisted frequency. - * Since cpufreq_online() always "rounds up" in the case of an - * unlisted frequency, this function always "rounds down" in case - * of an unlisted frequency. That way, when cpufreq_online() - * triggers the first ever call to cpr_set_performance_state(), - * it will correctly determine the direction as UP. - */ - for (iter = drv->corners; iter <= end; iter++) { - if (iter->freq > rate) - break; - i++; - if (iter->freq == rate) { - drv->corner = iter; - break; - } - if (iter->freq < rate) - drv->corner = iter; - } - - if (!drv->corner) { - dev_err(drv->dev, "boot up corner not found\n"); - return -EINVAL; - } - - dev_dbg(drv->dev, "boot up perf state: %u\n", i); - - return 0; -} - -static const struct cpr_desc qcs404_cpr_desc = { - .num_fuse_corners = 3, - .min_diff_quot = CPR_FUSE_MIN_QUOT_DIFF, - .step_quot = (int []){ 25, 25, 25, }, - .timer_delay_us = 5000, - .timer_cons_up = 0, - .timer_cons_down = 2, - .up_threshold = 1, - .down_threshold = 3, - .idle_clocks = 15, - .gcnt_us = 1, - .vdd_apc_step_up_limit = 1, - .vdd_apc_step_down_limit = 1, - .cpr_fuses = { - .init_voltage_step = 8000, - .init_voltage_width = 6, - .fuse_corner_data = (struct fuse_corner_data[]){ - /* fuse corner 0 */ - { - .ref_uV = 1224000, - .max_uV = 1224000, - .min_uV = 1048000, - .max_volt_scale = 0, - .max_quot_scale = 0, - .quot_offset = 0, - .quot_scale = 1, - .quot_adjust = 0, - .quot_offset_scale = 5, - .quot_offset_adjust = 0, - }, - /* fuse corner 1 */ - { - .ref_uV = 1288000, - .max_uV = 1288000, - .min_uV = 1048000, - .max_volt_scale = 2000, - .max_quot_scale = 1400, - .quot_offset = 0, - .quot_scale = 1, - .quot_adjust = -20, - .quot_offset_scale = 5, - .quot_offset_adjust = 0, - }, - /* fuse corner 2 */ - { - .ref_uV = 1352000, - .max_uV = 1384000, - .min_uV = 1088000, - .max_volt_scale = 2000, - .max_quot_scale = 1400, - .quot_offset = 0, - .quot_scale = 1, - .quot_adjust = 0, - .quot_offset_scale = 5, - .quot_offset_adjust = 0, - }, - }, - }, -}; - -static const struct acc_desc qcs404_acc_desc = { - .settings = (struct reg_sequence[]){ - { 0xb120, 0x1041040 }, - { 0xb124, 0x41 }, - { 0xb120, 0x0 }, - { 0xb124, 0x0 }, - { 0xb120, 0x0 }, - { 0xb124, 0x0 }, - }, - .config = (struct reg_sequence[]){ - { 0xb138, 0xff }, - { 0xb130, 0x5555 }, - }, - .num_regs_per_fuse = 2, -}; - -static const struct cpr_acc_desc qcs404_cpr_acc_desc = { - .cpr_desc = &qcs404_cpr_desc, - .acc_desc = &qcs404_acc_desc, -}; - -static unsigned int cpr_get_performance_state(struct generic_pm_domain *genpd, - struct dev_pm_opp *opp) -{ - return dev_pm_opp_get_level(opp); -} - -static int cpr_power_off(struct generic_pm_domain *domain) -{ - struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); - - return cpr_disable(drv); -} - -static int cpr_power_on(struct generic_pm_domain *domain) -{ - struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); - - return cpr_enable(drv); -} - -static int cpr_pd_attach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); - const struct acc_desc *acc_desc = drv->acc_desc; - int ret = 0; - - mutex_lock(&drv->lock); - - dev_dbg(drv->dev, "attach callback for: %s\n", dev_name(dev)); - - /* - * This driver only supports scaling voltage for a CPU cluster - * where all CPUs in the cluster share a single regulator. - * Therefore, save the struct device pointer only for the first - * CPU device that gets attached. There is no need to do any - * additional initialization when further CPUs get attached. - */ - if (drv->attached_cpu_dev) - goto unlock; - - /* - * cpr_scale_voltage() requires the direction (if we are changing - * to a higher or lower OPP). The first time - * cpr_set_performance_state() is called, there is no previous - * performance state defined. Therefore, we call - * cpr_find_initial_corner() that gets the CPU clock frequency - * set by the bootloader, so that we can determine the direction - * the first time cpr_set_performance_state() is called. - */ - drv->cpu_clk = devm_clk_get(dev, NULL); - if (IS_ERR(drv->cpu_clk)) { - ret = PTR_ERR(drv->cpu_clk); - if (ret != -EPROBE_DEFER) - dev_err(drv->dev, "could not get cpu clk: %d\n", ret); - goto unlock; - } - drv->attached_cpu_dev = dev; - - dev_dbg(drv->dev, "using cpu clk from: %s\n", - dev_name(drv->attached_cpu_dev)); - - /* - * Everything related to (virtual) corners has to be initialized - * here, when attaching to the power domain, since we need to know - * the maximum frequency for each fuse corner, and this is only - * available after the cpufreq driver has attached to us. - * The reason for this is that we need to know the highest - * frequency associated with each fuse corner. - */ - ret = dev_pm_opp_get_opp_count(&drv->pd.dev); - if (ret < 0) { - dev_err(drv->dev, "could not get OPP count\n"); - goto unlock; - } - drv->num_corners = ret; - - if (drv->num_corners < 2) { - dev_err(drv->dev, "need at least 2 OPPs to use CPR\n"); - ret = -EINVAL; - goto unlock; - } - - drv->corners = devm_kcalloc(drv->dev, drv->num_corners, - sizeof(*drv->corners), - GFP_KERNEL); - if (!drv->corners) { - ret = -ENOMEM; - goto unlock; - } - - ret = cpr_corner_init(drv); - if (ret) - goto unlock; - - cpr_set_loop_allowed(drv); - - ret = cpr_init_parameters(drv); - if (ret) - goto unlock; - - /* Configure CPR HW but keep it disabled */ - ret = cpr_config(drv); - if (ret) - goto unlock; - - ret = cpr_find_initial_corner(drv); - if (ret) - goto unlock; - - if (acc_desc->config) - regmap_multi_reg_write(drv->tcsr, acc_desc->config, - acc_desc->num_regs_per_fuse); - - /* Enable ACC if required */ - if (acc_desc->enable_mask) - regmap_update_bits(drv->tcsr, acc_desc->enable_reg, - acc_desc->enable_mask, - acc_desc->enable_mask); - - dev_info(drv->dev, "driver initialized with %u OPPs\n", - drv->num_corners); - -unlock: - mutex_unlock(&drv->lock); - - return ret; -} - -static int cpr_debug_info_show(struct seq_file *s, void *unused) -{ - u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps; - u32 step_dn, step_up, error, error_lt0, busy; - struct cpr_drv *drv = s->private; - struct fuse_corner *fuse_corner; - struct corner *corner; - - corner = drv->corner; - fuse_corner = corner->fuse_corner; - - seq_printf(s, "corner, current_volt = %d uV\n", - corner->last_uV); - - ro_sel = fuse_corner->ring_osc_idx; - gcnt = cpr_read(drv, REG_RBCPR_GCNT_TARGET(ro_sel)); - seq_printf(s, "rbcpr_gcnt_target (%u) = %#02X\n", ro_sel, gcnt); - - ctl = cpr_read(drv, REG_RBCPR_CTL); - seq_printf(s, "rbcpr_ctl = %#02X\n", ctl); - - irq_status = cpr_read(drv, REG_RBIF_IRQ_STATUS); - seq_printf(s, "rbcpr_irq_status = %#02X\n", irq_status); - - reg = cpr_read(drv, REG_RBCPR_RESULT_0); - seq_printf(s, "rbcpr_result_0 = %#02X\n", reg); - - step_dn = reg & 0x01; - step_up = (reg >> RBCPR_RESULT0_STEP_UP_SHIFT) & 0x01; - seq_printf(s, " [step_dn = %u", step_dn); - - seq_printf(s, ", step_up = %u", step_up); - - error_steps = (reg >> RBCPR_RESULT0_ERROR_STEPS_SHIFT) - & RBCPR_RESULT0_ERROR_STEPS_MASK; - seq_printf(s, ", error_steps = %u", error_steps); - - error = (reg >> RBCPR_RESULT0_ERROR_SHIFT) & RBCPR_RESULT0_ERROR_MASK; - seq_printf(s, ", error = %u", error); - - error_lt0 = (reg >> RBCPR_RESULT0_ERROR_LT0_SHIFT) & 0x01; - seq_printf(s, ", error_lt_0 = %u", error_lt0); - - busy = (reg >> RBCPR_RESULT0_BUSY_SHIFT) & 0x01; - seq_printf(s, ", busy = %u]\n", busy); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(cpr_debug_info); - -static void cpr_debugfs_init(struct cpr_drv *drv) -{ - drv->debugfs = debugfs_create_dir("qcom_cpr", NULL); - - debugfs_create_file("debug_info", 0444, drv->debugfs, - drv, &cpr_debug_info_fops); -} - -static int cpr_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct cpr_drv *drv; - int irq, ret; - const struct cpr_acc_desc *data; - struct device_node *np; - u32 cpr_rev = FUSE_REVISION_UNKNOWN; - - data = of_device_get_match_data(dev); - if (!data || !data->cpr_desc || !data->acc_desc) - return -EINVAL; - - drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); - if (!drv) - return -ENOMEM; - drv->dev = dev; - drv->desc = data->cpr_desc; - drv->acc_desc = data->acc_desc; - - drv->fuse_corners = devm_kcalloc(dev, drv->desc->num_fuse_corners, - sizeof(*drv->fuse_corners), - GFP_KERNEL); - if (!drv->fuse_corners) - return -ENOMEM; - - np = of_parse_phandle(dev->of_node, "acc-syscon", 0); - if (!np) - return -ENODEV; - - drv->tcsr = syscon_node_to_regmap(np); - of_node_put(np); - if (IS_ERR(drv->tcsr)) - return PTR_ERR(drv->tcsr); - - drv->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(drv->base)) - return PTR_ERR(drv->base); - - irq = platform_get_irq(pdev, 0); - if (irq < 0) - return -EINVAL; - - drv->vdd_apc = devm_regulator_get(dev, "vdd-apc"); - if (IS_ERR(drv->vdd_apc)) - return PTR_ERR(drv->vdd_apc); - - /* - * Initialize fuse corners, since it simply depends - * on data in efuses. - * Everything related to (virtual) corners has to be - * initialized after attaching to the power domain, - * since it depends on the CPU's OPP table. - */ - ret = nvmem_cell_read_variable_le_u32(dev, "cpr_fuse_revision", &cpr_rev); - if (ret) - return ret; - - drv->cpr_fuses = cpr_get_fuses(drv); - if (IS_ERR(drv->cpr_fuses)) - return PTR_ERR(drv->cpr_fuses); - - ret = cpr_populate_ring_osc_idx(drv); - if (ret) - return ret; - - ret = cpr_fuse_corner_init(drv); - if (ret) - return ret; - - mutex_init(&drv->lock); - - ret = devm_request_threaded_irq(dev, irq, NULL, - cpr_irq_handler, - IRQF_ONESHOT | IRQF_TRIGGER_RISING, - "cpr", drv); - if (ret) - return ret; - - drv->pd.name = devm_kstrdup_const(dev, dev->of_node->full_name, - GFP_KERNEL); - if (!drv->pd.name) - return -EINVAL; - - drv->pd.power_off = cpr_power_off; - drv->pd.power_on = cpr_power_on; - drv->pd.set_performance_state = cpr_set_performance_state; - drv->pd.opp_to_performance_state = cpr_get_performance_state; - drv->pd.attach_dev = cpr_pd_attach_dev; - - ret = pm_genpd_init(&drv->pd, NULL, true); - if (ret) - return ret; - - ret = of_genpd_add_provider_simple(dev->of_node, &drv->pd); - if (ret) - goto err_remove_genpd; - - platform_set_drvdata(pdev, drv); - cpr_debugfs_init(drv); - - return 0; - -err_remove_genpd: - pm_genpd_remove(&drv->pd); - return ret; -} - -static int cpr_remove(struct platform_device *pdev) -{ - struct cpr_drv *drv = platform_get_drvdata(pdev); - - if (cpr_is_allowed(drv)) { - cpr_ctl_disable(drv); - cpr_irq_set(drv, 0); - } - - of_genpd_del_provider(pdev->dev.of_node); - pm_genpd_remove(&drv->pd); - - debugfs_remove_recursive(drv->debugfs); - - return 0; -} - -static const struct of_device_id cpr_match_table[] = { - { .compatible = "qcom,qcs404-cpr", .data = &qcs404_cpr_acc_desc }, - { } -}; -MODULE_DEVICE_TABLE(of, cpr_match_table); - -static struct platform_driver cpr_driver = { - .probe = cpr_probe, - .remove = cpr_remove, - .driver = { - .name = "qcom-cpr", - .of_match_table = cpr_match_table, - }, -}; -module_platform_driver(cpr_driver); - -MODULE_DESCRIPTION("Core Power Reduction (CPR) driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/genpd/qcom/rpmhpd.c b/drivers/genpd/qcom/rpmhpd.c deleted file mode 100644 index a87e336d5e33..000000000000 --- a/drivers/genpd/qcom/rpmhpd.c +++ /dev/null @@ -1,886 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd) - -#define RPMH_ARC_MAX_LEVELS 16 - -/** - * struct rpmhpd - top level RPMh power domain resource data structure - * @dev: rpmh power domain controller device - * @pd: generic_pm_domain corresponding to the power domain - * @parent: generic_pm_domain corresponding to the parent's power domain - * @peer: A peer power domain in case Active only Voting is - * supported - * @active_only: True if it represents an Active only peer - * @corner: current corner - * @active_corner: current active corner - * @enable_corner: lowest non-zero corner - * @level: An array of level (vlvl) to corner (hlvl) mappings - * derived from cmd-db - * @level_count: Number of levels supported by the power domain. max - * being 16 (0 - 15) - * @enabled: true if the power domain is enabled - * @res_name: Resource name used for cmd-db lookup - * @addr: Resource address as looped up using resource name from - * cmd-db - * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource - */ -struct rpmhpd { - struct device *dev; - struct generic_pm_domain pd; - struct generic_pm_domain *parent; - struct rpmhpd *peer; - const bool active_only; - unsigned int corner; - unsigned int active_corner; - unsigned int enable_corner; - u32 level[RPMH_ARC_MAX_LEVELS]; - size_t level_count; - bool enabled; - const char *res_name; - u32 addr; - bool state_synced; -}; - -struct rpmhpd_desc { - struct rpmhpd **rpmhpds; - size_t num_pds; -}; - -static DEFINE_MUTEX(rpmhpd_lock); - -/* RPMH powerdomains */ - -static struct rpmhpd cx_ao; -static struct rpmhpd mx; -static struct rpmhpd mx_ao; -static struct rpmhpd cx = { - .pd = { .name = "cx", }, - .peer = &cx_ao, - .res_name = "cx.lvl", -}; - -static struct rpmhpd cx_ao = { - .pd = { .name = "cx_ao", }, - .active_only = true, - .peer = &cx, - .res_name = "cx.lvl", -}; - -static struct rpmhpd cx_ao_w_mx_parent; -static struct rpmhpd cx_w_mx_parent = { - .pd = { .name = "cx", }, - .peer = &cx_ao_w_mx_parent, - .parent = &mx.pd, - .res_name = "cx.lvl", -}; - -static struct rpmhpd cx_ao_w_mx_parent = { - .pd = { .name = "cx_ao", }, - .active_only = true, - .peer = &cx_w_mx_parent, - .parent = &mx_ao.pd, - .res_name = "cx.lvl", -}; - -static struct rpmhpd ebi = { - .pd = { .name = "ebi", }, - .res_name = "ebi.lvl", -}; - -static struct rpmhpd gfx = { - .pd = { .name = "gfx", }, - .res_name = "gfx.lvl", -}; - -static struct rpmhpd lcx = { - .pd = { .name = "lcx", }, - .res_name = "lcx.lvl", -}; - -static struct rpmhpd lmx = { - .pd = { .name = "lmx", }, - .res_name = "lmx.lvl", -}; - -static struct rpmhpd mmcx_ao; -static struct rpmhpd mmcx = { - .pd = { .name = "mmcx", }, - .peer = &mmcx_ao, - .res_name = "mmcx.lvl", -}; - -static struct rpmhpd mmcx_ao = { - .pd = { .name = "mmcx_ao", }, - .active_only = true, - .peer = &mmcx, - .res_name = "mmcx.lvl", -}; - -static struct rpmhpd mmcx_ao_w_cx_parent; -static struct rpmhpd mmcx_w_cx_parent = { - .pd = { .name = "mmcx", }, - .peer = &mmcx_ao_w_cx_parent, - .parent = &cx.pd, - .res_name = "mmcx.lvl", -}; - -static struct rpmhpd mmcx_ao_w_cx_parent = { - .pd = { .name = "mmcx_ao", }, - .active_only = true, - .peer = &mmcx_w_cx_parent, - .parent = &cx_ao.pd, - .res_name = "mmcx.lvl", -}; - -static struct rpmhpd mss = { - .pd = { .name = "mss", }, - .res_name = "mss.lvl", -}; - -static struct rpmhpd mx_ao; -static struct rpmhpd mx = { - .pd = { .name = "mx", }, - .peer = &mx_ao, - .res_name = "mx.lvl", -}; - -static struct rpmhpd mx_ao = { - .pd = { .name = "mx_ao", }, - .active_only = true, - .peer = &mx, - .res_name = "mx.lvl", -}; - -static struct rpmhpd mxc_ao; -static struct rpmhpd mxc = { - .pd = { .name = "mxc", }, - .peer = &mxc_ao, - .res_name = "mxc.lvl", -}; - -static struct rpmhpd mxc_ao = { - .pd = { .name = "mxc_ao", }, - .active_only = true, - .peer = &mxc, - .res_name = "mxc.lvl", -}; - -static struct rpmhpd nsp = { - .pd = { .name = "nsp", }, - .res_name = "nsp.lvl", -}; - -static struct rpmhpd nsp0 = { - .pd = { .name = "nsp0", }, - .res_name = "nsp0.lvl", -}; - -static struct rpmhpd nsp1 = { - .pd = { .name = "nsp1", }, - .res_name = "nsp1.lvl", -}; - -static struct rpmhpd qphy = { - .pd = { .name = "qphy", }, - .res_name = "qphy.lvl", -}; - -/* SA8540P RPMH powerdomains */ -static struct rpmhpd *sa8540p_rpmhpds[] = { - [SC8280XP_CX] = &cx, - [SC8280XP_CX_AO] = &cx_ao, - [SC8280XP_EBI] = &ebi, - [SC8280XP_GFX] = &gfx, - [SC8280XP_LCX] = &lcx, - [SC8280XP_LMX] = &lmx, - [SC8280XP_MMCX] = &mmcx, - [SC8280XP_MMCX_AO] = &mmcx_ao, - [SC8280XP_MX] = &mx, - [SC8280XP_MX_AO] = &mx_ao, - [SC8280XP_NSP] = &nsp, -}; - -static const struct rpmhpd_desc sa8540p_desc = { - .rpmhpds = sa8540p_rpmhpds, - .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), -}; - -/* SA8775P RPMH power domains */ -static struct rpmhpd *sa8775p_rpmhpds[] = { - [SA8775P_CX] = &cx, - [SA8775P_CX_AO] = &cx_ao, - [SA8775P_EBI] = &ebi, - [SA8775P_GFX] = &gfx, - [SA8775P_LCX] = &lcx, - [SA8775P_LMX] = &lmx, - [SA8775P_MMCX] = &mmcx, - [SA8775P_MMCX_AO] = &mmcx_ao, - [SA8775P_MXC] = &mxc, - [SA8775P_MXC_AO] = &mxc_ao, - [SA8775P_MX] = &mx, - [SA8775P_MX_AO] = &mx_ao, - [SA8775P_NSP0] = &nsp0, - [SA8775P_NSP1] = &nsp1, -}; - -static const struct rpmhpd_desc sa8775p_desc = { - .rpmhpds = sa8775p_rpmhpds, - .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), -}; - -/* SDM670 RPMH powerdomains */ -static struct rpmhpd *sdm670_rpmhpds[] = { - [SDM670_CX] = &cx_w_mx_parent, - [SDM670_CX_AO] = &cx_ao_w_mx_parent, - [SDM670_GFX] = &gfx, - [SDM670_LCX] = &lcx, - [SDM670_LMX] = &lmx, - [SDM670_MSS] = &mss, - [SDM670_MX] = &mx, - [SDM670_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sdm670_desc = { - .rpmhpds = sdm670_rpmhpds, - .num_pds = ARRAY_SIZE(sdm670_rpmhpds), -}; - -/* SDM845 RPMH powerdomains */ -static struct rpmhpd *sdm845_rpmhpds[] = { - [SDM845_CX] = &cx_w_mx_parent, - [SDM845_CX_AO] = &cx_ao_w_mx_parent, - [SDM845_EBI] = &ebi, - [SDM845_GFX] = &gfx, - [SDM845_LCX] = &lcx, - [SDM845_LMX] = &lmx, - [SDM845_MSS] = &mss, - [SDM845_MX] = &mx, - [SDM845_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sdm845_desc = { - .rpmhpds = sdm845_rpmhpds, - .num_pds = ARRAY_SIZE(sdm845_rpmhpds), -}; - -/* SDX55 RPMH powerdomains */ -static struct rpmhpd *sdx55_rpmhpds[] = { - [SDX55_CX] = &cx_w_mx_parent, - [SDX55_MSS] = &mss, - [SDX55_MX] = &mx, -}; - -static const struct rpmhpd_desc sdx55_desc = { - .rpmhpds = sdx55_rpmhpds, - .num_pds = ARRAY_SIZE(sdx55_rpmhpds), -}; - -/* SDX65 RPMH powerdomains */ -static struct rpmhpd *sdx65_rpmhpds[] = { - [SDX65_CX] = &cx_w_mx_parent, - [SDX65_CX_AO] = &cx_ao_w_mx_parent, - [SDX65_MSS] = &mss, - [SDX65_MX] = &mx, - [SDX65_MX_AO] = &mx_ao, - [SDX65_MXC] = &mxc, -}; - -static const struct rpmhpd_desc sdx65_desc = { - .rpmhpds = sdx65_rpmhpds, - .num_pds = ARRAY_SIZE(sdx65_rpmhpds), -}; - -/* SDX75 RPMH powerdomains */ -static struct rpmhpd *sdx75_rpmhpds[] = { - [RPMHPD_CX] = &cx, - [RPMHPD_CX_AO] = &cx_ao, - [RPMHPD_MSS] = &mss, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MXC] = &mxc, -}; - -static const struct rpmhpd_desc sdx75_desc = { - .rpmhpds = sdx75_rpmhpds, - .num_pds = ARRAY_SIZE(sdx75_rpmhpds), -}; - -/* SM6350 RPMH powerdomains */ -static struct rpmhpd *sm6350_rpmhpds[] = { - [SM6350_CX] = &cx_w_mx_parent, - [SM6350_GFX] = &gfx, - [SM6350_LCX] = &lcx, - [SM6350_LMX] = &lmx, - [SM6350_MSS] = &mss, - [SM6350_MX] = &mx, -}; - -static const struct rpmhpd_desc sm6350_desc = { - .rpmhpds = sm6350_rpmhpds, - .num_pds = ARRAY_SIZE(sm6350_rpmhpds), -}; - -/* SM8150 RPMH powerdomains */ -static struct rpmhpd *sm8150_rpmhpds[] = { - [SM8150_CX] = &cx_w_mx_parent, - [SM8150_CX_AO] = &cx_ao_w_mx_parent, - [SM8150_EBI] = &ebi, - [SM8150_GFX] = &gfx, - [SM8150_LCX] = &lcx, - [SM8150_LMX] = &lmx, - [SM8150_MMCX] = &mmcx, - [SM8150_MMCX_AO] = &mmcx_ao, - [SM8150_MSS] = &mss, - [SM8150_MX] = &mx, - [SM8150_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sm8150_desc = { - .rpmhpds = sm8150_rpmhpds, - .num_pds = ARRAY_SIZE(sm8150_rpmhpds), -}; - -static struct rpmhpd *sa8155p_rpmhpds[] = { - [SA8155P_CX] = &cx_w_mx_parent, - [SA8155P_CX_AO] = &cx_ao_w_mx_parent, - [SA8155P_EBI] = &ebi, - [SA8155P_GFX] = &gfx, - [SA8155P_MSS] = &mss, - [SA8155P_MX] = &mx, - [SA8155P_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sa8155p_desc = { - .rpmhpds = sa8155p_rpmhpds, - .num_pds = ARRAY_SIZE(sa8155p_rpmhpds), -}; - -/* SM8250 RPMH powerdomains */ -static struct rpmhpd *sm8250_rpmhpds[] = { - [RPMHPD_CX] = &cx_w_mx_parent, - [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, - [RPMHPD_EBI] = &ebi, - [RPMHPD_GFX] = &gfx, - [RPMHPD_LCX] = &lcx, - [RPMHPD_LMX] = &lmx, - [RPMHPD_MMCX] = &mmcx, - [RPMHPD_MMCX_AO] = &mmcx_ao, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sm8250_desc = { - .rpmhpds = sm8250_rpmhpds, - .num_pds = ARRAY_SIZE(sm8250_rpmhpds), -}; - -/* SM8350 Power domains */ -static struct rpmhpd *sm8350_rpmhpds[] = { - [RPMHPD_CX] = &cx_w_mx_parent, - [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, - [RPMHPD_EBI] = &ebi, - [RPMHPD_GFX] = &gfx, - [RPMHPD_LCX] = &lcx, - [RPMHPD_LMX] = &lmx, - [RPMHPD_MMCX] = &mmcx, - [RPMHPD_MMCX_AO] = &mmcx_ao, - [RPMHPD_MSS] = &mss, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MXC] = &mxc, - [RPMHPD_MXC_AO] = &mxc_ao, -}; - -static const struct rpmhpd_desc sm8350_desc = { - .rpmhpds = sm8350_rpmhpds, - .num_pds = ARRAY_SIZE(sm8350_rpmhpds), -}; - -/* SM8450 RPMH powerdomains */ -static struct rpmhpd *sm8450_rpmhpds[] = { - [RPMHPD_CX] = &cx, - [RPMHPD_CX_AO] = &cx_ao, - [RPMHPD_EBI] = &ebi, - [RPMHPD_GFX] = &gfx, - [RPMHPD_LCX] = &lcx, - [RPMHPD_LMX] = &lmx, - [RPMHPD_MMCX] = &mmcx_w_cx_parent, - [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, - [RPMHPD_MSS] = &mss, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MXC] = &mxc, - [RPMHPD_MXC_AO] = &mxc_ao, -}; - -static const struct rpmhpd_desc sm8450_desc = { - .rpmhpds = sm8450_rpmhpds, - .num_pds = ARRAY_SIZE(sm8450_rpmhpds), -}; - -/* SM8550 RPMH powerdomains */ -static struct rpmhpd *sm8550_rpmhpds[] = { - [RPMHPD_CX] = &cx, - [RPMHPD_CX_AO] = &cx_ao, - [RPMHPD_EBI] = &ebi, - [RPMHPD_GFX] = &gfx, - [RPMHPD_LCX] = &lcx, - [RPMHPD_LMX] = &lmx, - [RPMHPD_MMCX] = &mmcx_w_cx_parent, - [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, - [RPMHPD_MSS] = &mss, - [RPMHPD_MX] = &mx, - [RPMHPD_MX_AO] = &mx_ao, - [RPMHPD_MXC] = &mxc, - [RPMHPD_MXC_AO] = &mxc_ao, - [RPMHPD_NSP] = &nsp, -}; - -static const struct rpmhpd_desc sm8550_desc = { - .rpmhpds = sm8550_rpmhpds, - .num_pds = ARRAY_SIZE(sm8550_rpmhpds), -}; - -/* QDU1000/QRU1000 RPMH powerdomains */ -static struct rpmhpd *qdu1000_rpmhpds[] = { - [QDU1000_CX] = &cx, - [QDU1000_EBI] = &ebi, - [QDU1000_MSS] = &mss, - [QDU1000_MX] = &mx, -}; - -static const struct rpmhpd_desc qdu1000_desc = { - .rpmhpds = qdu1000_rpmhpds, - .num_pds = ARRAY_SIZE(qdu1000_rpmhpds), -}; - -/* SC7180 RPMH powerdomains */ -static struct rpmhpd *sc7180_rpmhpds[] = { - [SC7180_CX] = &cx_w_mx_parent, - [SC7180_CX_AO] = &cx_ao_w_mx_parent, - [SC7180_GFX] = &gfx, - [SC7180_LCX] = &lcx, - [SC7180_LMX] = &lmx, - [SC7180_MSS] = &mss, - [SC7180_MX] = &mx, - [SC7180_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sc7180_desc = { - .rpmhpds = sc7180_rpmhpds, - .num_pds = ARRAY_SIZE(sc7180_rpmhpds), -}; - -/* SC7280 RPMH powerdomains */ -static struct rpmhpd *sc7280_rpmhpds[] = { - [SC7280_CX] = &cx, - [SC7280_CX_AO] = &cx_ao, - [SC7280_EBI] = &ebi, - [SC7280_GFX] = &gfx, - [SC7280_LCX] = &lcx, - [SC7280_LMX] = &lmx, - [SC7280_MSS] = &mss, - [SC7280_MX] = &mx, - [SC7280_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sc7280_desc = { - .rpmhpds = sc7280_rpmhpds, - .num_pds = ARRAY_SIZE(sc7280_rpmhpds), -}; - -/* SC8180x RPMH powerdomains */ -static struct rpmhpd *sc8180x_rpmhpds[] = { - [SC8180X_CX] = &cx_w_mx_parent, - [SC8180X_CX_AO] = &cx_ao_w_mx_parent, - [SC8180X_EBI] = &ebi, - [SC8180X_GFX] = &gfx, - [SC8180X_LCX] = &lcx, - [SC8180X_LMX] = &lmx, - [SC8180X_MMCX] = &mmcx, - [SC8180X_MMCX_AO] = &mmcx_ao, - [SC8180X_MSS] = &mss, - [SC8180X_MX] = &mx, - [SC8180X_MX_AO] = &mx_ao, -}; - -static const struct rpmhpd_desc sc8180x_desc = { - .rpmhpds = sc8180x_rpmhpds, - .num_pds = ARRAY_SIZE(sc8180x_rpmhpds), -}; - -/* SC8280xp RPMH powerdomains */ -static struct rpmhpd *sc8280xp_rpmhpds[] = { - [SC8280XP_CX] = &cx, - [SC8280XP_CX_AO] = &cx_ao, - [SC8280XP_EBI] = &ebi, - [SC8280XP_GFX] = &gfx, - [SC8280XP_LCX] = &lcx, - [SC8280XP_LMX] = &lmx, - [SC8280XP_MMCX] = &mmcx, - [SC8280XP_MMCX_AO] = &mmcx_ao, - [SC8280XP_MX] = &mx, - [SC8280XP_MX_AO] = &mx_ao, - [SC8280XP_NSP] = &nsp, - [SC8280XP_QPHY] = &qphy, -}; - -static const struct rpmhpd_desc sc8280xp_desc = { - .rpmhpds = sc8280xp_rpmhpds, - .num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), -}; - -static const struct of_device_id rpmhpd_match_table[] = { - { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, - { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, - { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, - { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, - { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, - { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, - { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, - { .compatible = "qcom,sc8280xp-rpmhpd", .data = &sc8280xp_desc }, - { .compatible = "qcom,sdm670-rpmhpd", .data = &sdm670_desc }, - { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, - { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, - { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, - { .compatible = "qcom,sdx75-rpmhpd", .data = &sdx75_desc}, - { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, - { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, - { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, - { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, - { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, - { .compatible = "qcom,sm8550-rpmhpd", .data = &sm8550_desc }, - { } -}; -MODULE_DEVICE_TABLE(of, rpmhpd_match_table); - -static int rpmhpd_send_corner(struct rpmhpd *pd, int state, - unsigned int corner, bool sync) -{ - struct tcs_cmd cmd = { - .addr = pd->addr, - .data = corner, - }; - - /* - * Wait for an ack only when we are increasing the - * perf state of the power domain - */ - if (sync) - return rpmh_write(pd->dev, state, &cmd, 1); - else - return rpmh_write_async(pd->dev, state, &cmd, 1); -} - -static void to_active_sleep(struct rpmhpd *pd, unsigned int corner, - unsigned int *active, unsigned int *sleep) -{ - *active = corner; - - if (pd->active_only) - *sleep = 0; - else - *sleep = *active; -} - -/* - * This function is used to aggregate the votes across the active only - * resources and its peers. The aggregated votes are sent to RPMh as - * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes - * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh - * on system sleep). - * We send ACTIVE_ONLY votes for resources without any peers. For others, - * which have an active only peer, all 3 votes are sent. - */ -static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) -{ - int ret; - struct rpmhpd *peer = pd->peer; - unsigned int active_corner, sleep_corner; - unsigned int this_active_corner = 0, this_sleep_corner = 0; - unsigned int peer_active_corner = 0, peer_sleep_corner = 0; - - if (pd->state_synced) { - to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); - } else { - /* Clamp to highest corner if sync_state hasn't happened */ - this_active_corner = pd->level_count - 1; - this_sleep_corner = pd->level_count - 1; - } - - if (peer && peer->enabled) - to_active_sleep(peer, peer->corner, &peer_active_corner, - &peer_sleep_corner); - - active_corner = max(this_active_corner, peer_active_corner); - - ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner, - active_corner > pd->active_corner); - if (ret) - return ret; - - pd->active_corner = active_corner; - - if (peer) { - peer->active_corner = active_corner; - - ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE, - active_corner, false); - if (ret) - return ret; - - sleep_corner = max(this_sleep_corner, peer_sleep_corner); - - return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner, - false); - } - - return ret; -} - -static int rpmhpd_power_on(struct generic_pm_domain *domain) -{ - struct rpmhpd *pd = domain_to_rpmhpd(domain); - unsigned int corner; - int ret; - - mutex_lock(&rpmhpd_lock); - - corner = max(pd->corner, pd->enable_corner); - ret = rpmhpd_aggregate_corner(pd, corner); - if (!ret) - pd->enabled = true; - - mutex_unlock(&rpmhpd_lock); - - return ret; -} - -static int rpmhpd_power_off(struct generic_pm_domain *domain) -{ - struct rpmhpd *pd = domain_to_rpmhpd(domain); - int ret; - - mutex_lock(&rpmhpd_lock); - - ret = rpmhpd_aggregate_corner(pd, 0); - if (!ret) - pd->enabled = false; - - mutex_unlock(&rpmhpd_lock); - - return ret; -} - -static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, - unsigned int level) -{ - struct rpmhpd *pd = domain_to_rpmhpd(domain); - int ret = 0, i; - - mutex_lock(&rpmhpd_lock); - - for (i = 0; i < pd->level_count; i++) - if (level <= pd->level[i]) - break; - - /* - * If the level requested is more than that supported by the - * max corner, just set it to max anyway. - */ - if (i == pd->level_count) - i--; - - if (pd->enabled) { - /* Ensure that the domain isn't turn off */ - if (i < pd->enable_corner) - i = pd->enable_corner; - - ret = rpmhpd_aggregate_corner(pd, i); - if (ret) - goto out; - } - - pd->corner = i; -out: - mutex_unlock(&rpmhpd_lock); - - return ret; -} - -static unsigned int rpmhpd_get_performance_state(struct generic_pm_domain *genpd, - struct dev_pm_opp *opp) -{ - return dev_pm_opp_get_level(opp); -} - -static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) -{ - int i; - const u16 *buf; - - buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count); - if (IS_ERR(buf)) - return PTR_ERR(buf); - - /* 2 bytes used for each command DB aux data entry */ - rpmhpd->level_count >>= 1; - - if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS) - return -EINVAL; - - for (i = 0; i < rpmhpd->level_count; i++) { - rpmhpd->level[i] = buf[i]; - - /* Remember the first corner with non-zero level */ - if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i]) - rpmhpd->enable_corner = i; - - /* - * The AUX data may be zero padded. These 0 valued entries at - * the end of the map must be ignored. - */ - if (i > 0 && rpmhpd->level[i] == 0) { - rpmhpd->level_count = i; - break; - } - pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, - rpmhpd->level[i]); - } - - return 0; -} - -static int rpmhpd_probe(struct platform_device *pdev) -{ - int i, ret; - size_t num_pds; - struct device *dev = &pdev->dev; - struct genpd_onecell_data *data; - struct rpmhpd **rpmhpds; - const struct rpmhpd_desc *desc; - - desc = of_device_get_match_data(dev); - if (!desc) - return -EINVAL; - - rpmhpds = desc->rpmhpds; - num_pds = desc->num_pds; - - data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains), - GFP_KERNEL); - if (!data->domains) - return -ENOMEM; - - data->num_domains = num_pds; - - for (i = 0; i < num_pds; i++) { - if (!rpmhpds[i]) - continue; - - rpmhpds[i]->dev = dev; - rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); - if (!rpmhpds[i]->addr) { - dev_err(dev, "Could not find RPMh address for resource %s\n", - rpmhpds[i]->res_name); - return -ENODEV; - } - - ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); - if (ret != CMD_DB_HW_ARC) { - dev_err(dev, "RPMh slave ID mismatch\n"); - return -EINVAL; - } - - ret = rpmhpd_update_level_mapping(rpmhpds[i]); - if (ret) - return ret; - - rpmhpds[i]->pd.power_off = rpmhpd_power_off; - rpmhpds[i]->pd.power_on = rpmhpd_power_on; - rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state; - rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance_state; - pm_genpd_init(&rpmhpds[i]->pd, NULL, true); - - data->domains[i] = &rpmhpds[i]->pd; - } - - /* Add subdomains */ - for (i = 0; i < num_pds; i++) { - if (!rpmhpds[i]) - continue; - if (rpmhpds[i]->parent) - pm_genpd_add_subdomain(rpmhpds[i]->parent, - &rpmhpds[i]->pd); - } - - return of_genpd_add_provider_onecell(pdev->dev.of_node, data); -} - -static void rpmhpd_sync_state(struct device *dev) -{ - const struct rpmhpd_desc *desc = of_device_get_match_data(dev); - struct rpmhpd **rpmhpds = desc->rpmhpds; - unsigned int corner; - struct rpmhpd *pd; - unsigned int i; - int ret; - - mutex_lock(&rpmhpd_lock); - for (i = 0; i < desc->num_pds; i++) { - pd = rpmhpds[i]; - if (!pd) - continue; - - pd->state_synced = true; - if (pd->enabled) - corner = max(pd->corner, pd->enable_corner); - else - corner = 0; - - ret = rpmhpd_aggregate_corner(pd, corner); - if (ret) - dev_err(dev, "failed to sync %s\n", pd->res_name); - } - mutex_unlock(&rpmhpd_lock); -} - -static struct platform_driver rpmhpd_driver = { - .driver = { - .name = "qcom-rpmhpd", - .of_match_table = rpmhpd_match_table, - .suppress_bind_attrs = true, - .sync_state = rpmhpd_sync_state, - }, - .probe = rpmhpd_probe, -}; - -static int __init rpmhpd_init(void) -{ - return platform_driver_register(&rpmhpd_driver); -} -core_initcall(rpmhpd_init); - -MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/genpd/qcom/rpmpd.c b/drivers/genpd/qcom/rpmpd.c deleted file mode 100644 index 3135dd1dafe0..000000000000 --- a/drivers/genpd/qcom/rpmpd.c +++ /dev/null @@ -1,1023 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd) - -/* Resource types: - * RPMPD_X is X encoded as a little-endian, lower-case, ASCII string */ -#define RPMPD_SMPA 0x61706d73 -#define RPMPD_LDOA 0x616f646c -#define RPMPD_SMPB 0x62706d73 -#define RPMPD_LDOB 0x626f646c -#define RPMPD_RWCX 0x78637772 -#define RPMPD_RWMX 0x786d7772 -#define RPMPD_RWLC 0x636c7772 -#define RPMPD_RWLM 0x6d6c7772 -#define RPMPD_RWSC 0x63737772 -#define RPMPD_RWSM 0x6d737772 -#define RPMPD_RWGX 0x78677772 - -/* Operation Keys */ -#define KEY_CORNER 0x6e726f63 /* corn */ -#define KEY_ENABLE 0x6e657773 /* swen */ -#define KEY_FLOOR_CORNER 0x636676 /* vfc */ -#define KEY_FLOOR_LEVEL 0x6c6676 /* vfl */ -#define KEY_LEVEL 0x6c766c76 /* vlvl */ - -#define MAX_CORNER_RPMPD_STATE 6 - -struct rpmpd_req { - __le32 key; - __le32 nbytes; - __le32 value; -}; - -struct rpmpd { - struct generic_pm_domain pd; - struct generic_pm_domain *parent; - struct rpmpd *peer; - const bool active_only; - unsigned int corner; - bool enabled; - const int res_type; - const int res_id; - struct qcom_smd_rpm *rpm; - unsigned int max_state; - __le32 key; - bool state_synced; -}; - -struct rpmpd_desc { - struct rpmpd **rpmpds; - size_t num_pds; - unsigned int max_state; -}; - -static DEFINE_MUTEX(rpmpd_lock); - -/* CX */ -static struct rpmpd cx_rwcx0_lvl_ao; -static struct rpmpd cx_rwcx0_lvl = { - .pd = { .name = "cx", }, - .peer = &cx_rwcx0_lvl_ao, - .res_type = RPMPD_RWCX, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd cx_rwcx0_lvl_ao = { - .pd = { .name = "cx_ao", }, - .peer = &cx_rwcx0_lvl, - .active_only = true, - .res_type = RPMPD_RWCX, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd cx_s1a_corner_ao; -static struct rpmpd cx_s1a_corner = { - .pd = { .name = "cx", }, - .peer = &cx_s1a_corner_ao, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_CORNER, -}; - -static struct rpmpd cx_s1a_corner_ao = { - .pd = { .name = "cx_ao", }, - .peer = &cx_s1a_corner, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_CORNER, -}; - -static struct rpmpd cx_s2a_corner_ao; -static struct rpmpd cx_s2a_corner = { - .pd = { .name = "cx", }, - .peer = &cx_s2a_corner_ao, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_CORNER, -}; - -static struct rpmpd cx_s2a_corner_ao = { - .pd = { .name = "cx_ao", }, - .peer = &cx_s2a_corner, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_CORNER, -}; - -static struct rpmpd cx_s2a_lvl_ao; -static struct rpmpd cx_s2a_lvl = { - .pd = { .name = "cx", }, - .peer = &cx_s2a_lvl_ao, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_LEVEL, -}; - -static struct rpmpd cx_s2a_lvl_ao = { - .pd = { .name = "cx_ao", }, - .peer = &cx_s2a_lvl, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_LEVEL, -}; - -static struct rpmpd cx_s3a_lvl_ao; -static struct rpmpd cx_s3a_lvl = { - .pd = { .name = "cx", }, - .peer = &cx_s3a_lvl_ao, - .res_type = RPMPD_SMPA, - .res_id = 3, - .key = KEY_LEVEL, -}; - -static struct rpmpd cx_s3a_lvl_ao = { - .pd = { .name = "cx_ao", }, - .peer = &cx_s3a_lvl, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 3, - .key = KEY_LEVEL, -}; - -static struct rpmpd cx_rwcx0_vfl = { - .pd = { .name = "cx_vfl", }, - .res_type = RPMPD_RWCX, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd cx_rwsc2_vfl = { - .pd = { .name = "cx_vfl", }, - .res_type = RPMPD_RWSC, - .res_id = 2, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd cx_s1a_vfc = { - .pd = { .name = "cx_vfc", }, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_FLOOR_CORNER, -}; - -static struct rpmpd cx_s2a_vfc = { - .pd = { .name = "cx_vfc", }, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_FLOOR_CORNER, -}; - -static struct rpmpd cx_s2a_vfl = { - .pd = { .name = "cx_vfl", }, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd cx_s3a_vfl = { - .pd = { .name = "cx_vfl", }, - .res_type = RPMPD_SMPA, - .res_id = 3, - .key = KEY_FLOOR_LEVEL, -}; - -/* G(F)X */ -static struct rpmpd gfx_s2b_corner = { - .pd = { .name = "gfx", }, - .res_type = RPMPD_SMPB, - .res_id = 2, - .key = KEY_CORNER, -}; - -static struct rpmpd gfx_s2b_vfc = { - .pd = { .name = "gfx_vfc", }, - .res_type = RPMPD_SMPB, - .res_id = 2, - .key = KEY_FLOOR_CORNER, -}; - -static struct rpmpd mx_rwmx0_lvl; -static struct rpmpd gx_rwgx0_lvl_ao; -static struct rpmpd gx_rwgx0_lvl = { - .pd = { .name = "gx", }, - .peer = &gx_rwgx0_lvl_ao, - .res_type = RPMPD_RWGX, - .parent = &mx_rwmx0_lvl.pd, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_rwmx0_lvl_ao; -static struct rpmpd gx_rwgx0_lvl_ao = { - .pd = { .name = "gx_ao", }, - .peer = &gx_rwgx0_lvl, - .parent = &mx_rwmx0_lvl_ao.pd, - .active_only = true, - .res_type = RPMPD_RWGX, - .res_id = 0, - .key = KEY_LEVEL, -}; - -/* MX */ -static struct rpmpd mx_l3a_corner_ao; -static struct rpmpd mx_l3a_corner = { - .pd = { .name = "mx", }, - .peer = &mx_l3a_corner_ao, - .res_type = RPMPD_LDOA, - .res_id = 3, - .key = KEY_CORNER, -}; - -static struct rpmpd mx_l3a_corner_ao = { - .pd = { .name = "mx_ao", }, - .peer = &mx_l3a_corner, - .active_only = true, - .res_type = RPMPD_LDOA, - .res_id = 3, - .key = KEY_CORNER, -}; - -static struct rpmpd mx_l12a_lvl_ao; -static struct rpmpd mx_l12a_lvl = { - .pd = { .name = "mx", }, - .peer = &mx_l12a_lvl_ao, - .res_type = RPMPD_LDOA, - .res_id = 12, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_l12a_lvl_ao = { - .pd = { .name = "mx_ao", }, - .peer = &mx_l12a_lvl, - .active_only = true, - .res_type = RPMPD_LDOA, - .res_id = 12, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_s2a_corner_ao; -static struct rpmpd mx_s2a_corner = { - .pd = { .name = "mx", }, - .peer = &mx_s2a_corner_ao, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_CORNER, -}; - -static struct rpmpd mx_s2a_corner_ao = { - .pd = { .name = "mx_ao", }, - .peer = &mx_s2a_corner, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 2, - .key = KEY_CORNER, -}; - -static struct rpmpd mx_rwmx0_lvl_ao; -static struct rpmpd mx_rwmx0_lvl = { - .pd = { .name = "mx", }, - .peer = &mx_rwmx0_lvl_ao, - .res_type = RPMPD_RWMX, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_rwmx0_lvl_ao = { - .pd = { .name = "mx_ao", }, - .peer = &mx_rwmx0_lvl, - .active_only = true, - .res_type = RPMPD_RWMX, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_s6a_lvl_ao; -static struct rpmpd mx_s6a_lvl = { - .pd = { .name = "mx", }, - .peer = &mx_s6a_lvl_ao, - .res_type = RPMPD_SMPA, - .res_id = 6, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_s6a_lvl_ao = { - .pd = { .name = "mx_ao", }, - .peer = &mx_s6a_lvl, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 6, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_s7a_lvl_ao; -static struct rpmpd mx_s7a_lvl = { - .pd = { .name = "mx", }, - .peer = &mx_s7a_lvl_ao, - .res_type = RPMPD_SMPA, - .res_id = 7, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_s7a_lvl_ao = { - .pd = { .name = "mx_ao", }, - .peer = &mx_s7a_lvl, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 7, - .key = KEY_LEVEL, -}; - -static struct rpmpd mx_l12a_vfl = { - .pd = { .name = "mx_vfl", }, - .res_type = RPMPD_LDOA, - .res_id = 12, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd mx_rwmx0_vfl = { - .pd = { .name = "mx_vfl", }, - .res_type = RPMPD_RWMX, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd mx_rwsm6_vfl = { - .pd = { .name = "mx_vfl", }, - .res_type = RPMPD_RWSM, - .res_id = 6, - .key = KEY_FLOOR_LEVEL, -}; - -/* MD */ -static struct rpmpd md_s1a_corner_ao; -static struct rpmpd md_s1a_corner = { - .pd = { .name = "md", }, - .peer = &md_s1a_corner_ao, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_CORNER, -}; - -static struct rpmpd md_s1a_corner_ao = { - .pd = { .name = "md_ao", }, - .peer = &md_s1a_corner, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_CORNER, -}; - -static struct rpmpd md_s1a_lvl_ao; -static struct rpmpd md_s1a_lvl = { - .pd = { .name = "md", }, - .peer = &md_s1a_lvl_ao, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_LEVEL, -}; - -static struct rpmpd md_s1a_lvl_ao = { - .pd = { .name = "md_ao", }, - .peer = &md_s1a_lvl, - .active_only = true, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_LEVEL, -}; - -static struct rpmpd md_s1a_vfc = { - .pd = { .name = "md_vfc", }, - .res_type = RPMPD_SMPA, - .res_id = 1, - .key = KEY_FLOOR_CORNER, -}; - -/* LPI_CX */ -static struct rpmpd lpi_cx_rwlc0_lvl = { - .pd = { .name = "lpi_cx", }, - .res_type = RPMPD_RWLC, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd lpi_cx_rwlc0_vfl = { - .pd = { .name = "lpi_cx_vfl", }, - .res_type = RPMPD_RWLC, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -/* LPI_MX */ -static struct rpmpd lpi_mx_rwlm0_lvl = { - .pd = { .name = "lpi_mx", }, - .res_type = RPMPD_RWLM, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd lpi_mx_rwlm0_vfl = { - .pd = { .name = "lpi_mx_vfl", }, - .res_type = RPMPD_RWLM, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -/* SSC_CX */ -static struct rpmpd ssc_cx_l26a_corner = { - .pd = { .name = "ssc_cx", }, - .res_type = RPMPD_LDOA, - .res_id = 26, - .key = KEY_CORNER, -}; - -static struct rpmpd ssc_cx_rwlc0_lvl = { - .pd = { .name = "ssc_cx", }, - .res_type = RPMPD_RWLC, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd ssc_cx_rwsc0_lvl = { - .pd = { .name = "ssc_cx", }, - .res_type = RPMPD_RWSC, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd ssc_cx_l26a_vfc = { - .pd = { .name = "ssc_cx_vfc", }, - .res_type = RPMPD_LDOA, - .res_id = 26, - .key = KEY_FLOOR_CORNER, -}; - -static struct rpmpd ssc_cx_rwlc0_vfl = { - .pd = { .name = "ssc_cx_vfl", }, - .res_type = RPMPD_RWLC, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd ssc_cx_rwsc0_vfl = { - .pd = { .name = "ssc_cx_vfl", }, - .res_type = RPMPD_RWSC, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -/* SSC_MX */ -static struct rpmpd ssc_mx_rwlm0_lvl = { - .pd = { .name = "ssc_mx", }, - .res_type = RPMPD_RWLM, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd ssc_mx_rwsm0_lvl = { - .pd = { .name = "ssc_mx", }, - .res_type = RPMPD_RWSM, - .res_id = 0, - .key = KEY_LEVEL, -}; - -static struct rpmpd ssc_mx_rwlm0_vfl = { - .pd = { .name = "ssc_mx_vfl", }, - .res_type = RPMPD_RWLM, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd ssc_mx_rwsm0_vfl = { - .pd = { .name = "ssc_mx_vfl", }, - .res_type = RPMPD_RWSM, - .res_id = 0, - .key = KEY_FLOOR_LEVEL, -}; - -static struct rpmpd *mdm9607_rpmpds[] = { - [MDM9607_VDDCX] = &cx_s3a_lvl, - [MDM9607_VDDCX_AO] = &cx_s3a_lvl_ao, - [MDM9607_VDDCX_VFL] = &cx_s3a_vfl, - [MDM9607_VDDMX] = &mx_l12a_lvl, - [MDM9607_VDDMX_AO] = &mx_l12a_lvl_ao, - [MDM9607_VDDMX_VFL] = &mx_l12a_vfl, -}; - -static const struct rpmpd_desc mdm9607_desc = { - .rpmpds = mdm9607_rpmpds, - .num_pds = ARRAY_SIZE(mdm9607_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO, -}; - -static struct rpmpd *msm8226_rpmpds[] = { - [MSM8226_VDDCX] = &cx_s1a_corner, - [MSM8226_VDDCX_AO] = &cx_s1a_corner_ao, - [MSM8226_VDDCX_VFC] = &cx_s1a_vfc, -}; - -static const struct rpmpd_desc msm8226_desc = { - .rpmpds = msm8226_rpmpds, - .num_pds = ARRAY_SIZE(msm8226_rpmpds), - .max_state = MAX_CORNER_RPMPD_STATE, -}; - -static struct rpmpd *msm8939_rpmpds[] = { - [MSM8939_VDDMDCX] = &md_s1a_corner, - [MSM8939_VDDMDCX_AO] = &md_s1a_corner_ao, - [MSM8939_VDDMDCX_VFC] = &md_s1a_vfc, - [MSM8939_VDDCX] = &cx_s2a_corner, - [MSM8939_VDDCX_AO] = &cx_s2a_corner_ao, - [MSM8939_VDDCX_VFC] = &cx_s2a_vfc, - [MSM8939_VDDMX] = &mx_l3a_corner, - [MSM8939_VDDMX_AO] = &mx_l3a_corner_ao, -}; - -static const struct rpmpd_desc msm8939_desc = { - .rpmpds = msm8939_rpmpds, - .num_pds = ARRAY_SIZE(msm8939_rpmpds), - .max_state = MAX_CORNER_RPMPD_STATE, -}; - -static struct rpmpd *msm8916_rpmpds[] = { - [MSM8916_VDDCX] = &cx_s1a_corner, - [MSM8916_VDDCX_AO] = &cx_s1a_corner_ao, - [MSM8916_VDDCX_VFC] = &cx_s1a_vfc, - [MSM8916_VDDMX] = &mx_l3a_corner, - [MSM8916_VDDMX_AO] = &mx_l3a_corner_ao, -}; - -static const struct rpmpd_desc msm8916_desc = { - .rpmpds = msm8916_rpmpds, - .num_pds = ARRAY_SIZE(msm8916_rpmpds), - .max_state = MAX_CORNER_RPMPD_STATE, -}; - -static struct rpmpd *msm8953_rpmpds[] = { - [MSM8953_VDDMD] = &md_s1a_lvl, - [MSM8953_VDDMD_AO] = &md_s1a_lvl_ao, - [MSM8953_VDDCX] = &cx_s2a_lvl, - [MSM8953_VDDCX_AO] = &cx_s2a_lvl_ao, - [MSM8953_VDDCX_VFL] = &cx_s2a_vfl, - [MSM8953_VDDMX] = &mx_s7a_lvl, - [MSM8953_VDDMX_AO] = &mx_s7a_lvl_ao, -}; - -static const struct rpmpd_desc msm8953_desc = { - .rpmpds = msm8953_rpmpds, - .num_pds = ARRAY_SIZE(msm8953_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO, -}; - -static struct rpmpd *msm8976_rpmpds[] = { - [MSM8976_VDDCX] = &cx_s2a_lvl, - [MSM8976_VDDCX_AO] = &cx_s2a_lvl_ao, - [MSM8976_VDDCX_VFL] = &cx_rwsc2_vfl, - [MSM8976_VDDMX] = &mx_s6a_lvl, - [MSM8976_VDDMX_AO] = &mx_s6a_lvl_ao, - [MSM8976_VDDMX_VFL] = &mx_rwsm6_vfl, -}; - -static const struct rpmpd_desc msm8976_desc = { - .rpmpds = msm8976_rpmpds, - .num_pds = ARRAY_SIZE(msm8976_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO_HIGH, -}; - -static struct rpmpd *msm8994_rpmpds[] = { - [MSM8994_VDDCX] = &cx_s1a_corner, - [MSM8994_VDDCX_AO] = &cx_s1a_corner_ao, - [MSM8994_VDDCX_VFC] = &cx_s1a_vfc, - [MSM8994_VDDMX] = &mx_s2a_corner, - [MSM8994_VDDMX_AO] = &mx_s2a_corner_ao, - - /* Attention! *Some* 8994 boards with pm8004 may use SMPC here! */ - [MSM8994_VDDGFX] = &gfx_s2b_corner, - [MSM8994_VDDGFX_VFC] = &gfx_s2b_vfc, -}; - -static const struct rpmpd_desc msm8994_desc = { - .rpmpds = msm8994_rpmpds, - .num_pds = ARRAY_SIZE(msm8994_rpmpds), - .max_state = MAX_CORNER_RPMPD_STATE, -}; - -static struct rpmpd *msm8996_rpmpds[] = { - [MSM8996_VDDCX] = &cx_s1a_corner, - [MSM8996_VDDCX_AO] = &cx_s1a_corner_ao, - [MSM8996_VDDCX_VFC] = &cx_s1a_vfc, - [MSM8996_VDDMX] = &mx_s2a_corner, - [MSM8996_VDDMX_AO] = &mx_s2a_corner_ao, - [MSM8996_VDDSSCX] = &ssc_cx_l26a_corner, - [MSM8996_VDDSSCX_VFC] = &ssc_cx_l26a_vfc, -}; - -static const struct rpmpd_desc msm8996_desc = { - .rpmpds = msm8996_rpmpds, - .num_pds = ARRAY_SIZE(msm8996_rpmpds), - .max_state = MAX_CORNER_RPMPD_STATE, -}; - -static struct rpmpd *msm8998_rpmpds[] = { - [MSM8998_VDDCX] = &cx_rwcx0_lvl, - [MSM8998_VDDCX_AO] = &cx_rwcx0_lvl_ao, - [MSM8998_VDDCX_VFL] = &cx_rwcx0_vfl, - [MSM8998_VDDMX] = &mx_rwmx0_lvl, - [MSM8998_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [MSM8998_VDDMX_VFL] = &mx_rwmx0_vfl, - [MSM8998_SSCCX] = &ssc_cx_rwsc0_lvl, - [MSM8998_SSCCX_VFL] = &ssc_cx_rwsc0_vfl, - [MSM8998_SSCMX] = &ssc_mx_rwsm0_lvl, - [MSM8998_SSCMX_VFL] = &ssc_mx_rwsm0_vfl, -}; - -static const struct rpmpd_desc msm8998_desc = { - .rpmpds = msm8998_rpmpds, - .num_pds = ARRAY_SIZE(msm8998_rpmpds), - .max_state = RPM_SMD_LEVEL_BINNING, -}; - -static struct rpmpd *qcs404_rpmpds[] = { - [QCS404_VDDMX] = &mx_rwmx0_lvl, - [QCS404_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [QCS404_VDDMX_VFL] = &mx_rwmx0_vfl, - [QCS404_LPICX] = &lpi_cx_rwlc0_lvl, - [QCS404_LPICX_VFL] = &lpi_cx_rwlc0_vfl, - [QCS404_LPIMX] = &lpi_mx_rwlm0_lvl, - [QCS404_LPIMX_VFL] = &lpi_mx_rwlm0_vfl, -}; - -static const struct rpmpd_desc qcs404_desc = { - .rpmpds = qcs404_rpmpds, - .num_pds = ARRAY_SIZE(qcs404_rpmpds), - .max_state = RPM_SMD_LEVEL_BINNING, -}; - -static struct rpmpd *sdm660_rpmpds[] = { - [SDM660_VDDCX] = &cx_rwcx0_lvl, - [SDM660_VDDCX_AO] = &cx_rwcx0_lvl_ao, - [SDM660_VDDCX_VFL] = &cx_rwcx0_vfl, - [SDM660_VDDMX] = &mx_rwmx0_lvl, - [SDM660_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [SDM660_VDDMX_VFL] = &mx_rwmx0_vfl, - [SDM660_SSCCX] = &ssc_cx_rwlc0_lvl, - [SDM660_SSCCX_VFL] = &ssc_cx_rwlc0_vfl, - [SDM660_SSCMX] = &ssc_mx_rwlm0_lvl, - [SDM660_SSCMX_VFL] = &ssc_mx_rwlm0_vfl, -}; - -static const struct rpmpd_desc sdm660_desc = { - .rpmpds = sdm660_rpmpds, - .num_pds = ARRAY_SIZE(sdm660_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO, -}; - -static struct rpmpd *sm6115_rpmpds[] = { - [SM6115_VDDCX] = &cx_rwcx0_lvl, - [SM6115_VDDCX_AO] = &cx_rwcx0_lvl_ao, - [SM6115_VDDCX_VFL] = &cx_rwcx0_vfl, - [SM6115_VDDMX] = &mx_rwmx0_lvl, - [SM6115_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [SM6115_VDDMX_VFL] = &mx_rwmx0_vfl, - [SM6115_VDD_LPI_CX] = &lpi_cx_rwlc0_lvl, - [SM6115_VDD_LPI_MX] = &lpi_mx_rwlm0_lvl, -}; - -static const struct rpmpd_desc sm6115_desc = { - .rpmpds = sm6115_rpmpds, - .num_pds = ARRAY_SIZE(sm6115_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, -}; - -static struct rpmpd *sm6125_rpmpds[] = { - [SM6125_VDDCX] = &cx_rwcx0_lvl, - [SM6125_VDDCX_AO] = &cx_rwcx0_lvl_ao, - [SM6125_VDDCX_VFL] = &cx_rwcx0_vfl, - [SM6125_VDDMX] = &mx_rwmx0_lvl, - [SM6125_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [SM6125_VDDMX_VFL] = &mx_rwmx0_vfl, -}; - -static const struct rpmpd_desc sm6125_desc = { - .rpmpds = sm6125_rpmpds, - .num_pds = ARRAY_SIZE(sm6125_rpmpds), - .max_state = RPM_SMD_LEVEL_BINNING, -}; - -static struct rpmpd *sm6375_rpmpds[] = { - [SM6375_VDDCX] = &cx_rwcx0_lvl, - [SM6375_VDDCX_AO] = &cx_rwcx0_lvl_ao, - [SM6375_VDDCX_VFL] = &cx_rwcx0_vfl, - [SM6375_VDDMX] = &mx_rwmx0_lvl, - [SM6375_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [SM6375_VDDMX_VFL] = &mx_rwmx0_vfl, - [SM6375_VDDGX] = &gx_rwgx0_lvl, - [SM6375_VDDGX_AO] = &gx_rwgx0_lvl_ao, - [SM6375_VDD_LPI_CX] = &lpi_cx_rwlc0_lvl, - [SM6375_VDD_LPI_MX] = &lpi_mx_rwlm0_lvl, -}; - -static const struct rpmpd_desc sm6375_desc = { - .rpmpds = sm6375_rpmpds, - .num_pds = ARRAY_SIZE(sm6375_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, -}; - -static struct rpmpd *qcm2290_rpmpds[] = { - [QCM2290_VDDCX] = &cx_rwcx0_lvl, - [QCM2290_VDDCX_AO] = &cx_rwcx0_lvl_ao, - [QCM2290_VDDCX_VFL] = &cx_rwcx0_vfl, - [QCM2290_VDDMX] = &mx_rwmx0_lvl, - [QCM2290_VDDMX_AO] = &mx_rwmx0_lvl_ao, - [QCM2290_VDDMX_VFL] = &mx_rwmx0_vfl, - [QCM2290_VDD_LPI_CX] = &lpi_cx_rwlc0_lvl, - [QCM2290_VDD_LPI_MX] = &lpi_mx_rwlm0_lvl, -}; - -static const struct rpmpd_desc qcm2290_desc = { - .rpmpds = qcm2290_rpmpds, - .num_pds = ARRAY_SIZE(qcm2290_rpmpds), - .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, -}; - -static const struct of_device_id rpmpd_match_table[] = { - { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, - { .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc }, - { .compatible = "qcom,msm8909-rpmpd", .data = &msm8916_desc }, - { .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc }, - { .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc }, - { .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc }, - { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, - { .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc }, - { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, - { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, - { .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc }, - { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, - { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, - { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, - { .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc }, - { .compatible = "qcom,sm6375-rpmpd", .data = &sm6375_desc }, - { } -}; -MODULE_DEVICE_TABLE(of, rpmpd_match_table); - -static int rpmpd_send_enable(struct rpmpd *pd, bool enable) -{ - struct rpmpd_req req = { - .key = KEY_ENABLE, - .nbytes = cpu_to_le32(sizeof(u32)), - .value = cpu_to_le32(enable), - }; - - return qcom_rpm_smd_write(pd->rpm, QCOM_SMD_RPM_ACTIVE_STATE, - pd->res_type, pd->res_id, &req, sizeof(req)); -} - -static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner) -{ - struct rpmpd_req req = { - .key = pd->key, - .nbytes = cpu_to_le32(sizeof(u32)), - .value = cpu_to_le32(corner), - }; - - return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id, - &req, sizeof(req)); -}; - -static void to_active_sleep(struct rpmpd *pd, unsigned int corner, - unsigned int *active, unsigned int *sleep) -{ - *active = corner; - - if (pd->active_only) - *sleep = 0; - else - *sleep = *active; -} - -static int rpmpd_aggregate_corner(struct rpmpd *pd) -{ - int ret; - struct rpmpd *peer = pd->peer; - unsigned int active_corner, sleep_corner; - unsigned int this_active_corner = 0, this_sleep_corner = 0; - unsigned int peer_active_corner = 0, peer_sleep_corner = 0; - - /* Clamp to the highest corner/level if sync_state isn't done yet */ - if (!pd->state_synced) - this_active_corner = this_sleep_corner = pd->max_state - 1; - else - to_active_sleep(pd, pd->corner, &this_active_corner, &this_sleep_corner); - - if (peer && peer->enabled) - to_active_sleep(peer, peer->corner, &peer_active_corner, - &peer_sleep_corner); - - active_corner = max(this_active_corner, peer_active_corner); - - ret = rpmpd_send_corner(pd, QCOM_SMD_RPM_ACTIVE_STATE, active_corner); - if (ret) - return ret; - - sleep_corner = max(this_sleep_corner, peer_sleep_corner); - - return rpmpd_send_corner(pd, QCOM_SMD_RPM_SLEEP_STATE, sleep_corner); -} - -static int rpmpd_power_on(struct generic_pm_domain *domain) -{ - int ret; - struct rpmpd *pd = domain_to_rpmpd(domain); - - mutex_lock(&rpmpd_lock); - - ret = rpmpd_send_enable(pd, true); - if (ret) - goto out; - - pd->enabled = true; - - if (pd->corner) - ret = rpmpd_aggregate_corner(pd); - -out: - mutex_unlock(&rpmpd_lock); - - return ret; -} - -static int rpmpd_power_off(struct generic_pm_domain *domain) -{ - int ret; - struct rpmpd *pd = domain_to_rpmpd(domain); - - mutex_lock(&rpmpd_lock); - - ret = rpmpd_send_enable(pd, false); - if (!ret) - pd->enabled = false; - - mutex_unlock(&rpmpd_lock); - - return ret; -} - -static int rpmpd_set_performance(struct generic_pm_domain *domain, - unsigned int state) -{ - int ret = 0; - struct rpmpd *pd = domain_to_rpmpd(domain); - - if (state > pd->max_state) - state = pd->max_state; - - mutex_lock(&rpmpd_lock); - - pd->corner = state; - - /* Always send updates for vfc and vfl */ - if (!pd->enabled && pd->key != cpu_to_le32(KEY_FLOOR_CORNER) && - pd->key != cpu_to_le32(KEY_FLOOR_LEVEL)) - goto out; - - ret = rpmpd_aggregate_corner(pd); - -out: - mutex_unlock(&rpmpd_lock); - - return ret; -} - -static unsigned int rpmpd_get_performance(struct generic_pm_domain *genpd, - struct dev_pm_opp *opp) -{ - return dev_pm_opp_get_level(opp); -} - -static int rpmpd_probe(struct platform_device *pdev) -{ - int i; - size_t num; - struct genpd_onecell_data *data; - struct qcom_smd_rpm *rpm; - struct rpmpd **rpmpds; - const struct rpmpd_desc *desc; - - rpm = dev_get_drvdata(pdev->dev.parent); - if (!rpm) { - dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); - return -ENODEV; - } - - desc = of_device_get_match_data(&pdev->dev); - if (!desc) - return -EINVAL; - - rpmpds = desc->rpmpds; - num = desc->num_pds; - - data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); - if (!data) - return -ENOMEM; - - data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains), - GFP_KERNEL); - if (!data->domains) - return -ENOMEM; - - data->num_domains = num; - - for (i = 0; i < num; i++) { - if (!rpmpds[i]) { - dev_warn(&pdev->dev, "rpmpds[] with empty entry at index=%d\n", - i); - continue; - } - - rpmpds[i]->rpm = rpm; - rpmpds[i]->max_state = desc->max_state; - rpmpds[i]->pd.power_off = rpmpd_power_off; - rpmpds[i]->pd.power_on = rpmpd_power_on; - rpmpds[i]->pd.set_performance_state = rpmpd_set_performance; - rpmpds[i]->pd.opp_to_performance_state = rpmpd_get_performance; - pm_genpd_init(&rpmpds[i]->pd, NULL, true); - - data->domains[i] = &rpmpds[i]->pd; - } - - /* Add subdomains */ - for (i = 0; i < num; i++) { - if (!rpmpds[i]) - continue; - - if (rpmpds[i]->parent) - pm_genpd_add_subdomain(rpmpds[i]->parent, &rpmpds[i]->pd); - } - - return of_genpd_add_provider_onecell(pdev->dev.of_node, data); -} - -static void rpmpd_sync_state(struct device *dev) -{ - const struct rpmpd_desc *desc = of_device_get_match_data(dev); - struct rpmpd **rpmpds = desc->rpmpds; - struct rpmpd *pd; - unsigned int i; - int ret; - - mutex_lock(&rpmpd_lock); - for (i = 0; i < desc->num_pds; i++) { - pd = rpmpds[i]; - if (!pd) - continue; - - pd->state_synced = true; - - if (!pd->enabled) - pd->corner = 0; - - ret = rpmpd_aggregate_corner(pd); - if (ret) - dev_err(dev, "failed to sync %s: %d\n", pd->pd.name, ret); - } - mutex_unlock(&rpmpd_lock); -} - -static struct platform_driver rpmpd_driver = { - .driver = { - .name = "qcom-rpmpd", - .of_match_table = rpmpd_match_table, - .suppress_bind_attrs = true, - .sync_state = rpmpd_sync_state, - }, - .probe = rpmpd_probe, -}; - -static int __init rpmpd_init(void) -{ - return platform_driver_register(&rpmpd_driver); -} -core_initcall(rpmpd_init); - -MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPM Power Domain Driver"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/genpd/renesas/Makefile b/drivers/genpd/renesas/Makefile deleted file mode 100644 index e306e396fc8c..000000000000 --- a/drivers/genpd/renesas/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -# SoC -obj-$(CONFIG_SYSC_R8A7742) += r8a7742-sysc.o -obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o -obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o -obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o -obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o -obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o -obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o -obj-$(CONFIG_SYSC_R8A774E1) += r8a774e1-sysc.o -obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o -obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o -obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o -obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o -obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o -obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o -obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o -obj-$(CONFIG_SYSC_R8A77961) += r8a7796-sysc.o -obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o -obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o -obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o -obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o -obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o -obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o -obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o -obj-$(CONFIG_SYSC_R8A779G0) += r8a779g0-sysc.o -# Family -obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o -obj-$(CONFIG_SYSC_RCAR_GEN4) += rcar-gen4-sysc.o -obj-$(CONFIG_SYSC_RMOBILE) += rmobile-sysc.o diff --git a/drivers/genpd/renesas/r8a7742-sysc.c b/drivers/genpd/renesas/r8a7742-sysc.c deleted file mode 100644 index 219a675f83f4..000000000000 --- a/drivers/genpd/renesas/r8a7742-sysc.c +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G1H System Controller - * - * Copyright (C) 2020 Renesas Electronics Corp. - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7742_areas[] __initconst = { - { "always-on", 0, 0, R8A7742_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca15-scu", 0x180, 0, R8A7742_PD_CA15_SCU, R8A7742_PD_ALWAYS_ON, - PD_SCU }, - { "ca15-cpu0", 0x40, 0, R8A7742_PD_CA15_CPU0, R8A7742_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu1", 0x40, 1, R8A7742_PD_CA15_CPU1, R8A7742_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu2", 0x40, 2, R8A7742_PD_CA15_CPU2, R8A7742_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu3", 0x40, 3, R8A7742_PD_CA15_CPU3, R8A7742_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca7-scu", 0x100, 0, R8A7742_PD_CA7_SCU, R8A7742_PD_ALWAYS_ON, - PD_SCU }, - { "ca7-cpu0", 0x1c0, 0, R8A7742_PD_CA7_CPU0, R8A7742_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu1", 0x1c0, 1, R8A7742_PD_CA7_CPU1, R8A7742_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu2", 0x1c0, 2, R8A7742_PD_CA7_CPU2, R8A7742_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu3", 0x1c0, 3, R8A7742_PD_CA7_CPU3, R8A7742_PD_CA7_SCU, - PD_CPU_NOCR }, - { "rgx", 0xc0, 0, R8A7742_PD_RGX, R8A7742_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7742_sysc_info __initconst = { - .areas = r8a7742_areas, - .num_areas = ARRAY_SIZE(r8a7742_areas), -}; diff --git a/drivers/genpd/renesas/r8a7743-sysc.c b/drivers/genpd/renesas/r8a7743-sysc.c deleted file mode 100644 index 4e2c0ab951b3..000000000000 --- a/drivers/genpd/renesas/r8a7743-sysc.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G1M System Controller - * - * Copyright (C) 2016 Cogent Embedded Inc. - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7743_areas[] __initconst = { - { "always-on", 0, 0, R8A7743_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca15-scu", 0x180, 0, R8A7743_PD_CA15_SCU, R8A7743_PD_ALWAYS_ON, - PD_SCU }, - { "ca15-cpu0", 0x40, 0, R8A7743_PD_CA15_CPU0, R8A7743_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu1", 0x40, 1, R8A7743_PD_CA15_CPU1, R8A7743_PD_CA15_SCU, - PD_CPU_NOCR }, - { "sgx", 0xc0, 0, R8A7743_PD_SGX, R8A7743_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7743_sysc_info __initconst = { - .areas = r8a7743_areas, - .num_areas = ARRAY_SIZE(r8a7743_areas), -}; diff --git a/drivers/genpd/renesas/r8a7745-sysc.c b/drivers/genpd/renesas/r8a7745-sysc.c deleted file mode 100644 index 865821a2f0c6..000000000000 --- a/drivers/genpd/renesas/r8a7745-sysc.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G1E System Controller - * - * Copyright (C) 2016 Cogent Embedded Inc. - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7745_areas[] __initconst = { - { "always-on", 0, 0, R8A7745_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca7-scu", 0x100, 0, R8A7745_PD_CA7_SCU, R8A7745_PD_ALWAYS_ON, - PD_SCU }, - { "ca7-cpu0", 0x1c0, 0, R8A7745_PD_CA7_CPU0, R8A7745_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu1", 0x1c0, 1, R8A7745_PD_CA7_CPU1, R8A7745_PD_CA7_SCU, - PD_CPU_NOCR }, - { "sgx", 0xc0, 0, R8A7745_PD_SGX, R8A7745_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7745_sysc_info __initconst = { - .areas = r8a7745_areas, - .num_areas = ARRAY_SIZE(r8a7745_areas), -}; diff --git a/drivers/genpd/renesas/r8a77470-sysc.c b/drivers/genpd/renesas/r8a77470-sysc.c deleted file mode 100644 index 1eeb8018df50..000000000000 --- a/drivers/genpd/renesas/r8a77470-sysc.c +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G1C System Controller - * - * Copyright (C) 2018 Renesas Electronics Corp. - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a77470_areas[] __initconst = { - { "always-on", 0, 0, R8A77470_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca7-scu", 0x100, 0, R8A77470_PD_CA7_SCU, R8A77470_PD_ALWAYS_ON, - PD_SCU }, - { "ca7-cpu0", 0x1c0, 0, R8A77470_PD_CA7_CPU0, R8A77470_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu1", 0x1c0, 1, R8A77470_PD_CA7_CPU1, R8A77470_PD_CA7_SCU, - PD_CPU_NOCR }, - { "sgx", 0xc0, 0, R8A77470_PD_SGX, R8A77470_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a77470_sysc_info __initconst = { - .areas = r8a77470_areas, - .num_areas = ARRAY_SIZE(r8a77470_areas), -}; diff --git a/drivers/genpd/renesas/r8a774a1-sysc.c b/drivers/genpd/renesas/r8a774a1-sysc.c deleted file mode 100644 index 38ac2c689ff0..000000000000 --- a/drivers/genpd/renesas/r8a774a1-sysc.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G2M System Controller - * Copyright (C) 2018 Renesas Electronics Corp. - * - * Based on Renesas R-Car M3-W System Controller - * Copyright (C) 2016 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a774a1_areas[] __initconst = { - { "always-on", 0, 0, R8A774A1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca57-scu", 0x1c0, 0, R8A774A1_PD_CA57_SCU, R8A774A1_PD_ALWAYS_ON, - PD_SCU }, - { "ca57-cpu0", 0x80, 0, R8A774A1_PD_CA57_CPU0, R8A774A1_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu1", 0x80, 1, R8A774A1_PD_CA57_CPU1, R8A774A1_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca53-scu", 0x140, 0, R8A774A1_PD_CA53_SCU, R8A774A1_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A774A1_PD_CA53_CPU0, R8A774A1_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A774A1_PD_CA53_CPU1, R8A774A1_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu2", 0x200, 2, R8A774A1_PD_CA53_CPU2, R8A774A1_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu3", 0x200, 3, R8A774A1_PD_CA53_CPU3, R8A774A1_PD_CA53_SCU, - PD_CPU_NOCR }, - { "a3vc", 0x380, 0, R8A774A1_PD_A3VC, R8A774A1_PD_ALWAYS_ON }, - { "a2vc0", 0x3c0, 0, R8A774A1_PD_A2VC0, R8A774A1_PD_A3VC }, - { "a2vc1", 0x3c0, 1, R8A774A1_PD_A2VC1, R8A774A1_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A774A1_PD_3DG_A, R8A774A1_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A774A1_PD_3DG_B, R8A774A1_PD_3DG_A }, -}; - -const struct rcar_sysc_info r8a774a1_sysc_info __initconst = { - .areas = r8a774a1_areas, - .num_areas = ARRAY_SIZE(r8a774a1_areas), -}; diff --git a/drivers/genpd/renesas/r8a774b1-sysc.c b/drivers/genpd/renesas/r8a774b1-sysc.c deleted file mode 100644 index 5f97ff26f3f8..000000000000 --- a/drivers/genpd/renesas/r8a774b1-sysc.c +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G2N System Controller - * Copyright (C) 2019 Renesas Electronics Corp. - * - * Based on Renesas R-Car M3-W System Controller - * Copyright (C) 2016 Glider bvba - */ - -#include -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a774b1_areas[] __initconst = { - { "always-on", 0, 0, R8A774B1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca57-scu", 0x1c0, 0, R8A774B1_PD_CA57_SCU, R8A774B1_PD_ALWAYS_ON, - PD_SCU }, - { "ca57-cpu0", 0x80, 0, R8A774B1_PD_CA57_CPU0, R8A774B1_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu1", 0x80, 1, R8A774B1_PD_CA57_CPU1, R8A774B1_PD_CA57_SCU, - PD_CPU_NOCR }, - { "a3vc", 0x380, 0, R8A774B1_PD_A3VC, R8A774B1_PD_ALWAYS_ON }, - { "a3vp", 0x340, 0, R8A774B1_PD_A3VP, R8A774B1_PD_ALWAYS_ON }, - { "a2vc1", 0x3c0, 1, R8A774B1_PD_A2VC1, R8A774B1_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A774B1_PD_3DG_A, R8A774B1_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A774B1_PD_3DG_B, R8A774B1_PD_3DG_A }, -}; - -const struct rcar_sysc_info r8a774b1_sysc_info __initconst = { - .areas = r8a774b1_areas, - .num_areas = ARRAY_SIZE(r8a774b1_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a774c0-sysc.c b/drivers/genpd/renesas/r8a774c0-sysc.c deleted file mode 100644 index c1c216f7d073..000000000000 --- a/drivers/genpd/renesas/r8a774c0-sysc.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G2E System Controller - * Copyright (C) 2018 Renesas Electronics Corp. - * - * Based on Renesas R-Car E3 System Controller - */ - -#include -#include -#include - -#include - -#include "rcar-sysc.h" - -static struct rcar_sysc_area r8a774c0_areas[] __initdata = { - { "always-on", 0, 0, R8A774C0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca53-scu", 0x140, 0, R8A774C0_PD_CA53_SCU, R8A774C0_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A774C0_PD_CA53_CPU0, R8A774C0_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A774C0_PD_CA53_CPU1, R8A774C0_PD_CA53_SCU, - PD_CPU_NOCR }, - { "a3vc", 0x380, 0, R8A774C0_PD_A3VC, R8A774C0_PD_ALWAYS_ON }, - { "a2vc1", 0x3c0, 1, R8A774C0_PD_A2VC1, R8A774C0_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A774C0_PD_3DG_A, R8A774C0_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A774C0_PD_3DG_B, R8A774C0_PD_3DG_A }, -}; - -/* Fixups for RZ/G2E ES1.0 revision */ -static const struct soc_device_attribute r8a774c0[] __initconst = { - { .soc_id = "r8a774c0", .revision = "ES1.0" }, - { /* sentinel */ } -}; - -static int __init r8a774c0_sysc_init(void) -{ - if (soc_device_match(r8a774c0)) { - /* Fix incorrect 3DG hierarchy */ - swap(r8a774c0_areas[6], r8a774c0_areas[7]); - r8a774c0_areas[6].parent = R8A774C0_PD_ALWAYS_ON; - r8a774c0_areas[7].parent = R8A774C0_PD_3DG_B; - } - - return 0; -} - -const struct rcar_sysc_info r8a774c0_sysc_info __initconst = { - .init = r8a774c0_sysc_init, - .areas = r8a774c0_areas, - .num_areas = ARRAY_SIZE(r8a774c0_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a774e1-sysc.c b/drivers/genpd/renesas/r8a774e1-sysc.c deleted file mode 100644 index 18449f746455..000000000000 --- a/drivers/genpd/renesas/r8a774e1-sysc.c +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas RZ/G2H System Controller - * Copyright (C) 2020 Renesas Electronics Corp. - * - * Based on Renesas R-Car H3 System Controller - * Copyright (C) 2016-2017 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a774e1_areas[] __initconst = { - { "always-on", 0, 0, R8A774E1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca57-scu", 0x1c0, 0, R8A774E1_PD_CA57_SCU, R8A774E1_PD_ALWAYS_ON, PD_SCU }, - { "ca57-cpu0", 0x80, 0, R8A774E1_PD_CA57_CPU0, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, - { "ca57-cpu1", 0x80, 1, R8A774E1_PD_CA57_CPU1, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, - { "ca57-cpu2", 0x80, 2, R8A774E1_PD_CA57_CPU2, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, - { "ca57-cpu3", 0x80, 3, R8A774E1_PD_CA57_CPU3, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, - { "ca53-scu", 0x140, 0, R8A774E1_PD_CA53_SCU, R8A774E1_PD_ALWAYS_ON, PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A774E1_PD_CA53_CPU0, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A774E1_PD_CA53_CPU1, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, - { "ca53-cpu2", 0x200, 2, R8A774E1_PD_CA53_CPU2, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, - { "ca53-cpu3", 0x200, 3, R8A774E1_PD_CA53_CPU3, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, - { "a3vp", 0x340, 0, R8A774E1_PD_A3VP, R8A774E1_PD_ALWAYS_ON }, - { "a3vc", 0x380, 0, R8A774E1_PD_A3VC, R8A774E1_PD_ALWAYS_ON }, - { "a2vc1", 0x3c0, 1, R8A774E1_PD_A2VC1, R8A774E1_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A774E1_PD_3DG_A, R8A774E1_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A774E1_PD_3DG_B, R8A774E1_PD_3DG_A }, - { "3dg-c", 0x100, 2, R8A774E1_PD_3DG_C, R8A774E1_PD_3DG_B }, - { "3dg-d", 0x100, 3, R8A774E1_PD_3DG_D, R8A774E1_PD_3DG_C }, - { "3dg-e", 0x100, 4, R8A774E1_PD_3DG_E, R8A774E1_PD_3DG_D }, -}; - -const struct rcar_sysc_info r8a774e1_sysc_info __initconst = { - .areas = r8a774e1_areas, - .num_areas = ARRAY_SIZE(r8a774e1_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a7779-sysc.c b/drivers/genpd/renesas/r8a7779-sysc.c deleted file mode 100644 index e24a7151d55f..000000000000 --- a/drivers/genpd/renesas/r8a7779-sysc.c +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car H1 System Controller - * - * Copyright (C) 2016 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7779_areas[] __initconst = { - { "always-on", 0, 0, R8A7779_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "arm1", 0x40, 1, R8A7779_PD_ARM1, R8A7779_PD_ALWAYS_ON, - PD_CPU_CR }, - { "arm2", 0x40, 2, R8A7779_PD_ARM2, R8A7779_PD_ALWAYS_ON, - PD_CPU_CR }, - { "arm3", 0x40, 3, R8A7779_PD_ARM3, R8A7779_PD_ALWAYS_ON, - PD_CPU_CR }, - { "sgx", 0xc0, 0, R8A7779_PD_SGX, R8A7779_PD_ALWAYS_ON }, - { "vdp", 0x100, 0, R8A7779_PD_VDP, R8A7779_PD_ALWAYS_ON }, - { "imp", 0x140, 0, R8A7779_PD_IMP, R8A7779_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7779_sysc_info __initconst = { - .areas = r8a7779_areas, - .num_areas = ARRAY_SIZE(r8a7779_areas), -}; diff --git a/drivers/genpd/renesas/r8a7790-sysc.c b/drivers/genpd/renesas/r8a7790-sysc.c deleted file mode 100644 index b9afe7f6245b..000000000000 --- a/drivers/genpd/renesas/r8a7790-sysc.c +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car H2 System Controller - * - * Copyright (C) 2016 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7790_areas[] __initconst = { - { "always-on", 0, 0, R8A7790_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca15-scu", 0x180, 0, R8A7790_PD_CA15_SCU, R8A7790_PD_ALWAYS_ON, - PD_SCU }, - { "ca15-cpu0", 0x40, 0, R8A7790_PD_CA15_CPU0, R8A7790_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu1", 0x40, 1, R8A7790_PD_CA15_CPU1, R8A7790_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu2", 0x40, 2, R8A7790_PD_CA15_CPU2, R8A7790_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu3", 0x40, 3, R8A7790_PD_CA15_CPU3, R8A7790_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca7-scu", 0x100, 0, R8A7790_PD_CA7_SCU, R8A7790_PD_ALWAYS_ON, - PD_SCU }, - { "ca7-cpu0", 0x1c0, 0, R8A7790_PD_CA7_CPU0, R8A7790_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu1", 0x1c0, 1, R8A7790_PD_CA7_CPU1, R8A7790_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu2", 0x1c0, 2, R8A7790_PD_CA7_CPU2, R8A7790_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu3", 0x1c0, 3, R8A7790_PD_CA7_CPU3, R8A7790_PD_CA7_SCU, - PD_CPU_NOCR }, - { "sh-4a", 0x80, 0, R8A7790_PD_SH_4A, R8A7790_PD_ALWAYS_ON }, - { "rgx", 0xc0, 0, R8A7790_PD_RGX, R8A7790_PD_ALWAYS_ON }, - { "imp", 0x140, 0, R8A7790_PD_IMP, R8A7790_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7790_sysc_info __initconst = { - .areas = r8a7790_areas, - .num_areas = ARRAY_SIZE(r8a7790_areas), -}; diff --git a/drivers/genpd/renesas/r8a7791-sysc.c b/drivers/genpd/renesas/r8a7791-sysc.c deleted file mode 100644 index f00fa24522a3..000000000000 --- a/drivers/genpd/renesas/r8a7791-sysc.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car M2-W/N System Controller - * - * Copyright (C) 2016 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7791_areas[] __initconst = { - { "always-on", 0, 0, R8A7791_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca15-scu", 0x180, 0, R8A7791_PD_CA15_SCU, R8A7791_PD_ALWAYS_ON, - PD_SCU }, - { "ca15-cpu0", 0x40, 0, R8A7791_PD_CA15_CPU0, R8A7791_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu1", 0x40, 1, R8A7791_PD_CA15_CPU1, R8A7791_PD_CA15_SCU, - PD_CPU_NOCR }, - { "sh-4a", 0x80, 0, R8A7791_PD_SH_4A, R8A7791_PD_ALWAYS_ON }, - { "sgx", 0xc0, 0, R8A7791_PD_SGX, R8A7791_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7791_sysc_info __initconst = { - .areas = r8a7791_areas, - .num_areas = ARRAY_SIZE(r8a7791_areas), -}; diff --git a/drivers/genpd/renesas/r8a7792-sysc.c b/drivers/genpd/renesas/r8a7792-sysc.c deleted file mode 100644 index 60aae242c43f..000000000000 --- a/drivers/genpd/renesas/r8a7792-sysc.c +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car V2H (R8A7792) System Controller - * - * Copyright (C) 2016 Cogent Embedded Inc. - */ - -#include -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7792_areas[] __initconst = { - { "always-on", 0, 0, R8A7792_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca15-scu", 0x180, 0, R8A7792_PD_CA15_SCU, R8A7792_PD_ALWAYS_ON, - PD_SCU }, - { "ca15-cpu0", 0x40, 0, R8A7792_PD_CA15_CPU0, R8A7792_PD_CA15_SCU, - PD_CPU_NOCR }, - { "ca15-cpu1", 0x40, 1, R8A7792_PD_CA15_CPU1, R8A7792_PD_CA15_SCU, - PD_CPU_NOCR }, - { "sgx", 0xc0, 0, R8A7792_PD_SGX, R8A7792_PD_ALWAYS_ON }, - { "imp", 0x140, 0, R8A7792_PD_IMP, R8A7792_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7792_sysc_info __initconst = { - .areas = r8a7792_areas, - .num_areas = ARRAY_SIZE(r8a7792_areas), -}; diff --git a/drivers/genpd/renesas/r8a7794-sysc.c b/drivers/genpd/renesas/r8a7794-sysc.c deleted file mode 100644 index 72ef4e85458f..000000000000 --- a/drivers/genpd/renesas/r8a7794-sysc.c +++ /dev/null @@ -1,29 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car E2 System Controller - * - * Copyright (C) 2016 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a7794_areas[] __initconst = { - { "always-on", 0, 0, R8A7794_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca7-scu", 0x100, 0, R8A7794_PD_CA7_SCU, R8A7794_PD_ALWAYS_ON, - PD_SCU }, - { "ca7-cpu0", 0x1c0, 0, R8A7794_PD_CA7_CPU0, R8A7794_PD_CA7_SCU, - PD_CPU_NOCR }, - { "ca7-cpu1", 0x1c0, 1, R8A7794_PD_CA7_CPU1, R8A7794_PD_CA7_SCU, - PD_CPU_NOCR }, - { "sh-4a", 0x80, 0, R8A7794_PD_SH_4A, R8A7794_PD_ALWAYS_ON }, - { "sgx", 0xc0, 0, R8A7794_PD_SGX, R8A7794_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a7794_sysc_info __initconst = { - .areas = r8a7794_areas, - .num_areas = ARRAY_SIZE(r8a7794_areas), -}; diff --git a/drivers/genpd/renesas/r8a7795-sysc.c b/drivers/genpd/renesas/r8a7795-sysc.c deleted file mode 100644 index cbe1ff0fc583..000000000000 --- a/drivers/genpd/renesas/r8a7795-sysc.c +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car H3 System Controller - * - * Copyright (C) 2016-2017 Glider bvba - */ - -#include -#include -#include - -#include - -#include "rcar-sysc.h" - -static struct rcar_sysc_area r8a7795_areas[] __initdata = { - { "always-on", 0, 0, R8A7795_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca57-scu", 0x1c0, 0, R8A7795_PD_CA57_SCU, R8A7795_PD_ALWAYS_ON, - PD_SCU }, - { "ca57-cpu0", 0x80, 0, R8A7795_PD_CA57_CPU0, R8A7795_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu1", 0x80, 1, R8A7795_PD_CA57_CPU1, R8A7795_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu2", 0x80, 2, R8A7795_PD_CA57_CPU2, R8A7795_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu3", 0x80, 3, R8A7795_PD_CA57_CPU3, R8A7795_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca53-scu", 0x140, 0, R8A7795_PD_CA53_SCU, R8A7795_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A7795_PD_CA53_CPU0, R8A7795_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A7795_PD_CA53_CPU1, R8A7795_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu2", 0x200, 2, R8A7795_PD_CA53_CPU2, R8A7795_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu3", 0x200, 3, R8A7795_PD_CA53_CPU3, R8A7795_PD_CA53_SCU, - PD_CPU_NOCR }, - { "a3vp", 0x340, 0, R8A7795_PD_A3VP, R8A7795_PD_ALWAYS_ON }, - { "cr7", 0x240, 0, R8A7795_PD_CR7, R8A7795_PD_ALWAYS_ON }, - { "a3vc", 0x380, 0, R8A7795_PD_A3VC, R8A7795_PD_ALWAYS_ON }, - { "a2vc1", 0x3c0, 1, R8A7795_PD_A2VC1, R8A7795_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A7795_PD_3DG_A, R8A7795_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A7795_PD_3DG_B, R8A7795_PD_3DG_A }, - { "3dg-c", 0x100, 2, R8A7795_PD_3DG_C, R8A7795_PD_3DG_B }, - { "3dg-d", 0x100, 3, R8A7795_PD_3DG_D, R8A7795_PD_3DG_C }, - { "3dg-e", 0x100, 4, R8A7795_PD_3DG_E, R8A7795_PD_3DG_D }, - { "a3ir", 0x180, 0, R8A7795_PD_A3IR, R8A7795_PD_ALWAYS_ON }, -}; - - - /* - * Fixups for R-Car H3 revisions - */ - -#define NO_EXTMASK BIT(1) /* Missing SYSCEXTMASK register */ - -static const struct soc_device_attribute r8a7795_quirks_match[] __initconst = { - { - .soc_id = "r8a7795", .revision = "ES2.*", - .data = (void *)(NO_EXTMASK), - }, - { /* sentinel */ } -}; - -static int __init r8a7795_sysc_init(void) -{ - const struct soc_device_attribute *attr; - u32 quirks = 0; - - attr = soc_device_match(r8a7795_quirks_match); - if (attr) - quirks = (uintptr_t)attr->data; - - if (quirks & NO_EXTMASK) - r8a7795_sysc_info.extmask_val = 0; - - return 0; -} - -struct rcar_sysc_info r8a7795_sysc_info __initdata = { - .init = r8a7795_sysc_init, - .areas = r8a7795_areas, - .num_areas = ARRAY_SIZE(r8a7795_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a7796-sysc.c b/drivers/genpd/renesas/r8a7796-sysc.c deleted file mode 100644 index 471bd5b3b6ad..000000000000 --- a/drivers/genpd/renesas/r8a7796-sysc.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car M3-W/W+ System Controller - * - * Copyright (C) 2016 Glider bvba - * Copyright (C) 2018-2019 Renesas Electronics Corporation - */ - -#include -#include - -#include - -#include "rcar-sysc.h" - -static struct rcar_sysc_area r8a7796_areas[] __initdata = { - { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, - PD_SCU }, - { "ca57-cpu0", 0x80, 0, R8A7796_PD_CA57_CPU0, R8A7796_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu1", 0x80, 1, R8A7796_PD_CA57_CPU1, R8A7796_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca53-scu", 0x140, 0, R8A7796_PD_CA53_SCU, R8A7796_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A7796_PD_CA53_CPU0, R8A7796_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A7796_PD_CA53_CPU1, R8A7796_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu2", 0x200, 2, R8A7796_PD_CA53_CPU2, R8A7796_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu3", 0x200, 3, R8A7796_PD_CA53_CPU3, R8A7796_PD_CA53_SCU, - PD_CPU_NOCR }, - { "cr7", 0x240, 0, R8A7796_PD_CR7, R8A7796_PD_ALWAYS_ON }, - { "a3vc", 0x380, 0, R8A7796_PD_A3VC, R8A7796_PD_ALWAYS_ON }, - { "a2vc0", 0x3c0, 0, R8A7796_PD_A2VC0, R8A7796_PD_A3VC }, - { "a2vc1", 0x3c0, 1, R8A7796_PD_A2VC1, R8A7796_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A7796_PD_3DG_A, R8A7796_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A7796_PD_3DG_B, R8A7796_PD_3DG_A }, - { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON }, -}; - - -#ifdef CONFIG_SYSC_R8A77960 -const struct rcar_sysc_info r8a77960_sysc_info __initconst = { - .areas = r8a7796_areas, - .num_areas = ARRAY_SIZE(r8a7796_areas), -}; -#endif /* CONFIG_SYSC_R8A77960 */ - -#ifdef CONFIG_SYSC_R8A77961 -static int __init r8a77961_sysc_init(void) -{ - rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas), - R8A7796_PD_A2VC0); - - return 0; -} - -const struct rcar_sysc_info r8a77961_sysc_info __initconst = { - .init = r8a77961_sysc_init, - .areas = r8a7796_areas, - .num_areas = ARRAY_SIZE(r8a7796_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; -#endif /* CONFIG_SYSC_R8A77961 */ diff --git a/drivers/genpd/renesas/r8a77965-sysc.c b/drivers/genpd/renesas/r8a77965-sysc.c deleted file mode 100644 index ff0b0d116992..000000000000 --- a/drivers/genpd/renesas/r8a77965-sysc.c +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car M3-N System Controller - * Copyright (C) 2018 Jacopo Mondi - * - * Based on Renesas R-Car M3-W System Controller - * Copyright (C) 2016 Glider bvba - */ - -#include -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a77965_areas[] __initconst = { - { "always-on", 0, 0, R8A77965_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca57-scu", 0x1c0, 0, R8A77965_PD_CA57_SCU, R8A77965_PD_ALWAYS_ON, - PD_SCU }, - { "ca57-cpu0", 0x80, 0, R8A77965_PD_CA57_CPU0, R8A77965_PD_CA57_SCU, - PD_CPU_NOCR }, - { "ca57-cpu1", 0x80, 1, R8A77965_PD_CA57_CPU1, R8A77965_PD_CA57_SCU, - PD_CPU_NOCR }, - { "cr7", 0x240, 0, R8A77965_PD_CR7, R8A77965_PD_ALWAYS_ON }, - { "a3vc", 0x380, 0, R8A77965_PD_A3VC, R8A77965_PD_ALWAYS_ON }, - { "a3vp", 0x340, 0, R8A77965_PD_A3VP, R8A77965_PD_ALWAYS_ON }, - { "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A77965_PD_3DG_B, R8A77965_PD_3DG_A }, -}; - -const struct rcar_sysc_info r8a77965_sysc_info __initconst = { - .areas = r8a77965_areas, - .num_areas = ARRAY_SIZE(r8a77965_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a77970-sysc.c b/drivers/genpd/renesas/r8a77970-sysc.c deleted file mode 100644 index 706258250600..000000000000 --- a/drivers/genpd/renesas/r8a77970-sysc.c +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car V3M System Controller - * - * Copyright (C) 2017 Cogent Embedded Inc. - */ - -#include -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a77970_areas[] __initconst = { - { "always-on", 0, 0, R8A77970_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca53-scu", 0x140, 0, R8A77970_PD_CA53_SCU, R8A77970_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A77970_PD_CA53_CPU0, R8A77970_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU, - PD_CPU_NOCR }, - { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, - { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR }, - { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR }, - { "a2dp", 0x400, 2, R8A77970_PD_A2DP, R8A77970_PD_A3IR }, - { "a2cn", 0x400, 3, R8A77970_PD_A2CN, R8A77970_PD_A3IR }, - { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR }, - { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR }, -}; - -const struct rcar_sysc_info r8a77970_sysc_info __initconst = { - .areas = r8a77970_areas, - .num_areas = ARRAY_SIZE(r8a77970_areas), - .extmask_offs = 0x1b0, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a77980-sysc.c b/drivers/genpd/renesas/r8a77980-sysc.c deleted file mode 100644 index 39ca84a67daa..000000000000 --- a/drivers/genpd/renesas/r8a77980-sysc.c +++ /dev/null @@ -1,54 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car V3H System Controller - * - * Copyright (C) 2018 Renesas Electronics Corp. - * Copyright (C) 2018 Cogent Embedded, Inc. - */ - -#include -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a77980_areas[] __initconst = { - { "always-on", 0, 0, R8A77980_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca53-scu", 0x140, 0, R8A77980_PD_CA53_SCU, R8A77980_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A77980_PD_CA53_CPU0, R8A77980_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A77980_PD_CA53_CPU1, R8A77980_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu2", 0x200, 2, R8A77980_PD_CA53_CPU2, R8A77980_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu3", 0x200, 3, R8A77980_PD_CA53_CPU3, R8A77980_PD_CA53_SCU, - PD_CPU_NOCR }, - { "cr7", 0x240, 0, R8A77980_PD_CR7, R8A77980_PD_ALWAYS_ON }, - { "a3ir", 0x180, 0, R8A77980_PD_A3IR, R8A77980_PD_ALWAYS_ON }, - { "a2ir0", 0x400, 0, R8A77980_PD_A2IR0, R8A77980_PD_A3IR }, - { "a2ir1", 0x400, 1, R8A77980_PD_A2IR1, R8A77980_PD_A3IR }, - { "a2ir2", 0x400, 2, R8A77980_PD_A2IR2, R8A77980_PD_A3IR }, - { "a2ir3", 0x400, 3, R8A77980_PD_A2IR3, R8A77980_PD_A3IR }, - { "a2ir4", 0x400, 4, R8A77980_PD_A2IR4, R8A77980_PD_A3IR }, - { "a2ir5", 0x400, 5, R8A77980_PD_A2IR5, R8A77980_PD_A3IR }, - { "a2sc0", 0x400, 6, R8A77980_PD_A2SC0, R8A77980_PD_A3IR }, - { "a2sc1", 0x400, 7, R8A77980_PD_A2SC1, R8A77980_PD_A3IR }, - { "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR }, - { "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR }, - { "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR }, - { "a2dp0", 0x400, 11, R8A77980_PD_A2DP0, R8A77980_PD_A3IR }, - { "a2dp1", 0x400, 12, R8A77980_PD_A2DP1, R8A77980_PD_A3IR }, - { "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR }, - { "a3vip0", 0x2c0, 0, R8A77980_PD_A3VIP0, R8A77980_PD_ALWAYS_ON }, - { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_ALWAYS_ON }, - { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_ALWAYS_ON }, -}; - -const struct rcar_sysc_info r8a77980_sysc_info __initconst = { - .areas = r8a77980_areas, - .num_areas = ARRAY_SIZE(r8a77980_areas), - .extmask_offs = 0x138, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a77990-sysc.c b/drivers/genpd/renesas/r8a77990-sysc.c deleted file mode 100644 index 9f92737dc352..000000000000 --- a/drivers/genpd/renesas/r8a77990-sysc.c +++ /dev/null @@ -1,55 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car E3 System Controller - * - * Copyright (C) 2018 Renesas Electronics Corp. - */ - -#include -#include -#include - -#include - -#include "rcar-sysc.h" - -static struct rcar_sysc_area r8a77990_areas[] __initdata = { - { "always-on", 0, 0, R8A77990_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca53-scu", 0x140, 0, R8A77990_PD_CA53_SCU, R8A77990_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A77990_PD_CA53_CPU0, R8A77990_PD_CA53_SCU, - PD_CPU_NOCR }, - { "ca53-cpu1", 0x200, 1, R8A77990_PD_CA53_CPU1, R8A77990_PD_CA53_SCU, - PD_CPU_NOCR }, - { "cr7", 0x240, 0, R8A77990_PD_CR7, R8A77990_PD_ALWAYS_ON }, - { "a3vc", 0x380, 0, R8A77990_PD_A3VC, R8A77990_PD_ALWAYS_ON }, - { "a2vc1", 0x3c0, 1, R8A77990_PD_A2VC1, R8A77990_PD_A3VC }, - { "3dg-a", 0x100, 0, R8A77990_PD_3DG_A, R8A77990_PD_ALWAYS_ON }, - { "3dg-b", 0x100, 1, R8A77990_PD_3DG_B, R8A77990_PD_3DG_A }, -}; - -/* Fixups for R-Car E3 ES1.0 revision */ -static const struct soc_device_attribute r8a77990[] __initconst = { - { .soc_id = "r8a77990", .revision = "ES1.0" }, - { /* sentinel */ } -}; - -static int __init r8a77990_sysc_init(void) -{ - if (soc_device_match(r8a77990)) { - /* Fix incorrect 3DG hierarchy */ - swap(r8a77990_areas[7], r8a77990_areas[8]); - r8a77990_areas[7].parent = R8A77990_PD_ALWAYS_ON; - r8a77990_areas[8].parent = R8A77990_PD_3DG_B; - } - - return 0; -} - -const struct rcar_sysc_info r8a77990_sysc_info __initconst = { - .init = r8a77990_sysc_init, - .areas = r8a77990_areas, - .num_areas = ARRAY_SIZE(r8a77990_areas), - .extmask_offs = 0x2f8, - .extmask_val = BIT(0), -}; diff --git a/drivers/genpd/renesas/r8a77995-sysc.c b/drivers/genpd/renesas/r8a77995-sysc.c deleted file mode 100644 index efcc67e3d76d..000000000000 --- a/drivers/genpd/renesas/r8a77995-sysc.c +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car D3 System Controller - * - * Copyright (C) 2017 Glider bvba - */ - -#include - -#include - -#include "rcar-sysc.h" - -static const struct rcar_sysc_area r8a77995_areas[] __initconst = { - { "always-on", 0, 0, R8A77995_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "ca53-scu", 0x140, 0, R8A77995_PD_CA53_SCU, R8A77995_PD_ALWAYS_ON, - PD_SCU }, - { "ca53-cpu0", 0x200, 0, R8A77995_PD_CA53_CPU0, R8A77995_PD_CA53_SCU, - PD_CPU_NOCR }, -}; - - -const struct rcar_sysc_info r8a77995_sysc_info __initconst = { - .areas = r8a77995_areas, - .num_areas = ARRAY_SIZE(r8a77995_areas), -}; diff --git a/drivers/genpd/renesas/r8a779a0-sysc.c b/drivers/genpd/renesas/r8a779a0-sysc.c deleted file mode 100644 index 04f1bc322ae7..000000000000 --- a/drivers/genpd/renesas/r8a779a0-sysc.c +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car V3U System Controller - * - * Copyright (C) 2020 Renesas Electronics Corp. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "rcar-gen4-sysc.h" - -static struct rcar_gen4_sysc_area r8a779a0_areas[] __initdata = { - { "always-on", R8A779A0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "a3e0", R8A779A0_PD_A3E0, R8A779A0_PD_ALWAYS_ON, PD_SCU }, - { "a3e1", R8A779A0_PD_A3E1, R8A779A0_PD_ALWAYS_ON, PD_SCU }, - { "a2e0d0", R8A779A0_PD_A2E0D0, R8A779A0_PD_A3E0, PD_SCU }, - { "a2e0d1", R8A779A0_PD_A2E0D1, R8A779A0_PD_A3E0, PD_SCU }, - { "a2e1d0", R8A779A0_PD_A2E1D0, R8A779A0_PD_A3E1, PD_SCU }, - { "a2e1d1", R8A779A0_PD_A2E1D1, R8A779A0_PD_A3E1, PD_SCU }, - { "a1e0d0c0", R8A779A0_PD_A1E0D0C0, R8A779A0_PD_A2E0D0, PD_CPU_NOCR }, - { "a1e0d0c1", R8A779A0_PD_A1E0D0C1, R8A779A0_PD_A2E0D0, PD_CPU_NOCR }, - { "a1e0d1c0", R8A779A0_PD_A1E0D1C0, R8A779A0_PD_A2E0D1, PD_CPU_NOCR }, - { "a1e0d1c1", R8A779A0_PD_A1E0D1C1, R8A779A0_PD_A2E0D1, PD_CPU_NOCR }, - { "a1e1d0c0", R8A779A0_PD_A1E1D0C0, R8A779A0_PD_A2E1D0, PD_CPU_NOCR }, - { "a1e1d0c1", R8A779A0_PD_A1E1D0C1, R8A779A0_PD_A2E1D0, PD_CPU_NOCR }, - { "a1e1d1c0", R8A779A0_PD_A1E1D1C0, R8A779A0_PD_A2E1D1, PD_CPU_NOCR }, - { "a1e1d1c1", R8A779A0_PD_A1E1D1C1, R8A779A0_PD_A2E1D1, PD_CPU_NOCR }, - { "3dg-a", R8A779A0_PD_3DG_A, R8A779A0_PD_ALWAYS_ON }, - { "3dg-b", R8A779A0_PD_3DG_B, R8A779A0_PD_3DG_A }, - { "a3vip0", R8A779A0_PD_A3VIP0, R8A779A0_PD_ALWAYS_ON }, - { "a3vip1", R8A779A0_PD_A3VIP1, R8A779A0_PD_ALWAYS_ON }, - { "a3vip3", R8A779A0_PD_A3VIP3, R8A779A0_PD_ALWAYS_ON }, - { "a3vip2", R8A779A0_PD_A3VIP2, R8A779A0_PD_ALWAYS_ON }, - { "a3isp01", R8A779A0_PD_A3ISP01, R8A779A0_PD_ALWAYS_ON }, - { "a3isp23", R8A779A0_PD_A3ISP23, R8A779A0_PD_ALWAYS_ON }, - { "a3ir", R8A779A0_PD_A3IR, R8A779A0_PD_ALWAYS_ON }, - { "a2cn0", R8A779A0_PD_A2CN0, R8A779A0_PD_A3IR }, - { "a2imp01", R8A779A0_PD_A2IMP01, R8A779A0_PD_A3IR }, - { "a2dp0", R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR }, - { "a2cv0", R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR }, - { "a2cv1", R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR }, - { "a2cv4", R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR }, - { "a2cv6", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR }, - { "a2cn2", R8A779A0_PD_A2CN2, R8A779A0_PD_A3IR }, - { "a2imp23", R8A779A0_PD_A2IMP23, R8A779A0_PD_A3IR }, - { "a2dp1", R8A779A0_PD_A2DP1, R8A779A0_PD_A3IR }, - { "a2cv2", R8A779A0_PD_A2CV2, R8A779A0_PD_A3IR }, - { "a2cv3", R8A779A0_PD_A2CV3, R8A779A0_PD_A3IR }, - { "a2cv5", R8A779A0_PD_A2CV5, R8A779A0_PD_A3IR }, - { "a2cv7", R8A779A0_PD_A2CV7, R8A779A0_PD_A3IR }, - { "a2cn1", R8A779A0_PD_A2CN1, R8A779A0_PD_A3IR }, - { "a1cnn0", R8A779A0_PD_A1CNN0, R8A779A0_PD_A2CN0 }, - { "a1cnn2", R8A779A0_PD_A1CNN2, R8A779A0_PD_A2CN2 }, - { "a1dsp0", R8A779A0_PD_A1DSP0, R8A779A0_PD_A2CN2 }, - { "a1cnn1", R8A779A0_PD_A1CNN1, R8A779A0_PD_A2CN1 }, - { "a1dsp1", R8A779A0_PD_A1DSP1, R8A779A0_PD_A2CN1 }, -}; - -const struct rcar_gen4_sysc_info r8a779a0_sysc_info __initconst = { - .areas = r8a779a0_areas, - .num_areas = ARRAY_SIZE(r8a779a0_areas), -}; diff --git a/drivers/genpd/renesas/r8a779f0-sysc.c b/drivers/genpd/renesas/r8a779f0-sysc.c deleted file mode 100644 index 5602aa6bd7ed..000000000000 --- a/drivers/genpd/renesas/r8a779f0-sysc.c +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car S4-8 System Controller - * - * Copyright (C) 2021 Renesas Electronics Corp. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "rcar-gen4-sysc.h" - -static struct rcar_gen4_sysc_area r8a779f0_areas[] __initdata = { - { "always-on", R8A779F0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "a3e0", R8A779F0_PD_A3E0, R8A779F0_PD_ALWAYS_ON, PD_SCU }, - { "a3e1", R8A779F0_PD_A3E1, R8A779F0_PD_ALWAYS_ON, PD_SCU }, - { "a2e0d0", R8A779F0_PD_A2E0D0, R8A779F0_PD_A3E0, PD_SCU }, - { "a2e0d1", R8A779F0_PD_A2E0D1, R8A779F0_PD_A3E0, PD_SCU }, - { "a2e1d0", R8A779F0_PD_A2E1D0, R8A779F0_PD_A3E1, PD_SCU }, - { "a2e1d1", R8A779F0_PD_A2E1D1, R8A779F0_PD_A3E1, PD_SCU }, - { "a1e0d0c0", R8A779F0_PD_A1E0D0C0, R8A779F0_PD_A2E0D0, PD_CPU_NOCR }, - { "a1e0d0c1", R8A779F0_PD_A1E0D0C1, R8A779F0_PD_A2E0D0, PD_CPU_NOCR }, - { "a1e0d1c0", R8A779F0_PD_A1E0D1C0, R8A779F0_PD_A2E0D1, PD_CPU_NOCR }, - { "a1e0d1c1", R8A779F0_PD_A1E0D1C1, R8A779F0_PD_A2E0D1, PD_CPU_NOCR }, - { "a1e1d0c0", R8A779F0_PD_A1E1D0C0, R8A779F0_PD_A2E1D0, PD_CPU_NOCR }, - { "a1e1d0c1", R8A779F0_PD_A1E1D0C1, R8A779F0_PD_A2E1D0, PD_CPU_NOCR }, - { "a1e1d1c0", R8A779F0_PD_A1E1D1C0, R8A779F0_PD_A2E1D1, PD_CPU_NOCR }, - { "a1e1d1c1", R8A779F0_PD_A1E1D1C1, R8A779F0_PD_A2E1D1, PD_CPU_NOCR }, -}; - -const struct rcar_gen4_sysc_info r8a779f0_sysc_info __initconst = { - .areas = r8a779f0_areas, - .num_areas = ARRAY_SIZE(r8a779f0_areas), -}; diff --git a/drivers/genpd/renesas/r8a779g0-sysc.c b/drivers/genpd/renesas/r8a779g0-sysc.c deleted file mode 100644 index b932eba1b804..000000000000 --- a/drivers/genpd/renesas/r8a779g0-sysc.c +++ /dev/null @@ -1,63 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * Renesas R-Car V4H System Controller - * - * Copyright (C) 2022 Renesas Electronics Corp. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "rcar-gen4-sysc.h" - -static struct rcar_gen4_sysc_area r8a779g0_areas[] __initdata = { - { "always-on", R8A779G0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, - { "a3e0", R8A779G0_PD_A3E0, R8A779G0_PD_ALWAYS_ON, PD_SCU }, - { "a2e0d0", R8A779G0_PD_A2E0D0, R8A779G0_PD_A3E0, PD_SCU }, - { "a2e0d1", R8A779G0_PD_A2E0D1, R8A779G0_PD_A3E0, PD_SCU }, - { "a1e0d0c0", R8A779G0_PD_A1E0D0C0, R8A779G0_PD_A2E0D0, PD_CPU_NOCR }, - { "a1e0d0c1", R8A779G0_PD_A1E0D0C1, R8A779G0_PD_A2E0D0, PD_CPU_NOCR }, - { "a1e0d1c0", R8A779G0_PD_A1E0D1C0, R8A779G0_PD_A2E0D1, PD_CPU_NOCR }, - { "a1e0d1c1", R8A779G0_PD_A1E0D1C1, R8A779G0_PD_A2E0D1, PD_CPU_NOCR }, - { "a33dga", R8A779G0_PD_A33DGA, R8A779G0_PD_ALWAYS_ON }, - { "a23dgb", R8A779G0_PD_A23DGB, R8A779G0_PD_A33DGA }, - { "a3vip0", R8A779G0_PD_A3VIP0, R8A779G0_PD_ALWAYS_ON }, - { "a3vip1", R8A779G0_PD_A3VIP1, R8A779G0_PD_ALWAYS_ON }, - { "a3vip2", R8A779G0_PD_A3VIP2, R8A779G0_PD_ALWAYS_ON }, - { "a3dul", R8A779G0_PD_A3DUL, R8A779G0_PD_ALWAYS_ON }, - { "a3isp0", R8A779G0_PD_A3ISP0, R8A779G0_PD_ALWAYS_ON }, - { "a3isp1", R8A779G0_PD_A3ISP1, R8A779G0_PD_ALWAYS_ON }, - { "a3ir", R8A779G0_PD_A3IR, R8A779G0_PD_ALWAYS_ON }, - { "a2cn0", R8A779G0_PD_A2CN0, R8A779G0_PD_A3IR }, - { "a1cnn0", R8A779G0_PD_A1CNN0, R8A779G0_PD_A2CN0 }, - { "a1dsp0", R8A779G0_PD_A1DSP0, R8A779G0_PD_A2CN0 }, - { "a1dsp1", R8A779G0_PD_A1DSP1, R8A779G0_PD_A2CN0 }, - { "a1dsp2", R8A779G0_PD_A1DSP2, R8A779G0_PD_A2CN0 }, - { "a1dsp3", R8A779G0_PD_A1DSP3, R8A779G0_PD_A2CN0 }, - { "a2imp01", R8A779G0_PD_A2IMP01, R8A779G0_PD_A3IR }, - { "a2imp23", R8A779G0_PD_A2IMP23, R8A779G0_PD_A3IR }, - { "a2psc", R8A779G0_PD_A2PSC, R8A779G0_PD_A3IR }, - { "a2dma", R8A779G0_PD_A2DMA, R8A779G0_PD_A3IR }, - { "a2cv0", R8A779G0_PD_A2CV0, R8A779G0_PD_A3IR }, - { "a2cv1", R8A779G0_PD_A2CV1, R8A779G0_PD_A3IR }, - { "a2cv2", R8A779G0_PD_A2CV2, R8A779G0_PD_A3IR }, - { "a2cv3", R8A779G0_PD_A2CV3, R8A779G0_PD_A3IR }, -}; - -const struct rcar_gen4_sysc_info r8a779g0_sysc_info __initconst = { - .areas = r8a779g0_areas, - .num_areas = ARRAY_SIZE(r8a779g0_areas), -}; diff --git a/drivers/genpd/renesas/rcar-gen4-sysc.c b/drivers/genpd/renesas/rcar-gen4-sysc.c deleted file mode 100644 index 9e5e6e077abc..000000000000 --- a/drivers/genpd/renesas/rcar-gen4-sysc.c +++ /dev/null @@ -1,379 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * R-Car Gen4 SYSC Power management support - * - * Copyright (C) 2021 Renesas Electronics Corp. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rcar-gen4-sysc.h" - -/* SYSC Common */ -#define SYSCSR 0x000 /* SYSC Status Register */ -#define SYSCPONSR(x) (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */ -#define SYSCPOFFSR(x) (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */ -#define SYSCISCR(x) (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */ -#define SYSCIER(x) (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */ -#define SYSCIMR(x) (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */ - -/* Power Domain Registers */ -#define PDRSR(n) (0x1000 + ((n) * 0x40)) -#define PDRONCR(n) (0x1004 + ((n) * 0x40)) -#define PDROFFCR(n) (0x1008 + ((n) * 0x40)) -#define PDRESR(n) (0x100C + ((n) * 0x40)) - -/* PWRON/PWROFF */ -#define PWRON_PWROFF BIT(0) /* Power-ON/OFF request */ - -/* PDRESR */ -#define PDRESR_ERR BIT(0) - -/* PDRSR */ -#define PDRSR_OFF BIT(0) /* Power-OFF state */ -#define PDRSR_ON BIT(4) /* Power-ON state */ -#define PDRSR_OFF_STATE BIT(8) /* Processing Power-OFF sequence */ -#define PDRSR_ON_STATE BIT(12) /* Processing Power-ON sequence */ - -#define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */ - -#define SYSCSR_TIMEOUT 10000 -#define SYSCSR_DELAY_US 10 - -#define PDRESR_RETRIES 1000 -#define PDRESR_DELAY_US 10 - -#define SYSCISR_TIMEOUT 10000 -#define SYSCISR_DELAY_US 10 - -#define RCAR_GEN4_PD_ALWAYS_ON 64 -#define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32) - -static void __iomem *rcar_gen4_sysc_base; -static DEFINE_SPINLOCK(rcar_gen4_sysc_lock); /* SMP CPUs + I/O devices */ - -static int rcar_gen4_sysc_pwr_on_off(u8 pdr, bool on) -{ - unsigned int reg_offs; - u32 val; - int ret; - - if (on) - reg_offs = PDRONCR(pdr); - else - reg_offs = PDROFFCR(pdr); - - /* Wait until SYSC is ready to accept a power request */ - ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCSR, val, - (val & SYSCSR_BUSY) == SYSCSR_BUSY, - SYSCSR_DELAY_US, SYSCSR_TIMEOUT); - if (ret < 0) - return -EAGAIN; - - /* Submit power shutoff or power resume request */ - iowrite32(PWRON_PWROFF, rcar_gen4_sysc_base + reg_offs); - - return 0; -} - -static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask) -{ - u32 val; - int ret; - - iowrite32(isr_mask, rcar_gen4_sysc_base + SYSCISCR(reg_idx)); - - ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), - val, !(val & isr_mask), - SYSCISR_DELAY_US, SYSCISR_TIMEOUT); - if (ret < 0) { - pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__); - return -EIO; - } - - return 0; -} - -static int rcar_gen4_sysc_power(u8 pdr, bool on) -{ - unsigned int isr_mask; - unsigned int reg_idx, bit_idx; - unsigned int status; - unsigned long flags; - int ret = 0; - u32 val; - int k; - - spin_lock_irqsave(&rcar_gen4_sysc_lock, flags); - - reg_idx = pdr / NUM_DOMAINS_EACH_REG; - bit_idx = pdr % NUM_DOMAINS_EACH_REG; - - isr_mask = BIT(bit_idx); - - /* - * The interrupt source needs to be enabled, but masked, to prevent the - * CPU from receiving it. - */ - iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIER(reg_idx)) | isr_mask, - rcar_gen4_sysc_base + SYSCIER(reg_idx)); - iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIMR(reg_idx)) | isr_mask, - rcar_gen4_sysc_base + SYSCIMR(reg_idx)); - - ret = clear_irq_flags(reg_idx, isr_mask); - if (ret) - goto out; - - /* Submit power shutoff or resume request until it was accepted */ - for (k = 0; k < PDRESR_RETRIES; k++) { - ret = rcar_gen4_sysc_pwr_on_off(pdr, on); - if (ret) - goto out; - - status = ioread32(rcar_gen4_sysc_base + PDRESR(pdr)); - if (!(status & PDRESR_ERR)) - break; - - udelay(PDRESR_DELAY_US); - } - - if (k == PDRESR_RETRIES) { - ret = -EIO; - goto out; - } - - /* Wait until the power shutoff or resume request has completed * */ - ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), - val, (val & isr_mask), - SYSCISR_DELAY_US, SYSCISR_TIMEOUT); - if (ret < 0) { - ret = -EIO; - goto out; - } - - /* Clear interrupt flags */ - ret = clear_irq_flags(reg_idx, isr_mask); - if (ret) - goto out; - - out: - spin_unlock_irqrestore(&rcar_gen4_sysc_lock, flags); - - pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", - pdr, ioread32(rcar_gen4_sysc_base + SYSCISCR(reg_idx)), ret); - return ret; -} - -static bool rcar_gen4_sysc_power_is_off(u8 pdr) -{ - unsigned int st; - - st = ioread32(rcar_gen4_sysc_base + PDRSR(pdr)); - - if (st & PDRSR_OFF) - return true; - - return false; -} - -struct rcar_gen4_sysc_pd { - struct generic_pm_domain genpd; - u8 pdr; - unsigned int flags; - char name[]; -}; - -static inline struct rcar_gen4_sysc_pd *to_rcar_gen4_pd(struct generic_pm_domain *d) -{ - return container_of(d, struct rcar_gen4_sysc_pd, genpd); -} - -static int rcar_gen4_sysc_pd_power_off(struct generic_pm_domain *genpd) -{ - struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd); - - pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_gen4_sysc_power(pd->pdr, false); -} - -static int rcar_gen4_sysc_pd_power_on(struct generic_pm_domain *genpd) -{ - struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd); - - pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_gen4_sysc_power(pd->pdr, true); -} - -static int __init rcar_gen4_sysc_pd_setup(struct rcar_gen4_sysc_pd *pd) -{ - struct generic_pm_domain *genpd = &pd->genpd; - const char *name = pd->genpd.name; - int error; - - if (pd->flags & PD_CPU) { - /* - * This domain contains a CPU core and therefore it should - * only be turned off if the CPU is not in use. - */ - pr_debug("PM domain %s contains %s\n", name, "CPU"); - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } else if (pd->flags & PD_SCU) { - /* - * This domain contains an SCU and cache-controller, and - * therefore it should only be turned off if the CPU cores are - * not in use. - */ - pr_debug("PM domain %s contains %s\n", name, "SCU"); - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } else if (pd->flags & PD_NO_CR) { - /* - * This domain cannot be turned off. - */ - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } - - if (!(pd->flags & (PD_CPU | PD_SCU))) { - /* Enable Clock Domain for I/O devices */ - genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - genpd->attach_dev = cpg_mssr_attach_dev; - genpd->detach_dev = cpg_mssr_detach_dev; - } - - genpd->power_off = rcar_gen4_sysc_pd_power_off; - genpd->power_on = rcar_gen4_sysc_pd_power_on; - - if (pd->flags & (PD_CPU | PD_NO_CR)) { - /* Skip CPUs (handled by SMP code) and areas without control */ - pr_debug("%s: Not touching %s\n", __func__, genpd->name); - goto finalize; - } - - if (!rcar_gen4_sysc_power_is_off(pd->pdr)) { - pr_debug("%s: %s is already powered\n", __func__, genpd->name); - goto finalize; - } - - rcar_gen4_sysc_power(pd->pdr, true); - -finalize: - error = pm_genpd_init(genpd, &simple_qos_governor, false); - if (error) - pr_err("Failed to init PM domain %s: %d\n", name, error); - - return error; -} - -static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = { -#ifdef CONFIG_SYSC_R8A779A0 - { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A779F0 - { .compatible = "renesas,r8a779f0-sysc", .data = &r8a779f0_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A779G0 - { .compatible = "renesas,r8a779g0-sysc", .data = &r8a779g0_sysc_info }, -#endif - { /* sentinel */ } -}; - -struct rcar_gen4_pm_domains { - struct genpd_onecell_data onecell_data; - struct generic_pm_domain *domains[RCAR_GEN4_PD_ALWAYS_ON + 1]; -}; - -static struct genpd_onecell_data *rcar_gen4_sysc_onecell_data; - -static int __init rcar_gen4_sysc_pd_init(void) -{ - const struct rcar_gen4_sysc_info *info; - const struct of_device_id *match; - struct rcar_gen4_pm_domains *domains; - struct device_node *np; - void __iomem *base; - unsigned int i; - int error; - - np = of_find_matching_node_and_match(NULL, rcar_gen4_sysc_matches, &match); - if (!np) - return -ENODEV; - - info = match->data; - - base = of_iomap(np, 0); - if (!base) { - pr_warn("%pOF: Cannot map regs\n", np); - error = -ENOMEM; - goto out_put; - } - - rcar_gen4_sysc_base = base; - - domains = kzalloc(sizeof(*domains), GFP_KERNEL); - if (!domains) { - error = -ENOMEM; - goto out_put; - } - - domains->onecell_data.domains = domains->domains; - domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); - rcar_gen4_sysc_onecell_data = &domains->onecell_data; - - for (i = 0; i < info->num_areas; i++) { - const struct rcar_gen4_sysc_area *area = &info->areas[i]; - struct rcar_gen4_sysc_pd *pd; - size_t n; - - if (!area->name) { - /* Skip NULLified area */ - continue; - } - - n = strlen(area->name) + 1; - pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); - if (!pd) { - error = -ENOMEM; - goto out_put; - } - - memcpy(pd->name, area->name, n); - pd->genpd.name = pd->name; - pd->pdr = area->pdr; - pd->flags = area->flags; - - error = rcar_gen4_sysc_pd_setup(pd); - if (error) - goto out_put; - - domains->domains[area->pdr] = &pd->genpd; - - if (area->parent < 0) - continue; - - error = pm_genpd_add_subdomain(domains->domains[area->parent], - &pd->genpd); - if (error) { - pr_warn("Failed to add PM subdomain %s to parent %u\n", - area->name, area->parent); - goto out_put; - } - } - - error = of_genpd_add_provider_onecell(np, &domains->onecell_data); - -out_put: - of_node_put(np); - return error; -} -early_initcall(rcar_gen4_sysc_pd_init); diff --git a/drivers/genpd/renesas/rcar-gen4-sysc.h b/drivers/genpd/renesas/rcar-gen4-sysc.h deleted file mode 100644 index 388cfa8f8f9f..000000000000 --- a/drivers/genpd/renesas/rcar-gen4-sysc.h +++ /dev/null @@ -1,44 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * R-Car Gen4 System Controller - * - * Copyright (C) 2021 Renesas Electronics Corp. - */ -#ifndef __SOC_RENESAS_RCAR_GEN4_SYSC_H__ -#define __SOC_RENESAS_RCAR_GEN4_SYSC_H__ - -#include - -/* - * Power Domain flags - */ -#define PD_CPU BIT(0) /* Area contains main CPU core */ -#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ -#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ - -#define PD_CPU_NOCR (PD_CPU | PD_NO_CR) /* CPU area lacks CR */ -#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ - -/* - * Description of a Power Area - */ -struct rcar_gen4_sysc_area { - const char *name; - u8 pdr; /* PDRn */ - s8 parent; /* -1 if none */ - u8 flags; /* See PD_* */ -}; - -/* - * SoC-specific Power Area Description - */ -struct rcar_gen4_sysc_info { - const struct rcar_gen4_sysc_area *areas; - unsigned int num_areas; -}; - -extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info; -extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info; -extern const struct rcar_gen4_sysc_info r8a779g0_sysc_info; - -#endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */ diff --git a/drivers/genpd/renesas/rcar-sysc.c b/drivers/genpd/renesas/rcar-sysc.c deleted file mode 100644 index eed47696e825..000000000000 --- a/drivers/genpd/renesas/rcar-sysc.c +++ /dev/null @@ -1,494 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * R-Car SYSC Power management support - * - * Copyright (C) 2014 Magnus Damm - * Copyright (C) 2015-2017 Glider bvba - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "rcar-sysc.h" - -/* SYSC Common */ -#define SYSCSR 0x00 /* SYSC Status Register */ -#define SYSCISR 0x04 /* Interrupt Status Register */ -#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ -#define SYSCIER 0x0c /* Interrupt Enable Register */ -#define SYSCIMR 0x10 /* Interrupt Mask Register */ - -/* SYSC Status Register */ -#define SYSCSR_PONENB 1 /* Ready for power resume requests */ -#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ - -/* - * Power Control Register Offsets inside the register block for each domain - * Note: The "CR" registers for ARM cores exist on H1 only - * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 - * Use PSCI on R-Car Gen3 - */ -#define PWRSR_OFFS 0x00 /* Power Status Register */ -#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ -#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ -#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ -#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ -#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ - - -#define SYSCSR_TIMEOUT 100 -#define SYSCSR_DELAY_US 1 - -#define PWRER_RETRIES 100 -#define PWRER_DELAY_US 1 - -#define SYSCISR_TIMEOUT 1000 -#define SYSCISR_DELAY_US 1 - -#define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ - -struct rcar_sysc_ch { - u16 chan_offs; - u8 chan_bit; - u8 isr_bit; -}; - -static void __iomem *rcar_sysc_base; -static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ -static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; - -static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) -{ - unsigned int sr_bit, reg_offs; - u32 val; - int ret; - - if (on) { - sr_bit = SYSCSR_PONENB; - reg_offs = PWRONCR_OFFS; - } else { - sr_bit = SYSCSR_POFFENB; - reg_offs = PWROFFCR_OFFS; - } - - /* Wait until SYSC is ready to accept a power request */ - ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCSR, val, - val & BIT(sr_bit), SYSCSR_DELAY_US, - SYSCSR_TIMEOUT); - if (ret) - return -EAGAIN; - - /* Submit power shutoff or power resume request */ - iowrite32(BIT(sysc_ch->chan_bit), - rcar_sysc_base + sysc_ch->chan_offs + reg_offs); - - return 0; -} - -static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) -{ - unsigned int isr_mask = BIT(sysc_ch->isr_bit); - unsigned int chan_mask = BIT(sysc_ch->chan_bit); - unsigned int status, k; - unsigned long flags; - int ret; - - spin_lock_irqsave(&rcar_sysc_lock, flags); - - /* - * Mask external power requests for CPU or 3DG domains - */ - if (rcar_sysc_extmask_val) { - iowrite32(rcar_sysc_extmask_val, - rcar_sysc_base + rcar_sysc_extmask_offs); - } - - /* - * The interrupt source needs to be enabled, but masked, to prevent the - * CPU from receiving it. - */ - iowrite32(ioread32(rcar_sysc_base + SYSCIMR) | isr_mask, - rcar_sysc_base + SYSCIMR); - iowrite32(ioread32(rcar_sysc_base + SYSCIER) | isr_mask, - rcar_sysc_base + SYSCIER); - - iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); - - /* Submit power shutoff or resume request until it was accepted */ - for (k = 0; k < PWRER_RETRIES; k++) { - ret = rcar_sysc_pwr_on_off(sysc_ch, on); - if (ret) - goto out; - - status = ioread32(rcar_sysc_base + - sysc_ch->chan_offs + PWRER_OFFS); - if (!(status & chan_mask)) - break; - - udelay(PWRER_DELAY_US); - } - - if (k == PWRER_RETRIES) { - ret = -EIO; - goto out; - } - - /* Wait until the power shutoff or resume request has completed * */ - ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCISR, status, - status & isr_mask, SYSCISR_DELAY_US, - SYSCISR_TIMEOUT); - if (ret) - ret = -EIO; - - iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); - - out: - if (rcar_sysc_extmask_val) - iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs); - - spin_unlock_irqrestore(&rcar_sysc_lock, flags); - - pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", - sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); - return ret; -} - -static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) -{ - unsigned int st; - - st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); - if (st & BIT(sysc_ch->chan_bit)) - return true; - - return false; -} - -struct rcar_sysc_pd { - struct generic_pm_domain genpd; - struct rcar_sysc_ch ch; - unsigned int flags; - char name[]; -}; - -static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) -{ - return container_of(d, struct rcar_sysc_pd, genpd); -} - -static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) -{ - struct rcar_sysc_pd *pd = to_rcar_pd(genpd); - - pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_sysc_power(&pd->ch, false); -} - -static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) -{ - struct rcar_sysc_pd *pd = to_rcar_pd(genpd); - - pr_debug("%s: %s\n", __func__, genpd->name); - return rcar_sysc_power(&pd->ch, true); -} - -static bool has_cpg_mstp; - -static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) -{ - struct generic_pm_domain *genpd = &pd->genpd; - const char *name = pd->genpd.name; - int error; - - if (pd->flags & PD_CPU) { - /* - * This domain contains a CPU core and therefore it should - * only be turned off if the CPU is not in use. - */ - pr_debug("PM domain %s contains %s\n", name, "CPU"); - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } else if (pd->flags & PD_SCU) { - /* - * This domain contains an SCU and cache-controller, and - * therefore it should only be turned off if the CPU cores are - * not in use. - */ - pr_debug("PM domain %s contains %s\n", name, "SCU"); - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } else if (pd->flags & PD_NO_CR) { - /* - * This domain cannot be turned off. - */ - genpd->flags |= GENPD_FLAG_ALWAYS_ON; - } - - if (!(pd->flags & (PD_CPU | PD_SCU))) { - /* Enable Clock Domain for I/O devices */ - genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - if (has_cpg_mstp) { - genpd->attach_dev = cpg_mstp_attach_dev; - genpd->detach_dev = cpg_mstp_detach_dev; - } else { - genpd->attach_dev = cpg_mssr_attach_dev; - genpd->detach_dev = cpg_mssr_detach_dev; - } - } - - genpd->power_off = rcar_sysc_pd_power_off; - genpd->power_on = rcar_sysc_pd_power_on; - - if (pd->flags & (PD_CPU | PD_NO_CR)) { - /* Skip CPUs (handled by SMP code) and areas without control */ - pr_debug("%s: Not touching %s\n", __func__, genpd->name); - goto finalize; - } - - if (!rcar_sysc_power_is_off(&pd->ch)) { - pr_debug("%s: %s is already powered\n", __func__, genpd->name); - goto finalize; - } - - rcar_sysc_power(&pd->ch, true); - -finalize: - error = pm_genpd_init(genpd, &simple_qos_governor, false); - if (error) - pr_err("Failed to init PM domain %s: %d\n", name, error); - - return error; -} - -static const struct of_device_id rcar_sysc_matches[] __initconst = { -#ifdef CONFIG_SYSC_R8A7742 - { .compatible = "renesas,r8a7742-sysc", .data = &r8a7742_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7743 - { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, - /* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */ - { .compatible = "renesas,r8a7744-sysc", .data = &r8a7743_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7745 - { .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77470 - { .compatible = "renesas,r8a77470-sysc", .data = &r8a77470_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A774A1 - { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A774B1 - { .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A774C0 - { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A774E1 - { .compatible = "renesas,r8a774e1-sysc", .data = &r8a774e1_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7779 - { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7790 - { .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7791 - { .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info }, - /* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */ - { .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7792 - { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7794 - { .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A7795 - { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77960 - { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77961 - { .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77965 - { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77970 - { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77980 - { .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77990 - { .compatible = "renesas,r8a77990-sysc", .data = &r8a77990_sysc_info }, -#endif -#ifdef CONFIG_SYSC_R8A77995 - { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info }, -#endif - { /* sentinel */ } -}; - -struct rcar_pm_domains { - struct genpd_onecell_data onecell_data; - struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1]; -}; - -static struct genpd_onecell_data *rcar_sysc_onecell_data; - -static int __init rcar_sysc_pd_init(void) -{ - const struct rcar_sysc_info *info; - const struct of_device_id *match; - struct rcar_pm_domains *domains; - struct device_node *np; - void __iomem *base; - unsigned int i; - int error; - - np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match); - if (!np) - return -ENODEV; - - info = match->data; - - if (info->init) { - error = info->init(); - if (error) - goto out_put; - } - - has_cpg_mstp = of_find_compatible_node(NULL, NULL, - "renesas,cpg-mstp-clocks"); - - base = of_iomap(np, 0); - if (!base) { - pr_warn("%pOF: Cannot map regs\n", np); - error = -ENOMEM; - goto out_put; - } - - rcar_sysc_base = base; - - /* Optional External Request Mask Register */ - rcar_sysc_extmask_offs = info->extmask_offs; - rcar_sysc_extmask_val = info->extmask_val; - - domains = kzalloc(sizeof(*domains), GFP_KERNEL); - if (!domains) { - error = -ENOMEM; - goto out_put; - } - - domains->onecell_data.domains = domains->domains; - domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); - rcar_sysc_onecell_data = &domains->onecell_data; - - for (i = 0; i < info->num_areas; i++) { - const struct rcar_sysc_area *area = &info->areas[i]; - struct rcar_sysc_pd *pd; - size_t n; - - if (!area->name) { - /* Skip NULLified area */ - continue; - } - - n = strlen(area->name) + 1; - pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); - if (!pd) { - error = -ENOMEM; - goto out_put; - } - - memcpy(pd->name, area->name, n); - pd->genpd.name = pd->name; - pd->ch.chan_offs = area->chan_offs; - pd->ch.chan_bit = area->chan_bit; - pd->ch.isr_bit = area->isr_bit; - pd->flags = area->flags; - - error = rcar_sysc_pd_setup(pd); - if (error) - goto out_put; - - domains->domains[area->isr_bit] = &pd->genpd; - - if (area->parent < 0) - continue; - - error = pm_genpd_add_subdomain(domains->domains[area->parent], - &pd->genpd); - if (error) { - pr_warn("Failed to add PM subdomain %s to parent %u\n", - area->name, area->parent); - goto out_put; - } - } - - error = of_genpd_add_provider_onecell(np, &domains->onecell_data); - if (!error) - fwnode_dev_initialized(of_fwnode_handle(np), true); - -out_put: - of_node_put(np); - return error; -} -early_initcall(rcar_sysc_pd_init); - -void __init rcar_sysc_nullify(struct rcar_sysc_area *areas, - unsigned int num_areas, u8 id) -{ - unsigned int i; - - for (i = 0; i < num_areas; i++) - if (areas[i].isr_bit == id) { - areas[i].name = NULL; - return; - } -} - -#ifdef CONFIG_ARCH_R8A7779 -static int rcar_sysc_power_cpu(unsigned int idx, bool on) -{ - struct generic_pm_domain *genpd; - struct rcar_sysc_pd *pd; - unsigned int i; - - if (!rcar_sysc_onecell_data) - return -ENODEV; - - for (i = 0; i < rcar_sysc_onecell_data->num_domains; i++) { - genpd = rcar_sysc_onecell_data->domains[i]; - if (!genpd) - continue; - - pd = to_rcar_pd(genpd); - if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx) - continue; - - return rcar_sysc_power(&pd->ch, on); - } - - return -ENOENT; -} - -int rcar_sysc_power_down_cpu(unsigned int cpu) -{ - return rcar_sysc_power_cpu(cpu, false); -} - -int rcar_sysc_power_up_cpu(unsigned int cpu) -{ - return rcar_sysc_power_cpu(cpu, true); -} -#endif /* CONFIG_ARCH_R8A7779 */ diff --git a/drivers/genpd/renesas/rcar-sysc.h b/drivers/genpd/renesas/rcar-sysc.h deleted file mode 100644 index 266c599a0a9b..000000000000 --- a/drivers/genpd/renesas/rcar-sysc.h +++ /dev/null @@ -1,82 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * Renesas R-Car System Controller - * - * Copyright (C) 2016 Glider bvba - */ -#ifndef __SOC_RENESAS_RCAR_SYSC_H__ -#define __SOC_RENESAS_RCAR_SYSC_H__ - -#include - - -/* - * Power Domain flags - */ -#define PD_CPU BIT(0) /* Area contains main CPU core */ -#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ -#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ - -#define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */ -#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */ -#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ - - -/* - * Description of a Power Area - */ - -struct rcar_sysc_area { - const char *name; - u16 chan_offs; /* Offset of PWRSR register for this area */ - u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */ - u8 isr_bit; /* Bit in SYSCI*R */ - s8 parent; /* -1 if none */ - u8 flags; /* See PD_* */ -}; - - -/* - * SoC-specific Power Area Description - */ - -struct rcar_sysc_info { - int (*init)(void); /* Optional */ - const struct rcar_sysc_area *areas; - unsigned int num_areas; - /* Optional External Request Mask Register */ - u32 extmask_offs; /* SYSCEXTMASK register offset */ - u32 extmask_val; /* SYSCEXTMASK register mask value */ -}; - -extern const struct rcar_sysc_info r8a7742_sysc_info; -extern const struct rcar_sysc_info r8a7743_sysc_info; -extern const struct rcar_sysc_info r8a7745_sysc_info; -extern const struct rcar_sysc_info r8a77470_sysc_info; -extern const struct rcar_sysc_info r8a774a1_sysc_info; -extern const struct rcar_sysc_info r8a774b1_sysc_info; -extern const struct rcar_sysc_info r8a774c0_sysc_info; -extern const struct rcar_sysc_info r8a774e1_sysc_info; -extern const struct rcar_sysc_info r8a7779_sysc_info; -extern const struct rcar_sysc_info r8a7790_sysc_info; -extern const struct rcar_sysc_info r8a7791_sysc_info; -extern const struct rcar_sysc_info r8a7792_sysc_info; -extern const struct rcar_sysc_info r8a7794_sysc_info; -extern struct rcar_sysc_info r8a7795_sysc_info; -extern const struct rcar_sysc_info r8a77960_sysc_info; -extern const struct rcar_sysc_info r8a77961_sysc_info; -extern const struct rcar_sysc_info r8a77965_sysc_info; -extern const struct rcar_sysc_info r8a77970_sysc_info; -extern const struct rcar_sysc_info r8a77980_sysc_info; -extern const struct rcar_sysc_info r8a77990_sysc_info; -extern const struct rcar_sysc_info r8a77995_sysc_info; - - - /* - * Helpers for fixing up power area tables depending on SoC revision - */ - -extern void rcar_sysc_nullify(struct rcar_sysc_area *areas, - unsigned int num_areas, u8 id); - -#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */ diff --git a/drivers/genpd/renesas/rmobile-sysc.c b/drivers/genpd/renesas/rmobile-sysc.c deleted file mode 100644 index 912daadaa10d..000000000000 --- a/drivers/genpd/renesas/rmobile-sysc.c +++ /dev/null @@ -1,343 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * rmobile power management support - * - * Copyright (C) 2012 Renesas Solutions Corp. - * Copyright (C) 2012 Kuninori Morimoto - * Copyright (C) 2014 Glider bvba - * - * based on pm-sh7372.c - * Copyright (C) 2011 Magnus Damm - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* SYSC */ -#define SPDCR 0x08 /* SYS Power Down Control Register */ -#define SWUCR 0x14 /* SYS Wakeup Control Register */ -#define PSTR 0x80 /* Power Status Register */ - -#define PSTR_RETRIES 100 -#define PSTR_DELAY_US 10 - -struct rmobile_pm_domain { - struct generic_pm_domain genpd; - struct dev_power_governor *gov; - int (*suspend)(void); - void __iomem *base; - unsigned int bit_shift; -}; - -static inline -struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d) -{ - return container_of(d, struct rmobile_pm_domain, genpd); -} - -static int rmobile_pd_power_down(struct generic_pm_domain *genpd) -{ - struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); - unsigned int mask = BIT(rmobile_pd->bit_shift); - u32 val; - - if (rmobile_pd->suspend) { - int ret = rmobile_pd->suspend(); - - if (ret) - return ret; - } - - if (readl(rmobile_pd->base + PSTR) & mask) { - writel(mask, rmobile_pd->base + SPDCR); - - readl_poll_timeout_atomic(rmobile_pd->base + SPDCR, val, - !(val & mask), 0, PSTR_RETRIES); - } - - pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", genpd->name, mask, - readl(rmobile_pd->base + PSTR)); - - return 0; -} - -static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd) -{ - unsigned int val, mask = BIT(rmobile_pd->bit_shift); - int ret = 0; - - if (readl(rmobile_pd->base + PSTR) & mask) - return ret; - - writel(mask, rmobile_pd->base + SWUCR); - - ret = readl_poll_timeout_atomic(rmobile_pd->base + SWUCR, val, - (val & mask), PSTR_DELAY_US, - PSTR_RETRIES * PSTR_DELAY_US); - - pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", - rmobile_pd->genpd.name, mask, - readl(rmobile_pd->base + PSTR)); - - return ret; -} - -static int rmobile_pd_power_up(struct generic_pm_domain *genpd) -{ - return __rmobile_pd_power_up(to_rmobile_pd(genpd)); -} - -static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) -{ - struct generic_pm_domain *genpd = &rmobile_pd->genpd; - struct dev_power_governor *gov = rmobile_pd->gov; - - genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; - genpd->attach_dev = cpg_mstp_attach_dev; - genpd->detach_dev = cpg_mstp_detach_dev; - - if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) { - genpd->power_off = rmobile_pd_power_down; - genpd->power_on = rmobile_pd_power_up; - __rmobile_pd_power_up(rmobile_pd); - } - - pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); -} - -static int rmobile_pd_suspend_console(void) -{ - /* - * Serial consoles make use of SCIF hardware located in this domain, - * hence keep the power domain on if "no_console_suspend" is set. - */ - return console_suspend_enabled ? 0 : -EBUSY; -} - -enum pd_types { - PD_NORMAL, - PD_CPU, - PD_CONSOLE, - PD_DEBUG, - PD_MEMCTL, -}; - -#define MAX_NUM_SPECIAL_PDS 16 - -static struct special_pd { - struct device_node *pd; - enum pd_types type; -} special_pds[MAX_NUM_SPECIAL_PDS] __initdata; - -static unsigned int num_special_pds __initdata; - -static const struct of_device_id special_ids[] __initconst = { - { .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG }, - { .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, }, - { .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, }, - { .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, }, - { /* sentinel */ }, -}; - -static void __init add_special_pd(struct device_node *np, enum pd_types type) -{ - unsigned int i; - struct device_node *pd; - - pd = of_parse_phandle(np, "power-domains", 0); - if (!pd) - return; - - for (i = 0; i < num_special_pds; i++) - if (pd == special_pds[i].pd && type == special_pds[i].type) { - of_node_put(pd); - return; - } - - if (num_special_pds == ARRAY_SIZE(special_pds)) { - pr_warn("Too many special PM domains\n"); - of_node_put(pd); - return; - } - - pr_debug("Special PM domain %pOFn type %d for %pOF\n", pd, type, np); - - special_pds[num_special_pds].pd = pd; - special_pds[num_special_pds].type = type; - num_special_pds++; -} - -static void __init get_special_pds(void) -{ - struct device_node *np; - const struct of_device_id *id; - - /* PM domains containing CPUs */ - for_each_of_cpu_node(np) - add_special_pd(np, PD_CPU); - - /* PM domain containing console */ - if (of_stdout) - add_special_pd(of_stdout, PD_CONSOLE); - - /* PM domains containing other special devices */ - for_each_matching_node_and_match(np, special_ids, &id) - add_special_pd(np, (enum pd_types)id->data); -} - -static void __init put_special_pds(void) -{ - unsigned int i; - - for (i = 0; i < num_special_pds; i++) - of_node_put(special_pds[i].pd); -} - -static enum pd_types __init pd_type(const struct device_node *pd) -{ - unsigned int i; - - for (i = 0; i < num_special_pds; i++) - if (pd == special_pds[i].pd) - return special_pds[i].type; - - return PD_NORMAL; -} - -static void __init rmobile_setup_pm_domain(struct device_node *np, - struct rmobile_pm_domain *pd) -{ - const char *name = pd->genpd.name; - - switch (pd_type(np)) { - case PD_CPU: - /* - * This domain contains the CPU core and therefore it should - * only be turned off if the CPU is not in use. - */ - pr_debug("PM domain %s contains CPU\n", name); - pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; - break; - - case PD_CONSOLE: - pr_debug("PM domain %s contains serial console\n", name); - pd->gov = &pm_domain_always_on_gov; - pd->suspend = rmobile_pd_suspend_console; - break; - - case PD_DEBUG: - /* - * This domain contains the Coresight-ETM hardware block and - * therefore it should only be turned off if the debug module - * is not in use. - */ - pr_debug("PM domain %s contains Coresight-ETM\n", name); - pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; - break; - - case PD_MEMCTL: - /* - * This domain contains a memory-controller and therefore it - * should only be turned off if memory is not in use. - */ - pr_debug("PM domain %s contains MEMCTL\n", name); - pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; - break; - - case PD_NORMAL: - if (pd->bit_shift == ~0) { - /* Top-level always-on domain */ - pr_debug("PM domain %s is always-on domain\n", name); - pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; - } - break; - } - - rmobile_init_pm_domain(pd); -} - -static int __init rmobile_add_pm_domains(void __iomem *base, - struct device_node *parent, - struct generic_pm_domain *genpd_parent) -{ - struct device_node *np; - - for_each_child_of_node(parent, np) { - struct rmobile_pm_domain *pd; - u32 idx = ~0; - - if (of_property_read_u32(np, "reg", &idx)) { - /* always-on domain */ - } - - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) { - of_node_put(np); - return -ENOMEM; - } - - pd->genpd.name = np->name; - pd->base = base; - pd->bit_shift = idx; - - rmobile_setup_pm_domain(np, pd); - if (genpd_parent) - pm_genpd_add_subdomain(genpd_parent, &pd->genpd); - of_genpd_add_provider_simple(np, &pd->genpd); - - rmobile_add_pm_domains(base, np, &pd->genpd); - } - return 0; -} - -static int __init rmobile_init_pm_domains(void) -{ - struct device_node *np, *pmd; - bool scanned = false; - void __iomem *base; - int ret = 0; - - for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") { - base = of_iomap(np, 0); - if (!base) { - pr_warn("%pOF cannot map reg 0\n", np); - continue; - } - - pmd = of_get_child_by_name(np, "pm-domains"); - if (!pmd) { - iounmap(base); - pr_warn("%pOF lacks pm-domains node\n", np); - continue; - } - - if (!scanned) { - /* Find PM domains containing special blocks */ - get_special_pds(); - scanned = true; - } - - ret = rmobile_add_pm_domains(base, pmd, NULL); - of_node_put(pmd); - if (ret) { - of_node_put(np); - break; - } - - fwnode_dev_initialized(of_fwnode_handle(np), true); - } - - put_special_pds(); - - return ret; -} - -core_initcall(rmobile_init_pm_domains); diff --git a/drivers/genpd/rockchip/Makefile b/drivers/genpd/rockchip/Makefile deleted file mode 100644 index 8fb9d88a3492..000000000000 --- a/drivers/genpd/rockchip/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm-domains.o diff --git a/drivers/genpd/rockchip/pm-domains.c b/drivers/genpd/rockchip/pm-domains.c deleted file mode 100644 index d5d3ecb38283..000000000000 --- a/drivers/genpd/rockchip/pm-domains.c +++ /dev/null @@ -1,1396 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Rockchip Generic power domain support. - * - * Copyright (c) 2015 ROCKCHIP, Co. Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct rockchip_domain_info { - const char *name; - int pwr_mask; - int status_mask; - int req_mask; - int idle_mask; - int ack_mask; - bool active_wakeup; - int pwr_w_mask; - int req_w_mask; - int mem_status_mask; - int repair_status_mask; - u32 pwr_offset; - u32 mem_offset; - u32 req_offset; -}; - -struct rockchip_pmu_info { - u32 pwr_offset; - u32 status_offset; - u32 req_offset; - u32 idle_offset; - u32 ack_offset; - u32 mem_pwr_offset; - u32 chain_status_offset; - u32 mem_status_offset; - u32 repair_status_offset; - - u32 core_pwrcnt_offset; - u32 gpu_pwrcnt_offset; - - unsigned int core_power_transition_time; - unsigned int gpu_power_transition_time; - - int num_domains; - const struct rockchip_domain_info *domain_info; -}; - -#define MAX_QOS_REGS_NUM 5 -#define QOS_PRIORITY 0x08 -#define QOS_MODE 0x0c -#define QOS_BANDWIDTH 0x10 -#define QOS_SATURATION 0x14 -#define QOS_EXTCONTROL 0x18 - -struct rockchip_pm_domain { - struct generic_pm_domain genpd; - const struct rockchip_domain_info *info; - struct rockchip_pmu *pmu; - int num_qos; - struct regmap **qos_regmap; - u32 *qos_save_regs[MAX_QOS_REGS_NUM]; - int num_clks; - struct clk_bulk_data *clks; -}; - -struct rockchip_pmu { - struct device *dev; - struct regmap *regmap; - const struct rockchip_pmu_info *info; - struct mutex mutex; /* mutex lock for pmu */ - struct genpd_onecell_data genpd_data; - struct generic_pm_domain *domains[]; -}; - -#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd) - -#define DOMAIN(_name, pwr, status, req, idle, ack, wakeup) \ -{ \ - .name = _name, \ - .pwr_mask = (pwr), \ - .status_mask = (status), \ - .req_mask = (req), \ - .idle_mask = (idle), \ - .ack_mask = (ack), \ - .active_wakeup = (wakeup), \ -} - -#define DOMAIN_M(_name, pwr, status, req, idle, ack, wakeup) \ -{ \ - .name = _name, \ - .pwr_w_mask = (pwr) << 16, \ - .pwr_mask = (pwr), \ - .status_mask = (status), \ - .req_w_mask = (req) << 16, \ - .req_mask = (req), \ - .idle_mask = (idle), \ - .ack_mask = (ack), \ - .active_wakeup = wakeup, \ -} - -#define DOMAIN_M_O_R(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, wakeup) \ -{ \ - .name = _name, \ - .pwr_offset = p_offset, \ - .pwr_w_mask = (pwr) << 16, \ - .pwr_mask = (pwr), \ - .status_mask = (status), \ - .mem_offset = m_offset, \ - .mem_status_mask = (m_status), \ - .repair_status_mask = (r_status), \ - .req_offset = r_offset, \ - .req_w_mask = (req) << 16, \ - .req_mask = (req), \ - .idle_mask = (idle), \ - .ack_mask = (ack), \ - .active_wakeup = wakeup, \ -} - -#define DOMAIN_RK3036(_name, req, ack, idle, wakeup) \ -{ \ - .name = _name, \ - .req_mask = (req), \ - .req_w_mask = (req) << 16, \ - .ack_mask = (ack), \ - .idle_mask = (idle), \ - .active_wakeup = wakeup, \ -} - -#define DOMAIN_PX30(name, pwr, status, req, wakeup) \ - DOMAIN_M(name, pwr, status, req, (req) << 16, req, wakeup) - -#define DOMAIN_RV1126(name, pwr, req, idle, wakeup) \ - DOMAIN_M(name, pwr, pwr, req, idle, idle, wakeup) - -#define DOMAIN_RK3288(name, pwr, status, req, wakeup) \ - DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup) - -#define DOMAIN_RK3328(name, pwr, status, req, wakeup) \ - DOMAIN_M(name, pwr, pwr, req, (req) << 10, req, wakeup) - -#define DOMAIN_RK3368(name, pwr, status, req, wakeup) \ - DOMAIN(name, pwr, status, req, (req) << 16, req, wakeup) - -#define DOMAIN_RK3399(name, pwr, status, req, wakeup) \ - DOMAIN(name, pwr, status, req, req, req, wakeup) - -#define DOMAIN_RK3568(name, pwr, req, wakeup) \ - DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) - -/* - * Dynamic Memory Controller may need to coordinate with us -- see - * rockchip_pmu_block(). - * - * dmc_pmu_mutex protects registration-time races, so DMC driver doesn't try to - * block() while we're initializing the PMU. - */ -static DEFINE_MUTEX(dmc_pmu_mutex); -static struct rockchip_pmu *dmc_pmu; - -/* - * Block PMU transitions and make sure they don't interfere with ARM Trusted - * Firmware operations. There are two conflicts, noted in the comments below. - * - * Caller must unblock PMU transitions via rockchip_pmu_unblock(). - */ -int rockchip_pmu_block(void) -{ - struct rockchip_pmu *pmu; - struct generic_pm_domain *genpd; - struct rockchip_pm_domain *pd; - int i, ret; - - mutex_lock(&dmc_pmu_mutex); - - /* No PMU (yet)? Then we just block rockchip_pmu_probe(). */ - if (!dmc_pmu) - return 0; - pmu = dmc_pmu; - - /* - * mutex blocks all idle transitions: we can't touch the - * PMU_BUS_IDLE_REQ (our ".idle_offset") register while ARM Trusted - * Firmware might be using it. - */ - mutex_lock(&pmu->mutex); - - /* - * Power domain clocks: Per Rockchip, we *must* keep certain clocks - * enabled for the duration of power-domain transitions. Most - * transitions are handled by this driver, but some cases (in - * particular, DRAM DVFS / memory-controller idle) must be handled by - * firmware. Firmware can handle most clock management via a special - * "ungate" register (PMU_CRU_GATEDIS_CON0), but unfortunately, this - * doesn't handle PLLs. We can assist this transition by doing the - * clock management on behalf of firmware. - */ - for (i = 0; i < pmu->genpd_data.num_domains; i++) { - genpd = pmu->genpd_data.domains[i]; - if (genpd) { - pd = to_rockchip_pd(genpd); - ret = clk_bulk_enable(pd->num_clks, pd->clks); - if (ret < 0) { - dev_err(pmu->dev, - "failed to enable clks for domain '%s': %d\n", - genpd->name, ret); - goto err; - } - } - } - - return 0; - -err: - for (i = i - 1; i >= 0; i--) { - genpd = pmu->genpd_data.domains[i]; - if (genpd) { - pd = to_rockchip_pd(genpd); - clk_bulk_disable(pd->num_clks, pd->clks); - } - } - mutex_unlock(&pmu->mutex); - mutex_unlock(&dmc_pmu_mutex); - - return ret; -} -EXPORT_SYMBOL_GPL(rockchip_pmu_block); - -/* Unblock PMU transitions. */ -void rockchip_pmu_unblock(void) -{ - struct rockchip_pmu *pmu; - struct generic_pm_domain *genpd; - struct rockchip_pm_domain *pd; - int i; - - if (dmc_pmu) { - pmu = dmc_pmu; - for (i = 0; i < pmu->genpd_data.num_domains; i++) { - genpd = pmu->genpd_data.domains[i]; - if (genpd) { - pd = to_rockchip_pd(genpd); - clk_bulk_disable(pd->num_clks, pd->clks); - } - } - - mutex_unlock(&pmu->mutex); - } - - mutex_unlock(&dmc_pmu_mutex); -} -EXPORT_SYMBOL_GPL(rockchip_pmu_unblock); - -#define DOMAIN_RK3588(name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, wakeup) \ - DOMAIN_M_O_R(name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, idle, wakeup) - -static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd) -{ - struct rockchip_pmu *pmu = pd->pmu; - const struct rockchip_domain_info *pd_info = pd->info; - unsigned int val; - - regmap_read(pmu->regmap, pmu->info->idle_offset, &val); - return (val & pd_info->idle_mask) == pd_info->idle_mask; -} - -static unsigned int rockchip_pmu_read_ack(struct rockchip_pmu *pmu) -{ - unsigned int val; - - regmap_read(pmu->regmap, pmu->info->ack_offset, &val); - return val; -} - -static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, - bool idle) -{ - const struct rockchip_domain_info *pd_info = pd->info; - struct generic_pm_domain *genpd = &pd->genpd; - struct rockchip_pmu *pmu = pd->pmu; - u32 pd_req_offset = pd_info->req_offset; - unsigned int target_ack; - unsigned int val; - bool is_idle; - int ret; - - if (pd_info->req_mask == 0) - return 0; - else if (pd_info->req_w_mask) - regmap_write(pmu->regmap, pmu->info->req_offset + pd_req_offset, - idle ? (pd_info->req_mask | pd_info->req_w_mask) : - pd_info->req_w_mask); - else - regmap_update_bits(pmu->regmap, pmu->info->req_offset + pd_req_offset, - pd_info->req_mask, idle ? -1U : 0); - - wmb(); - - /* Wait util idle_ack = 1 */ - target_ack = idle ? pd_info->ack_mask : 0; - ret = readx_poll_timeout_atomic(rockchip_pmu_read_ack, pmu, val, - (val & pd_info->ack_mask) == target_ack, - 0, 10000); - if (ret) { - dev_err(pmu->dev, - "failed to get ack on domain '%s', val=0x%x\n", - genpd->name, val); - return ret; - } - - ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_idle, pd, - is_idle, is_idle == idle, 0, 10000); - if (ret) { - dev_err(pmu->dev, - "failed to set idle on domain '%s', val=%d\n", - genpd->name, is_idle); - return ret; - } - - return 0; -} - -static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd) -{ - int i; - - for (i = 0; i < pd->num_qos; i++) { - regmap_read(pd->qos_regmap[i], - QOS_PRIORITY, - &pd->qos_save_regs[0][i]); - regmap_read(pd->qos_regmap[i], - QOS_MODE, - &pd->qos_save_regs[1][i]); - regmap_read(pd->qos_regmap[i], - QOS_BANDWIDTH, - &pd->qos_save_regs[2][i]); - regmap_read(pd->qos_regmap[i], - QOS_SATURATION, - &pd->qos_save_regs[3][i]); - regmap_read(pd->qos_regmap[i], - QOS_EXTCONTROL, - &pd->qos_save_regs[4][i]); - } - return 0; -} - -static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd) -{ - int i; - - for (i = 0; i < pd->num_qos; i++) { - regmap_write(pd->qos_regmap[i], - QOS_PRIORITY, - pd->qos_save_regs[0][i]); - regmap_write(pd->qos_regmap[i], - QOS_MODE, - pd->qos_save_regs[1][i]); - regmap_write(pd->qos_regmap[i], - QOS_BANDWIDTH, - pd->qos_save_regs[2][i]); - regmap_write(pd->qos_regmap[i], - QOS_SATURATION, - pd->qos_save_regs[3][i]); - regmap_write(pd->qos_regmap[i], - QOS_EXTCONTROL, - pd->qos_save_regs[4][i]); - } - - return 0; -} - -static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd) -{ - struct rockchip_pmu *pmu = pd->pmu; - unsigned int val; - - if (pd->info->repair_status_mask) { - regmap_read(pmu->regmap, pmu->info->repair_status_offset, &val); - /* 1'b1: power on, 1'b0: power off */ - return val & pd->info->repair_status_mask; - } - - /* check idle status for idle-only domains */ - if (pd->info->status_mask == 0) - return !rockchip_pmu_domain_is_idle(pd); - - regmap_read(pmu->regmap, pmu->info->status_offset, &val); - - /* 1'b0: power on, 1'b1: power off */ - return !(val & pd->info->status_mask); -} - -static bool rockchip_pmu_domain_is_mem_on(struct rockchip_pm_domain *pd) -{ - struct rockchip_pmu *pmu = pd->pmu; - unsigned int val; - - regmap_read(pmu->regmap, - pmu->info->mem_status_offset + pd->info->mem_offset, &val); - - /* 1'b0: power on, 1'b1: power off */ - return !(val & pd->info->mem_status_mask); -} - -static bool rockchip_pmu_domain_is_chain_on(struct rockchip_pm_domain *pd) -{ - struct rockchip_pmu *pmu = pd->pmu; - unsigned int val; - - regmap_read(pmu->regmap, - pmu->info->chain_status_offset + pd->info->mem_offset, &val); - - /* 1'b1: power on, 1'b0: power off */ - return val & pd->info->mem_status_mask; -} - -static int rockchip_pmu_domain_mem_reset(struct rockchip_pm_domain *pd) -{ - struct rockchip_pmu *pmu = pd->pmu; - struct generic_pm_domain *genpd = &pd->genpd; - bool is_on; - int ret = 0; - - ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_chain_on, pd, is_on, - is_on == true, 0, 10000); - if (ret) { - dev_err(pmu->dev, - "failed to get chain status '%s', target_on=1, val=%d\n", - genpd->name, is_on); - goto error; - } - - udelay(20); - - regmap_write(pmu->regmap, pmu->info->mem_pwr_offset + pd->info->pwr_offset, - (pd->info->pwr_mask | pd->info->pwr_w_mask)); - wmb(); - - ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_mem_on, pd, is_on, - is_on == false, 0, 10000); - if (ret) { - dev_err(pmu->dev, - "failed to get mem status '%s', target_on=0, val=%d\n", - genpd->name, is_on); - goto error; - } - - regmap_write(pmu->regmap, pmu->info->mem_pwr_offset + pd->info->pwr_offset, - pd->info->pwr_w_mask); - wmb(); - - ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_mem_on, pd, is_on, - is_on == true, 0, 10000); - if (ret) { - dev_err(pmu->dev, - "failed to get mem status '%s', target_on=1, val=%d\n", - genpd->name, is_on); - } - -error: - return ret; -} - -static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, - bool on) -{ - struct rockchip_pmu *pmu = pd->pmu; - struct generic_pm_domain *genpd = &pd->genpd; - u32 pd_pwr_offset = pd->info->pwr_offset; - bool is_on, is_mem_on = false; - - if (pd->info->pwr_mask == 0) - return; - - if (on && pd->info->mem_status_mask) - is_mem_on = rockchip_pmu_domain_is_mem_on(pd); - - if (pd->info->pwr_w_mask) - regmap_write(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset, - on ? pd->info->pwr_w_mask : - (pd->info->pwr_mask | pd->info->pwr_w_mask)); - else - regmap_update_bits(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset, - pd->info->pwr_mask, on ? 0 : -1U); - - wmb(); - - if (is_mem_on && rockchip_pmu_domain_mem_reset(pd)) - return; - - if (readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on, - is_on == on, 0, 10000)) { - dev_err(pmu->dev, - "failed to set domain '%s', val=%d\n", - genpd->name, is_on); - return; - } -} - -static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) -{ - struct rockchip_pmu *pmu = pd->pmu; - int ret; - - mutex_lock(&pmu->mutex); - - if (rockchip_pmu_domain_is_on(pd) != power_on) { - ret = clk_bulk_enable(pd->num_clks, pd->clks); - if (ret < 0) { - dev_err(pmu->dev, "failed to enable clocks\n"); - mutex_unlock(&pmu->mutex); - return ret; - } - - if (!power_on) { - rockchip_pmu_save_qos(pd); - - /* if powering down, idle request to NIU first */ - rockchip_pmu_set_idle_request(pd, true); - } - - rockchip_do_pmu_set_power_domain(pd, power_on); - - if (power_on) { - /* if powering up, leave idle mode */ - rockchip_pmu_set_idle_request(pd, false); - - rockchip_pmu_restore_qos(pd); - } - - clk_bulk_disable(pd->num_clks, pd->clks); - } - - mutex_unlock(&pmu->mutex); - return 0; -} - -static int rockchip_pd_power_on(struct generic_pm_domain *domain) -{ - struct rockchip_pm_domain *pd = to_rockchip_pd(domain); - - return rockchip_pd_power(pd, true); -} - -static int rockchip_pd_power_off(struct generic_pm_domain *domain) -{ - struct rockchip_pm_domain *pd = to_rockchip_pd(domain); - - return rockchip_pd_power(pd, false); -} - -static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd, - struct device *dev) -{ - struct clk *clk; - int i; - int error; - - dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name); - - error = pm_clk_create(dev); - if (error) { - dev_err(dev, "pm_clk_create failed %d\n", error); - return error; - } - - i = 0; - while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) { - dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk); - error = pm_clk_add_clk(dev, clk); - if (error) { - dev_err(dev, "pm_clk_add_clk failed %d\n", error); - clk_put(clk); - pm_clk_destroy(dev); - return error; - } - } - - return 0; -} - -static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd, - struct device *dev) -{ - dev_dbg(dev, "detaching from power domain '%s'\n", genpd->name); - - pm_clk_destroy(dev); -} - -static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, - struct device_node *node) -{ - const struct rockchip_domain_info *pd_info; - struct rockchip_pm_domain *pd; - struct device_node *qos_node; - int i, j; - u32 id; - int error; - - error = of_property_read_u32(node, "reg", &id); - if (error) { - dev_err(pmu->dev, - "%pOFn: failed to retrieve domain id (reg): %d\n", - node, error); - return -EINVAL; - } - - if (id >= pmu->info->num_domains) { - dev_err(pmu->dev, "%pOFn: invalid domain id %d\n", - node, id); - return -EINVAL; - } - /* RK3588 has domains with two parents (RKVDEC0/RKVDEC1) */ - if (pmu->genpd_data.domains[id]) - return 0; - - pd_info = &pmu->info->domain_info[id]; - if (!pd_info) { - dev_err(pmu->dev, "%pOFn: undefined domain id %d\n", - node, id); - return -EINVAL; - } - - pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - pd->info = pd_info; - pd->pmu = pmu; - - pd->num_clks = of_clk_get_parent_count(node); - if (pd->num_clks > 0) { - pd->clks = devm_kcalloc(pmu->dev, pd->num_clks, - sizeof(*pd->clks), GFP_KERNEL); - if (!pd->clks) - return -ENOMEM; - } else { - dev_dbg(pmu->dev, "%pOFn: doesn't have clocks: %d\n", - node, pd->num_clks); - pd->num_clks = 0; - } - - for (i = 0; i < pd->num_clks; i++) { - pd->clks[i].clk = of_clk_get(node, i); - if (IS_ERR(pd->clks[i].clk)) { - error = PTR_ERR(pd->clks[i].clk); - dev_err(pmu->dev, - "%pOFn: failed to get clk at index %d: %d\n", - node, i, error); - return error; - } - } - - error = clk_bulk_prepare(pd->num_clks, pd->clks); - if (error) - goto err_put_clocks; - - pd->num_qos = of_count_phandle_with_args(node, "pm_qos", - NULL); - - if (pd->num_qos > 0) { - pd->qos_regmap = devm_kcalloc(pmu->dev, pd->num_qos, - sizeof(*pd->qos_regmap), - GFP_KERNEL); - if (!pd->qos_regmap) { - error = -ENOMEM; - goto err_unprepare_clocks; - } - - for (j = 0; j < MAX_QOS_REGS_NUM; j++) { - pd->qos_save_regs[j] = devm_kcalloc(pmu->dev, - pd->num_qos, - sizeof(u32), - GFP_KERNEL); - if (!pd->qos_save_regs[j]) { - error = -ENOMEM; - goto err_unprepare_clocks; - } - } - - for (j = 0; j < pd->num_qos; j++) { - qos_node = of_parse_phandle(node, "pm_qos", j); - if (!qos_node) { - error = -ENODEV; - goto err_unprepare_clocks; - } - pd->qos_regmap[j] = syscon_node_to_regmap(qos_node); - if (IS_ERR(pd->qos_regmap[j])) { - error = -ENODEV; - of_node_put(qos_node); - goto err_unprepare_clocks; - } - of_node_put(qos_node); - } - } - - if (pd->info->name) - pd->genpd.name = pd->info->name; - else - pd->genpd.name = kbasename(node->full_name); - pd->genpd.power_off = rockchip_pd_power_off; - pd->genpd.power_on = rockchip_pd_power_on; - pd->genpd.attach_dev = rockchip_pd_attach_dev; - pd->genpd.detach_dev = rockchip_pd_detach_dev; - pd->genpd.flags = GENPD_FLAG_PM_CLK; - if (pd_info->active_wakeup) - pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; - pm_genpd_init(&pd->genpd, NULL, - !rockchip_pmu_domain_is_on(pd) || - (pd->info->mem_status_mask && !rockchip_pmu_domain_is_mem_on(pd))); - - pmu->genpd_data.domains[id] = &pd->genpd; - return 0; - -err_unprepare_clocks: - clk_bulk_unprepare(pd->num_clks, pd->clks); -err_put_clocks: - clk_bulk_put(pd->num_clks, pd->clks); - return error; -} - -static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd) -{ - int ret; - - /* - * We're in the error cleanup already, so we only complain, - * but won't emit another error on top of the original one. - */ - ret = pm_genpd_remove(&pd->genpd); - if (ret < 0) - dev_err(pd->pmu->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n", - pd->genpd.name, ret); - - clk_bulk_unprepare(pd->num_clks, pd->clks); - clk_bulk_put(pd->num_clks, pd->clks); - - /* protect the zeroing of pm->num_clks */ - mutex_lock(&pd->pmu->mutex); - pd->num_clks = 0; - mutex_unlock(&pd->pmu->mutex); - - /* devm will free our memory */ -} - -static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu) -{ - struct generic_pm_domain *genpd; - struct rockchip_pm_domain *pd; - int i; - - for (i = 0; i < pmu->genpd_data.num_domains; i++) { - genpd = pmu->genpd_data.domains[i]; - if (genpd) { - pd = to_rockchip_pd(genpd); - rockchip_pm_remove_one_domain(pd); - } - } - - /* devm will free our memory */ -} - -static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu, - u32 domain_reg_offset, - unsigned int count) -{ - /* First configure domain power down transition count ... */ - regmap_write(pmu->regmap, domain_reg_offset, count); - /* ... and then power up count. */ - regmap_write(pmu->regmap, domain_reg_offset + 4, count); -} - -static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, - struct device_node *parent) -{ - struct device_node *np; - struct generic_pm_domain *child_domain, *parent_domain; - int error; - - for_each_child_of_node(parent, np) { - u32 idx; - - error = of_property_read_u32(parent, "reg", &idx); - if (error) { - dev_err(pmu->dev, - "%pOFn: failed to retrieve domain id (reg): %d\n", - parent, error); - goto err_out; - } - parent_domain = pmu->genpd_data.domains[idx]; - - error = rockchip_pm_add_one_domain(pmu, np); - if (error) { - dev_err(pmu->dev, "failed to handle node %pOFn: %d\n", - np, error); - goto err_out; - } - - error = of_property_read_u32(np, "reg", &idx); - if (error) { - dev_err(pmu->dev, - "%pOFn: failed to retrieve domain id (reg): %d\n", - np, error); - goto err_out; - } - child_domain = pmu->genpd_data.domains[idx]; - - error = pm_genpd_add_subdomain(parent_domain, child_domain); - if (error) { - dev_err(pmu->dev, "%s failed to add subdomain %s: %d\n", - parent_domain->name, child_domain->name, error); - goto err_out; - } else { - dev_dbg(pmu->dev, "%s add subdomain: %s\n", - parent_domain->name, child_domain->name); - } - - rockchip_pm_add_subdomain(pmu, np); - } - - return 0; - -err_out: - of_node_put(np); - return error; -} - -static int rockchip_pm_domain_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct device_node *node; - struct device *parent; - struct rockchip_pmu *pmu; - const struct of_device_id *match; - const struct rockchip_pmu_info *pmu_info; - int error; - - if (!np) { - dev_err(dev, "device tree node not found\n"); - return -ENODEV; - } - - match = of_match_device(dev->driver->of_match_table, dev); - if (!match || !match->data) { - dev_err(dev, "missing pmu data\n"); - return -EINVAL; - } - - pmu_info = match->data; - - pmu = devm_kzalloc(dev, - struct_size(pmu, domains, pmu_info->num_domains), - GFP_KERNEL); - if (!pmu) - return -ENOMEM; - - pmu->dev = &pdev->dev; - mutex_init(&pmu->mutex); - - pmu->info = pmu_info; - - pmu->genpd_data.domains = pmu->domains; - pmu->genpd_data.num_domains = pmu_info->num_domains; - - parent = dev->parent; - if (!parent) { - dev_err(dev, "no parent for syscon devices\n"); - return -ENODEV; - } - - pmu->regmap = syscon_node_to_regmap(parent->of_node); - if (IS_ERR(pmu->regmap)) { - dev_err(dev, "no regmap available\n"); - return PTR_ERR(pmu->regmap); - } - - /* - * Configure power up and down transition delays for CORE - * and GPU domains. - */ - if (pmu_info->core_power_transition_time) - rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset, - pmu_info->core_power_transition_time); - if (pmu_info->gpu_pwrcnt_offset) - rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset, - pmu_info->gpu_power_transition_time); - - error = -ENODEV; - - /* - * Prevent any rockchip_pmu_block() from racing with the remainder of - * setup (clocks, register initialization). - */ - mutex_lock(&dmc_pmu_mutex); - - for_each_available_child_of_node(np, node) { - error = rockchip_pm_add_one_domain(pmu, node); - if (error) { - dev_err(dev, "failed to handle node %pOFn: %d\n", - node, error); - of_node_put(node); - goto err_out; - } - - error = rockchip_pm_add_subdomain(pmu, node); - if (error < 0) { - dev_err(dev, "failed to handle subdomain node %pOFn: %d\n", - node, error); - of_node_put(node); - goto err_out; - } - } - - if (error) { - dev_dbg(dev, "no power domains defined\n"); - goto err_out; - } - - error = of_genpd_add_provider_onecell(np, &pmu->genpd_data); - if (error) { - dev_err(dev, "failed to add provider: %d\n", error); - goto err_out; - } - - /* We only expect one PMU. */ - if (!WARN_ON_ONCE(dmc_pmu)) - dmc_pmu = pmu; - - mutex_unlock(&dmc_pmu_mutex); - - return 0; - -err_out: - rockchip_pm_domain_cleanup(pmu); - mutex_unlock(&dmc_pmu_mutex); - return error; -} - -static const struct rockchip_domain_info px30_pm_domains[] = { - [PX30_PD_USB] = DOMAIN_PX30("usb", BIT(5), BIT(5), BIT(10), false), - [PX30_PD_SDCARD] = DOMAIN_PX30("sdcard", BIT(8), BIT(8), BIT(9), false), - [PX30_PD_GMAC] = DOMAIN_PX30("gmac", BIT(10), BIT(10), BIT(6), false), - [PX30_PD_MMC_NAND] = DOMAIN_PX30("mmc_nand", BIT(11), BIT(11), BIT(5), false), - [PX30_PD_VPU] = DOMAIN_PX30("vpu", BIT(12), BIT(12), BIT(14), false), - [PX30_PD_VO] = DOMAIN_PX30("vo", BIT(13), BIT(13), BIT(7), false), - [PX30_PD_VI] = DOMAIN_PX30("vi", BIT(14), BIT(14), BIT(8), false), - [PX30_PD_GPU] = DOMAIN_PX30("gpu", BIT(15), BIT(15), BIT(2), false), -}; - -static const struct rockchip_domain_info rv1126_pm_domains[] = { - [RV1126_PD_VEPU] = DOMAIN_RV1126("vepu", BIT(2), BIT(9), BIT(9), false), - [RV1126_PD_VI] = DOMAIN_RV1126("vi", BIT(4), BIT(6), BIT(6), false), - [RV1126_PD_VO] = DOMAIN_RV1126("vo", BIT(5), BIT(7), BIT(7), false), - [RV1126_PD_ISPP] = DOMAIN_RV1126("ispp", BIT(1), BIT(8), BIT(8), false), - [RV1126_PD_VDPU] = DOMAIN_RV1126("vdpu", BIT(3), BIT(10), BIT(10), false), - [RV1126_PD_NVM] = DOMAIN_RV1126("nvm", BIT(7), BIT(11), BIT(11), false), - [RV1126_PD_SDIO] = DOMAIN_RV1126("sdio", BIT(8), BIT(13), BIT(13), false), - [RV1126_PD_USB] = DOMAIN_RV1126("usb", BIT(9), BIT(15), BIT(15), false), -}; - -static const struct rockchip_domain_info rk3036_pm_domains[] = { - [RK3036_PD_MSCH] = DOMAIN_RK3036("msch", BIT(14), BIT(23), BIT(30), true), - [RK3036_PD_CORE] = DOMAIN_RK3036("core", BIT(13), BIT(17), BIT(24), false), - [RK3036_PD_PERI] = DOMAIN_RK3036("peri", BIT(12), BIT(18), BIT(25), false), - [RK3036_PD_VIO] = DOMAIN_RK3036("vio", BIT(11), BIT(19), BIT(26), false), - [RK3036_PD_VPU] = DOMAIN_RK3036("vpu", BIT(10), BIT(20), BIT(27), false), - [RK3036_PD_GPU] = DOMAIN_RK3036("gpu", BIT(9), BIT(21), BIT(28), false), - [RK3036_PD_SYS] = DOMAIN_RK3036("sys", BIT(8), BIT(22), BIT(29), false), -}; - -static const struct rockchip_domain_info rk3066_pm_domains[] = { - [RK3066_PD_GPU] = DOMAIN("gpu", BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false), - [RK3066_PD_VIDEO] = DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false), - [RK3066_PD_VIO] = DOMAIN("vio", BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false), - [RK3066_PD_PERI] = DOMAIN("peri", BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false), - [RK3066_PD_CPU] = DOMAIN("cpu", 0, BIT(5), BIT(1), BIT(26), BIT(31), false), -}; - -static const struct rockchip_domain_info rk3128_pm_domains[] = { - [RK3128_PD_CORE] = DOMAIN_RK3288("core", BIT(0), BIT(0), BIT(4), false), - [RK3128_PD_MSCH] = DOMAIN_RK3288("msch", 0, 0, BIT(6), true), - [RK3128_PD_VIO] = DOMAIN_RK3288("vio", BIT(3), BIT(3), BIT(2), false), - [RK3128_PD_VIDEO] = DOMAIN_RK3288("video", BIT(2), BIT(2), BIT(1), false), - [RK3128_PD_GPU] = DOMAIN_RK3288("gpu", BIT(1), BIT(1), BIT(3), false), -}; - -static const struct rockchip_domain_info rk3188_pm_domains[] = { - [RK3188_PD_GPU] = DOMAIN("gpu", BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false), - [RK3188_PD_VIDEO] = DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false), - [RK3188_PD_VIO] = DOMAIN("vio", BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false), - [RK3188_PD_PERI] = DOMAIN("peri", BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false), - [RK3188_PD_CPU] = DOMAIN("cpu", BIT(5), BIT(5), BIT(1), BIT(26), BIT(31), false), -}; - -static const struct rockchip_domain_info rk3228_pm_domains[] = { - [RK3228_PD_CORE] = DOMAIN_RK3036("core", BIT(0), BIT(0), BIT(16), true), - [RK3228_PD_MSCH] = DOMAIN_RK3036("msch", BIT(1), BIT(1), BIT(17), true), - [RK3228_PD_BUS] = DOMAIN_RK3036("bus", BIT(2), BIT(2), BIT(18), true), - [RK3228_PD_SYS] = DOMAIN_RK3036("sys", BIT(3), BIT(3), BIT(19), true), - [RK3228_PD_VIO] = DOMAIN_RK3036("vio", BIT(4), BIT(4), BIT(20), false), - [RK3228_PD_VOP] = DOMAIN_RK3036("vop", BIT(5), BIT(5), BIT(21), false), - [RK3228_PD_VPU] = DOMAIN_RK3036("vpu", BIT(6), BIT(6), BIT(22), false), - [RK3228_PD_RKVDEC] = DOMAIN_RK3036("vdec", BIT(7), BIT(7), BIT(23), false), - [RK3228_PD_GPU] = DOMAIN_RK3036("gpu", BIT(8), BIT(8), BIT(24), false), - [RK3228_PD_PERI] = DOMAIN_RK3036("peri", BIT(9), BIT(9), BIT(25), true), - [RK3228_PD_GMAC] = DOMAIN_RK3036("gmac", BIT(10), BIT(10), BIT(26), false), -}; - -static const struct rockchip_domain_info rk3288_pm_domains[] = { - [RK3288_PD_VIO] = DOMAIN_RK3288("vio", BIT(7), BIT(7), BIT(4), false), - [RK3288_PD_HEVC] = DOMAIN_RK3288("hevc", BIT(14), BIT(10), BIT(9), false), - [RK3288_PD_VIDEO] = DOMAIN_RK3288("video", BIT(8), BIT(8), BIT(3), false), - [RK3288_PD_GPU] = DOMAIN_RK3288("gpu", BIT(9), BIT(9), BIT(2), false), -}; - -static const struct rockchip_domain_info rk3328_pm_domains[] = { - [RK3328_PD_CORE] = DOMAIN_RK3328("core", 0, BIT(0), BIT(0), false), - [RK3328_PD_GPU] = DOMAIN_RK3328("gpu", 0, BIT(1), BIT(1), false), - [RK3328_PD_BUS] = DOMAIN_RK3328("bus", 0, BIT(2), BIT(2), true), - [RK3328_PD_MSCH] = DOMAIN_RK3328("msch", 0, BIT(3), BIT(3), true), - [RK3328_PD_PERI] = DOMAIN_RK3328("peri", 0, BIT(4), BIT(4), true), - [RK3328_PD_VIDEO] = DOMAIN_RK3328("video", 0, BIT(5), BIT(5), false), - [RK3328_PD_HEVC] = DOMAIN_RK3328("hevc", 0, BIT(6), BIT(6), false), - [RK3328_PD_VIO] = DOMAIN_RK3328("vio", 0, BIT(8), BIT(8), false), - [RK3328_PD_VPU] = DOMAIN_RK3328("vpu", 0, BIT(9), BIT(9), false), -}; - -static const struct rockchip_domain_info rk3366_pm_domains[] = { - [RK3366_PD_PERI] = DOMAIN_RK3368("peri", BIT(10), BIT(10), BIT(6), true), - [RK3366_PD_VIO] = DOMAIN_RK3368("vio", BIT(14), BIT(14), BIT(8), false), - [RK3366_PD_VIDEO] = DOMAIN_RK3368("video", BIT(13), BIT(13), BIT(7), false), - [RK3366_PD_RKVDEC] = DOMAIN_RK3368("vdec", BIT(11), BIT(11), BIT(7), false), - [RK3366_PD_WIFIBT] = DOMAIN_RK3368("wifibt", BIT(8), BIT(8), BIT(9), false), - [RK3366_PD_VPU] = DOMAIN_RK3368("vpu", BIT(12), BIT(12), BIT(7), false), - [RK3366_PD_GPU] = DOMAIN_RK3368("gpu", BIT(15), BIT(15), BIT(2), false), -}; - -static const struct rockchip_domain_info rk3368_pm_domains[] = { - [RK3368_PD_PERI] = DOMAIN_RK3368("peri", BIT(13), BIT(12), BIT(6), true), - [RK3368_PD_VIO] = DOMAIN_RK3368("vio", BIT(15), BIT(14), BIT(8), false), - [RK3368_PD_VIDEO] = DOMAIN_RK3368("video", BIT(14), BIT(13), BIT(7), false), - [RK3368_PD_GPU_0] = DOMAIN_RK3368("gpu_0", BIT(16), BIT(15), BIT(2), false), - [RK3368_PD_GPU_1] = DOMAIN_RK3368("gpu_1", BIT(17), BIT(16), BIT(2), false), -}; - -static const struct rockchip_domain_info rk3399_pm_domains[] = { - [RK3399_PD_TCPD0] = DOMAIN_RK3399("tcpd0", BIT(8), BIT(8), 0, false), - [RK3399_PD_TCPD1] = DOMAIN_RK3399("tcpd1", BIT(9), BIT(9), 0, false), - [RK3399_PD_CCI] = DOMAIN_RK3399("cci", BIT(10), BIT(10), 0, true), - [RK3399_PD_CCI0] = DOMAIN_RK3399("cci0", 0, 0, BIT(15), true), - [RK3399_PD_CCI1] = DOMAIN_RK3399("cci1", 0, 0, BIT(16), true), - [RK3399_PD_PERILP] = DOMAIN_RK3399("perilp", BIT(11), BIT(11), BIT(1), true), - [RK3399_PD_PERIHP] = DOMAIN_RK3399("perihp", BIT(12), BIT(12), BIT(2), true), - [RK3399_PD_CENTER] = DOMAIN_RK3399("center", BIT(13), BIT(13), BIT(14), true), - [RK3399_PD_VIO] = DOMAIN_RK3399("vio", BIT(14), BIT(14), BIT(17), false), - [RK3399_PD_GPU] = DOMAIN_RK3399("gpu", BIT(15), BIT(15), BIT(0), false), - [RK3399_PD_VCODEC] = DOMAIN_RK3399("vcodec", BIT(16), BIT(16), BIT(3), false), - [RK3399_PD_VDU] = DOMAIN_RK3399("vdu", BIT(17), BIT(17), BIT(4), false), - [RK3399_PD_RGA] = DOMAIN_RK3399("rga", BIT(18), BIT(18), BIT(5), false), - [RK3399_PD_IEP] = DOMAIN_RK3399("iep", BIT(19), BIT(19), BIT(6), false), - [RK3399_PD_VO] = DOMAIN_RK3399("vo", BIT(20), BIT(20), 0, false), - [RK3399_PD_VOPB] = DOMAIN_RK3399("vopb", 0, 0, BIT(7), false), - [RK3399_PD_VOPL] = DOMAIN_RK3399("vopl", 0, 0, BIT(8), false), - [RK3399_PD_ISP0] = DOMAIN_RK3399("isp0", BIT(22), BIT(22), BIT(9), false), - [RK3399_PD_ISP1] = DOMAIN_RK3399("isp1", BIT(23), BIT(23), BIT(10), false), - [RK3399_PD_HDCP] = DOMAIN_RK3399("hdcp", BIT(24), BIT(24), BIT(11), false), - [RK3399_PD_GMAC] = DOMAIN_RK3399("gmac", BIT(25), BIT(25), BIT(23), true), - [RK3399_PD_EMMC] = DOMAIN_RK3399("emmc", BIT(26), BIT(26), BIT(24), true), - [RK3399_PD_USB3] = DOMAIN_RK3399("usb3", BIT(27), BIT(27), BIT(12), true), - [RK3399_PD_EDP] = DOMAIN_RK3399("edp", BIT(28), BIT(28), BIT(22), false), - [RK3399_PD_GIC] = DOMAIN_RK3399("gic", BIT(29), BIT(29), BIT(27), true), - [RK3399_PD_SD] = DOMAIN_RK3399("sd", BIT(30), BIT(30), BIT(28), true), - [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399("sdioaudio", BIT(31), BIT(31), BIT(29), true), -}; - -static const struct rockchip_domain_info rk3568_pm_domains[] = { - [RK3568_PD_NPU] = DOMAIN_RK3568("npu", BIT(1), BIT(2), false), - [RK3568_PD_GPU] = DOMAIN_RK3568("gpu", BIT(0), BIT(1), false), - [RK3568_PD_VI] = DOMAIN_RK3568("vi", BIT(6), BIT(3), false), - [RK3568_PD_VO] = DOMAIN_RK3568("vo", BIT(7), BIT(4), false), - [RK3568_PD_RGA] = DOMAIN_RK3568("rga", BIT(5), BIT(5), false), - [RK3568_PD_VPU] = DOMAIN_RK3568("vpu", BIT(2), BIT(6), false), - [RK3568_PD_RKVDEC] = DOMAIN_RK3568("vdec", BIT(4), BIT(8), false), - [RK3568_PD_RKVENC] = DOMAIN_RK3568("venc", BIT(3), BIT(7), false), - [RK3568_PD_PIPE] = DOMAIN_RK3568("pipe", BIT(8), BIT(11), false), -}; - -static const struct rockchip_domain_info rk3588_pm_domains[] = { - [RK3588_PD_GPU] = DOMAIN_RK3588("gpu", 0x0, BIT(0), 0, 0x0, 0, BIT(1), 0x0, BIT(0), BIT(0), false), - [RK3588_PD_NPU] = DOMAIN_RK3588("npu", 0x0, BIT(1), BIT(1), 0x0, 0, 0, 0x0, 0, 0, false), - [RK3588_PD_VCODEC] = DOMAIN_RK3588("vcodec", 0x0, BIT(2), BIT(2), 0x0, 0, 0, 0x0, 0, 0, false), - [RK3588_PD_NPUTOP] = DOMAIN_RK3588("nputop", 0x0, BIT(3), 0, 0x0, BIT(11), BIT(2), 0x0, BIT(1), BIT(1), false), - [RK3588_PD_NPU1] = DOMAIN_RK3588("npu1", 0x0, BIT(4), 0, 0x0, BIT(12), BIT(3), 0x0, BIT(2), BIT(2), false), - [RK3588_PD_NPU2] = DOMAIN_RK3588("npu2", 0x0, BIT(5), 0, 0x0, BIT(13), BIT(4), 0x0, BIT(3), BIT(3), false), - [RK3588_PD_VENC0] = DOMAIN_RK3588("venc0", 0x0, BIT(6), 0, 0x0, BIT(14), BIT(5), 0x0, BIT(4), BIT(4), false), - [RK3588_PD_VENC1] = DOMAIN_RK3588("venc1", 0x0, BIT(7), 0, 0x0, BIT(15), BIT(6), 0x0, BIT(5), BIT(5), false), - [RK3588_PD_RKVDEC0] = DOMAIN_RK3588("rkvdec0", 0x0, BIT(8), 0, 0x0, BIT(16), BIT(7), 0x0, BIT(6), BIT(6), false), - [RK3588_PD_RKVDEC1] = DOMAIN_RK3588("rkvdec1", 0x0, BIT(9), 0, 0x0, BIT(17), BIT(8), 0x0, BIT(7), BIT(7), false), - [RK3588_PD_VDPU] = DOMAIN_RK3588("vdpu", 0x0, BIT(10), 0, 0x0, BIT(18), BIT(9), 0x0, BIT(8), BIT(8), false), - [RK3588_PD_RGA30] = DOMAIN_RK3588("rga30", 0x0, BIT(11), 0, 0x0, BIT(19), BIT(10), 0x0, 0, 0, false), - [RK3588_PD_AV1] = DOMAIN_RK3588("av1", 0x0, BIT(12), 0, 0x0, BIT(20), BIT(11), 0x0, BIT(9), BIT(9), false), - [RK3588_PD_VI] = DOMAIN_RK3588("vi", 0x0, BIT(13), 0, 0x0, BIT(21), BIT(12), 0x0, BIT(10), BIT(10), false), - [RK3588_PD_FEC] = DOMAIN_RK3588("fec", 0x0, BIT(14), 0, 0x0, BIT(22), BIT(13), 0x0, 0, 0, false), - [RK3588_PD_ISP1] = DOMAIN_RK3588("isp1", 0x0, BIT(15), 0, 0x0, BIT(23), BIT(14), 0x0, BIT(11), BIT(11), false), - [RK3588_PD_RGA31] = DOMAIN_RK3588("rga31", 0x4, BIT(0), 0, 0x0, BIT(24), BIT(15), 0x0, BIT(12), BIT(12), false), - [RK3588_PD_VOP] = DOMAIN_RK3588("vop", 0x4, BIT(1), 0, 0x0, BIT(25), BIT(16), 0x0, BIT(13) | BIT(14), BIT(13) | BIT(14), false), - [RK3588_PD_VO0] = DOMAIN_RK3588("vo0", 0x4, BIT(2), 0, 0x0, BIT(26), BIT(17), 0x0, BIT(15), BIT(15), false), - [RK3588_PD_VO1] = DOMAIN_RK3588("vo1", 0x4, BIT(3), 0, 0x0, BIT(27), BIT(18), 0x4, BIT(0), BIT(16), false), - [RK3588_PD_AUDIO] = DOMAIN_RK3588("audio", 0x4, BIT(4), 0, 0x0, BIT(28), BIT(19), 0x4, BIT(1), BIT(17), false), - [RK3588_PD_PHP] = DOMAIN_RK3588("php", 0x4, BIT(5), 0, 0x0, BIT(29), BIT(20), 0x4, BIT(5), BIT(21), false), - [RK3588_PD_GMAC] = DOMAIN_RK3588("gmac", 0x4, BIT(6), 0, 0x0, BIT(30), BIT(21), 0x0, 0, 0, false), - [RK3588_PD_PCIE] = DOMAIN_RK3588("pcie", 0x4, BIT(7), 0, 0x0, BIT(31), BIT(22), 0x0, 0, 0, true), - [RK3588_PD_NVM] = DOMAIN_RK3588("nvm", 0x4, BIT(8), BIT(24), 0x4, 0, 0, 0x4, BIT(2), BIT(18), false), - [RK3588_PD_NVM0] = DOMAIN_RK3588("nvm0", 0x4, BIT(9), 0, 0x4, BIT(1), BIT(23), 0x0, 0, 0, false), - [RK3588_PD_SDIO] = DOMAIN_RK3588("sdio", 0x4, BIT(10), 0, 0x4, BIT(2), BIT(24), 0x4, BIT(3), BIT(19), false), - [RK3588_PD_USB] = DOMAIN_RK3588("usb", 0x4, BIT(11), 0, 0x4, BIT(3), BIT(25), 0x4, BIT(4), BIT(20), true), - [RK3588_PD_SDMMC] = DOMAIN_RK3588("sdmmc", 0x4, BIT(13), 0, 0x4, BIT(5), BIT(26), 0x0, 0, 0, false), -}; - -static const struct rockchip_pmu_info px30_pmu = { - .pwr_offset = 0x18, - .status_offset = 0x20, - .req_offset = 0x64, - .idle_offset = 0x6c, - .ack_offset = 0x6c, - - .num_domains = ARRAY_SIZE(px30_pm_domains), - .domain_info = px30_pm_domains, -}; - -static const struct rockchip_pmu_info rk3036_pmu = { - .req_offset = 0x148, - .idle_offset = 0x14c, - .ack_offset = 0x14c, - - .num_domains = ARRAY_SIZE(rk3036_pm_domains), - .domain_info = rk3036_pm_domains, -}; - -static const struct rockchip_pmu_info rk3066_pmu = { - .pwr_offset = 0x08, - .status_offset = 0x0c, - .req_offset = 0x38, /* PMU_MISC_CON1 */ - .idle_offset = 0x0c, - .ack_offset = 0x0c, - - .num_domains = ARRAY_SIZE(rk3066_pm_domains), - .domain_info = rk3066_pm_domains, -}; - -static const struct rockchip_pmu_info rk3128_pmu = { - .pwr_offset = 0x04, - .status_offset = 0x08, - .req_offset = 0x0c, - .idle_offset = 0x10, - .ack_offset = 0x10, - - .num_domains = ARRAY_SIZE(rk3128_pm_domains), - .domain_info = rk3128_pm_domains, -}; - -static const struct rockchip_pmu_info rk3188_pmu = { - .pwr_offset = 0x08, - .status_offset = 0x0c, - .req_offset = 0x38, /* PMU_MISC_CON1 */ - .idle_offset = 0x0c, - .ack_offset = 0x0c, - - .num_domains = ARRAY_SIZE(rk3188_pm_domains), - .domain_info = rk3188_pm_domains, -}; - -static const struct rockchip_pmu_info rk3228_pmu = { - .req_offset = 0x40c, - .idle_offset = 0x488, - .ack_offset = 0x488, - - .num_domains = ARRAY_SIZE(rk3228_pm_domains), - .domain_info = rk3228_pm_domains, -}; - -static const struct rockchip_pmu_info rk3288_pmu = { - .pwr_offset = 0x08, - .status_offset = 0x0c, - .req_offset = 0x10, - .idle_offset = 0x14, - .ack_offset = 0x14, - - .core_pwrcnt_offset = 0x34, - .gpu_pwrcnt_offset = 0x3c, - - .core_power_transition_time = 24, /* 1us */ - .gpu_power_transition_time = 24, /* 1us */ - - .num_domains = ARRAY_SIZE(rk3288_pm_domains), - .domain_info = rk3288_pm_domains, -}; - -static const struct rockchip_pmu_info rk3328_pmu = { - .req_offset = 0x414, - .idle_offset = 0x484, - .ack_offset = 0x484, - - .num_domains = ARRAY_SIZE(rk3328_pm_domains), - .domain_info = rk3328_pm_domains, -}; - -static const struct rockchip_pmu_info rk3366_pmu = { - .pwr_offset = 0x0c, - .status_offset = 0x10, - .req_offset = 0x3c, - .idle_offset = 0x40, - .ack_offset = 0x40, - - .core_pwrcnt_offset = 0x48, - .gpu_pwrcnt_offset = 0x50, - - .core_power_transition_time = 24, - .gpu_power_transition_time = 24, - - .num_domains = ARRAY_SIZE(rk3366_pm_domains), - .domain_info = rk3366_pm_domains, -}; - -static const struct rockchip_pmu_info rk3368_pmu = { - .pwr_offset = 0x0c, - .status_offset = 0x10, - .req_offset = 0x3c, - .idle_offset = 0x40, - .ack_offset = 0x40, - - .core_pwrcnt_offset = 0x48, - .gpu_pwrcnt_offset = 0x50, - - .core_power_transition_time = 24, - .gpu_power_transition_time = 24, - - .num_domains = ARRAY_SIZE(rk3368_pm_domains), - .domain_info = rk3368_pm_domains, -}; - -static const struct rockchip_pmu_info rk3399_pmu = { - .pwr_offset = 0x14, - .status_offset = 0x18, - .req_offset = 0x60, - .idle_offset = 0x64, - .ack_offset = 0x68, - - /* ARM Trusted Firmware manages power transition times */ - - .num_domains = ARRAY_SIZE(rk3399_pm_domains), - .domain_info = rk3399_pm_domains, -}; - -static const struct rockchip_pmu_info rk3568_pmu = { - .pwr_offset = 0xa0, - .status_offset = 0x98, - .req_offset = 0x50, - .idle_offset = 0x68, - .ack_offset = 0x60, - - .num_domains = ARRAY_SIZE(rk3568_pm_domains), - .domain_info = rk3568_pm_domains, -}; - -static const struct rockchip_pmu_info rk3588_pmu = { - .pwr_offset = 0x14c, - .status_offset = 0x180, - .req_offset = 0x10c, - .idle_offset = 0x120, - .ack_offset = 0x118, - .mem_pwr_offset = 0x1a0, - .chain_status_offset = 0x1f0, - .mem_status_offset = 0x1f8, - .repair_status_offset = 0x290, - - .num_domains = ARRAY_SIZE(rk3588_pm_domains), - .domain_info = rk3588_pm_domains, -}; - -static const struct rockchip_pmu_info rv1126_pmu = { - .pwr_offset = 0x110, - .status_offset = 0x108, - .req_offset = 0xc0, - .idle_offset = 0xd8, - .ack_offset = 0xd0, - - .num_domains = ARRAY_SIZE(rv1126_pm_domains), - .domain_info = rv1126_pm_domains, -}; - -static const struct of_device_id rockchip_pm_domain_dt_match[] = { - { - .compatible = "rockchip,px30-power-controller", - .data = (void *)&px30_pmu, - }, - { - .compatible = "rockchip,rk3036-power-controller", - .data = (void *)&rk3036_pmu, - }, - { - .compatible = "rockchip,rk3066-power-controller", - .data = (void *)&rk3066_pmu, - }, - { - .compatible = "rockchip,rk3128-power-controller", - .data = (void *)&rk3128_pmu, - }, - { - .compatible = "rockchip,rk3188-power-controller", - .data = (void *)&rk3188_pmu, - }, - { - .compatible = "rockchip,rk3228-power-controller", - .data = (void *)&rk3228_pmu, - }, - { - .compatible = "rockchip,rk3288-power-controller", - .data = (void *)&rk3288_pmu, - }, - { - .compatible = "rockchip,rk3328-power-controller", - .data = (void *)&rk3328_pmu, - }, - { - .compatible = "rockchip,rk3366-power-controller", - .data = (void *)&rk3366_pmu, - }, - { - .compatible = "rockchip,rk3368-power-controller", - .data = (void *)&rk3368_pmu, - }, - { - .compatible = "rockchip,rk3399-power-controller", - .data = (void *)&rk3399_pmu, - }, - { - .compatible = "rockchip,rk3568-power-controller", - .data = (void *)&rk3568_pmu, - }, - { - .compatible = "rockchip,rk3588-power-controller", - .data = (void *)&rk3588_pmu, - }, - { - .compatible = "rockchip,rv1126-power-controller", - .data = (void *)&rv1126_pmu, - }, - { /* sentinel */ }, -}; - -static struct platform_driver rockchip_pm_domain_driver = { - .probe = rockchip_pm_domain_probe, - .driver = { - .name = "rockchip-pm-domain", - .of_match_table = rockchip_pm_domain_dt_match, - /* - * We can't forcibly eject devices from the power - * domain, so we can't really remove power domains - * once they were added. - */ - .suppress_bind_attrs = true, - }, -}; - -static int __init rockchip_pm_domain_drv_register(void) -{ - return platform_driver_register(&rockchip_pm_domain_driver); -} -postcore_initcall(rockchip_pm_domain_drv_register); diff --git a/drivers/genpd/samsung/Makefile b/drivers/genpd/samsung/Makefile deleted file mode 100644 index 397aa5908c1d..000000000000 --- a/drivers/genpd/samsung/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_EXYNOS_PM_DOMAINS) += exynos-pm-domains.o diff --git a/drivers/genpd/samsung/exynos-pm-domains.c b/drivers/genpd/samsung/exynos-pm-domains.c deleted file mode 100644 index 9b502e8751d1..000000000000 --- a/drivers/genpd/samsung/exynos-pm-domains.c +++ /dev/null @@ -1,167 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -// -// Exynos Generic power domain support. -// -// Copyright (c) 2012 Samsung Electronics Co., Ltd. -// http://www.samsung.com -// -// Implementation of Exynos specific power domain control which is used in -// conjunction with runtime-pm. Support for both device-tree and non-device-tree -// based power domain support is included. - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct exynos_pm_domain_config { - /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ - u32 local_pwr_cfg; -}; - -/* - * Exynos specific wrapper around the generic power domain - */ -struct exynos_pm_domain { - void __iomem *base; - struct generic_pm_domain pd; - u32 local_pwr_cfg; -}; - -static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) -{ - struct exynos_pm_domain *pd; - void __iomem *base; - u32 timeout, pwr; - char *op; - - pd = container_of(domain, struct exynos_pm_domain, pd); - base = pd->base; - - pwr = power_on ? pd->local_pwr_cfg : 0; - writel_relaxed(pwr, base); - - /* Wait max 1ms */ - timeout = 10; - - while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) { - if (!timeout) { - op = (power_on) ? "enable" : "disable"; - pr_err("Power domain %s %s failed\n", domain->name, op); - return -ETIMEDOUT; - } - timeout--; - cpu_relax(); - usleep_range(80, 100); - } - - return 0; -} - -static int exynos_pd_power_on(struct generic_pm_domain *domain) -{ - return exynos_pd_power(domain, true); -} - -static int exynos_pd_power_off(struct generic_pm_domain *domain) -{ - return exynos_pd_power(domain, false); -} - -static const struct exynos_pm_domain_config exynos4210_cfg = { - .local_pwr_cfg = 0x7, -}; - -static const struct exynos_pm_domain_config exynos5433_cfg = { - .local_pwr_cfg = 0xf, -}; - -static const struct of_device_id exynos_pm_domain_of_match[] = { - { - .compatible = "samsung,exynos4210-pd", - .data = &exynos4210_cfg, - }, { - .compatible = "samsung,exynos5433-pd", - .data = &exynos5433_cfg, - }, - { }, -}; - -static const char *exynos_get_domain_name(struct device_node *node) -{ - const char *name; - - if (of_property_read_string(node, "label", &name) < 0) - name = kbasename(node->full_name); - return kstrdup_const(name, GFP_KERNEL); -} - -static int exynos_pd_probe(struct platform_device *pdev) -{ - const struct exynos_pm_domain_config *pm_domain_cfg; - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct of_phandle_args child, parent; - struct exynos_pm_domain *pd; - int on, ret; - - pm_domain_cfg = of_device_get_match_data(dev); - pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - pd->pd.name = exynos_get_domain_name(np); - if (!pd->pd.name) - return -ENOMEM; - - pd->base = of_iomap(np, 0); - if (!pd->base) { - kfree_const(pd->pd.name); - return -ENODEV; - } - - pd->pd.power_off = exynos_pd_power_off; - pd->pd.power_on = exynos_pd_power_on; - pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; - - on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg; - - pm_genpd_init(&pd->pd, NULL, !on); - ret = of_genpd_add_provider_simple(np, &pd->pd); - - if (ret == 0 && of_parse_phandle_with_args(np, "power-domains", - "#power-domain-cells", 0, &parent) == 0) { - child.np = np; - child.args_count = 0; - - if (of_genpd_add_subdomain(&parent, &child)) - pr_warn("%pOF failed to add subdomain: %pOF\n", - parent.np, child.np); - else - pr_info("%pOF has as child subdomain: %pOF.\n", - parent.np, child.np); - } - - pm_runtime_enable(dev); - return ret; -} - -static struct platform_driver exynos_pd_driver = { - .probe = exynos_pd_probe, - .driver = { - .name = "exynos-pd", - .of_match_table = exynos_pm_domain_of_match, - .suppress_bind_attrs = true, - } -}; - -static __init int exynos4_pm_init_power_domain(void) -{ - return platform_driver_register(&exynos_pd_driver); -} -core_initcall(exynos4_pm_init_power_domain); diff --git a/drivers/genpd/st/Makefile b/drivers/genpd/st/Makefile deleted file mode 100644 index 8fa5f9855460..000000000000 --- a/drivers/genpd/st/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_ARCH_U8500) += ste-ux500-pm-domain.o diff --git a/drivers/genpd/st/ste-ux500-pm-domain.c b/drivers/genpd/st/ste-ux500-pm-domain.c deleted file mode 100644 index 3d4f111ed156..000000000000 --- a/drivers/genpd/st/ste-ux500-pm-domain.c +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (C) 2014 Linaro Ltd. - * - * Author: Ulf Hansson - * - * Implements PM domains using the generic PM domain for ux500. - */ -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -static int pd_power_off(struct generic_pm_domain *domain) -{ - /* - * Handle the gating of the PM domain regulator here. - * - * Drivers/subsystems handling devices in the PM domain needs to perform - * register context save/restore from their respective runtime PM - * callbacks, to be able to enable PM domain gating/ungating. - */ - return 0; -} - -static int pd_power_on(struct generic_pm_domain *domain) -{ - /* - * Handle the ungating of the PM domain regulator here. - * - * Drivers/subsystems handling devices in the PM domain needs to perform - * register context save/restore from their respective runtime PM - * callbacks, to be able to enable PM domain gating/ungating. - */ - return 0; -} - -static struct generic_pm_domain ux500_pm_domain_vape = { - .name = "VAPE", - .power_off = pd_power_off, - .power_on = pd_power_on, -}; - -static struct generic_pm_domain *ux500_pm_domains[NR_DOMAINS] = { - [DOMAIN_VAPE] = &ux500_pm_domain_vape, -}; - -static const struct of_device_id ux500_pm_domain_matches[] = { - { .compatible = "stericsson,ux500-pm-domains", }, - { }, -}; - -static int ux500_pm_domains_probe(struct platform_device *pdev) -{ - struct device_node *np = pdev->dev.of_node; - struct genpd_onecell_data *genpd_data; - int i; - - if (!np) - return -ENODEV; - - genpd_data = kzalloc(sizeof(*genpd_data), GFP_KERNEL); - if (!genpd_data) - return -ENOMEM; - - genpd_data->domains = ux500_pm_domains; - genpd_data->num_domains = ARRAY_SIZE(ux500_pm_domains); - - for (i = 0; i < ARRAY_SIZE(ux500_pm_domains); ++i) - pm_genpd_init(ux500_pm_domains[i], NULL, false); - - of_genpd_add_provider_onecell(np, genpd_data); - return 0; -} - -static struct platform_driver ux500_pm_domains_driver = { - .probe = ux500_pm_domains_probe, - .driver = { - .name = "ux500_pm_domains", - .of_match_table = ux500_pm_domain_matches, - }, -}; - -static int __init ux500_pm_domains_init(void) -{ - return platform_driver_register(&ux500_pm_domains_driver); -} -arch_initcall(ux500_pm_domains_init); diff --git a/drivers/genpd/starfive/Makefile b/drivers/genpd/starfive/Makefile deleted file mode 100644 index 975bba2a29a9..000000000000 --- a/drivers/genpd/starfive/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_JH71XX_PMU) += jh71xx-pmu.o diff --git a/drivers/genpd/starfive/jh71xx-pmu.c b/drivers/genpd/starfive/jh71xx-pmu.c deleted file mode 100644 index 7d5f50d71c0d..000000000000 --- a/drivers/genpd/starfive/jh71xx-pmu.c +++ /dev/null @@ -1,383 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * StarFive JH71XX PMU (Power Management Unit) Controller Driver - * - * Copyright (C) 2022 StarFive Technology Co., Ltd. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* register offset */ -#define JH71XX_PMU_SW_TURN_ON_POWER 0x0C -#define JH71XX_PMU_SW_TURN_OFF_POWER 0x10 -#define JH71XX_PMU_SW_ENCOURAGE 0x44 -#define JH71XX_PMU_TIMER_INT_MASK 0x48 -#define JH71XX_PMU_CURR_POWER_MODE 0x80 -#define JH71XX_PMU_EVENT_STATUS 0x88 -#define JH71XX_PMU_INT_STATUS 0x8C - -/* sw encourage cfg */ -#define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05 -#define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50 -#define JH71XX_PMU_SW_ENCOURAGE_DIS_LO 0x0A -#define JH71XX_PMU_SW_ENCOURAGE_DIS_HI 0xA0 -#define JH71XX_PMU_SW_ENCOURAGE_ON 0xFF - -/* pmu int status */ -#define JH71XX_PMU_INT_SEQ_DONE BIT(0) -#define JH71XX_PMU_INT_HW_REQ BIT(1) -#define JH71XX_PMU_INT_SW_FAIL GENMASK(3, 2) -#define JH71XX_PMU_INT_HW_FAIL GENMASK(5, 4) -#define JH71XX_PMU_INT_PCH_FAIL GENMASK(8, 6) -#define JH71XX_PMU_INT_ALL_MASK GENMASK(8, 0) - -/* - * The time required for switching power status is based on the time - * to turn on the largest domain's power, which is at microsecond level - */ -#define JH71XX_PMU_TIMEOUT_US 100 - -struct jh71xx_domain_info { - const char * const name; - unsigned int flags; - u8 bit; -}; - -struct jh71xx_pmu_match_data { - const struct jh71xx_domain_info *domain_info; - int num_domains; -}; - -struct jh71xx_pmu { - struct device *dev; - const struct jh71xx_pmu_match_data *match_data; - void __iomem *base; - struct generic_pm_domain **genpd; - struct genpd_onecell_data genpd_data; - int irq; - spinlock_t lock; /* protects pmu reg */ -}; - -struct jh71xx_pmu_dev { - const struct jh71xx_domain_info *domain_info; - struct jh71xx_pmu *pmu; - struct generic_pm_domain genpd; -}; - -static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool *is_on) -{ - struct jh71xx_pmu *pmu = pmd->pmu; - - if (!mask) - return -EINVAL; - - *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask; - - return 0; -} - -static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) -{ - struct jh71xx_pmu *pmu = pmd->pmu; - unsigned long flags; - u32 val; - u32 mode; - u32 encourage_lo; - u32 encourage_hi; - bool is_on; - int ret; - - ret = jh71xx_pmu_get_state(pmd, mask, &is_on); - if (ret) { - dev_dbg(pmu->dev, "unable to get current state for %s\n", - pmd->genpd.name); - return ret; - } - - if (is_on == on) { - dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", - pmd->genpd.name, on ? "en" : "dis"); - return 0; - } - - spin_lock_irqsave(&pmu->lock, flags); - - /* - * The PMU accepts software encourage to switch power mode in the following 2 steps: - * - * 1.Configure the register SW_TURN_ON_POWER (offset 0x0c) by writing 1 to - * the bit corresponding to the power domain that will be turned on - * and writing 0 to the others. - * Likewise, configure the register SW_TURN_OFF_POWER (offset 0x10) by - * writing 1 to the bit corresponding to the power domain that will be - * turned off and writing 0 to the others. - */ - if (on) { - mode = JH71XX_PMU_SW_TURN_ON_POWER; - encourage_lo = JH71XX_PMU_SW_ENCOURAGE_EN_LO; - encourage_hi = JH71XX_PMU_SW_ENCOURAGE_EN_HI; - } else { - mode = JH71XX_PMU_SW_TURN_OFF_POWER; - encourage_lo = JH71XX_PMU_SW_ENCOURAGE_DIS_LO; - encourage_hi = JH71XX_PMU_SW_ENCOURAGE_DIS_HI; - } - - writel(mask, pmu->base + mode); - - /* - * 2.Write SW encourage command sequence to the Software Encourage Reg (offset 0x44) - * First write SW_MODE_ENCOURAGE_ON to JH71XX_PMU_SW_ENCOURAGE. This will reset - * the state machine which parses the command sequence. This register must be - * written every time software wants to power on/off a domain. - * Then write the lower bits of the command sequence, followed by the upper - * bits. The sequence differs between powering on & off a domain. - */ - writel(JH71XX_PMU_SW_ENCOURAGE_ON, pmu->base + JH71XX_PMU_SW_ENCOURAGE); - writel(encourage_lo, pmu->base + JH71XX_PMU_SW_ENCOURAGE); - writel(encourage_hi, pmu->base + JH71XX_PMU_SW_ENCOURAGE); - - spin_unlock_irqrestore(&pmu->lock, flags); - - /* Wait for the power domain bit to be enabled / disabled */ - if (on) { - ret = readl_poll_timeout_atomic(pmu->base + JH71XX_PMU_CURR_POWER_MODE, - val, val & mask, - 1, JH71XX_PMU_TIMEOUT_US); - } else { - ret = readl_poll_timeout_atomic(pmu->base + JH71XX_PMU_CURR_POWER_MODE, - val, !(val & mask), - 1, JH71XX_PMU_TIMEOUT_US); - } - - if (ret) { - dev_err(pmu->dev, "%s: failed to power %s\n", - pmd->genpd.name, on ? "on" : "off"); - return -ETIMEDOUT; - } - - return 0; -} - -static int jh71xx_pmu_on(struct generic_pm_domain *genpd) -{ - struct jh71xx_pmu_dev *pmd = container_of(genpd, - struct jh71xx_pmu_dev, genpd); - u32 pwr_mask = BIT(pmd->domain_info->bit); - - return jh71xx_pmu_set_state(pmd, pwr_mask, true); -} - -static int jh71xx_pmu_off(struct generic_pm_domain *genpd) -{ - struct jh71xx_pmu_dev *pmd = container_of(genpd, - struct jh71xx_pmu_dev, genpd); - u32 pwr_mask = BIT(pmd->domain_info->bit); - - return jh71xx_pmu_set_state(pmd, pwr_mask, false); -} - -static void jh71xx_pmu_int_enable(struct jh71xx_pmu *pmu, u32 mask, bool enable) -{ - u32 val; - unsigned long flags; - - spin_lock_irqsave(&pmu->lock, flags); - val = readl(pmu->base + JH71XX_PMU_TIMER_INT_MASK); - - if (enable) - val &= ~mask; - else - val |= mask; - - writel(val, pmu->base + JH71XX_PMU_TIMER_INT_MASK); - spin_unlock_irqrestore(&pmu->lock, flags); -} - -static irqreturn_t jh71xx_pmu_interrupt(int irq, void *data) -{ - struct jh71xx_pmu *pmu = data; - u32 val; - - val = readl(pmu->base + JH71XX_PMU_INT_STATUS); - - if (val & JH71XX_PMU_INT_SEQ_DONE) - dev_dbg(pmu->dev, "sequence done.\n"); - if (val & JH71XX_PMU_INT_HW_REQ) - dev_dbg(pmu->dev, "hardware encourage requestion.\n"); - if (val & JH71XX_PMU_INT_SW_FAIL) - dev_err(pmu->dev, "software encourage fail.\n"); - if (val & JH71XX_PMU_INT_HW_FAIL) - dev_err(pmu->dev, "hardware encourage fail.\n"); - if (val & JH71XX_PMU_INT_PCH_FAIL) - dev_err(pmu->dev, "p-channel fail event.\n"); - - /* clear interrupts */ - writel(val, pmu->base + JH71XX_PMU_INT_STATUS); - writel(val, pmu->base + JH71XX_PMU_EVENT_STATUS); - - return IRQ_HANDLED; -} - -static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index) -{ - struct jh71xx_pmu_dev *pmd; - u32 pwr_mask; - int ret; - bool is_on = false; - - pmd = devm_kzalloc(pmu->dev, sizeof(*pmd), GFP_KERNEL); - if (!pmd) - return -ENOMEM; - - pmd->domain_info = &pmu->match_data->domain_info[index]; - pmd->pmu = pmu; - pwr_mask = BIT(pmd->domain_info->bit); - - pmd->genpd.name = pmd->domain_info->name; - pmd->genpd.flags = pmd->domain_info->flags; - - ret = jh71xx_pmu_get_state(pmd, pwr_mask, &is_on); - if (ret) - dev_warn(pmu->dev, "unable to get current state for %s\n", - pmd->genpd.name); - - pmd->genpd.power_on = jh71xx_pmu_on; - pmd->genpd.power_off = jh71xx_pmu_off; - pm_genpd_init(&pmd->genpd, NULL, !is_on); - - pmu->genpd_data.domains[index] = &pmd->genpd; - - return 0; -} - -static int jh71xx_pmu_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - const struct jh71xx_pmu_match_data *match_data; - struct jh71xx_pmu *pmu; - unsigned int i; - int ret; - - pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL); - if (!pmu) - return -ENOMEM; - - pmu->base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(pmu->base)) - return PTR_ERR(pmu->base); - - pmu->irq = platform_get_irq(pdev, 0); - if (pmu->irq < 0) - return pmu->irq; - - ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, - 0, pdev->name, pmu); - if (ret) - dev_err(dev, "failed to request irq\n"); - - match_data = of_device_get_match_data(dev); - if (!match_data) - return -EINVAL; - - pmu->genpd = devm_kcalloc(dev, match_data->num_domains, - sizeof(struct generic_pm_domain *), - GFP_KERNEL); - if (!pmu->genpd) - return -ENOMEM; - - pmu->dev = dev; - pmu->match_data = match_data; - pmu->genpd_data.domains = pmu->genpd; - pmu->genpd_data.num_domains = match_data->num_domains; - - for (i = 0; i < match_data->num_domains; i++) { - ret = jh71xx_pmu_init_domain(pmu, i); - if (ret) { - dev_err(dev, "failed to initialize power domain\n"); - return ret; - } - } - - spin_lock_init(&pmu->lock); - jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); - - ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data); - if (ret) { - dev_err(dev, "failed to register genpd driver: %d\n", ret); - return ret; - } - - dev_dbg(dev, "registered %u power domains\n", i); - - return 0; -} - -static const struct jh71xx_domain_info jh7110_power_domains[] = { - [JH7110_PD_SYSTOP] = { - .name = "SYSTOP", - .bit = 0, - .flags = GENPD_FLAG_ALWAYS_ON, - }, - [JH7110_PD_CPU] = { - .name = "CPU", - .bit = 1, - .flags = GENPD_FLAG_ALWAYS_ON, - }, - [JH7110_PD_GPUA] = { - .name = "GPUA", - .bit = 2, - }, - [JH7110_PD_VDEC] = { - .name = "VDEC", - .bit = 3, - }, - [JH7110_PD_VOUT] = { - .name = "VOUT", - .bit = 4, - }, - [JH7110_PD_ISP] = { - .name = "ISP", - .bit = 5, - }, - [JH7110_PD_VENC] = { - .name = "VENC", - .bit = 6, - }, -}; - -static const struct jh71xx_pmu_match_data jh7110_pmu = { - .num_domains = ARRAY_SIZE(jh7110_power_domains), - .domain_info = jh7110_power_domains, -}; - -static const struct of_device_id jh71xx_pmu_of_match[] = { - { - .compatible = "starfive,jh7110-pmu", - .data = (void *)&jh7110_pmu, - }, { - /* sentinel */ - } -}; - -static struct platform_driver jh71xx_pmu_driver = { - .probe = jh71xx_pmu_probe, - .driver = { - .name = "jh71xx-pmu", - .of_match_table = jh71xx_pmu_of_match, - .suppress_bind_attrs = true, - }, -}; -builtin_platform_driver(jh71xx_pmu_driver); - -MODULE_AUTHOR("Walker Chen "); -MODULE_DESCRIPTION("StarFive JH71XX PMU Driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/genpd/sunxi/Makefile b/drivers/genpd/sunxi/Makefile deleted file mode 100644 index ec1d7a2fb21d..000000000000 --- a/drivers/genpd/sunxi/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o diff --git a/drivers/genpd/sunxi/sun20i-ppu.c b/drivers/genpd/sunxi/sun20i-ppu.c deleted file mode 100644 index 8700f9dd5f75..000000000000 --- a/drivers/genpd/sunxi/sun20i-ppu.c +++ /dev/null @@ -1,207 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PD_STATE_ON 1 -#define PD_STATE_OFF 2 - -#define PD_RSTN_REG 0x00 -#define PD_CLK_GATE_REG 0x04 -#define PD_PWROFF_GATE_REG 0x08 -#define PD_PSW_ON_REG 0x0c -#define PD_PSW_OFF_REG 0x10 -#define PD_PSW_DELAY_REG 0x14 -#define PD_OFF_DELAY_REG 0x18 -#define PD_ON_DELAY_REG 0x1c -#define PD_COMMAND_REG 0x20 -#define PD_STATUS_REG 0x24 -#define PD_STATUS_COMPLETE BIT(1) -#define PD_STATUS_BUSY BIT(3) -#define PD_STATUS_STATE GENMASK(17, 16) -#define PD_ACTIVE_CTRL_REG 0x2c -#define PD_GATE_STATUS_REG 0x30 -#define PD_RSTN_STATUS BIT(0) -#define PD_CLK_GATE_STATUS BIT(1) -#define PD_PWROFF_GATE_STATUS BIT(2) -#define PD_PSW_STATUS_REG 0x34 - -#define PD_REGS_SIZE 0x80 - -struct sun20i_ppu_desc { - const char *const *names; - unsigned int num_domains; -}; - -struct sun20i_ppu_pd { - struct generic_pm_domain genpd; - void __iomem *base; -}; - -#define to_sun20i_ppu_pd(_genpd) \ - container_of(_genpd, struct sun20i_ppu_pd, genpd) - -static bool sun20i_ppu_pd_is_on(const struct sun20i_ppu_pd *pd) -{ - u32 status = readl(pd->base + PD_STATUS_REG); - - return FIELD_GET(PD_STATUS_STATE, status) == PD_STATE_ON; -} - -static int sun20i_ppu_pd_set_power(const struct sun20i_ppu_pd *pd, bool power_on) -{ - u32 state, status; - int ret; - - if (sun20i_ppu_pd_is_on(pd) == power_on) - return 0; - - /* Wait for the power controller to be idle. */ - ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status, - !(status & PD_STATUS_BUSY), 100, 1000); - if (ret) - return ret; - - state = power_on ? PD_STATE_ON : PD_STATE_OFF; - writel(state, pd->base + PD_COMMAND_REG); - - /* Wait for the state transition to complete. */ - ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status, - FIELD_GET(PD_STATUS_STATE, status) == state && - (status & PD_STATUS_COMPLETE), 100, 1000); - if (ret) - return ret; - - /* Clear the completion flag. */ - writel(status, pd->base + PD_STATUS_REG); - - return 0; -} - -static int sun20i_ppu_pd_power_on(struct generic_pm_domain *genpd) -{ - const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd); - - return sun20i_ppu_pd_set_power(pd, true); -} - -static int sun20i_ppu_pd_power_off(struct generic_pm_domain *genpd) -{ - const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd); - - return sun20i_ppu_pd_set_power(pd, false); -} - -static int sun20i_ppu_probe(struct platform_device *pdev) -{ - const struct sun20i_ppu_desc *desc; - struct device *dev = &pdev->dev; - struct genpd_onecell_data *ppu; - struct sun20i_ppu_pd *pds; - struct reset_control *rst; - void __iomem *base; - struct clk *clk; - int ret; - - desc = of_device_get_match_data(dev); - if (!desc) - return -EINVAL; - - pds = devm_kcalloc(dev, desc->num_domains, sizeof(*pds), GFP_KERNEL); - if (!pds) - return -ENOMEM; - - ppu = devm_kzalloc(dev, sizeof(*ppu), GFP_KERNEL); - if (!ppu) - return -ENOMEM; - - ppu->domains = devm_kcalloc(dev, desc->num_domains, - sizeof(*ppu->domains), GFP_KERNEL); - if (!ppu->domains) - return -ENOMEM; - - ppu->num_domains = desc->num_domains; - platform_set_drvdata(pdev, ppu); - - base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(base)) - return PTR_ERR(base); - - clk = devm_clk_get_enabled(dev, NULL); - if (IS_ERR(clk)) - return PTR_ERR(clk); - - rst = devm_reset_control_get_exclusive(dev, NULL); - if (IS_ERR(rst)) - return PTR_ERR(rst); - - ret = reset_control_deassert(rst); - if (ret) - return ret; - - for (unsigned int i = 0; i < ppu->num_domains; ++i) { - struct sun20i_ppu_pd *pd = &pds[i]; - - pd->genpd.name = desc->names[i]; - pd->genpd.power_off = sun20i_ppu_pd_power_off; - pd->genpd.power_on = sun20i_ppu_pd_power_on; - pd->base = base + PD_REGS_SIZE * i; - - ret = pm_genpd_init(&pd->genpd, NULL, sun20i_ppu_pd_is_on(pd)); - if (ret) { - dev_warn(dev, "Failed to add '%s' domain: %d\n", - pd->genpd.name, ret); - continue; - } - - ppu->domains[i] = &pd->genpd; - } - - ret = of_genpd_add_provider_onecell(dev->of_node, ppu); - if (ret) - dev_warn(dev, "Failed to add provider: %d\n", ret); - - return 0; -} - -static const char *const sun20i_d1_ppu_pd_names[] = { - "CPU", - "VE", - "DSP", -}; - -static const struct sun20i_ppu_desc sun20i_d1_ppu_desc = { - .names = sun20i_d1_ppu_pd_names, - .num_domains = ARRAY_SIZE(sun20i_d1_ppu_pd_names), -}; - -static const struct of_device_id sun20i_ppu_of_match[] = { - { - .compatible = "allwinner,sun20i-d1-ppu", - .data = &sun20i_d1_ppu_desc, - }, - { } -}; -MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match); - -static struct platform_driver sun20i_ppu_driver = { - .probe = sun20i_ppu_probe, - .driver = { - .name = "sun20i-ppu", - .of_match_table = sun20i_ppu_of_match, - /* Power domains cannot be removed while they are in use. */ - .suppress_bind_attrs = true, - }, -}; -module_platform_driver(sun20i_ppu_driver); - -MODULE_AUTHOR("Samuel Holland "); -MODULE_DESCRIPTION("Allwinner D1 PPU power domain driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/genpd/tegra/Makefile b/drivers/genpd/tegra/Makefile deleted file mode 100644 index ec8acfd2c77c..000000000000 --- a/drivers/genpd/tegra/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o diff --git a/drivers/genpd/tegra/powergate-bpmp.c b/drivers/genpd/tegra/powergate-bpmp.c deleted file mode 100644 index 179ed895c279..000000000000 --- a/drivers/genpd/tegra/powergate-bpmp.c +++ /dev/null @@ -1,361 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved - */ - -#include -#include -#include -#include - -#include -#include - -struct tegra_powergate_info { - unsigned int id; - char *name; -}; - -struct tegra_powergate { - struct generic_pm_domain genpd; - struct tegra_bpmp *bpmp; - unsigned int id; -}; - -static inline struct tegra_powergate * -to_tegra_powergate(struct generic_pm_domain *genpd) -{ - return container_of(genpd, struct tegra_powergate, genpd); -} - -static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, - unsigned int id, u32 state) -{ - struct mrq_pg_request request; - struct tegra_bpmp_message msg; - int err; - - memset(&request, 0, sizeof(request)); - request.cmd = CMD_PG_SET_STATE; - request.id = id; - request.set_state.state = state; - - memset(&msg, 0, sizeof(msg)); - msg.mrq = MRQ_PG; - msg.tx.data = &request; - msg.tx.size = sizeof(request); - - err = tegra_bpmp_transfer(bpmp, &msg); - if (err < 0) - return err; - else if (msg.rx.ret < 0) - return -EINVAL; - - return 0; -} - -static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, - unsigned int id) -{ - struct mrq_pg_response response; - struct mrq_pg_request request; - struct tegra_bpmp_message msg; - int err; - - memset(&request, 0, sizeof(request)); - request.cmd = CMD_PG_GET_STATE; - request.id = id; - - memset(&response, 0, sizeof(response)); - - memset(&msg, 0, sizeof(msg)); - msg.mrq = MRQ_PG; - msg.tx.data = &request; - msg.tx.size = sizeof(request); - msg.rx.data = &response; - msg.rx.size = sizeof(response); - - err = tegra_bpmp_transfer(bpmp, &msg); - if (err < 0) - return PG_STATE_OFF; - else if (msg.rx.ret < 0) - return -EINVAL; - - return response.get_state.state; -} - -static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp) -{ - struct mrq_pg_response response; - struct mrq_pg_request request; - struct tegra_bpmp_message msg; - int err; - - memset(&request, 0, sizeof(request)); - request.cmd = CMD_PG_GET_MAX_ID; - - memset(&response, 0, sizeof(response)); - - memset(&msg, 0, sizeof(msg)); - msg.mrq = MRQ_PG; - msg.tx.data = &request; - msg.tx.size = sizeof(request); - msg.rx.data = &response; - msg.rx.size = sizeof(response); - - err = tegra_bpmp_transfer(bpmp, &msg); - if (err < 0) - return err; - else if (msg.rx.ret < 0) - return -EINVAL; - - return response.get_max_id.max_id; -} - -static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, - unsigned int id) -{ - struct mrq_pg_response response; - struct mrq_pg_request request; - struct tegra_bpmp_message msg; - int err; - - memset(&request, 0, sizeof(request)); - request.cmd = CMD_PG_GET_NAME; - request.id = id; - - memset(&response, 0, sizeof(response)); - - memset(&msg, 0, sizeof(msg)); - msg.mrq = MRQ_PG; - msg.tx.data = &request; - msg.tx.size = sizeof(request); - msg.rx.data = &response; - msg.rx.size = sizeof(response); - - err = tegra_bpmp_transfer(bpmp, &msg); - if (err < 0 || msg.rx.ret < 0) - return NULL; - - return kstrdup(response.get_name.name, GFP_KERNEL); -} - -static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp, - unsigned int id) -{ - return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF; -} - -static int tegra_powergate_power_on(struct generic_pm_domain *domain) -{ - struct tegra_powergate *powergate = to_tegra_powergate(domain); - struct tegra_bpmp *bpmp = powergate->bpmp; - - return tegra_bpmp_powergate_set_state(bpmp, powergate->id, - PG_STATE_ON); -} - -static int tegra_powergate_power_off(struct generic_pm_domain *domain) -{ - struct tegra_powergate *powergate = to_tegra_powergate(domain); - struct tegra_bpmp *bpmp = powergate->bpmp; - - return tegra_bpmp_powergate_set_state(bpmp, powergate->id, - PG_STATE_OFF); -} - -static struct tegra_powergate * -tegra_powergate_add(struct tegra_bpmp *bpmp, - const struct tegra_powergate_info *info) -{ - struct tegra_powergate *powergate; - bool off; - int err; - - off = !tegra_bpmp_powergate_is_powered(bpmp, info->id); - - powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL); - if (!powergate) - return ERR_PTR(-ENOMEM); - - powergate->id = info->id; - powergate->bpmp = bpmp; - - powergate->genpd.name = kstrdup(info->name, GFP_KERNEL); - powergate->genpd.power_on = tegra_powergate_power_on; - powergate->genpd.power_off = tegra_powergate_power_off; - - err = pm_genpd_init(&powergate->genpd, NULL, off); - if (err < 0) { - kfree(powergate->genpd.name); - return ERR_PTR(err); - } - - return powergate; -} - -static void tegra_powergate_remove(struct tegra_powergate *powergate) -{ - struct generic_pm_domain *genpd = &powergate->genpd; - struct tegra_bpmp *bpmp = powergate->bpmp; - int err; - - err = pm_genpd_remove(genpd); - if (err < 0) - dev_err(bpmp->dev, "failed to remove power domain %s: %d\n", - genpd->name, err); - - kfree(genpd->name); -} - -static int -tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp, - struct tegra_powergate_info **powergatesp) -{ - struct tegra_powergate_info *powergates; - unsigned int max_id, id, count = 0; - unsigned int num_holes = 0; - int err; - - err = tegra_bpmp_powergate_get_max_id(bpmp); - if (err < 0) - return err; - - max_id = err; - - dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id); - - powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL); - if (!powergates) - return -ENOMEM; - - for (id = 0; id <= max_id; id++) { - struct tegra_powergate_info *info = &powergates[count]; - - info->name = tegra_bpmp_powergate_get_name(bpmp, id); - if (!info->name || info->name[0] == '\0') { - num_holes++; - continue; - } - - info->id = id; - count++; - } - - dev_dbg(bpmp->dev, "holes: %u\n", num_holes); - - *powergatesp = powergates; - - return count; -} - -static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp, - struct tegra_powergate_info *powergates, - unsigned int count) -{ - struct genpd_onecell_data *genpd = &bpmp->genpd; - struct generic_pm_domain **domains; - struct tegra_powergate *powergate; - unsigned int i; - int err; - - domains = kcalloc(count, sizeof(*domains), GFP_KERNEL); - if (!domains) - return -ENOMEM; - - for (i = 0; i < count; i++) { - powergate = tegra_powergate_add(bpmp, &powergates[i]); - if (IS_ERR(powergate)) { - err = PTR_ERR(powergate); - goto remove; - } - - dev_dbg(bpmp->dev, "added power domain %s\n", - powergate->genpd.name); - domains[i] = &powergate->genpd; - } - - genpd->num_domains = count; - genpd->domains = domains; - - return 0; - -remove: - while (i--) { - powergate = to_tegra_powergate(domains[i]); - tegra_powergate_remove(powergate); - } - - kfree(domains); - return err; -} - -static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp) -{ - struct genpd_onecell_data *genpd = &bpmp->genpd; - unsigned int i = genpd->num_domains; - struct tegra_powergate *powergate; - - while (i--) { - dev_dbg(bpmp->dev, "removing power domain %s\n", - genpd->domains[i]->name); - powergate = to_tegra_powergate(genpd->domains[i]); - tegra_powergate_remove(powergate); - } -} - -static struct generic_pm_domain * -tegra_powergate_xlate(struct of_phandle_args *spec, void *data) -{ - struct generic_pm_domain *domain = ERR_PTR(-ENOENT); - struct genpd_onecell_data *genpd = data; - unsigned int i; - - for (i = 0; i < genpd->num_domains; i++) { - struct tegra_powergate *powergate; - - powergate = to_tegra_powergate(genpd->domains[i]); - if (powergate->id == spec->args[0]) { - domain = &powergate->genpd; - break; - } - } - - return domain; -} - -int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) -{ - struct device_node *np = bpmp->dev->of_node; - struct tegra_powergate_info *powergates; - struct device *dev = bpmp->dev; - unsigned int count, i; - int err; - - err = tegra_bpmp_probe_powergates(bpmp, &powergates); - if (err < 0) - return err; - - count = err; - - dev_dbg(dev, "%u power domains probed\n", count); - - err = tegra_bpmp_add_powergates(bpmp, powergates, count); - if (err < 0) - goto free; - - bpmp->genpd.xlate = tegra_powergate_xlate; - - err = of_genpd_add_provider_onecell(np, &bpmp->genpd); - if (err < 0) { - dev_err(dev, "failed to add power domain provider: %d\n", err); - tegra_bpmp_remove_powergates(bpmp); - } - -free: - for (i = 0; i < count; i++) - kfree(powergates[i].name); - - kfree(powergates); - return err; -} diff --git a/drivers/genpd/ti/Makefile b/drivers/genpd/ti/Makefile deleted file mode 100644 index 69580afbb436..000000000000 --- a/drivers/genpd/ti/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_ARCH_OMAP2PLUS) += omap_prm.o -obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o diff --git a/drivers/genpd/ti/omap_prm.c b/drivers/genpd/ti/omap_prm.c deleted file mode 100644 index c2feae3a634c..000000000000 --- a/drivers/genpd/ti/omap_prm.c +++ /dev/null @@ -1,989 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * OMAP2+ PRM driver - * - * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ - * Tero Kristo - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -enum omap_prm_domain_mode { - OMAP_PRMD_OFF, - OMAP_PRMD_RETENTION, - OMAP_PRMD_ON_INACTIVE, - OMAP_PRMD_ON_ACTIVE, -}; - -struct omap_prm_domain_map { - unsigned int usable_modes; /* Mask of hardware supported modes */ - unsigned long statechange:1; /* Optional low-power state change */ - unsigned long logicretstate:1; /* Optional logic off mode */ -}; - -struct omap_prm_domain { - struct device *dev; - struct omap_prm *prm; - struct generic_pm_domain pd; - u16 pwrstctrl; - u16 pwrstst; - const struct omap_prm_domain_map *cap; - u32 pwrstctrl_saved; - unsigned int uses_pm_clk:1; -}; - -struct omap_rst_map { - s8 rst; - s8 st; -}; - -struct omap_prm_data { - u32 base; - const char *name; - const char *clkdm_name; - u16 pwrstctrl; - u16 pwrstst; - const struct omap_prm_domain_map *dmap; - u16 rstctrl; - u16 rstst; - const struct omap_rst_map *rstmap; - u8 flags; -}; - -struct omap_prm { - const struct omap_prm_data *data; - void __iomem *base; - struct omap_prm_domain *prmd; -}; - -struct omap_reset_data { - struct reset_controller_dev rcdev; - struct omap_prm *prm; - u32 mask; - spinlock_t lock; - struct clockdomain *clkdm; - struct device *dev; -}; - -#define genpd_to_prm_domain(gpd) container_of(gpd, struct omap_prm_domain, pd) -#define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) - -#define OMAP_MAX_RESETS 8 -#define OMAP_RESET_MAX_WAIT 10000 - -#define OMAP_PRM_HAS_RSTCTRL BIT(0) -#define OMAP_PRM_HAS_RSTST BIT(1) -#define OMAP_PRM_HAS_NO_CLKDM BIT(2) -#define OMAP_PRM_RET_WHEN_IDLE BIT(3) - -#define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) - -#define PRM_STATE_MAX_WAIT 10000 -#define PRM_LOGICRETSTATE BIT(2) -#define PRM_LOWPOWERSTATECHANGE BIT(4) -#define PRM_POWERSTATE_MASK OMAP_PRMD_ON_ACTIVE - -#define PRM_ST_INTRANSITION BIT(20) - -static const struct omap_prm_domain_map omap_prm_all = { - .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | - BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF), - .statechange = 1, - .logicretstate = 1, -}; - -static const struct omap_prm_domain_map omap_prm_noinact = { - .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) | - BIT(OMAP_PRMD_OFF), - .statechange = 1, - .logicretstate = 1, -}; - -static const struct omap_prm_domain_map omap_prm_nooff = { - .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | - BIT(OMAP_PRMD_RETENTION), - .statechange = 1, - .logicretstate = 1, -}; - -static const struct omap_prm_domain_map omap_prm_onoff_noauto = { - .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF), - .statechange = 1, -}; - -static const struct omap_prm_domain_map omap_prm_alwon = { - .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE), -}; - -static const struct omap_prm_domain_map omap_prm_reton = { - .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION), - .statechange = 1, - .logicretstate = 1, -}; - -static const struct omap_rst_map rst_map_0[] = { - { .rst = 0, .st = 0 }, - { .rst = -1 }, -}; - -static const struct omap_rst_map rst_map_01[] = { - { .rst = 0, .st = 0 }, - { .rst = 1, .st = 1 }, - { .rst = -1 }, -}; - -static const struct omap_rst_map rst_map_012[] = { - { .rst = 0, .st = 0 }, - { .rst = 1, .st = 1 }, - { .rst = 2, .st = 2 }, - { .rst = -1 }, -}; - -static const struct omap_prm_data omap4_prm_data[] = { - { - .name = "mpu", .base = 0x4a306300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, - }, - { - .name = "tesla", .base = 0x4a306400, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "abe", .base = 0x4a306500, - .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_all, - }, - { - .name = "always_on_core", .base = 0x4a306600, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "core", .base = 0x4a306700, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, - .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati", - .rstmap = rst_map_012, - .flags = OMAP_PRM_RET_WHEN_IDLE, - }, - { - .name = "ivahd", .base = 0x4a306f00, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 - }, - { - .name = "cam", .base = 0x4a307000, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "dss", .base = 0x4a307100, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact - }, - { - .name = "gfx", .base = 0x4a307200, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "l3init", .base = 0x4a307300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton - }, - { - .name = "l4per", .base = 0x4a307400, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, - .flags = OMAP_PRM_RET_WHEN_IDLE, - }, - { - .name = "cefuse", .base = 0x4a307600, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "wkup", .base = 0x4a307700, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon - }, - { - .name = "emu", .base = 0x4a307900, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "device", .base = 0x4a307b00, - .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, - .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM - }, - { }, -}; - -static const struct omap_prm_data omap5_prm_data[] = { - { - .name = "mpu", .base = 0x4ae06300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, - }, - { - .name = "dsp", .base = 0x4ae06400, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "abe", .base = 0x4ae06500, - .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_nooff, - }, - { - .name = "coreaon", .base = 0x4ae06600, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon - }, - { - .name = "core", .base = 0x4ae06700, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, - .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu", - .rstmap = rst_map_012 - }, - { - .name = "iva", .base = 0x4ae07200, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 - }, - { - .name = "cam", .base = 0x4ae07300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "dss", .base = 0x4ae07400, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact - }, - { - .name = "gpu", .base = 0x4ae07500, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "l3init", .base = 0x4ae07600, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton - }, - { - .name = "custefuse", .base = 0x4ae07700, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "wkupaon", .base = 0x4ae07800, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon - }, - { - .name = "emu", .base = 0x4ae07a00, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto - }, - { - .name = "device", .base = 0x4ae07c00, - .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, - .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM - }, - { }, -}; - -static const struct omap_prm_data dra7_prm_data[] = { - { - .name = "mpu", .base = 0x4ae06300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, - }, - { - .name = "dsp1", .base = 0x4ae06400, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01, - }, - { - .name = "ipu", .base = 0x4ae06500, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012, - .clkdm_name = "ipu1" - }, - { - .name = "coreaon", .base = 0x4ae06628, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "core", .base = 0x4ae06700, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - .rstctrl = 0x210, .rstst = 0x214, .rstmap = rst_map_012, - .clkdm_name = "ipu2" - }, - { - .name = "iva", .base = 0x4ae06f00, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012, - }, - { - .name = "cam", .base = 0x4ae07000, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "dss", .base = 0x4ae07100, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "gpu", .base = 0x4ae07200, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "l3init", .base = 0x4ae07300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01, - .clkdm_name = "pcie" - }, - { - .name = "l4per", .base = 0x4ae07400, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "custefuse", .base = 0x4ae07600, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "wkupaon", .base = 0x4ae07724, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "emu", .base = 0x4ae07900, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "dsp2", .base = 0x4ae07b00, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "eve1", .base = 0x4ae07b40, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "eve2", .base = 0x4ae07b80, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "eve3", .base = 0x4ae07bc0, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "eve4", .base = 0x4ae07c00, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 - }, - { - .name = "rtc", .base = 0x4ae07c60, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "vpe", .base = 0x4ae07c80, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { }, -}; - -static const struct omap_rst_map am3_per_rst_map[] = { - { .rst = 1 }, - { .rst = -1 }, -}; - -static const struct omap_rst_map am3_wkup_rst_map[] = { - { .rst = 3, .st = 5 }, - { .rst = -1 }, -}; - -static const struct omap_prm_data am3_prm_data[] = { - { - .name = "per", .base = 0x44e00c00, - .pwrstctrl = 0xc, .pwrstst = 0x8, .dmap = &omap_prm_noinact, - .rstctrl = 0x0, .rstmap = am3_per_rst_map, - .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" - }, - { - .name = "wkup", .base = 0x44e00d00, - .pwrstctrl = 0x4, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map, - .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM - }, - { - .name = "mpu", .base = 0x44e00e00, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - }, - { - .name = "device", .base = 0x44e00f00, - .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01, - .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM - }, - { - .name = "rtc", .base = 0x44e01000, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "gfx", .base = 0x44e01100, - .pwrstctrl = 0, .pwrstst = 0x10, .dmap = &omap_prm_noinact, - .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", - }, - { - .name = "cefuse", .base = 0x44e01200, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { }, -}; - -static const struct omap_rst_map am4_per_rst_map[] = { - { .rst = 1, .st = 0 }, - { .rst = -1 }, -}; - -static const struct omap_rst_map am4_device_rst_map[] = { - { .rst = 0, .st = 1 }, - { .rst = 1, .st = 0 }, - { .rst = -1 }, -}; - -static const struct omap_prm_data am4_prm_data[] = { - { - .name = "mpu", .base = 0x44df0300, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - }, - { - .name = "gfx", .base = 0x44df0400, - .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", - }, - { - .name = "rtc", .base = 0x44df0500, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "tamper", .base = 0x44df0600, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - }, - { - .name = "cefuse", .base = 0x44df0700, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, - }, - { - .name = "per", .base = 0x44df0800, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map, - .clkdm_name = "pruss_ocp" - }, - { - .name = "wkup", .base = 0x44df2000, - .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, - .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map, - .flags = OMAP_PRM_HAS_NO_CLKDM - }, - { - .name = "device", .base = 0x44df4000, - .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map, - .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM - }, - { }, -}; - -static const struct of_device_id omap_prm_id_table[] = { - { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, - { .compatible = "ti,omap5-prm-inst", .data = omap5_prm_data }, - { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, - { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, - { .compatible = "ti,am4-prm-inst", .data = am4_prm_data }, - { }, -}; - -#ifdef DEBUG -static void omap_prm_domain_show_state(struct omap_prm_domain *prmd, - const char *desc) -{ - dev_dbg(prmd->dev, "%s %s: %08x/%08x\n", - prmd->pd.name, desc, - readl_relaxed(prmd->prm->base + prmd->pwrstctrl), - readl_relaxed(prmd->prm->base + prmd->pwrstst)); -} -#else -static inline void omap_prm_domain_show_state(struct omap_prm_domain *prmd, - const char *desc) -{ -} -#endif - -static int omap_prm_domain_power_on(struct generic_pm_domain *domain) -{ - struct omap_prm_domain *prmd; - int ret; - u32 v, mode; - - prmd = genpd_to_prm_domain(domain); - if (!prmd->cap) - return 0; - - omap_prm_domain_show_state(prmd, "on: previous state"); - - if (prmd->pwrstctrl_saved) - v = prmd->pwrstctrl_saved; - else - v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); - - if (prmd->prm->data->flags & OMAP_PRM_RET_WHEN_IDLE) - mode = OMAP_PRMD_RETENTION; - else - mode = OMAP_PRMD_ON_ACTIVE; - - writel_relaxed((v & ~PRM_POWERSTATE_MASK) | mode, - prmd->prm->base + prmd->pwrstctrl); - - /* wait for the transition bit to get cleared */ - ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, - v, !(v & PRM_ST_INTRANSITION), 1, - PRM_STATE_MAX_WAIT); - if (ret) - dev_err(prmd->dev, "%s: %s timed out\n", - prmd->pd.name, __func__); - - omap_prm_domain_show_state(prmd, "on: new state"); - - return ret; -} - -/* No need to check for holes in the mask for the lowest mode */ -static int omap_prm_domain_find_lowest(struct omap_prm_domain *prmd) -{ - return __ffs(prmd->cap->usable_modes); -} - -static int omap_prm_domain_power_off(struct generic_pm_domain *domain) -{ - struct omap_prm_domain *prmd; - int ret; - u32 v; - - prmd = genpd_to_prm_domain(domain); - if (!prmd->cap) - return 0; - - omap_prm_domain_show_state(prmd, "off: previous state"); - - v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); - prmd->pwrstctrl_saved = v; - - v &= ~PRM_POWERSTATE_MASK; - v |= omap_prm_domain_find_lowest(prmd); - - if (prmd->cap->statechange) - v |= PRM_LOWPOWERSTATECHANGE; - if (prmd->cap->logicretstate) - v &= ~PRM_LOGICRETSTATE; - else - v |= PRM_LOGICRETSTATE; - - writel_relaxed(v, prmd->prm->base + prmd->pwrstctrl); - - /* wait for the transition bit to get cleared */ - ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, - v, !(v & PRM_ST_INTRANSITION), 1, - PRM_STATE_MAX_WAIT); - if (ret) - dev_warn(prmd->dev, "%s: %s timed out\n", - __func__, prmd->pd.name); - - omap_prm_domain_show_state(prmd, "off: new state"); - - return 0; -} - -/* - * Note that ti-sysc already manages the module clocks separately so - * no need to manage those. Interconnect instances need clocks managed - * for simple-pm-bus. - */ -static int omap_prm_domain_attach_clock(struct device *dev, - struct omap_prm_domain *prmd) -{ - struct device_node *np = dev->of_node; - int error; - - if (!of_device_is_compatible(np, "simple-pm-bus")) - return 0; - - if (!of_property_read_bool(np, "clocks")) - return 0; - - error = pm_clk_create(dev); - if (error) - return error; - - error = of_pm_clk_add_clks(dev); - if (error < 0) { - pm_clk_destroy(dev); - return error; - } - - prmd->uses_pm_clk = 1; - - return 0; -} - -static int omap_prm_domain_attach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - struct generic_pm_domain_data *genpd_data; - struct of_phandle_args pd_args; - struct omap_prm_domain *prmd; - struct device_node *np; - int ret; - - prmd = genpd_to_prm_domain(domain); - np = dev->of_node; - - ret = of_parse_phandle_with_args(np, "power-domains", - "#power-domain-cells", 0, &pd_args); - if (ret < 0) - return ret; - - if (pd_args.args_count != 0) - dev_warn(dev, "%s: unusupported #power-domain-cells: %i\n", - prmd->pd.name, pd_args.args_count); - - genpd_data = dev_gpd_data(dev); - genpd_data->data = NULL; - - ret = omap_prm_domain_attach_clock(dev, prmd); - if (ret) - return ret; - - return 0; -} - -static void omap_prm_domain_detach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - struct generic_pm_domain_data *genpd_data; - struct omap_prm_domain *prmd; - - prmd = genpd_to_prm_domain(domain); - if (prmd->uses_pm_clk) - pm_clk_destroy(dev); - genpd_data = dev_gpd_data(dev); - genpd_data->data = NULL; -} - -static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm) -{ - struct omap_prm_domain *prmd; - struct device_node *np = dev->of_node; - const struct omap_prm_data *data; - const char *name; - int error; - - if (!of_property_present(dev->of_node, "#power-domain-cells")) - return 0; - - of_node_put(dev->of_node); - - prmd = devm_kzalloc(dev, sizeof(*prmd), GFP_KERNEL); - if (!prmd) - return -ENOMEM; - - data = prm->data; - name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s", - data->name); - - prmd->dev = dev; - prmd->prm = prm; - prmd->cap = prmd->prm->data->dmap; - prmd->pwrstctrl = prmd->prm->data->pwrstctrl; - prmd->pwrstst = prmd->prm->data->pwrstst; - - prmd->pd.name = name; - prmd->pd.power_on = omap_prm_domain_power_on; - prmd->pd.power_off = omap_prm_domain_power_off; - prmd->pd.attach_dev = omap_prm_domain_attach_dev; - prmd->pd.detach_dev = omap_prm_domain_detach_dev; - prmd->pd.flags = GENPD_FLAG_PM_CLK; - - pm_genpd_init(&prmd->pd, NULL, true); - error = of_genpd_add_provider_simple(np, &prmd->pd); - if (error) - pm_genpd_remove(&prmd->pd); - else - prm->prmd = prmd; - - return error; -} - -static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id) -{ - if (reset->mask & BIT(id)) - return true; - - return false; -} - -static int omap_reset_get_st_bit(struct omap_reset_data *reset, - unsigned long id) -{ - const struct omap_rst_map *map = reset->prm->data->rstmap; - - while (map->rst >= 0) { - if (map->rst == id) - return map->st; - - map++; - } - - return id; -} - -static int omap_reset_status(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct omap_reset_data *reset = to_omap_reset_data(rcdev); - u32 v; - int st_bit = omap_reset_get_st_bit(reset, id); - bool has_rstst = reset->prm->data->rstst || - (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); - - /* Check if we have rstst */ - if (!has_rstst) - return -ENOTSUPP; - - /* Check if hw reset line is asserted */ - v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); - if (v & BIT(id)) - return 1; - - /* - * Check reset status, high value means reset sequence has been - * completed successfully so we can return 0 here (reset deasserted) - */ - v = readl_relaxed(reset->prm->base + reset->prm->data->rstst); - v >>= st_bit; - v &= 1; - - return !v; -} - -static int omap_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct omap_reset_data *reset = to_omap_reset_data(rcdev); - u32 v; - unsigned long flags; - - /* assert the reset control line */ - spin_lock_irqsave(&reset->lock, flags); - v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); - v |= 1 << id; - writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); - spin_unlock_irqrestore(&reset->lock, flags); - - return 0; -} - -static int omap_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) -{ - struct omap_reset_data *reset = to_omap_reset_data(rcdev); - u32 v; - int st_bit; - bool has_rstst; - unsigned long flags; - struct ti_prm_platform_data *pdata = dev_get_platdata(reset->dev); - int ret = 0; - - /* Nothing to do if the reset is already deasserted */ - if (!omap_reset_status(rcdev, id)) - return 0; - - has_rstst = reset->prm->data->rstst || - (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); - - if (has_rstst) { - st_bit = omap_reset_get_st_bit(reset, id); - - /* Clear the reset status by writing 1 to the status bit */ - v = 1 << st_bit; - writel_relaxed(v, reset->prm->base + reset->prm->data->rstst); - } - - if (reset->clkdm) - pdata->clkdm_deny_idle(reset->clkdm); - - /* de-assert the reset control line */ - spin_lock_irqsave(&reset->lock, flags); - v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); - v &= ~(1 << id); - writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); - spin_unlock_irqrestore(&reset->lock, flags); - - /* wait for the reset bit to clear */ - ret = readl_relaxed_poll_timeout_atomic(reset->prm->base + - reset->prm->data->rstctrl, - v, !(v & BIT(id)), 1, - OMAP_RESET_MAX_WAIT); - if (ret) - pr_err("%s: timedout waiting for %s:%lu\n", __func__, - reset->prm->data->name, id); - - /* wait for the status to be set */ - if (has_rstst) { - ret = readl_relaxed_poll_timeout_atomic(reset->prm->base + - reset->prm->data->rstst, - v, v & BIT(st_bit), 1, - OMAP_RESET_MAX_WAIT); - if (ret) - pr_err("%s: timedout waiting for %s:%lu\n", __func__, - reset->prm->data->name, id); - } - - if (reset->clkdm) - pdata->clkdm_allow_idle(reset->clkdm); - - return ret; -} - -static const struct reset_control_ops omap_reset_ops = { - .assert = omap_reset_assert, - .deassert = omap_reset_deassert, - .status = omap_reset_status, -}; - -static int omap_prm_reset_xlate(struct reset_controller_dev *rcdev, - const struct of_phandle_args *reset_spec) -{ - struct omap_reset_data *reset = to_omap_reset_data(rcdev); - - if (!_is_valid_reset(reset, reset_spec->args[0])) - return -EINVAL; - - return reset_spec->args[0]; -} - -static int omap_prm_reset_init(struct platform_device *pdev, - struct omap_prm *prm) -{ - struct omap_reset_data *reset; - const struct omap_rst_map *map; - struct ti_prm_platform_data *pdata = dev_get_platdata(&pdev->dev); - char buf[32]; - u32 v; - - /* - * Check if we have controllable resets. If either rstctrl is non-zero - * or OMAP_PRM_HAS_RSTCTRL flag is set, we have reset control register - * for the domain. - */ - if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL)) - return 0; - - /* Check if we have the pdata callbacks in place */ - if (!pdata || !pdata->clkdm_lookup || !pdata->clkdm_deny_idle || - !pdata->clkdm_allow_idle) - return -EINVAL; - - map = prm->data->rstmap; - if (!map) - return -EINVAL; - - reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); - if (!reset) - return -ENOMEM; - - reset->rcdev.owner = THIS_MODULE; - reset->rcdev.ops = &omap_reset_ops; - reset->rcdev.of_node = pdev->dev.of_node; - reset->rcdev.nr_resets = OMAP_MAX_RESETS; - reset->rcdev.of_xlate = omap_prm_reset_xlate; - reset->rcdev.of_reset_n_cells = 1; - reset->dev = &pdev->dev; - spin_lock_init(&reset->lock); - - reset->prm = prm; - - sprintf(buf, "%s_clkdm", prm->data->clkdm_name ? prm->data->clkdm_name : - prm->data->name); - - if (!(prm->data->flags & OMAP_PRM_HAS_NO_CLKDM)) { - reset->clkdm = pdata->clkdm_lookup(buf); - if (!reset->clkdm) - return -EINVAL; - } - - while (map->rst >= 0) { - reset->mask |= BIT(map->rst); - map++; - } - - /* Quirk handling to assert rst_map_012 bits on reset and avoid errors */ - if (prm->data->rstmap == rst_map_012) { - v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); - if ((v & reset->mask) != reset->mask) { - dev_dbg(&pdev->dev, "Asserting all resets: %08x\n", v); - writel_relaxed(reset->mask, reset->prm->base + - reset->prm->data->rstctrl); - } - } - - return devm_reset_controller_register(&pdev->dev, &reset->rcdev); -} - -static int omap_prm_probe(struct platform_device *pdev) -{ - struct resource *res; - const struct omap_prm_data *data; - struct omap_prm *prm; - int ret; - - data = of_device_get_match_data(&pdev->dev); - if (!data) - return -ENOTSUPP; - - prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL); - if (!prm) - return -ENOMEM; - - prm->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); - if (IS_ERR(prm->base)) - return PTR_ERR(prm->base); - - while (data->base != res->start) { - if (!data->base) - return -EINVAL; - data++; - } - - prm->data = data; - - ret = omap_prm_domain_init(&pdev->dev, prm); - if (ret) - return ret; - - ret = omap_prm_reset_init(pdev, prm); - if (ret) - goto err_domain; - - return 0; - -err_domain: - of_genpd_del_provider(pdev->dev.of_node); - pm_genpd_remove(&prm->prmd->pd); - - return ret; -} - -static struct platform_driver omap_prm_driver = { - .probe = omap_prm_probe, - .driver = { - .name = KBUILD_MODNAME, - .of_match_table = omap_prm_id_table, - }, -}; -builtin_platform_driver(omap_prm_driver); diff --git a/drivers/genpd/ti/ti_sci_pm_domains.c b/drivers/genpd/ti/ti_sci_pm_domains.c deleted file mode 100644 index 34645104fe45..000000000000 --- a/drivers/genpd/ti/ti_sci_pm_domains.c +++ /dev/null @@ -1,204 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-only -/* - * TI SCI Generic Power Domain Driver - * - * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ - * J Keerthy - * Dave Gerlach - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -/** - * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data - * @ti_sci: handle to TI SCI protocol driver that provides ops to - * communicate with system control processor. - * @dev: pointer to dev for the driver for devm allocs - * @pd_list: list of all the power domains on the device - * @data: onecell data for genpd core - */ -struct ti_sci_genpd_provider { - const struct ti_sci_handle *ti_sci; - struct device *dev; - struct list_head pd_list; - struct genpd_onecell_data data; -}; - -/** - * struct ti_sci_pm_domain: TI specific data needed for power domain - * @idx: index of the device that identifies it with the system - * control processor. - * @exclusive: Permissions for exclusive request or shared request of the - * device. - * @pd: generic_pm_domain for use with the genpd framework - * @node: link for the genpd list - * @parent: link to the parent TI SCI genpd provider - */ -struct ti_sci_pm_domain { - int idx; - u8 exclusive; - struct generic_pm_domain pd; - struct list_head node; - struct ti_sci_genpd_provider *parent; -}; - -#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) - -/* - * ti_sci_pd_power_off(): genpd power down hook - * @domain: pointer to the powerdomain to power off - */ -static int ti_sci_pd_power_off(struct generic_pm_domain *domain) -{ - struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); - const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; - - return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx); -} - -/* - * ti_sci_pd_power_on(): genpd power up hook - * @domain: pointer to the powerdomain to power on - */ -static int ti_sci_pd_power_on(struct generic_pm_domain *domain) -{ - struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); - const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; - - if (pd->exclusive) - return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, - pd->idx); - else - return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx); -} - -/* - * ti_sci_pd_xlate(): translation service for TI SCI genpds - * @genpdspec: DT identification data for the genpd - * @data: genpd core data for all the powerdomains on the device - */ -static struct generic_pm_domain *ti_sci_pd_xlate( - struct of_phandle_args *genpdspec, - void *data) -{ - struct genpd_onecell_data *genpd_data = data; - unsigned int idx = genpdspec->args[0]; - - if (genpdspec->args_count != 1 && genpdspec->args_count != 2) - return ERR_PTR(-EINVAL); - - if (idx >= genpd_data->num_domains) { - pr_err("%s: invalid domain index %u\n", __func__, idx); - return ERR_PTR(-EINVAL); - } - - if (!genpd_data->domains[idx]) - return ERR_PTR(-ENOENT); - - genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive = - genpdspec->args[1]; - - return genpd_data->domains[idx]; -} - -static const struct of_device_id ti_sci_pm_domain_matches[] = { - { .compatible = "ti,sci-pm-domain", }, - { }, -}; -MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); - -static int ti_sci_pm_domain_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct ti_sci_genpd_provider *pd_provider; - struct ti_sci_pm_domain *pd; - struct device_node *np; - struct of_phandle_args args; - int ret; - u32 max_id = 0; - int index; - - pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL); - if (!pd_provider) - return -ENOMEM; - - pd_provider->ti_sci = devm_ti_sci_get_handle(dev); - if (IS_ERR(pd_provider->ti_sci)) - return PTR_ERR(pd_provider->ti_sci); - - pd_provider->dev = dev; - - INIT_LIST_HEAD(&pd_provider->pd_list); - - /* Find highest device ID used for power domains */ - for_each_node_with_property(np, "power-domains") { - index = 0; - - while (1) { - ret = of_parse_phandle_with_args(np, "power-domains", - "#power-domain-cells", - index, &args); - if (ret) - break; - - if (args.args_count >= 1 && args.np == dev->of_node) { - if (args.args[0] > max_id) - max_id = args.args[0]; - - pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - pd->pd.name = devm_kasprintf(dev, GFP_KERNEL, - "pd:%d", - args.args[0]); - if (!pd->pd.name) - return -ENOMEM; - - pd->pd.power_off = ti_sci_pd_power_off; - pd->pd.power_on = ti_sci_pd_power_on; - pd->idx = args.args[0]; - pd->parent = pd_provider; - - pm_genpd_init(&pd->pd, NULL, true); - - list_add(&pd->node, &pd_provider->pd_list); - } - index++; - } - } - - pd_provider->data.domains = - devm_kcalloc(dev, max_id + 1, - sizeof(*pd_provider->data.domains), - GFP_KERNEL); - if (!pd_provider->data.domains) - return -ENOMEM; - - pd_provider->data.num_domains = max_id + 1; - pd_provider->data.xlate = ti_sci_pd_xlate; - - list_for_each_entry(pd, &pd_provider->pd_list, node) - pd_provider->data.domains[pd->idx] = &pd->pd; - - return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data); -} - -static struct platform_driver ti_sci_pm_domains_driver = { - .probe = ti_sci_pm_domain_probe, - .driver = { - .name = "ti_sci_pm_domains", - .of_match_table = ti_sci_pm_domain_matches, - }, -}; -module_platform_driver(ti_sci_pm_domains_driver); -MODULE_LICENSE("GPL v2"); -MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver"); -MODULE_AUTHOR("Dave Gerlach"); diff --git a/drivers/genpd/xilinx/Makefile b/drivers/genpd/xilinx/Makefile deleted file mode 100644 index a706ab699cfa..000000000000 --- a/drivers/genpd/xilinx/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += zynqmp-pm-domains.o diff --git a/drivers/genpd/xilinx/zynqmp-pm-domains.c b/drivers/genpd/xilinx/zynqmp-pm-domains.c deleted file mode 100644 index 69d03ad4cf1e..000000000000 --- a/drivers/genpd/xilinx/zynqmp-pm-domains.c +++ /dev/null @@ -1,322 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ZynqMP Generic PM domain support - * - * Copyright (C) 2015-2019 Xilinx, Inc. - * - * Davorin Mista - * Jolly Shah - * Rajan Vaja - */ - -#include -#include -#include -#include -#include -#include -#include - -#include - -#define ZYNQMP_NUM_DOMAINS (100) - -static int min_capability; - -/** - * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain - * @gpd: Generic power domain - * @node_id: PM node ID corresponding to device inside PM domain - * @requested: The PM node mapped to the PM domain has been requested - */ -struct zynqmp_pm_domain { - struct generic_pm_domain gpd; - u32 node_id; - bool requested; -}; - -#define to_zynqmp_pm_domain(pm_domain) \ - container_of(pm_domain, struct zynqmp_pm_domain, gpd) - -/** - * zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source - * path - * @dev: Device to check for wakeup source path - * @not_used: Data member (not required) - * - * This function is checks device's child hierarchy and checks if any device is - * set as wakeup source. - * - * Return: 1 if device is in wakeup source path else 0 - */ -static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used) -{ - int may_wakeup; - - may_wakeup = device_may_wakeup(dev); - if (may_wakeup) - return may_wakeup; - - return device_for_each_child(dev, NULL, - zynqmp_gpd_is_active_wakeup_path); -} - -/** - * zynqmp_gpd_power_on() - Power on PM domain - * @domain: Generic PM domain - * - * This function is called before devices inside a PM domain are resumed, to - * power on PM domain. - * - * Return: 0 on success, error code otherwise - */ -static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) -{ - struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); - int ret; - - ret = zynqmp_pm_set_requirement(pd->node_id, - ZYNQMP_PM_CAPABILITY_ACCESS, - ZYNQMP_PM_MAX_QOS, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); - if (ret) { - dev_err(&domain->dev, - "failed to set requirement to 0x%x for PM node id %d: %d\n", - ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id, ret); - return ret; - } - - dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n", - ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id); - - return 0; -} - -/** - * zynqmp_gpd_power_off() - Power off PM domain - * @domain: Generic PM domain - * - * This function is called after devices inside a PM domain are suspended, to - * power off PM domain. - * - * Return: 0 on success, error code otherwise - */ -static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) -{ - struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); - int ret; - struct pm_domain_data *pdd, *tmp; - u32 capabilities = min_capability; - bool may_wakeup; - - /* If domain is already released there is nothing to be done */ - if (!pd->requested) { - dev_dbg(&domain->dev, "PM node id %d is already released\n", - pd->node_id); - return 0; - } - - list_for_each_entry_safe(pdd, tmp, &domain->dev_list, list_node) { - /* If device is in wakeup path, set capability to WAKEUP */ - may_wakeup = zynqmp_gpd_is_active_wakeup_path(pdd->dev, NULL); - if (may_wakeup) { - dev_dbg(pdd->dev, "device is in wakeup path in %s\n", - domain->name); - capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP; - break; - } - } - - ret = zynqmp_pm_set_requirement(pd->node_id, capabilities, 0, - ZYNQMP_PM_REQUEST_ACK_NO); - if (ret) { - dev_err(&domain->dev, - "failed to set requirement to 0x%x for PM node id %d: %d\n", - capabilities, pd->node_id, ret); - return ret; - } - - dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n", - capabilities, pd->node_id); - - return 0; -} - -/** - * zynqmp_gpd_attach_dev() - Attach device to the PM domain - * @domain: Generic PM domain - * @dev: Device to attach - * - * Return: 0 on success, error code otherwise - */ -static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); - struct device_link *link; - int ret; - - link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY); - if (!link) - dev_dbg(&domain->dev, "failed to create device link for %s\n", - dev_name(dev)); - - /* If this is not the first device to attach there is nothing to do */ - if (domain->device_count) - return 0; - - ret = zynqmp_pm_request_node(pd->node_id, 0, 0, - ZYNQMP_PM_REQUEST_ACK_BLOCKING); - if (ret) { - dev_err(&domain->dev, "%s request failed for node %d: %d\n", - domain->name, pd->node_id, ret); - return ret; - } - - pd->requested = true; - - dev_dbg(&domain->dev, "%s requested PM node id %d\n", - dev_name(dev), pd->node_id); - - return 0; -} - -/** - * zynqmp_gpd_detach_dev() - Detach device from the PM domain - * @domain: Generic PM domain - * @dev: Device to detach - */ -static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, - struct device *dev) -{ - struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); - int ret; - - /* If this is not the last device to detach there is nothing to do */ - if (domain->device_count) - return; - - ret = zynqmp_pm_release_node(pd->node_id); - if (ret) { - dev_err(&domain->dev, "failed to release PM node id %d: %d\n", - pd->node_id, ret); - return; - } - - pd->requested = false; - - dev_dbg(&domain->dev, "%s released PM node id %d\n", - dev_name(dev), pd->node_id); -} - -static struct generic_pm_domain *zynqmp_gpd_xlate - (struct of_phandle_args *genpdspec, void *data) -{ - struct genpd_onecell_data *genpd_data = data; - unsigned int i, idx = genpdspec->args[0]; - struct zynqmp_pm_domain *pd; - - pd = to_zynqmp_pm_domain(genpd_data->domains[0]); - - if (genpdspec->args_count != 1) - return ERR_PTR(-EINVAL); - - /* Check for existing pm domains */ - for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) { - if (pd[i].node_id == idx) - goto done; - } - - /* - * Add index in empty node_id of power domain list as no existing - * power domain found for current index. - */ - for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) { - if (pd[i].node_id == 0) { - pd[i].node_id = idx; - break; - } - } - -done: - if (!genpd_data->domains[i] || i == ZYNQMP_NUM_DOMAINS) - return ERR_PTR(-ENOENT); - - return genpd_data->domains[i]; -} - -static int zynqmp_gpd_probe(struct platform_device *pdev) -{ - int i; - struct genpd_onecell_data *zynqmp_pd_data; - struct generic_pm_domain **domains; - struct zynqmp_pm_domain *pd; - struct device *dev = &pdev->dev; - - pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - - zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL); - if (!zynqmp_pd_data) - return -ENOMEM; - - zynqmp_pd_data->xlate = zynqmp_gpd_xlate; - - domains = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*domains), - GFP_KERNEL); - if (!domains) - return -ENOMEM; - - if (!of_device_is_compatible(dev->parent->of_node, - "xlnx,zynqmp-firmware")) - min_capability = ZYNQMP_PM_CAPABILITY_UNUSABLE; - - for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) { - pd->node_id = 0; - pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i); - pd->gpd.power_off = zynqmp_gpd_power_off; - pd->gpd.power_on = zynqmp_gpd_power_on; - pd->gpd.attach_dev = zynqmp_gpd_attach_dev; - pd->gpd.detach_dev = zynqmp_gpd_detach_dev; - - domains[i] = &pd->gpd; - - /* Mark all PM domains as initially powered off */ - pm_genpd_init(&pd->gpd, NULL, true); - } - - zynqmp_pd_data->domains = domains; - zynqmp_pd_data->num_domains = ZYNQMP_NUM_DOMAINS; - of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data); - - return 0; -} - -static int zynqmp_gpd_remove(struct platform_device *pdev) -{ - of_genpd_del_provider(pdev->dev.parent->of_node); - - return 0; -} - -static void zynqmp_gpd_sync_state(struct device *dev) -{ - int ret; - - ret = zynqmp_pm_init_finalize(); - if (ret) - dev_warn(dev, "failed to release power management to firmware\n"); -} - -static struct platform_driver zynqmp_power_domain_driver = { - .driver = { - .name = "zynqmp_power_controller", - .sync_state = zynqmp_gpd_sync_state, - }, - .probe = zynqmp_gpd_probe, - .remove = zynqmp_gpd_remove, -}; -module_platform_driver(zynqmp_power_domain_driver); - -MODULE_ALIAS("platform:zynqmp_power_controller"); diff --git a/drivers/pmdomain/Makefile b/drivers/pmdomain/Makefile new file mode 100644 index 000000000000..666753676e5c --- /dev/null +++ b/drivers/pmdomain/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-y += actions/ +obj-y += amlogic/ +obj-y += apple/ +obj-y += bcm/ +obj-y += imx/ +obj-y += mediatek/ +obj-y += qcom/ +obj-y += renesas/ +obj-y += rockchip/ +obj-y += samsung/ +obj-y += st/ +obj-y += starfive/ +obj-y += sunxi/ +obj-y += tegra/ +obj-y += ti/ +obj-y += xilinx/ diff --git a/drivers/pmdomain/actions/Makefile b/drivers/pmdomain/actions/Makefile new file mode 100644 index 000000000000..7e8aa473d12d --- /dev/null +++ b/drivers/pmdomain/actions/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0+ +obj-$(CONFIG_OWL_PM_DOMAINS_HELPER) += owl-sps-helper.o +obj-$(CONFIG_OWL_PM_DOMAINS) += owl-sps.o diff --git a/drivers/pmdomain/actions/owl-sps-helper.c b/drivers/pmdomain/actions/owl-sps-helper.c new file mode 100644 index 000000000000..e3f36603dd53 --- /dev/null +++ b/drivers/pmdomain/actions/owl-sps-helper.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Actions Semi Owl Smart Power System (SPS) shared helpers + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 Andreas Färber + */ + +#include +#include +#include + +#define OWL_SPS_PG_CTL 0x0 + +int owl_sps_set_pg(void __iomem *base, u32 pwr_mask, u32 ack_mask, bool enable) +{ + u32 val; + bool ack; + int timeout; + + val = readl(base + OWL_SPS_PG_CTL); + ack = val & ack_mask; + if (ack == enable) + return 0; + + if (enable) + val |= pwr_mask; + else + val &= ~pwr_mask; + + writel(val, base + OWL_SPS_PG_CTL); + + for (timeout = 5000; timeout > 0; timeout -= 50) { + val = readl(base + OWL_SPS_PG_CTL); + if ((val & ack_mask) == (enable ? ack_mask : 0)) + break; + udelay(50); + } + if (timeout <= 0) + return -ETIMEDOUT; + + udelay(10); + + return 0; +} +EXPORT_SYMBOL_GPL(owl_sps_set_pg); diff --git a/drivers/pmdomain/actions/owl-sps.c b/drivers/pmdomain/actions/owl-sps.c new file mode 100644 index 000000000000..73a9e0bb7e8e --- /dev/null +++ b/drivers/pmdomain/actions/owl-sps.c @@ -0,0 +1,320 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Actions Semi Owl Smart Power System (SPS) + * + * Copyright 2012 Actions Semi Inc. + * Author: Actions Semi, Inc. + * + * Copyright (c) 2017 Andreas Färber + */ + +#include +#include +#include +#include +#include +#include +#include + +struct owl_sps_domain_info { + const char *name; + int pwr_bit; + int ack_bit; + unsigned int genpd_flags; +}; + +struct owl_sps_info { + unsigned num_domains; + const struct owl_sps_domain_info *domains; +}; + +struct owl_sps { + struct device *dev; + const struct owl_sps_info *info; + void __iomem *base; + struct genpd_onecell_data genpd_data; + struct generic_pm_domain *domains[]; +}; + +#define to_owl_pd(gpd) container_of(gpd, struct owl_sps_domain, genpd) + +struct owl_sps_domain { + struct generic_pm_domain genpd; + const struct owl_sps_domain_info *info; + struct owl_sps *sps; +}; + +static int owl_sps_set_power(struct owl_sps_domain *pd, bool enable) +{ + u32 pwr_mask, ack_mask; + + ack_mask = BIT(pd->info->ack_bit); + pwr_mask = BIT(pd->info->pwr_bit); + + return owl_sps_set_pg(pd->sps->base, pwr_mask, ack_mask, enable); +} + +static int owl_sps_power_on(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + dev_dbg(pd->sps->dev, "%s power on", pd->info->name); + + return owl_sps_set_power(pd, true); +} + +static int owl_sps_power_off(struct generic_pm_domain *domain) +{ + struct owl_sps_domain *pd = to_owl_pd(domain); + + dev_dbg(pd->sps->dev, "%s power off", pd->info->name); + + return owl_sps_set_power(pd, false); +} + +static int owl_sps_init_domain(struct owl_sps *sps, int index) +{ + struct owl_sps_domain *pd; + + pd = devm_kzalloc(sps->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->info = &sps->info->domains[index]; + pd->sps = sps; + + pd->genpd.name = pd->info->name; + pd->genpd.power_on = owl_sps_power_on; + pd->genpd.power_off = owl_sps_power_off; + pd->genpd.flags = pd->info->genpd_flags; + pm_genpd_init(&pd->genpd, NULL, false); + + sps->genpd_data.domains[index] = &pd->genpd; + + return 0; +} + +static int owl_sps_probe(struct platform_device *pdev) +{ + const struct of_device_id *match; + const struct owl_sps_info *sps_info; + struct owl_sps *sps; + int i, ret; + + if (!pdev->dev.of_node) { + dev_err(&pdev->dev, "no device node\n"); + return -ENODEV; + } + + match = of_match_device(pdev->dev.driver->of_match_table, &pdev->dev); + if (!match || !match->data) { + dev_err(&pdev->dev, "unknown compatible or missing data\n"); + return -EINVAL; + } + + sps_info = match->data; + + sps = devm_kzalloc(&pdev->dev, + struct_size(sps, domains, sps_info->num_domains), + GFP_KERNEL); + if (!sps) + return -ENOMEM; + + sps->base = of_io_request_and_map(pdev->dev.of_node, 0, "owl-sps"); + if (IS_ERR(sps->base)) { + dev_err(&pdev->dev, "failed to map sps registers\n"); + return PTR_ERR(sps->base); + } + + sps->dev = &pdev->dev; + sps->info = sps_info; + sps->genpd_data.domains = sps->domains; + sps->genpd_data.num_domains = sps_info->num_domains; + + for (i = 0; i < sps_info->num_domains; i++) { + ret = owl_sps_init_domain(sps, i); + if (ret) + return ret; + } + + ret = of_genpd_add_provider_onecell(pdev->dev.of_node, &sps->genpd_data); + if (ret) { + dev_err(&pdev->dev, "failed to add provider (%d)", ret); + return ret; + } + + return 0; +} + +static const struct owl_sps_domain_info s500_sps_domains[] = { + [S500_PD_VDE] = { + .name = "VDE", + .pwr_bit = 0, + .ack_bit = 16, + }, + [S500_PD_VCE_SI] = { + .name = "VCE_SI", + .pwr_bit = 1, + .ack_bit = 17, + }, + [S500_PD_USB2_1] = { + .name = "USB2_1", + .pwr_bit = 2, + .ack_bit = 18, + }, + [S500_PD_CPU2] = { + .name = "CPU2", + .pwr_bit = 5, + .ack_bit = 21, + .genpd_flags = GENPD_FLAG_ALWAYS_ON, + }, + [S500_PD_CPU3] = { + .name = "CPU3", + .pwr_bit = 6, + .ack_bit = 22, + .genpd_flags = GENPD_FLAG_ALWAYS_ON, + }, + [S500_PD_DMA] = { + .name = "DMA", + .pwr_bit = 8, + .ack_bit = 12, + }, + [S500_PD_DS] = { + .name = "DS", + .pwr_bit = 9, + .ack_bit = 13, + }, + [S500_PD_USB3] = { + .name = "USB3", + .pwr_bit = 10, + .ack_bit = 14, + }, + [S500_PD_USB2_0] = { + .name = "USB2_0", + .pwr_bit = 11, + .ack_bit = 15, + }, +}; + +static const struct owl_sps_info s500_sps_info = { + .num_domains = ARRAY_SIZE(s500_sps_domains), + .domains = s500_sps_domains, +}; + +static const struct owl_sps_domain_info s700_sps_domains[] = { + [S700_PD_VDE] = { + .name = "VDE", + .pwr_bit = 0, + }, + [S700_PD_VCE_SI] = { + .name = "VCE_SI", + .pwr_bit = 1, + }, + [S700_PD_USB2_1] = { + .name = "USB2_1", + .pwr_bit = 2, + }, + [S700_PD_HDE] = { + .name = "HDE", + .pwr_bit = 7, + }, + [S700_PD_DMA] = { + .name = "DMA", + .pwr_bit = 8, + }, + [S700_PD_DS] = { + .name = "DS", + .pwr_bit = 9, + }, + [S700_PD_USB3] = { + .name = "USB3", + .pwr_bit = 10, + }, + [S700_PD_USB2_0] = { + .name = "USB2_0", + .pwr_bit = 11, + }, +}; + +static const struct owl_sps_info s700_sps_info = { + .num_domains = ARRAY_SIZE(s700_sps_domains), + .domains = s700_sps_domains, +}; + +static const struct owl_sps_domain_info s900_sps_domains[] = { + [S900_PD_GPU_B] = { + .name = "GPU_B", + .pwr_bit = 3, + }, + [S900_PD_VCE] = { + .name = "VCE", + .pwr_bit = 4, + }, + [S900_PD_SENSOR] = { + .name = "SENSOR", + .pwr_bit = 5, + }, + [S900_PD_VDE] = { + .name = "VDE", + .pwr_bit = 6, + }, + [S900_PD_HDE] = { + .name = "HDE", + .pwr_bit = 7, + }, + [S900_PD_USB3] = { + .name = "USB3", + .pwr_bit = 8, + }, + [S900_PD_DDR0] = { + .name = "DDR0", + .pwr_bit = 9, + }, + [S900_PD_DDR1] = { + .name = "DDR1", + .pwr_bit = 10, + }, + [S900_PD_DE] = { + .name = "DE", + .pwr_bit = 13, + }, + [S900_PD_NAND] = { + .name = "NAND", + .pwr_bit = 14, + }, + [S900_PD_USB2_H0] = { + .name = "USB2_H0", + .pwr_bit = 15, + }, + [S900_PD_USB2_H1] = { + .name = "USB2_H1", + .pwr_bit = 16, + }, +}; + +static const struct owl_sps_info s900_sps_info = { + .num_domains = ARRAY_SIZE(s900_sps_domains), + .domains = s900_sps_domains, +}; + +static const struct of_device_id owl_sps_of_matches[] = { + { .compatible = "actions,s500-sps", .data = &s500_sps_info }, + { .compatible = "actions,s700-sps", .data = &s700_sps_info }, + { .compatible = "actions,s900-sps", .data = &s900_sps_info }, + { } +}; + +static struct platform_driver owl_sps_platform_driver = { + .probe = owl_sps_probe, + .driver = { + .name = "owl-sps", + .of_match_table = owl_sps_of_matches, + .suppress_bind_attrs = true, + }, +}; + +static int __init owl_sps_init(void) +{ + return platform_driver_register(&owl_sps_platform_driver); +} +postcore_initcall(owl_sps_init); diff --git a/drivers/pmdomain/amlogic/Makefile b/drivers/pmdomain/amlogic/Makefile new file mode 100644 index 000000000000..3d58abd574f9 --- /dev/null +++ b/drivers/pmdomain/amlogic/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MESON_GX_PM_DOMAINS) += meson-gx-pwrc-vpu.o +obj-$(CONFIG_MESON_EE_PM_DOMAINS) += meson-ee-pwrc.o +obj-$(CONFIG_MESON_SECURE_PM_DOMAINS) += meson-secure-pwrc.o diff --git a/drivers/pmdomain/amlogic/meson-ee-pwrc.c b/drivers/pmdomain/amlogic/meson-ee-pwrc.c new file mode 100644 index 000000000000..cfb796d40d9d --- /dev/null +++ b/drivers/pmdomain/amlogic/meson-ee-pwrc.c @@ -0,0 +1,635 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2019 BayLibre, SAS + * Author: Neil Armstrong + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AO Offsets */ + +#define GX_AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) +#define GX_AO_RTI_GEN_PWR_ISO0 (0x3b << 2) + +/* + * Meson8/Meson8b/Meson8m2 only expose the power management registers of the + * AO-bus as syscon. 0x3a from GX translates to 0x02, 0x3b translates to 0x03 + * and so on. + */ +#define MESON8_AO_RTI_GEN_PWR_SLEEP0 (0x02 << 2) +#define MESON8_AO_RTI_GEN_PWR_ISO0 (0x03 << 2) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG3 (0x43 << 2) +#define HHI_VPU_MEM_PD_REG4 (0x44 << 2) +#define HHI_AUDIO_MEM_PD_REG0 (0x45 << 2) +#define HHI_NANOQ_MEM_PD_REG0 (0x46 << 2) +#define HHI_NANOQ_MEM_PD_REG1 (0x47 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) + +#define G12A_HHI_NANOQ_MEM_PD_REG0 (0x43 << 2) +#define G12A_HHI_NANOQ_MEM_PD_REG1 (0x44 << 2) + +struct meson_ee_pwrc; +struct meson_ee_pwrc_domain; + +struct meson_ee_pwrc_mem_domain { + unsigned int reg; + unsigned int mask; +}; + +struct meson_ee_pwrc_top_domain { + unsigned int sleep_reg; + unsigned int sleep_mask; + unsigned int iso_reg; + unsigned int iso_mask; +}; + +struct meson_ee_pwrc_domain_desc { + char *name; + unsigned int reset_names_count; + unsigned int clk_names_count; + struct meson_ee_pwrc_top_domain *top_pd; + unsigned int mem_pd_count; + struct meson_ee_pwrc_mem_domain *mem_pd; + bool (*is_powered_off)(struct meson_ee_pwrc_domain *pwrc_domain); +}; + +struct meson_ee_pwrc_domain_data { + unsigned int count; + struct meson_ee_pwrc_domain_desc *domains; +}; + +/* TOP Power Domains */ + +static struct meson_ee_pwrc_top_domain gx_pwrc_vpu = { + .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = GX_AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +static struct meson_ee_pwrc_top_domain meson8_pwrc_vpu = { + .sleep_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(8), + .iso_reg = MESON8_AO_RTI_GEN_PWR_SLEEP0, + .iso_mask = BIT(9), +}; + +#define SM1_EE_PD(__bit) \ + { \ + .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, \ + .sleep_mask = BIT(__bit), \ + .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, \ + .iso_mask = BIT(__bit), \ + } + +static struct meson_ee_pwrc_top_domain sm1_pwrc_vpu = SM1_EE_PD(8); +static struct meson_ee_pwrc_top_domain sm1_pwrc_nna = SM1_EE_PD(16); +static struct meson_ee_pwrc_top_domain sm1_pwrc_usb = SM1_EE_PD(17); +static struct meson_ee_pwrc_top_domain sm1_pwrc_pci = SM1_EE_PD(18); +static struct meson_ee_pwrc_top_domain sm1_pwrc_ge2d = SM1_EE_PD(19); + +static struct meson_ee_pwrc_top_domain g12a_pwrc_nna = { + .sleep_reg = GX_AO_RTI_GEN_PWR_SLEEP0, + .sleep_mask = BIT(16) | BIT(17), + .iso_reg = GX_AO_RTI_GEN_PWR_ISO0, + .iso_mask = BIT(16) | BIT(17), +}; + +/* Memory PD Domains */ + +#define VPU_MEMPD(__reg) \ + { __reg, GENMASK(1, 0) }, \ + { __reg, GENMASK(3, 2) }, \ + { __reg, GENMASK(5, 4) }, \ + { __reg, GENMASK(7, 6) }, \ + { __reg, GENMASK(9, 8) }, \ + { __reg, GENMASK(11, 10) }, \ + { __reg, GENMASK(13, 12) }, \ + { __reg, GENMASK(15, 14) }, \ + { __reg, GENMASK(17, 16) }, \ + { __reg, GENMASK(19, 18) }, \ + { __reg, GENMASK(21, 20) }, \ + { __reg, GENMASK(23, 22) }, \ + { __reg, GENMASK(25, 24) }, \ + { __reg, GENMASK(27, 26) }, \ + { __reg, GENMASK(29, 28) }, \ + { __reg, GENMASK(31, 30) } + +#define VPU_HHI_MEMPD(__reg) \ + { __reg, BIT(8) }, \ + { __reg, BIT(9) }, \ + { __reg, BIT(10) }, \ + { __reg, BIT(11) }, \ + { __reg, BIT(12) }, \ + { __reg, BIT(13) }, \ + { __reg, BIT(14) }, \ + { __reg, BIT(15) } + +static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain gxbb_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain meson_pwrc_mem_eth[] = { + { HHI_MEM_PD_REG0, GENMASK(3, 2) }, +}; + +static struct meson_ee_pwrc_mem_domain meson8_pwrc_audio_dsp_mem[] = { + { HHI_MEM_PD_REG0, GENMASK(1, 0) }, +}; + +static struct meson_ee_pwrc_mem_domain meson8_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_vpu[] = { + VPU_MEMPD(HHI_VPU_MEM_PD_REG0), + VPU_MEMPD(HHI_VPU_MEM_PD_REG1), + VPU_MEMPD(HHI_VPU_MEM_PD_REG2), + VPU_MEMPD(HHI_VPU_MEM_PD_REG3), + { HHI_VPU_MEM_PD_REG4, GENMASK(1, 0) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(3, 2) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(5, 4) }, + { HHI_VPU_MEM_PD_REG4, GENMASK(7, 6) }, + VPU_HHI_MEMPD(HHI_MEM_PD_REG0), +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_nna[] = { + { HHI_NANOQ_MEM_PD_REG0, 0xff }, + { HHI_NANOQ_MEM_PD_REG1, 0xff }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_usb[] = { + { HHI_MEM_PD_REG0, GENMASK(31, 30) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_pcie[] = { + { HHI_MEM_PD_REG0, GENMASK(29, 26) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_ge2d[] = { + { HHI_MEM_PD_REG0, GENMASK(25, 18) }, +}; + +static struct meson_ee_pwrc_mem_domain axg_pwrc_mem_audio[] = { + { HHI_MEM_PD_REG0, GENMASK(5, 4) }, +}; + +static struct meson_ee_pwrc_mem_domain sm1_pwrc_mem_audio[] = { + { HHI_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(1, 0) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(3, 2) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(5, 4) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(7, 6) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(13, 12) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(15, 14) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(17, 16) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(19, 18) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(21, 20) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(23, 22) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(25, 24) }, + { HHI_AUDIO_MEM_PD_REG0, GENMASK(27, 26) }, +}; + +static struct meson_ee_pwrc_mem_domain g12a_pwrc_mem_nna[] = { + { G12A_HHI_NANOQ_MEM_PD_REG0, GENMASK(31, 0) }, + { G12A_HHI_NANOQ_MEM_PD_REG1, GENMASK(23, 0) }, +}; + +#define VPU_PD(__name, __top_pd, __mem, __is_pwr_off, __resets, __clks) \ + { \ + .name = __name, \ + .reset_names_count = __resets, \ + .clk_names_count = __clks, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .is_powered_off = __is_pwr_off, \ + } + +#define TOP_PD(__name, __top_pd, __mem, __is_pwr_off) \ + { \ + .name = __name, \ + .top_pd = __top_pd, \ + .mem_pd_count = ARRAY_SIZE(__mem), \ + .mem_pd = __mem, \ + .is_powered_off = __is_pwr_off, \ + } + +#define MEM_PD(__name, __mem) \ + TOP_PD(__name, NULL, __mem, NULL) + +static bool pwrc_ee_is_powered_off(struct meson_ee_pwrc_domain *pwrc_domain); + +static struct meson_ee_pwrc_domain_desc axg_pwrc_domains[] = { + [PWRC_AXG_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, axg_pwrc_mem_vpu, + pwrc_ee_is_powered_off, 5, 2), + [PWRC_AXG_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), + [PWRC_AXG_AUDIO_ID] = MEM_PD("AUDIO", axg_pwrc_mem_audio), +}; + +static struct meson_ee_pwrc_domain_desc g12a_pwrc_domains[] = { + [PWRC_G12A_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, g12a_pwrc_mem_vpu, + pwrc_ee_is_powered_off, 11, 2), + [PWRC_G12A_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), + [PWRC_G12A_NNA_ID] = TOP_PD("NNA", &g12a_pwrc_nna, g12a_pwrc_mem_nna, + pwrc_ee_is_powered_off), +}; + +static struct meson_ee_pwrc_domain_desc gxbb_pwrc_domains[] = { + [PWRC_GXBB_VPU_ID] = VPU_PD("VPU", &gx_pwrc_vpu, gxbb_pwrc_mem_vpu, + pwrc_ee_is_powered_off, 12, 2), + [PWRC_GXBB_ETHERNET_MEM_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), +}; + +static struct meson_ee_pwrc_domain_desc meson8_pwrc_domains[] = { + [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, + meson8_pwrc_mem_vpu, + pwrc_ee_is_powered_off, 0, 1), + [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM", + meson_pwrc_mem_eth), + [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM", + meson8_pwrc_audio_dsp_mem), +}; + +static struct meson_ee_pwrc_domain_desc meson8b_pwrc_domains[] = { + [PWRC_MESON8_VPU_ID] = VPU_PD("VPU", &meson8_pwrc_vpu, + meson8_pwrc_mem_vpu, + pwrc_ee_is_powered_off, 11, 1), + [PWRC_MESON8_ETHERNET_MEM_ID] = MEM_PD("ETHERNET_MEM", + meson_pwrc_mem_eth), + [PWRC_MESON8_AUDIO_DSP_MEM_ID] = MEM_PD("AUDIO_DSP_MEM", + meson8_pwrc_audio_dsp_mem), +}; + +static struct meson_ee_pwrc_domain_desc sm1_pwrc_domains[] = { + [PWRC_SM1_VPU_ID] = VPU_PD("VPU", &sm1_pwrc_vpu, sm1_pwrc_mem_vpu, + pwrc_ee_is_powered_off, 11, 2), + [PWRC_SM1_NNA_ID] = TOP_PD("NNA", &sm1_pwrc_nna, sm1_pwrc_mem_nna, + pwrc_ee_is_powered_off), + [PWRC_SM1_USB_ID] = TOP_PD("USB", &sm1_pwrc_usb, sm1_pwrc_mem_usb, + pwrc_ee_is_powered_off), + [PWRC_SM1_PCIE_ID] = TOP_PD("PCI", &sm1_pwrc_pci, sm1_pwrc_mem_pcie, + pwrc_ee_is_powered_off), + [PWRC_SM1_GE2D_ID] = TOP_PD("GE2D", &sm1_pwrc_ge2d, sm1_pwrc_mem_ge2d, + pwrc_ee_is_powered_off), + [PWRC_SM1_AUDIO_ID] = MEM_PD("AUDIO", sm1_pwrc_mem_audio), + [PWRC_SM1_ETH_ID] = MEM_PD("ETH", meson_pwrc_mem_eth), +}; + +struct meson_ee_pwrc_domain { + struct generic_pm_domain base; + bool enabled; + struct meson_ee_pwrc *pwrc; + struct meson_ee_pwrc_domain_desc desc; + struct clk_bulk_data *clks; + int num_clks; + struct reset_control *rstc; + int num_rstc; +}; + +struct meson_ee_pwrc { + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct meson_ee_pwrc_domain *domains; + struct genpd_onecell_data xlate; +}; + +static bool pwrc_ee_is_powered_off(struct meson_ee_pwrc_domain *pwrc_domain) +{ + u32 reg; + + regmap_read(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, ®); + + return (reg & pwrc_domain->desc.top_pd->sleep_mask); +} + +static int meson_ee_pwrc_off(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, + pwrc_domain->desc.top_pd->sleep_mask); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, + pwrc_domain->desc.mem_pd[i].mask); + + udelay(20); + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, + pwrc_domain->desc.top_pd->iso_mask); + + if (pwrc_domain->num_clks) { + msleep(20); + clk_bulk_disable_unprepare(pwrc_domain->num_clks, + pwrc_domain->clks); + } + + return 0; +} + +static int meson_ee_pwrc_on(struct generic_pm_domain *domain) +{ + struct meson_ee_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_ee_pwrc_domain, base); + int i, ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->sleep_reg, + pwrc_domain->desc.top_pd->sleep_mask, 0); + udelay(20); + + for (i = 0 ; i < pwrc_domain->desc.mem_pd_count ; ++i) + regmap_update_bits(pwrc_domain->pwrc->regmap_hhi, + pwrc_domain->desc.mem_pd[i].reg, + pwrc_domain->desc.mem_pd[i].mask, 0); + + udelay(20); + + ret = reset_control_assert(pwrc_domain->rstc); + if (ret) + return ret; + + if (pwrc_domain->desc.top_pd) + regmap_update_bits(pwrc_domain->pwrc->regmap_ao, + pwrc_domain->desc.top_pd->iso_reg, + pwrc_domain->desc.top_pd->iso_mask, 0); + + ret = reset_control_deassert(pwrc_domain->rstc); + if (ret) + return ret; + + return clk_bulk_prepare_enable(pwrc_domain->num_clks, + pwrc_domain->clks); +} + +static int meson_ee_pwrc_init_domain(struct platform_device *pdev, + struct meson_ee_pwrc *pwrc, + struct meson_ee_pwrc_domain *dom) +{ + int ret; + + dom->pwrc = pwrc; + dom->num_rstc = dom->desc.reset_names_count; + dom->num_clks = dom->desc.clk_names_count; + + if (dom->num_rstc) { + int count = reset_control_get_count(&pdev->dev); + + if (count != dom->num_rstc) + dev_warn(&pdev->dev, "Invalid resets count %d for domain %s\n", + count, dom->desc.name); + + dom->rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(dom->rstc)) + return PTR_ERR(dom->rstc); + } + + if (dom->num_clks) { + int ret = devm_clk_bulk_get_all(&pdev->dev, &dom->clks); + if (ret < 0) + return ret; + + if (dom->num_clks != ret) { + dev_warn(&pdev->dev, "Invalid clocks count %d for domain %s\n", + ret, dom->desc.name); + dom->num_clks = ret; + } + } + + dom->base.name = dom->desc.name; + dom->base.power_on = meson_ee_pwrc_on; + dom->base.power_off = meson_ee_pwrc_off; + + /* + * TOFIX: This is a special case for the VPU power domain, which can + * be enabled previously by the bootloader. In this case the VPU + * pipeline may be functional but no driver maybe never attach + * to this power domain, and if the domain is disabled it could + * cause system errors. This is why the pm_domain_always_on_gov + * is used here. + * For the same reason, the clocks should be enabled in case + * we need to power the domain off, otherwise the internal clocks + * prepare/enable counters won't be in sync. + */ + if (dom->num_clks && dom->desc.is_powered_off && !dom->desc.is_powered_off(dom)) { + ret = clk_bulk_prepare_enable(dom->num_clks, dom->clks); + if (ret) + return ret; + + dom->base.flags = GENPD_FLAG_ALWAYS_ON; + ret = pm_genpd_init(&dom->base, NULL, false); + if (ret) + return ret; + } else { + ret = pm_genpd_init(&dom->base, NULL, + (dom->desc.is_powered_off ? + dom->desc.is_powered_off(dom) : true)); + if (ret) + return ret; + } + + return 0; +} + +static int meson_ee_pwrc_probe(struct platform_device *pdev) +{ + const struct meson_ee_pwrc_domain_data *match; + struct regmap *regmap_ao, *regmap_hhi; + struct device_node *parent_np; + struct meson_ee_pwrc *pwrc; + int i, ret; + + match = of_device_get_match_data(&pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); + if (!pwrc) + return -ENOMEM; + + pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->xlate.domains), + GFP_KERNEL); + if (!pwrc->xlate.domains) + return -ENOMEM; + + pwrc->domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->domains), GFP_KERNEL); + if (!pwrc->domains) + return -ENOMEM; + + pwrc->xlate.num_domains = match->count; + + parent_np = of_get_parent(pdev->dev.of_node); + regmap_hhi = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + if (IS_ERR(regmap_hhi)) { + dev_err(&pdev->dev, "failed to get HHI regmap\n"); + return PTR_ERR(regmap_hhi); + } + + regmap_ao = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,ao-sysctrl"); + if (IS_ERR(regmap_ao)) { + dev_err(&pdev->dev, "failed to get AO regmap\n"); + return PTR_ERR(regmap_ao); + } + + pwrc->regmap_ao = regmap_ao; + pwrc->regmap_hhi = regmap_hhi; + + platform_set_drvdata(pdev, pwrc); + + for (i = 0 ; i < match->count ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + memcpy(&dom->desc, &match->domains[i], sizeof(dom->desc)); + + ret = meson_ee_pwrc_init_domain(pdev, pwrc, dom); + if (ret) + return ret; + + pwrc->xlate.domains[i] = &dom->base; + } + + return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); +} + +static void meson_ee_pwrc_shutdown(struct platform_device *pdev) +{ + struct meson_ee_pwrc *pwrc = platform_get_drvdata(pdev); + int i; + + for (i = 0 ; i < pwrc->xlate.num_domains ; ++i) { + struct meson_ee_pwrc_domain *dom = &pwrc->domains[i]; + + if (dom->desc.is_powered_off && !dom->desc.is_powered_off(dom)) + meson_ee_pwrc_off(&dom->base); + } +} + +static struct meson_ee_pwrc_domain_data meson_ee_g12a_pwrc_data = { + .count = ARRAY_SIZE(g12a_pwrc_domains), + .domains = g12a_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_axg_pwrc_data = { + .count = ARRAY_SIZE(axg_pwrc_domains), + .domains = axg_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_gxbb_pwrc_data = { + .count = ARRAY_SIZE(gxbb_pwrc_domains), + .domains = gxbb_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_m8_pwrc_data = { + .count = ARRAY_SIZE(meson8_pwrc_domains), + .domains = meson8_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_m8b_pwrc_data = { + .count = ARRAY_SIZE(meson8b_pwrc_domains), + .domains = meson8b_pwrc_domains, +}; + +static struct meson_ee_pwrc_domain_data meson_ee_sm1_pwrc_data = { + .count = ARRAY_SIZE(sm1_pwrc_domains), + .domains = sm1_pwrc_domains, +}; + +static const struct of_device_id meson_ee_pwrc_match_table[] = { + { + .compatible = "amlogic,meson8-pwrc", + .data = &meson_ee_m8_pwrc_data, + }, + { + .compatible = "amlogic,meson8b-pwrc", + .data = &meson_ee_m8b_pwrc_data, + }, + { + .compatible = "amlogic,meson8m2-pwrc", + .data = &meson_ee_m8b_pwrc_data, + }, + { + .compatible = "amlogic,meson-axg-pwrc", + .data = &meson_ee_axg_pwrc_data, + }, + { + .compatible = "amlogic,meson-gxbb-pwrc", + .data = &meson_ee_gxbb_pwrc_data, + }, + { + .compatible = "amlogic,meson-g12a-pwrc", + .data = &meson_ee_g12a_pwrc_data, + }, + { + .compatible = "amlogic,meson-sm1-pwrc", + .data = &meson_ee_sm1_pwrc_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_ee_pwrc_match_table); + +static struct platform_driver meson_ee_pwrc_driver = { + .probe = meson_ee_pwrc_probe, + .shutdown = meson_ee_pwrc_shutdown, + .driver = { + .name = "meson_ee_pwrc", + .of_match_table = meson_ee_pwrc_match_table, + }, +}; +module_platform_driver(meson_ee_pwrc_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c b/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c new file mode 100644 index 000000000000..33df520eab95 --- /dev/null +++ b/drivers/pmdomain/amlogic/meson-gx-pwrc-vpu.c @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2017 BayLibre, SAS + * Author: Neil Armstrong + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* AO Offsets */ + +#define AO_RTI_GEN_PWR_SLEEP0 (0x3a << 2) + +#define GEN_PWR_VPU_HDMI BIT(8) +#define GEN_PWR_VPU_HDMI_ISO BIT(9) + +/* HHI Offsets */ + +#define HHI_MEM_PD_REG0 (0x40 << 2) +#define HHI_VPU_MEM_PD_REG0 (0x41 << 2) +#define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) + +struct meson_gx_pwrc_vpu { + struct generic_pm_domain genpd; + struct regmap *regmap_ao; + struct regmap *regmap_hhi; + struct reset_control *rstc; + struct clk *vpu_clk; + struct clk *vapb_clk; +}; + +static inline +struct meson_gx_pwrc_vpu *genpd_to_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct meson_gx_pwrc_vpu, genpd); +} + +static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); + udelay(20); + + /* Power Down Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), BIT(i)); + udelay(5); + } + udelay(20); + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); + + msleep(20); + + clk_disable_unprepare(pd->vpu_clk); + clk_disable_unprepare(pd->vapb_clk); + + return 0; +} + +static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); + udelay(20); + + /* Power Down Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), BIT(i)); + udelay(5); + } + udelay(20); + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); + + msleep(20); + + clk_disable_unprepare(pd->vpu_clk); + clk_disable_unprepare(pd->vapb_clk); + + return 0; +} + +static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) +{ + int ret; + + ret = clk_prepare_enable(pd->vpu_clk); + if (ret) + return ret; + + ret = clk_prepare_enable(pd->vapb_clk); + if (ret) + clk_disable_unprepare(pd->vpu_clk); + + return ret; +} + +static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int ret; + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, 0); + udelay(20); + + /* Power Up Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x3 << i, 0); + udelay(5); + } + + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x3 << i, 0); + udelay(5); + } + + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), 0); + udelay(5); + } + udelay(20); + + ret = reset_control_assert(pd->rstc); + if (ret) + return ret; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, 0); + + ret = reset_control_deassert(pd->rstc); + if (ret) + return ret; + + ret = meson_gx_pwrc_vpu_setup_clk(pd); + if (ret) + return ret; + + return 0; +} + +static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int ret; + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, 0); + udelay(20); + + /* Power Up Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x3 << i, 0); + udelay(5); + } + + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x3 << i, 0); + udelay(5); + } + + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, + 0x3 << i, 0); + udelay(5); + } + + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), 0); + udelay(5); + } + udelay(20); + + ret = reset_control_assert(pd->rstc); + if (ret) + return ret; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, 0); + + ret = reset_control_deassert(pd->rstc); + if (ret) + return ret; + + ret = meson_gx_pwrc_vpu_setup_clk(pd); + if (ret) + return ret; + + return 0; +} + +static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) +{ + u32 reg; + + regmap_read(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, ®); + + return (reg & GEN_PWR_VPU_HDMI); +} + +static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { + .genpd = { + .name = "vpu_hdmi", + .power_off = meson_gx_pwrc_vpu_power_off, + .power_on = meson_gx_pwrc_vpu_power_on, + }, +}; + +static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = { + .genpd = { + .name = "vpu_hdmi", + .power_off = meson_g12a_pwrc_vpu_power_off, + .power_on = meson_g12a_pwrc_vpu_power_on, + }, +}; + +static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) +{ + const struct meson_gx_pwrc_vpu *vpu_pd_match; + struct regmap *regmap_ao, *regmap_hhi; + struct meson_gx_pwrc_vpu *vpu_pd; + struct device_node *parent_np; + struct reset_control *rstc; + struct clk *vpu_clk; + struct clk *vapb_clk; + bool powered_off; + int ret; + + vpu_pd_match = of_device_get_match_data(&pdev->dev); + if (!vpu_pd_match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); + if (!vpu_pd) + return -ENOMEM; + + memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); + + parent_np = of_get_parent(pdev->dev.of_node); + regmap_ao = syscon_node_to_regmap(parent_np); + of_node_put(parent_np); + if (IS_ERR(regmap_ao)) { + dev_err(&pdev->dev, "failed to get regmap\n"); + return PTR_ERR(regmap_ao); + } + + regmap_hhi = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "amlogic,hhi-sysctrl"); + if (IS_ERR(regmap_hhi)) { + dev_err(&pdev->dev, "failed to get HHI regmap\n"); + return PTR_ERR(regmap_hhi); + } + + rstc = devm_reset_control_array_get_exclusive(&pdev->dev); + if (IS_ERR(rstc)) + return dev_err_probe(&pdev->dev, PTR_ERR(rstc), + "failed to get reset lines\n"); + + vpu_clk = devm_clk_get(&pdev->dev, "vpu"); + if (IS_ERR(vpu_clk)) { + dev_err(&pdev->dev, "vpu clock request failed\n"); + return PTR_ERR(vpu_clk); + } + + vapb_clk = devm_clk_get(&pdev->dev, "vapb"); + if (IS_ERR(vapb_clk)) { + dev_err(&pdev->dev, "vapb clock request failed\n"); + return PTR_ERR(vapb_clk); + } + + vpu_pd->regmap_ao = regmap_ao; + vpu_pd->regmap_hhi = regmap_hhi; + vpu_pd->rstc = rstc; + vpu_pd->vpu_clk = vpu_clk; + vpu_pd->vapb_clk = vapb_clk; + + platform_set_drvdata(pdev, vpu_pd); + + powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); + + /* If already powered, sync the clock states */ + if (!powered_off) { + ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); + if (ret) + return ret; + } + + vpu_pd->genpd.flags = GENPD_FLAG_ALWAYS_ON; + pm_genpd_init(&vpu_pd->genpd, NULL, powered_off); + + return of_genpd_add_provider_simple(pdev->dev.of_node, + &vpu_pd->genpd); +} + +static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) +{ + struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); + bool powered_off; + + powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); + if (!powered_off) + vpu_pd->genpd.power_off(&vpu_pd->genpd); +} + +static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { + { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, + { + .compatible = "amlogic,meson-g12a-pwrc-vpu", + .data = &vpu_hdmi_pd_g12a + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_gx_pwrc_vpu_match_table); + +static struct platform_driver meson_gx_pwrc_vpu_driver = { + .probe = meson_gx_pwrc_vpu_probe, + .shutdown = meson_gx_pwrc_vpu_shutdown, + .driver = { + .name = "meson_gx_pwrc_vpu", + .of_match_table = meson_gx_pwrc_vpu_match_table, + }, +}; +module_platform_driver(meson_gx_pwrc_vpu_driver); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/amlogic/meson-secure-pwrc.c b/drivers/pmdomain/amlogic/meson-secure-pwrc.c new file mode 100644 index 000000000000..89c881c56cd7 --- /dev/null +++ b/drivers/pmdomain/amlogic/meson-secure-pwrc.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: (GPL-2.0+ OR MIT) +/* + * Copyright (c) 2019 Amlogic, Inc. + * Author: Jianxin Pan + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PWRC_ON 1 +#define PWRC_OFF 0 + +struct meson_secure_pwrc_domain { + struct generic_pm_domain base; + unsigned int index; + struct meson_secure_pwrc *pwrc; +}; + +struct meson_secure_pwrc { + struct meson_secure_pwrc_domain *domains; + struct genpd_onecell_data xlate; + struct meson_sm_firmware *fw; +}; + +struct meson_secure_pwrc_domain_desc { + unsigned int index; + unsigned int flags; + char *name; + bool (*is_off)(struct meson_secure_pwrc_domain *pwrc_domain); +}; + +struct meson_secure_pwrc_domain_data { + unsigned int count; + struct meson_secure_pwrc_domain_desc *domains; +}; + +static bool pwrc_secure_is_off(struct meson_secure_pwrc_domain *pwrc_domain) +{ + int is_off = 1; + + if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_GET, &is_off, + pwrc_domain->index, 0, 0, 0, 0) < 0) + pr_err("failed to get power domain status\n"); + + return is_off; +} + +static int meson_secure_pwrc_off(struct generic_pm_domain *domain) +{ + int ret = 0; + struct meson_secure_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_secure_pwrc_domain, base); + + if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, + pwrc_domain->index, PWRC_OFF, 0, 0, 0) < 0) { + pr_err("failed to set power domain off\n"); + ret = -EINVAL; + } + + return ret; +} + +static int meson_secure_pwrc_on(struct generic_pm_domain *domain) +{ + int ret = 0; + struct meson_secure_pwrc_domain *pwrc_domain = + container_of(domain, struct meson_secure_pwrc_domain, base); + + if (meson_sm_call(pwrc_domain->pwrc->fw, SM_A1_PWRC_SET, NULL, + pwrc_domain->index, PWRC_ON, 0, 0, 0) < 0) { + pr_err("failed to set power domain on\n"); + ret = -EINVAL; + } + + return ret; +} + +#define SEC_PD(__name, __flag) \ +[PWRC_##__name##_ID] = \ +{ \ + .name = #__name, \ + .index = PWRC_##__name##_ID, \ + .is_off = pwrc_secure_is_off, \ + .flags = __flag, \ +} + +static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = { + SEC_PD(DSPA, 0), + SEC_PD(DSPB, 0), + /* UART should keep working in ATF after suspend and before resume */ + SEC_PD(UART, GENPD_FLAG_ALWAYS_ON), + /* DMC is for DDR PHY ana/dig and DMC, and should be always on */ + SEC_PD(DMC, GENPD_FLAG_ALWAYS_ON), + SEC_PD(I2C, 0), + SEC_PD(PSRAM, 0), + SEC_PD(ACODEC, 0), + SEC_PD(AUDIO, 0), + SEC_PD(OTP, 0), + SEC_PD(DMA, GENPD_FLAG_ALWAYS_ON | GENPD_FLAG_IRQ_SAFE), + SEC_PD(SD_EMMC, 0), + SEC_PD(RAMA, 0), + /* SRAMB is used as ATF runtime memory, and should be always on */ + SEC_PD(RAMB, GENPD_FLAG_ALWAYS_ON), + SEC_PD(IR, 0), + SEC_PD(SPICC, 0), + SEC_PD(SPIFC, 0), + SEC_PD(USB, 0), + /* NIC is for the Arm NIC-400 interconnect, and should be always on */ + SEC_PD(NIC, GENPD_FLAG_ALWAYS_ON), + SEC_PD(PDMIN, 0), + SEC_PD(RSA, 0), +}; + +static struct meson_secure_pwrc_domain_desc c3_pwrc_domains[] = { + SEC_PD(C3_NNA, 0), + SEC_PD(C3_AUDIO, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_SDIOA, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_EMMC, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_USB_COMB, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_SDCARD, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_ETH, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_GE2D, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_CVE, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_GDC_WRAP, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_ISP_TOP, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_MIPI_ISP_WRAP, GENPD_FLAG_ALWAYS_ON), + SEC_PD(C3_VCODEC, 0), +}; + +static struct meson_secure_pwrc_domain_desc s4_pwrc_domains[] = { + SEC_PD(S4_DOS_HEVC, 0), + SEC_PD(S4_DOS_VDEC, 0), + SEC_PD(S4_VPU_HDMI, 0), + SEC_PD(S4_USB_COMB, 0), + SEC_PD(S4_GE2D, 0), + /* ETH is for ethernet online wakeup, and should be always on */ + SEC_PD(S4_ETH, GENPD_FLAG_ALWAYS_ON), + SEC_PD(S4_DEMOD, 0), + SEC_PD(S4_AUDIO, 0), +}; + +static int meson_secure_pwrc_probe(struct platform_device *pdev) +{ + int i; + struct device_node *sm_np; + struct meson_secure_pwrc *pwrc; + const struct meson_secure_pwrc_domain_data *match; + + match = of_device_get_match_data(&pdev->dev); + if (!match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + sm_np = of_find_compatible_node(NULL, NULL, "amlogic,meson-gxbb-sm"); + if (!sm_np) { + dev_err(&pdev->dev, "no secure-monitor node\n"); + return -ENODEV; + } + + pwrc = devm_kzalloc(&pdev->dev, sizeof(*pwrc), GFP_KERNEL); + if (!pwrc) { + of_node_put(sm_np); + return -ENOMEM; + } + + pwrc->fw = meson_sm_get(sm_np); + of_node_put(sm_np); + if (!pwrc->fw) + return -EPROBE_DEFER; + + pwrc->xlate.domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->xlate.domains), + GFP_KERNEL); + if (!pwrc->xlate.domains) + return -ENOMEM; + + pwrc->domains = devm_kcalloc(&pdev->dev, match->count, + sizeof(*pwrc->domains), GFP_KERNEL); + if (!pwrc->domains) + return -ENOMEM; + + pwrc->xlate.num_domains = match->count; + platform_set_drvdata(pdev, pwrc); + + for (i = 0 ; i < match->count ; ++i) { + struct meson_secure_pwrc_domain *dom = &pwrc->domains[i]; + + if (!match->domains[i].name) + continue; + + dom->pwrc = pwrc; + dom->index = match->domains[i].index; + dom->base.name = match->domains[i].name; + dom->base.flags = match->domains[i].flags; + dom->base.power_on = meson_secure_pwrc_on; + dom->base.power_off = meson_secure_pwrc_off; + + pm_genpd_init(&dom->base, NULL, match->domains[i].is_off(dom)); + + pwrc->xlate.domains[i] = &dom->base; + } + + return of_genpd_add_provider_onecell(pdev->dev.of_node, &pwrc->xlate); +} + +static struct meson_secure_pwrc_domain_data meson_secure_a1_pwrc_data = { + .domains = a1_pwrc_domains, + .count = ARRAY_SIZE(a1_pwrc_domains), +}; + +static struct meson_secure_pwrc_domain_data amlogic_secure_c3_pwrc_data = { + .domains = c3_pwrc_domains, + .count = ARRAY_SIZE(c3_pwrc_domains), +}; + +static struct meson_secure_pwrc_domain_data meson_secure_s4_pwrc_data = { + .domains = s4_pwrc_domains, + .count = ARRAY_SIZE(s4_pwrc_domains), +}; + +static const struct of_device_id meson_secure_pwrc_match_table[] = { + { + .compatible = "amlogic,meson-a1-pwrc", + .data = &meson_secure_a1_pwrc_data, + }, + { + .compatible = "amlogic,c3-pwrc", + .data = &amlogic_secure_c3_pwrc_data, + }, + { + .compatible = "amlogic,meson-s4-pwrc", + .data = &meson_secure_s4_pwrc_data, + }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, meson_secure_pwrc_match_table); + +static struct platform_driver meson_secure_pwrc_driver = { + .probe = meson_secure_pwrc_probe, + .driver = { + .name = "meson_secure_pwrc", + .of_match_table = meson_secure_pwrc_match_table, + }, +}; +module_platform_driver(meson_secure_pwrc_driver); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/pmdomain/apple/Makefile b/drivers/pmdomain/apple/Makefile new file mode 100644 index 000000000000..53665af630be --- /dev/null +++ b/drivers/pmdomain/apple/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_APPLE_PMGR_PWRSTATE) += pmgr-pwrstate.o diff --git a/drivers/pmdomain/apple/pmgr-pwrstate.c b/drivers/pmdomain/apple/pmgr-pwrstate.c new file mode 100644 index 000000000000..d62a776c89a1 --- /dev/null +++ b/drivers/pmdomain/apple/pmgr-pwrstate.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple SoC PMGR device power state driver + * + * Copyright The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define APPLE_PMGR_RESET BIT(31) +#define APPLE_PMGR_AUTO_ENABLE BIT(28) +#define APPLE_PMGR_PS_AUTO GENMASK(27, 24) +#define APPLE_PMGR_PS_MIN GENMASK(19, 16) +#define APPLE_PMGR_PARENT_OFF BIT(11) +#define APPLE_PMGR_DEV_DISABLE BIT(10) +#define APPLE_PMGR_WAS_CLKGATED BIT(9) +#define APPLE_PMGR_WAS_PWRGATED BIT(8) +#define APPLE_PMGR_PS_ACTUAL GENMASK(7, 4) +#define APPLE_PMGR_PS_TARGET GENMASK(3, 0) + +#define APPLE_PMGR_FLAGS (APPLE_PMGR_WAS_CLKGATED | APPLE_PMGR_WAS_PWRGATED) + +#define APPLE_PMGR_PS_ACTIVE 0xf +#define APPLE_PMGR_PS_CLKGATE 0x4 +#define APPLE_PMGR_PS_PWRGATE 0x0 + +#define APPLE_PMGR_PS_SET_TIMEOUT 100 +#define APPLE_PMGR_RESET_TIME 1 + +struct apple_pmgr_ps { + struct device *dev; + struct generic_pm_domain genpd; + struct reset_controller_dev rcdev; + struct regmap *regmap; + u32 offset; + u32 min_state; +}; + +#define genpd_to_apple_pmgr_ps(_genpd) container_of(_genpd, struct apple_pmgr_ps, genpd) +#define rcdev_to_apple_pmgr_ps(_rcdev) container_of(_rcdev, struct apple_pmgr_ps, rcdev) + +static int apple_pmgr_ps_set(struct generic_pm_domain *genpd, u32 pstate, bool auto_enable) +{ + int ret; + struct apple_pmgr_ps *ps = genpd_to_apple_pmgr_ps(genpd); + u32 reg; + + ret = regmap_read(ps->regmap, ps->offset, ®); + if (ret < 0) + return ret; + + /* Resets are synchronous, and only work if the device is powered and clocked. */ + if (reg & APPLE_PMGR_RESET && pstate != APPLE_PMGR_PS_ACTIVE) + dev_err(ps->dev, "PS %s: powering off with RESET active\n", + genpd->name); + + reg &= ~(APPLE_PMGR_AUTO_ENABLE | APPLE_PMGR_FLAGS | APPLE_PMGR_PS_TARGET); + reg |= FIELD_PREP(APPLE_PMGR_PS_TARGET, pstate); + + dev_dbg(ps->dev, "PS %s: pwrstate = 0x%x: 0x%x\n", genpd->name, pstate, reg); + + regmap_write(ps->regmap, ps->offset, reg); + + ret = regmap_read_poll_timeout_atomic( + ps->regmap, ps->offset, reg, + (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == pstate), 1, + APPLE_PMGR_PS_SET_TIMEOUT); + if (ret < 0) + dev_err(ps->dev, "PS %s: Failed to reach power state 0x%x (now: 0x%x)\n", + genpd->name, pstate, reg); + + if (auto_enable) { + /* Not all devices implement this; this is a no-op where not implemented. */ + reg &= ~APPLE_PMGR_FLAGS; + reg |= APPLE_PMGR_AUTO_ENABLE; + regmap_write(ps->regmap, ps->offset, reg); + } + + return ret; +} + +static bool apple_pmgr_ps_is_active(struct apple_pmgr_ps *ps) +{ + u32 reg = 0; + + regmap_read(ps->regmap, ps->offset, ®); + /* + * We consider domains as active if they are actually on, or if they have auto-PM + * enabled and the intended target is on. + */ + return (FIELD_GET(APPLE_PMGR_PS_ACTUAL, reg) == APPLE_PMGR_PS_ACTIVE || + (FIELD_GET(APPLE_PMGR_PS_TARGET, reg) == APPLE_PMGR_PS_ACTIVE && + reg & APPLE_PMGR_AUTO_ENABLE)); +} + +static int apple_pmgr_ps_power_on(struct generic_pm_domain *genpd) +{ + return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_ACTIVE, true); +} + +static int apple_pmgr_ps_power_off(struct generic_pm_domain *genpd) +{ + return apple_pmgr_ps_set(genpd, APPLE_PMGR_PS_PWRGATE, false); +} + +static int apple_pmgr_reset_assert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); + unsigned long flags; + + spin_lock_irqsave(&ps->genpd.slock, flags); + + if (ps->genpd.status == GENPD_STATE_OFF) + dev_err(ps->dev, "PS 0x%x: asserting RESET while powered down\n", ps->offset); + + dev_dbg(ps->dev, "PS 0x%x: assert reset\n", ps->offset); + /* Quiesce device before asserting reset */ + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, + APPLE_PMGR_DEV_DISABLE); + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, + APPLE_PMGR_RESET); + + spin_unlock_irqrestore(&ps->genpd.slock, flags); + + return 0; +} + +static int apple_pmgr_reset_deassert(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); + unsigned long flags; + + spin_lock_irqsave(&ps->genpd.slock, flags); + + dev_dbg(ps->dev, "PS 0x%x: deassert reset\n", ps->offset); + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_RESET, 0); + regmap_update_bits(ps->regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_DEV_DISABLE, 0); + + if (ps->genpd.status == GENPD_STATE_OFF) + dev_err(ps->dev, "PS 0x%x: RESET was deasserted while powered down\n", ps->offset); + + spin_unlock_irqrestore(&ps->genpd.slock, flags); + + return 0; +} + +static int apple_pmgr_reset_reset(struct reset_controller_dev *rcdev, unsigned long id) +{ + int ret; + + ret = apple_pmgr_reset_assert(rcdev, id); + if (ret) + return ret; + + usleep_range(APPLE_PMGR_RESET_TIME, 2 * APPLE_PMGR_RESET_TIME); + + return apple_pmgr_reset_deassert(rcdev, id); +} + +static int apple_pmgr_reset_status(struct reset_controller_dev *rcdev, unsigned long id) +{ + struct apple_pmgr_ps *ps = rcdev_to_apple_pmgr_ps(rcdev); + u32 reg = 0; + + regmap_read(ps->regmap, ps->offset, ®); + + return !!(reg & APPLE_PMGR_RESET); +} + +const struct reset_control_ops apple_pmgr_reset_ops = { + .assert = apple_pmgr_reset_assert, + .deassert = apple_pmgr_reset_deassert, + .reset = apple_pmgr_reset_reset, + .status = apple_pmgr_reset_status, +}; + +static int apple_pmgr_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + return 0; +} + +static int apple_pmgr_ps_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *node = dev->of_node; + struct apple_pmgr_ps *ps; + struct regmap *regmap; + struct of_phandle_iterator it; + int ret; + const char *name; + bool active; + + regmap = syscon_node_to_regmap(node->parent); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + ps = devm_kzalloc(dev, sizeof(*ps), GFP_KERNEL); + if (!ps) + return -ENOMEM; + + ps->dev = dev; + ps->regmap = regmap; + + ret = of_property_read_string(node, "label", &name); + if (ret < 0) { + dev_err(dev, "missing label property\n"); + return ret; + } + + ret = of_property_read_u32(node, "reg", &ps->offset); + if (ret < 0) { + dev_err(dev, "missing reg property\n"); + return ret; + } + + ps->genpd.flags |= GENPD_FLAG_IRQ_SAFE; + ps->genpd.name = name; + ps->genpd.power_on = apple_pmgr_ps_power_on; + ps->genpd.power_off = apple_pmgr_ps_power_off; + + ret = of_property_read_u32(node, "apple,min-state", &ps->min_state); + if (ret == 0 && ps->min_state <= APPLE_PMGR_PS_ACTIVE) + regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_PS_MIN, + FIELD_PREP(APPLE_PMGR_PS_MIN, ps->min_state)); + + active = apple_pmgr_ps_is_active(ps); + if (of_property_read_bool(node, "apple,always-on")) { + ps->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + if (!active) { + dev_warn(dev, "always-on domain %s is not on at boot\n", name); + /* Turn it on so pm_genpd_init does not fail */ + active = apple_pmgr_ps_power_on(&ps->genpd) == 0; + } + } + + /* Turn on auto-PM if the domain is already on */ + if (active) + regmap_update_bits(regmap, ps->offset, APPLE_PMGR_FLAGS | APPLE_PMGR_AUTO_ENABLE, + APPLE_PMGR_AUTO_ENABLE); + + ret = pm_genpd_init(&ps->genpd, NULL, !active); + if (ret < 0) { + dev_err(dev, "pm_genpd_init failed\n"); + return ret; + } + + ret = of_genpd_add_provider_simple(node, &ps->genpd); + if (ret < 0) { + dev_err(dev, "of_genpd_add_provider_simple failed\n"); + return ret; + } + + of_for_each_phandle(&it, ret, node, "power-domains", "#power-domain-cells", -1) { + struct of_phandle_args parent, child; + + parent.np = it.node; + parent.args_count = of_phandle_iterator_args(&it, parent.args, MAX_PHANDLE_ARGS); + child.np = node; + child.args_count = 0; + ret = of_genpd_add_subdomain(&parent, &child); + + if (ret == -EPROBE_DEFER) { + of_node_put(parent.np); + goto err_remove; + } else if (ret < 0) { + dev_err(dev, "failed to add to parent domain: %d (%s -> %s)\n", + ret, it.node->name, node->name); + of_node_put(parent.np); + goto err_remove; + } + } + + /* + * Do not participate in regular PM; parent power domains are handled via the + * genpd hierarchy. + */ + pm_genpd_remove_device(dev); + + ps->rcdev.owner = THIS_MODULE; + ps->rcdev.nr_resets = 1; + ps->rcdev.ops = &apple_pmgr_reset_ops; + ps->rcdev.of_node = dev->of_node; + ps->rcdev.of_reset_n_cells = 0; + ps->rcdev.of_xlate = apple_pmgr_reset_xlate; + + ret = devm_reset_controller_register(dev, &ps->rcdev); + if (ret < 0) + goto err_remove; + + return 0; +err_remove: + of_genpd_del_provider(node); + pm_genpd_remove(&ps->genpd); + return ret; +} + +static const struct of_device_id apple_pmgr_ps_of_match[] = { + { .compatible = "apple,pmgr-pwrstate" }, + {} +}; + +MODULE_DEVICE_TABLE(of, apple_pmgr_ps_of_match); + +static struct platform_driver apple_pmgr_ps_driver = { + .probe = apple_pmgr_ps_probe, + .driver = { + .name = "apple-pmgr-pwrstate", + .of_match_table = apple_pmgr_ps_of_match, + }, +}; + +MODULE_AUTHOR("Hector Martin "); +MODULE_DESCRIPTION("PMGR power state driver for Apple SoCs"); + +module_platform_driver(apple_pmgr_ps_driver); diff --git a/drivers/pmdomain/bcm/Makefile b/drivers/pmdomain/bcm/Makefile new file mode 100644 index 000000000000..6bfbe4e4db13 --- /dev/null +++ b/drivers/pmdomain/bcm/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_BCM_PMB) += bcm-pmb.o +obj-$(CONFIG_BCM2835_POWER) += bcm2835-power.o +obj-$(CONFIG_BCM63XX_POWER) += bcm63xx-power.o +obj-$(CONFIG_RASPBERRYPI_POWER) += raspberrypi-power.o diff --git a/drivers/pmdomain/bcm/bcm-pmb.c b/drivers/pmdomain/bcm/bcm-pmb.c new file mode 100644 index 000000000000..a72ba26ecf9d --- /dev/null +++ b/drivers/pmdomain/bcm/bcm-pmb.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (c) 2013 Broadcom + * Copyright (C) 2020 Rafał Miłecki + */ + +#include +#include +#include +#include +#include +#include +#include + +#define BPCM_ID_REG 0x00 +#define BPCM_CAPABILITIES 0x04 +#define BPCM_CAP_NUM_ZONES 0x000000ff +#define BPCM_CAP_SR_REG_BITS 0x0000ff00 +#define BPCM_CAP_PLLTYPE 0x00030000 +#define BPCM_CAP_UBUS 0x00080000 +#define BPCM_CONTROL 0x08 +#define BPCM_STATUS 0x0c +#define BPCM_ROSC_CONTROL 0x10 +#define BPCM_ROSC_THRESH_H 0x14 +#define BPCM_ROSC_THRESHOLD_BCM6838 0x14 +#define BPCM_ROSC_THRESH_S 0x18 +#define BPCM_ROSC_COUNT_BCM6838 0x18 +#define BPCM_ROSC_COUNT 0x1c +#define BPCM_PWD_CONTROL_BCM6838 0x1c +#define BPCM_PWD_CONTROL 0x20 +#define BPCM_SR_CONTROL_BCM6838 0x20 +#define BPCM_PWD_ACCUM_CONTROL 0x24 +#define BPCM_SR_CONTROL 0x28 +#define BPCM_GLOBAL_CONTROL 0x2c +#define BPCM_MISC_CONTROL 0x30 +#define BPCM_MISC_CONTROL2 0x34 +#define BPCM_SGPHY_CNTL 0x38 +#define BPCM_SGPHY_STATUS 0x3c +#define BPCM_ZONE0 0x40 +#define BPCM_ZONE_CONTROL 0x00 +#define BPCM_ZONE_CONTROL_MANUAL_CLK_EN 0x00000001 +#define BPCM_ZONE_CONTROL_MANUAL_RESET_CTL 0x00000002 +#define BPCM_ZONE_CONTROL_FREQ_SCALE_USED 0x00000004 /* R/O */ +#define BPCM_ZONE_CONTROL_DPG_CAPABLE 0x00000008 /* R/O */ +#define BPCM_ZONE_CONTROL_MANUAL_MEM_PWR 0x00000030 +#define BPCM_ZONE_CONTROL_MANUAL_ISO_CTL 0x00000040 +#define BPCM_ZONE_CONTROL_MANUAL_CTL 0x00000080 +#define BPCM_ZONE_CONTROL_DPG_CTL_EN 0x00000100 +#define BPCM_ZONE_CONTROL_PWR_DN_REQ 0x00000200 +#define BPCM_ZONE_CONTROL_PWR_UP_REQ 0x00000400 +#define BPCM_ZONE_CONTROL_MEM_PWR_CTL_EN 0x00000800 +#define BPCM_ZONE_CONTROL_BLK_RESET_ASSERT 0x00001000 +#define BPCM_ZONE_CONTROL_MEM_STBY 0x00002000 +#define BPCM_ZONE_CONTROL_RESERVED 0x0007c000 +#define BPCM_ZONE_CONTROL_PWR_CNTL_STATE 0x00f80000 +#define BPCM_ZONE_CONTROL_FREQ_SCALAR_DYN_SEL 0x01000000 /* R/O */ +#define BPCM_ZONE_CONTROL_PWR_OFF_STATE 0x02000000 /* R/O */ +#define BPCM_ZONE_CONTROL_PWR_ON_STATE 0x04000000 /* R/O */ +#define BPCM_ZONE_CONTROL_PWR_GOOD 0x08000000 /* R/O */ +#define BPCM_ZONE_CONTROL_DPG_PWR_STATE 0x10000000 /* R/O */ +#define BPCM_ZONE_CONTROL_MEM_PWR_STATE 0x20000000 /* R/O */ +#define BPCM_ZONE_CONTROL_ISO_STATE 0x40000000 /* R/O */ +#define BPCM_ZONE_CONTROL_RESET_STATE 0x80000000 /* R/O */ +#define BPCM_ZONE_CONFIG1 0x04 +#define BPCM_ZONE_CONFIG2 0x08 +#define BPCM_ZONE_FREQ_SCALAR_CONTROL 0x0c +#define BPCM_ZONE_SIZE 0x10 + +struct bcm_pmb { + struct device *dev; + void __iomem *base; + spinlock_t lock; + bool little_endian; + struct genpd_onecell_data genpd_onecell_data; +}; + +struct bcm_pmb_pd_data { + const char * const name; + int id; + u8 bus; + u8 device; +}; + +struct bcm_pmb_pm_domain { + struct bcm_pmb *pmb; + const struct bcm_pmb_pd_data *data; + struct generic_pm_domain genpd; +}; + +static int bcm_pmb_bpcm_read(struct bcm_pmb *pmb, int bus, u8 device, + int offset, u32 *val) +{ + void __iomem *base = pmb->base + bus * 0x20; + unsigned long flags; + int err; + + spin_lock_irqsave(&pmb->lock, flags); + err = bpcm_rd(base, device, offset, val); + spin_unlock_irqrestore(&pmb->lock, flags); + + if (!err) + *val = pmb->little_endian ? le32_to_cpu(*val) : be32_to_cpu(*val); + + return err; +} + +static int bcm_pmb_bpcm_write(struct bcm_pmb *pmb, int bus, u8 device, + int offset, u32 val) +{ + void __iomem *base = pmb->base + bus * 0x20; + unsigned long flags; + int err; + + val = pmb->little_endian ? cpu_to_le32(val) : cpu_to_be32(val); + + spin_lock_irqsave(&pmb->lock, flags); + err = bpcm_wr(base, device, offset, val); + spin_unlock_irqrestore(&pmb->lock, flags); + + return err; +} + +static int bcm_pmb_power_off_zone(struct bcm_pmb *pmb, int bus, u8 device, + int zone) +{ + int offset; + u32 val; + int err; + + offset = BPCM_ZONE0 + zone * BPCM_ZONE_SIZE + BPCM_ZONE_CONTROL; + + err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val); + if (err) + return err; + + val |= BPCM_ZONE_CONTROL_PWR_DN_REQ; + val &= ~BPCM_ZONE_CONTROL_PWR_UP_REQ; + + err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val); + + return err; +} + +static int bcm_pmb_power_on_zone(struct bcm_pmb *pmb, int bus, u8 device, + int zone) +{ + int offset; + u32 val; + int err; + + offset = BPCM_ZONE0 + zone * BPCM_ZONE_SIZE + BPCM_ZONE_CONTROL; + + err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val); + if (err) + return err; + + if (!(val & BPCM_ZONE_CONTROL_PWR_ON_STATE)) { + val &= ~BPCM_ZONE_CONTROL_PWR_DN_REQ; + val |= BPCM_ZONE_CONTROL_DPG_CTL_EN; + val |= BPCM_ZONE_CONTROL_PWR_UP_REQ; + val |= BPCM_ZONE_CONTROL_MEM_PWR_CTL_EN; + val |= BPCM_ZONE_CONTROL_BLK_RESET_ASSERT; + + err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val); + } + + return err; +} + +static int bcm_pmb_power_off_device(struct bcm_pmb *pmb, int bus, u8 device) +{ + int offset; + u32 val; + int err; + + /* Entire device can be powered off by powering off the 0th zone */ + offset = BPCM_ZONE0 + BPCM_ZONE_CONTROL; + + err = bcm_pmb_bpcm_read(pmb, bus, device, offset, &val); + if (err) + return err; + + if (!(val & BPCM_ZONE_CONTROL_PWR_OFF_STATE)) { + val = BPCM_ZONE_CONTROL_PWR_DN_REQ; + + err = bcm_pmb_bpcm_write(pmb, bus, device, offset, val); + } + + return err; +} + +static int bcm_pmb_power_on_device(struct bcm_pmb *pmb, int bus, u8 device) +{ + u32 val; + int err; + int i; + + err = bcm_pmb_bpcm_read(pmb, bus, device, BPCM_CAPABILITIES, &val); + if (err) + return err; + + for (i = 0; i < (val & BPCM_CAP_NUM_ZONES); i++) { + err = bcm_pmb_power_on_zone(pmb, bus, device, i); + if (err) + return err; + } + + return err; +} + +static int bcm_pmb_power_on_sata(struct bcm_pmb *pmb, int bus, u8 device) +{ + int err; + + err = bcm_pmb_power_on_zone(pmb, bus, device, 0); + if (err) + return err; + + /* Does not apply to the BCM963158 */ + err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_MISC_CONTROL, 0); + if (err) + return err; + + err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_SR_CONTROL, 0xffffffff); + if (err) + return err; + + err = bcm_pmb_bpcm_write(pmb, bus, device, BPCM_SR_CONTROL, 0); + + return err; +} + +static int bcm_pmb_power_on(struct generic_pm_domain *genpd) +{ + struct bcm_pmb_pm_domain *pd = container_of(genpd, struct bcm_pmb_pm_domain, genpd); + const struct bcm_pmb_pd_data *data = pd->data; + struct bcm_pmb *pmb = pd->pmb; + + switch (data->id) { + case BCM_PMB_PCIE0: + case BCM_PMB_PCIE1: + case BCM_PMB_PCIE2: + return bcm_pmb_power_on_zone(pmb, data->bus, data->device, 0); + case BCM_PMB_HOST_USB: + return bcm_pmb_power_on_device(pmb, data->bus, data->device); + case BCM_PMB_SATA: + return bcm_pmb_power_on_sata(pmb, data->bus, data->device); + default: + dev_err(pmb->dev, "unsupported device id: %d\n", data->id); + return -EINVAL; + } +} + +static int bcm_pmb_power_off(struct generic_pm_domain *genpd) +{ + struct bcm_pmb_pm_domain *pd = container_of(genpd, struct bcm_pmb_pm_domain, genpd); + const struct bcm_pmb_pd_data *data = pd->data; + struct bcm_pmb *pmb = pd->pmb; + + switch (data->id) { + case BCM_PMB_PCIE0: + case BCM_PMB_PCIE1: + case BCM_PMB_PCIE2: + return bcm_pmb_power_off_zone(pmb, data->bus, data->device, 0); + case BCM_PMB_HOST_USB: + return bcm_pmb_power_off_device(pmb, data->bus, data->device); + default: + dev_err(pmb->dev, "unsupported device id: %d\n", data->id); + return -EINVAL; + } +} + +static int bcm_pmb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct bcm_pmb_pd_data *table; + const struct bcm_pmb_pd_data *e; + struct bcm_pmb *pmb; + int max_id; + int err; + + pmb = devm_kzalloc(dev, sizeof(*pmb), GFP_KERNEL); + if (!pmb) + return -ENOMEM; + + pmb->dev = dev; + + pmb->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pmb->base)) + return PTR_ERR(pmb->base); + + spin_lock_init(&pmb->lock); + + pmb->little_endian = !of_device_is_big_endian(dev->of_node); + + table = of_device_get_match_data(dev); + if (!table) + return -EINVAL; + + max_id = 0; + for (e = table; e->name; e++) + max_id = max(max_id, e->id); + + pmb->genpd_onecell_data.num_domains = max_id + 1; + pmb->genpd_onecell_data.domains = + devm_kcalloc(dev, pmb->genpd_onecell_data.num_domains, + sizeof(struct generic_pm_domain *), GFP_KERNEL); + if (!pmb->genpd_onecell_data.domains) + return -ENOMEM; + + for (e = table; e->name; e++) { + struct bcm_pmb_pm_domain *pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + + if (!pd) + return -ENOMEM; + + pd->pmb = pmb; + pd->data = e; + pd->genpd.name = e->name; + pd->genpd.power_on = bcm_pmb_power_on; + pd->genpd.power_off = bcm_pmb_power_off; + + pm_genpd_init(&pd->genpd, NULL, true); + pmb->genpd_onecell_data.domains[e->id] = &pd->genpd; + } + + err = of_genpd_add_provider_onecell(dev->of_node, &pmb->genpd_onecell_data); + if (err) { + dev_err(dev, "failed to add genpd provider: %d\n", err); + return err; + } + + return 0; +} + +static const struct bcm_pmb_pd_data bcm_pmb_bcm4908_data[] = { + { .name = "pcie2", .id = BCM_PMB_PCIE2, .bus = 0, .device = 2, }, + { .name = "pcie0", .id = BCM_PMB_PCIE0, .bus = 1, .device = 14, }, + { .name = "pcie1", .id = BCM_PMB_PCIE1, .bus = 1, .device = 15, }, + { .name = "usb", .id = BCM_PMB_HOST_USB, .bus = 1, .device = 17, }, + { }, +}; + +static const struct bcm_pmb_pd_data bcm_pmb_bcm63138_data[] = { + { .name = "sata", .id = BCM_PMB_SATA, .bus = 0, .device = 3, }, + { }, +}; + +static const struct of_device_id bcm_pmb_of_match[] = { + { .compatible = "brcm,bcm4908-pmb", .data = &bcm_pmb_bcm4908_data, }, + { .compatible = "brcm,bcm63138-pmb", .data = &bcm_pmb_bcm63138_data, }, + { }, +}; + +static struct platform_driver bcm_pmb_driver = { + .driver = { + .name = "bcm-pmb", + .of_match_table = bcm_pmb_of_match, + }, + .probe = bcm_pmb_probe, +}; + +builtin_platform_driver(bcm_pmb_driver); diff --git a/drivers/pmdomain/bcm/bcm2835-power.c b/drivers/pmdomain/bcm/bcm2835-power.c new file mode 100644 index 000000000000..1a179d4e011c --- /dev/null +++ b/drivers/pmdomain/bcm/bcm2835-power.c @@ -0,0 +1,713 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Power domain driver for Broadcom BCM2835 + * + * Copyright (C) 2018 Broadcom + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PM_GNRIC 0x00 +#define PM_AUDIO 0x04 +#define PM_STATUS 0x18 +#define PM_RSTC 0x1c +#define PM_RSTS 0x20 +#define PM_WDOG 0x24 +#define PM_PADS0 0x28 +#define PM_PADS2 0x2c +#define PM_PADS3 0x30 +#define PM_PADS4 0x34 +#define PM_PADS5 0x38 +#define PM_PADS6 0x3c +#define PM_CAM0 0x44 +#define PM_CAM0_LDOHPEN BIT(2) +#define PM_CAM0_LDOLPEN BIT(1) +#define PM_CAM0_CTRLEN BIT(0) + +#define PM_CAM1 0x48 +#define PM_CAM1_LDOHPEN BIT(2) +#define PM_CAM1_LDOLPEN BIT(1) +#define PM_CAM1_CTRLEN BIT(0) + +#define PM_CCP2TX 0x4c +#define PM_CCP2TX_LDOEN BIT(1) +#define PM_CCP2TX_CTRLEN BIT(0) + +#define PM_DSI0 0x50 +#define PM_DSI0_LDOHPEN BIT(2) +#define PM_DSI0_LDOLPEN BIT(1) +#define PM_DSI0_CTRLEN BIT(0) + +#define PM_DSI1 0x54 +#define PM_DSI1_LDOHPEN BIT(2) +#define PM_DSI1_LDOLPEN BIT(1) +#define PM_DSI1_CTRLEN BIT(0) + +#define PM_HDMI 0x58 +#define PM_HDMI_RSTDR BIT(19) +#define PM_HDMI_LDOPD BIT(1) +#define PM_HDMI_CTRLEN BIT(0) + +#define PM_USB 0x5c +/* The power gates must be enabled with this bit before enabling the LDO in the + * USB block. + */ +#define PM_USB_CTRLEN BIT(0) + +#define PM_PXLDO 0x60 +#define PM_PXBG 0x64 +#define PM_DFT 0x68 +#define PM_SMPS 0x6c +#define PM_XOSC 0x70 +#define PM_SPAREW 0x74 +#define PM_SPARER 0x78 +#define PM_AVS_RSTDR 0x7c +#define PM_AVS_STAT 0x80 +#define PM_AVS_EVENT 0x84 +#define PM_AVS_INTEN 0x88 +#define PM_DUMMY 0xfc + +#define PM_IMAGE 0x108 +#define PM_GRAFX 0x10c +#define PM_PROC 0x110 +#define PM_ENAB BIT(12) +#define PM_ISPRSTN BIT(8) +#define PM_H264RSTN BIT(7) +#define PM_PERIRSTN BIT(6) +#define PM_V3DRSTN BIT(6) +#define PM_ISFUNC BIT(5) +#define PM_MRDONE BIT(4) +#define PM_MEMREP BIT(3) +#define PM_ISPOW BIT(2) +#define PM_POWOK BIT(1) +#define PM_POWUP BIT(0) +#define PM_INRUSH_SHIFT 13 +#define PM_INRUSH_3_5_MA 0 +#define PM_INRUSH_5_MA 1 +#define PM_INRUSH_10_MA 2 +#define PM_INRUSH_20_MA 3 +#define PM_INRUSH_MASK (3 << PM_INRUSH_SHIFT) + +#define PM_PASSWORD 0x5a000000 + +#define PM_WDOG_TIME_SET 0x000fffff +#define PM_RSTC_WRCFG_CLR 0xffffffcf +#define PM_RSTS_HADWRH_SET 0x00000040 +#define PM_RSTC_WRCFG_SET 0x00000030 +#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 +#define PM_RSTC_RESET 0x00000102 + +#define PM_READ(reg) readl(power->base + (reg)) +#define PM_WRITE(reg, val) writel(PM_PASSWORD | (val), power->base + (reg)) + +#define ASB_BRDG_VERSION 0x00 +#define ASB_CPR_CTRL 0x04 + +#define ASB_V3D_S_CTRL 0x08 +#define ASB_V3D_M_CTRL 0x0c +#define ASB_ISP_S_CTRL 0x10 +#define ASB_ISP_M_CTRL 0x14 +#define ASB_H264_S_CTRL 0x18 +#define ASB_H264_M_CTRL 0x1c + +#define ASB_REQ_STOP BIT(0) +#define ASB_ACK BIT(1) +#define ASB_EMPTY BIT(2) +#define ASB_FULL BIT(3) + +#define ASB_AXI_BRDG_ID 0x20 + +#define BCM2835_BRDG_ID 0x62726467 + +struct bcm2835_power_domain { + struct generic_pm_domain base; + struct bcm2835_power *power; + u32 domain; + struct clk *clk; +}; + +struct bcm2835_power { + struct device *dev; + /* PM registers. */ + void __iomem *base; + /* AXI Async bridge registers. */ + void __iomem *asb; + /* RPiVid bridge registers. */ + void __iomem *rpivid_asb; + + struct genpd_onecell_data pd_xlate; + struct bcm2835_power_domain domains[BCM2835_POWER_DOMAIN_COUNT]; + struct reset_controller_dev reset; +}; + +static int bcm2835_asb_control(struct bcm2835_power *power, u32 reg, bool enable) +{ + void __iomem *base = power->asb; + u64 start; + u32 val; + + switch (reg) { + case 0: + return 0; + case ASB_V3D_S_CTRL: + case ASB_V3D_M_CTRL: + if (power->rpivid_asb) + base = power->rpivid_asb; + break; + } + + start = ktime_get_ns(); + + /* Enable the module's async AXI bridges. */ + if (enable) { + val = readl(base + reg) & ~ASB_REQ_STOP; + } else { + val = readl(base + reg) | ASB_REQ_STOP; + } + writel(PM_PASSWORD | val, base + reg); + + while (readl(base + reg) & ASB_ACK) { + cpu_relax(); + if (ktime_get_ns() - start >= 1000) + return -ETIMEDOUT; + } + + return 0; +} + +static int bcm2835_asb_enable(struct bcm2835_power *power, u32 reg) +{ + return bcm2835_asb_control(power, reg, true); +} + +static int bcm2835_asb_disable(struct bcm2835_power *power, u32 reg) +{ + return bcm2835_asb_control(power, reg, false); +} + +static int bcm2835_power_power_off(struct bcm2835_power_domain *pd, u32 pm_reg) +{ + struct bcm2835_power *power = pd->power; + + /* We don't run this on BCM2711 */ + if (power->rpivid_asb) + return 0; + + /* Enable functional isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISFUNC); + + /* Enable electrical isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); + + /* Open the power switches. */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_POWUP); + + return 0; +} + +static int bcm2835_power_power_on(struct bcm2835_power_domain *pd, u32 pm_reg) +{ + struct bcm2835_power *power = pd->power; + struct device *dev = power->dev; + u64 start; + int ret; + int inrush; + bool powok; + + /* We don't run this on BCM2711 */ + if (power->rpivid_asb) + return 0; + + /* If it was already powered on by the fw, leave it that way. */ + if (PM_READ(pm_reg) & PM_POWUP) + return 0; + + /* Enable power. Allowing too much current at once may result + * in POWOK never getting set, so start low and ramp it up as + * necessary to succeed. + */ + powok = false; + for (inrush = PM_INRUSH_3_5_MA; inrush <= PM_INRUSH_20_MA; inrush++) { + PM_WRITE(pm_reg, + (PM_READ(pm_reg) & ~PM_INRUSH_MASK) | + (inrush << PM_INRUSH_SHIFT) | + PM_POWUP); + + start = ktime_get_ns(); + while (!(powok = !!(PM_READ(pm_reg) & PM_POWOK))) { + cpu_relax(); + if (ktime_get_ns() - start >= 3000) + break; + } + } + if (!powok) { + dev_err(dev, "Timeout waiting for %s power OK\n", + pd->base.name); + ret = -ETIMEDOUT; + goto err_disable_powup; + } + + /* Disable electrical isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISPOW); + + /* Repair memory */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_MEMREP); + start = ktime_get_ns(); + while (!(PM_READ(pm_reg) & PM_MRDONE)) { + cpu_relax(); + if (ktime_get_ns() - start >= 1000) { + dev_err(dev, "Timeout waiting for %s memory repair\n", + pd->base.name); + ret = -ETIMEDOUT; + goto err_disable_ispow; + } + } + + /* Disable functional isolation */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | PM_ISFUNC); + + return 0; + +err_disable_ispow: + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~PM_ISPOW); +err_disable_powup: + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~(PM_POWUP | PM_INRUSH_MASK)); + return ret; +} + +static int bcm2835_asb_power_on(struct bcm2835_power_domain *pd, + u32 pm_reg, + u32 asb_m_reg, + u32 asb_s_reg, + u32 reset_flags) +{ + struct bcm2835_power *power = pd->power; + int ret; + + ret = clk_prepare_enable(pd->clk); + if (ret) { + dev_err(power->dev, "Failed to enable clock for %s\n", + pd->base.name); + return ret; + } + + /* Wait 32 clocks for reset to propagate, 1 us will be enough */ + udelay(1); + + clk_disable_unprepare(pd->clk); + + /* Deassert the resets. */ + PM_WRITE(pm_reg, PM_READ(pm_reg) | reset_flags); + + ret = clk_prepare_enable(pd->clk); + if (ret) { + dev_err(power->dev, "Failed to enable clock for %s\n", + pd->base.name); + goto err_enable_resets; + } + + ret = bcm2835_asb_enable(power, asb_m_reg); + if (ret) { + dev_err(power->dev, "Failed to enable ASB master for %s\n", + pd->base.name); + goto err_disable_clk; + } + ret = bcm2835_asb_enable(power, asb_s_reg); + if (ret) { + dev_err(power->dev, "Failed to enable ASB slave for %s\n", + pd->base.name); + goto err_disable_asb_master; + } + + return 0; + +err_disable_asb_master: + bcm2835_asb_disable(power, asb_m_reg); +err_disable_clk: + clk_disable_unprepare(pd->clk); +err_enable_resets: + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); + return ret; +} + +static int bcm2835_asb_power_off(struct bcm2835_power_domain *pd, + u32 pm_reg, + u32 asb_m_reg, + u32 asb_s_reg, + u32 reset_flags) +{ + struct bcm2835_power *power = pd->power; + int ret; + + ret = bcm2835_asb_disable(power, asb_s_reg); + if (ret) { + dev_warn(power->dev, "Failed to disable ASB slave for %s\n", + pd->base.name); + return ret; + } + ret = bcm2835_asb_disable(power, asb_m_reg); + if (ret) { + dev_warn(power->dev, "Failed to disable ASB master for %s\n", + pd->base.name); + bcm2835_asb_enable(power, asb_s_reg); + return ret; + } + + clk_disable_unprepare(pd->clk); + + /* Assert the resets. */ + PM_WRITE(pm_reg, PM_READ(pm_reg) & ~reset_flags); + + return 0; +} + +static int bcm2835_power_pd_power_on(struct generic_pm_domain *domain) +{ + struct bcm2835_power_domain *pd = + container_of(domain, struct bcm2835_power_domain, base); + struct bcm2835_power *power = pd->power; + + switch (pd->domain) { + case BCM2835_POWER_DOMAIN_GRAFX: + return bcm2835_power_power_on(pd, PM_GRAFX); + + case BCM2835_POWER_DOMAIN_GRAFX_V3D: + return bcm2835_asb_power_on(pd, PM_GRAFX, + ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, + PM_V3DRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE: + return bcm2835_power_power_on(pd, PM_IMAGE); + + case BCM2835_POWER_DOMAIN_IMAGE_PERI: + return bcm2835_asb_power_on(pd, PM_IMAGE, + 0, 0, + PM_PERIRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_ISP: + return bcm2835_asb_power_on(pd, PM_IMAGE, + ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, + PM_ISPRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_H264: + return bcm2835_asb_power_on(pd, PM_IMAGE, + ASB_H264_M_CTRL, ASB_H264_S_CTRL, + PM_H264RSTN); + + case BCM2835_POWER_DOMAIN_USB: + PM_WRITE(PM_USB, PM_USB_CTRLEN); + return 0; + + case BCM2835_POWER_DOMAIN_DSI0: + PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); + PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN | PM_DSI0_LDOHPEN); + return 0; + + case BCM2835_POWER_DOMAIN_DSI1: + PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); + PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN | PM_DSI1_LDOHPEN); + return 0; + + case BCM2835_POWER_DOMAIN_CCP2TX: + PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); + PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN | PM_CCP2TX_LDOEN); + return 0; + + case BCM2835_POWER_DOMAIN_HDMI: + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_RSTDR); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_CTRLEN); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_LDOPD); + usleep_range(100, 200); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_RSTDR); + return 0; + + default: + dev_err(power->dev, "Invalid domain %d\n", pd->domain); + return -EINVAL; + } +} + +static int bcm2835_power_pd_power_off(struct generic_pm_domain *domain) +{ + struct bcm2835_power_domain *pd = + container_of(domain, struct bcm2835_power_domain, base); + struct bcm2835_power *power = pd->power; + + switch (pd->domain) { + case BCM2835_POWER_DOMAIN_GRAFX: + return bcm2835_power_power_off(pd, PM_GRAFX); + + case BCM2835_POWER_DOMAIN_GRAFX_V3D: + return bcm2835_asb_power_off(pd, PM_GRAFX, + ASB_V3D_M_CTRL, ASB_V3D_S_CTRL, + PM_V3DRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE: + return bcm2835_power_power_off(pd, PM_IMAGE); + + case BCM2835_POWER_DOMAIN_IMAGE_PERI: + return bcm2835_asb_power_off(pd, PM_IMAGE, + 0, 0, + PM_PERIRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_ISP: + return bcm2835_asb_power_off(pd, PM_IMAGE, + ASB_ISP_M_CTRL, ASB_ISP_S_CTRL, + PM_ISPRSTN); + + case BCM2835_POWER_DOMAIN_IMAGE_H264: + return bcm2835_asb_power_off(pd, PM_IMAGE, + ASB_H264_M_CTRL, ASB_H264_S_CTRL, + PM_H264RSTN); + + case BCM2835_POWER_DOMAIN_USB: + PM_WRITE(PM_USB, 0); + return 0; + + case BCM2835_POWER_DOMAIN_DSI0: + PM_WRITE(PM_DSI0, PM_DSI0_CTRLEN); + PM_WRITE(PM_DSI0, 0); + return 0; + + case BCM2835_POWER_DOMAIN_DSI1: + PM_WRITE(PM_DSI1, PM_DSI1_CTRLEN); + PM_WRITE(PM_DSI1, 0); + return 0; + + case BCM2835_POWER_DOMAIN_CCP2TX: + PM_WRITE(PM_CCP2TX, PM_CCP2TX_CTRLEN); + PM_WRITE(PM_CCP2TX, 0); + return 0; + + case BCM2835_POWER_DOMAIN_HDMI: + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) | PM_HDMI_LDOPD); + PM_WRITE(PM_HDMI, PM_READ(PM_HDMI) & ~PM_HDMI_CTRLEN); + return 0; + + default: + dev_err(power->dev, "Invalid domain %d\n", pd->domain); + return -EINVAL; + } +} + +static int +bcm2835_init_power_domain(struct bcm2835_power *power, + int pd_xlate_index, const char *name) +{ + struct device *dev = power->dev; + struct bcm2835_power_domain *dom = &power->domains[pd_xlate_index]; + + dom->clk = devm_clk_get(dev->parent, name); + if (IS_ERR(dom->clk)) { + int ret = PTR_ERR(dom->clk); + + if (ret == -EPROBE_DEFER) + return ret; + + /* Some domains don't have a clk, so make sure that we + * don't deref an error pointer later. + */ + dom->clk = NULL; + } + + dom->base.name = name; + dom->base.power_on = bcm2835_power_pd_power_on; + dom->base.power_off = bcm2835_power_pd_power_off; + + dom->domain = pd_xlate_index; + dom->power = power; + + /* XXX: on/off at boot? */ + pm_genpd_init(&dom->base, NULL, true); + + power->pd_xlate.domains[pd_xlate_index] = &dom->base; + + return 0; +} + +/** bcm2835_reset_reset - Resets a block that has a reset line in the + * PM block. + * + * The consumer of the reset controller must have the power domain up + * -- there's no reset ability with the power domain down. To reset + * the sub-block, we just disable its access to memory through the + * ASB, reset, and re-enable. + */ +static int bcm2835_reset_reset(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, + reset); + struct bcm2835_power_domain *pd; + int ret; + + switch (id) { + case BCM2835_RESET_V3D: + pd = &power->domains[BCM2835_POWER_DOMAIN_GRAFX_V3D]; + break; + case BCM2835_RESET_H264: + pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_H264]; + break; + case BCM2835_RESET_ISP: + pd = &power->domains[BCM2835_POWER_DOMAIN_IMAGE_ISP]; + break; + default: + dev_err(power->dev, "Bad reset id %ld\n", id); + return -EINVAL; + } + + ret = bcm2835_power_pd_power_off(&pd->base); + if (ret) + return ret; + + return bcm2835_power_pd_power_on(&pd->base); +} + +static int bcm2835_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct bcm2835_power *power = container_of(rcdev, struct bcm2835_power, + reset); + + switch (id) { + case BCM2835_RESET_V3D: + return !PM_READ(PM_GRAFX & PM_V3DRSTN); + case BCM2835_RESET_H264: + return !PM_READ(PM_IMAGE & PM_H264RSTN); + case BCM2835_RESET_ISP: + return !PM_READ(PM_IMAGE & PM_ISPRSTN); + default: + return -EINVAL; + } +} + +static const struct reset_control_ops bcm2835_reset_ops = { + .reset = bcm2835_reset_reset, + .status = bcm2835_reset_status, +}; + +static const char *const power_domain_names[] = { + [BCM2835_POWER_DOMAIN_GRAFX] = "grafx", + [BCM2835_POWER_DOMAIN_GRAFX_V3D] = "v3d", + + [BCM2835_POWER_DOMAIN_IMAGE] = "image", + [BCM2835_POWER_DOMAIN_IMAGE_PERI] = "peri_image", + [BCM2835_POWER_DOMAIN_IMAGE_H264] = "h264", + [BCM2835_POWER_DOMAIN_IMAGE_ISP] = "isp", + + [BCM2835_POWER_DOMAIN_USB] = "usb", + [BCM2835_POWER_DOMAIN_DSI0] = "dsi0", + [BCM2835_POWER_DOMAIN_DSI1] = "dsi1", + [BCM2835_POWER_DOMAIN_CAM0] = "cam0", + [BCM2835_POWER_DOMAIN_CAM1] = "cam1", + [BCM2835_POWER_DOMAIN_CCP2TX] = "ccp2tx", + [BCM2835_POWER_DOMAIN_HDMI] = "hdmi", +}; + +static int bcm2835_power_probe(struct platform_device *pdev) +{ + struct bcm2835_pm *pm = dev_get_drvdata(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct bcm2835_power *power; + static const struct { + int parent, child; + } domain_deps[] = { + { BCM2835_POWER_DOMAIN_GRAFX, BCM2835_POWER_DOMAIN_GRAFX_V3D }, + { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_PERI }, + { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_H264 }, + { BCM2835_POWER_DOMAIN_IMAGE, BCM2835_POWER_DOMAIN_IMAGE_ISP }, + { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_USB }, + { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM0 }, + { BCM2835_POWER_DOMAIN_IMAGE_PERI, BCM2835_POWER_DOMAIN_CAM1 }, + }; + int ret = 0, i; + u32 id; + + power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + platform_set_drvdata(pdev, power); + + power->dev = dev; + power->base = pm->base; + power->asb = pm->asb; + power->rpivid_asb = pm->rpivid_asb; + + id = readl(power->asb + ASB_AXI_BRDG_ID); + if (id != BCM2835_BRDG_ID /* "BRDG" */) { + dev_err(dev, "ASB register ID returned 0x%08x\n", id); + return -ENODEV; + } + + if (power->rpivid_asb) { + id = readl(power->rpivid_asb + ASB_AXI_BRDG_ID); + if (id != BCM2835_BRDG_ID /* "BRDG" */) { + dev_err(dev, "RPiVid ASB register ID returned 0x%08x\n", + id); + return -ENODEV; + } + } + + power->pd_xlate.domains = devm_kcalloc(dev, + ARRAY_SIZE(power_domain_names), + sizeof(*power->pd_xlate.domains), + GFP_KERNEL); + if (!power->pd_xlate.domains) + return -ENOMEM; + + power->pd_xlate.num_domains = ARRAY_SIZE(power_domain_names); + + for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { + ret = bcm2835_init_power_domain(power, i, power_domain_names[i]); + if (ret) + goto fail; + } + + for (i = 0; i < ARRAY_SIZE(domain_deps); i++) { + pm_genpd_add_subdomain(&power->domains[domain_deps[i].parent].base, + &power->domains[domain_deps[i].child].base); + } + + power->reset.owner = THIS_MODULE; + power->reset.nr_resets = BCM2835_RESET_COUNT; + power->reset.ops = &bcm2835_reset_ops; + power->reset.of_node = dev->parent->of_node; + + ret = devm_reset_controller_register(dev, &power->reset); + if (ret) + goto fail; + + of_genpd_add_provider_onecell(dev->parent->of_node, &power->pd_xlate); + + dev_info(dev, "Broadcom BCM2835 power domains driver"); + return 0; + +fail: + for (i = 0; i < ARRAY_SIZE(power_domain_names); i++) { + struct generic_pm_domain *dom = &power->domains[i].base; + + if (dom->name) + pm_genpd_remove(dom); + } + return ret; +} + +static struct platform_driver bcm2835_power_driver = { + .probe = bcm2835_power_probe, + .driver = { + .name = "bcm2835-power", + }, +}; +module_platform_driver(bcm2835_power_driver); + +MODULE_AUTHOR("Eric Anholt "); +MODULE_DESCRIPTION("Driver for Broadcom BCM2835 PM power domains and reset"); diff --git a/drivers/pmdomain/bcm/bcm63xx-power.c b/drivers/pmdomain/bcm/bcm63xx-power.c new file mode 100644 index 000000000000..98b0c2430dbc --- /dev/null +++ b/drivers/pmdomain/bcm/bcm63xx-power.c @@ -0,0 +1,375 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * BCM63xx Power Domain Controller Driver + * + * Copyright (C) 2020 Álvaro Fernández Rojas + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bcm63xx_power_dev { + struct generic_pm_domain genpd; + struct bcm63xx_power *power; + uint32_t mask; +}; + +struct bcm63xx_power { + void __iomem *base; + spinlock_t lock; + struct bcm63xx_power_dev *dev; + struct genpd_onecell_data genpd_data; + struct generic_pm_domain **genpd; +}; + +struct bcm63xx_power_data { + const char * const name; + uint8_t bit; + unsigned int flags; +}; + +static int bcm63xx_power_get_state(struct bcm63xx_power_dev *pmd, bool *is_on) +{ + struct bcm63xx_power *power = pmd->power; + + if (!pmd->mask) { + *is_on = false; + return -EINVAL; + } + + *is_on = !(__raw_readl(power->base) & pmd->mask); + + return 0; +} + +static int bcm63xx_power_set_state(struct bcm63xx_power_dev *pmd, bool on) +{ + struct bcm63xx_power *power = pmd->power; + unsigned long flags; + uint32_t val; + + if (!pmd->mask) + return -EINVAL; + + spin_lock_irqsave(&power->lock, flags); + val = __raw_readl(power->base); + if (on) + val &= ~pmd->mask; + else + val |= pmd->mask; + __raw_writel(val, power->base); + spin_unlock_irqrestore(&power->lock, flags); + + return 0; +} + +static int bcm63xx_power_on(struct generic_pm_domain *genpd) +{ + struct bcm63xx_power_dev *pmd = container_of(genpd, + struct bcm63xx_power_dev, genpd); + + return bcm63xx_power_set_state(pmd, true); +} + +static int bcm63xx_power_off(struct generic_pm_domain *genpd) +{ + struct bcm63xx_power_dev *pmd = container_of(genpd, + struct bcm63xx_power_dev, genpd); + + return bcm63xx_power_set_state(pmd, false); +} + +static int bcm63xx_power_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct bcm63xx_power_data *entry, *table; + struct bcm63xx_power *power; + unsigned int ndom; + uint8_t max_bit = 0; + int ret; + + power = devm_kzalloc(dev, sizeof(*power), GFP_KERNEL); + if (!power) + return -ENOMEM; + + power->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(power->base)) + return PTR_ERR(power->base); + + table = of_device_get_match_data(dev); + if (!table) + return -EINVAL; + + power->genpd_data.num_domains = 0; + ndom = 0; + for (entry = table; entry->name; entry++) { + max_bit = max(max_bit, entry->bit); + ndom++; + } + + if (!ndom) + return -ENODEV; + + power->genpd_data.num_domains = max_bit + 1; + + power->dev = devm_kcalloc(dev, power->genpd_data.num_domains, + sizeof(struct bcm63xx_power_dev), + GFP_KERNEL); + if (!power->dev) + return -ENOMEM; + + power->genpd = devm_kcalloc(dev, power->genpd_data.num_domains, + sizeof(struct generic_pm_domain *), + GFP_KERNEL); + if (!power->genpd) + return -ENOMEM; + + power->genpd_data.domains = power->genpd; + + ndom = 0; + for (entry = table; entry->name; entry++) { + struct bcm63xx_power_dev *pmd = &power->dev[ndom]; + bool is_on; + + pmd->power = power; + pmd->mask = BIT(entry->bit); + pmd->genpd.name = entry->name; + pmd->genpd.flags = entry->flags; + + ret = bcm63xx_power_get_state(pmd, &is_on); + if (ret) + dev_warn(dev, "unable to get current state for %s\n", + pmd->genpd.name); + + pmd->genpd.power_on = bcm63xx_power_on; + pmd->genpd.power_off = bcm63xx_power_off; + + pm_genpd_init(&pmd->genpd, NULL, !is_on); + power->genpd[entry->bit] = &pmd->genpd; + + ndom++; + } + + spin_lock_init(&power->lock); + + ret = of_genpd_add_provider_onecell(np, &power->genpd_data); + if (ret) { + dev_err(dev, "failed to register genpd driver: %d\n", ret); + return ret; + } + + dev_info(dev, "registered %u power domains\n", ndom); + + return 0; +} + +static const struct bcm63xx_power_data bcm6318_power_domains[] = { + { + .name = "pcie", + .bit = BCM6318_POWER_DOMAIN_PCIE, + }, { + .name = "usb", + .bit = BCM6318_POWER_DOMAIN_USB, + }, { + .name = "ephy0", + .bit = BCM6318_POWER_DOMAIN_EPHY0, + }, { + .name = "ephy1", + .bit = BCM6318_POWER_DOMAIN_EPHY1, + }, { + .name = "ephy2", + .bit = BCM6318_POWER_DOMAIN_EPHY2, + }, { + .name = "ephy3", + .bit = BCM6318_POWER_DOMAIN_EPHY3, + }, { + .name = "ldo2p5", + .bit = BCM6318_POWER_DOMAIN_LDO2P5, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "ldo2p9", + .bit = BCM6318_POWER_DOMAIN_LDO2P9, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "sw1p0", + .bit = BCM6318_POWER_DOMAIN_SW1P0, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "pad", + .bit = BCM6318_POWER_DOMAIN_PAD, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + /* sentinel */ + }, +}; + +static const struct bcm63xx_power_data bcm6328_power_domains[] = { + { + .name = "adsl2-mips", + .bit = BCM6328_POWER_DOMAIN_ADSL2_MIPS, + }, { + .name = "adsl2-phy", + .bit = BCM6328_POWER_DOMAIN_ADSL2_PHY, + }, { + .name = "adsl2-afe", + .bit = BCM6328_POWER_DOMAIN_ADSL2_AFE, + }, { + .name = "sar", + .bit = BCM6328_POWER_DOMAIN_SAR, + }, { + .name = "pcm", + .bit = BCM6328_POWER_DOMAIN_PCM, + }, { + .name = "usbd", + .bit = BCM6328_POWER_DOMAIN_USBD, + }, { + .name = "usbh", + .bit = BCM6328_POWER_DOMAIN_USBH, + }, { + .name = "pcie", + .bit = BCM6328_POWER_DOMAIN_PCIE, + }, { + .name = "robosw", + .bit = BCM6328_POWER_DOMAIN_ROBOSW, + }, { + .name = "ephy", + .bit = BCM6328_POWER_DOMAIN_EPHY, + }, { + /* sentinel */ + }, +}; + +static const struct bcm63xx_power_data bcm6362_power_domains[] = { + { + .name = "sar", + .bit = BCM6362_POWER_DOMAIN_SAR, + }, { + .name = "ipsec", + .bit = BCM6362_POWER_DOMAIN_IPSEC, + }, { + .name = "mips", + .bit = BCM6362_POWER_DOMAIN_MIPS, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "dect", + .bit = BCM6362_POWER_DOMAIN_DECT, + }, { + .name = "usbh", + .bit = BCM6362_POWER_DOMAIN_USBH, + }, { + .name = "usbd", + .bit = BCM6362_POWER_DOMAIN_USBD, + }, { + .name = "robosw", + .bit = BCM6362_POWER_DOMAIN_ROBOSW, + }, { + .name = "pcm", + .bit = BCM6362_POWER_DOMAIN_PCM, + }, { + .name = "periph", + .bit = BCM6362_POWER_DOMAIN_PERIPH, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "adsl-phy", + .bit = BCM6362_POWER_DOMAIN_ADSL_PHY, + }, { + .name = "gmii-pads", + .bit = BCM6362_POWER_DOMAIN_GMII_PADS, + }, { + .name = "fap", + .bit = BCM6362_POWER_DOMAIN_FAP, + }, { + .name = "pcie", + .bit = BCM6362_POWER_DOMAIN_PCIE, + }, { + .name = "wlan-pads", + .bit = BCM6362_POWER_DOMAIN_WLAN_PADS, + }, { + /* sentinel */ + }, +}; + +static const struct bcm63xx_power_data bcm63268_power_domains[] = { + { + .name = "sar", + .bit = BCM63268_POWER_DOMAIN_SAR, + }, { + .name = "ipsec", + .bit = BCM63268_POWER_DOMAIN_IPSEC, + }, { + .name = "mips", + .bit = BCM63268_POWER_DOMAIN_MIPS, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "dect", + .bit = BCM63268_POWER_DOMAIN_DECT, + }, { + .name = "usbh", + .bit = BCM63268_POWER_DOMAIN_USBH, + }, { + .name = "usbd", + .bit = BCM63268_POWER_DOMAIN_USBD, + }, { + .name = "robosw", + .bit = BCM63268_POWER_DOMAIN_ROBOSW, + }, { + .name = "pcm", + .bit = BCM63268_POWER_DOMAIN_PCM, + }, { + .name = "periph", + .bit = BCM63268_POWER_DOMAIN_PERIPH, + .flags = GENPD_FLAG_ALWAYS_ON, + }, { + .name = "vdsl-phy", + .bit = BCM63268_POWER_DOMAIN_VDSL_PHY, + }, { + .name = "vdsl-mips", + .bit = BCM63268_POWER_DOMAIN_VDSL_MIPS, + }, { + .name = "fap", + .bit = BCM63268_POWER_DOMAIN_FAP, + }, { + .name = "pcie", + .bit = BCM63268_POWER_DOMAIN_PCIE, + }, { + .name = "wlan-pads", + .bit = BCM63268_POWER_DOMAIN_WLAN_PADS, + }, { + /* sentinel */ + }, +}; + +static const struct of_device_id bcm63xx_power_of_match[] = { + { + .compatible = "brcm,bcm6318-power-controller", + .data = &bcm6318_power_domains, + }, { + .compatible = "brcm,bcm6328-power-controller", + .data = &bcm6328_power_domains, + }, { + .compatible = "brcm,bcm6362-power-controller", + .data = &bcm6362_power_domains, + }, { + .compatible = "brcm,bcm63268-power-controller", + .data = &bcm63268_power_domains, + }, { + /* sentinel */ + } +}; + +static struct platform_driver bcm63xx_power_driver = { + .driver = { + .name = "bcm63xx-power-controller", + .of_match_table = bcm63xx_power_of_match, + }, + .probe = bcm63xx_power_probe, +}; +builtin_platform_driver(bcm63xx_power_driver); diff --git a/drivers/pmdomain/bcm/raspberrypi-power.c b/drivers/pmdomain/bcm/raspberrypi-power.c new file mode 100644 index 000000000000..06196ebfe03b --- /dev/null +++ b/drivers/pmdomain/bcm/raspberrypi-power.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0 +/* (C) 2015 Pengutronix, Alexander Aring + * + * Authors: + * Alexander Aring + * Eric Anholt + */ + +#include +#include +#include +#include +#include +#include + +/* + * Firmware indices for the old power domains interface. Only a few + * of them were actually implemented. + */ +#define RPI_OLD_POWER_DOMAIN_USB 3 +#define RPI_OLD_POWER_DOMAIN_V3D 10 + +struct rpi_power_domain { + u32 domain; + bool enabled; + bool old_interface; + struct generic_pm_domain base; + struct rpi_firmware *fw; +}; + +struct rpi_power_domains { + bool has_new_interface; + struct genpd_onecell_data xlate; + struct rpi_firmware *fw; + struct rpi_power_domain domains[RPI_POWER_DOMAIN_COUNT]; +}; + +/* + * Packet definition used by RPI_FIRMWARE_SET_POWER_STATE and + * RPI_FIRMWARE_SET_DOMAIN_STATE + */ +struct rpi_power_domain_packet { + u32 domain; + u32 on; +}; + +/* + * Asks the firmware to enable or disable power on a specific power + * domain. + */ +static int rpi_firmware_set_power(struct rpi_power_domain *rpi_domain, bool on) +{ + struct rpi_power_domain_packet packet; + + packet.domain = rpi_domain->domain; + packet.on = on; + return rpi_firmware_property(rpi_domain->fw, + rpi_domain->old_interface ? + RPI_FIRMWARE_SET_POWER_STATE : + RPI_FIRMWARE_SET_DOMAIN_STATE, + &packet, sizeof(packet)); +} + +static int rpi_domain_off(struct generic_pm_domain *domain) +{ + struct rpi_power_domain *rpi_domain = + container_of(domain, struct rpi_power_domain, base); + + return rpi_firmware_set_power(rpi_domain, false); +} + +static int rpi_domain_on(struct generic_pm_domain *domain) +{ + struct rpi_power_domain *rpi_domain = + container_of(domain, struct rpi_power_domain, base); + + return rpi_firmware_set_power(rpi_domain, true); +} + +static void rpi_common_init_power_domain(struct rpi_power_domains *rpi_domains, + int xlate_index, const char *name) +{ + struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; + + dom->fw = rpi_domains->fw; + + dom->base.name = name; + dom->base.power_on = rpi_domain_on; + dom->base.power_off = rpi_domain_off; + + /* + * Treat all power domains as off at boot. + * + * The firmware itself may be keeping some domains on, but + * from Linux's perspective all we control is the refcounts + * that we give to the firmware, and we can't ask the firmware + * to turn off something that we haven't ourselves turned on. + */ + pm_genpd_init(&dom->base, NULL, true); + + rpi_domains->xlate.domains[xlate_index] = &dom->base; +} + +static void rpi_init_power_domain(struct rpi_power_domains *rpi_domains, + int xlate_index, const char *name) +{ + struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; + + if (!rpi_domains->has_new_interface) + return; + + /* The DT binding index is the firmware's domain index minus one. */ + dom->domain = xlate_index + 1; + + rpi_common_init_power_domain(rpi_domains, xlate_index, name); +} + +static void rpi_init_old_power_domain(struct rpi_power_domains *rpi_domains, + int xlate_index, int domain, + const char *name) +{ + struct rpi_power_domain *dom = &rpi_domains->domains[xlate_index]; + + dom->old_interface = true; + dom->domain = domain; + + rpi_common_init_power_domain(rpi_domains, xlate_index, name); +} + +/* + * Detects whether the firmware supports the new power domains interface. + * + * The firmware doesn't actually return an error on an unknown tag, + * and just skips over it, so we do the detection by putting an + * unexpected value in the return field and checking if it was + * unchanged. + */ +static bool +rpi_has_new_domain_support(struct rpi_power_domains *rpi_domains) +{ + struct rpi_power_domain_packet packet; + int ret; + + packet.domain = RPI_POWER_DOMAIN_ARM; + packet.on = ~0; + + ret = rpi_firmware_property(rpi_domains->fw, + RPI_FIRMWARE_GET_DOMAIN_STATE, + &packet, sizeof(packet)); + + return ret == 0 && packet.on != ~0; +} + +static int rpi_power_probe(struct platform_device *pdev) +{ + struct device_node *fw_np; + struct device *dev = &pdev->dev; + struct rpi_power_domains *rpi_domains; + + rpi_domains = devm_kzalloc(dev, sizeof(*rpi_domains), GFP_KERNEL); + if (!rpi_domains) + return -ENOMEM; + + rpi_domains->xlate.domains = + devm_kcalloc(dev, + RPI_POWER_DOMAIN_COUNT, + sizeof(*rpi_domains->xlate.domains), + GFP_KERNEL); + if (!rpi_domains->xlate.domains) + return -ENOMEM; + + rpi_domains->xlate.num_domains = RPI_POWER_DOMAIN_COUNT; + + fw_np = of_parse_phandle(pdev->dev.of_node, "firmware", 0); + if (!fw_np) { + dev_err(&pdev->dev, "no firmware node\n"); + return -ENODEV; + } + + rpi_domains->fw = devm_rpi_firmware_get(&pdev->dev, fw_np); + of_node_put(fw_np); + if (!rpi_domains->fw) + return -EPROBE_DEFER; + + rpi_domains->has_new_interface = + rpi_has_new_domain_support(rpi_domains); + + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C0, "I2C0"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C1, "I2C1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_I2C2, "I2C2"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VIDEO_SCALER, + "VIDEO_SCALER"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VPU1, "VPU1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_HDMI, "HDMI"); + + /* + * Use the old firmware interface for USB power, so that we + * can turn it on even if the firmware hasn't been updated. + */ + rpi_init_old_power_domain(rpi_domains, RPI_POWER_DOMAIN_USB, + RPI_OLD_POWER_DOMAIN_USB, "USB"); + + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_VEC, "VEC"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_JPEG, "JPEG"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_H264, "H264"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_V3D, "V3D"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ISP, "ISP"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM0, "UNICAM0"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_UNICAM1, "UNICAM1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2RX, "CCP2RX"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CSI2, "CSI2"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CPI, "CPI"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI0, "DSI0"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_DSI1, "DSI1"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_TRANSPOSER, + "TRANSPOSER"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CCP2TX, "CCP2TX"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_CDP, "CDP"); + rpi_init_power_domain(rpi_domains, RPI_POWER_DOMAIN_ARM, "ARM"); + + of_genpd_add_provider_onecell(dev->of_node, &rpi_domains->xlate); + + platform_set_drvdata(pdev, rpi_domains); + + return 0; +} + +static const struct of_device_id rpi_power_of_match[] = { + { .compatible = "raspberrypi,bcm2835-power", }, + {}, +}; +MODULE_DEVICE_TABLE(of, rpi_power_of_match); + +static struct platform_driver rpi_power_driver = { + .driver = { + .name = "raspberrypi-power", + .of_match_table = rpi_power_of_match, + }, + .probe = rpi_power_probe, +}; +builtin_platform_driver(rpi_power_driver); + +MODULE_AUTHOR("Alexander Aring "); +MODULE_AUTHOR("Eric Anholt "); +MODULE_DESCRIPTION("Raspberry Pi power domain driver"); diff --git a/drivers/pmdomain/imx/Makefile b/drivers/pmdomain/imx/Makefile new file mode 100644 index 000000000000..52d2629014a7 --- /dev/null +++ b/drivers/pmdomain/imx/Makefile @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o +obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o +obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8m-blk-ctrl.o +obj-$(CONFIG_IMX8M_BLK_CTRL) += imx8mp-blk-ctrl.o +obj-$(CONFIG_SOC_IMX9) += imx93-pd.o +obj-$(CONFIG_IMX9_BLK_CTRL) += imx93-blk-ctrl.o diff --git a/drivers/pmdomain/imx/gpc.c b/drivers/pmdomain/imx/gpc.c new file mode 100644 index 000000000000..90a8b2c0676f --- /dev/null +++ b/drivers/pmdomain/imx/gpc.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2015-2017 Pengutronix, Lucas Stach + * Copyright 2011-2013 Freescale Semiconductor, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPC_CNTR 0x000 + +#define GPC_PGC_CTRL_OFFS 0x0 +#define GPC_PGC_PUPSCR_OFFS 0x4 +#define GPC_PGC_PDNSCR_OFFS 0x8 +#define GPC_PGC_SW2ISO_SHIFT 0x8 +#define GPC_PGC_SW_SHIFT 0x0 + +#define GPC_PGC_PCI_PDN 0x200 +#define GPC_PGC_PCI_SR 0x20c + +#define GPC_PGC_GPU_PDN 0x260 +#define GPC_PGC_GPU_PUPSCR 0x264 +#define GPC_PGC_GPU_PDNSCR 0x268 +#define GPC_PGC_GPU_SR 0x26c + +#define GPC_PGC_DISP_PDN 0x240 +#define GPC_PGC_DISP_SR 0x24c + +#define GPU_VPU_PUP_REQ BIT(1) +#define GPU_VPU_PDN_REQ BIT(0) + +#define GPC_CLK_MAX 7 + +#define PGC_DOMAIN_FLAG_NO_PD BIT(0) + +struct imx_pm_domain { + struct generic_pm_domain base; + struct regmap *regmap; + struct regulator *supply; + struct clk *clk[GPC_CLK_MAX]; + int num_clks; + unsigned int reg_offs; + signed char cntr_pdn_bit; + unsigned int ipg_rate_mhz; +}; + +static inline struct imx_pm_domain * +to_imx_pm_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx_pm_domain, base); +} + +static int imx6_pm_domain_power_off(struct generic_pm_domain *genpd) +{ + struct imx_pm_domain *pd = to_imx_pm_domain(genpd); + int iso, iso2sw; + u32 val; + + /* Read ISO and ISO2SW power down delays */ + regmap_read(pd->regmap, pd->reg_offs + GPC_PGC_PDNSCR_OFFS, &val); + iso = val & 0x3f; + iso2sw = (val >> 8) & 0x3f; + + /* Gate off domain when powered down */ + regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, + 0x1, 0x1); + + /* Request GPC to power down domain */ + val = BIT(pd->cntr_pdn_bit); + regmap_update_bits(pd->regmap, GPC_CNTR, val, val); + + /* Wait ISO + ISO2SW IPG clock cycles */ + udelay(DIV_ROUND_UP(iso + iso2sw, pd->ipg_rate_mhz)); + + if (pd->supply) + regulator_disable(pd->supply); + + return 0; +} + +static int imx6_pm_domain_power_on(struct generic_pm_domain *genpd) +{ + struct imx_pm_domain *pd = to_imx_pm_domain(genpd); + int i, ret; + u32 val, req; + + if (pd->supply) { + ret = regulator_enable(pd->supply); + if (ret) { + pr_err("%s: failed to enable regulator: %d\n", + __func__, ret); + return ret; + } + } + + /* Enable reset clocks for all devices in the domain */ + for (i = 0; i < pd->num_clks; i++) + clk_prepare_enable(pd->clk[i]); + + /* Gate off domain when powered down */ + regmap_update_bits(pd->regmap, pd->reg_offs + GPC_PGC_CTRL_OFFS, + 0x1, 0x1); + + /* Request GPC to power up domain */ + req = BIT(pd->cntr_pdn_bit + 1); + regmap_update_bits(pd->regmap, GPC_CNTR, req, req); + + /* Wait for the PGC to handle the request */ + ret = regmap_read_poll_timeout(pd->regmap, GPC_CNTR, val, !(val & req), + 1, 50); + if (ret) + pr_err("powerup request on domain %s timed out\n", genpd->name); + + /* Wait for reset to propagate through peripherals */ + usleep_range(5, 10); + + /* Disable reset clocks for all devices in the domain */ + for (i = 0; i < pd->num_clks; i++) + clk_disable_unprepare(pd->clk[i]); + + return 0; +} + +static int imx_pgc_get_clocks(struct device *dev, struct imx_pm_domain *domain) +{ + int i, ret; + + for (i = 0; ; i++) { + struct clk *clk = of_clk_get(dev->of_node, i); + if (IS_ERR(clk)) + break; + if (i >= GPC_CLK_MAX) { + dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX); + ret = -EINVAL; + goto clk_err; + } + domain->clk[i] = clk; + } + domain->num_clks = i; + + return 0; + +clk_err: + while (i--) + clk_put(domain->clk[i]); + + return ret; +} + +static void imx_pgc_put_clocks(struct imx_pm_domain *domain) +{ + int i; + + for (i = domain->num_clks - 1; i >= 0; i--) + clk_put(domain->clk[i]); +} + +static int imx_pgc_parse_dt(struct device *dev, struct imx_pm_domain *domain) +{ + /* try to get the domain supply regulator */ + domain->supply = devm_regulator_get_optional(dev, "power"); + if (IS_ERR(domain->supply)) { + if (PTR_ERR(domain->supply) == -ENODEV) + domain->supply = NULL; + else + return PTR_ERR(domain->supply); + } + + /* try to get all clocks needed for reset propagation */ + return imx_pgc_get_clocks(dev, domain); +} + +static int imx_pgc_power_domain_probe(struct platform_device *pdev) +{ + struct imx_pm_domain *domain = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + int ret; + + /* if this PD is associated with a DT node try to parse it */ + if (dev->of_node) { + ret = imx_pgc_parse_dt(dev, domain); + if (ret) + return ret; + } + + /* initially power on the domain */ + if (domain->base.power_on) + domain->base.power_on(&domain->base); + + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { + pm_genpd_init(&domain->base, NULL, false); + ret = of_genpd_add_provider_simple(dev->of_node, &domain->base); + if (ret) + goto genpd_err; + } + + device_link_add(dev, dev->parent, DL_FLAG_AUTOREMOVE_CONSUMER); + + return 0; + +genpd_err: + pm_genpd_remove(&domain->base); + imx_pgc_put_clocks(domain); + + return ret; +} + +static int imx_pgc_power_domain_remove(struct platform_device *pdev) +{ + struct imx_pm_domain *domain = pdev->dev.platform_data; + + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { + of_genpd_del_provider(pdev->dev.of_node); + pm_genpd_remove(&domain->base); + imx_pgc_put_clocks(domain); + } + + return 0; +} + +static const struct platform_device_id imx_pgc_power_domain_id[] = { + { "imx-pgc-power-domain"}, + { }, +}; + +static struct platform_driver imx_pgc_power_domain_driver = { + .driver = { + .name = "imx-pgc-pd", + }, + .probe = imx_pgc_power_domain_probe, + .remove = imx_pgc_power_domain_remove, + .id_table = imx_pgc_power_domain_id, +}; +builtin_platform_driver(imx_pgc_power_domain_driver) + +#define GPC_PGC_DOMAIN_ARM 0 +#define GPC_PGC_DOMAIN_PU 1 +#define GPC_PGC_DOMAIN_DISPLAY 2 +#define GPC_PGC_DOMAIN_PCI 3 + +static struct genpd_power_state imx6_pm_domain_pu_state = { + .power_off_latency_ns = 25000, + .power_on_latency_ns = 2000000, +}; + +static struct imx_pm_domain imx_gpc_domains[] = { + [GPC_PGC_DOMAIN_ARM] = { + .base = { + .name = "ARM", + .flags = GENPD_FLAG_ALWAYS_ON, + }, + }, + [GPC_PGC_DOMAIN_PU] = { + .base = { + .name = "PU", + .power_off = imx6_pm_domain_power_off, + .power_on = imx6_pm_domain_power_on, + .states = &imx6_pm_domain_pu_state, + .state_count = 1, + }, + .reg_offs = 0x260, + .cntr_pdn_bit = 0, + }, + [GPC_PGC_DOMAIN_DISPLAY] = { + .base = { + .name = "DISPLAY", + .power_off = imx6_pm_domain_power_off, + .power_on = imx6_pm_domain_power_on, + }, + .reg_offs = 0x240, + .cntr_pdn_bit = 4, + }, + [GPC_PGC_DOMAIN_PCI] = { + .base = { + .name = "PCI", + .power_off = imx6_pm_domain_power_off, + .power_on = imx6_pm_domain_power_on, + }, + .reg_offs = 0x200, + .cntr_pdn_bit = 6, + }, +}; + +struct imx_gpc_dt_data { + int num_domains; + bool err009619_present; + bool err006287_present; +}; + +static const struct imx_gpc_dt_data imx6q_dt_data = { + .num_domains = 2, + .err009619_present = false, + .err006287_present = false, +}; + +static const struct imx_gpc_dt_data imx6qp_dt_data = { + .num_domains = 2, + .err009619_present = true, + .err006287_present = false, +}; + +static const struct imx_gpc_dt_data imx6sl_dt_data = { + .num_domains = 3, + .err009619_present = false, + .err006287_present = true, +}; + +static const struct imx_gpc_dt_data imx6sx_dt_data = { + .num_domains = 4, + .err009619_present = false, + .err006287_present = false, +}; + +static const struct of_device_id imx_gpc_dt_ids[] = { + { .compatible = "fsl,imx6q-gpc", .data = &imx6q_dt_data }, + { .compatible = "fsl,imx6qp-gpc", .data = &imx6qp_dt_data }, + { .compatible = "fsl,imx6sl-gpc", .data = &imx6sl_dt_data }, + { .compatible = "fsl,imx6sx-gpc", .data = &imx6sx_dt_data }, + { } +}; + +static const struct regmap_range yes_ranges[] = { + regmap_reg_range(GPC_CNTR, GPC_CNTR), + regmap_reg_range(GPC_PGC_PCI_PDN, GPC_PGC_PCI_SR), + regmap_reg_range(GPC_PGC_GPU_PDN, GPC_PGC_GPU_SR), + regmap_reg_range(GPC_PGC_DISP_PDN, GPC_PGC_DISP_SR), +}; + +static const struct regmap_access_table access_table = { + .yes_ranges = yes_ranges, + .n_yes_ranges = ARRAY_SIZE(yes_ranges), +}; + +static const struct regmap_config imx_gpc_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .rd_table = &access_table, + .wr_table = &access_table, + .max_register = 0x2ac, + .fast_io = true, +}; + +static struct generic_pm_domain *imx_gpc_onecell_domains[] = { + &imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base, + &imx_gpc_domains[GPC_PGC_DOMAIN_PU].base, +}; + +static struct genpd_onecell_data imx_gpc_onecell_data = { + .domains = imx_gpc_onecell_domains, + .num_domains = 2, +}; + +static int imx_gpc_old_dt_init(struct device *dev, struct regmap *regmap, + unsigned int num_domains) +{ + struct imx_pm_domain *domain; + int i, ret; + + for (i = 0; i < num_domains; i++) { + domain = &imx_gpc_domains[i]; + domain->regmap = regmap; + domain->ipg_rate_mhz = 66; + + if (i == 1) { + domain->supply = devm_regulator_get(dev, "pu"); + if (IS_ERR(domain->supply)) + return PTR_ERR(domain->supply); + + ret = imx_pgc_get_clocks(dev, domain); + if (ret) + goto clk_err; + + domain->base.power_on(&domain->base); + } + } + + for (i = 0; i < num_domains; i++) + pm_genpd_init(&imx_gpc_domains[i].base, NULL, false); + + if (IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS)) { + ret = of_genpd_add_provider_onecell(dev->of_node, + &imx_gpc_onecell_data); + if (ret) + goto genpd_err; + } + + return 0; + +genpd_err: + for (i = 0; i < num_domains; i++) + pm_genpd_remove(&imx_gpc_domains[i].base); + imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); +clk_err: + return ret; +} + +static int imx_gpc_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id = + of_match_device(imx_gpc_dt_ids, &pdev->dev); + const struct imx_gpc_dt_data *of_id_data = of_id->data; + struct device_node *pgc_node; + struct regmap *regmap; + void __iomem *base; + int ret; + + pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); + + /* bail out if DT too old and doesn't provide the necessary info */ + if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + !pgc_node) + return 0; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio_clk(&pdev->dev, NULL, base, + &imx_gpc_regmap_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&pdev->dev, "failed to init regmap: %d\n", + ret); + return ret; + } + + /* + * Disable PU power down by runtime PM if ERR009619 is present. + * + * The PRE clock will be paused for several cycles when turning on the + * PU domain LDO from power down state. If PRE is in use at that time, + * the IPU/PRG cannot get the correct display data from the PRE. + * + * This is not a concern when the whole system enters suspend state, so + * it's safe to power down PU in this case. + */ + if (of_id_data->err009619_present) + imx_gpc_domains[GPC_PGC_DOMAIN_PU].base.flags |= + GENPD_FLAG_RPM_ALWAYS_ON; + + /* Keep DISP always on if ERR006287 is present */ + if (of_id_data->err006287_present) + imx_gpc_domains[GPC_PGC_DOMAIN_DISPLAY].base.flags |= + GENPD_FLAG_ALWAYS_ON; + + if (!pgc_node) { + ret = imx_gpc_old_dt_init(&pdev->dev, regmap, + of_id_data->num_domains); + if (ret) + return ret; + } else { + struct imx_pm_domain *domain; + struct platform_device *pd_pdev; + struct device_node *np; + struct clk *ipg_clk; + unsigned int ipg_rate_mhz; + int domain_index; + + ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(ipg_clk)) + return PTR_ERR(ipg_clk); + ipg_rate_mhz = clk_get_rate(ipg_clk) / 1000000; + + for_each_child_of_node(pgc_node, np) { + ret = of_property_read_u32(np, "reg", &domain_index); + if (ret) { + of_node_put(np); + return ret; + } + if (domain_index >= of_id_data->num_domains) + continue; + + pd_pdev = platform_device_alloc("imx-pgc-power-domain", + domain_index); + if (!pd_pdev) { + of_node_put(np); + return -ENOMEM; + } + + ret = platform_device_add_data(pd_pdev, + &imx_gpc_domains[domain_index], + sizeof(imx_gpc_domains[domain_index])); + if (ret) { + platform_device_put(pd_pdev); + of_node_put(np); + return ret; + } + domain = pd_pdev->dev.platform_data; + domain->regmap = regmap; + domain->ipg_rate_mhz = ipg_rate_mhz; + + pd_pdev->dev.parent = &pdev->dev; + pd_pdev->dev.of_node = np; + + ret = platform_device_add(pd_pdev); + if (ret) { + platform_device_put(pd_pdev); + of_node_put(np); + return ret; + } + } + } + + return 0; +} + +static int imx_gpc_remove(struct platform_device *pdev) +{ + struct device_node *pgc_node; + int ret; + + pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc"); + + /* bail out if DT too old and doesn't provide the necessary info */ + if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells") && + !pgc_node) + return 0; + + /* + * If the old DT binding is used the toplevel driver needs to + * de-register the power domains + */ + if (!pgc_node) { + of_genpd_del_provider(pdev->dev.of_node); + + ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base); + if (ret) + return ret; + imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU]); + + ret = pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base); + if (ret) + return ret; + } + + return 0; +} + +static struct platform_driver imx_gpc_driver = { + .driver = { + .name = "imx-gpc", + .of_match_table = imx_gpc_dt_ids, + }, + .probe = imx_gpc_probe, + .remove = imx_gpc_remove, +}; +builtin_platform_driver(imx_gpc_driver) diff --git a/drivers/pmdomain/imx/gpcv2.c b/drivers/pmdomain/imx/gpcv2.c new file mode 100644 index 000000000000..fbd3d92f8cd8 --- /dev/null +++ b/drivers/pmdomain/imx/gpcv2.c @@ -0,0 +1,1550 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2017 Impinj, Inc + * Author: Andrey Smirnov + * + * Based on the code of analogus driver: + * + * Copyright 2015-2017 Pengutronix, Lucas Stach + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define GPC_LPCR_A_CORE_BSC 0x000 + +#define GPC_PGC_CPU_MAPPING 0x0ec +#define IMX8MP_GPC_PGC_CPU_MAPPING 0x1cc + +#define IMX7_USB_HSIC_PHY_A_CORE_DOMAIN BIT(6) +#define IMX7_USB_OTG2_PHY_A_CORE_DOMAIN BIT(5) +#define IMX7_USB_OTG1_PHY_A_CORE_DOMAIN BIT(4) +#define IMX7_PCIE_PHY_A_CORE_DOMAIN BIT(3) +#define IMX7_MIPI_PHY_A_CORE_DOMAIN BIT(2) + +#define IMX8M_PCIE2_A53_DOMAIN BIT(15) +#define IMX8M_MIPI_CSI2_A53_DOMAIN BIT(14) +#define IMX8M_MIPI_CSI1_A53_DOMAIN BIT(13) +#define IMX8M_DISP_A53_DOMAIN BIT(12) +#define IMX8M_HDMI_A53_DOMAIN BIT(11) +#define IMX8M_VPU_A53_DOMAIN BIT(10) +#define IMX8M_GPU_A53_DOMAIN BIT(9) +#define IMX8M_DDR2_A53_DOMAIN BIT(8) +#define IMX8M_DDR1_A53_DOMAIN BIT(7) +#define IMX8M_OTG2_A53_DOMAIN BIT(5) +#define IMX8M_OTG1_A53_DOMAIN BIT(4) +#define IMX8M_PCIE1_A53_DOMAIN BIT(3) +#define IMX8M_MIPI_A53_DOMAIN BIT(2) + +#define IMX8MM_VPUH1_A53_DOMAIN BIT(15) +#define IMX8MM_VPUG2_A53_DOMAIN BIT(14) +#define IMX8MM_VPUG1_A53_DOMAIN BIT(13) +#define IMX8MM_DISPMIX_A53_DOMAIN BIT(12) +#define IMX8MM_VPUMIX_A53_DOMAIN BIT(10) +#define IMX8MM_GPUMIX_A53_DOMAIN BIT(9) +#define IMX8MM_GPU_A53_DOMAIN (BIT(8) | BIT(11)) +#define IMX8MM_DDR1_A53_DOMAIN BIT(7) +#define IMX8MM_OTG2_A53_DOMAIN BIT(5) +#define IMX8MM_OTG1_A53_DOMAIN BIT(4) +#define IMX8MM_PCIE_A53_DOMAIN BIT(3) +#define IMX8MM_MIPI_A53_DOMAIN BIT(2) + +#define IMX8MN_DISPMIX_A53_DOMAIN BIT(12) +#define IMX8MN_GPUMIX_A53_DOMAIN BIT(9) +#define IMX8MN_DDR1_A53_DOMAIN BIT(7) +#define IMX8MN_OTG1_A53_DOMAIN BIT(4) +#define IMX8MN_MIPI_A53_DOMAIN BIT(2) + +#define IMX8MP_MEDIA_ISPDWP_A53_DOMAIN BIT(20) +#define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19) +#define IMX8MP_MIPI_PHY2_A53_DOMAIN BIT(18) +#define IMX8MP_HDMI_PHY_A53_DOMAIN BIT(17) +#define IMX8MP_HDMIMIX_A53_DOMAIN BIT(16) +#define IMX8MP_VPU_VC8000E_A53_DOMAIN BIT(15) +#define IMX8MP_VPU_G2_A53_DOMAIN BIT(14) +#define IMX8MP_VPU_G1_A53_DOMAIN BIT(13) +#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12) +#define IMX8MP_GPU3D_A53_DOMAIN BIT(11) +#define IMX8MP_VPUMIX_A53_DOMAIN BIT(10) +#define IMX8MP_GPUMIX_A53_DOMAIN BIT(9) +#define IMX8MP_GPU2D_A53_DOMAIN BIT(8) +#define IMX8MP_AUDIOMIX_A53_DOMAIN BIT(7) +#define IMX8MP_MLMIX_A53_DOMAIN BIT(6) +#define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5) +#define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4) +#define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3) +#define IMX8MP_MIPI_PHY1_A53_DOMAIN BIT(2) + +#define IMX8MP_GPC_PU_PGC_SW_PUP_REQ 0x0d8 +#define IMX8MP_GPC_PU_PGC_SW_PDN_REQ 0x0e4 + +#define GPC_PU_PGC_SW_PUP_REQ 0x0f8 +#define GPC_PU_PGC_SW_PDN_REQ 0x104 + +#define IMX7_USB_HSIC_PHY_SW_Pxx_REQ BIT(4) +#define IMX7_USB_OTG2_PHY_SW_Pxx_REQ BIT(3) +#define IMX7_USB_OTG1_PHY_SW_Pxx_REQ BIT(2) +#define IMX7_PCIE_PHY_SW_Pxx_REQ BIT(1) +#define IMX7_MIPI_PHY_SW_Pxx_REQ BIT(0) + +#define IMX8M_PCIE2_SW_Pxx_REQ BIT(13) +#define IMX8M_MIPI_CSI2_SW_Pxx_REQ BIT(12) +#define IMX8M_MIPI_CSI1_SW_Pxx_REQ BIT(11) +#define IMX8M_DISP_SW_Pxx_REQ BIT(10) +#define IMX8M_HDMI_SW_Pxx_REQ BIT(9) +#define IMX8M_VPU_SW_Pxx_REQ BIT(8) +#define IMX8M_GPU_SW_Pxx_REQ BIT(7) +#define IMX8M_DDR2_SW_Pxx_REQ BIT(6) +#define IMX8M_DDR1_SW_Pxx_REQ BIT(5) +#define IMX8M_OTG2_SW_Pxx_REQ BIT(3) +#define IMX8M_OTG1_SW_Pxx_REQ BIT(2) +#define IMX8M_PCIE1_SW_Pxx_REQ BIT(1) +#define IMX8M_MIPI_SW_Pxx_REQ BIT(0) + +#define IMX8MM_VPUH1_SW_Pxx_REQ BIT(13) +#define IMX8MM_VPUG2_SW_Pxx_REQ BIT(12) +#define IMX8MM_VPUG1_SW_Pxx_REQ BIT(11) +#define IMX8MM_DISPMIX_SW_Pxx_REQ BIT(10) +#define IMX8MM_VPUMIX_SW_Pxx_REQ BIT(8) +#define IMX8MM_GPUMIX_SW_Pxx_REQ BIT(7) +#define IMX8MM_GPU_SW_Pxx_REQ (BIT(6) | BIT(9)) +#define IMX8MM_DDR1_SW_Pxx_REQ BIT(5) +#define IMX8MM_OTG2_SW_Pxx_REQ BIT(3) +#define IMX8MM_OTG1_SW_Pxx_REQ BIT(2) +#define IMX8MM_PCIE_SW_Pxx_REQ BIT(1) +#define IMX8MM_MIPI_SW_Pxx_REQ BIT(0) + +#define IMX8MN_DISPMIX_SW_Pxx_REQ BIT(10) +#define IMX8MN_GPUMIX_SW_Pxx_REQ BIT(7) +#define IMX8MN_DDR1_SW_Pxx_REQ BIT(5) +#define IMX8MN_OTG1_SW_Pxx_REQ BIT(2) +#define IMX8MN_MIPI_SW_Pxx_REQ BIT(0) + +#define IMX8MP_DDRMIX_Pxx_REQ BIT(19) +#define IMX8MP_MEDIA_ISP_DWP_Pxx_REQ BIT(18) +#define IMX8MP_HSIOMIX_Pxx_REQ BIT(17) +#define IMX8MP_MIPI_PHY2_Pxx_REQ BIT(16) +#define IMX8MP_HDMI_PHY_Pxx_REQ BIT(15) +#define IMX8MP_HDMIMIX_Pxx_REQ BIT(14) +#define IMX8MP_VPU_VC8K_Pxx_REQ BIT(13) +#define IMX8MP_VPU_G2_Pxx_REQ BIT(12) +#define IMX8MP_VPU_G1_Pxx_REQ BIT(11) +#define IMX8MP_MEDIMIX_Pxx_REQ BIT(10) +#define IMX8MP_GPU_3D_Pxx_REQ BIT(9) +#define IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ BIT(8) +#define IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ BIT(7) +#define IMX8MP_GPU_2D_Pxx_REQ BIT(6) +#define IMX8MP_AUDIOMIX_Pxx_REQ BIT(5) +#define IMX8MP_MLMIX_Pxx_REQ BIT(4) +#define IMX8MP_USB2_PHY_Pxx_REQ BIT(3) +#define IMX8MP_USB1_PHY_Pxx_REQ BIT(2) +#define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1) +#define IMX8MP_MIPI_PHY1_SW_Pxx_REQ BIT(0) + +#define GPC_M4_PU_PDN_FLG 0x1bc + +#define IMX8MP_GPC_PU_PWRHSK 0x190 +#define GPC_PU_PWRHSK 0x1fc + +#define IMX8M_GPU_HSK_PWRDNACKN BIT(26) +#define IMX8M_VPU_HSK_PWRDNACKN BIT(25) +#define IMX8M_DISP_HSK_PWRDNACKN BIT(24) +#define IMX8M_GPU_HSK_PWRDNREQN BIT(6) +#define IMX8M_VPU_HSK_PWRDNREQN BIT(5) +#define IMX8M_DISP_HSK_PWRDNREQN BIT(4) + +#define IMX8MM_GPUMIX_HSK_PWRDNACKN BIT(29) +#define IMX8MM_GPU_HSK_PWRDNACKN (BIT(27) | BIT(28)) +#define IMX8MM_VPUMIX_HSK_PWRDNACKN BIT(26) +#define IMX8MM_DISPMIX_HSK_PWRDNACKN BIT(25) +#define IMX8MM_HSIO_HSK_PWRDNACKN (BIT(23) | BIT(24)) +#define IMX8MM_GPUMIX_HSK_PWRDNREQN BIT(11) +#define IMX8MM_GPU_HSK_PWRDNREQN (BIT(9) | BIT(10)) +#define IMX8MM_VPUMIX_HSK_PWRDNREQN BIT(8) +#define IMX8MM_DISPMIX_HSK_PWRDNREQN BIT(7) +#define IMX8MM_HSIO_HSK_PWRDNREQN (BIT(5) | BIT(6)) + +#define IMX8MN_GPUMIX_HSK_PWRDNACKN (BIT(29) | BIT(27)) +#define IMX8MN_DISPMIX_HSK_PWRDNACKN BIT(25) +#define IMX8MN_HSIO_HSK_PWRDNACKN BIT(23) +#define IMX8MN_GPUMIX_HSK_PWRDNREQN (BIT(11) | BIT(9)) +#define IMX8MN_DISPMIX_HSK_PWRDNREQN BIT(7) +#define IMX8MN_HSIO_HSK_PWRDNREQN BIT(5) + +#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30) +#define IMX8MP_HDMIMIX_PWRDNACKN BIT(29) +#define IMX8MP_HSIOMIX_PWRDNACKN BIT(28) +#define IMX8MP_VPUMIX_PWRDNACKN BIT(26) +#define IMX8MP_GPUMIX_PWRDNACKN BIT(25) +#define IMX8MP_MLMIX_PWRDNACKN (BIT(23) | BIT(24)) +#define IMX8MP_AUDIOMIX_PWRDNACKN (BIT(20) | BIT(31)) +#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14) +#define IMX8MP_HDMIMIX_PWRDNREQN BIT(13) +#define IMX8MP_HSIOMIX_PWRDNREQN BIT(12) +#define IMX8MP_VPUMIX_PWRDNREQN BIT(10) +#define IMX8MP_GPUMIX_PWRDNREQN BIT(9) +#define IMX8MP_MLMIX_PWRDNREQN (BIT(7) | BIT(8)) +#define IMX8MP_AUDIOMIX_PWRDNREQN (BIT(4) | BIT(15)) + +/* + * The PGC offset values in Reference Manual + * (Rev. 1, 01/2018 and the older ones) GPC chapter's + * GPC_PGC memory map are incorrect, below offset + * values are from design RTL. + */ +#define IMX7_PGC_MIPI 16 +#define IMX7_PGC_PCIE 17 +#define IMX7_PGC_USB_HSIC 20 + +#define IMX8M_PGC_MIPI 16 +#define IMX8M_PGC_PCIE1 17 +#define IMX8M_PGC_OTG1 18 +#define IMX8M_PGC_OTG2 19 +#define IMX8M_PGC_DDR1 21 +#define IMX8M_PGC_GPU 23 +#define IMX8M_PGC_VPU 24 +#define IMX8M_PGC_DISP 26 +#define IMX8M_PGC_MIPI_CSI1 27 +#define IMX8M_PGC_MIPI_CSI2 28 +#define IMX8M_PGC_PCIE2 29 + +#define IMX8MM_PGC_MIPI 16 +#define IMX8MM_PGC_PCIE 17 +#define IMX8MM_PGC_OTG1 18 +#define IMX8MM_PGC_OTG2 19 +#define IMX8MM_PGC_DDR1 21 +#define IMX8MM_PGC_GPU2D 22 +#define IMX8MM_PGC_GPUMIX 23 +#define IMX8MM_PGC_VPUMIX 24 +#define IMX8MM_PGC_GPU3D 25 +#define IMX8MM_PGC_DISPMIX 26 +#define IMX8MM_PGC_VPUG1 27 +#define IMX8MM_PGC_VPUG2 28 +#define IMX8MM_PGC_VPUH1 29 + +#define IMX8MN_PGC_MIPI 16 +#define IMX8MN_PGC_OTG1 18 +#define IMX8MN_PGC_DDR1 21 +#define IMX8MN_PGC_GPUMIX 23 +#define IMX8MN_PGC_DISPMIX 26 + +#define IMX8MP_PGC_NOC 9 +#define IMX8MP_PGC_MIPI1 12 +#define IMX8MP_PGC_PCIE 13 +#define IMX8MP_PGC_USB1 14 +#define IMX8MP_PGC_USB2 15 +#define IMX8MP_PGC_MLMIX 16 +#define IMX8MP_PGC_AUDIOMIX 17 +#define IMX8MP_PGC_GPU2D 18 +#define IMX8MP_PGC_GPUMIX 19 +#define IMX8MP_PGC_VPUMIX 20 +#define IMX8MP_PGC_GPU3D 21 +#define IMX8MP_PGC_MEDIAMIX 22 +#define IMX8MP_PGC_VPU_G1 23 +#define IMX8MP_PGC_VPU_G2 24 +#define IMX8MP_PGC_VPU_VC8000E 25 +#define IMX8MP_PGC_HDMIMIX 26 +#define IMX8MP_PGC_HDMI 27 +#define IMX8MP_PGC_MIPI2 28 +#define IMX8MP_PGC_HSIOMIX 29 +#define IMX8MP_PGC_MEDIA_ISP_DWP 30 +#define IMX8MP_PGC_DDRMIX 31 + +#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40) +#define GPC_PGC_SR(n) (GPC_PGC_CTRL(n) + 0xc) + +#define GPC_PGC_CTRL_PCR BIT(0) + +struct imx_pgc_regs { + u16 map; + u16 pup; + u16 pdn; + u16 hsk; +}; + +struct imx_pgc_domain { + struct generic_pm_domain genpd; + struct regmap *regmap; + const struct imx_pgc_regs *regs; + struct regulator *regulator; + struct reset_control *reset; + struct clk_bulk_data *clks; + int num_clks; + + unsigned long pgc; + + const struct { + u32 pxx; + u32 map; + u32 hskreq; + u32 hskack; + } bits; + + const int voltage; + const bool keep_clocks; + struct device *dev; + + unsigned int pgc_sw_pup_reg; + unsigned int pgc_sw_pdn_reg; +}; + +struct imx_pgc_domain_data { + const struct imx_pgc_domain *domains; + size_t domains_num; + const struct regmap_access_table *reg_access_table; + const struct imx_pgc_regs *pgc_regs; +}; + +static inline struct imx_pgc_domain * +to_imx_pgc_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx_pgc_domain, genpd); +} + +static int imx_pgc_power_up(struct generic_pm_domain *genpd) +{ + struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd); + u32 reg_val, pgc; + int ret; + + ret = pm_runtime_get_sync(domain->dev); + if (ret < 0) { + pm_runtime_put_noidle(domain->dev); + return ret; + } + + if (!IS_ERR(domain->regulator)) { + ret = regulator_enable(domain->regulator); + if (ret) { + dev_err(domain->dev, + "failed to enable regulator: %pe\n", + ERR_PTR(ret)); + goto out_put_pm; + } + } + + reset_control_assert(domain->reset); + + /* Enable reset clocks for all devices in the domain */ + ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); + if (ret) { + dev_err(domain->dev, "failed to enable reset clocks\n"); + goto out_regulator_disable; + } + + /* delays for reset to propagate */ + udelay(5); + + if (domain->bits.pxx) { + /* request the domain to power up */ + regmap_update_bits(domain->regmap, domain->regs->pup, + domain->bits.pxx, domain->bits.pxx); + /* + * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait + * for PUP_REQ/PDN_REQ bit to be cleared + */ + ret = regmap_read_poll_timeout(domain->regmap, + domain->regs->pup, reg_val, + !(reg_val & domain->bits.pxx), + 0, USEC_PER_MSEC); + if (ret) { + dev_err(domain->dev, "failed to command PGC\n"); + goto out_clk_disable; + } + + /* disable power control */ + for_each_set_bit(pgc, &domain->pgc, 32) { + regmap_clear_bits(domain->regmap, GPC_PGC_CTRL(pgc), + GPC_PGC_CTRL_PCR); + } + } + + /* delay for reset to propagate */ + udelay(5); + + reset_control_deassert(domain->reset); + + /* request the ADB400 to power up */ + if (domain->bits.hskreq) { + regmap_update_bits(domain->regmap, domain->regs->hsk, + domain->bits.hskreq, domain->bits.hskreq); + + /* + * ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, reg_val, + * (reg_val & domain->bits.hskack), 0, + * USEC_PER_MSEC); + * Technically we need the commented code to wait handshake. But that needs + * the BLK-CTL module BUS clk-en bit being set. + * + * There is a separate BLK-CTL module and we will have such a driver for it, + * that driver will set the BUS clk-en bit and handshake will be triggered + * automatically there. Just add a delay and suppose the handshake finish + * after that. + */ + } + + /* Disable reset clocks for all devices in the domain */ + if (!domain->keep_clocks) + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + return 0; + +out_clk_disable: + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); +out_regulator_disable: + if (!IS_ERR(domain->regulator)) + regulator_disable(domain->regulator); +out_put_pm: + pm_runtime_put(domain->dev); + + return ret; +} + +static int imx_pgc_power_down(struct generic_pm_domain *genpd) +{ + struct imx_pgc_domain *domain = to_imx_pgc_domain(genpd); + u32 reg_val, pgc; + int ret; + + /* Enable reset clocks for all devices in the domain */ + if (!domain->keep_clocks) { + ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); + if (ret) { + dev_err(domain->dev, "failed to enable reset clocks\n"); + return ret; + } + } + + /* request the ADB400 to power down */ + if (domain->bits.hskreq) { + regmap_clear_bits(domain->regmap, domain->regs->hsk, + domain->bits.hskreq); + + ret = regmap_read_poll_timeout(domain->regmap, domain->regs->hsk, + reg_val, + !(reg_val & domain->bits.hskack), + 0, USEC_PER_MSEC); + if (ret) { + dev_err(domain->dev, "failed to power down ADB400\n"); + goto out_clk_disable; + } + } + + if (domain->bits.pxx) { + /* enable power control */ + for_each_set_bit(pgc, &domain->pgc, 32) { + regmap_update_bits(domain->regmap, GPC_PGC_CTRL(pgc), + GPC_PGC_CTRL_PCR, GPC_PGC_CTRL_PCR); + } + + /* request the domain to power down */ + regmap_update_bits(domain->regmap, domain->regs->pdn, + domain->bits.pxx, domain->bits.pxx); + /* + * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait + * for PUP_REQ/PDN_REQ bit to be cleared + */ + ret = regmap_read_poll_timeout(domain->regmap, + domain->regs->pdn, reg_val, + !(reg_val & domain->bits.pxx), + 0, USEC_PER_MSEC); + if (ret) { + dev_err(domain->dev, "failed to command PGC\n"); + goto out_clk_disable; + } + } + + /* Disable reset clocks for all devices in the domain */ + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + if (!IS_ERR(domain->regulator)) { + ret = regulator_disable(domain->regulator); + if (ret) { + dev_err(domain->dev, + "failed to disable regulator: %pe\n", + ERR_PTR(ret)); + return ret; + } + } + + pm_runtime_put_sync_suspend(domain->dev); + + return 0; + +out_clk_disable: + if (!domain->keep_clocks) + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + return ret; +} + +static const struct imx_pgc_domain imx7_pgc_domains[] = { + [IMX7_POWER_DOMAIN_MIPI_PHY] = { + .genpd = { + .name = "mipi-phy", + }, + .bits = { + .pxx = IMX7_MIPI_PHY_SW_Pxx_REQ, + .map = IMX7_MIPI_PHY_A_CORE_DOMAIN, + }, + .voltage = 1000000, + .pgc = BIT(IMX7_PGC_MIPI), + }, + + [IMX7_POWER_DOMAIN_PCIE_PHY] = { + .genpd = { + .name = "pcie-phy", + }, + .bits = { + .pxx = IMX7_PCIE_PHY_SW_Pxx_REQ, + .map = IMX7_PCIE_PHY_A_CORE_DOMAIN, + }, + .voltage = 1000000, + .pgc = BIT(IMX7_PGC_PCIE), + }, + + [IMX7_POWER_DOMAIN_USB_HSIC_PHY] = { + .genpd = { + .name = "usb-hsic-phy", + }, + .bits = { + .pxx = IMX7_USB_HSIC_PHY_SW_Pxx_REQ, + .map = IMX7_USB_HSIC_PHY_A_CORE_DOMAIN, + }, + .voltage = 1200000, + .pgc = BIT(IMX7_PGC_USB_HSIC), + }, +}; + +static const struct regmap_range imx7_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + GPC_M4_PU_PDN_FLG), + regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_MIPI), + GPC_PGC_SR(IMX7_PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_PCIE), + GPC_PGC_SR(IMX7_PGC_PCIE)), + regmap_reg_range(GPC_PGC_CTRL(IMX7_PGC_USB_HSIC), + GPC_PGC_SR(IMX7_PGC_USB_HSIC)), +}; + +static const struct regmap_access_table imx7_access_table = { + .yes_ranges = imx7_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx7_yes_ranges), +}; + +static const struct imx_pgc_regs imx7_pgc_regs = { + .map = GPC_PGC_CPU_MAPPING, + .pup = GPC_PU_PGC_SW_PUP_REQ, + .pdn = GPC_PU_PGC_SW_PDN_REQ, + .hsk = GPC_PU_PWRHSK, +}; + +static const struct imx_pgc_domain_data imx7_pgc_domain_data = { + .domains = imx7_pgc_domains, + .domains_num = ARRAY_SIZE(imx7_pgc_domains), + .reg_access_table = &imx7_access_table, + .pgc_regs = &imx7_pgc_regs, +}; + +static const struct imx_pgc_domain imx8m_pgc_domains[] = { + [IMX8M_POWER_DOMAIN_MIPI] = { + .genpd = { + .name = "mipi", + }, + .bits = { + .pxx = IMX8M_MIPI_SW_Pxx_REQ, + .map = IMX8M_MIPI_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_MIPI), + }, + + [IMX8M_POWER_DOMAIN_PCIE1] = { + .genpd = { + .name = "pcie1", + }, + .bits = { + .pxx = IMX8M_PCIE1_SW_Pxx_REQ, + .map = IMX8M_PCIE1_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_PCIE1), + }, + + [IMX8M_POWER_DOMAIN_USB_OTG1] = { + .genpd = { + .name = "usb-otg1", + }, + .bits = { + .pxx = IMX8M_OTG1_SW_Pxx_REQ, + .map = IMX8M_OTG1_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_OTG1), + }, + + [IMX8M_POWER_DOMAIN_USB_OTG2] = { + .genpd = { + .name = "usb-otg2", + }, + .bits = { + .pxx = IMX8M_OTG2_SW_Pxx_REQ, + .map = IMX8M_OTG2_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_OTG2), + }, + + [IMX8M_POWER_DOMAIN_DDR1] = { + .genpd = { + .name = "ddr1", + }, + .bits = { + .pxx = IMX8M_DDR1_SW_Pxx_REQ, + .map = IMX8M_DDR2_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_DDR1), + }, + + [IMX8M_POWER_DOMAIN_GPU] = { + .genpd = { + .name = "gpu", + }, + .bits = { + .pxx = IMX8M_GPU_SW_Pxx_REQ, + .map = IMX8M_GPU_A53_DOMAIN, + .hskreq = IMX8M_GPU_HSK_PWRDNREQN, + .hskack = IMX8M_GPU_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8M_PGC_GPU), + }, + + [IMX8M_POWER_DOMAIN_VPU] = { + .genpd = { + .name = "vpu", + }, + .bits = { + .pxx = IMX8M_VPU_SW_Pxx_REQ, + .map = IMX8M_VPU_A53_DOMAIN, + .hskreq = IMX8M_VPU_HSK_PWRDNREQN, + .hskack = IMX8M_VPU_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8M_PGC_VPU), + .keep_clocks = true, + }, + + [IMX8M_POWER_DOMAIN_DISP] = { + .genpd = { + .name = "disp", + }, + .bits = { + .pxx = IMX8M_DISP_SW_Pxx_REQ, + .map = IMX8M_DISP_A53_DOMAIN, + .hskreq = IMX8M_DISP_HSK_PWRDNREQN, + .hskack = IMX8M_DISP_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8M_PGC_DISP), + }, + + [IMX8M_POWER_DOMAIN_MIPI_CSI1] = { + .genpd = { + .name = "mipi-csi1", + }, + .bits = { + .pxx = IMX8M_MIPI_CSI1_SW_Pxx_REQ, + .map = IMX8M_MIPI_CSI1_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_MIPI_CSI1), + }, + + [IMX8M_POWER_DOMAIN_MIPI_CSI2] = { + .genpd = { + .name = "mipi-csi2", + }, + .bits = { + .pxx = IMX8M_MIPI_CSI2_SW_Pxx_REQ, + .map = IMX8M_MIPI_CSI2_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_MIPI_CSI2), + }, + + [IMX8M_POWER_DOMAIN_PCIE2] = { + .genpd = { + .name = "pcie2", + }, + .bits = { + .pxx = IMX8M_PCIE2_SW_Pxx_REQ, + .map = IMX8M_PCIE2_A53_DOMAIN, + }, + .pgc = BIT(IMX8M_PGC_PCIE2), + }, +}; + +static const struct regmap_range imx8m_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + GPC_PU_PWRHSK), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI), + GPC_PGC_SR(IMX8M_PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE1), + GPC_PGC_SR(IMX8M_PGC_PCIE1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG1), + GPC_PGC_SR(IMX8M_PGC_OTG1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_OTG2), + GPC_PGC_SR(IMX8M_PGC_OTG2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DDR1), + GPC_PGC_SR(IMX8M_PGC_DDR1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_GPU), + GPC_PGC_SR(IMX8M_PGC_GPU)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_VPU), + GPC_PGC_SR(IMX8M_PGC_VPU)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_DISP), + GPC_PGC_SR(IMX8M_PGC_DISP)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI1), + GPC_PGC_SR(IMX8M_PGC_MIPI_CSI1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_MIPI_CSI2), + GPC_PGC_SR(IMX8M_PGC_MIPI_CSI2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8M_PGC_PCIE2), + GPC_PGC_SR(IMX8M_PGC_PCIE2)), +}; + +static const struct regmap_access_table imx8m_access_table = { + .yes_ranges = imx8m_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx8m_yes_ranges), +}; + +static const struct imx_pgc_domain_data imx8m_pgc_domain_data = { + .domains = imx8m_pgc_domains, + .domains_num = ARRAY_SIZE(imx8m_pgc_domains), + .reg_access_table = &imx8m_access_table, + .pgc_regs = &imx7_pgc_regs, +}; + +static const struct imx_pgc_domain imx8mm_pgc_domains[] = { + [IMX8MM_POWER_DOMAIN_HSIOMIX] = { + .genpd = { + .name = "hsiomix", + }, + .bits = { + .pxx = 0, /* no power sequence control */ + .map = 0, /* no power sequence control */ + .hskreq = IMX8MM_HSIO_HSK_PWRDNREQN, + .hskack = IMX8MM_HSIO_HSK_PWRDNACKN, + }, + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_PCIE] = { + .genpd = { + .name = "pcie", + }, + .bits = { + .pxx = IMX8MM_PCIE_SW_Pxx_REQ, + .map = IMX8MM_PCIE_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_PCIE), + }, + + [IMX8MM_POWER_DOMAIN_OTG1] = { + .genpd = { + .name = "usb-otg1", + .flags = GENPD_FLAG_ACTIVE_WAKEUP, + }, + .bits = { + .pxx = IMX8MM_OTG1_SW_Pxx_REQ, + .map = IMX8MM_OTG1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_OTG1), + }, + + [IMX8MM_POWER_DOMAIN_OTG2] = { + .genpd = { + .name = "usb-otg2", + .flags = GENPD_FLAG_ACTIVE_WAKEUP, + }, + .bits = { + .pxx = IMX8MM_OTG2_SW_Pxx_REQ, + .map = IMX8MM_OTG2_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_OTG2), + }, + + [IMX8MM_POWER_DOMAIN_GPUMIX] = { + .genpd = { + .name = "gpumix", + }, + .bits = { + .pxx = IMX8MM_GPUMIX_SW_Pxx_REQ, + .map = IMX8MM_GPUMIX_A53_DOMAIN, + .hskreq = IMX8MM_GPUMIX_HSK_PWRDNREQN, + .hskack = IMX8MM_GPUMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_GPUMIX), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_GPU] = { + .genpd = { + .name = "gpu", + }, + .bits = { + .pxx = IMX8MM_GPU_SW_Pxx_REQ, + .map = IMX8MM_GPU_A53_DOMAIN, + .hskreq = IMX8MM_GPU_HSK_PWRDNREQN, + .hskack = IMX8MM_GPU_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_GPU2D) | BIT(IMX8MM_PGC_GPU3D), + }, + + [IMX8MM_POWER_DOMAIN_VPUMIX] = { + .genpd = { + .name = "vpumix", + }, + .bits = { + .pxx = IMX8MM_VPUMIX_SW_Pxx_REQ, + .map = IMX8MM_VPUMIX_A53_DOMAIN, + .hskreq = IMX8MM_VPUMIX_HSK_PWRDNREQN, + .hskack = IMX8MM_VPUMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_VPUMIX), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_VPUG1] = { + .genpd = { + .name = "vpu-g1", + }, + .bits = { + .pxx = IMX8MM_VPUG1_SW_Pxx_REQ, + .map = IMX8MM_VPUG1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_VPUG1), + }, + + [IMX8MM_POWER_DOMAIN_VPUG2] = { + .genpd = { + .name = "vpu-g2", + }, + .bits = { + .pxx = IMX8MM_VPUG2_SW_Pxx_REQ, + .map = IMX8MM_VPUG2_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_VPUG2), + }, + + [IMX8MM_POWER_DOMAIN_VPUH1] = { + .genpd = { + .name = "vpu-h1", + }, + .bits = { + .pxx = IMX8MM_VPUH1_SW_Pxx_REQ, + .map = IMX8MM_VPUH1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_VPUH1), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_DISPMIX] = { + .genpd = { + .name = "dispmix", + }, + .bits = { + .pxx = IMX8MM_DISPMIX_SW_Pxx_REQ, + .map = IMX8MM_DISPMIX_A53_DOMAIN, + .hskreq = IMX8MM_DISPMIX_HSK_PWRDNREQN, + .hskack = IMX8MM_DISPMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MM_PGC_DISPMIX), + .keep_clocks = true, + }, + + [IMX8MM_POWER_DOMAIN_MIPI] = { + .genpd = { + .name = "mipi", + }, + .bits = { + .pxx = IMX8MM_MIPI_SW_Pxx_REQ, + .map = IMX8MM_MIPI_A53_DOMAIN, + }, + .pgc = BIT(IMX8MM_PGC_MIPI), + }, +}; + +static const struct regmap_range imx8mm_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + GPC_PU_PWRHSK), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_MIPI), + GPC_PGC_SR(IMX8MM_PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_PCIE), + GPC_PGC_SR(IMX8MM_PGC_PCIE)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_OTG1), + GPC_PGC_SR(IMX8MM_PGC_OTG1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_OTG2), + GPC_PGC_SR(IMX8MM_PGC_OTG2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_DDR1), + GPC_PGC_SR(IMX8MM_PGC_DDR1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPU2D), + GPC_PGC_SR(IMX8MM_PGC_GPU2D)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPUMIX), + GPC_PGC_SR(IMX8MM_PGC_GPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUMIX), + GPC_PGC_SR(IMX8MM_PGC_VPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_GPU3D), + GPC_PGC_SR(IMX8MM_PGC_GPU3D)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_DISPMIX), + GPC_PGC_SR(IMX8MM_PGC_DISPMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUG1), + GPC_PGC_SR(IMX8MM_PGC_VPUG1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUG2), + GPC_PGC_SR(IMX8MM_PGC_VPUG2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MM_PGC_VPUH1), + GPC_PGC_SR(IMX8MM_PGC_VPUH1)), +}; + +static const struct regmap_access_table imx8mm_access_table = { + .yes_ranges = imx8mm_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx8mm_yes_ranges), +}; + +static const struct imx_pgc_domain_data imx8mm_pgc_domain_data = { + .domains = imx8mm_pgc_domains, + .domains_num = ARRAY_SIZE(imx8mm_pgc_domains), + .reg_access_table = &imx8mm_access_table, + .pgc_regs = &imx7_pgc_regs, +}; + +static const struct imx_pgc_domain imx8mp_pgc_domains[] = { + [IMX8MP_POWER_DOMAIN_MIPI_PHY1] = { + .genpd = { + .name = "mipi-phy1", + }, + .bits = { + .pxx = IMX8MP_MIPI_PHY1_SW_Pxx_REQ, + .map = IMX8MP_MIPI_PHY1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MIPI1), + }, + + [IMX8MP_POWER_DOMAIN_PCIE_PHY] = { + .genpd = { + .name = "pcie-phy1", + }, + .bits = { + .pxx = IMX8MP_PCIE_PHY_SW_Pxx_REQ, + .map = IMX8MP_PCIE_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_PCIE), + }, + + [IMX8MP_POWER_DOMAIN_USB1_PHY] = { + .genpd = { + .name = "usb-otg1", + }, + .bits = { + .pxx = IMX8MP_USB1_PHY_Pxx_REQ, + .map = IMX8MP_USB1_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_USB1), + }, + + [IMX8MP_POWER_DOMAIN_USB2_PHY] = { + .genpd = { + .name = "usb-otg2", + }, + .bits = { + .pxx = IMX8MP_USB2_PHY_Pxx_REQ, + .map = IMX8MP_USB2_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_USB2), + }, + + [IMX8MP_POWER_DOMAIN_MLMIX] = { + .genpd = { + .name = "mlmix", + }, + .bits = { + .pxx = IMX8MP_MLMIX_Pxx_REQ, + .map = IMX8MP_MLMIX_A53_DOMAIN, + .hskreq = IMX8MP_MLMIX_PWRDNREQN, + .hskack = IMX8MP_MLMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MLMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_AUDIOMIX] = { + .genpd = { + .name = "audiomix", + }, + .bits = { + .pxx = IMX8MP_AUDIOMIX_Pxx_REQ, + .map = IMX8MP_AUDIOMIX_A53_DOMAIN, + .hskreq = IMX8MP_AUDIOMIX_PWRDNREQN, + .hskack = IMX8MP_AUDIOMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_AUDIOMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_GPU2D] = { + .genpd = { + .name = "gpu2d", + }, + .bits = { + .pxx = IMX8MP_GPU_2D_Pxx_REQ, + .map = IMX8MP_GPU2D_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_GPU2D), + }, + + [IMX8MP_POWER_DOMAIN_GPUMIX] = { + .genpd = { + .name = "gpumix", + }, + .bits = { + .pxx = IMX8MP_GPU_SHARE_LOGIC_Pxx_REQ, + .map = IMX8MP_GPUMIX_A53_DOMAIN, + .hskreq = IMX8MP_GPUMIX_PWRDNREQN, + .hskack = IMX8MP_GPUMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_GPUMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_VPUMIX] = { + .genpd = { + .name = "vpumix", + }, + .bits = { + .pxx = IMX8MP_VPU_MIX_SHARE_LOGIC_Pxx_REQ, + .map = IMX8MP_VPUMIX_A53_DOMAIN, + .hskreq = IMX8MP_VPUMIX_PWRDNREQN, + .hskack = IMX8MP_VPUMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_VPUMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_GPU3D] = { + .genpd = { + .name = "gpu3d", + }, + .bits = { + .pxx = IMX8MP_GPU_3D_Pxx_REQ, + .map = IMX8MP_GPU3D_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_GPU3D), + }, + + [IMX8MP_POWER_DOMAIN_MEDIAMIX] = { + .genpd = { + .name = "mediamix", + }, + .bits = { + .pxx = IMX8MP_MEDIMIX_Pxx_REQ, + .map = IMX8MP_MEDIAMIX_A53_DOMAIN, + .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN, + .hskack = IMX8MP_MEDIAMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIAMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_VPU_G1] = { + .genpd = { + .name = "vpu-g1", + }, + .bits = { + .pxx = IMX8MP_VPU_G1_Pxx_REQ, + .map = IMX8MP_VPU_G1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_VPU_G1), + }, + + [IMX8MP_POWER_DOMAIN_VPU_G2] = { + .genpd = { + .name = "vpu-g2", + }, + .bits = { + .pxx = IMX8MP_VPU_G2_Pxx_REQ, + .map = IMX8MP_VPU_G2_A53_DOMAIN + }, + .pgc = BIT(IMX8MP_PGC_VPU_G2), + }, + + [IMX8MP_POWER_DOMAIN_VPU_VC8000E] = { + .genpd = { + .name = "vpu-h1", + }, + .bits = { + .pxx = IMX8MP_VPU_VC8K_Pxx_REQ, + .map = IMX8MP_VPU_VC8000E_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_VPU_VC8000E), + }, + + [IMX8MP_POWER_DOMAIN_HDMIMIX] = { + .genpd = { + .name = "hdmimix", + }, + .bits = { + .pxx = IMX8MP_HDMIMIX_Pxx_REQ, + .map = IMX8MP_HDMIMIX_A53_DOMAIN, + .hskreq = IMX8MP_HDMIMIX_PWRDNREQN, + .hskack = IMX8MP_HDMIMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_HDMIMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_HDMI_PHY] = { + .genpd = { + .name = "hdmi-phy", + }, + .bits = { + .pxx = IMX8MP_HDMI_PHY_Pxx_REQ, + .map = IMX8MP_HDMI_PHY_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_HDMI), + }, + + [IMX8MP_POWER_DOMAIN_MIPI_PHY2] = { + .genpd = { + .name = "mipi-phy2", + }, + .bits = { + .pxx = IMX8MP_MIPI_PHY2_Pxx_REQ, + .map = IMX8MP_MIPI_PHY2_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MIPI2), + }, + + [IMX8MP_POWER_DOMAIN_HSIOMIX] = { + .genpd = { + .name = "hsiomix", + }, + .bits = { + .pxx = IMX8MP_HSIOMIX_Pxx_REQ, + .map = IMX8MP_HSIOMIX_A53_DOMAIN, + .hskreq = IMX8MP_HSIOMIX_PWRDNREQN, + .hskack = IMX8MP_HSIOMIX_PWRDNACKN, + }, + .pgc = BIT(IMX8MP_PGC_HSIOMIX), + .keep_clocks = true, + }, + + [IMX8MP_POWER_DOMAIN_MEDIAMIX_ISPDWP] = { + .genpd = { + .name = "mediamix-isp-dwp", + }, + .bits = { + .pxx = IMX8MP_MEDIA_ISP_DWP_Pxx_REQ, + .map = IMX8MP_MEDIA_ISPDWP_A53_DOMAIN, + }, + .pgc = BIT(IMX8MP_PGC_MEDIA_ISP_DWP), + }, +}; + +static const struct regmap_range imx8mp_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + IMX8MP_GPC_PGC_CPU_MAPPING), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_NOC), + GPC_PGC_SR(IMX8MP_PGC_NOC)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI1), + GPC_PGC_SR(IMX8MP_PGC_MIPI1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_PCIE), + GPC_PGC_SR(IMX8MP_PGC_PCIE)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB1), + GPC_PGC_SR(IMX8MP_PGC_USB1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_USB2), + GPC_PGC_SR(IMX8MP_PGC_USB2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MLMIX), + GPC_PGC_SR(IMX8MP_PGC_MLMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_AUDIOMIX), + GPC_PGC_SR(IMX8MP_PGC_AUDIOMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU2D), + GPC_PGC_SR(IMX8MP_PGC_GPU2D)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPUMIX), + GPC_PGC_SR(IMX8MP_PGC_GPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPUMIX), + GPC_PGC_SR(IMX8MP_PGC_VPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_GPU3D), + GPC_PGC_SR(IMX8MP_PGC_GPU3D)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIAMIX), + GPC_PGC_SR(IMX8MP_PGC_MEDIAMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G1), + GPC_PGC_SR(IMX8MP_PGC_VPU_G1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_G2), + GPC_PGC_SR(IMX8MP_PGC_VPU_G2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_VPU_VC8000E), + GPC_PGC_SR(IMX8MP_PGC_VPU_VC8000E)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMIMIX), + GPC_PGC_SR(IMX8MP_PGC_HDMIMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HDMI), + GPC_PGC_SR(IMX8MP_PGC_HDMI)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MIPI2), + GPC_PGC_SR(IMX8MP_PGC_MIPI2)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_HSIOMIX), + GPC_PGC_SR(IMX8MP_PGC_HSIOMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_MEDIA_ISP_DWP), + GPC_PGC_SR(IMX8MP_PGC_MEDIA_ISP_DWP)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MP_PGC_DDRMIX), + GPC_PGC_SR(IMX8MP_PGC_DDRMIX)), +}; + +static const struct regmap_access_table imx8mp_access_table = { + .yes_ranges = imx8mp_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx8mp_yes_ranges), +}; + +static const struct imx_pgc_regs imx8mp_pgc_regs = { + .map = IMX8MP_GPC_PGC_CPU_MAPPING, + .pup = IMX8MP_GPC_PU_PGC_SW_PUP_REQ, + .pdn = IMX8MP_GPC_PU_PGC_SW_PDN_REQ, + .hsk = IMX8MP_GPC_PU_PWRHSK, +}; +static const struct imx_pgc_domain_data imx8mp_pgc_domain_data = { + .domains = imx8mp_pgc_domains, + .domains_num = ARRAY_SIZE(imx8mp_pgc_domains), + .reg_access_table = &imx8mp_access_table, + .pgc_regs = &imx8mp_pgc_regs, +}; + +static const struct imx_pgc_domain imx8mn_pgc_domains[] = { + [IMX8MN_POWER_DOMAIN_HSIOMIX] = { + .genpd = { + .name = "hsiomix", + }, + .bits = { + .pxx = 0, /* no power sequence control */ + .map = 0, /* no power sequence control */ + .hskreq = IMX8MN_HSIO_HSK_PWRDNREQN, + .hskack = IMX8MN_HSIO_HSK_PWRDNACKN, + }, + .keep_clocks = true, + }, + + [IMX8MN_POWER_DOMAIN_OTG1] = { + .genpd = { + .name = "usb-otg1", + .flags = GENPD_FLAG_ACTIVE_WAKEUP, + }, + .bits = { + .pxx = IMX8MN_OTG1_SW_Pxx_REQ, + .map = IMX8MN_OTG1_A53_DOMAIN, + }, + .pgc = BIT(IMX8MN_PGC_OTG1), + }, + + [IMX8MN_POWER_DOMAIN_GPUMIX] = { + .genpd = { + .name = "gpumix", + }, + .bits = { + .pxx = IMX8MN_GPUMIX_SW_Pxx_REQ, + .map = IMX8MN_GPUMIX_A53_DOMAIN, + .hskreq = IMX8MN_GPUMIX_HSK_PWRDNREQN, + .hskack = IMX8MN_GPUMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MN_PGC_GPUMIX), + .keep_clocks = true, + }, + + [IMX8MN_POWER_DOMAIN_DISPMIX] = { + .genpd = { + .name = "dispmix", + }, + .bits = { + .pxx = IMX8MN_DISPMIX_SW_Pxx_REQ, + .map = IMX8MN_DISPMIX_A53_DOMAIN, + .hskreq = IMX8MN_DISPMIX_HSK_PWRDNREQN, + .hskack = IMX8MN_DISPMIX_HSK_PWRDNACKN, + }, + .pgc = BIT(IMX8MN_PGC_DISPMIX), + .keep_clocks = true, + }, + + [IMX8MN_POWER_DOMAIN_MIPI] = { + .genpd = { + .name = "mipi", + }, + .bits = { + .pxx = IMX8MN_MIPI_SW_Pxx_REQ, + .map = IMX8MN_MIPI_A53_DOMAIN, + }, + .pgc = BIT(IMX8MN_PGC_MIPI), + }, +}; + +static const struct regmap_range imx8mn_yes_ranges[] = { + regmap_reg_range(GPC_LPCR_A_CORE_BSC, + GPC_PU_PWRHSK), + regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_MIPI), + GPC_PGC_SR(IMX8MN_PGC_MIPI)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_OTG1), + GPC_PGC_SR(IMX8MN_PGC_OTG1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_DDR1), + GPC_PGC_SR(IMX8MN_PGC_DDR1)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_GPUMIX), + GPC_PGC_SR(IMX8MN_PGC_GPUMIX)), + regmap_reg_range(GPC_PGC_CTRL(IMX8MN_PGC_DISPMIX), + GPC_PGC_SR(IMX8MN_PGC_DISPMIX)), +}; + +static const struct regmap_access_table imx8mn_access_table = { + .yes_ranges = imx8mn_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx8mn_yes_ranges), +}; + +static const struct imx_pgc_domain_data imx8mn_pgc_domain_data = { + .domains = imx8mn_pgc_domains, + .domains_num = ARRAY_SIZE(imx8mn_pgc_domains), + .reg_access_table = &imx8mn_access_table, + .pgc_regs = &imx7_pgc_regs, +}; + +static int imx_pgc_domain_probe(struct platform_device *pdev) +{ + struct imx_pgc_domain *domain = pdev->dev.platform_data; + int ret; + + domain->dev = &pdev->dev; + + domain->regulator = devm_regulator_get_optional(domain->dev, "power"); + if (IS_ERR(domain->regulator)) { + if (PTR_ERR(domain->regulator) != -ENODEV) + return dev_err_probe(domain->dev, PTR_ERR(domain->regulator), + "Failed to get domain's regulator\n"); + } else if (domain->voltage) { + regulator_set_voltage(domain->regulator, + domain->voltage, domain->voltage); + } + + domain->num_clks = devm_clk_bulk_get_all(domain->dev, &domain->clks); + if (domain->num_clks < 0) + return dev_err_probe(domain->dev, domain->num_clks, + "Failed to get domain's clocks\n"); + + domain->reset = devm_reset_control_array_get_optional_exclusive(domain->dev); + if (IS_ERR(domain->reset)) + return dev_err_probe(domain->dev, PTR_ERR(domain->reset), + "Failed to get domain's resets\n"); + + pm_runtime_enable(domain->dev); + + if (domain->bits.map) + regmap_update_bits(domain->regmap, domain->regs->map, + domain->bits.map, domain->bits.map); + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err(domain->dev, "Failed to init power domain\n"); + goto out_domain_unmap; + } + + if (IS_ENABLED(CONFIG_LOCKDEP) && + of_property_read_bool(domain->dev->of_node, "power-domains")) + lockdep_set_subclass(&domain->genpd.mlock, 1); + + ret = of_genpd_add_provider_simple(domain->dev->of_node, + &domain->genpd); + if (ret) { + dev_err(domain->dev, "Failed to add genpd provider\n"); + goto out_genpd_remove; + } + + return 0; + +out_genpd_remove: + pm_genpd_remove(&domain->genpd); +out_domain_unmap: + if (domain->bits.map) + regmap_update_bits(domain->regmap, domain->regs->map, + domain->bits.map, 0); + pm_runtime_disable(domain->dev); + + return ret; +} + +static int imx_pgc_domain_remove(struct platform_device *pdev) +{ + struct imx_pgc_domain *domain = pdev->dev.platform_data; + + of_genpd_del_provider(domain->dev->of_node); + pm_genpd_remove(&domain->genpd); + + if (domain->bits.map) + regmap_update_bits(domain->regmap, domain->regs->map, + domain->bits.map, 0); + + pm_runtime_disable(domain->dev); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int imx_pgc_domain_suspend(struct device *dev) +{ + int ret; + + /* + * This may look strange, but is done so the generic PM_SLEEP code + * can power down our domain and more importantly power it up again + * after resume, without tripping over our usage of runtime PM to + * power up/down the nested domains. + */ + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + pm_runtime_put_noidle(dev); + return ret; + } + + return 0; +} + +static int imx_pgc_domain_resume(struct device *dev) +{ + return pm_runtime_put(dev); +} +#endif + +static const struct dev_pm_ops imx_pgc_domain_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx_pgc_domain_suspend, imx_pgc_domain_resume) +}; + +static const struct platform_device_id imx_pgc_domain_id[] = { + { "imx-pgc-domain", }, + { }, +}; + +static struct platform_driver imx_pgc_domain_driver = { + .driver = { + .name = "imx-pgc", + .pm = &imx_pgc_domain_pm_ops, + }, + .probe = imx_pgc_domain_probe, + .remove = imx_pgc_domain_remove, + .id_table = imx_pgc_domain_id, +}; +builtin_platform_driver(imx_pgc_domain_driver) + +static int imx_gpcv2_probe(struct platform_device *pdev) +{ + const struct imx_pgc_domain_data *domain_data = + of_device_get_match_data(&pdev->dev); + + struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .rd_table = domain_data->reg_access_table, + .wr_table = domain_data->reg_access_table, + .max_register = SZ_4K, + }; + struct device *dev = &pdev->dev; + struct device_node *pgc_np, *np; + struct regmap *regmap; + void __iomem *base; + int ret; + + pgc_np = of_get_child_by_name(dev->of_node, "pgc"); + if (!pgc_np) { + dev_err(dev, "No power domains specified in DT\n"); + return -EINVAL; + } + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(dev, "failed to init regmap (%d)\n", ret); + return ret; + } + + for_each_child_of_node(pgc_np, np) { + struct platform_device *pd_pdev; + struct imx_pgc_domain *domain; + u32 domain_index; + + if (!of_device_is_available(np)) + continue; + + ret = of_property_read_u32(np, "reg", &domain_index); + if (ret) { + dev_err(dev, "Failed to read 'reg' property\n"); + of_node_put(np); + return ret; + } + + if (domain_index >= domain_data->domains_num) { + dev_warn(dev, + "Domain index %d is out of bounds\n", + domain_index); + continue; + } + + pd_pdev = platform_device_alloc("imx-pgc-domain", + domain_index); + if (!pd_pdev) { + dev_err(dev, "Failed to allocate platform device\n"); + of_node_put(np); + return -ENOMEM; + } + + ret = platform_device_add_data(pd_pdev, + &domain_data->domains[domain_index], + sizeof(domain_data->domains[domain_index])); + if (ret) { + platform_device_put(pd_pdev); + of_node_put(np); + return ret; + } + + domain = pd_pdev->dev.platform_data; + domain->regmap = regmap; + domain->regs = domain_data->pgc_regs; + + domain->genpd.power_on = imx_pgc_power_up; + domain->genpd.power_off = imx_pgc_power_down; + + pd_pdev->dev.parent = dev; + device_set_node(&pd_pdev->dev, of_fwnode_handle(np)); + + ret = platform_device_add(pd_pdev); + if (ret) { + platform_device_put(pd_pdev); + of_node_put(np); + return ret; + } + } + + return 0; +} + +static const struct of_device_id imx_gpcv2_dt_ids[] = { + { .compatible = "fsl,imx7d-gpc", .data = &imx7_pgc_domain_data, }, + { .compatible = "fsl,imx8mm-gpc", .data = &imx8mm_pgc_domain_data, }, + { .compatible = "fsl,imx8mn-gpc", .data = &imx8mn_pgc_domain_data, }, + { .compatible = "fsl,imx8mp-gpc", .data = &imx8mp_pgc_domain_data, }, + { .compatible = "fsl,imx8mq-gpc", .data = &imx8m_pgc_domain_data, }, + { } +}; + +static struct platform_driver imx_gpc_driver = { + .driver = { + .name = "imx-gpcv2", + .of_match_table = imx_gpcv2_dt_ids, + }, + .probe = imx_gpcv2_probe, +}; +builtin_platform_driver(imx_gpc_driver) diff --git a/drivers/pmdomain/imx/imx8m-blk-ctrl.c b/drivers/pmdomain/imx/imx8m-blk-ctrl.c new file mode 100644 index 000000000000..cc5ef6e2f0a8 --- /dev/null +++ b/drivers/pmdomain/imx/imx8m-blk-ctrl.c @@ -0,0 +1,899 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2021 Pengutronix, Lucas Stach + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MIPI_RESET_DIV 0x8 /* Mini/Nano/Plus DISPLAY_BLK_CTRL only */ + +struct imx8m_blk_ctrl_domain; + +struct imx8m_blk_ctrl { + struct device *dev; + struct notifier_block power_nb; + struct device *bus_power_dev; + struct regmap *regmap; + struct imx8m_blk_ctrl_domain *domains; + struct genpd_onecell_data onecell_data; +}; + +struct imx8m_blk_ctrl_domain_data { + const char *name; + const char * const *clk_names; + const char * const *path_names; + const char *gpc_name; + int num_clks; + int num_paths; + u32 rst_mask; + u32 clk_mask; + + /* + * i.MX8M Mini, Nano and Plus have a third DISPLAY_BLK_CTRL register + * which is used to control the reset for the MIPI Phy. + * Since it's only present in certain circumstances, + * an if-statement should be used before setting and clearing this + * register. + */ + u32 mipi_phy_rst_mask; +}; + +#define DOMAIN_MAX_CLKS 4 +#define DOMAIN_MAX_PATHS 4 + +struct imx8m_blk_ctrl_domain { + struct generic_pm_domain genpd; + const struct imx8m_blk_ctrl_domain_data *data; + struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; + struct icc_bulk_data paths[DOMAIN_MAX_PATHS]; + struct device *power_dev; + struct imx8m_blk_ctrl *bc; + int num_paths; +}; + +struct imx8m_blk_ctrl_data { + int max_reg; + notifier_fn_t power_notifier_fn; + const struct imx8m_blk_ctrl_domain_data *domains; + int num_domains; +}; + +static inline struct imx8m_blk_ctrl_domain * +to_imx8m_blk_ctrl_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx8m_blk_ctrl_domain, genpd); +} + +static int imx8m_blk_ctrl_power_on(struct generic_pm_domain *genpd) +{ + struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd); + const struct imx8m_blk_ctrl_domain_data *data = domain->data; + struct imx8m_blk_ctrl *bc = domain->bc; + int ret; + + /* make sure bus domain is awake */ + ret = pm_runtime_get_sync(bc->bus_power_dev); + if (ret < 0) { + pm_runtime_put_noidle(bc->bus_power_dev); + dev_err(bc->dev, "failed to power up bus domain\n"); + return ret; + } + + /* put devices into reset */ + regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); + if (data->mipi_phy_rst_mask) + regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); + + /* enable upstream and blk-ctrl clocks to allow reset to propagate */ + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); + if (ret) { + dev_err(bc->dev, "failed to enable clocks\n"); + goto bus_put; + } + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + /* power up upstream GPC domain */ + ret = pm_runtime_get_sync(domain->power_dev); + if (ret < 0) { + dev_err(bc->dev, "failed to power up peripheral domain\n"); + goto clk_disable; + } + + /* wait for reset to propagate */ + udelay(5); + + /* release reset */ + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); + if (data->mipi_phy_rst_mask) + regmap_set_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); + + ret = icc_bulk_set_bw(domain->num_paths, domain->paths); + if (ret) + dev_err(bc->dev, "failed to set icc bw\n"); + + /* disable upstream clocks */ + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + return 0; + +clk_disable: + clk_bulk_disable_unprepare(data->num_clks, domain->clks); +bus_put: + pm_runtime_put(bc->bus_power_dev); + + return ret; +} + +static int imx8m_blk_ctrl_power_off(struct generic_pm_domain *genpd) +{ + struct imx8m_blk_ctrl_domain *domain = to_imx8m_blk_ctrl_domain(genpd); + const struct imx8m_blk_ctrl_domain_data *data = domain->data; + struct imx8m_blk_ctrl *bc = domain->bc; + + /* put devices into reset and disable clocks */ + if (data->mipi_phy_rst_mask) + regmap_clear_bits(bc->regmap, BLK_MIPI_RESET_DIV, data->mipi_phy_rst_mask); + + regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); + regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + /* power down upstream GPC domain */ + pm_runtime_put(domain->power_dev); + + /* allow bus domain to suspend */ + pm_runtime_put(bc->bus_power_dev); + + return 0; +} + +static struct lock_class_key blk_ctrl_genpd_lock_class; + +static int imx8m_blk_ctrl_probe(struct platform_device *pdev) +{ + const struct imx8m_blk_ctrl_data *bc_data; + struct device *dev = &pdev->dev; + struct imx8m_blk_ctrl *bc; + void __iomem *base; + int i, ret; + + struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + + bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); + if (!bc) + return -ENOMEM; + + bc->dev = dev; + + bc_data = of_device_get_match_data(dev); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap_config.max_register = bc_data->max_reg; + bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(bc->regmap)) + return dev_err_probe(dev, PTR_ERR(bc->regmap), + "failed to init regmap\n"); + + bc->domains = devm_kcalloc(dev, bc_data->num_domains, + sizeof(struct imx8m_blk_ctrl_domain), + GFP_KERNEL); + if (!bc->domains) + return -ENOMEM; + + bc->onecell_data.num_domains = bc_data->num_domains; + bc->onecell_data.domains = + devm_kcalloc(dev, bc_data->num_domains, + sizeof(struct generic_pm_domain *), GFP_KERNEL); + if (!bc->onecell_data.domains) + return -ENOMEM; + + bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); + if (IS_ERR(bc->bus_power_dev)) { + if (PTR_ERR(bc->bus_power_dev) == -ENODEV) + return dev_err_probe(dev, -EPROBE_DEFER, + "failed to attach power domain \"bus\"\n"); + else + return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), + "failed to attach power domain \"bus\"\n"); + } + + for (i = 0; i < bc_data->num_domains; i++) { + const struct imx8m_blk_ctrl_domain_data *data = &bc_data->domains[i]; + struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; + int j; + + domain->data = data; + domain->num_paths = data->num_paths; + + for (j = 0; j < data->num_clks; j++) + domain->clks[j].id = data->clk_names[j]; + + for (j = 0; j < data->num_paths; j++) { + domain->paths[j].name = data->path_names[j]; + /* Fake value for now, just let ICC could configure NoC mode/priority */ + domain->paths[j].avg_bw = 1; + domain->paths[j].peak_bw = 1; + } + + ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n"); + domain->num_paths = 0; + } else { + dev_err_probe(dev, ret, "failed to get noc entries\n"); + goto cleanup_pds; + } + } + + ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); + if (ret) { + dev_err_probe(dev, ret, "failed to get clock\n"); + goto cleanup_pds; + } + + domain->power_dev = + dev_pm_domain_attach_by_name(dev, data->gpc_name); + if (IS_ERR(domain->power_dev)) { + dev_err_probe(dev, PTR_ERR(domain->power_dev), + "failed to attach power domain \"%s\"\n", + data->gpc_name); + ret = PTR_ERR(domain->power_dev); + goto cleanup_pds; + } + + domain->genpd.name = data->name; + domain->genpd.power_on = imx8m_blk_ctrl_power_on; + domain->genpd.power_off = imx8m_blk_ctrl_power_off; + domain->bc = bc; + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err_probe(dev, ret, + "failed to init power domain \"%s\"\n", + data->gpc_name); + dev_pm_domain_detach(domain->power_dev, true); + goto cleanup_pds; + } + + /* + * We use runtime PM to trigger power on/off of the upstream GPC + * domain, as a strict hierarchical parent/child power domain + * setup doesn't allow us to meet the sequencing requirements. + * This means we have nested locking of genpd locks, without the + * nesting being visible at the genpd level, so we need a + * separate lock class to make lockdep aware of the fact that + * this are separate domain locks that can be nested without a + * self-deadlock. + */ + lockdep_set_class(&domain->genpd.mlock, + &blk_ctrl_genpd_lock_class); + + bc->onecell_data.domains[i] = &domain->genpd; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); + if (ret) { + dev_err_probe(dev, ret, "failed to add power domain provider\n"); + goto cleanup_pds; + } + + bc->power_nb.notifier_call = bc_data->power_notifier_fn; + ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); + if (ret) { + dev_err_probe(dev, ret, "failed to add power notifier\n"); + goto cleanup_provider; + } + + dev_set_drvdata(dev, bc); + + ret = devm_of_platform_populate(dev); + if (ret) + goto cleanup_provider; + + return 0; + +cleanup_provider: + of_genpd_del_provider(dev->of_node); +cleanup_pds: + for (i--; i >= 0; i--) { + pm_genpd_remove(&bc->domains[i].genpd); + dev_pm_domain_detach(bc->domains[i].power_dev, true); + } + + dev_pm_domain_detach(bc->bus_power_dev, true); + + return ret; +} + +static int imx8m_blk_ctrl_remove(struct platform_device *pdev) +{ + struct imx8m_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); + int i; + + of_genpd_del_provider(pdev->dev.of_node); + + for (i = 0; bc->onecell_data.num_domains; i++) { + struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; + + pm_genpd_remove(&domain->genpd); + dev_pm_domain_detach(domain->power_dev, true); + } + + dev_pm_genpd_remove_notifier(bc->bus_power_dev); + + dev_pm_domain_detach(bc->bus_power_dev, true); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int imx8m_blk_ctrl_suspend(struct device *dev) +{ + struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev); + int ret, i; + + /* + * This may look strange, but is done so the generic PM_SLEEP code + * can power down our domains and more importantly power them up again + * after resume, without tripping over our usage of runtime PM to + * control the upstream GPC domains. Things happen in the right order + * in the system suspend/resume paths due to the device parent/child + * hierarchy. + */ + ret = pm_runtime_get_sync(bc->bus_power_dev); + if (ret < 0) { + pm_runtime_put_noidle(bc->bus_power_dev); + return ret; + } + + for (i = 0; i < bc->onecell_data.num_domains; i++) { + struct imx8m_blk_ctrl_domain *domain = &bc->domains[i]; + + ret = pm_runtime_get_sync(domain->power_dev); + if (ret < 0) { + pm_runtime_put_noidle(domain->power_dev); + goto out_fail; + } + } + + return 0; + +out_fail: + for (i--; i >= 0; i--) + pm_runtime_put(bc->domains[i].power_dev); + + pm_runtime_put(bc->bus_power_dev); + + return ret; +} + +static int imx8m_blk_ctrl_resume(struct device *dev) +{ + struct imx8m_blk_ctrl *bc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < bc->onecell_data.num_domains; i++) + pm_runtime_put(bc->domains[i].power_dev); + + pm_runtime_put(bc->bus_power_dev); + + return 0; +} +#endif + +static const struct dev_pm_ops imx8m_blk_ctrl_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx8m_blk_ctrl_suspend, imx8m_blk_ctrl_resume) +}; + +static int imx8mm_vpu_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* + * The ADB in the VPUMIX domain has no separate reset and clock + * enable bits, but is ungated together with the VPU clocks. To + * allow the handshake with the GPC to progress we put the VPUs + * in reset and ungate the clocks. + */ + regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1) | BIT(2)); + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1) | BIT(2)); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + udelay(5); + + /* set "fuse" bits to enable the VPUs */ + regmap_set_bits(bc->regmap, 0x8, 0xffffffff); + regmap_set_bits(bc->regmap, 0xc, 0xffffffff); + regmap_set_bits(bc->regmap, 0x10, 0xffffffff); + regmap_set_bits(bc->regmap, 0x14, 0xffffffff); + } + + return NOTIFY_OK; +} + +static const struct imx8m_blk_ctrl_domain_data imx8mm_vpu_blk_ctl_domain_data[] = { + [IMX8MM_VPUBLK_PD_G1] = { + .name = "vpublk-g1", + .clk_names = (const char *[]){ "g1", }, + .num_clks = 1, + .gpc_name = "g1", + .rst_mask = BIT(1), + .clk_mask = BIT(1), + }, + [IMX8MM_VPUBLK_PD_G2] = { + .name = "vpublk-g2", + .clk_names = (const char *[]){ "g2", }, + .num_clks = 1, + .gpc_name = "g2", + .rst_mask = BIT(0), + .clk_mask = BIT(0), + }, + [IMX8MM_VPUBLK_PD_H1] = { + .name = "vpublk-h1", + .clk_names = (const char *[]){ "h1", }, + .num_clks = 1, + .gpc_name = "h1", + .rst_mask = BIT(2), + .clk_mask = BIT(2), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mm_vpu_blk_ctl_dev_data = { + .max_reg = 0x18, + .power_notifier_fn = imx8mm_vpu_power_notifier, + .domains = imx8mm_vpu_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mm_vpu_blk_ctl_domain_data), +}; + +static const struct imx8m_blk_ctrl_domain_data imx8mp_vpu_blk_ctl_domain_data[] = { + [IMX8MP_VPUBLK_PD_G1] = { + .name = "vpublk-g1", + .clk_names = (const char *[]){ "g1", }, + .num_clks = 1, + .gpc_name = "g1", + .rst_mask = BIT(1), + .clk_mask = BIT(1), + .path_names = (const char *[]){"g1"}, + .num_paths = 1, + }, + [IMX8MP_VPUBLK_PD_G2] = { + .name = "vpublk-g2", + .clk_names = (const char *[]){ "g2", }, + .num_clks = 1, + .gpc_name = "g2", + .rst_mask = BIT(0), + .clk_mask = BIT(0), + .path_names = (const char *[]){"g2"}, + .num_paths = 1, + }, + [IMX8MP_VPUBLK_PD_VC8000E] = { + .name = "vpublk-vc8000e", + .clk_names = (const char *[]){ "vc8000e", }, + .num_clks = 1, + .gpc_name = "vc8000e", + .rst_mask = BIT(2), + .clk_mask = BIT(2), + .path_names = (const char *[]){"vc8000e"}, + .num_paths = 1, + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mp_vpu_blk_ctl_dev_data = { + .max_reg = 0x18, + .power_notifier_fn = imx8mm_vpu_power_notifier, + .domains = imx8mp_vpu_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_vpu_blk_ctl_domain_data), +}; + +static int imx8mm_disp_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* Enable bus clock and deassert bus reset */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(12)); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(6)); + + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + if (action == GENPD_NOTIFY_ON) + udelay(5); + + + return NOTIFY_OK; +} + +static const struct imx8m_blk_ctrl_domain_data imx8mm_disp_blk_ctl_domain_data[] = { + [IMX8MM_DISPBLK_PD_CSI_BRIDGE] = { + .name = "dispblk-csi-bridge", + .clk_names = (const char *[]){ "csi-bridge-axi", "csi-bridge-apb", + "csi-bridge-core", }, + .num_clks = 3, + .gpc_name = "csi-bridge", + .rst_mask = BIT(0) | BIT(1) | BIT(2), + .clk_mask = BIT(0) | BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5), + }, + [IMX8MM_DISPBLK_PD_LCDIF] = { + .name = "dispblk-lcdif", + .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, + .num_clks = 3, + .gpc_name = "lcdif", + .clk_mask = BIT(6) | BIT(7), + }, + [IMX8MM_DISPBLK_PD_MIPI_DSI] = { + .name = "dispblk-mipi-dsi", + .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, + .num_clks = 2, + .gpc_name = "mipi-dsi", + .rst_mask = BIT(5), + .clk_mask = BIT(8) | BIT(9), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MM_DISPBLK_PD_MIPI_CSI] = { + .name = "dispblk-mipi-csi", + .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, + .num_clks = 2, + .gpc_name = "mipi-csi", + .rst_mask = BIT(3) | BIT(4), + .clk_mask = BIT(10) | BIT(11), + .mipi_phy_rst_mask = BIT(16), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mm_disp_blk_ctl_dev_data = { + .max_reg = 0x2c, + .power_notifier_fn = imx8mm_disp_power_notifier, + .domains = imx8mm_disp_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mm_disp_blk_ctl_domain_data), +}; + + +static int imx8mn_disp_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* Enable bus clock and deassert bus reset */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); + + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + if (action == GENPD_NOTIFY_ON) + udelay(5); + + + return NOTIFY_OK; +} + +static const struct imx8m_blk_ctrl_domain_data imx8mn_disp_blk_ctl_domain_data[] = { + [IMX8MN_DISPBLK_PD_MIPI_DSI] = { + .name = "dispblk-mipi-dsi", + .clk_names = (const char *[]){ "dsi-pclk", "dsi-ref", }, + .num_clks = 2, + .gpc_name = "mipi-dsi", + .rst_mask = BIT(0) | BIT(1), + .clk_mask = BIT(0) | BIT(1), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MN_DISPBLK_PD_MIPI_CSI] = { + .name = "dispblk-mipi-csi", + .clk_names = (const char *[]){ "csi-aclk", "csi-pclk" }, + .num_clks = 2, + .gpc_name = "mipi-csi", + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .mipi_phy_rst_mask = BIT(16), + }, + [IMX8MN_DISPBLK_PD_LCDIF] = { + .name = "dispblk-lcdif", + .clk_names = (const char *[]){ "lcdif-axi", "lcdif-apb", "lcdif-pix", }, + .num_clks = 3, + .gpc_name = "lcdif", + .rst_mask = BIT(4) | BIT(5), + .clk_mask = BIT(4) | BIT(5), + }, + [IMX8MN_DISPBLK_PD_ISI] = { + .name = "dispblk-isi", + .clk_names = (const char *[]){ "disp_axi", "disp_apb", "disp_axi_root", + "disp_apb_root"}, + .num_clks = 4, + .gpc_name = "isi", + .rst_mask = BIT(6) | BIT(7), + .clk_mask = BIT(6) | BIT(7), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mn_disp_blk_ctl_dev_data = { + .max_reg = 0x84, + .power_notifier_fn = imx8mn_disp_power_notifier, + .domains = imx8mn_disp_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mn_disp_blk_ctl_domain_data), +}; + +#define LCDIF_ARCACHE_CTRL 0x4c +#define LCDIF_1_RD_HURRY GENMASK(15, 13) +#define LCDIF_0_RD_HURRY GENMASK(12, 10) + +static int imx8mp_media_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* Enable bus clock and deassert bus reset */ + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(8)); + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(8)); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + udelay(5); + + /* + * Set panic read hurry level for both LCDIF interfaces to + * maximum priority to minimize chances of display FIFO + * underflow. + */ + regmap_set_bits(bc->regmap, LCDIF_ARCACHE_CTRL, + FIELD_PREP(LCDIF_1_RD_HURRY, 7) | + FIELD_PREP(LCDIF_0_RD_HURRY, 7)); + } + + return NOTIFY_OK; +} + +/* + * From i.MX 8M Plus Applications Processor Reference Manual, Rev. 1, + * section 13.2.2, 13.2.3 + * isp-ahb and dwe are not in Figure 13-5. Media BLK_CTRL Clocks + */ +static const struct imx8m_blk_ctrl_domain_data imx8mp_media_blk_ctl_domain_data[] = { + [IMX8MP_MEDIABLK_PD_MIPI_DSI_1] = { + .name = "mediablk-mipi-dsi-1", + .clk_names = (const char *[]){ "apb", "phy", }, + .num_clks = 2, + .gpc_name = "mipi-dsi1", + .rst_mask = BIT(0) | BIT(1), + .clk_mask = BIT(0) | BIT(1), + .mipi_phy_rst_mask = BIT(17), + }, + [IMX8MP_MEDIABLK_PD_MIPI_CSI2_1] = { + .name = "mediablk-mipi-csi2-1", + .clk_names = (const char *[]){ "apb", "cam1" }, + .num_clks = 2, + .gpc_name = "mipi-csi1", + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .mipi_phy_rst_mask = BIT(16), + }, + [IMX8MP_MEDIABLK_PD_LCDIF_1] = { + .name = "mediablk-lcdif-1", + .clk_names = (const char *[]){ "disp1", "apb", "axi", }, + .num_clks = 3, + .gpc_name = "lcdif1", + .rst_mask = BIT(4) | BIT(5) | BIT(23), + .clk_mask = BIT(4) | BIT(5) | BIT(23), + .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"}, + .num_paths = 2, + }, + [IMX8MP_MEDIABLK_PD_ISI] = { + .name = "mediablk-isi", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "isi", + .rst_mask = BIT(6) | BIT(7), + .clk_mask = BIT(6) | BIT(7), + .path_names = (const char *[]){"isi0", "isi1", "isi2"}, + .num_paths = 3, + }, + [IMX8MP_MEDIABLK_PD_MIPI_CSI2_2] = { + .name = "mediablk-mipi-csi2-2", + .clk_names = (const char *[]){ "apb", "cam2" }, + .num_clks = 2, + .gpc_name = "mipi-csi2", + .rst_mask = BIT(9) | BIT(10), + .clk_mask = BIT(9) | BIT(10), + .mipi_phy_rst_mask = BIT(30), + }, + [IMX8MP_MEDIABLK_PD_LCDIF_2] = { + .name = "mediablk-lcdif-2", + .clk_names = (const char *[]){ "disp2", "apb", "axi", }, + .num_clks = 3, + .gpc_name = "lcdif2", + .rst_mask = BIT(11) | BIT(12) | BIT(24), + .clk_mask = BIT(11) | BIT(12) | BIT(24), + .path_names = (const char *[]){"lcdif-rd", "lcdif-wr"}, + .num_paths = 2, + }, + [IMX8MP_MEDIABLK_PD_ISP] = { + .name = "mediablk-isp", + .clk_names = (const char *[]){ "isp", "axi", "apb" }, + .num_clks = 3, + .gpc_name = "isp", + .rst_mask = BIT(16) | BIT(17) | BIT(18), + .clk_mask = BIT(16) | BIT(17) | BIT(18), + .path_names = (const char *[]){"isp0", "isp1"}, + .num_paths = 2, + }, + [IMX8MP_MEDIABLK_PD_DWE] = { + .name = "mediablk-dwe", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "dwe", + .rst_mask = BIT(19) | BIT(20) | BIT(21), + .clk_mask = BIT(19) | BIT(20) | BIT(21), + .path_names = (const char *[]){"dwe"}, + .num_paths = 1, + }, + [IMX8MP_MEDIABLK_PD_MIPI_DSI_2] = { + .name = "mediablk-mipi-dsi-2", + .clk_names = (const char *[]){ "phy", }, + .num_clks = 1, + .gpc_name = "mipi-dsi2", + .rst_mask = BIT(22), + .clk_mask = BIT(22), + .mipi_phy_rst_mask = BIT(29), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mp_media_blk_ctl_dev_data = { + .max_reg = 0x138, + .power_notifier_fn = imx8mp_media_power_notifier, + .domains = imx8mp_media_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_media_blk_ctl_domain_data), +}; + +static int imx8mq_vpu_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8m_blk_ctrl *bc = container_of(nb, struct imx8m_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON && action != GENPD_NOTIFY_PRE_OFF) + return NOTIFY_OK; + + /* + * The ADB in the VPUMIX domain has no separate reset and clock + * enable bits, but is ungated and reset together with the VPUs. The + * reset and clock enable inputs to the ADB is a logical OR of the + * VPU bits. In order to set the G2 fuse bits, the G2 clock must + * also be enabled. + */ + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, BIT(0) | BIT(1)); + regmap_set_bits(bc->regmap, BLK_CLK_EN, BIT(0) | BIT(1)); + + if (action == GENPD_NOTIFY_ON) { + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + udelay(5); + + /* set "fuse" bits to enable the VPUs */ + regmap_set_bits(bc->regmap, 0x8, 0xffffffff); + regmap_set_bits(bc->regmap, 0xc, 0xffffffff); + regmap_set_bits(bc->regmap, 0x10, 0xffffffff); + } + + return NOTIFY_OK; +} + +static const struct imx8m_blk_ctrl_domain_data imx8mq_vpu_blk_ctl_domain_data[] = { + [IMX8MQ_VPUBLK_PD_G1] = { + .name = "vpublk-g1", + .clk_names = (const char *[]){ "g1", }, + .num_clks = 1, + .gpc_name = "g1", + .rst_mask = BIT(1), + .clk_mask = BIT(1), + }, + [IMX8MQ_VPUBLK_PD_G2] = { + .name = "vpublk-g2", + .clk_names = (const char *[]){ "g2", }, + .num_clks = 1, + .gpc_name = "g2", + .rst_mask = BIT(0), + .clk_mask = BIT(0), + }, +}; + +static const struct imx8m_blk_ctrl_data imx8mq_vpu_blk_ctl_dev_data = { + .max_reg = 0x14, + .power_notifier_fn = imx8mq_vpu_power_notifier, + .domains = imx8mq_vpu_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx8mq_vpu_blk_ctl_domain_data), +}; + +static const struct of_device_id imx8m_blk_ctrl_of_match[] = { + { + .compatible = "fsl,imx8mm-vpu-blk-ctrl", + .data = &imx8mm_vpu_blk_ctl_dev_data + }, { + .compatible = "fsl,imx8mm-disp-blk-ctrl", + .data = &imx8mm_disp_blk_ctl_dev_data + }, { + .compatible = "fsl,imx8mn-disp-blk-ctrl", + .data = &imx8mn_disp_blk_ctl_dev_data + }, { + .compatible = "fsl,imx8mp-media-blk-ctrl", + .data = &imx8mp_media_blk_ctl_dev_data + }, { + .compatible = "fsl,imx8mq-vpu-blk-ctrl", + .data = &imx8mq_vpu_blk_ctl_dev_data + }, { + .compatible = "fsl,imx8mp-vpu-blk-ctrl", + .data = &imx8mp_vpu_blk_ctl_dev_data + }, { + /* Sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx8m_blk_ctrl_of_match); + +static struct platform_driver imx8m_blk_ctrl_driver = { + .probe = imx8m_blk_ctrl_probe, + .remove = imx8m_blk_ctrl_remove, + .driver = { + .name = "imx8m-blk-ctrl", + .pm = &imx8m_blk_ctrl_pm_ops, + .of_match_table = imx8m_blk_ctrl_of_match, + }, +}; +module_platform_driver(imx8m_blk_ctrl_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/imx/imx8mp-blk-ctrl.c b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c new file mode 100644 index 000000000000..c6ac32c1a8c1 --- /dev/null +++ b/drivers/pmdomain/imx/imx8mp-blk-ctrl.c @@ -0,0 +1,867 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright 2022 Pengutronix, Lucas Stach + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define GPR_REG0 0x0 +#define PCIE_CLOCK_MODULE_EN BIT(0) +#define USB_CLOCK_MODULE_EN BIT(1) +#define PCIE_PHY_APB_RST BIT(4) +#define PCIE_PHY_INIT_RST BIT(5) +#define GPR_REG1 0x4 +#define PLL_LOCK BIT(13) +#define GPR_REG2 0x8 +#define P_PLL_MASK GENMASK(5, 0) +#define M_PLL_MASK GENMASK(15, 6) +#define S_PLL_MASK GENMASK(18, 16) +#define GPR_REG3 0xc +#define PLL_CKE BIT(17) +#define PLL_RST BIT(31) + +struct imx8mp_blk_ctrl_domain; + +struct imx8mp_blk_ctrl { + struct device *dev; + struct notifier_block power_nb; + struct device *bus_power_dev; + struct regmap *regmap; + struct imx8mp_blk_ctrl_domain *domains; + struct genpd_onecell_data onecell_data; + void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); + void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); +}; + +struct imx8mp_blk_ctrl_domain_data { + const char *name; + const char * const *clk_names; + int num_clks; + const char * const *path_names; + int num_paths; + const char *gpc_name; +}; + +#define DOMAIN_MAX_CLKS 2 +#define DOMAIN_MAX_PATHS 3 + +struct imx8mp_blk_ctrl_domain { + struct generic_pm_domain genpd; + const struct imx8mp_blk_ctrl_domain_data *data; + struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; + struct icc_bulk_data paths[DOMAIN_MAX_PATHS]; + struct device *power_dev; + struct imx8mp_blk_ctrl *bc; + int num_paths; + int id; +}; + +struct imx8mp_blk_ctrl_data { + int max_reg; + int (*probe) (struct imx8mp_blk_ctrl *bc); + notifier_fn_t power_notifier_fn; + void (*power_off) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); + void (*power_on) (struct imx8mp_blk_ctrl *bc, struct imx8mp_blk_ctrl_domain *domain); + const struct imx8mp_blk_ctrl_domain_data *domains; + int num_domains; +}; + +static inline struct imx8mp_blk_ctrl_domain * +to_imx8mp_blk_ctrl_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx8mp_blk_ctrl_domain, genpd); +} + +struct clk_hsio_pll { + struct clk_hw hw; + struct regmap *regmap; +}; + +static inline struct clk_hsio_pll *to_clk_hsio_pll(struct clk_hw *hw) +{ + return container_of(hw, struct clk_hsio_pll, hw); +} + +static int clk_hsio_pll_prepare(struct clk_hw *hw) +{ + struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); + u32 val; + + /* set the PLL configuration */ + regmap_update_bits(clk->regmap, GPR_REG2, + P_PLL_MASK | M_PLL_MASK | S_PLL_MASK, + FIELD_PREP(P_PLL_MASK, 12) | + FIELD_PREP(M_PLL_MASK, 800) | + FIELD_PREP(S_PLL_MASK, 4)); + + /* de-assert PLL reset */ + regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST, PLL_RST); + + /* enable PLL */ + regmap_update_bits(clk->regmap, GPR_REG3, PLL_CKE, PLL_CKE); + + return regmap_read_poll_timeout(clk->regmap, GPR_REG1, val, + val & PLL_LOCK, 10, 100); +} + +static void clk_hsio_pll_unprepare(struct clk_hw *hw) +{ + struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); + + regmap_update_bits(clk->regmap, GPR_REG3, PLL_RST | PLL_CKE, 0); +} + +static int clk_hsio_pll_is_prepared(struct clk_hw *hw) +{ + struct clk_hsio_pll *clk = to_clk_hsio_pll(hw); + + return regmap_test_bits(clk->regmap, GPR_REG1, PLL_LOCK); +} + +static unsigned long clk_hsio_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + return 100000000; +} + +static const struct clk_ops clk_hsio_pll_ops = { + .prepare = clk_hsio_pll_prepare, + .unprepare = clk_hsio_pll_unprepare, + .is_prepared = clk_hsio_pll_is_prepared, + .recalc_rate = clk_hsio_pll_recalc_rate, +}; + +static int imx8mp_hsio_blk_ctrl_probe(struct imx8mp_blk_ctrl *bc) +{ + struct clk_hsio_pll *clk_hsio_pll; + struct clk_hw *hw; + struct clk_init_data init = {}; + int ret; + + clk_hsio_pll = devm_kzalloc(bc->dev, sizeof(*clk_hsio_pll), GFP_KERNEL); + if (!clk_hsio_pll) + return -ENOMEM; + + init.name = "hsio_pll"; + init.ops = &clk_hsio_pll_ops; + init.parent_names = (const char *[]){"osc_24m"}; + init.num_parents = 1; + + clk_hsio_pll->regmap = bc->regmap; + clk_hsio_pll->hw.init = &init; + + hw = &clk_hsio_pll->hw; + ret = devm_clk_hw_register(bc->bus_power_dev, hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(bc->dev, of_clk_hw_simple_get, hw); +} + +static void imx8mp_hsio_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HSIOBLK_PD_USB: + regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + break; + case IMX8MP_HSIOBLK_PD_PCIE: + regmap_set_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); + break; + case IMX8MP_HSIOBLK_PD_PCIE_PHY: + regmap_set_bits(bc->regmap, GPR_REG0, + PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST); + break; + default: + break; + } +} + +static void imx8mp_hsio_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HSIOBLK_PD_USB: + regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + break; + case IMX8MP_HSIOBLK_PD_PCIE: + regmap_clear_bits(bc->regmap, GPR_REG0, PCIE_CLOCK_MODULE_EN); + break; + case IMX8MP_HSIOBLK_PD_PCIE_PHY: + regmap_clear_bits(bc->regmap, GPR_REG0, + PCIE_PHY_APB_RST | PCIE_PHY_INIT_RST); + break; + default: + break; + } +} + +static int imx8mp_hsio_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, + power_nb); + struct clk_bulk_data *usb_clk = bc->domains[IMX8MP_HSIOBLK_PD_USB].clks; + int num_clks = bc->domains[IMX8MP_HSIOBLK_PD_USB].data->num_clks; + int ret; + + switch (action) { + case GENPD_NOTIFY_ON: + /* + * enable USB clock for a moment for the power-on ADB handshake + * to proceed + */ + ret = clk_bulk_prepare_enable(num_clks, usb_clk); + if (ret) + return NOTIFY_BAD; + regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + + udelay(5); + + regmap_clear_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + clk_bulk_disable_unprepare(num_clks, usb_clk); + break; + case GENPD_NOTIFY_PRE_OFF: + /* enable USB clock for the power-down ADB handshake to work */ + ret = clk_bulk_prepare_enable(num_clks, usb_clk); + if (ret) + return NOTIFY_BAD; + + regmap_set_bits(bc->regmap, GPR_REG0, USB_CLOCK_MODULE_EN); + break; + case GENPD_NOTIFY_OFF: + clk_bulk_disable_unprepare(num_clks, usb_clk); + break; + default: + break; + } + + return NOTIFY_OK; +} + +static const struct imx8mp_blk_ctrl_domain_data imx8mp_hsio_domain_data[] = { + [IMX8MP_HSIOBLK_PD_USB] = { + .name = "hsioblk-usb", + .clk_names = (const char *[]){ "usb" }, + .num_clks = 1, + .gpc_name = "usb", + .path_names = (const char *[]){"usb1", "usb2"}, + .num_paths = 2, + }, + [IMX8MP_HSIOBLK_PD_USB_PHY1] = { + .name = "hsioblk-usb-phy1", + .gpc_name = "usb-phy1", + }, + [IMX8MP_HSIOBLK_PD_USB_PHY2] = { + .name = "hsioblk-usb-phy2", + .gpc_name = "usb-phy2", + }, + [IMX8MP_HSIOBLK_PD_PCIE] = { + .name = "hsioblk-pcie", + .clk_names = (const char *[]){ "pcie" }, + .num_clks = 1, + .gpc_name = "pcie", + .path_names = (const char *[]){"noc-pcie", "pcie"}, + .num_paths = 2, + }, + [IMX8MP_HSIOBLK_PD_PCIE_PHY] = { + .name = "hsioblk-pcie-phy", + .gpc_name = "pcie-phy", + }, +}; + +static const struct imx8mp_blk_ctrl_data imx8mp_hsio_blk_ctl_dev_data = { + .max_reg = 0x24, + .probe = imx8mp_hsio_blk_ctrl_probe, + .power_on = imx8mp_hsio_blk_ctrl_power_on, + .power_off = imx8mp_hsio_blk_ctrl_power_off, + .power_notifier_fn = imx8mp_hsio_power_notifier, + .domains = imx8mp_hsio_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_hsio_domain_data), +}; + +#define HDMI_RTX_RESET_CTL0 0x20 +#define HDMI_RTX_CLK_CTL0 0x40 +#define HDMI_RTX_CLK_CTL1 0x50 +#define HDMI_RTX_CLK_CTL2 0x60 +#define HDMI_RTX_CLK_CTL3 0x70 +#define HDMI_RTX_CLK_CTL4 0x80 +#define HDMI_TX_CONTROL0 0x200 +#define HDMI_LCDIF_NOC_HURRY_MASK GENMASK(14, 12) + +static void imx8mp_hdmi_blk_ctrl_power_on(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HDMIBLK_PD_IRQSTEER: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); + break; + case IMX8MP_HDMIBLK_PD_LCDIF: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(16) | BIT(17) | BIT(18) | + BIT(19) | BIT(20)); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(4) | BIT(5) | BIT(6)); + regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, + FIELD_PREP(HDMI_LCDIF_NOC_HURRY_MASK, 7)); + break; + case IMX8MP_HDMIBLK_PD_PAI: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); + break; + case IMX8MP_HDMIBLK_PD_PVI: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); + break; + case IMX8MP_HDMIBLK_PD_TRNG: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(2) | BIT(4) | BIT(5)); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, + BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | + BIT(18) | BIT(19) | BIT(20) | BIT(21)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(7) | BIT(10) | BIT(11)); + regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); + regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); + break; + case IMX8MP_HDMIBLK_PD_HDCP: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); + break; + case IMX8MP_HDMIBLK_PD_HRV: + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); + break; + default: + break; + } +} + +static void imx8mp_hdmi_blk_ctrl_power_off(struct imx8mp_blk_ctrl *bc, + struct imx8mp_blk_ctrl_domain *domain) +{ + switch (domain->id) { + case IMX8MP_HDMIBLK_PD_IRQSTEER: + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(9)); + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(16)); + break; + case IMX8MP_HDMIBLK_PD_LCDIF: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(4) | BIT(5) | BIT(6)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(11)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(16) | BIT(17) | BIT(18) | + BIT(19) | BIT(20)); + break; + case IMX8MP_HDMIBLK_PD_PAI: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(18)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(17)); + break; + case IMX8MP_HDMIBLK_PD_PVI: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(22)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(28)); + break; + case IMX8MP_HDMIBLK_PD_TRNG: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(20)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(27) | BIT(30)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX: + regmap_clear_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(1)); + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, + BIT(7) | BIT(10) | BIT(11)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, + BIT(12) | BIT(13) | BIT(14) | BIT(15) | BIT(16) | + BIT(18) | BIT(19) | BIT(20) | BIT(21)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(2) | BIT(4) | BIT(5)); + break; + case IMX8MP_HDMIBLK_PD_HDMI_TX_PHY: + regmap_set_bits(bc->regmap, HDMI_TX_CONTROL0, BIT(3)); + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(12)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(7)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(22) | BIT(24)); + break; + case IMX8MP_HDMIBLK_PD_HDCP: + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL0, BIT(11)); + break; + case IMX8MP_HDMIBLK_PD_HRV: + regmap_clear_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(15)); + regmap_clear_bits(bc->regmap, HDMI_RTX_CLK_CTL1, BIT(3) | BIT(4) | BIT(5)); + break; + default: + break; + } +} + +static int imx8mp_hdmi_power_notifier(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct imx8mp_blk_ctrl *bc = container_of(nb, struct imx8mp_blk_ctrl, + power_nb); + + if (action != GENPD_NOTIFY_ON) + return NOTIFY_OK; + + /* + * Contrary to other blk-ctrls the reset and clock don't clear when the + * power domain is powered down. To ensure the proper reset pulsing, + * first clear them all to asserted state, then enable the bus clocks + * and then release the ADB reset. + */ + regmap_write(bc->regmap, HDMI_RTX_RESET_CTL0, 0x0); + regmap_write(bc->regmap, HDMI_RTX_CLK_CTL0, 0x0); + regmap_write(bc->regmap, HDMI_RTX_CLK_CTL1, 0x0); + regmap_set_bits(bc->regmap, HDMI_RTX_CLK_CTL0, + BIT(0) | BIT(1) | BIT(10)); + regmap_set_bits(bc->regmap, HDMI_RTX_RESET_CTL0, BIT(0)); + + /* + * On power up we have no software backchannel to the GPC to + * wait for the ADB handshake to happen, so we just delay for a + * bit. On power down the GPC driver waits for the handshake. + */ + udelay(5); + + return NOTIFY_OK; +} + +static const struct imx8mp_blk_ctrl_domain_data imx8mp_hdmi_domain_data[] = { + [IMX8MP_HDMIBLK_PD_IRQSTEER] = { + .name = "hdmiblk-irqsteer", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "irqsteer", + }, + [IMX8MP_HDMIBLK_PD_LCDIF] = { + .name = "hdmiblk-lcdif", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "lcdif", + .path_names = (const char *[]){"lcdif-hdmi"}, + .num_paths = 1, + }, + [IMX8MP_HDMIBLK_PD_PAI] = { + .name = "hdmiblk-pai", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "pai", + }, + [IMX8MP_HDMIBLK_PD_PVI] = { + .name = "hdmiblk-pvi", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "pvi", + }, + [IMX8MP_HDMIBLK_PD_TRNG] = { + .name = "hdmiblk-trng", + .clk_names = (const char *[]){ "apb" }, + .num_clks = 1, + .gpc_name = "trng", + }, + [IMX8MP_HDMIBLK_PD_HDMI_TX] = { + .name = "hdmiblk-hdmi-tx", + .clk_names = (const char *[]){ "apb", "ref_266m" }, + .num_clks = 2, + .gpc_name = "hdmi-tx", + }, + [IMX8MP_HDMIBLK_PD_HDMI_TX_PHY] = { + .name = "hdmiblk-hdmi-tx-phy", + .clk_names = (const char *[]){ "apb", "ref_24m" }, + .num_clks = 2, + .gpc_name = "hdmi-tx-phy", + }, + [IMX8MP_HDMIBLK_PD_HRV] = { + .name = "hdmiblk-hrv", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "hrv", + .path_names = (const char *[]){"hrv"}, + .num_paths = 1, + }, + [IMX8MP_HDMIBLK_PD_HDCP] = { + .name = "hdmiblk-hdcp", + .clk_names = (const char *[]){ "axi", "apb" }, + .num_clks = 2, + .gpc_name = "hdcp", + .path_names = (const char *[]){"hdcp"}, + .num_paths = 1, + }, +}; + +static const struct imx8mp_blk_ctrl_data imx8mp_hdmi_blk_ctl_dev_data = { + .max_reg = 0x23c, + .power_on = imx8mp_hdmi_blk_ctrl_power_on, + .power_off = imx8mp_hdmi_blk_ctrl_power_off, + .power_notifier_fn = imx8mp_hdmi_power_notifier, + .domains = imx8mp_hdmi_domain_data, + .num_domains = ARRAY_SIZE(imx8mp_hdmi_domain_data), +}; + +static int imx8mp_blk_ctrl_power_on(struct generic_pm_domain *genpd) +{ + struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); + const struct imx8mp_blk_ctrl_domain_data *data = domain->data; + struct imx8mp_blk_ctrl *bc = domain->bc; + int ret; + + /* make sure bus domain is awake */ + ret = pm_runtime_resume_and_get(bc->bus_power_dev); + if (ret < 0) { + dev_err(bc->dev, "failed to power up bus domain\n"); + return ret; + } + + /* enable upstream clocks */ + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); + if (ret) { + dev_err(bc->dev, "failed to enable clocks\n"); + goto bus_put; + } + + /* domain specific blk-ctrl manipulation */ + bc->power_on(bc, domain); + + /* power up upstream GPC domain */ + ret = pm_runtime_resume_and_get(domain->power_dev); + if (ret < 0) { + dev_err(bc->dev, "failed to power up peripheral domain\n"); + goto clk_disable; + } + + ret = icc_bulk_set_bw(domain->num_paths, domain->paths); + if (ret) + dev_err(bc->dev, "failed to set icc bw\n"); + + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + return 0; + +clk_disable: + clk_bulk_disable_unprepare(data->num_clks, domain->clks); +bus_put: + pm_runtime_put(bc->bus_power_dev); + + return ret; +} + +static int imx8mp_blk_ctrl_power_off(struct generic_pm_domain *genpd) +{ + struct imx8mp_blk_ctrl_domain *domain = to_imx8mp_blk_ctrl_domain(genpd); + const struct imx8mp_blk_ctrl_domain_data *data = domain->data; + struct imx8mp_blk_ctrl *bc = domain->bc; + int ret; + + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); + if (ret) { + dev_err(bc->dev, "failed to enable clocks\n"); + return ret; + } + + /* domain specific blk-ctrl manipulation */ + bc->power_off(bc, domain); + + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + /* power down upstream GPC domain */ + pm_runtime_put(domain->power_dev); + + /* allow bus domain to suspend */ + pm_runtime_put(bc->bus_power_dev); + + return 0; +} + +static struct lock_class_key blk_ctrl_genpd_lock_class; + +static int imx8mp_blk_ctrl_probe(struct platform_device *pdev) +{ + const struct imx8mp_blk_ctrl_data *bc_data; + struct device *dev = &pdev->dev; + struct imx8mp_blk_ctrl *bc; + void __iomem *base; + int num_domains, i, ret; + + struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + }; + + bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); + if (!bc) + return -ENOMEM; + + bc->dev = dev; + + bc_data = of_device_get_match_data(dev); + num_domains = bc_data->num_domains; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + regmap_config.max_register = bc_data->max_reg; + bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(bc->regmap)) + return dev_err_probe(dev, PTR_ERR(bc->regmap), + "failed to init regmap\n"); + + bc->domains = devm_kcalloc(dev, num_domains, + sizeof(struct imx8mp_blk_ctrl_domain), + GFP_KERNEL); + if (!bc->domains) + return -ENOMEM; + + bc->onecell_data.num_domains = num_domains; + bc->onecell_data.domains = + devm_kcalloc(dev, num_domains, + sizeof(struct generic_pm_domain *), GFP_KERNEL); + if (!bc->onecell_data.domains) + return -ENOMEM; + + bc->bus_power_dev = dev_pm_domain_attach_by_name(dev, "bus"); + if (IS_ERR(bc->bus_power_dev)) + return dev_err_probe(dev, PTR_ERR(bc->bus_power_dev), + "failed to attach bus power domain\n"); + + bc->power_off = bc_data->power_off; + bc->power_on = bc_data->power_on; + + for (i = 0; i < num_domains; i++) { + const struct imx8mp_blk_ctrl_domain_data *data = &bc_data->domains[i]; + struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; + int j; + + domain->data = data; + domain->num_paths = data->num_paths; + + for (j = 0; j < data->num_clks; j++) + domain->clks[j].id = data->clk_names[j]; + + for (j = 0; j < data->num_paths; j++) { + domain->paths[j].name = data->path_names[j]; + /* Fake value for now, just let ICC could configure NoC mode/priority */ + domain->paths[j].avg_bw = 1; + domain->paths[j].peak_bw = 1; + } + + ret = devm_of_icc_bulk_get(dev, data->num_paths, domain->paths); + if (ret) { + if (ret != -EPROBE_DEFER) { + dev_warn_once(dev, "Could not get interconnect paths, NoC will stay unconfigured!\n"); + domain->num_paths = 0; + } else { + dev_err_probe(dev, ret, "failed to get noc entries\n"); + goto cleanup_pds; + } + } + + ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); + if (ret) { + dev_err_probe(dev, ret, "failed to get clock\n"); + goto cleanup_pds; + } + + domain->power_dev = + dev_pm_domain_attach_by_name(dev, data->gpc_name); + if (IS_ERR(domain->power_dev)) { + dev_err_probe(dev, PTR_ERR(domain->power_dev), + "failed to attach power domain %s\n", + data->gpc_name); + ret = PTR_ERR(domain->power_dev); + goto cleanup_pds; + } + + domain->genpd.name = data->name; + domain->genpd.power_on = imx8mp_blk_ctrl_power_on; + domain->genpd.power_off = imx8mp_blk_ctrl_power_off; + domain->bc = bc; + domain->id = i; + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err_probe(dev, ret, "failed to init power domain\n"); + dev_pm_domain_detach(domain->power_dev, true); + goto cleanup_pds; + } + + /* + * We use runtime PM to trigger power on/off of the upstream GPC + * domain, as a strict hierarchical parent/child power domain + * setup doesn't allow us to meet the sequencing requirements. + * This means we have nested locking of genpd locks, without the + * nesting being visible at the genpd level, so we need a + * separate lock class to make lockdep aware of the fact that + * this are separate domain locks that can be nested without a + * self-deadlock. + */ + lockdep_set_class(&domain->genpd.mlock, + &blk_ctrl_genpd_lock_class); + + bc->onecell_data.domains[i] = &domain->genpd; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); + if (ret) { + dev_err_probe(dev, ret, "failed to add power domain provider\n"); + goto cleanup_pds; + } + + bc->power_nb.notifier_call = bc_data->power_notifier_fn; + ret = dev_pm_genpd_add_notifier(bc->bus_power_dev, &bc->power_nb); + if (ret) { + dev_err_probe(dev, ret, "failed to add power notifier\n"); + goto cleanup_provider; + } + + if (bc_data->probe) { + ret = bc_data->probe(bc); + if (ret) + goto cleanup_provider; + } + + dev_set_drvdata(dev, bc); + + return 0; + +cleanup_provider: + of_genpd_del_provider(dev->of_node); +cleanup_pds: + for (i--; i >= 0; i--) { + pm_genpd_remove(&bc->domains[i].genpd); + dev_pm_domain_detach(bc->domains[i].power_dev, true); + } + + dev_pm_domain_detach(bc->bus_power_dev, true); + + return ret; +} + +static int imx8mp_blk_ctrl_remove(struct platform_device *pdev) +{ + struct imx8mp_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); + int i; + + of_genpd_del_provider(pdev->dev.of_node); + + for (i = 0; bc->onecell_data.num_domains; i++) { + struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; + + pm_genpd_remove(&domain->genpd); + dev_pm_domain_detach(domain->power_dev, true); + } + + dev_pm_genpd_remove_notifier(bc->bus_power_dev); + + dev_pm_domain_detach(bc->bus_power_dev, true); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int imx8mp_blk_ctrl_suspend(struct device *dev) +{ + struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); + int ret, i; + + /* + * This may look strange, but is done so the generic PM_SLEEP code + * can power down our domains and more importantly power them up again + * after resume, without tripping over our usage of runtime PM to + * control the upstream GPC domains. Things happen in the right order + * in the system suspend/resume paths due to the device parent/child + * hierarchy. + */ + ret = pm_runtime_get_sync(bc->bus_power_dev); + if (ret < 0) { + pm_runtime_put_noidle(bc->bus_power_dev); + return ret; + } + + for (i = 0; i < bc->onecell_data.num_domains; i++) { + struct imx8mp_blk_ctrl_domain *domain = &bc->domains[i]; + + ret = pm_runtime_get_sync(domain->power_dev); + if (ret < 0) { + pm_runtime_put_noidle(domain->power_dev); + goto out_fail; + } + } + + return 0; + +out_fail: + for (i--; i >= 0; i--) + pm_runtime_put(bc->domains[i].power_dev); + + pm_runtime_put(bc->bus_power_dev); + + return ret; +} + +static int imx8mp_blk_ctrl_resume(struct device *dev) +{ + struct imx8mp_blk_ctrl *bc = dev_get_drvdata(dev); + int i; + + for (i = 0; i < bc->onecell_data.num_domains; i++) + pm_runtime_put(bc->domains[i].power_dev); + + pm_runtime_put(bc->bus_power_dev); + + return 0; +} +#endif + +static const struct dev_pm_ops imx8mp_blk_ctrl_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(imx8mp_blk_ctrl_suspend, + imx8mp_blk_ctrl_resume) +}; + +static const struct of_device_id imx8mp_blk_ctrl_of_match[] = { + { + .compatible = "fsl,imx8mp-hsio-blk-ctrl", + .data = &imx8mp_hsio_blk_ctl_dev_data, + }, { + .compatible = "fsl,imx8mp-hdmi-blk-ctrl", + .data = &imx8mp_hdmi_blk_ctl_dev_data, + }, { + /* Sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx8mp_blk_ctrl_of_match); + +static struct platform_driver imx8mp_blk_ctrl_driver = { + .probe = imx8mp_blk_ctrl_probe, + .remove = imx8mp_blk_ctrl_remove, + .driver = { + .name = "imx8mp-blk-ctrl", + .pm = &imx8mp_blk_ctrl_pm_ops, + .of_match_table = imx8mp_blk_ctrl_of_match, + }, +}; +module_platform_driver(imx8mp_blk_ctrl_driver); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/imx/imx93-blk-ctrl.c b/drivers/pmdomain/imx/imx93-blk-ctrl.c new file mode 100644 index 000000000000..40bd90f8b977 --- /dev/null +++ b/drivers/pmdomain/imx/imx93-blk-ctrl.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2022 NXP, Peng Fan + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define BLK_SFT_RSTN 0x0 +#define BLK_CLK_EN 0x4 +#define BLK_MAX_CLKS 4 + +#define DOMAIN_MAX_CLKS 4 + +#define LCDIF_QOS_REG 0xC +#define LCDIF_DEFAULT_QOS_OFF 12 +#define LCDIF_CFG_QOS_OFF 8 + +#define PXP_QOS_REG 0x10 +#define PXP_R_DEFAULT_QOS_OFF 28 +#define PXP_R_CFG_QOS_OFF 24 +#define PXP_W_DEFAULT_QOS_OFF 20 +#define PXP_W_CFG_QOS_OFF 16 + +#define ISI_CACHE_REG 0x14 + +#define ISI_QOS_REG 0x1C +#define ISI_V_DEFAULT_QOS_OFF 28 +#define ISI_V_CFG_QOS_OFF 24 +#define ISI_U_DEFAULT_QOS_OFF 20 +#define ISI_U_CFG_QOS_OFF 16 +#define ISI_Y_R_DEFAULT_QOS_OFF 12 +#define ISI_Y_R_CFG_QOS_OFF 8 +#define ISI_Y_W_DEFAULT_QOS_OFF 4 +#define ISI_Y_W_CFG_QOS_OFF 0 + +#define PRIO_MASK 0xF + +#define PRIO(X) (X) + +struct imx93_blk_ctrl_domain; + +struct imx93_blk_ctrl { + struct device *dev; + struct regmap *regmap; + int num_clks; + struct clk_bulk_data clks[BLK_MAX_CLKS]; + struct imx93_blk_ctrl_domain *domains; + struct genpd_onecell_data onecell_data; +}; + +#define DOMAIN_MAX_QOS 4 + +struct imx93_blk_ctrl_qos { + u32 reg; + u32 cfg_off; + u32 default_prio; + u32 cfg_prio; +}; + +struct imx93_blk_ctrl_domain_data { + const char *name; + const char * const *clk_names; + int num_clks; + u32 rst_mask; + u32 clk_mask; + int num_qos; + struct imx93_blk_ctrl_qos qos[DOMAIN_MAX_QOS]; +}; + +struct imx93_blk_ctrl_domain { + struct generic_pm_domain genpd; + const struct imx93_blk_ctrl_domain_data *data; + struct clk_bulk_data clks[DOMAIN_MAX_CLKS]; + struct imx93_blk_ctrl *bc; +}; + +struct imx93_blk_ctrl_data { + const struct imx93_blk_ctrl_domain_data *domains; + int num_domains; + const char * const *clk_names; + int num_clks; + const struct regmap_access_table *reg_access_table; +}; + +static inline struct imx93_blk_ctrl_domain * +to_imx93_blk_ctrl_domain(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx93_blk_ctrl_domain, genpd); +} + +static int imx93_blk_ctrl_set_qos(struct imx93_blk_ctrl_domain *domain) +{ + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl *bc = domain->bc; + const struct imx93_blk_ctrl_qos *qos; + u32 val, mask; + int i; + + for (i = 0; i < data->num_qos; i++) { + qos = &data->qos[i]; + + mask = PRIO_MASK << qos->cfg_off; + mask |= PRIO_MASK << (qos->cfg_off + 4); + val = qos->cfg_prio << qos->cfg_off; + val |= qos->default_prio << (qos->cfg_off + 4); + + regmap_write_bits(bc->regmap, qos->reg, mask, val); + + dev_dbg(bc->dev, "data->qos[i].reg 0x%x 0x%x\n", qos->reg, val); + } + + return 0; +} + +static int imx93_blk_ctrl_power_on(struct generic_pm_domain *genpd) +{ + struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd); + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl *bc = domain->bc; + int ret; + + ret = clk_bulk_prepare_enable(bc->num_clks, bc->clks); + if (ret) { + dev_err(bc->dev, "failed to enable bus clocks\n"); + return ret; + } + + ret = clk_bulk_prepare_enable(data->num_clks, domain->clks); + if (ret) { + clk_bulk_disable_unprepare(bc->num_clks, bc->clks); + dev_err(bc->dev, "failed to enable clocks\n"); + return ret; + } + + ret = pm_runtime_get_sync(bc->dev); + if (ret < 0) { + pm_runtime_put_noidle(bc->dev); + dev_err(bc->dev, "failed to power up domain\n"); + goto disable_clk; + } + + /* ungate clk */ + regmap_clear_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + /* release reset */ + regmap_set_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); + + dev_dbg(bc->dev, "pd_on: name: %s\n", genpd->name); + + return imx93_blk_ctrl_set_qos(domain); + +disable_clk: + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + clk_bulk_disable_unprepare(bc->num_clks, bc->clks); + + return ret; +} + +static int imx93_blk_ctrl_power_off(struct generic_pm_domain *genpd) +{ + struct imx93_blk_ctrl_domain *domain = to_imx93_blk_ctrl_domain(genpd); + const struct imx93_blk_ctrl_domain_data *data = domain->data; + struct imx93_blk_ctrl *bc = domain->bc; + + dev_dbg(bc->dev, "pd_off: name: %s\n", genpd->name); + + regmap_clear_bits(bc->regmap, BLK_SFT_RSTN, data->rst_mask); + regmap_set_bits(bc->regmap, BLK_CLK_EN, data->clk_mask); + + pm_runtime_put(bc->dev); + + clk_bulk_disable_unprepare(data->num_clks, domain->clks); + + clk_bulk_disable_unprepare(bc->num_clks, bc->clks); + + return 0; +} + +static struct lock_class_key blk_ctrl_genpd_lock_class; + +static int imx93_blk_ctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct imx93_blk_ctrl_data *bc_data = of_device_get_match_data(dev); + struct imx93_blk_ctrl *bc; + void __iomem *base; + int i, ret; + + struct regmap_config regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .rd_table = bc_data->reg_access_table, + .wr_table = bc_data->reg_access_table, + .max_register = SZ_4K, + }; + + bc = devm_kzalloc(dev, sizeof(*bc), GFP_KERNEL); + if (!bc) + return -ENOMEM; + + bc->dev = dev; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + bc->regmap = devm_regmap_init_mmio(dev, base, ®map_config); + if (IS_ERR(bc->regmap)) + return dev_err_probe(dev, PTR_ERR(bc->regmap), + "failed to init regmap\n"); + + bc->domains = devm_kcalloc(dev, bc_data->num_domains, + sizeof(struct imx93_blk_ctrl_domain), + GFP_KERNEL); + if (!bc->domains) + return -ENOMEM; + + bc->onecell_data.num_domains = bc_data->num_domains; + bc->onecell_data.domains = + devm_kcalloc(dev, bc_data->num_domains, + sizeof(struct generic_pm_domain *), GFP_KERNEL); + if (!bc->onecell_data.domains) + return -ENOMEM; + + for (i = 0; i < bc_data->num_clks; i++) + bc->clks[i].id = bc_data->clk_names[i]; + bc->num_clks = bc_data->num_clks; + + ret = devm_clk_bulk_get(dev, bc->num_clks, bc->clks); + if (ret) { + dev_err_probe(dev, ret, "failed to get bus clock\n"); + return ret; + } + + for (i = 0; i < bc_data->num_domains; i++) { + const struct imx93_blk_ctrl_domain_data *data = &bc_data->domains[i]; + struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; + int j; + + domain->data = data; + + for (j = 0; j < data->num_clks; j++) + domain->clks[j].id = data->clk_names[j]; + + ret = devm_clk_bulk_get(dev, data->num_clks, domain->clks); + if (ret) { + dev_err_probe(dev, ret, "failed to get clock\n"); + goto cleanup_pds; + } + + domain->genpd.name = data->name; + domain->genpd.power_on = imx93_blk_ctrl_power_on; + domain->genpd.power_off = imx93_blk_ctrl_power_off; + domain->bc = bc; + + ret = pm_genpd_init(&domain->genpd, NULL, true); + if (ret) { + dev_err_probe(dev, ret, "failed to init power domain\n"); + goto cleanup_pds; + } + + /* + * We use runtime PM to trigger power on/off of the upstream GPC + * domain, as a strict hierarchical parent/child power domain + * setup doesn't allow us to meet the sequencing requirements. + * This means we have nested locking of genpd locks, without the + * nesting being visible at the genpd level, so we need a + * separate lock class to make lockdep aware of the fact that + * this are separate domain locks that can be nested without a + * self-deadlock. + */ + lockdep_set_class(&domain->genpd.mlock, + &blk_ctrl_genpd_lock_class); + + bc->onecell_data.domains[i] = &domain->genpd; + } + + pm_runtime_enable(dev); + + ret = of_genpd_add_provider_onecell(dev->of_node, &bc->onecell_data); + if (ret) { + dev_err_probe(dev, ret, "failed to add power domain provider\n"); + goto cleanup_pds; + } + + dev_set_drvdata(dev, bc); + + return 0; + +cleanup_pds: + for (i--; i >= 0; i--) + pm_genpd_remove(&bc->domains[i].genpd); + + return ret; +} + +static int imx93_blk_ctrl_remove(struct platform_device *pdev) +{ + struct imx93_blk_ctrl *bc = dev_get_drvdata(&pdev->dev); + int i; + + of_genpd_del_provider(pdev->dev.of_node); + + for (i = 0; bc->onecell_data.num_domains; i++) { + struct imx93_blk_ctrl_domain *domain = &bc->domains[i]; + + pm_genpd_remove(&domain->genpd); + } + + return 0; +} + +static const struct imx93_blk_ctrl_domain_data imx93_media_blk_ctl_domain_data[] = { + [IMX93_MEDIABLK_PD_MIPI_DSI] = { + .name = "mediablk-mipi-dsi", + .clk_names = (const char *[]){ "dsi" }, + .num_clks = 1, + .rst_mask = BIT(11) | BIT(12), + .clk_mask = BIT(11) | BIT(12), + }, + [IMX93_MEDIABLK_PD_MIPI_CSI] = { + .name = "mediablk-mipi-csi", + .clk_names = (const char *[]){ "cam", "csi" }, + .num_clks = 2, + .rst_mask = BIT(9) | BIT(10), + .clk_mask = BIT(9) | BIT(10), + }, + [IMX93_MEDIABLK_PD_PXP] = { + .name = "mediablk-pxp", + .clk_names = (const char *[]){ "pxp" }, + .num_clks = 1, + .rst_mask = BIT(7) | BIT(8), + .clk_mask = BIT(7) | BIT(8), + .num_qos = 2, + .qos = { + { + .reg = PXP_QOS_REG, + .cfg_off = PXP_R_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(6), + }, { + .reg = PXP_QOS_REG, + .cfg_off = PXP_W_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(6), + } + } + }, + [IMX93_MEDIABLK_PD_LCDIF] = { + .name = "mediablk-lcdif", + .clk_names = (const char *[]){ "disp", "lcdif" }, + .num_clks = 2, + .rst_mask = BIT(4) | BIT(5) | BIT(6), + .clk_mask = BIT(4) | BIT(5) | BIT(6), + .num_qos = 1, + .qos = { + { + .reg = LCDIF_QOS_REG, + .cfg_off = LCDIF_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(7), + } + } + }, + [IMX93_MEDIABLK_PD_ISI] = { + .name = "mediablk-isi", + .clk_names = (const char *[]){ "isi" }, + .num_clks = 1, + .rst_mask = BIT(2) | BIT(3), + .clk_mask = BIT(2) | BIT(3), + .num_qos = 4, + .qos = { + { + .reg = ISI_QOS_REG, + .cfg_off = ISI_Y_W_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(7), + }, { + .reg = ISI_QOS_REG, + .cfg_off = ISI_Y_R_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(7), + }, { + .reg = ISI_QOS_REG, + .cfg_off = ISI_U_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(7), + }, { + .reg = ISI_QOS_REG, + .cfg_off = ISI_V_CFG_QOS_OFF, + .default_prio = PRIO(3), + .cfg_prio = PRIO(7), + } + } + }, +}; + +static const struct regmap_range imx93_media_blk_ctl_yes_ranges[] = { + regmap_reg_range(BLK_SFT_RSTN, BLK_CLK_EN), + regmap_reg_range(LCDIF_QOS_REG, ISI_CACHE_REG), + regmap_reg_range(ISI_QOS_REG, ISI_QOS_REG), +}; + +static const struct regmap_access_table imx93_media_blk_ctl_access_table = { + .yes_ranges = imx93_media_blk_ctl_yes_ranges, + .n_yes_ranges = ARRAY_SIZE(imx93_media_blk_ctl_yes_ranges), +}; + +static const struct imx93_blk_ctrl_data imx93_media_blk_ctl_dev_data = { + .domains = imx93_media_blk_ctl_domain_data, + .num_domains = ARRAY_SIZE(imx93_media_blk_ctl_domain_data), + .clk_names = (const char *[]){ "axi", "apb", "nic", }, + .num_clks = 3, + .reg_access_table = &imx93_media_blk_ctl_access_table, +}; + +static const struct of_device_id imx93_blk_ctrl_of_match[] = { + { + .compatible = "fsl,imx93-media-blk-ctrl", + .data = &imx93_media_blk_ctl_dev_data + }, { + /* Sentinel */ + } +}; +MODULE_DEVICE_TABLE(of, imx93_blk_ctrl_of_match); + +static struct platform_driver imx93_blk_ctrl_driver = { + .probe = imx93_blk_ctrl_probe, + .remove = imx93_blk_ctrl_remove, + .driver = { + .name = "imx93-blk-ctrl", + .of_match_table = imx93_blk_ctrl_of_match, + }, +}; +module_platform_driver(imx93_blk_ctrl_driver); + +MODULE_AUTHOR("Peng Fan "); +MODULE_DESCRIPTION("i.MX93 BLK CTRL driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/imx/imx93-pd.c b/drivers/pmdomain/imx/imx93-pd.c new file mode 100644 index 000000000000..b9e60d136875 --- /dev/null +++ b/drivers/pmdomain/imx/imx93-pd.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2022 NXP + */ + +#include +#include +#include +#include +#include +#include +#include + +#define MIX_SLICE_SW_CTRL_OFF 0x20 +#define SLICE_SW_CTRL_PSW_CTRL_OFF_MASK BIT(4) +#define SLICE_SW_CTRL_PDN_SOFT_MASK BIT(31) + +#define MIX_FUNC_STAT_OFF 0xB4 + +#define FUNC_STAT_PSW_STAT_MASK BIT(0) +#define FUNC_STAT_RST_STAT_MASK BIT(2) +#define FUNC_STAT_ISO_STAT_MASK BIT(4) + +struct imx93_power_domain { + struct generic_pm_domain genpd; + struct device *dev; + void __iomem *addr; + struct clk_bulk_data *clks; + int num_clks; + bool init_off; +}; + +#define to_imx93_pd(_genpd) container_of(_genpd, struct imx93_power_domain, genpd) + +static int imx93_pd_on(struct generic_pm_domain *genpd) +{ + struct imx93_power_domain *domain = to_imx93_pd(genpd); + void __iomem *addr = domain->addr; + u32 val; + int ret; + + ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); + if (ret) { + dev_err(domain->dev, "failed to enable clocks for domain: %s\n", genpd->name); + return ret; + } + + val = readl(addr + MIX_SLICE_SW_CTRL_OFF); + val &= ~SLICE_SW_CTRL_PDN_SOFT_MASK; + writel(val, addr + MIX_SLICE_SW_CTRL_OFF); + + ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, + !(val & FUNC_STAT_ISO_STAT_MASK), 1, 10000); + if (ret) { + dev_err(domain->dev, "pd_on timeout: name: %s, stat: %x\n", genpd->name, val); + return ret; + } + + return 0; +} + +static int imx93_pd_off(struct generic_pm_domain *genpd) +{ + struct imx93_power_domain *domain = to_imx93_pd(genpd); + void __iomem *addr = domain->addr; + int ret; + u32 val; + + /* Power off MIX */ + val = readl(addr + MIX_SLICE_SW_CTRL_OFF); + val |= SLICE_SW_CTRL_PDN_SOFT_MASK; + writel(val, addr + MIX_SLICE_SW_CTRL_OFF); + + ret = readl_poll_timeout(addr + MIX_FUNC_STAT_OFF, val, + val & FUNC_STAT_PSW_STAT_MASK, 1, 1000); + if (ret) { + dev_err(domain->dev, "pd_off timeout: name: %s, stat: %x\n", genpd->name, val); + return ret; + } + + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + return 0; +}; + +static int imx93_pd_remove(struct platform_device *pdev) +{ + struct imx93_power_domain *domain = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + + if (!domain->init_off) + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + of_genpd_del_provider(np); + pm_genpd_remove(&domain->genpd); + + return 0; +} + +static int imx93_pd_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct imx93_power_domain *domain; + int ret; + + domain = devm_kzalloc(dev, sizeof(*domain), GFP_KERNEL); + if (!domain) + return -ENOMEM; + + domain->addr = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(domain->addr)) + return PTR_ERR(domain->addr); + + domain->num_clks = devm_clk_bulk_get_all(dev, &domain->clks); + if (domain->num_clks < 0) + return dev_err_probe(dev, domain->num_clks, "Failed to get domain's clocks\n"); + + domain->genpd.name = dev_name(dev); + domain->genpd.power_off = imx93_pd_off; + domain->genpd.power_on = imx93_pd_on; + domain->dev = dev; + + domain->init_off = readl(domain->addr + MIX_FUNC_STAT_OFF) & FUNC_STAT_ISO_STAT_MASK; + /* Just to sync the status of hardware */ + if (!domain->init_off) { + ret = clk_bulk_prepare_enable(domain->num_clks, domain->clks); + if (ret) { + dev_err(domain->dev, "failed to enable clocks for domain: %s\n", + domain->genpd.name); + return ret; + } + } + + ret = pm_genpd_init(&domain->genpd, NULL, domain->init_off); + if (ret) + goto err_clk_unprepare; + + platform_set_drvdata(pdev, domain); + + ret = of_genpd_add_provider_simple(np, &domain->genpd); + if (ret) + goto err_genpd_remove; + + return 0; + +err_genpd_remove: + pm_genpd_remove(&domain->genpd); + +err_clk_unprepare: + if (!domain->init_off) + clk_bulk_disable_unprepare(domain->num_clks, domain->clks); + + return ret; +} + +static const struct of_device_id imx93_pd_ids[] = { + { .compatible = "fsl,imx93-src-slice" }, + { } +}; +MODULE_DEVICE_TABLE(of, imx93_pd_ids); + +static struct platform_driver imx93_power_domain_driver = { + .driver = { + .name = "imx93_power_domain", + .of_match_table = imx93_pd_ids, + }, + .probe = imx93_pd_probe, + .remove = imx93_pd_remove, +}; +module_platform_driver(imx93_power_domain_driver); + +MODULE_AUTHOR("Peng Fan "); +MODULE_DESCRIPTION("NXP i.MX93 power domain driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/imx/scu-pd.c b/drivers/pmdomain/imx/scu-pd.c new file mode 100644 index 000000000000..2f693b67ddb4 --- /dev/null +++ b/drivers/pmdomain/imx/scu-pd.c @@ -0,0 +1,550 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017-2018 NXP + * Dong Aisheng + * + * Implementation of the SCU based Power Domains + * + * NOTE: a better implementation suggested by Ulf Hansson is using a + * single global power domain and implement the ->attach|detach_dev() + * callback for the genpd and use the regular of_genpd_add_provider_simple(). + * From within the ->attach_dev(), we could get the OF node for + * the device that is being attached and then parse the power-domain + * cell containing the "resource id" and store that in the per device + * struct generic_pm_domain_data (we have void pointer there for + * storing these kind of things). + * + * Additionally, we need to implement the ->stop() and ->start() + * callbacks of genpd, which is where you "power on/off" devices, + * rather than using the above ->power_on|off() callbacks. + * + * However, there're two known issues: + * 1. The ->attach_dev() of power domain infrastructure still does + * not support multi domains case as the struct device *dev passed + * in is a virtual PD device, it does not help for parsing the real + * device resource id from device tree, so it's unware of which + * real sub power domain of device should be attached. + * + * The framework needs some proper extension to support multi power + * domain cases. + * + * Update: Genpd assigns the ->of_node for the virtual device before it + * invokes ->attach_dev() callback, hence parsing for device resources via + * DT should work fine. + * + * 2. It also breaks most of current drivers as the driver probe sequence + * behavior changed if removing ->power_on|off() callback and use + * ->start() and ->stop() instead. genpd_dev_pm_attach will only power + * up the domain and attach device, but will not call .start() which + * relies on device runtime pm. That means the device power is still + * not up before running driver probe function. For SCU enabled + * platforms, all device drivers accessing registers/clock without power + * domain enabled will trigger a HW access error. That means we need fix + * most drivers probe sequence with proper runtime pm. + * + * Update: Runtime PM support isn't necessary. Instead, this can easily be + * fixed in drivers by adding a call to dev_pm_domain_start() during probe. + * + * In summary, the second part needs to be addressed via minor updates to the + * relevant drivers, before the "single global power domain" model can be used. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SCU Power Mode Protocol definition */ +struct imx_sc_msg_req_set_resource_power_mode { + struct imx_sc_rpc_msg hdr; + u16 resource; + u8 mode; +} __packed __aligned(4); + +struct req_get_resource_mode { + u16 resource; +}; + +struct resp_get_resource_mode { + u8 mode; +}; + +struct imx_sc_msg_req_get_resource_power_mode { + struct imx_sc_rpc_msg hdr; + union { + struct req_get_resource_mode req; + struct resp_get_resource_mode resp; + } data; +} __packed __aligned(4); + +#define IMX_SCU_PD_NAME_SIZE 20 +struct imx_sc_pm_domain { + struct generic_pm_domain pd; + char name[IMX_SCU_PD_NAME_SIZE]; + u32 rsrc; +}; + +struct imx_sc_pd_range { + char *name; + u32 rsrc; + u8 num; + + /* add domain index */ + bool postfix; + u8 start_from; +}; + +struct imx_sc_pd_soc { + const struct imx_sc_pd_range *pd_ranges; + u8 num_ranges; +}; + +static int imx_con_rsrc; + +/* Align with the IMX_SC_PM_PW_MODE_[OFF,STBY,LP,ON] macros */ +static const char * const imx_sc_pm_mode[] = { + "IMX_SC_PM_PW_MODE_OFF", + "IMX_SC_PM_PW_MODE_STBY", + "IMX_SC_PM_PW_MODE_LP", + "IMX_SC_PM_PW_MODE_ON" +}; + +static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { + /* LSIO SS */ + { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "mu_a", IMX_SC_R_MU_0A, 14, true, 0 }, + { "mu_b", IMX_SC_R_MU_5B, 9, true, 5 }, + + /* CONN SS */ + { "usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb1phy", IMX_SC_R_USB_1_PHY, 1, false, 0}, + { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "nand", IMX_SC_R_NAND, 1, false, 0 }, + { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, + + /* AUDIO SS */ + { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "audio-clk-1", IMX_SC_R_AUDIO_CLK_1, 1, false, 0 }, + { "mclk-out-0", IMX_SC_R_MCLK_OUT_0, 1, false, 0 }, + { "mclk-out-1", IMX_SC_R_MCLK_OUT_1, 1, false, 0 }, + { "dma0-ch", IMX_SC_R_DMA_0_CH0, 32, true, 0 }, + { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "dma2-ch", IMX_SC_R_DMA_2_CH0, 32, true, 0 }, + { "dma3-ch", IMX_SC_R_DMA_3_CH0, 32, true, 0 }, + { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "esai1", IMX_SC_R_ESAI_1, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "spdif1", IMX_SC_R_SPDIF_1, 1, false, 0 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "sai3", IMX_SC_R_SAI_3, 1, false, 0 }, + { "sai4", IMX_SC_R_SAI_4, 1, false, 0 }, + { "sai5", IMX_SC_R_SAI_5, 1, false, 0 }, + { "sai6", IMX_SC_R_SAI_6, 1, false, 0 }, + { "sai7", IMX_SC_R_SAI_7, 1, false, 0 }, + { "amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + + /* DMA SS */ + { "can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "lpi2c", IMX_SC_R_I2C_0, 5, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 2, true, 0 }, + { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "lcd-pll", IMX_SC_R_ELCDIF_PLL, 1, true, 0 }, + { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 5, true, 0 }, + { "sim", IMX_SC_R_EMVSIM_0, 2, true, 0 }, + { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + { "irqstr_dsp", IMX_SC_R_IRQSTR_DSP, 1, false, 0 }, + + /* VPU SS */ + { "vpu", IMX_SC_R_VPU, 1, false, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, + { "vpu-enc1", IMX_SC_R_VPU_ENC_1, 1, false, 0 }, + { "vpu-mu0", IMX_SC_R_VPU_MU_0, 1, false, 0 }, + { "vpu-mu1", IMX_SC_R_VPU_MU_1, 1, false, 0 }, + { "vpu-mu2", IMX_SC_R_VPU_MU_2, 1, false, 0 }, + + /* GPU SS */ + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, + { "gpu1-pid", IMX_SC_R_GPU_1_PID0, 4, true, 0 }, + + + /* HSIO SS */ + { "pcie-a", IMX_SC_R_PCIE_A, 1, false, 0 }, + { "serdes-0", IMX_SC_R_SERDES_0, 1, false, 0 }, + { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "sata-0", IMX_SC_R_SATA_0, 1, false, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, + + /* MIPI SS */ + { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, + + { "mipi1", IMX_SC_R_MIPI_1, 1, false, 0 }, + { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, false, 0 }, + { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, true, 0 }, + + /* LVDS SS */ + { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, + { "lvds0-pwm", IMX_SC_R_LVDS_0_PWM_0, 1, false, 0 }, + { "lvds0-lpi2c", IMX_SC_R_LVDS_0_I2C_0, 2, true, 0 }, + { "lvds1", IMX_SC_R_LVDS_1, 1, false, 0 }, + { "lvds1-pwm", IMX_SC_R_LVDS_1_PWM_0, 1, false, 0 }, + { "lvds1-lpi2c", IMX_SC_R_LVDS_1_I2C_0, 2, true, 0 }, + + { "mipi1", IMX_SC_R_MIPI_1, 1, 0 }, + { "mipi1-pwm0", IMX_SC_R_MIPI_1_PWM_0, 1, 0 }, + { "mipi1-i2c", IMX_SC_R_MIPI_1_I2C_0, 2, 1 }, + { "lvds1", IMX_SC_R_LVDS_1, 1, 0 }, + + /* DC SS */ + { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, + { "dc0-video", IMX_SC_R_DC_0_VIDEO0, 2, true, 0 }, + + { "dc1", IMX_SC_R_DC_1, 1, false, 0 }, + { "dc1-pll", IMX_SC_R_DC_1_PLL_0, 2, true, 0 }, + { "dc1-video", IMX_SC_R_DC_1_VIDEO0, 2, true, 0 }, + + /* CM40 SS */ + { "cm40-i2c", IMX_SC_R_M4_0_I2C, 1, false, 0 }, + { "cm40-intmux", IMX_SC_R_M4_0_INTMUX, 1, false, 0 }, + { "cm40-pid", IMX_SC_R_M4_0_PID0, 5, true, 0}, + { "cm40-mu-a1", IMX_SC_R_M4_0_MU_1A, 1, false, 0}, + { "cm40-lpuart", IMX_SC_R_M4_0_UART, 1, false, 0}, + + /* CM41 SS */ + { "cm41-i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, + { "cm41-intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, + { "cm41-pid", IMX_SC_R_M4_1_PID0, 5, true, 0}, + { "cm41-mu-a1", IMX_SC_R_M4_1_MU_1A, 1, false, 0}, + { "cm41-lpuart", IMX_SC_R_M4_1_UART, 1, false, 0}, + + /* CM41 SS */ + { "cm41_i2c", IMX_SC_R_M4_1_I2C, 1, false, 0 }, + { "cm41_intmux", IMX_SC_R_M4_1_INTMUX, 1, false, 0 }, + + /* DB SS */ + { "perf", IMX_SC_R_PERF, 1, false, 0}, + + /* IMAGE SS */ + { "img-jpegdec-mp", IMX_SC_R_MJPEG_DEC_MP, 1, false, 0 }, + { "img-jpegdec-s0", IMX_SC_R_MJPEG_DEC_S0, 4, true, 0 }, + { "img-jpegenc-mp", IMX_SC_R_MJPEG_ENC_MP, 1, false, 0 }, + { "img-jpegenc-s0", IMX_SC_R_MJPEG_ENC_S0, 4, true, 0 }, + + /* SECO SS */ + { "seco_mu", IMX_SC_R_SECO_MU_2, 3, true, 2}, + + /* V2X SS */ + { "v2x_mu", IMX_SC_R_V2X_MU_0, 2, true, 0}, + { "v2x_mu", IMX_SC_R_V2X_MU_2, 1, true, 2}, + { "v2x_mu", IMX_SC_R_V2X_MU_3, 2, true, 3}, + { "img-pdma", IMX_SC_R_ISI_CH0, 8, true, 0 }, + { "img-csi0", IMX_SC_R_CSI_0, 1, false, 0 }, + { "img-csi0-i2c0", IMX_SC_R_CSI_0_I2C_0, 1, false, 0 }, + { "img-csi0-pwm0", IMX_SC_R_CSI_0_PWM_0, 1, false, 0 }, + { "img-csi1", IMX_SC_R_CSI_1, 1, false, 0 }, + { "img-csi1-i2c0", IMX_SC_R_CSI_1_I2C_0, 1, false, 0 }, + { "img-csi1-pwm0", IMX_SC_R_CSI_1_PWM_0, 1, false, 0 }, + { "img-parallel", IMX_SC_R_PI_0, 1, false, 0 }, + { "img-parallel-i2c0", IMX_SC_R_PI_0_I2C_0, 1, false, 0 }, + { "img-parallel-pwm0", IMX_SC_R_PI_0_PWM_0, 2, true, 0 }, + { "img-parallel-pll", IMX_SC_R_PI_0_PLL, 1, false, 0 }, + + /* HDMI TX SS */ + { "hdmi-tx", IMX_SC_R_HDMI, 1, false, 0}, + { "hdmi-tx-i2s", IMX_SC_R_HDMI_I2S, 1, false, 0}, + { "hdmi-tx-i2c0", IMX_SC_R_HDMI_I2C_0, 1, false, 0}, + { "hdmi-tx-pll0", IMX_SC_R_HDMI_PLL_0, 1, false, 0}, + { "hdmi-tx-pll1", IMX_SC_R_HDMI_PLL_1, 1, false, 0}, + + /* HDMI RX SS */ + { "hdmi-rx", IMX_SC_R_HDMI_RX, 1, false, 0}, + { "hdmi-rx-pwm", IMX_SC_R_HDMI_RX_PWM_0, 1, false, 0}, + { "hdmi-rx-i2c0", IMX_SC_R_HDMI_RX_I2C_0, 1, false, 0}, + { "hdmi-rx-bypass", IMX_SC_R_HDMI_RX_BYPASS, 1, false, 0}, + + /* SECURITY SS */ + { "sec-jr", IMX_SC_R_CAAM_JR2, 2, true, 2}, + + /* BOARD SS */ + { "board", IMX_SC_R_BOARD_R0, 8, true, 0}, +}; + +static const struct imx_sc_pd_soc imx8qxp_scu_pd = { + .pd_ranges = imx8qxp_scu_pd_ranges, + .num_ranges = ARRAY_SIZE(imx8qxp_scu_pd_ranges), +}; + +static struct imx_sc_ipc *pm_ipc_handle; + +static inline struct imx_sc_pm_domain * +to_imx_sc_pd(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct imx_sc_pm_domain, pd); +} + +static void imx_sc_pd_get_console_rsrc(void) +{ + struct of_phandle_args specs; + int ret; + + if (!of_stdout) + return; + + ret = of_parse_phandle_with_args(of_stdout, "power-domains", + "#power-domain-cells", + 0, &specs); + if (ret) + return; + + imx_con_rsrc = specs.args[0]; +} + +static int imx_sc_get_pd_power(struct device *dev, u32 rsrc) +{ + struct imx_sc_msg_req_get_resource_power_mode msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_GET_RESOURCE_POWER_MODE; + hdr->size = 2; + + msg.data.req.resource = rsrc; + + ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); + if (ret) + dev_err(dev, "failed to get power resource %d mode, ret %d\n", + rsrc, ret); + + return msg.data.resp.mode; +} + +static int imx_sc_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + struct imx_sc_msg_req_set_resource_power_mode msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + struct imx_sc_pm_domain *pd; + int ret; + + pd = to_imx_sc_pd(domain); + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_SET_RESOURCE_POWER_MODE; + hdr->size = 2; + + msg.resource = pd->rsrc; + msg.mode = power_on ? IMX_SC_PM_PW_MODE_ON : IMX_SC_PM_PW_MODE_LP; + + /* keep uart console power on for no_console_suspend */ + if (imx_con_rsrc == pd->rsrc && !console_suspend_enabled && !power_on) + return -EBUSY; + + ret = imx_scu_call_rpc(pm_ipc_handle, &msg, true); + if (ret) + dev_err(&domain->dev, "failed to power %s resource %d ret %d\n", + power_on ? "up" : "off", pd->rsrc, ret); + + return ret; +} + +static int imx_sc_pd_power_on(struct generic_pm_domain *domain) +{ + return imx_sc_pd_power(domain, true); +} + +static int imx_sc_pd_power_off(struct generic_pm_domain *domain) +{ + return imx_sc_pd_power(domain, false); +} + +static struct generic_pm_domain *imx_scu_pd_xlate(struct of_phandle_args *spec, + void *data) +{ + struct generic_pm_domain *domain = ERR_PTR(-ENOENT); + struct genpd_onecell_data *pd_data = data; + unsigned int i; + + for (i = 0; i < pd_data->num_domains; i++) { + struct imx_sc_pm_domain *sc_pd; + + sc_pd = to_imx_sc_pd(pd_data->domains[i]); + if (sc_pd->rsrc == spec->args[0]) { + domain = &sc_pd->pd; + break; + } + } + + return domain; +} + +static struct imx_sc_pm_domain * +imx_scu_add_pm_domain(struct device *dev, int idx, + const struct imx_sc_pd_range *pd_ranges) +{ + struct imx_sc_pm_domain *sc_pd; + bool is_off; + int mode, ret; + + if (!imx_sc_rm_is_resource_owned(pm_ipc_handle, pd_ranges->rsrc + idx)) + return NULL; + + sc_pd = devm_kzalloc(dev, sizeof(*sc_pd), GFP_KERNEL); + if (!sc_pd) + return ERR_PTR(-ENOMEM); + + sc_pd->rsrc = pd_ranges->rsrc + idx; + sc_pd->pd.power_off = imx_sc_pd_power_off; + sc_pd->pd.power_on = imx_sc_pd_power_on; + + if (pd_ranges->postfix) + snprintf(sc_pd->name, sizeof(sc_pd->name), + "%s%i", pd_ranges->name, pd_ranges->start_from + idx); + else + snprintf(sc_pd->name, sizeof(sc_pd->name), + "%s", pd_ranges->name); + + sc_pd->pd.name = sc_pd->name; + if (imx_con_rsrc == sc_pd->rsrc) + sc_pd->pd.flags = GENPD_FLAG_RPM_ALWAYS_ON; + + mode = imx_sc_get_pd_power(dev, pd_ranges->rsrc + idx); + if (mode == IMX_SC_PM_PW_MODE_ON) + is_off = false; + else + is_off = true; + + dev_dbg(dev, "%s : %s\n", sc_pd->name, imx_sc_pm_mode[mode]); + + if (sc_pd->rsrc >= IMX_SC_R_LAST) { + dev_warn(dev, "invalid pd %s rsrc id %d found", + sc_pd->name, sc_pd->rsrc); + + devm_kfree(dev, sc_pd); + return NULL; + } + + ret = pm_genpd_init(&sc_pd->pd, NULL, is_off); + if (ret) { + dev_warn(dev, "failed to init pd %s rsrc id %d", + sc_pd->name, sc_pd->rsrc); + devm_kfree(dev, sc_pd); + return NULL; + } + + return sc_pd; +} + +static int imx_scu_init_pm_domains(struct device *dev, + const struct imx_sc_pd_soc *pd_soc) +{ + const struct imx_sc_pd_range *pd_ranges = pd_soc->pd_ranges; + struct generic_pm_domain **domains; + struct genpd_onecell_data *pd_data; + struct imx_sc_pm_domain *sc_pd; + u32 count = 0; + int i, j; + + for (i = 0; i < pd_soc->num_ranges; i++) + count += pd_ranges[i].num; + + domains = devm_kcalloc(dev, count, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + pd_data = devm_kzalloc(dev, sizeof(*pd_data), GFP_KERNEL); + if (!pd_data) + return -ENOMEM; + + count = 0; + for (i = 0; i < pd_soc->num_ranges; i++) { + for (j = 0; j < pd_ranges[i].num; j++) { + sc_pd = imx_scu_add_pm_domain(dev, j, &pd_ranges[i]); + if (IS_ERR_OR_NULL(sc_pd)) + continue; + + domains[count++] = &sc_pd->pd; + dev_dbg(dev, "added power domain %s\n", sc_pd->pd.name); + } + } + + pd_data->domains = domains; + pd_data->num_domains = count; + pd_data->xlate = imx_scu_pd_xlate; + + of_genpd_add_provider_onecell(dev->of_node, pd_data); + + return 0; +} + +static int imx_sc_pd_probe(struct platform_device *pdev) +{ + const struct imx_sc_pd_soc *pd_soc; + int ret; + + ret = imx_scu_get_handle(&pm_ipc_handle); + if (ret) + return ret; + + pd_soc = of_device_get_match_data(&pdev->dev); + if (!pd_soc) + return -ENODEV; + + imx_sc_pd_get_console_rsrc(); + + return imx_scu_init_pm_domains(&pdev->dev, pd_soc); +} + +static const struct of_device_id imx_sc_pd_match[] = { + { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, + { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, + { /* sentinel */ } +}; + +static struct platform_driver imx_sc_pd_driver = { + .driver = { + .name = "imx-scu-pd", + .of_match_table = imx_sc_pd_match, + .suppress_bind_attrs = true, + }, + .probe = imx_sc_pd_probe, +}; +builtin_platform_driver(imx_sc_pd_driver); + +MODULE_AUTHOR("Dong Aisheng "); +MODULE_DESCRIPTION("IMX SCU Power Domain driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/mediatek/Makefile b/drivers/pmdomain/mediatek/Makefile new file mode 100644 index 000000000000..8cde09e654b3 --- /dev/null +++ b/drivers/pmdomain/mediatek/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_MTK_SCPSYS) += mtk-scpsys.o +obj-$(CONFIG_MTK_SCPSYS_PM_DOMAINS) += mtk-pm-domains.o diff --git a/drivers/pmdomain/mediatek/mt6795-pm-domains.h b/drivers/pmdomain/mediatek/mt6795-pm-domains.h new file mode 100644 index 000000000000..ef07c9dfdd9b --- /dev/null +++ b/drivers/pmdomain/mediatek/mt6795-pm-domains.h @@ -0,0 +1,112 @@ +/* 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 + +/* + * 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 */ diff --git a/drivers/pmdomain/mediatek/mt8167-pm-domains.h b/drivers/pmdomain/mediatek/mt8167-pm-domains.h new file mode 100644 index 000000000000..4d6c32759606 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8167-pm-domains.h @@ -0,0 +1,105 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT8167_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8167_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +#define MT8167_PWR_STATUS_MFG_2D BIT(24) +#define MT8167_PWR_STATUS_MFG_ASYNC BIT(25) + +/* + * MT8167 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8167[] = { + [MT8167_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(MT8167_TOP_AXI_PROT_EN_MM_EMI | + MT8167_TOP_AXI_PROT_EN_MCU_MM), + }, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8167_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(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8167_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), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8167_POWER_DOMAIN_MFG_ASYNC] = { + .name = "mfg_async", + .sta_mask = MT8167_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 = 0, + .sram_pdn_ack_bits = 0, + .bp_infracfg = { + BUS_PROT_UPDATE_TOPAXI(MT8167_TOP_AXI_PROT_EN_MCU_MFG | + MT8167_TOP_AXI_PROT_EN_MFG_EMI), + }, + }, + [MT8167_POWER_DOMAIN_MFG_2D] = { + .name = "mfg_2d", + .sta_mask = MT8167_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(15, 12), + }, + [MT8167_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(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, + [MT8167_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = SPM_CONN_PWR_CON, + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = 0, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + .bp_infracfg = { + BUS_PROT_UPDATE_TOPAXI(MT8167_TOP_AXI_PROT_EN_CONN_EMI | + MT8167_TOP_AXI_PROT_EN_CONN_MCU | + MT8167_TOP_AXI_PROT_EN_MCU_CONN), + }, + }, +}; + +static const struct scpsys_soc_data mt8167_scpsys_data = { + .domains_data = scpsys_domain_data_mt8167, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8167), +}; + +#endif /* __SOC_MEDIATEK_MT8167_PM_DOMAINS_H */ + diff --git a/drivers/pmdomain/mediatek/mt8173-pm-domains.h b/drivers/pmdomain/mediatek/mt8173-pm-domains.h new file mode 100644 index 000000000000..1a5dc63b7357 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8173-pm-domains.h @@ -0,0 +1,123 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT8173_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8173_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT8173 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8173[] = { + [MT8173_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), + }, + [MT8173_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), + }, + [MT8173_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), + }, + [MT8173_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), + }, + }, + [MT8173_POWER_DOMAIN_VENC_LT] = { + .name = "venc_lt", + .sta_mask = PWR_STATUS_VENC_LT, + .ctl_offs = SPM_VEN2_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), + }, + [MT8173_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), + }, + [MT8173_POWER_DOMAIN_USB] = { + .name = "usb", + .sta_mask = PWR_STATUS_USB, + .ctl_offs = SPM_USB_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), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8173_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, + .caps = MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8173_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), + }, + [MT8173_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 mt8173_scpsys_data = { + .domains_data = scpsys_domain_data_mt8173, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8173), +}; + +#endif /* __SOC_MEDIATEK_MT8173_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8183-pm-domains.h b/drivers/pmdomain/mediatek/mt8183-pm-domains.h new file mode 100644 index 000000000000..99de67fe5de8 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8183-pm-domains.h @@ -0,0 +1,266 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT8183_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8183_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT8183 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8183[] = { + [MT8183_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = PWR_STATUS_AUDIO, + .ctl_offs = 0x0314, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + }, + [MT8183_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = 0x032c, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_CONN, MT8183_TOP_AXI_PROT_EN_SET, + MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), + }, + }, + [MT8183_POWER_DOMAIN_MFG_ASYNC] = { + .name = "mfg_async", + .sta_mask = PWR_STATUS_MFG_ASYNC, + .ctl_offs = 0x0334, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .caps = MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8183_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = 0x0338, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8183_POWER_DOMAIN_MFG_CORE0] = { + .name = "mfg_core0", + .sta_mask = BIT(7), + .ctl_offs = 0x034c, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8183_POWER_DOMAIN_MFG_CORE1] = { + .name = "mfg_core1", + .sta_mask = BIT(20), + .ctl_offs = 0x0310, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8183_POWER_DOMAIN_MFG_2D] = { + .name = "mfg_2d", + .sta_mask = PWR_STATUS_MFG_2D, + .ctl_offs = 0x0348, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_1_MFG, MT8183_TOP_AXI_PROT_EN_1_SET, + MT8183_TOP_AXI_PROT_EN_1_CLR, MT8183_TOP_AXI_PROT_EN_STA1_1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MFG, MT8183_TOP_AXI_PROT_EN_SET, + MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), + }, + }, + [MT8183_POWER_DOMAIN_DISP] = { + .name = "disp", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = 0x030c, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_1_DISP, MT8183_TOP_AXI_PROT_EN_1_SET, + MT8183_TOP_AXI_PROT_EN_1_CLR, MT8183_TOP_AXI_PROT_EN_STA1_1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_DISP, MT8183_TOP_AXI_PROT_EN_SET, + MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), + }, + .bp_smi = { + BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_DISP, + MT8183_SMI_COMMON_CLAMP_EN_SET, + MT8183_SMI_COMMON_CLAMP_EN_CLR, + MT8183_SMI_COMMON_CLAMP_EN), + }, + }, + [MT8183_POWER_DOMAIN_CAM] = { + .name = "cam", + .sta_mask = BIT(25), + .ctl_offs = 0x0344, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(9, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_CAM, MT8183_TOP_AXI_PROT_EN_MM_SET, + MT8183_TOP_AXI_PROT_EN_MM_CLR, MT8183_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_CAM, MT8183_TOP_AXI_PROT_EN_SET, + MT8183_TOP_AXI_PROT_EN_CLR, MT8183_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR_IGN(MT8183_TOP_AXI_PROT_EN_MM_CAM_2ND, + MT8183_TOP_AXI_PROT_EN_MM_SET, + MT8183_TOP_AXI_PROT_EN_MM_CLR, + MT8183_TOP_AXI_PROT_EN_MM_STA1), + }, + .bp_smi = { + BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_CAM, + MT8183_SMI_COMMON_CLAMP_EN_SET, + MT8183_SMI_COMMON_CLAMP_EN_CLR, + MT8183_SMI_COMMON_CLAMP_EN), + }, + }, + [MT8183_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = 0x0308, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(9, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_ISP, + MT8183_TOP_AXI_PROT_EN_MM_SET, + MT8183_TOP_AXI_PROT_EN_MM_CLR, + MT8183_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR_IGN(MT8183_TOP_AXI_PROT_EN_MM_ISP_2ND, + MT8183_TOP_AXI_PROT_EN_MM_SET, + MT8183_TOP_AXI_PROT_EN_MM_CLR, + MT8183_TOP_AXI_PROT_EN_MM_STA1), + }, + .bp_smi = { + BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_ISP, + MT8183_SMI_COMMON_CLAMP_EN_SET, + MT8183_SMI_COMMON_CLAMP_EN_CLR, + MT8183_SMI_COMMON_CLAMP_EN), + }, + }, + [MT8183_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = BIT(31), + .ctl_offs = 0x0300, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_smi = { + BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_VDEC, + MT8183_SMI_COMMON_CLAMP_EN_SET, + MT8183_SMI_COMMON_CLAMP_EN_CLR, + MT8183_SMI_COMMON_CLAMP_EN), + }, + }, + [MT8183_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = PWR_STATUS_VENC, + .ctl_offs = 0x0304, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .bp_smi = { + BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_VENC, + MT8183_SMI_COMMON_CLAMP_EN_SET, + MT8183_SMI_COMMON_CLAMP_EN_CLR, + MT8183_SMI_COMMON_CLAMP_EN), + }, + }, + [MT8183_POWER_DOMAIN_VPU_TOP] = { + .name = "vpu_top", + .sta_mask = BIT(26), + .ctl_offs = 0x0324, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_VPU_TOP, + MT8183_TOP_AXI_PROT_EN_MM_SET, + MT8183_TOP_AXI_PROT_EN_MM_CLR, + MT8183_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_VPU_TOP, + MT8183_TOP_AXI_PROT_EN_SET, + MT8183_TOP_AXI_PROT_EN_CLR, + MT8183_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MM_VPU_TOP_2ND, + MT8183_TOP_AXI_PROT_EN_MM_SET, + MT8183_TOP_AXI_PROT_EN_MM_CLR, + MT8183_TOP_AXI_PROT_EN_MM_STA1), + }, + .bp_smi = { + BUS_PROT_WR(MT8183_SMI_COMMON_SMI_CLAMP_VPU_TOP, + MT8183_SMI_COMMON_CLAMP_EN_SET, + MT8183_SMI_COMMON_CLAMP_EN_CLR, + MT8183_SMI_COMMON_CLAMP_EN), + }, + }, + [MT8183_POWER_DOMAIN_VPU_CORE0] = { + .name = "vpu_core0", + .sta_mask = BIT(27), + .ctl_offs = 0x33c, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE0, + MT8183_TOP_AXI_PROT_EN_MCU_SET, + MT8183_TOP_AXI_PROT_EN_MCU_CLR, + MT8183_TOP_AXI_PROT_EN_MCU_STA1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE0_2ND, + MT8183_TOP_AXI_PROT_EN_MCU_SET, + MT8183_TOP_AXI_PROT_EN_MCU_CLR, + MT8183_TOP_AXI_PROT_EN_MCU_STA1), + }, + .caps = MTK_SCPD_SRAM_ISO, + }, + [MT8183_POWER_DOMAIN_VPU_CORE1] = { + .name = "vpu_core1", + .sta_mask = BIT(28), + .ctl_offs = 0x0340, + .pwr_sta_offs = 0x0180, + .pwr_sta2nd_offs = 0x0184, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE1, + MT8183_TOP_AXI_PROT_EN_MCU_SET, + MT8183_TOP_AXI_PROT_EN_MCU_CLR, + MT8183_TOP_AXI_PROT_EN_MCU_STA1), + BUS_PROT_WR(MT8183_TOP_AXI_PROT_EN_MCU_VPU_CORE1_2ND, + MT8183_TOP_AXI_PROT_EN_MCU_SET, + MT8183_TOP_AXI_PROT_EN_MCU_CLR, + MT8183_TOP_AXI_PROT_EN_MCU_STA1), + }, + .caps = MTK_SCPD_SRAM_ISO, + }, +}; + +static const struct scpsys_soc_data mt8183_scpsys_data = { + .domains_data = scpsys_domain_data_mt8183, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8183), +}; + +#endif /* __SOC_MEDIATEK_MT8183_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8186-pm-domains.h b/drivers/pmdomain/mediatek/mt8186-pm-domains.h new file mode 100644 index 000000000000..fce86f79c505 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8186-pm-domains.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Chun-Jie Chen + */ + +#ifndef __SOC_MEDIATEK_MT8186_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8186_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT8186 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8186[] = { + [MT8186_POWER_DOMAIN_MFG0] = { + .name = "mfg0", + .sta_mask = BIT(2), + .ctl_offs = 0x308, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8186_POWER_DOMAIN_MFG1] = { + .name = "mfg1", + .sta_mask = BIT(3), + .ctl_offs = 0x30c, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_MFG1_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_MFG1_STEP2, + MT8186_TOP_AXI_PROT_EN_SET, + MT8186_TOP_AXI_PROT_EN_CLR, + MT8186_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_MFG1_STEP3, + MT8186_TOP_AXI_PROT_EN_SET, + MT8186_TOP_AXI_PROT_EN_CLR, + MT8186_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_MFG1_STEP4, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8186_POWER_DOMAIN_MFG2] = { + .name = "mfg2", + .sta_mask = BIT(4), + .ctl_offs = 0x310, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_MFG3] = { + .name = "mfg3", + .sta_mask = BIT(5), + .ctl_offs = 0x314, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_SSUSB] = { + .name = "ssusb", + .sta_mask = BIT(20), + .ctl_offs = 0x9F0, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8186_POWER_DOMAIN_SSUSB_P1] = { + .name = "ssusb_p1", + .sta_mask = BIT(19), + .ctl_offs = 0x9F4, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8186_POWER_DOMAIN_DIS] = { + .name = "dis", + .sta_mask = BIT(21), + .ctl_offs = 0x354, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_DIS_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_DIS_STEP2, + MT8186_TOP_AXI_PROT_EN_SET, + MT8186_TOP_AXI_PROT_EN_CLR, + MT8186_TOP_AXI_PROT_EN_STA), + }, + }, + [MT8186_POWER_DOMAIN_IMG] = { + .name = "img", + .sta_mask = BIT(13), + .ctl_offs = 0x334, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IMG_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IMG_STEP2, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_IMG2] = { + .name = "img2", + .sta_mask = BIT(14), + .ctl_offs = 0x338, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_IPE] = { + .name = "ipe", + .sta_mask = BIT(15), + .ctl_offs = 0x33C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IPE_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_IPE_STEP2, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_CAM] = { + .name = "cam", + .sta_mask = BIT(23), + .ctl_offs = 0x35C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_CAM_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_CAM_STEP2, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_CAM_RAWA] = { + .name = "cam_rawa", + .sta_mask = BIT(24), + .ctl_offs = 0x360, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_CAM_RAWB] = { + .name = "cam_rawb", + .sta_mask = BIT(25), + .ctl_offs = 0x364, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = BIT(18), + .ctl_offs = 0x348, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VENC_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VENC_STEP2, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = BIT(16), + .ctl_offs = 0x340, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VDEC_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_VDEC_STEP2, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_WPE] = { + .name = "wpe", + .sta_mask = BIT(0), + .ctl_offs = 0x3F8, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_2_WPE_STEP1, + MT8186_TOP_AXI_PROT_EN_2_SET, + MT8186_TOP_AXI_PROT_EN_2_CLR, + MT8186_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_2_WPE_STEP2, + MT8186_TOP_AXI_PROT_EN_2_SET, + MT8186_TOP_AXI_PROT_EN_2_CLR, + MT8186_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_CONN_ON] = { + .name = "conn_on", + .sta_mask = BIT(1), + .ctl_offs = 0x304, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_1_CONN_ON_STEP1, + MT8186_TOP_AXI_PROT_EN_1_SET, + MT8186_TOP_AXI_PROT_EN_1_CLR, + MT8186_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_CONN_ON_STEP2, + MT8186_TOP_AXI_PROT_EN_SET, + MT8186_TOP_AXI_PROT_EN_CLR, + MT8186_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_CONN_ON_STEP3, + MT8186_TOP_AXI_PROT_EN_SET, + MT8186_TOP_AXI_PROT_EN_CLR, + MT8186_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_CONN_ON_STEP4, + MT8186_TOP_AXI_PROT_EN_SET, + MT8186_TOP_AXI_PROT_EN_CLR, + MT8186_TOP_AXI_PROT_EN_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8186_POWER_DOMAIN_CSIRX_TOP] = { + .name = "csirx_top", + .sta_mask = BIT(6), + .ctl_offs = 0x318, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8186_POWER_DOMAIN_ADSP_AO] = { + .name = "adsp_ao", + .sta_mask = BIT(17), + .ctl_offs = 0x9FC, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + }, + [MT8186_POWER_DOMAIN_ADSP_INFRA] = { + .name = "adsp_infra", + .sta_mask = BIT(10), + .ctl_offs = 0x9F8, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + }, + [MT8186_POWER_DOMAIN_ADSP_TOP] = { + .name = "adsp_top", + .sta_mask = BIT(31), + .ctl_offs = 0x3E4, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_3_ADSP_TOP_STEP1, + MT8186_TOP_AXI_PROT_EN_3_SET, + MT8186_TOP_AXI_PROT_EN_3_CLR, + MT8186_TOP_AXI_PROT_EN_3_STA), + BUS_PROT_WR_IGN(MT8186_TOP_AXI_PROT_EN_3_ADSP_TOP_STEP2, + MT8186_TOP_AXI_PROT_EN_3_SET, + MT8186_TOP_AXI_PROT_EN_3_CLR, + MT8186_TOP_AXI_PROT_EN_3_STA), + }, + .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_ACTIVE_WAKEUP, + }, +}; + +static const struct scpsys_soc_data mt8186_scpsys_data = { + .domains_data = scpsys_domain_data_mt8186, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8186), +}; + +#endif /* __SOC_MEDIATEK_MT8186_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8188-pm-domains.h b/drivers/pmdomain/mediatek/mt8188-pm-domains.h new file mode 100644 index 000000000000..0692cb444ed0 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8188-pm-domains.h @@ -0,0 +1,623 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + * Author: Garmin Chang + */ + +#ifndef __SOC_MEDIATEK_MT8188_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8188_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT8188 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8188[] = { + [MT8188_POWER_DOMAIN_MFG0] = { + .name = "mfg0", + .sta_mask = BIT(1), + .ctl_offs = 0x300, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8188_POWER_DOMAIN_MFG1] = { + .name = "mfg1", + .sta_mask = BIT(2), + .ctl_offs = 0x304, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MFG1_STEP1, + MT8188_TOP_AXI_PROT_EN_SET, + MT8188_TOP_AXI_PROT_EN_CLR, + MT8188_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_MFG1_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_1_MFG1_STEP3, + MT8188_TOP_AXI_PROT_EN_1_SET, + MT8188_TOP_AXI_PROT_EN_1_CLR, + MT8188_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_MFG1_STEP4, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MFG1_STEP5, + MT8188_TOP_AXI_PROT_EN_SET, + MT8188_TOP_AXI_PROT_EN_CLR, + MT8188_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MFG1_STEP6, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8188_POWER_DOMAIN_MFG2] = { + .name = "mfg2", + .sta_mask = BIT(3), + .ctl_offs = 0x308, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_MFG3] = { + .name = "mfg3", + .sta_mask = BIT(4), + .ctl_offs = 0x30C, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_MFG4] = { + .name = "mfg4", + .sta_mask = BIT(5), + .ctl_offs = 0x310, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_PEXTP_MAC_P0] = { + .name = "pextp_mac_p0", + .sta_mask = BIT(10), + .ctl_offs = 0x324, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_PEXTP_MAC_P0_STEP1, + MT8188_TOP_AXI_PROT_EN_SET, + MT8188_TOP_AXI_PROT_EN_CLR, + MT8188_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_PEXTP_MAC_P0_STEP2, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_PEXTP_PHY_TOP] = { + .name = "pextp_phy_top", + .sta_mask = BIT(12), + .ctl_offs = 0x328, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_CSIRX_TOP] = { + .name = "pextp_csirx_top", + .sta_mask = BIT(17), + .ctl_offs = 0x3C4, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_ETHER] = { + .name = "ether", + .sta_mask = BIT(1), + .ctl_offs = 0x338, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_ETHER_STEP1, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8188_POWER_DOMAIN_HDMI_TX] = { + .name = "hdmi_tx", + .sta_mask = BIT(18), + .ctl_offs = 0x37C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_HDMI_TX_STEP1, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8188_POWER_DOMAIN_ADSP_AO] = { + .name = "adsp_ao", + .sta_mask = BIT(10), + .ctl_offs = 0x35C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_AO_STEP1, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_AO_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_ALWAYS_ON, + }, + [MT8188_POWER_DOMAIN_ADSP_INFRA] = { + .name = "adsp_infra", + .sta_mask = BIT(9), + .ctl_offs = 0x358, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_INFRA_STEP1, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_INFRA_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_ALWAYS_ON, + }, + [MT8188_POWER_DOMAIN_ADSP] = { + .name = "adsp", + .sta_mask = BIT(8), + .ctl_offs = 0x354, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_STEP1, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_ADSP_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_SRAM_ISO | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8188_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = BIT(6), + .ctl_offs = 0x34C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP1, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8188_POWER_DOMAIN_AUDIO_ASRC] = { + .name = "audio_asrc", + .sta_mask = BIT(7), + .ctl_offs = 0x350, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_ASRC_STEP1, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_AUDIO_ASRC_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_VPPSYS0] = { + .name = "vppsys0", + .sta_mask = BIT(11), + .ctl_offs = 0x360, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_VPPSYS0_STEP1, + MT8188_TOP_AXI_PROT_EN_SET, + MT8188_TOP_AXI_PROT_EN_CLR, + MT8188_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VPPSYS0_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_VPPSYS0_STEP3, + MT8188_TOP_AXI_PROT_EN_SET, + MT8188_TOP_AXI_PROT_EN_CLR, + MT8188_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VPPSYS0_STEP4, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VPPSYS0_STEP5, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA), + }, + }, + [MT8188_POWER_DOMAIN_VDOSYS0] = { + .name = "vdosys0", + .sta_mask = BIT(13), + .ctl_offs = 0x368, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDOSYS0_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_VDOSYS0_STEP2, + MT8188_TOP_AXI_PROT_EN_SET, + MT8188_TOP_AXI_PROT_EN_CLR, + MT8188_TOP_AXI_PROT_EN_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VDOSYS0_STEP3, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA), + }, + }, + [MT8188_POWER_DOMAIN_VDOSYS1] = { + .name = "vdosys1", + .sta_mask = BIT(14), + .ctl_offs = 0x36C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDOSYS1_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDOSYS1_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VDOSYS1_STEP3, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + }, + [MT8188_POWER_DOMAIN_DP_TX] = { + .name = "dp_tx", + .sta_mask = BIT(16), + .ctl_offs = 0x374, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_DP_TX_STEP1, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_EDP_TX] = { + .name = "edp_tx", + .sta_mask = BIT(17), + .ctl_offs = 0x378, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_EDP_TX_STEP1, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_SET, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_CLR, + MT8188_TOP_AXI_PROT_EN_INFRA_VDNR_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_VPPSYS1] = { + .name = "vppsys1", + .sta_mask = BIT(12), + .ctl_offs = 0x364, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VPPSYS1_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VPPSYS1_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VPPSYS1_STEP3, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + }, + [MT8188_POWER_DOMAIN_WPE] = { + .name = "wpe", + .sta_mask = BIT(15), + .ctl_offs = 0x370, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_WPE_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_WPE_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_VDEC0] = { + .name = "vdec0", + .sta_mask = BIT(19), + .ctl_offs = 0x380, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDEC0_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VDEC0_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_VDEC1] = { + .name = "vdec1", + .sta_mask = BIT(20), + .ctl_offs = 0x384, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDEC1_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VDEC1_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = BIT(22), + .ctl_offs = 0x38C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VENC_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_VENC_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_VENC_STEP3, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_IMG_VCORE] = { + .name = "vcore", + .sta_mask = BIT(28), + .ctl_offs = 0x3A4, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_IMG_VCORE_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_IMG_VCORE_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_IMG_VCORE_STEP3, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8188_POWER_DOMAIN_IMG_MAIN] = { + .name = "img_main", + .sta_mask = BIT(29), + .ctl_offs = 0x3A8, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_IMG_MAIN_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_IMG_MAIN_STEP2, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_DIP] = { + .name = "dip", + .sta_mask = BIT(30), + .ctl_offs = 0x3AC, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_IPE] = { + .name = "ipe", + .sta_mask = BIT(31), + .ctl_offs = 0x3B0, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_CAM_VCORE] = { + .name = "cam_vcore", + .sta_mask = BIT(27), + .ctl_offs = 0x3A0, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_CAM_VCORE_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_CAM_VCORE_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_1_CAM_VCORE_STEP3, + MT8188_TOP_AXI_PROT_EN_1_SET, + MT8188_TOP_AXI_PROT_EN_1_CLR, + MT8188_TOP_AXI_PROT_EN_1_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_CAM_VCORE_STEP4, + MT8188_TOP_AXI_PROT_EN_MM_SET, + MT8188_TOP_AXI_PROT_EN_MM_CLR, + MT8188_TOP_AXI_PROT_EN_MM_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_CAM_VCORE_STEP5, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8188_POWER_DOMAIN_CAM_MAIN] = { + .name = "cam_main", + .sta_mask = BIT(24), + .ctl_offs = 0x394, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .bp_infracfg = { + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_CAM_MAIN_STEP1, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_CAM_MAIN_STEP2, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_MM_2_CAM_MAIN_STEP3, + MT8188_TOP_AXI_PROT_EN_MM_2_SET, + MT8188_TOP_AXI_PROT_EN_MM_2_CLR, + MT8188_TOP_AXI_PROT_EN_MM_2_STA), + BUS_PROT_WR(MT8188_TOP_AXI_PROT_EN_2_CAM_MAIN_STEP4, + MT8188_TOP_AXI_PROT_EN_2_SET, + MT8188_TOP_AXI_PROT_EN_2_CLR, + MT8188_TOP_AXI_PROT_EN_2_STA), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_CAM_SUBA] = { + .name = "cam_suba", + .sta_mask = BIT(25), + .ctl_offs = 0x398, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8188_POWER_DOMAIN_CAM_SUBB] = { + .name = "cam_subb", + .sta_mask = BIT(26), + .ctl_offs = 0x39C, + .pwr_sta_offs = 0x16C, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = BIT(8), + .sram_pdn_ack_bits = BIT(12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, +}; + +static const struct scpsys_soc_data mt8188_scpsys_data = { + .domains_data = scpsys_domain_data_mt8188, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8188), +}; + +#endif /* __SOC_MEDIATEK_MT8188_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8192-pm-domains.h b/drivers/pmdomain/mediatek/mt8192-pm-domains.h new file mode 100644 index 000000000000..b97b2051920f --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8192-pm-domains.h @@ -0,0 +1,355 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MT8192_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8192_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT8192 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8192[] = { + [MT8192_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = BIT(21), + .ctl_offs = 0x0354, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_AUDIO, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + }, + }, + [MT8192_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = 0x0304, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_CONN, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_CONN_2ND, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_1_CONN, + MT8192_TOP_AXI_PROT_EN_1_SET, + MT8192_TOP_AXI_PROT_EN_1_CLR, + MT8192_TOP_AXI_PROT_EN_1_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8192_POWER_DOMAIN_MFG0] = { + .name = "mfg0", + .sta_mask = BIT(2), + .ctl_offs = 0x0308, + .pwr_sta_offs = 0x016c, + .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", + .sta_mask = BIT(3), + .ctl_offs = 0x030c, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_1_MFG1, + MT8192_TOP_AXI_PROT_EN_1_SET, + MT8192_TOP_AXI_PROT_EN_1_CLR, + MT8192_TOP_AXI_PROT_EN_1_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_MFG1, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MFG1, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_MFG1_2ND, + MT8192_TOP_AXI_PROT_EN_2_SET, + 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", + .sta_mask = BIT(4), + .ctl_offs = 0x0310, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_MFG3] = { + .name = "mfg3", + .sta_mask = BIT(5), + .ctl_offs = 0x0314, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_MFG4] = { + .name = "mfg4", + .sta_mask = BIT(6), + .ctl_offs = 0x0318, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_MFG5] = { + .name = "mfg5", + .sta_mask = BIT(7), + .ctl_offs = 0x031c, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_MFG6] = { + .name = "mfg6", + .sta_mask = BIT(8), + .ctl_offs = 0x0320, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_DISP] = { + .name = "disp", + .sta_mask = BIT(20), + .ctl_offs = 0x0350, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR_IGN(MT8192_TOP_AXI_PROT_EN_MM_DISP, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR_IGN(MT8192_TOP_AXI_PROT_EN_MM_2_DISP, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_DISP, + MT8192_TOP_AXI_PROT_EN_SET, + MT8192_TOP_AXI_PROT_EN_CLR, + MT8192_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_DISP_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_DISP_2ND, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT8192_POWER_DOMAIN_IPE] = { + .name = "ipe", + .sta_mask = BIT(14), + .ctl_offs = 0x0338, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_IPE, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_IPE_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT8192_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = BIT(12), + .ctl_offs = 0x0330, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_ISP, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_ISP_2ND, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT8192_POWER_DOMAIN_ISP2] = { + .name = "isp2", + .sta_mask = BIT(13), + .ctl_offs = 0x0334, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_ISP2, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_ISP2_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT8192_POWER_DOMAIN_MDP] = { + .name = "mdp", + .sta_mask = BIT(19), + .ctl_offs = 0x034c, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_MDP, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_2_MDP_2ND, + MT8192_TOP_AXI_PROT_EN_MM_2_SET, + MT8192_TOP_AXI_PROT_EN_MM_2_CLR, + MT8192_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT8192_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = BIT(17), + .ctl_offs = 0x0344, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VENC, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VENC_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT8192_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = BIT(15), + .ctl_offs = 0x033c, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VDEC, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_VDEC_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + }, + }, + [MT8192_POWER_DOMAIN_VDEC2] = { + .name = "vdec2", + .sta_mask = BIT(16), + .ctl_offs = 0x0340, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_CAM] = { + .name = "cam", + .sta_mask = BIT(23), + .ctl_offs = 0x035c, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_2_CAM, + MT8192_TOP_AXI_PROT_EN_2_SET, + MT8192_TOP_AXI_PROT_EN_2_CLR, + MT8192_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_CAM, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_1_CAM, + MT8192_TOP_AXI_PROT_EN_1_SET, + MT8192_TOP_AXI_PROT_EN_1_CLR, + MT8192_TOP_AXI_PROT_EN_1_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_MM_CAM_2ND, + MT8192_TOP_AXI_PROT_EN_MM_SET, + MT8192_TOP_AXI_PROT_EN_MM_CLR, + MT8192_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8192_TOP_AXI_PROT_EN_VDNR_CAM, + MT8192_TOP_AXI_PROT_EN_VDNR_SET, + MT8192_TOP_AXI_PROT_EN_VDNR_CLR, + MT8192_TOP_AXI_PROT_EN_VDNR_STA1), + }, + }, + [MT8192_POWER_DOMAIN_CAM_RAWA] = { + .name = "cam_rawa", + .sta_mask = BIT(24), + .ctl_offs = 0x0360, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_CAM_RAWB] = { + .name = "cam_rawb", + .sta_mask = BIT(25), + .ctl_offs = 0x0364, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, + [MT8192_POWER_DOMAIN_CAM_RAWC] = { + .name = "cam_rawc", + .sta_mask = BIT(26), + .ctl_offs = 0x0368, + .pwr_sta_offs = 0x016c, + .pwr_sta2nd_offs = 0x0170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + }, +}; + +static const struct scpsys_soc_data mt8192_scpsys_data = { + .domains_data = scpsys_domain_data_mt8192, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8192), +}; + +#endif /* __SOC_MEDIATEK_MT8192_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mt8195-pm-domains.h b/drivers/pmdomain/mediatek/mt8195-pm-domains.h new file mode 100644 index 000000000000..d7387ea1b9c9 --- /dev/null +++ b/drivers/pmdomain/mediatek/mt8195-pm-domains.h @@ -0,0 +1,613 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Chun-Jie Chen + */ + +#ifndef __SOC_MEDIATEK_MT8195_PM_DOMAINS_H +#define __SOC_MEDIATEK_MT8195_PM_DOMAINS_H + +#include "mtk-pm-domains.h" +#include + +/* + * MT8195 power domain support + */ + +static const struct scpsys_domain_data scpsys_domain_data_mt8195[] = { + [MT8195_POWER_DOMAIN_PCIE_MAC_P0] = { + .name = "pcie_mac_p0", + .sta_mask = BIT(11), + .ctl_offs = 0x328, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_PCIE_MAC_P0, + MT8195_TOP_AXI_PROT_EN_VDNR_SET, + MT8195_TOP_AXI_PROT_EN_VDNR_CLR, + MT8195_TOP_AXI_PROT_EN_VDNR_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_PCIE_MAC_P0, + MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, + MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, + MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), + }, + }, + [MT8195_POWER_DOMAIN_PCIE_MAC_P1] = { + .name = "pcie_mac_p1", + .sta_mask = BIT(12), + .ctl_offs = 0x32C, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_PCIE_MAC_P1, + MT8195_TOP_AXI_PROT_EN_VDNR_SET, + MT8195_TOP_AXI_PROT_EN_VDNR_CLR, + MT8195_TOP_AXI_PROT_EN_VDNR_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_PCIE_MAC_P1, + MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, + MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, + MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), + }, + }, + [MT8195_POWER_DOMAIN_PCIE_PHY] = { + .name = "pcie_phy", + .sta_mask = BIT(13), + .ctl_offs = 0x330, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8195_POWER_DOMAIN_SSUSB_PCIE_PHY] = { + .name = "ssusb_pcie_phy", + .sta_mask = BIT(14), + .ctl_offs = 0x334, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_ALWAYS_ON, + }, + [MT8195_POWER_DOMAIN_CSI_RX_TOP] = { + .name = "csi_rx_top", + .sta_mask = BIT(18), + .ctl_offs = 0x3C4, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_ETHER] = { + .name = "ether", + .sta_mask = BIT(3), + .ctl_offs = 0x344, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8195_POWER_DOMAIN_ADSP] = { + .name = "adsp", + .sta_mask = BIT(10), + .ctl_offs = 0x360, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_ADSP, + MT8195_TOP_AXI_PROT_EN_2_SET, + MT8195_TOP_AXI_PROT_EN_2_CLR, + MT8195_TOP_AXI_PROT_EN_2_STA1), + }, + .caps = MTK_SCPD_SRAM_ISO | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8195_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = BIT(8), + .ctl_offs = 0x358, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_AUDIO, + MT8195_TOP_AXI_PROT_EN_2_SET, + MT8195_TOP_AXI_PROT_EN_2_CLR, + MT8195_TOP_AXI_PROT_EN_2_STA1), + }, + }, + [MT8195_POWER_DOMAIN_MFG0] = { + .name = "mfg0", + .sta_mask = BIT(1), + .ctl_offs = 0x300, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8195_POWER_DOMAIN_MFG1] = { + .name = "mfg1", + .sta_mask = BIT(2), + .ctl_offs = 0x304, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MFG1, + MT8195_TOP_AXI_PROT_EN_SET, + MT8195_TOP_AXI_PROT_EN_CLR, + MT8195_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_MFG1, + MT8195_TOP_AXI_PROT_EN_2_SET, + MT8195_TOP_AXI_PROT_EN_2_CLR, + MT8195_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_1_MFG1, + MT8195_TOP_AXI_PROT_EN_1_SET, + MT8195_TOP_AXI_PROT_EN_1_CLR, + MT8195_TOP_AXI_PROT_EN_1_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_MFG1_2ND, + MT8195_TOP_AXI_PROT_EN_2_SET, + MT8195_TOP_AXI_PROT_EN_2_CLR, + MT8195_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MFG1_2ND, + MT8195_TOP_AXI_PROT_EN_SET, + MT8195_TOP_AXI_PROT_EN_CLR, + MT8195_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_MFG1, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_DOMAIN_SUPPLY, + }, + [MT8195_POWER_DOMAIN_MFG2] = { + .name = "mfg2", + .sta_mask = BIT(3), + .ctl_offs = 0x308, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_MFG3] = { + .name = "mfg3", + .sta_mask = BIT(4), + .ctl_offs = 0x30C, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_MFG4] = { + .name = "mfg4", + .sta_mask = BIT(5), + .ctl_offs = 0x310, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_MFG5] = { + .name = "mfg5", + .sta_mask = BIT(6), + .ctl_offs = 0x314, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_MFG6] = { + .name = "mfg6", + .sta_mask = BIT(7), + .ctl_offs = 0x318, + .pwr_sta_offs = 0x174, + .pwr_sta2nd_offs = 0x178, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_VPPSYS0] = { + .name = "vppsys0", + .sta_mask = BIT(11), + .ctl_offs = 0x364, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VPPSYS0, + MT8195_TOP_AXI_PROT_EN_SET, + MT8195_TOP_AXI_PROT_EN_CLR, + MT8195_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VPPSYS0, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VPPSYS0_2ND, + MT8195_TOP_AXI_PROT_EN_SET, + MT8195_TOP_AXI_PROT_EN_CLR, + MT8195_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VPPSYS0_2ND, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VPPSYS0, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), + }, + }, + [MT8195_POWER_DOMAIN_VDOSYS0] = { + .name = "vdosys0", + .sta_mask = BIT(13), + .ctl_offs = 0x36C, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDOSYS0, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDOSYS0, + MT8195_TOP_AXI_PROT_EN_SET, + MT8195_TOP_AXI_PROT_EN_CLR, + MT8195_TOP_AXI_PROT_EN_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_VDOSYS0, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_SET, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_CLR, + MT8195_TOP_AXI_PROT_EN_SUB_INFRA_VDNR_STA1), + }, + }, + [MT8195_POWER_DOMAIN_VPPSYS1] = { + .name = "vppsys1", + .sta_mask = BIT(12), + .ctl_offs = 0x368, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VPPSYS1, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VPPSYS1_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VPPSYS1, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT8195_POWER_DOMAIN_VDOSYS1] = { + .name = "vdosys1", + .sta_mask = BIT(14), + .ctl_offs = 0x370, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDOSYS1, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDOSYS1_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDOSYS1, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT8195_POWER_DOMAIN_DP_TX] = { + .name = "dp_tx", + .sta_mask = BIT(16), + .ctl_offs = 0x378, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_DP_TX, + MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, + MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, + MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_EPD_TX] = { + .name = "epd_tx", + .sta_mask = BIT(17), + .ctl_offs = 0x37C, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_VDNR_1_EPD_TX, + MT8195_TOP_AXI_PROT_EN_VDNR_1_SET, + MT8195_TOP_AXI_PROT_EN_VDNR_1_CLR, + MT8195_TOP_AXI_PROT_EN_VDNR_1_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_HDMI_TX] = { + .name = "hdmi_tx", + .sta_mask = BIT(18), + .ctl_offs = 0x380, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF | MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8195_POWER_DOMAIN_WPESYS] = { + .name = "wpesys", + .sta_mask = BIT(15), + .ctl_offs = 0x374, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_WPESYS, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_WPESYS, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_WPESYS_2ND, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + }, + [MT8195_POWER_DOMAIN_VDEC0] = { + .name = "vdec0", + .sta_mask = BIT(20), + .ctl_offs = 0x388, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC0, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC0, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC0_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC0_2ND, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_VDEC1] = { + .name = "vdec1", + .sta_mask = BIT(21), + .ctl_offs = 0x38C, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC1, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VDEC1_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_VDEC2] = { + .name = "vdec2", + .sta_mask = BIT(22), + .ctl_offs = 0x390, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC2, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VDEC2_2ND, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = BIT(23), + .ctl_offs = 0x394, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VENC, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VENC_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VENC, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_VENC_CORE1] = { + .name = "venc_core1", + .sta_mask = BIT(24), + .ctl_offs = 0x398, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_VENC_CORE1, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_VENC_CORE1, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_IMG] = { + .name = "img", + .sta_mask = BIT(29), + .ctl_offs = 0x3AC, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_IMG, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_IMG_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_DIP] = { + .name = "dip", + .sta_mask = BIT(30), + .ctl_offs = 0x3B0, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_IPE] = { + .name = "ipe", + .sta_mask = BIT(31), + .ctl_offs = 0x3B4, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_IPE, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_IPE, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_CAM] = { + .name = "cam", + .sta_mask = BIT(25), + .ctl_offs = 0x39C, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .bp_infracfg = { + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_2_CAM, + MT8195_TOP_AXI_PROT_EN_2_SET, + MT8195_TOP_AXI_PROT_EN_2_CLR, + MT8195_TOP_AXI_PROT_EN_2_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_CAM, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_1_CAM, + MT8195_TOP_AXI_PROT_EN_1_SET, + MT8195_TOP_AXI_PROT_EN_1_CLR, + MT8195_TOP_AXI_PROT_EN_1_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_CAM_2ND, + MT8195_TOP_AXI_PROT_EN_MM_SET, + MT8195_TOP_AXI_PROT_EN_MM_CLR, + MT8195_TOP_AXI_PROT_EN_MM_STA1), + BUS_PROT_WR(MT8195_TOP_AXI_PROT_EN_MM_2_CAM, + MT8195_TOP_AXI_PROT_EN_MM_2_SET, + MT8195_TOP_AXI_PROT_EN_MM_2_CLR, + MT8195_TOP_AXI_PROT_EN_MM_2_STA1), + }, + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_CAM_RAWA] = { + .name = "cam_rawa", + .sta_mask = BIT(26), + .ctl_offs = 0x3A0, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_CAM_RAWB] = { + .name = "cam_rawb", + .sta_mask = BIT(27), + .ctl_offs = 0x3A4, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, + [MT8195_POWER_DOMAIN_CAM_MRAW] = { + .name = "cam_mraw", + .sta_mask = BIT(28), + .ctl_offs = 0x3A8, + .pwr_sta_offs = 0x16c, + .pwr_sta2nd_offs = 0x170, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .caps = MTK_SCPD_KEEP_DEFAULT_OFF, + }, +}; + +static const struct scpsys_soc_data mt8195_scpsys_data = { + .domains_data = scpsys_domain_data_mt8195, + .num_domains = ARRAY_SIZE(scpsys_domain_data_mt8195), +}; + +#endif /* __SOC_MEDIATEK_MT8195_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.c b/drivers/pmdomain/mediatek/mtk-pm-domains.c new file mode 100644 index 000000000000..ee962804b830 --- /dev/null +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.c @@ -0,0 +1,688 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020 Collabora Ltd. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mt6795-pm-domains.h" +#include "mt8167-pm-domains.h" +#include "mt8173-pm-domains.h" +#include "mt8183-pm-domains.h" +#include "mt8186-pm-domains.h" +#include "mt8188-pm-domains.h" +#include "mt8192-pm-domains.h" +#include "mt8195-pm-domains.h" + +#define MTK_POLL_DELAY_US 10 +#define MTK_POLL_TIMEOUT USEC_PER_SEC + +#define PWR_RST_B_BIT BIT(0) +#define PWR_ISO_BIT BIT(1) +#define PWR_ON_BIT BIT(2) +#define PWR_ON_2ND_BIT BIT(3) +#define PWR_CLK_DIS_BIT BIT(4) +#define PWR_SRAM_CLKISO_BIT BIT(5) +#define PWR_SRAM_ISOINT_B_BIT BIT(6) + +struct scpsys_domain { + struct generic_pm_domain genpd; + const struct scpsys_domain_data *data; + struct scpsys *scpsys; + int num_clks; + struct clk_bulk_data *clks; + int num_subsys_clks; + struct clk_bulk_data *subsys_clks; + struct regmap *infracfg; + struct regmap *smi; + struct regulator *supply; +}; + +struct scpsys { + struct device *dev; + struct regmap *base; + const struct scpsys_soc_data *soc_data; + struct genpd_onecell_data pd_data; + struct generic_pm_domain *domains[]; +}; + +#define to_scpsys_domain(gpd) container_of(gpd, struct scpsys_domain, genpd) + +static bool scpsys_domain_is_on(struct scpsys_domain *pd) +{ + struct scpsys *scpsys = pd->scpsys; + u32 status, status2; + + regmap_read(scpsys->base, pd->data->pwr_sta_offs, &status); + status &= pd->data->sta_mask; + + regmap_read(scpsys->base, pd->data->pwr_sta2nd_offs, &status2); + status2 &= pd->data->sta_mask; + + /* A domain is on when both status bits are set. */ + return status && status2; +} + +static int scpsys_sram_enable(struct scpsys_domain *pd) +{ + u32 pdn_ack = pd->data->sram_pdn_ack_bits; + struct scpsys *scpsys = pd->scpsys; + unsigned int tmp; + int ret; + + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, pd->data->sram_pdn_bits); + + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + ret = regmap_read_poll_timeout(scpsys->base, pd->data->ctl_offs, tmp, + (tmp & pdn_ack) == 0, MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_SRAM_ISO)) { + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_ISOINT_B_BIT); + udelay(1); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_CLKISO_BIT); + } + + return 0; +} + +static int scpsys_sram_disable(struct scpsys_domain *pd) +{ + u32 pdn_ack = pd->data->sram_pdn_ack_bits; + struct scpsys *scpsys = pd->scpsys; + unsigned int tmp; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_SRAM_ISO)) { + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_CLKISO_BIT); + udelay(1); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_SRAM_ISOINT_B_BIT); + } + + regmap_set_bits(scpsys->base, pd->data->ctl_offs, pd->data->sram_pdn_bits); + + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + return regmap_read_poll_timeout(scpsys->base, pd->data->ctl_offs, tmp, + (tmp & pdn_ack) == pdn_ack, MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); +} + +static int _scpsys_bus_protect_enable(const struct scpsys_bus_prot_data *bpd, struct regmap *regmap) +{ + int i, ret; + + for (i = 0; i < SPM_MAX_BUS_PROT_DATA; i++) { + u32 val, mask = bpd[i].bus_prot_mask; + + if (!mask) + break; + + if (bpd[i].bus_prot_reg_update) + regmap_set_bits(regmap, bpd[i].bus_prot_set, mask); + else + regmap_write(regmap, bpd[i].bus_prot_set, mask); + + ret = regmap_read_poll_timeout(regmap, bpd[i].bus_prot_sta, + val, (val & mask) == mask, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret) + return ret; + } + + return 0; +} + +static int scpsys_bus_protect_enable(struct scpsys_domain *pd) +{ + int ret; + + ret = _scpsys_bus_protect_enable(pd->data->bp_infracfg, pd->infracfg); + if (ret) + return ret; + + return _scpsys_bus_protect_enable(pd->data->bp_smi, pd->smi); +} + +static int _scpsys_bus_protect_disable(const struct scpsys_bus_prot_data *bpd, + struct regmap *regmap) +{ + int i, ret; + + for (i = SPM_MAX_BUS_PROT_DATA - 1; i >= 0; i--) { + u32 val, mask = bpd[i].bus_prot_mask; + + if (!mask) + continue; + + if (bpd[i].bus_prot_reg_update) + regmap_clear_bits(regmap, bpd[i].bus_prot_clr, mask); + else + regmap_write(regmap, bpd[i].bus_prot_clr, mask); + + if (bpd[i].ignore_clr_ack) + continue; + + ret = regmap_read_poll_timeout(regmap, bpd[i].bus_prot_sta, + val, !(val & mask), + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret) + return ret; + } + + return 0; +} + +static int scpsys_bus_protect_disable(struct scpsys_domain *pd) +{ + int ret; + + ret = _scpsys_bus_protect_disable(pd->data->bp_smi, pd->smi); + if (ret) + return ret; + + return _scpsys_bus_protect_disable(pd->data->bp_infracfg, pd->infracfg); +} + +static int scpsys_regulator_enable(struct regulator *supply) +{ + return supply ? regulator_enable(supply) : 0; +} + +static int scpsys_regulator_disable(struct regulator *supply) +{ + return supply ? regulator_disable(supply) : 0; +} + +static int scpsys_power_on(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); + struct scpsys *scpsys = pd->scpsys; + bool tmp; + int ret; + + ret = scpsys_regulator_enable(pd->supply); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(pd->num_clks, pd->clks); + if (ret) + goto err_reg; + + if (pd->data->ext_buck_iso_offs && MTK_SCPD_CAPS(pd, MTK_SCPD_EXT_BUCK_ISO)) + regmap_clear_bits(scpsys->base, pd->data->ext_buck_iso_offs, + pd->data->ext_buck_iso_mask); + + /* subsys power on */ + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT); + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT); + + /* wait until PWR_ACK = 1 */ + ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, tmp, MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); + if (ret < 0) + goto err_pwr_ack; + + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT); + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); + + ret = clk_bulk_prepare_enable(pd->num_subsys_clks, pd->subsys_clks); + if (ret) + goto err_pwr_ack; + + ret = scpsys_sram_enable(pd); + if (ret < 0) + goto err_disable_subsys_clks; + + ret = scpsys_bus_protect_disable(pd); + if (ret < 0) + goto err_disable_sram; + + return 0; + +err_disable_sram: + scpsys_sram_disable(pd); +err_disable_subsys_clks: + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); +err_pwr_ack: + clk_bulk_disable_unprepare(pd->num_clks, pd->clks); +err_reg: + scpsys_regulator_disable(pd->supply); + return ret; +} + +static int scpsys_power_off(struct generic_pm_domain *genpd) +{ + struct scpsys_domain *pd = container_of(genpd, struct scpsys_domain, genpd); + struct scpsys *scpsys = pd->scpsys; + bool tmp; + int ret; + + ret = scpsys_bus_protect_enable(pd); + if (ret < 0) + return ret; + + ret = scpsys_sram_disable(pd); + if (ret < 0) + return ret; + + if (pd->data->ext_buck_iso_offs && MTK_SCPD_CAPS(pd, MTK_SCPD_EXT_BUCK_ISO)) + regmap_set_bits(scpsys->base, pd->data->ext_buck_iso_offs, + pd->data->ext_buck_iso_mask); + + clk_bulk_disable_unprepare(pd->num_subsys_clks, pd->subsys_clks); + + /* subsys power off */ + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_ISO_BIT); + regmap_set_bits(scpsys->base, pd->data->ctl_offs, PWR_CLK_DIS_BIT); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_RST_B_BIT); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_2ND_BIT); + regmap_clear_bits(scpsys->base, pd->data->ctl_offs, PWR_ON_BIT); + + /* wait until PWR_ACK = 0 */ + ret = readx_poll_timeout(scpsys_domain_is_on, pd, tmp, !tmp, MTK_POLL_DELAY_US, + MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + + clk_bulk_disable_unprepare(pd->num_clks, pd->clks); + + scpsys_regulator_disable(pd->supply); + + return 0; +} + +static struct +generic_pm_domain *scpsys_add_one_domain(struct scpsys *scpsys, struct device_node *node) +{ + const struct scpsys_domain_data *domain_data; + struct scpsys_domain *pd; + struct device_node *root_node = scpsys->dev->of_node; + struct device_node *smi_node; + struct property *prop; + const char *clk_name; + int i, ret, num_clks; + struct clk *clk; + int clk_ind = 0; + u32 id; + + ret = of_property_read_u32(node, "reg", &id); + if (ret) { + dev_err(scpsys->dev, "%pOF: failed to retrieve domain id from reg: %d\n", + node, ret); + return ERR_PTR(-EINVAL); + } + + if (id >= scpsys->soc_data->num_domains) { + dev_err(scpsys->dev, "%pOF: invalid domain id %d\n", node, id); + return ERR_PTR(-EINVAL); + } + + domain_data = &scpsys->soc_data->domains_data[id]; + if (domain_data->sta_mask == 0) { + dev_err(scpsys->dev, "%pOF: undefined domain id %d\n", node, id); + return ERR_PTR(-EINVAL); + } + + pd = devm_kzalloc(scpsys->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + + pd->data = domain_data; + pd->scpsys = scpsys; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_DOMAIN_SUPPLY)) { + /* + * Find regulator in current power domain node. + * devm_regulator_get() finds regulator in a node and its child + * node, so set of_node to current power domain node then change + * back to original node after regulator is found for current + * power domain node. + */ + scpsys->dev->of_node = node; + pd->supply = devm_regulator_get(scpsys->dev, "domain"); + scpsys->dev->of_node = root_node; + if (IS_ERR(pd->supply)) { + dev_err_probe(scpsys->dev, PTR_ERR(pd->supply), + "%pOF: failed to get power supply.\n", + node); + return ERR_CAST(pd->supply); + } + } + + pd->infracfg = syscon_regmap_lookup_by_phandle_optional(node, "mediatek,infracfg"); + if (IS_ERR(pd->infracfg)) + return ERR_CAST(pd->infracfg); + + smi_node = of_parse_phandle(node, "mediatek,smi", 0); + if (smi_node) { + pd->smi = device_node_to_regmap(smi_node); + of_node_put(smi_node); + if (IS_ERR(pd->smi)) + return ERR_CAST(pd->smi); + } + + num_clks = of_clk_get_parent_count(node); + if (num_clks > 0) { + /* Calculate number of subsys_clks */ + of_property_for_each_string(node, "clock-names", prop, clk_name) { + char *subsys; + + subsys = strchr(clk_name, '-'); + if (subsys) + pd->num_subsys_clks++; + else + pd->num_clks++; + } + + pd->clks = devm_kcalloc(scpsys->dev, pd->num_clks, sizeof(*pd->clks), GFP_KERNEL); + if (!pd->clks) + return ERR_PTR(-ENOMEM); + + pd->subsys_clks = devm_kcalloc(scpsys->dev, pd->num_subsys_clks, + sizeof(*pd->subsys_clks), GFP_KERNEL); + if (!pd->subsys_clks) + return ERR_PTR(-ENOMEM); + + } + + for (i = 0; i < pd->num_clks; i++) { + clk = of_clk_get(node, i); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err_probe(scpsys->dev, ret, + "%pOF: failed to get clk at index %d\n", node, i); + goto err_put_clocks; + } + + pd->clks[clk_ind++].clk = clk; + } + + for (i = 0; i < pd->num_subsys_clks; i++) { + clk = of_clk_get(node, i + clk_ind); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + dev_err_probe(scpsys->dev, ret, + "%pOF: failed to get clk at index %d\n", node, + i + clk_ind); + goto err_put_subsys_clocks; + } + + pd->subsys_clks[i].clk = clk; + } + + /* + * Initially turn on all domains to make the domains usable + * with !CONFIG_PM and to get the hardware in sync with the + * software. The unused domains will be switched off during + * late_init time. + */ + if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) { + if (scpsys_domain_is_on(pd)) + dev_warn(scpsys->dev, + "%pOF: A default off power domain has been ON\n", node); + } else { + ret = scpsys_power_on(&pd->genpd); + if (ret < 0) { + 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]) { + ret = -EINVAL; + dev_err(scpsys->dev, + "power domain with id %d already exists, check your device-tree\n", id); + goto err_put_subsys_clocks; + } + + if (!pd->data->name) + pd->genpd.name = node->name; + else + pd->genpd.name = pd->data->name; + + pd->genpd.power_off = scpsys_power_off; + pd->genpd.power_on = scpsys_power_on; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_ACTIVE_WAKEUP)) + pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; + + if (MTK_SCPD_CAPS(pd, MTK_SCPD_KEEP_DEFAULT_OFF)) + pm_genpd_init(&pd->genpd, NULL, true); + else + pm_genpd_init(&pd->genpd, NULL, false); + + scpsys->domains[id] = &pd->genpd; + + return scpsys->pd_data.domains[id]; + +err_put_subsys_clocks: + clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); +err_put_clocks: + clk_bulk_put(pd->num_clks, pd->clks); + return ERR_PTR(ret); +} + +static int scpsys_add_subdomain(struct scpsys *scpsys, struct device_node *parent) +{ + struct generic_pm_domain *child_pd, *parent_pd; + struct device_node *child; + int ret; + + for_each_child_of_node(parent, child) { + u32 id; + + ret = of_property_read_u32(parent, "reg", &id); + if (ret) { + dev_err(scpsys->dev, "%pOF: failed to get parent domain id\n", child); + goto err_put_node; + } + + if (!scpsys->pd_data.domains[id]) { + ret = -EINVAL; + dev_err(scpsys->dev, "power domain with id %d does not exist\n", id); + goto err_put_node; + } + + parent_pd = scpsys->pd_data.domains[id]; + + child_pd = scpsys_add_one_domain(scpsys, child); + if (IS_ERR(child_pd)) { + ret = PTR_ERR(child_pd); + dev_err_probe(scpsys->dev, ret, "%pOF: failed to get child domain id\n", + child); + goto err_put_node; + } + + ret = pm_genpd_add_subdomain(parent_pd, child_pd); + if (ret) { + dev_err(scpsys->dev, "failed to add %s subdomain to parent %s\n", + child_pd->name, parent_pd->name); + goto err_put_node; + } else { + dev_dbg(scpsys->dev, "%s add subdomain: %s\n", parent_pd->name, + child_pd->name); + } + + /* recursive call to add all subdomains */ + ret = scpsys_add_subdomain(scpsys, child); + if (ret) + goto err_put_node; + } + + return 0; + +err_put_node: + of_node_put(child); + return ret; +} + +static void scpsys_remove_one_domain(struct scpsys_domain *pd) +{ + int ret; + + if (scpsys_domain_is_on(pd)) + scpsys_power_off(&pd->genpd); + + /* + * We're in the error cleanup already, so we only complain, + * but won't emit another error on top of the original one. + */ + ret = pm_genpd_remove(&pd->genpd); + if (ret < 0) + dev_err(pd->scpsys->dev, + "failed to remove domain '%s' : %d - state may be inconsistent\n", + pd->genpd.name, ret); + + clk_bulk_put(pd->num_clks, pd->clks); + clk_bulk_put(pd->num_subsys_clks, pd->subsys_clks); +} + +static void scpsys_domain_cleanup(struct scpsys *scpsys) +{ + struct generic_pm_domain *genpd; + struct scpsys_domain *pd; + int i; + + for (i = scpsys->pd_data.num_domains - 1; i >= 0; i--) { + genpd = scpsys->pd_data.domains[i]; + if (genpd) { + pd = to_scpsys_domain(genpd); + scpsys_remove_one_domain(pd); + } + } +} + +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, + }, + { + .compatible = "mediatek,mt8173-power-controller", + .data = &mt8173_scpsys_data, + }, + { + .compatible = "mediatek,mt8183-power-controller", + .data = &mt8183_scpsys_data, + }, + { + .compatible = "mediatek,mt8186-power-controller", + .data = &mt8186_scpsys_data, + }, + { + .compatible = "mediatek,mt8188-power-controller", + .data = &mt8188_scpsys_data, + }, + { + .compatible = "mediatek,mt8192-power-controller", + .data = &mt8192_scpsys_data, + }, + { + .compatible = "mediatek,mt8195-power-controller", + .data = &mt8195_scpsys_data, + }, + { } +}; + +static int scpsys_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct scpsys_soc_data *soc; + struct device_node *node; + struct device *parent; + struct scpsys *scpsys; + int ret; + + soc = of_device_get_match_data(&pdev->dev); + if (!soc) { + dev_err(&pdev->dev, "no power controller data\n"); + return -EINVAL; + } + + scpsys = devm_kzalloc(dev, struct_size(scpsys, domains, soc->num_domains), GFP_KERNEL); + if (!scpsys) + return -ENOMEM; + + scpsys->dev = dev; + scpsys->soc_data = soc; + + scpsys->pd_data.domains = scpsys->domains; + scpsys->pd_data.num_domains = soc->num_domains; + + parent = dev->parent; + if (!parent) { + dev_err(dev, "no parent for syscon devices\n"); + return -ENODEV; + } + + scpsys->base = syscon_node_to_regmap(parent->of_node); + if (IS_ERR(scpsys->base)) { + dev_err(dev, "no regmap available\n"); + return PTR_ERR(scpsys->base); + } + + ret = -ENODEV; + for_each_available_child_of_node(np, node) { + struct generic_pm_domain *domain; + + domain = scpsys_add_one_domain(scpsys, node); + if (IS_ERR(domain)) { + ret = PTR_ERR(domain); + of_node_put(node); + goto err_cleanup_domains; + } + + ret = scpsys_add_subdomain(scpsys, node); + if (ret) { + of_node_put(node); + goto err_cleanup_domains; + } + } + + if (ret) { + dev_dbg(dev, "no power domains present\n"); + return ret; + } + + ret = of_genpd_add_provider_onecell(np, &scpsys->pd_data); + if (ret) { + dev_err(dev, "failed to add provider: %d\n", ret); + goto err_cleanup_domains; + } + + return 0; + +err_cleanup_domains: + scpsys_domain_cleanup(scpsys); + return ret; +} + +static struct platform_driver scpsys_pm_domain_driver = { + .probe = scpsys_probe, + .driver = { + .name = "mtk-power-controller", + .suppress_bind_attrs = true, + .of_match_table = scpsys_of_match, + }, +}; +builtin_platform_driver(scpsys_pm_domain_driver); diff --git a/drivers/pmdomain/mediatek/mtk-pm-domains.h b/drivers/pmdomain/mediatek/mtk-pm-domains.h new file mode 100644 index 000000000000..5ec53ee073c4 --- /dev/null +++ b/drivers/pmdomain/mediatek/mtk-pm-domains.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __SOC_MEDIATEK_MTK_PM_DOMAINS_H +#define __SOC_MEDIATEK_MTK_PM_DOMAINS_H + +#define MTK_SCPD_ACTIVE_WAKEUP BIT(0) +#define MTK_SCPD_FWAIT_SRAM BIT(1) +#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_EXT_BUCK_ISO BIT(6) +#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) + +#define SPM_VDE_PWR_CON 0x0210 +#define SPM_MFG_PWR_CON 0x0214 +#define SPM_VEN_PWR_CON 0x0230 +#define SPM_ISP_PWR_CON 0x0238 +#define SPM_DIS_PWR_CON 0x023c +#define SPM_CONN_PWR_CON 0x0280 +#define SPM_VEN2_PWR_CON 0x0298 +#define SPM_AUDIO_PWR_CON 0x029c +#define SPM_MFG_2D_PWR_CON 0x02c0 +#define SPM_MFG_ASYNC_PWR_CON 0x02c4 +#define SPM_USB_PWR_CON 0x02cc + +#define SPM_PWR_STATUS 0x060c +#define SPM_PWR_STATUS_2ND 0x0610 + +#define PWR_STATUS_CONN BIT(1) +#define PWR_STATUS_DISP BIT(3) +#define PWR_STATUS_MFG BIT(4) +#define PWR_STATUS_ISP BIT(5) +#define PWR_STATUS_VDEC BIT(7) +#define PWR_STATUS_VENC_LT BIT(20) +#define PWR_STATUS_VENC BIT(21) +#define PWR_STATUS_MFG_2D BIT(22) +#define PWR_STATUS_MFG_ASYNC BIT(23) +#define PWR_STATUS_AUDIO BIT(24) +#define PWR_STATUS_USB BIT(25) + +#define SPM_MAX_BUS_PROT_DATA 6 + +#define _BUS_PROT(_mask, _set, _clr, _sta, _update, _ignore) { \ + .bus_prot_mask = (_mask), \ + .bus_prot_set = _set, \ + .bus_prot_clr = _clr, \ + .bus_prot_sta = _sta, \ + .bus_prot_reg_update = _update, \ + .ignore_clr_ack = _ignore, \ + } + +#define BUS_PROT_WR(_mask, _set, _clr, _sta) \ + _BUS_PROT(_mask, _set, _clr, _sta, false, false) + +#define BUS_PROT_WR_IGN(_mask, _set, _clr, _sta) \ + _BUS_PROT(_mask, _set, _clr, _sta, false, true) + +#define BUS_PROT_UPDATE(_mask, _set, _clr, _sta) \ + _BUS_PROT(_mask, _set, _clr, _sta, true, false) + +#define BUS_PROT_UPDATE_TOPAXI(_mask) \ + BUS_PROT_UPDATE(_mask, \ + INFRA_TOPAXI_PROTECTEN, \ + INFRA_TOPAXI_PROTECTEN, \ + INFRA_TOPAXI_PROTECTSTA1) + +struct scpsys_bus_prot_data { + u32 bus_prot_mask; + u32 bus_prot_set; + u32 bus_prot_clr; + u32 bus_prot_sta; + bool bus_prot_reg_update; + bool ignore_clr_ack; +}; + +/** + * struct scpsys_domain_data - scp domain data for power on/off flow + * @name: The name of the power domain. + * @sta_mask: The mask for power on/off status bit. + * @ctl_offs: The offset for main power control register. + * @sram_pdn_bits: The mask for sram power control bits. + * @sram_pdn_ack_bits: The mask for sram power control acked bits. + * @ext_buck_iso_offs: The offset for external buck isolation + * @ext_buck_iso_mask: The mask for external buck isolation + * @caps: The flag for active wake-up action. + * @bp_infracfg: bus protection for infracfg subsystem + * @bp_smi: bus protection for smi subsystem + */ +struct scpsys_domain_data { + const char *name; + u32 sta_mask; + int ctl_offs; + u32 sram_pdn_bits; + u32 sram_pdn_ack_bits; + int ext_buck_iso_offs; + u32 ext_buck_iso_mask; + u8 caps; + const struct scpsys_bus_prot_data bp_infracfg[SPM_MAX_BUS_PROT_DATA]; + const struct scpsys_bus_prot_data bp_smi[SPM_MAX_BUS_PROT_DATA]; + int pwr_sta_offs; + int pwr_sta2nd_offs; +}; + +struct scpsys_soc_data { + const struct scpsys_domain_data *domains_data; + int num_domains; +}; + +#endif /* __SOC_MEDIATEK_MTK_PM_DOMAINS_H */ diff --git a/drivers/pmdomain/mediatek/mtk-scpsys.c b/drivers/pmdomain/mediatek/mtk-scpsys.c new file mode 100644 index 000000000000..b374d01fdac7 --- /dev/null +++ b/drivers/pmdomain/mediatek/mtk-scpsys.c @@ -0,0 +1,1147 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2015 Pengutronix, Sascha Hauer + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define MTK_POLL_DELAY_US 10 +#define MTK_POLL_TIMEOUT USEC_PER_SEC + +#define MTK_SCPD_ACTIVE_WAKEUP BIT(0) +#define MTK_SCPD_FWAIT_SRAM BIT(1) +#define MTK_SCPD_CAPS(_scpd, _x) ((_scpd)->data->caps & (_x)) + +#define SPM_VDE_PWR_CON 0x0210 +#define SPM_MFG_PWR_CON 0x0214 +#define SPM_VEN_PWR_CON 0x0230 +#define SPM_ISP_PWR_CON 0x0238 +#define SPM_DIS_PWR_CON 0x023c +#define SPM_CONN_PWR_CON 0x0280 +#define SPM_VEN2_PWR_CON 0x0298 +#define SPM_AUDIO_PWR_CON 0x029c /* MT8173, MT2712 */ +#define SPM_BDP_PWR_CON 0x029c /* MT2701 */ +#define SPM_ETH_PWR_CON 0x02a0 +#define SPM_HIF_PWR_CON 0x02a4 +#define SPM_IFR_MSC_PWR_CON 0x02a8 +#define SPM_MFG_2D_PWR_CON 0x02c0 +#define SPM_MFG_ASYNC_PWR_CON 0x02c4 +#define SPM_USB_PWR_CON 0x02cc +#define SPM_USB2_PWR_CON 0x02d4 /* MT2712 */ +#define SPM_ETHSYS_PWR_CON 0x02e0 /* MT7622 */ +#define SPM_HIF0_PWR_CON 0x02e4 /* MT7622 */ +#define SPM_HIF1_PWR_CON 0x02e8 /* MT7622 */ +#define SPM_WB_PWR_CON 0x02ec /* MT7622 */ + +#define SPM_PWR_STATUS 0x060c +#define SPM_PWR_STATUS_2ND 0x0610 + +#define PWR_RST_B_BIT BIT(0) +#define PWR_ISO_BIT BIT(1) +#define PWR_ON_BIT BIT(2) +#define PWR_ON_2ND_BIT BIT(3) +#define PWR_CLK_DIS_BIT BIT(4) + +#define PWR_STATUS_CONN BIT(1) +#define PWR_STATUS_DISP BIT(3) +#define PWR_STATUS_MFG BIT(4) +#define PWR_STATUS_ISP BIT(5) +#define PWR_STATUS_VDEC BIT(7) +#define PWR_STATUS_BDP BIT(14) +#define PWR_STATUS_ETH BIT(15) +#define PWR_STATUS_HIF BIT(16) +#define PWR_STATUS_IFR_MSC BIT(17) +#define PWR_STATUS_USB2 BIT(19) /* MT2712 */ +#define PWR_STATUS_VENC_LT BIT(20) +#define PWR_STATUS_VENC BIT(21) +#define PWR_STATUS_MFG_2D BIT(22) /* MT8173 */ +#define PWR_STATUS_MFG_ASYNC BIT(23) /* MT8173 */ +#define PWR_STATUS_AUDIO BIT(24) /* MT8173, MT2712 */ +#define PWR_STATUS_USB BIT(25) /* MT8173, MT2712 */ +#define PWR_STATUS_ETHSYS BIT(24) /* MT7622 */ +#define PWR_STATUS_HIF0 BIT(25) /* MT7622 */ +#define PWR_STATUS_HIF1 BIT(26) /* MT7622 */ +#define PWR_STATUS_WB BIT(27) /* MT7622 */ + +enum clk_id { + CLK_NONE, + CLK_MM, + CLK_MFG, + CLK_VENC, + CLK_VENC_LT, + CLK_ETHIF, + CLK_VDEC, + CLK_HIFSEL, + CLK_JPGDEC, + CLK_AUDIO, + CLK_MAX, +}; + +static const char * const clk_names[] = { + NULL, + "mm", + "mfg", + "venc", + "venc_lt", + "ethif", + "vdec", + "hif_sel", + "jpgdec", + "audio", + NULL, +}; + +#define MAX_CLKS 3 + +/** + * struct scp_domain_data - scp domain data for power on/off flow + * @name: The domain name. + * @sta_mask: The mask for power on/off status bit. + * @ctl_offs: The offset for main power control register. + * @sram_pdn_bits: The mask for sram power control bits. + * @sram_pdn_ack_bits: The mask for sram power control acked bits. + * @bus_prot_mask: The mask for single step bus protection. + * @clk_id: The basic clocks required by this power domain. + * @caps: The flag for active wake-up action. + */ +struct scp_domain_data { + const char *name; + u32 sta_mask; + int ctl_offs; + u32 sram_pdn_bits; + u32 sram_pdn_ack_bits; + u32 bus_prot_mask; + enum clk_id clk_id[MAX_CLKS]; + u8 caps; +}; + +struct scp; + +struct scp_domain { + struct generic_pm_domain genpd; + struct scp *scp; + struct clk *clk[MAX_CLKS]; + const struct scp_domain_data *data; + struct regulator *supply; +}; + +struct scp_ctrl_reg { + int pwr_sta_offs; + int pwr_sta2nd_offs; +}; + +struct scp { + struct scp_domain *domains; + struct genpd_onecell_data pd_data; + struct device *dev; + void __iomem *base; + struct regmap *infracfg; + struct scp_ctrl_reg ctrl_reg; + bool bus_prot_reg_update; +}; + +struct scp_subdomain { + int origin; + int subdomain; +}; + +struct scp_soc_data { + const struct scp_domain_data *domains; + int num_domains; + const struct scp_subdomain *subdomains; + int num_subdomains; + const struct scp_ctrl_reg regs; + bool bus_prot_reg_update; +}; + +static int scpsys_domain_is_on(struct scp_domain *scpd) +{ + struct scp *scp = scpd->scp; + + u32 status = readl(scp->base + scp->ctrl_reg.pwr_sta_offs) & + scpd->data->sta_mask; + u32 status2 = readl(scp->base + scp->ctrl_reg.pwr_sta2nd_offs) & + scpd->data->sta_mask; + + /* + * A domain is on when both status bits are set. If only one is set + * return an error. This happens while powering up a domain + */ + + if (status && status2) + return true; + if (!status && !status2) + return false; + + return -EINVAL; +} + +static int scpsys_regulator_enable(struct scp_domain *scpd) +{ + if (!scpd->supply) + return 0; + + return regulator_enable(scpd->supply); +} + +static int scpsys_regulator_disable(struct scp_domain *scpd) +{ + if (!scpd->supply) + return 0; + + return regulator_disable(scpd->supply); +} + +static void scpsys_clk_disable(struct clk *clk[], int max_num) +{ + int i; + + for (i = max_num - 1; i >= 0; i--) + clk_disable_unprepare(clk[i]); +} + +static int scpsys_clk_enable(struct clk *clk[], int max_num) +{ + int i, ret = 0; + + for (i = 0; i < max_num && clk[i]; i++) { + ret = clk_prepare_enable(clk[i]); + if (ret) { + scpsys_clk_disable(clk, i); + break; + } + } + + return ret; +} + +static int scpsys_sram_enable(struct scp_domain *scpd, void __iomem *ctl_addr) +{ + u32 val; + u32 pdn_ack = scpd->data->sram_pdn_ack_bits; + int tmp; + + val = readl(ctl_addr); + val &= ~scpd->data->sram_pdn_bits; + writel(val, ctl_addr); + + /* Either wait until SRAM_PDN_ACK all 0 or have a force wait */ + if (MTK_SCPD_CAPS(scpd, MTK_SCPD_FWAIT_SRAM)) { + /* + * Currently, MTK_SCPD_FWAIT_SRAM is necessary only for + * MT7622_POWER_DOMAIN_WB and thus just a trivial setup + * is applied here. + */ + usleep_range(12000, 12100); + } else { + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + int ret = readl_poll_timeout(ctl_addr, tmp, + (tmp & pdn_ack) == 0, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + return ret; + } + + return 0; +} + +static int scpsys_sram_disable(struct scp_domain *scpd, void __iomem *ctl_addr) +{ + u32 val; + u32 pdn_ack = scpd->data->sram_pdn_ack_bits; + int tmp; + + val = readl(ctl_addr); + val |= scpd->data->sram_pdn_bits; + writel(val, ctl_addr); + + /* Either wait until SRAM_PDN_ACK all 1 or 0 */ + return readl_poll_timeout(ctl_addr, tmp, + (tmp & pdn_ack) == pdn_ack, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); +} + +static int scpsys_bus_protect_enable(struct scp_domain *scpd) +{ + struct scp *scp = scpd->scp; + + if (!scpd->data->bus_prot_mask) + return 0; + + return mtk_infracfg_set_bus_protection(scp->infracfg, + scpd->data->bus_prot_mask, + scp->bus_prot_reg_update); +} + +static int scpsys_bus_protect_disable(struct scp_domain *scpd) +{ + struct scp *scp = scpd->scp; + + if (!scpd->data->bus_prot_mask) + return 0; + + return mtk_infracfg_clear_bus_protection(scp->infracfg, + scpd->data->bus_prot_mask, + scp->bus_prot_reg_update); +} + +static int scpsys_power_on(struct generic_pm_domain *genpd) +{ + struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); + struct scp *scp = scpd->scp; + void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; + u32 val; + int ret, tmp; + + ret = scpsys_regulator_enable(scpd); + if (ret < 0) + return ret; + + ret = scpsys_clk_enable(scpd->clk, MAX_CLKS); + if (ret) + goto err_clk; + + /* subsys power on */ + val = readl(ctl_addr); + val |= PWR_ON_BIT; + writel(val, ctl_addr); + val |= PWR_ON_2ND_BIT; + writel(val, ctl_addr); + + /* wait until PWR_ACK = 1 */ + ret = readx_poll_timeout(scpsys_domain_is_on, scpd, tmp, tmp > 0, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + goto err_pwr_ack; + + val &= ~PWR_CLK_DIS_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ISO_BIT; + writel(val, ctl_addr); + + val |= PWR_RST_B_BIT; + writel(val, ctl_addr); + + ret = scpsys_sram_enable(scpd, ctl_addr); + if (ret < 0) + goto err_pwr_ack; + + ret = scpsys_bus_protect_disable(scpd); + if (ret < 0) + goto err_pwr_ack; + + return 0; + +err_pwr_ack: + scpsys_clk_disable(scpd->clk, MAX_CLKS); +err_clk: + scpsys_regulator_disable(scpd); + + dev_err(scp->dev, "Failed to power on domain %s\n", genpd->name); + + return ret; +} + +static int scpsys_power_off(struct generic_pm_domain *genpd) +{ + struct scp_domain *scpd = container_of(genpd, struct scp_domain, genpd); + struct scp *scp = scpd->scp; + void __iomem *ctl_addr = scp->base + scpd->data->ctl_offs; + u32 val; + int ret, tmp; + + ret = scpsys_bus_protect_enable(scpd); + if (ret < 0) + goto out; + + ret = scpsys_sram_disable(scpd, ctl_addr); + if (ret < 0) + goto out; + + /* subsys power off */ + val = readl(ctl_addr); + val |= PWR_ISO_BIT; + writel(val, ctl_addr); + + val &= ~PWR_RST_B_BIT; + writel(val, ctl_addr); + + val |= PWR_CLK_DIS_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ON_BIT; + writel(val, ctl_addr); + + val &= ~PWR_ON_2ND_BIT; + writel(val, ctl_addr); + + /* wait until PWR_ACK = 0 */ + ret = readx_poll_timeout(scpsys_domain_is_on, scpd, tmp, tmp == 0, + MTK_POLL_DELAY_US, MTK_POLL_TIMEOUT); + if (ret < 0) + goto out; + + scpsys_clk_disable(scpd->clk, MAX_CLKS); + + ret = scpsys_regulator_disable(scpd); + if (ret < 0) + goto out; + + return 0; + +out: + dev_err(scp->dev, "Failed to power off domain %s\n", genpd->name); + + return ret; +} + +static void init_clks(struct platform_device *pdev, struct clk **clk) +{ + int i; + + for (i = CLK_NONE + 1; i < CLK_MAX; i++) + clk[i] = devm_clk_get(&pdev->dev, clk_names[i]); +} + +static struct scp *init_scp(struct platform_device *pdev, + const struct scp_domain_data *scp_domain_data, int num, + const struct scp_ctrl_reg *scp_ctrl_reg, + bool bus_prot_reg_update) +{ + struct genpd_onecell_data *pd_data; + struct resource *res; + int i, j; + struct scp *scp; + struct clk *clk[CLK_MAX]; + + scp = devm_kzalloc(&pdev->dev, sizeof(*scp), GFP_KERNEL); + if (!scp) + return ERR_PTR(-ENOMEM); + + scp->ctrl_reg.pwr_sta_offs = scp_ctrl_reg->pwr_sta_offs; + scp->ctrl_reg.pwr_sta2nd_offs = scp_ctrl_reg->pwr_sta2nd_offs; + + scp->bus_prot_reg_update = bus_prot_reg_update; + + scp->dev = &pdev->dev; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + scp->base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(scp->base)) + return ERR_CAST(scp->base); + + scp->domains = devm_kcalloc(&pdev->dev, + num, sizeof(*scp->domains), GFP_KERNEL); + if (!scp->domains) + return ERR_PTR(-ENOMEM); + + pd_data = &scp->pd_data; + + pd_data->domains = devm_kcalloc(&pdev->dev, + num, sizeof(*pd_data->domains), GFP_KERNEL); + if (!pd_data->domains) + return ERR_PTR(-ENOMEM); + + scp->infracfg = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, + "infracfg"); + if (IS_ERR(scp->infracfg)) { + dev_err(&pdev->dev, "Cannot find infracfg controller: %ld\n", + PTR_ERR(scp->infracfg)); + return ERR_CAST(scp->infracfg); + } + + for (i = 0; i < num; i++) { + struct scp_domain *scpd = &scp->domains[i]; + const struct scp_domain_data *data = &scp_domain_data[i]; + + scpd->supply = devm_regulator_get_optional(&pdev->dev, data->name); + if (IS_ERR(scpd->supply)) { + if (PTR_ERR(scpd->supply) == -ENODEV) + scpd->supply = NULL; + else + return ERR_CAST(scpd->supply); + } + } + + pd_data->num_domains = num; + + init_clks(pdev, clk); + + for (i = 0; i < num; i++) { + struct scp_domain *scpd = &scp->domains[i]; + struct generic_pm_domain *genpd = &scpd->genpd; + const struct scp_domain_data *data = &scp_domain_data[i]; + + pd_data->domains[i] = genpd; + scpd->scp = scp; + + scpd->data = data; + + for (j = 0; j < MAX_CLKS && data->clk_id[j]; j++) { + struct clk *c = clk[data->clk_id[j]]; + + if (IS_ERR(c)) { + dev_err(&pdev->dev, "%s: clk unavailable\n", + data->name); + return ERR_CAST(c); + } + + scpd->clk[j] = c; + } + + genpd->name = data->name; + genpd->power_off = scpsys_power_off; + genpd->power_on = scpsys_power_on; + if (MTK_SCPD_CAPS(scpd, MTK_SCPD_ACTIVE_WAKEUP)) + genpd->flags |= GENPD_FLAG_ACTIVE_WAKEUP; + } + + return scp; +} + +static void mtk_register_power_domains(struct platform_device *pdev, + struct scp *scp, int num) +{ + struct genpd_onecell_data *pd_data; + int i, ret; + + for (i = 0; i < num; i++) { + struct scp_domain *scpd = &scp->domains[i]; + struct generic_pm_domain *genpd = &scpd->genpd; + bool on; + + /* + * Initially turn on all domains to make the domains usable + * with !CONFIG_PM and to get the hardware in sync with the + * software. The unused domains will be switched off during + * late_init time. + */ + on = !WARN_ON(genpd->power_on(genpd) < 0); + + pm_genpd_init(genpd, NULL, !on); + } + + /* + * We are not allowed to fail here since there is no way to unregister + * a power domain. Once registered above we have to keep the domains + * valid. + */ + + pd_data = &scp->pd_data; + + ret = of_genpd_add_provider_onecell(pdev->dev.of_node, pd_data); + if (ret) + dev_err(&pdev->dev, "Failed to add OF provider: %d\n", ret); +} + +/* + * MT2701 power domain support + */ + +static const struct scp_domain_data scp_domain_data_mt2701[] = { + [MT2701_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = SPM_CONN_PWR_CON, + .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_CONN_M | + MT2701_TOP_AXI_PROT_EN_CONN_S, + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_DISP] = { + .name = "disp", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .clk_id = {CLK_MM}, + .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_MM_M0, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MFG}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MM}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = {CLK_MM}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_BDP] = { + .name = "bdp", + .sta_mask = PWR_STATUS_BDP, + .ctl_offs = SPM_BDP_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_ETH] = { + .name = "eth", + .sta_mask = PWR_STATUS_ETH, + .ctl_offs = SPM_ETH_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_ETHIF}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_HIF] = { + .name = "hif", + .sta_mask = PWR_STATUS_HIF, + .ctl_offs = SPM_HIF_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_ETHIF}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2701_POWER_DOMAIN_IFR_MSC] = { + .name = "ifr_msc", + .sta_mask = PWR_STATUS_IFR_MSC, + .ctl_offs = SPM_IFR_MSC_PWR_CON, + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, +}; + +/* + * MT2712 power domain support + */ +static const struct scp_domain_data scp_domain_data_mt2712[] = { + [MT2712_POWER_DOMAIN_MM] = { + .name = "mm", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MM}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MM, CLK_VDEC}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = PWR_STATUS_VENC, + .ctl_offs = SPM_VEN_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_MM, CLK_VENC, CLK_JPGDEC}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = {CLK_MM}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = PWR_STATUS_AUDIO, + .ctl_offs = SPM_AUDIO_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_AUDIO}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_USB] = { + .name = "usb", + .sta_mask = PWR_STATUS_USB, + .ctl_offs = SPM_USB_PWR_CON, + .sram_pdn_bits = GENMASK(10, 8), + .sram_pdn_ack_bits = GENMASK(14, 12), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_USB2] = { + .name = "usb2", + .sta_mask = PWR_STATUS_USB2, + .ctl_offs = SPM_USB2_PWR_CON, + .sram_pdn_bits = GENMASK(10, 8), + .sram_pdn_ack_bits = GENMASK(14, 12), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(16, 16), + .clk_id = {CLK_MFG}, + .bus_prot_mask = BIT(14) | BIT(21) | BIT(23), + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_MFG_SC1] = { + .name = "mfg_sc1", + .sta_mask = BIT(22), + .ctl_offs = 0x02c0, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(16, 16), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_MFG_SC2] = { + .name = "mfg_sc2", + .sta_mask = BIT(23), + .ctl_offs = 0x02c4, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(16, 16), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT2712_POWER_DOMAIN_MFG_SC3] = { + .name = "mfg_sc3", + .sta_mask = BIT(30), + .ctl_offs = 0x01f8, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(16, 16), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, +}; + +static const struct scp_subdomain scp_subdomain_mt2712[] = { + {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_VDEC}, + {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_VENC}, + {MT2712_POWER_DOMAIN_MM, MT2712_POWER_DOMAIN_ISP}, + {MT2712_POWER_DOMAIN_MFG, MT2712_POWER_DOMAIN_MFG_SC1}, + {MT2712_POWER_DOMAIN_MFG_SC1, MT2712_POWER_DOMAIN_MFG_SC2}, + {MT2712_POWER_DOMAIN_MFG_SC2, MT2712_POWER_DOMAIN_MFG_SC3}, +}; + +/* + * MT6797 power domain support + */ + +static const struct scp_domain_data scp_domain_data_mt6797[] = { + [MT6797_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = BIT(7), + .ctl_offs = 0x300, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_VDEC}, + }, + [MT6797_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = BIT(21), + .ctl_offs = 0x304, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_NONE}, + }, + [MT6797_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = BIT(5), + .ctl_offs = 0x308, + .sram_pdn_bits = GENMASK(9, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = {CLK_NONE}, + }, + [MT6797_POWER_DOMAIN_MM] = { + .name = "mm", + .sta_mask = BIT(3), + .ctl_offs = 0x30C, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MM}, + .bus_prot_mask = (BIT(1) | BIT(2)), + }, + [MT6797_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = BIT(24), + .ctl_offs = 0x314, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_NONE}, + }, + [MT6797_POWER_DOMAIN_MFG_ASYNC] = { + .name = "mfg_async", + .sta_mask = BIT(13), + .ctl_offs = 0x334, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .clk_id = {CLK_MFG}, + }, + [MT6797_POWER_DOMAIN_MJC] = { + .name = "mjc", + .sta_mask = BIT(20), + .ctl_offs = 0x310, + .sram_pdn_bits = GENMASK(8, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_NONE}, + }, +}; + +#define SPM_PWR_STATUS_MT6797 0x0180 +#define SPM_PWR_STATUS_2ND_MT6797 0x0184 + +static const struct scp_subdomain scp_subdomain_mt6797[] = { + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_VDEC}, + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_ISP}, + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_VENC}, + {MT6797_POWER_DOMAIN_MM, MT6797_POWER_DOMAIN_MJC}, +}; + +/* + * MT7622 power domain support + */ + +static const struct scp_domain_data scp_domain_data_mt7622[] = { + [MT7622_POWER_DOMAIN_ETHSYS] = { + .name = "ethsys", + .sta_mask = PWR_STATUS_ETHSYS, + .ctl_offs = SPM_ETHSYS_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_NONE}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_ETHSYS, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT7622_POWER_DOMAIN_HIF0] = { + .name = "hif0", + .sta_mask = PWR_STATUS_HIF0, + .ctl_offs = SPM_HIF0_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_HIFSEL}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_HIF0, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT7622_POWER_DOMAIN_HIF1] = { + .name = "hif1", + .sta_mask = PWR_STATUS_HIF1, + .ctl_offs = SPM_HIF1_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_HIFSEL}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_HIF1, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT7622_POWER_DOMAIN_WB] = { + .name = "wb", + .sta_mask = PWR_STATUS_WB, + .ctl_offs = SPM_WB_PWR_CON, + .sram_pdn_bits = 0, + .sram_pdn_ack_bits = 0, + .clk_id = {CLK_NONE}, + .bus_prot_mask = MT7622_TOP_AXI_PROT_EN_WB, + .caps = MTK_SCPD_ACTIVE_WAKEUP | MTK_SCPD_FWAIT_SRAM, + }, +}; + +/* + * MT7623A power domain support + */ + +static const struct scp_domain_data scp_domain_data_mt7623a[] = { + [MT7623A_POWER_DOMAIN_CONN] = { + .name = "conn", + .sta_mask = PWR_STATUS_CONN, + .ctl_offs = SPM_CONN_PWR_CON, + .bus_prot_mask = MT2701_TOP_AXI_PROT_EN_CONN_M | + MT2701_TOP_AXI_PROT_EN_CONN_S, + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT7623A_POWER_DOMAIN_ETH] = { + .name = "eth", + .sta_mask = PWR_STATUS_ETH, + .ctl_offs = SPM_ETH_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_ETHIF}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT7623A_POWER_DOMAIN_HIF] = { + .name = "hif", + .sta_mask = PWR_STATUS_HIF, + .ctl_offs = SPM_HIF_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_ETHIF}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT7623A_POWER_DOMAIN_IFR_MSC] = { + .name = "ifr_msc", + .sta_mask = PWR_STATUS_IFR_MSC, + .ctl_offs = SPM_IFR_MSC_PWR_CON, + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, +}; + +/* + * MT8173 power domain support + */ + +static const struct scp_domain_data scp_domain_data_mt8173[] = { + [MT8173_POWER_DOMAIN_VDEC] = { + .name = "vdec", + .sta_mask = PWR_STATUS_VDEC, + .ctl_offs = SPM_VDE_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MM}, + }, + [MT8173_POWER_DOMAIN_VENC] = { + .name = "venc", + .sta_mask = PWR_STATUS_VENC, + .ctl_offs = SPM_VEN_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_MM, CLK_VENC}, + }, + [MT8173_POWER_DOMAIN_ISP] = { + .name = "isp", + .sta_mask = PWR_STATUS_ISP, + .ctl_offs = SPM_ISP_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = {CLK_MM}, + }, + [MT8173_POWER_DOMAIN_MM] = { + .name = "mm", + .sta_mask = PWR_STATUS_DISP, + .ctl_offs = SPM_DIS_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(12, 12), + .clk_id = {CLK_MM}, + .bus_prot_mask = MT8173_TOP_AXI_PROT_EN_MM_M0 | + MT8173_TOP_AXI_PROT_EN_MM_M1, + }, + [MT8173_POWER_DOMAIN_VENC_LT] = { + .name = "venc_lt", + .sta_mask = PWR_STATUS_VENC_LT, + .ctl_offs = SPM_VEN2_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_MM, CLK_VENC_LT}, + }, + [MT8173_POWER_DOMAIN_AUDIO] = { + .name = "audio", + .sta_mask = PWR_STATUS_AUDIO, + .ctl_offs = SPM_AUDIO_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_NONE}, + }, + [MT8173_POWER_DOMAIN_USB] = { + .name = "usb", + .sta_mask = PWR_STATUS_USB, + .ctl_offs = SPM_USB_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(15, 12), + .clk_id = {CLK_NONE}, + .caps = MTK_SCPD_ACTIVE_WAKEUP, + }, + [MT8173_POWER_DOMAIN_MFG_ASYNC] = { + .name = "mfg_async", + .sta_mask = PWR_STATUS_MFG_ASYNC, + .ctl_offs = SPM_MFG_ASYNC_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = 0, + .clk_id = {CLK_MFG}, + }, + [MT8173_POWER_DOMAIN_MFG_2D] = { + .name = "mfg_2d", + .sta_mask = PWR_STATUS_MFG_2D, + .ctl_offs = SPM_MFG_2D_PWR_CON, + .sram_pdn_bits = GENMASK(11, 8), + .sram_pdn_ack_bits = GENMASK(13, 12), + .clk_id = {CLK_NONE}, + }, + [MT8173_POWER_DOMAIN_MFG] = { + .name = "mfg", + .sta_mask = PWR_STATUS_MFG, + .ctl_offs = SPM_MFG_PWR_CON, + .sram_pdn_bits = GENMASK(13, 8), + .sram_pdn_ack_bits = GENMASK(21, 16), + .clk_id = {CLK_NONE}, + .bus_prot_mask = 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 scp_subdomain scp_subdomain_mt8173[] = { + {MT8173_POWER_DOMAIN_MFG_ASYNC, MT8173_POWER_DOMAIN_MFG_2D}, + {MT8173_POWER_DOMAIN_MFG_2D, MT8173_POWER_DOMAIN_MFG}, +}; + +static const struct scp_soc_data mt2701_data = { + .domains = scp_domain_data_mt2701, + .num_domains = ARRAY_SIZE(scp_domain_data_mt2701), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + }, + .bus_prot_reg_update = true, +}; + +static const struct scp_soc_data mt2712_data = { + .domains = scp_domain_data_mt2712, + .num_domains = ARRAY_SIZE(scp_domain_data_mt2712), + .subdomains = scp_subdomain_mt2712, + .num_subdomains = ARRAY_SIZE(scp_subdomain_mt2712), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + }, + .bus_prot_reg_update = false, +}; + +static const struct scp_soc_data mt6797_data = { + .domains = scp_domain_data_mt6797, + .num_domains = ARRAY_SIZE(scp_domain_data_mt6797), + .subdomains = scp_subdomain_mt6797, + .num_subdomains = ARRAY_SIZE(scp_subdomain_mt6797), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS_MT6797, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND_MT6797 + }, + .bus_prot_reg_update = true, +}; + +static const struct scp_soc_data mt7622_data = { + .domains = scp_domain_data_mt7622, + .num_domains = ARRAY_SIZE(scp_domain_data_mt7622), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + }, + .bus_prot_reg_update = true, +}; + +static const struct scp_soc_data mt7623a_data = { + .domains = scp_domain_data_mt7623a, + .num_domains = ARRAY_SIZE(scp_domain_data_mt7623a), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + }, + .bus_prot_reg_update = true, +}; + +static const struct scp_soc_data mt8173_data = { + .domains = scp_domain_data_mt8173, + .num_domains = ARRAY_SIZE(scp_domain_data_mt8173), + .subdomains = scp_subdomain_mt8173, + .num_subdomains = ARRAY_SIZE(scp_subdomain_mt8173), + .regs = { + .pwr_sta_offs = SPM_PWR_STATUS, + .pwr_sta2nd_offs = SPM_PWR_STATUS_2ND + }, + .bus_prot_reg_update = true, +}; + +/* + * scpsys driver init + */ + +static const struct of_device_id of_scpsys_match_tbl[] = { + { + .compatible = "mediatek,mt2701-scpsys", + .data = &mt2701_data, + }, { + .compatible = "mediatek,mt2712-scpsys", + .data = &mt2712_data, + }, { + .compatible = "mediatek,mt6797-scpsys", + .data = &mt6797_data, + }, { + .compatible = "mediatek,mt7622-scpsys", + .data = &mt7622_data, + }, { + .compatible = "mediatek,mt7623a-scpsys", + .data = &mt7623a_data, + }, { + .compatible = "mediatek,mt8173-scpsys", + .data = &mt8173_data, + }, { + /* sentinel */ + } +}; + +static int scpsys_probe(struct platform_device *pdev) +{ + const struct scp_subdomain *sd; + const struct scp_soc_data *soc; + struct scp *scp; + struct genpd_onecell_data *pd_data; + int i, ret; + + soc = of_device_get_match_data(&pdev->dev); + + scp = init_scp(pdev, soc->domains, soc->num_domains, &soc->regs, + soc->bus_prot_reg_update); + if (IS_ERR(scp)) + return PTR_ERR(scp); + + mtk_register_power_domains(pdev, scp, soc->num_domains); + + pd_data = &scp->pd_data; + + for (i = 0, sd = soc->subdomains; i < soc->num_subdomains; i++, sd++) { + ret = pm_genpd_add_subdomain(pd_data->domains[sd->origin], + pd_data->domains[sd->subdomain]); + if (ret && IS_ENABLED(CONFIG_PM)) + dev_err(&pdev->dev, "Failed to add subdomain: %d\n", + ret); + } + + return 0; +} + +static struct platform_driver scpsys_drv = { + .probe = scpsys_probe, + .driver = { + .name = "mtk-scpsys", + .suppress_bind_attrs = true, + .owner = THIS_MODULE, + .of_match_table = of_scpsys_match_tbl, + }, +}; +builtin_platform_driver(scpsys_drv); diff --git a/drivers/pmdomain/qcom/Makefile b/drivers/pmdomain/qcom/Makefile new file mode 100644 index 000000000000..403dfc5af095 --- /dev/null +++ b/drivers/pmdomain/qcom/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_QCOM_CPR) += cpr.o +obj-$(CONFIG_QCOM_RPMPD) += rpmpd.o +obj-$(CONFIG_QCOM_RPMHPD) += rpmhpd.o diff --git a/drivers/pmdomain/qcom/cpr.c b/drivers/pmdomain/qcom/cpr.c new file mode 100644 index 000000000000..94a3f0977212 --- /dev/null +++ b/drivers/pmdomain/qcom/cpr.c @@ -0,0 +1,1756 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2019, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register Offsets for RB-CPR and Bit Definitions */ + +/* RBCPR Version Register */ +#define REG_RBCPR_VERSION 0 +#define RBCPR_VER_2 0x02 +#define FLAGS_IGNORE_1ST_IRQ_STATUS BIT(0) + +/* RBCPR Gate Count and Target Registers */ +#define REG_RBCPR_GCNT_TARGET(n) (0x60 + 4 * (n)) + +#define RBCPR_GCNT_TARGET_TARGET_SHIFT 0 +#define RBCPR_GCNT_TARGET_TARGET_MASK GENMASK(11, 0) +#define RBCPR_GCNT_TARGET_GCNT_SHIFT 12 +#define RBCPR_GCNT_TARGET_GCNT_MASK GENMASK(9, 0) + +/* RBCPR Timer Control */ +#define REG_RBCPR_TIMER_INTERVAL 0x44 +#define REG_RBIF_TIMER_ADJUST 0x4c + +#define RBIF_TIMER_ADJ_CONS_UP_MASK GENMASK(3, 0) +#define RBIF_TIMER_ADJ_CONS_UP_SHIFT 0 +#define RBIF_TIMER_ADJ_CONS_DOWN_MASK GENMASK(3, 0) +#define RBIF_TIMER_ADJ_CONS_DOWN_SHIFT 4 +#define RBIF_TIMER_ADJ_CLAMP_INT_MASK GENMASK(7, 0) +#define RBIF_TIMER_ADJ_CLAMP_INT_SHIFT 8 + +/* RBCPR Config Register */ +#define REG_RBIF_LIMIT 0x48 +#define RBIF_LIMIT_CEILING_MASK GENMASK(5, 0) +#define RBIF_LIMIT_CEILING_SHIFT 6 +#define RBIF_LIMIT_FLOOR_BITS 6 +#define RBIF_LIMIT_FLOOR_MASK GENMASK(5, 0) + +#define RBIF_LIMIT_CEILING_DEFAULT RBIF_LIMIT_CEILING_MASK +#define RBIF_LIMIT_FLOOR_DEFAULT 0 + +#define REG_RBIF_SW_VLEVEL 0x94 +#define RBIF_SW_VLEVEL_DEFAULT 0x20 + +#define REG_RBCPR_STEP_QUOT 0x80 +#define RBCPR_STEP_QUOT_STEPQUOT_MASK GENMASK(7, 0) +#define RBCPR_STEP_QUOT_IDLE_CLK_MASK GENMASK(3, 0) +#define RBCPR_STEP_QUOT_IDLE_CLK_SHIFT 8 + +/* RBCPR Control Register */ +#define REG_RBCPR_CTL 0x90 + +#define RBCPR_CTL_LOOP_EN BIT(0) +#define RBCPR_CTL_TIMER_EN BIT(3) +#define RBCPR_CTL_SW_AUTO_CONT_ACK_EN BIT(5) +#define RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN BIT(6) +#define RBCPR_CTL_COUNT_MODE BIT(10) +#define RBCPR_CTL_UP_THRESHOLD_MASK GENMASK(3, 0) +#define RBCPR_CTL_UP_THRESHOLD_SHIFT 24 +#define RBCPR_CTL_DN_THRESHOLD_MASK GENMASK(3, 0) +#define RBCPR_CTL_DN_THRESHOLD_SHIFT 28 + +/* RBCPR Ack/Nack Response */ +#define REG_RBIF_CONT_ACK_CMD 0x98 +#define REG_RBIF_CONT_NACK_CMD 0x9c + +/* RBCPR Result status Register */ +#define REG_RBCPR_RESULT_0 0xa0 + +#define RBCPR_RESULT0_BUSY_SHIFT 19 +#define RBCPR_RESULT0_BUSY_MASK BIT(RBCPR_RESULT0_BUSY_SHIFT) +#define RBCPR_RESULT0_ERROR_LT0_SHIFT 18 +#define RBCPR_RESULT0_ERROR_SHIFT 6 +#define RBCPR_RESULT0_ERROR_MASK GENMASK(11, 0) +#define RBCPR_RESULT0_ERROR_STEPS_SHIFT 2 +#define RBCPR_RESULT0_ERROR_STEPS_MASK GENMASK(3, 0) +#define RBCPR_RESULT0_STEP_UP_SHIFT 1 + +/* RBCPR Interrupt Control Register */ +#define REG_RBIF_IRQ_EN(n) (0x100 + 4 * (n)) +#define REG_RBIF_IRQ_CLEAR 0x110 +#define REG_RBIF_IRQ_STATUS 0x114 + +#define CPR_INT_DONE BIT(0) +#define CPR_INT_MIN BIT(1) +#define CPR_INT_DOWN BIT(2) +#define CPR_INT_MID BIT(3) +#define CPR_INT_UP BIT(4) +#define CPR_INT_MAX BIT(5) +#define CPR_INT_CLAMP BIT(6) +#define CPR_INT_ALL (CPR_INT_DONE | CPR_INT_MIN | CPR_INT_DOWN | \ + CPR_INT_MID | CPR_INT_UP | CPR_INT_MAX | CPR_INT_CLAMP) +#define CPR_INT_DEFAULT (CPR_INT_UP | CPR_INT_DOWN) + +#define CPR_NUM_RING_OSC 8 + +/* CPR eFuse parameters */ +#define CPR_FUSE_TARGET_QUOT_BITS_MASK GENMASK(11, 0) + +#define CPR_FUSE_MIN_QUOT_DIFF 50 + +#define FUSE_REVISION_UNKNOWN (-1) + +enum voltage_change_dir { + NO_CHANGE, + DOWN, + UP, +}; + +struct cpr_fuse { + char *ring_osc; + char *init_voltage; + char *quotient; + char *quotient_offset; +}; + +struct fuse_corner_data { + int ref_uV; + int max_uV; + int min_uV; + int max_volt_scale; + int max_quot_scale; + /* fuse quot */ + int quot_offset; + int quot_scale; + int quot_adjust; + /* fuse quot_offset */ + int quot_offset_scale; + int quot_offset_adjust; +}; + +struct cpr_fuses { + int init_voltage_step; + int init_voltage_width; + struct fuse_corner_data *fuse_corner_data; +}; + +struct corner_data { + unsigned int fuse_corner; + unsigned long freq; +}; + +struct cpr_desc { + unsigned int num_fuse_corners; + int min_diff_quot; + int *step_quot; + + unsigned int timer_delay_us; + unsigned int timer_cons_up; + unsigned int timer_cons_down; + unsigned int up_threshold; + unsigned int down_threshold; + unsigned int idle_clocks; + unsigned int gcnt_us; + unsigned int vdd_apc_step_up_limit; + unsigned int vdd_apc_step_down_limit; + unsigned int clamp_timer_interval; + + struct cpr_fuses cpr_fuses; + bool reduce_to_fuse_uV; + bool reduce_to_corner_uV; +}; + +struct acc_desc { + unsigned int enable_reg; + u32 enable_mask; + + struct reg_sequence *config; + struct reg_sequence *settings; + int num_regs_per_fuse; +}; + +struct cpr_acc_desc { + const struct cpr_desc *cpr_desc; + const struct acc_desc *acc_desc; +}; + +struct fuse_corner { + int min_uV; + int max_uV; + int uV; + int quot; + int step_quot; + const struct reg_sequence *accs; + int num_accs; + unsigned long max_freq; + u8 ring_osc_idx; +}; + +struct corner { + int min_uV; + int max_uV; + int uV; + int last_uV; + int quot_adjust; + u32 save_ctl; + u32 save_irq; + unsigned long freq; + struct fuse_corner *fuse_corner; +}; + +struct cpr_drv { + unsigned int num_corners; + unsigned int ref_clk_khz; + + struct generic_pm_domain pd; + struct device *dev; + struct device *attached_cpu_dev; + struct mutex lock; + void __iomem *base; + struct corner *corner; + struct regulator *vdd_apc; + struct clk *cpu_clk; + struct regmap *tcsr; + bool loop_disabled; + u32 gcnt; + unsigned long flags; + + struct fuse_corner *fuse_corners; + struct corner *corners; + + const struct cpr_desc *desc; + const struct acc_desc *acc_desc; + const struct cpr_fuse *cpr_fuses; + + struct dentry *debugfs; +}; + +static bool cpr_is_allowed(struct cpr_drv *drv) +{ + return !drv->loop_disabled; +} + +static void cpr_write(struct cpr_drv *drv, u32 offset, u32 value) +{ + writel_relaxed(value, drv->base + offset); +} + +static u32 cpr_read(struct cpr_drv *drv, u32 offset) +{ + return readl_relaxed(drv->base + offset); +} + +static void +cpr_masked_write(struct cpr_drv *drv, u32 offset, u32 mask, u32 value) +{ + u32 val; + + val = readl_relaxed(drv->base + offset); + val &= ~mask; + val |= value & mask; + writel_relaxed(val, drv->base + offset); +} + +static void cpr_irq_clr(struct cpr_drv *drv) +{ + cpr_write(drv, REG_RBIF_IRQ_CLEAR, CPR_INT_ALL); +} + +static void cpr_irq_clr_nack(struct cpr_drv *drv) +{ + cpr_irq_clr(drv); + cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1); +} + +static void cpr_irq_clr_ack(struct cpr_drv *drv) +{ + cpr_irq_clr(drv); + cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1); +} + +static void cpr_irq_set(struct cpr_drv *drv, u32 int_bits) +{ + cpr_write(drv, REG_RBIF_IRQ_EN(0), int_bits); +} + +static void cpr_ctl_modify(struct cpr_drv *drv, u32 mask, u32 value) +{ + cpr_masked_write(drv, REG_RBCPR_CTL, mask, value); +} + +static void cpr_ctl_enable(struct cpr_drv *drv, struct corner *corner) +{ + u32 val, mask; + const struct cpr_desc *desc = drv->desc; + + /* Program Consecutive Up & Down */ + val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT; + val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT; + mask = RBIF_TIMER_ADJ_CONS_UP_MASK | RBIF_TIMER_ADJ_CONS_DOWN_MASK; + cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST, mask, val); + cpr_masked_write(drv, REG_RBCPR_CTL, + RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN | + RBCPR_CTL_SW_AUTO_CONT_ACK_EN, + corner->save_ctl); + cpr_irq_set(drv, corner->save_irq); + + if (cpr_is_allowed(drv) && corner->max_uV > corner->min_uV) + val = RBCPR_CTL_LOOP_EN; + else + val = 0; + cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, val); +} + +static void cpr_ctl_disable(struct cpr_drv *drv) +{ + cpr_irq_set(drv, 0); + cpr_ctl_modify(drv, RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN | + RBCPR_CTL_SW_AUTO_CONT_ACK_EN, 0); + cpr_masked_write(drv, REG_RBIF_TIMER_ADJUST, + RBIF_TIMER_ADJ_CONS_UP_MASK | + RBIF_TIMER_ADJ_CONS_DOWN_MASK, 0); + cpr_irq_clr(drv); + cpr_write(drv, REG_RBIF_CONT_ACK_CMD, 1); + cpr_write(drv, REG_RBIF_CONT_NACK_CMD, 1); + cpr_ctl_modify(drv, RBCPR_CTL_LOOP_EN, 0); +} + +static bool cpr_ctl_is_enabled(struct cpr_drv *drv) +{ + u32 reg_val; + + reg_val = cpr_read(drv, REG_RBCPR_CTL); + return reg_val & RBCPR_CTL_LOOP_EN; +} + +static bool cpr_ctl_is_busy(struct cpr_drv *drv) +{ + u32 reg_val; + + reg_val = cpr_read(drv, REG_RBCPR_RESULT_0); + return reg_val & RBCPR_RESULT0_BUSY_MASK; +} + +static void cpr_corner_save(struct cpr_drv *drv, struct corner *corner) +{ + corner->save_ctl = cpr_read(drv, REG_RBCPR_CTL); + corner->save_irq = cpr_read(drv, REG_RBIF_IRQ_EN(0)); +} + +static void cpr_corner_restore(struct cpr_drv *drv, struct corner *corner) +{ + u32 gcnt, ctl, irq, ro_sel, step_quot; + struct fuse_corner *fuse = corner->fuse_corner; + const struct cpr_desc *desc = drv->desc; + int i; + + ro_sel = fuse->ring_osc_idx; + gcnt = drv->gcnt; + gcnt |= fuse->quot - corner->quot_adjust; + + /* Program the step quotient and idle clocks */ + step_quot = desc->idle_clocks << RBCPR_STEP_QUOT_IDLE_CLK_SHIFT; + step_quot |= fuse->step_quot & RBCPR_STEP_QUOT_STEPQUOT_MASK; + cpr_write(drv, REG_RBCPR_STEP_QUOT, step_quot); + + /* Clear the target quotient value and gate count of all ROs */ + for (i = 0; i < CPR_NUM_RING_OSC; i++) + cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0); + + cpr_write(drv, REG_RBCPR_GCNT_TARGET(ro_sel), gcnt); + ctl = corner->save_ctl; + cpr_write(drv, REG_RBCPR_CTL, ctl); + irq = corner->save_irq; + cpr_irq_set(drv, irq); + dev_dbg(drv->dev, "gcnt = %#08x, ctl = %#08x, irq = %#08x\n", gcnt, + ctl, irq); +} + +static void cpr_set_acc(struct regmap *tcsr, struct fuse_corner *f, + struct fuse_corner *end) +{ + if (f == end) + return; + + if (f < end) { + for (f += 1; f <= end; f++) + regmap_multi_reg_write(tcsr, f->accs, f->num_accs); + } else { + for (f -= 1; f >= end; f--) + regmap_multi_reg_write(tcsr, f->accs, f->num_accs); + } +} + +static int cpr_pre_voltage(struct cpr_drv *drv, + struct fuse_corner *fuse_corner, + enum voltage_change_dir dir) +{ + struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner; + + if (drv->tcsr && dir == DOWN) + cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner); + + return 0; +} + +static int cpr_post_voltage(struct cpr_drv *drv, + struct fuse_corner *fuse_corner, + enum voltage_change_dir dir) +{ + struct fuse_corner *prev_fuse_corner = drv->corner->fuse_corner; + + if (drv->tcsr && dir == UP) + cpr_set_acc(drv->tcsr, prev_fuse_corner, fuse_corner); + + return 0; +} + +static int cpr_scale_voltage(struct cpr_drv *drv, struct corner *corner, + int new_uV, enum voltage_change_dir dir) +{ + int ret; + struct fuse_corner *fuse_corner = corner->fuse_corner; + + ret = cpr_pre_voltage(drv, fuse_corner, dir); + if (ret) + return ret; + + ret = regulator_set_voltage(drv->vdd_apc, new_uV, new_uV); + if (ret) { + dev_err_ratelimited(drv->dev, "failed to set apc voltage %d\n", + new_uV); + return ret; + } + + ret = cpr_post_voltage(drv, fuse_corner, dir); + if (ret) + return ret; + + return 0; +} + +static unsigned int cpr_get_cur_perf_state(struct cpr_drv *drv) +{ + return drv->corner ? drv->corner - drv->corners + 1 : 0; +} + +static int cpr_scale(struct cpr_drv *drv, enum voltage_change_dir dir) +{ + u32 val, error_steps, reg_mask; + int last_uV, new_uV, step_uV, ret; + struct corner *corner; + const struct cpr_desc *desc = drv->desc; + + if (dir != UP && dir != DOWN) + return 0; + + step_uV = regulator_get_linear_step(drv->vdd_apc); + if (!step_uV) + return -EINVAL; + + corner = drv->corner; + + val = cpr_read(drv, REG_RBCPR_RESULT_0); + + error_steps = val >> RBCPR_RESULT0_ERROR_STEPS_SHIFT; + error_steps &= RBCPR_RESULT0_ERROR_STEPS_MASK; + last_uV = corner->last_uV; + + if (dir == UP) { + if (desc->clamp_timer_interval && + error_steps < desc->up_threshold) { + /* + * Handle the case where another measurement started + * after the interrupt was triggered due to a core + * exiting from power collapse. + */ + error_steps = max(desc->up_threshold, + desc->vdd_apc_step_up_limit); + } + + if (last_uV >= corner->max_uV) { + cpr_irq_clr_nack(drv); + + /* Maximize the UP threshold */ + reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK; + reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT; + val = reg_mask; + cpr_ctl_modify(drv, reg_mask, val); + + /* Disable UP interrupt */ + cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_UP); + + return 0; + } + + if (error_steps > desc->vdd_apc_step_up_limit) + error_steps = desc->vdd_apc_step_up_limit; + + /* Calculate new voltage */ + new_uV = last_uV + error_steps * step_uV; + new_uV = min(new_uV, corner->max_uV); + + dev_dbg(drv->dev, + "UP: -> new_uV: %d last_uV: %d perf state: %u\n", + new_uV, last_uV, cpr_get_cur_perf_state(drv)); + } else { + if (desc->clamp_timer_interval && + error_steps < desc->down_threshold) { + /* + * Handle the case where another measurement started + * after the interrupt was triggered due to a core + * exiting from power collapse. + */ + error_steps = max(desc->down_threshold, + desc->vdd_apc_step_down_limit); + } + + if (last_uV <= corner->min_uV) { + cpr_irq_clr_nack(drv); + + /* Enable auto nack down */ + reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN; + val = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN; + + cpr_ctl_modify(drv, reg_mask, val); + + /* Disable DOWN interrupt */ + cpr_irq_set(drv, CPR_INT_DEFAULT & ~CPR_INT_DOWN); + + return 0; + } + + if (error_steps > desc->vdd_apc_step_down_limit) + error_steps = desc->vdd_apc_step_down_limit; + + /* Calculate new voltage */ + new_uV = last_uV - error_steps * step_uV; + new_uV = max(new_uV, corner->min_uV); + + dev_dbg(drv->dev, + "DOWN: -> new_uV: %d last_uV: %d perf state: %u\n", + new_uV, last_uV, cpr_get_cur_perf_state(drv)); + } + + ret = cpr_scale_voltage(drv, corner, new_uV, dir); + if (ret) { + cpr_irq_clr_nack(drv); + return ret; + } + drv->corner->last_uV = new_uV; + + if (dir == UP) { + /* Disable auto nack down */ + reg_mask = RBCPR_CTL_SW_AUTO_CONT_NACK_DN_EN; + val = 0; + } else { + /* Restore default threshold for UP */ + reg_mask = RBCPR_CTL_UP_THRESHOLD_MASK; + reg_mask <<= RBCPR_CTL_UP_THRESHOLD_SHIFT; + val = desc->up_threshold; + val <<= RBCPR_CTL_UP_THRESHOLD_SHIFT; + } + + cpr_ctl_modify(drv, reg_mask, val); + + /* Re-enable default interrupts */ + cpr_irq_set(drv, CPR_INT_DEFAULT); + + /* Ack */ + cpr_irq_clr_ack(drv); + + return 0; +} + +static irqreturn_t cpr_irq_handler(int irq, void *dev) +{ + struct cpr_drv *drv = dev; + const struct cpr_desc *desc = drv->desc; + irqreturn_t ret = IRQ_HANDLED; + u32 val; + + mutex_lock(&drv->lock); + + val = cpr_read(drv, REG_RBIF_IRQ_STATUS); + if (drv->flags & FLAGS_IGNORE_1ST_IRQ_STATUS) + val = cpr_read(drv, REG_RBIF_IRQ_STATUS); + + dev_dbg(drv->dev, "IRQ_STATUS = %#02x\n", val); + + if (!cpr_ctl_is_enabled(drv)) { + dev_dbg(drv->dev, "CPR is disabled\n"); + ret = IRQ_NONE; + } else if (cpr_ctl_is_busy(drv) && !desc->clamp_timer_interval) { + dev_dbg(drv->dev, "CPR measurement is not ready\n"); + } else if (!cpr_is_allowed(drv)) { + val = cpr_read(drv, REG_RBCPR_CTL); + dev_err_ratelimited(drv->dev, + "Interrupt broken? RBCPR_CTL = %#02x\n", + val); + ret = IRQ_NONE; + } else { + /* + * Following sequence of handling is as per each IRQ's + * priority + */ + if (val & CPR_INT_UP) { + cpr_scale(drv, UP); + } else if (val & CPR_INT_DOWN) { + cpr_scale(drv, DOWN); + } else if (val & CPR_INT_MIN) { + cpr_irq_clr_nack(drv); + } else if (val & CPR_INT_MAX) { + cpr_irq_clr_nack(drv); + } else if (val & CPR_INT_MID) { + /* RBCPR_CTL_SW_AUTO_CONT_ACK_EN is enabled */ + dev_dbg(drv->dev, "IRQ occurred for Mid Flag\n"); + } else { + dev_dbg(drv->dev, + "IRQ occurred for unknown flag (%#08x)\n", val); + } + + /* Save register values for the corner */ + cpr_corner_save(drv, drv->corner); + } + + mutex_unlock(&drv->lock); + + return ret; +} + +static int cpr_enable(struct cpr_drv *drv) +{ + int ret; + + ret = regulator_enable(drv->vdd_apc); + if (ret) + return ret; + + mutex_lock(&drv->lock); + + if (cpr_is_allowed(drv) && drv->corner) { + cpr_irq_clr(drv); + cpr_corner_restore(drv, drv->corner); + cpr_ctl_enable(drv, drv->corner); + } + + mutex_unlock(&drv->lock); + + return 0; +} + +static int cpr_disable(struct cpr_drv *drv) +{ + mutex_lock(&drv->lock); + + if (cpr_is_allowed(drv)) { + cpr_ctl_disable(drv); + cpr_irq_clr(drv); + } + + mutex_unlock(&drv->lock); + + return regulator_disable(drv->vdd_apc); +} + +static int cpr_config(struct cpr_drv *drv) +{ + int i; + u32 val, gcnt; + struct corner *corner; + const struct cpr_desc *desc = drv->desc; + + /* Disable interrupt and CPR */ + cpr_write(drv, REG_RBIF_IRQ_EN(0), 0); + cpr_write(drv, REG_RBCPR_CTL, 0); + + /* Program the default HW ceiling, floor and vlevel */ + val = (RBIF_LIMIT_CEILING_DEFAULT & RBIF_LIMIT_CEILING_MASK) + << RBIF_LIMIT_CEILING_SHIFT; + val |= RBIF_LIMIT_FLOOR_DEFAULT & RBIF_LIMIT_FLOOR_MASK; + cpr_write(drv, REG_RBIF_LIMIT, val); + cpr_write(drv, REG_RBIF_SW_VLEVEL, RBIF_SW_VLEVEL_DEFAULT); + + /* + * Clear the target quotient value and gate count of all + * ring oscillators + */ + for (i = 0; i < CPR_NUM_RING_OSC; i++) + cpr_write(drv, REG_RBCPR_GCNT_TARGET(i), 0); + + /* Init and save gcnt */ + gcnt = (drv->ref_clk_khz * desc->gcnt_us) / 1000; + gcnt = gcnt & RBCPR_GCNT_TARGET_GCNT_MASK; + gcnt <<= RBCPR_GCNT_TARGET_GCNT_SHIFT; + drv->gcnt = gcnt; + + /* Program the delay count for the timer */ + val = (drv->ref_clk_khz * desc->timer_delay_us) / 1000; + cpr_write(drv, REG_RBCPR_TIMER_INTERVAL, val); + dev_dbg(drv->dev, "Timer count: %#0x (for %d us)\n", val, + desc->timer_delay_us); + + /* Program Consecutive Up & Down */ + val = desc->timer_cons_down << RBIF_TIMER_ADJ_CONS_DOWN_SHIFT; + val |= desc->timer_cons_up << RBIF_TIMER_ADJ_CONS_UP_SHIFT; + val |= desc->clamp_timer_interval << RBIF_TIMER_ADJ_CLAMP_INT_SHIFT; + cpr_write(drv, REG_RBIF_TIMER_ADJUST, val); + + /* Program the control register */ + val = desc->up_threshold << RBCPR_CTL_UP_THRESHOLD_SHIFT; + val |= desc->down_threshold << RBCPR_CTL_DN_THRESHOLD_SHIFT; + val |= RBCPR_CTL_TIMER_EN | RBCPR_CTL_COUNT_MODE; + val |= RBCPR_CTL_SW_AUTO_CONT_ACK_EN; + cpr_write(drv, REG_RBCPR_CTL, val); + + for (i = 0; i < drv->num_corners; i++) { + corner = &drv->corners[i]; + corner->save_ctl = val; + corner->save_irq = CPR_INT_DEFAULT; + } + + cpr_irq_set(drv, CPR_INT_DEFAULT); + + val = cpr_read(drv, REG_RBCPR_VERSION); + if (val <= RBCPR_VER_2) + drv->flags |= FLAGS_IGNORE_1ST_IRQ_STATUS; + + return 0; +} + +static int cpr_set_performance_state(struct generic_pm_domain *domain, + unsigned int state) +{ + struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); + struct corner *corner, *end; + enum voltage_change_dir dir; + int ret = 0, new_uV; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "%s: setting perf state: %u (prev state: %u)\n", + __func__, state, cpr_get_cur_perf_state(drv)); + + /* + * Determine new corner we're going to. + * Remove one since lowest performance state is 1. + */ + corner = drv->corners + state - 1; + end = &drv->corners[drv->num_corners - 1]; + if (corner > end || corner < drv->corners) { + ret = -EINVAL; + goto unlock; + } + + /* Determine direction */ + if (drv->corner > corner) + dir = DOWN; + else if (drv->corner < corner) + dir = UP; + else + dir = NO_CHANGE; + + if (cpr_is_allowed(drv)) + new_uV = corner->last_uV; + else + new_uV = corner->uV; + + if (cpr_is_allowed(drv)) + cpr_ctl_disable(drv); + + ret = cpr_scale_voltage(drv, corner, new_uV, dir); + if (ret) + goto unlock; + + if (cpr_is_allowed(drv)) { + cpr_irq_clr(drv); + if (drv->corner != corner) + cpr_corner_restore(drv, corner); + cpr_ctl_enable(drv, corner); + } + + drv->corner = corner; + +unlock: + mutex_unlock(&drv->lock); + + return ret; +} + +static int +cpr_populate_ring_osc_idx(struct cpr_drv *drv) +{ + struct fuse_corner *fuse = drv->fuse_corners; + struct fuse_corner *end = fuse + drv->desc->num_fuse_corners; + const struct cpr_fuse *fuses = drv->cpr_fuses; + u32 data; + int ret; + + for (; fuse < end; fuse++, fuses++) { + ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->ring_osc, &data); + if (ret) + return ret; + fuse->ring_osc_idx = data; + } + + return 0; +} + +static int cpr_read_fuse_uV(const struct cpr_desc *desc, + const struct fuse_corner_data *fdata, + const char *init_v_efuse, + int step_volt, + struct cpr_drv *drv) +{ + int step_size_uV, steps, uV; + u32 bits = 0; + int ret; + + ret = nvmem_cell_read_variable_le_u32(drv->dev, init_v_efuse, &bits); + if (ret) + return ret; + + steps = bits & ~BIT(desc->cpr_fuses.init_voltage_width - 1); + /* Not two's complement.. instead highest bit is sign bit */ + if (bits & BIT(desc->cpr_fuses.init_voltage_width - 1)) + steps = -steps; + + step_size_uV = desc->cpr_fuses.init_voltage_step; + + uV = fdata->ref_uV + steps * step_size_uV; + return DIV_ROUND_UP(uV, step_volt) * step_volt; +} + +static int cpr_fuse_corner_init(struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + const struct cpr_fuse *fuses = drv->cpr_fuses; + const struct acc_desc *acc_desc = drv->acc_desc; + int i; + unsigned int step_volt; + struct fuse_corner_data *fdata; + struct fuse_corner *fuse, *end; + int uV; + const struct reg_sequence *accs; + int ret; + + accs = acc_desc->settings; + + step_volt = regulator_get_linear_step(drv->vdd_apc); + if (!step_volt) + return -EINVAL; + + /* Populate fuse_corner members */ + fuse = drv->fuse_corners; + end = &fuse[desc->num_fuse_corners - 1]; + fdata = desc->cpr_fuses.fuse_corner_data; + + for (i = 0; fuse <= end; fuse++, fuses++, i++, fdata++) { + /* + * Update SoC voltages: platforms might choose a different + * regulators than the one used to characterize the algorithms + * (ie, init_voltage_step). + */ + fdata->min_uV = roundup(fdata->min_uV, step_volt); + fdata->max_uV = roundup(fdata->max_uV, step_volt); + + /* Populate uV */ + uV = cpr_read_fuse_uV(desc, fdata, fuses->init_voltage, + step_volt, drv); + if (uV < 0) + return uV; + + fuse->min_uV = fdata->min_uV; + fuse->max_uV = fdata->max_uV; + fuse->uV = clamp(uV, fuse->min_uV, fuse->max_uV); + + if (fuse == end) { + /* + * Allow the highest fuse corner's PVS voltage to + * define the ceiling voltage for that corner in order + * to support SoC's in which variable ceiling values + * are required. + */ + end->max_uV = max(end->max_uV, end->uV); + } + + /* Populate target quotient by scaling */ + ret = nvmem_cell_read_variable_le_u32(drv->dev, fuses->quotient, &fuse->quot); + if (ret) + return ret; + + fuse->quot *= fdata->quot_scale; + fuse->quot += fdata->quot_offset; + fuse->quot += fdata->quot_adjust; + fuse->step_quot = desc->step_quot[fuse->ring_osc_idx]; + + /* Populate acc settings */ + fuse->accs = accs; + fuse->num_accs = acc_desc->num_regs_per_fuse; + accs += acc_desc->num_regs_per_fuse; + } + + /* + * Restrict all fuse corner PVS voltages based upon per corner + * ceiling and floor voltages. + */ + for (fuse = drv->fuse_corners, i = 0; fuse <= end; fuse++, i++) { + if (fuse->uV > fuse->max_uV) + fuse->uV = fuse->max_uV; + else if (fuse->uV < fuse->min_uV) + fuse->uV = fuse->min_uV; + + ret = regulator_is_supported_voltage(drv->vdd_apc, + fuse->min_uV, + fuse->min_uV); + if (!ret) { + dev_err(drv->dev, + "min uV: %d (fuse corner: %d) not supported by regulator\n", + fuse->min_uV, i); + return -EINVAL; + } + + ret = regulator_is_supported_voltage(drv->vdd_apc, + fuse->max_uV, + fuse->max_uV); + if (!ret) { + dev_err(drv->dev, + "max uV: %d (fuse corner: %d) not supported by regulator\n", + fuse->max_uV, i); + return -EINVAL; + } + + dev_dbg(drv->dev, + "fuse corner %d: [%d %d %d] RO%hhu quot %d squot %d\n", + i, fuse->min_uV, fuse->uV, fuse->max_uV, + fuse->ring_osc_idx, fuse->quot, fuse->step_quot); + } + + return 0; +} + +static int cpr_calculate_scaling(const char *quot_offset, + struct cpr_drv *drv, + const struct fuse_corner_data *fdata, + const struct corner *corner) +{ + u32 quot_diff = 0; + unsigned long freq_diff; + int scaling; + const struct fuse_corner *fuse, *prev_fuse; + int ret; + + fuse = corner->fuse_corner; + prev_fuse = fuse - 1; + + if (quot_offset) { + ret = nvmem_cell_read_variable_le_u32(drv->dev, quot_offset, "_diff); + if (ret) + return ret; + + quot_diff *= fdata->quot_offset_scale; + quot_diff += fdata->quot_offset_adjust; + } else { + quot_diff = fuse->quot - prev_fuse->quot; + } + + freq_diff = fuse->max_freq - prev_fuse->max_freq; + freq_diff /= 1000000; /* Convert to MHz */ + scaling = 1000 * quot_diff / freq_diff; + return min(scaling, fdata->max_quot_scale); +} + +static int cpr_interpolate(const struct corner *corner, int step_volt, + const struct fuse_corner_data *fdata) +{ + unsigned long f_high, f_low, f_diff; + int uV_high, uV_low, uV; + u64 temp, temp_limit; + const struct fuse_corner *fuse, *prev_fuse; + + fuse = corner->fuse_corner; + prev_fuse = fuse - 1; + + f_high = fuse->max_freq; + f_low = prev_fuse->max_freq; + uV_high = fuse->uV; + uV_low = prev_fuse->uV; + f_diff = fuse->max_freq - corner->freq; + + /* + * Don't interpolate in the wrong direction. This could happen + * if the adjusted fuse voltage overlaps with the previous fuse's + * adjusted voltage. + */ + if (f_high <= f_low || uV_high <= uV_low || f_high <= corner->freq) + return corner->uV; + + temp = f_diff * (uV_high - uV_low); + temp = div64_ul(temp, f_high - f_low); + + /* + * max_volt_scale has units of uV/MHz while freq values + * have units of Hz. Divide by 1000000 to convert to. + */ + temp_limit = f_diff * fdata->max_volt_scale; + do_div(temp_limit, 1000000); + + uV = uV_high - min(temp, temp_limit); + return roundup(uV, step_volt); +} + +static unsigned int cpr_get_fuse_corner(struct dev_pm_opp *opp) +{ + struct device_node *np; + unsigned int fuse_corner = 0; + + np = dev_pm_opp_get_of_node(opp); + if (of_property_read_u32(np, "qcom,opp-fuse-level", &fuse_corner)) + pr_err("%s: missing 'qcom,opp-fuse-level' property\n", + __func__); + + of_node_put(np); + + return fuse_corner; +} + +static unsigned long cpr_get_opp_hz_for_req(struct dev_pm_opp *ref, + struct device *cpu_dev) +{ + u64 rate = 0; + struct device_node *ref_np; + struct device_node *desc_np; + struct device_node *child_np = NULL; + struct device_node *child_req_np = NULL; + + desc_np = dev_pm_opp_of_get_opp_desc_node(cpu_dev); + if (!desc_np) + return 0; + + ref_np = dev_pm_opp_get_of_node(ref); + if (!ref_np) + goto out_ref; + + do { + of_node_put(child_req_np); + child_np = of_get_next_available_child(desc_np, child_np); + child_req_np = of_parse_phandle(child_np, "required-opps", 0); + } while (child_np && child_req_np != ref_np); + + if (child_np && child_req_np == ref_np) + of_property_read_u64(child_np, "opp-hz", &rate); + + of_node_put(child_req_np); + of_node_put(child_np); + of_node_put(ref_np); +out_ref: + of_node_put(desc_np); + + return (unsigned long) rate; +} + +static int cpr_corner_init(struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + const struct cpr_fuse *fuses = drv->cpr_fuses; + int i, level, scaling = 0; + unsigned int fnum, fc; + const char *quot_offset; + struct fuse_corner *fuse, *prev_fuse; + struct corner *corner, *end; + struct corner_data *cdata; + const struct fuse_corner_data *fdata; + bool apply_scaling; + unsigned long freq_diff, freq_diff_mhz; + unsigned long freq; + int step_volt = regulator_get_linear_step(drv->vdd_apc); + struct dev_pm_opp *opp; + + if (!step_volt) + return -EINVAL; + + corner = drv->corners; + end = &corner[drv->num_corners - 1]; + + cdata = devm_kcalloc(drv->dev, drv->num_corners, + sizeof(struct corner_data), + GFP_KERNEL); + if (!cdata) + return -ENOMEM; + + /* + * Store maximum frequency for each fuse corner based on the frequency + * plan + */ + for (level = 1; level <= drv->num_corners; level++) { + opp = dev_pm_opp_find_level_exact(&drv->pd.dev, level); + if (IS_ERR(opp)) + return -EINVAL; + fc = cpr_get_fuse_corner(opp); + if (!fc) { + dev_pm_opp_put(opp); + return -EINVAL; + } + fnum = fc - 1; + freq = cpr_get_opp_hz_for_req(opp, drv->attached_cpu_dev); + if (!freq) { + dev_pm_opp_put(opp); + return -EINVAL; + } + cdata[level - 1].fuse_corner = fnum; + cdata[level - 1].freq = freq; + + fuse = &drv->fuse_corners[fnum]; + dev_dbg(drv->dev, "freq: %lu level: %u fuse level: %u\n", + freq, dev_pm_opp_get_level(opp) - 1, fnum); + if (freq > fuse->max_freq) + fuse->max_freq = freq; + dev_pm_opp_put(opp); + } + + /* + * Get the quotient adjustment scaling factor, according to: + * + * scaling = min(1000 * (QUOT(corner_N) - QUOT(corner_N-1)) + * / (freq(corner_N) - freq(corner_N-1)), max_factor) + * + * QUOT(corner_N): quotient read from fuse for fuse corner N + * QUOT(corner_N-1): quotient read from fuse for fuse corner (N - 1) + * freq(corner_N): max frequency in MHz supported by fuse corner N + * freq(corner_N-1): max frequency in MHz supported by fuse corner + * (N - 1) + * + * Then walk through the corners mapped to each fuse corner + * and calculate the quotient adjustment for each one using the + * following formula: + * + * quot_adjust = (freq_max - freq_corner) * scaling / 1000 + * + * freq_max: max frequency in MHz supported by the fuse corner + * freq_corner: frequency in MHz corresponding to the corner + * scaling: calculated from above equation + * + * + * + + + * | v | + * q | f c o | f c + * u | c l | c + * o | f t | f + * t | c a | c + * | c f g | c f + * | e | + * +--------------- +---------------- + * 0 1 2 3 4 5 6 0 1 2 3 4 5 6 + * corner corner + * + * c = corner + * f = fuse corner + * + */ + for (apply_scaling = false, i = 0; corner <= end; corner++, i++) { + fnum = cdata[i].fuse_corner; + fdata = &desc->cpr_fuses.fuse_corner_data[fnum]; + quot_offset = fuses[fnum].quotient_offset; + fuse = &drv->fuse_corners[fnum]; + if (fnum) + prev_fuse = &drv->fuse_corners[fnum - 1]; + else + prev_fuse = NULL; + + corner->fuse_corner = fuse; + corner->freq = cdata[i].freq; + corner->uV = fuse->uV; + + if (prev_fuse && cdata[i - 1].freq == prev_fuse->max_freq) { + scaling = cpr_calculate_scaling(quot_offset, drv, + fdata, corner); + if (scaling < 0) + return scaling; + + apply_scaling = true; + } else if (corner->freq == fuse->max_freq) { + /* This is a fuse corner; don't scale anything */ + apply_scaling = false; + } + + if (apply_scaling) { + freq_diff = fuse->max_freq - corner->freq; + freq_diff_mhz = freq_diff / 1000000; + corner->quot_adjust = scaling * freq_diff_mhz / 1000; + + corner->uV = cpr_interpolate(corner, step_volt, fdata); + } + + corner->max_uV = fuse->max_uV; + corner->min_uV = fuse->min_uV; + corner->uV = clamp(corner->uV, corner->min_uV, corner->max_uV); + corner->last_uV = corner->uV; + + /* Reduce the ceiling voltage if needed */ + if (desc->reduce_to_corner_uV && corner->uV < corner->max_uV) + corner->max_uV = corner->uV; + else if (desc->reduce_to_fuse_uV && fuse->uV < corner->max_uV) + corner->max_uV = max(corner->min_uV, fuse->uV); + + dev_dbg(drv->dev, "corner %d: [%d %d %d] quot %d\n", i, + corner->min_uV, corner->uV, corner->max_uV, + fuse->quot - corner->quot_adjust); + } + + return 0; +} + +static const struct cpr_fuse *cpr_get_fuses(struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + struct cpr_fuse *fuses; + int i; + + fuses = devm_kcalloc(drv->dev, desc->num_fuse_corners, + sizeof(struct cpr_fuse), + GFP_KERNEL); + if (!fuses) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < desc->num_fuse_corners; i++) { + char tbuf[32]; + + snprintf(tbuf, 32, "cpr_ring_osc%d", i + 1); + fuses[i].ring_osc = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); + if (!fuses[i].ring_osc) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, 32, "cpr_init_voltage%d", i + 1); + fuses[i].init_voltage = devm_kstrdup(drv->dev, tbuf, + GFP_KERNEL); + if (!fuses[i].init_voltage) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, 32, "cpr_quotient%d", i + 1); + fuses[i].quotient = devm_kstrdup(drv->dev, tbuf, GFP_KERNEL); + if (!fuses[i].quotient) + return ERR_PTR(-ENOMEM); + + snprintf(tbuf, 32, "cpr_quotient_offset%d", i + 1); + fuses[i].quotient_offset = devm_kstrdup(drv->dev, tbuf, + GFP_KERNEL); + if (!fuses[i].quotient_offset) + return ERR_PTR(-ENOMEM); + } + + return fuses; +} + +static void cpr_set_loop_allowed(struct cpr_drv *drv) +{ + drv->loop_disabled = false; +} + +static int cpr_init_parameters(struct cpr_drv *drv) +{ + const struct cpr_desc *desc = drv->desc; + struct clk *clk; + + clk = clk_get(drv->dev, "ref"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + drv->ref_clk_khz = clk_get_rate(clk) / 1000; + clk_put(clk); + + if (desc->timer_cons_up > RBIF_TIMER_ADJ_CONS_UP_MASK || + desc->timer_cons_down > RBIF_TIMER_ADJ_CONS_DOWN_MASK || + desc->up_threshold > RBCPR_CTL_UP_THRESHOLD_MASK || + desc->down_threshold > RBCPR_CTL_DN_THRESHOLD_MASK || + desc->idle_clocks > RBCPR_STEP_QUOT_IDLE_CLK_MASK || + desc->clamp_timer_interval > RBIF_TIMER_ADJ_CLAMP_INT_MASK) + return -EINVAL; + + dev_dbg(drv->dev, "up threshold = %u, down threshold = %u\n", + desc->up_threshold, desc->down_threshold); + + return 0; +} + +static int cpr_find_initial_corner(struct cpr_drv *drv) +{ + unsigned long rate; + const struct corner *end; + struct corner *iter; + unsigned int i = 0; + + if (!drv->cpu_clk) { + dev_err(drv->dev, "cannot get rate from NULL clk\n"); + return -EINVAL; + } + + end = &drv->corners[drv->num_corners - 1]; + rate = clk_get_rate(drv->cpu_clk); + + /* + * Some bootloaders set a CPU clock frequency that is not defined + * in the OPP table. When running at an unlisted frequency, + * cpufreq_online() will change to the OPP which has the lowest + * frequency, at or above the unlisted frequency. + * Since cpufreq_online() always "rounds up" in the case of an + * unlisted frequency, this function always "rounds down" in case + * of an unlisted frequency. That way, when cpufreq_online() + * triggers the first ever call to cpr_set_performance_state(), + * it will correctly determine the direction as UP. + */ + for (iter = drv->corners; iter <= end; iter++) { + if (iter->freq > rate) + break; + i++; + if (iter->freq == rate) { + drv->corner = iter; + break; + } + if (iter->freq < rate) + drv->corner = iter; + } + + if (!drv->corner) { + dev_err(drv->dev, "boot up corner not found\n"); + return -EINVAL; + } + + dev_dbg(drv->dev, "boot up perf state: %u\n", i); + + return 0; +} + +static const struct cpr_desc qcs404_cpr_desc = { + .num_fuse_corners = 3, + .min_diff_quot = CPR_FUSE_MIN_QUOT_DIFF, + .step_quot = (int []){ 25, 25, 25, }, + .timer_delay_us = 5000, + .timer_cons_up = 0, + .timer_cons_down = 2, + .up_threshold = 1, + .down_threshold = 3, + .idle_clocks = 15, + .gcnt_us = 1, + .vdd_apc_step_up_limit = 1, + .vdd_apc_step_down_limit = 1, + .cpr_fuses = { + .init_voltage_step = 8000, + .init_voltage_width = 6, + .fuse_corner_data = (struct fuse_corner_data[]){ + /* fuse corner 0 */ + { + .ref_uV = 1224000, + .max_uV = 1224000, + .min_uV = 1048000, + .max_volt_scale = 0, + .max_quot_scale = 0, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 1 */ + { + .ref_uV = 1288000, + .max_uV = 1288000, + .min_uV = 1048000, + .max_volt_scale = 2000, + .max_quot_scale = 1400, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = -20, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + /* fuse corner 2 */ + { + .ref_uV = 1352000, + .max_uV = 1384000, + .min_uV = 1088000, + .max_volt_scale = 2000, + .max_quot_scale = 1400, + .quot_offset = 0, + .quot_scale = 1, + .quot_adjust = 0, + .quot_offset_scale = 5, + .quot_offset_adjust = 0, + }, + }, + }, +}; + +static const struct acc_desc qcs404_acc_desc = { + .settings = (struct reg_sequence[]){ + { 0xb120, 0x1041040 }, + { 0xb124, 0x41 }, + { 0xb120, 0x0 }, + { 0xb124, 0x0 }, + { 0xb120, 0x0 }, + { 0xb124, 0x0 }, + }, + .config = (struct reg_sequence[]){ + { 0xb138, 0xff }, + { 0xb130, 0x5555 }, + }, + .num_regs_per_fuse = 2, +}; + +static const struct cpr_acc_desc qcs404_cpr_acc_desc = { + .cpr_desc = &qcs404_cpr_desc, + .acc_desc = &qcs404_acc_desc, +}; + +static unsigned int cpr_get_performance_state(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_level(opp); +} + +static int cpr_power_off(struct generic_pm_domain *domain) +{ + struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); + + return cpr_disable(drv); +} + +static int cpr_power_on(struct generic_pm_domain *domain) +{ + struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); + + return cpr_enable(drv); +} + +static int cpr_pd_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct cpr_drv *drv = container_of(domain, struct cpr_drv, pd); + const struct acc_desc *acc_desc = drv->acc_desc; + int ret = 0; + + mutex_lock(&drv->lock); + + dev_dbg(drv->dev, "attach callback for: %s\n", dev_name(dev)); + + /* + * This driver only supports scaling voltage for a CPU cluster + * where all CPUs in the cluster share a single regulator. + * Therefore, save the struct device pointer only for the first + * CPU device that gets attached. There is no need to do any + * additional initialization when further CPUs get attached. + */ + if (drv->attached_cpu_dev) + goto unlock; + + /* + * cpr_scale_voltage() requires the direction (if we are changing + * to a higher or lower OPP). The first time + * cpr_set_performance_state() is called, there is no previous + * performance state defined. Therefore, we call + * cpr_find_initial_corner() that gets the CPU clock frequency + * set by the bootloader, so that we can determine the direction + * the first time cpr_set_performance_state() is called. + */ + drv->cpu_clk = devm_clk_get(dev, NULL); + if (IS_ERR(drv->cpu_clk)) { + ret = PTR_ERR(drv->cpu_clk); + if (ret != -EPROBE_DEFER) + dev_err(drv->dev, "could not get cpu clk: %d\n", ret); + goto unlock; + } + drv->attached_cpu_dev = dev; + + dev_dbg(drv->dev, "using cpu clk from: %s\n", + dev_name(drv->attached_cpu_dev)); + + /* + * Everything related to (virtual) corners has to be initialized + * here, when attaching to the power domain, since we need to know + * the maximum frequency for each fuse corner, and this is only + * available after the cpufreq driver has attached to us. + * The reason for this is that we need to know the highest + * frequency associated with each fuse corner. + */ + ret = dev_pm_opp_get_opp_count(&drv->pd.dev); + if (ret < 0) { + dev_err(drv->dev, "could not get OPP count\n"); + goto unlock; + } + drv->num_corners = ret; + + if (drv->num_corners < 2) { + dev_err(drv->dev, "need at least 2 OPPs to use CPR\n"); + ret = -EINVAL; + goto unlock; + } + + drv->corners = devm_kcalloc(drv->dev, drv->num_corners, + sizeof(*drv->corners), + GFP_KERNEL); + if (!drv->corners) { + ret = -ENOMEM; + goto unlock; + } + + ret = cpr_corner_init(drv); + if (ret) + goto unlock; + + cpr_set_loop_allowed(drv); + + ret = cpr_init_parameters(drv); + if (ret) + goto unlock; + + /* Configure CPR HW but keep it disabled */ + ret = cpr_config(drv); + if (ret) + goto unlock; + + ret = cpr_find_initial_corner(drv); + if (ret) + goto unlock; + + if (acc_desc->config) + regmap_multi_reg_write(drv->tcsr, acc_desc->config, + acc_desc->num_regs_per_fuse); + + /* Enable ACC if required */ + if (acc_desc->enable_mask) + regmap_update_bits(drv->tcsr, acc_desc->enable_reg, + acc_desc->enable_mask, + acc_desc->enable_mask); + + dev_info(drv->dev, "driver initialized with %u OPPs\n", + drv->num_corners); + +unlock: + mutex_unlock(&drv->lock); + + return ret; +} + +static int cpr_debug_info_show(struct seq_file *s, void *unused) +{ + u32 gcnt, ro_sel, ctl, irq_status, reg, error_steps; + u32 step_dn, step_up, error, error_lt0, busy; + struct cpr_drv *drv = s->private; + struct fuse_corner *fuse_corner; + struct corner *corner; + + corner = drv->corner; + fuse_corner = corner->fuse_corner; + + seq_printf(s, "corner, current_volt = %d uV\n", + corner->last_uV); + + ro_sel = fuse_corner->ring_osc_idx; + gcnt = cpr_read(drv, REG_RBCPR_GCNT_TARGET(ro_sel)); + seq_printf(s, "rbcpr_gcnt_target (%u) = %#02X\n", ro_sel, gcnt); + + ctl = cpr_read(drv, REG_RBCPR_CTL); + seq_printf(s, "rbcpr_ctl = %#02X\n", ctl); + + irq_status = cpr_read(drv, REG_RBIF_IRQ_STATUS); + seq_printf(s, "rbcpr_irq_status = %#02X\n", irq_status); + + reg = cpr_read(drv, REG_RBCPR_RESULT_0); + seq_printf(s, "rbcpr_result_0 = %#02X\n", reg); + + step_dn = reg & 0x01; + step_up = (reg >> RBCPR_RESULT0_STEP_UP_SHIFT) & 0x01; + seq_printf(s, " [step_dn = %u", step_dn); + + seq_printf(s, ", step_up = %u", step_up); + + error_steps = (reg >> RBCPR_RESULT0_ERROR_STEPS_SHIFT) + & RBCPR_RESULT0_ERROR_STEPS_MASK; + seq_printf(s, ", error_steps = %u", error_steps); + + error = (reg >> RBCPR_RESULT0_ERROR_SHIFT) & RBCPR_RESULT0_ERROR_MASK; + seq_printf(s, ", error = %u", error); + + error_lt0 = (reg >> RBCPR_RESULT0_ERROR_LT0_SHIFT) & 0x01; + seq_printf(s, ", error_lt_0 = %u", error_lt0); + + busy = (reg >> RBCPR_RESULT0_BUSY_SHIFT) & 0x01; + seq_printf(s, ", busy = %u]\n", busy); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(cpr_debug_info); + +static void cpr_debugfs_init(struct cpr_drv *drv) +{ + drv->debugfs = debugfs_create_dir("qcom_cpr", NULL); + + debugfs_create_file("debug_info", 0444, drv->debugfs, + drv, &cpr_debug_info_fops); +} + +static int cpr_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cpr_drv *drv; + int irq, ret; + const struct cpr_acc_desc *data; + struct device_node *np; + u32 cpr_rev = FUSE_REVISION_UNKNOWN; + + data = of_device_get_match_data(dev); + if (!data || !data->cpr_desc || !data->acc_desc) + return -EINVAL; + + drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); + if (!drv) + return -ENOMEM; + drv->dev = dev; + drv->desc = data->cpr_desc; + drv->acc_desc = data->acc_desc; + + drv->fuse_corners = devm_kcalloc(dev, drv->desc->num_fuse_corners, + sizeof(*drv->fuse_corners), + GFP_KERNEL); + if (!drv->fuse_corners) + return -ENOMEM; + + np = of_parse_phandle(dev->of_node, "acc-syscon", 0); + if (!np) + return -ENODEV; + + drv->tcsr = syscon_node_to_regmap(np); + of_node_put(np); + if (IS_ERR(drv->tcsr)) + return PTR_ERR(drv->tcsr); + + drv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(drv->base)) + return PTR_ERR(drv->base); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return -EINVAL; + + drv->vdd_apc = devm_regulator_get(dev, "vdd-apc"); + if (IS_ERR(drv->vdd_apc)) + return PTR_ERR(drv->vdd_apc); + + /* + * Initialize fuse corners, since it simply depends + * on data in efuses. + * Everything related to (virtual) corners has to be + * initialized after attaching to the power domain, + * since it depends on the CPU's OPP table. + */ + ret = nvmem_cell_read_variable_le_u32(dev, "cpr_fuse_revision", &cpr_rev); + if (ret) + return ret; + + drv->cpr_fuses = cpr_get_fuses(drv); + if (IS_ERR(drv->cpr_fuses)) + return PTR_ERR(drv->cpr_fuses); + + ret = cpr_populate_ring_osc_idx(drv); + if (ret) + return ret; + + ret = cpr_fuse_corner_init(drv); + if (ret) + return ret; + + mutex_init(&drv->lock); + + ret = devm_request_threaded_irq(dev, irq, NULL, + cpr_irq_handler, + IRQF_ONESHOT | IRQF_TRIGGER_RISING, + "cpr", drv); + if (ret) + return ret; + + drv->pd.name = devm_kstrdup_const(dev, dev->of_node->full_name, + GFP_KERNEL); + if (!drv->pd.name) + return -EINVAL; + + drv->pd.power_off = cpr_power_off; + drv->pd.power_on = cpr_power_on; + drv->pd.set_performance_state = cpr_set_performance_state; + drv->pd.opp_to_performance_state = cpr_get_performance_state; + drv->pd.attach_dev = cpr_pd_attach_dev; + + ret = pm_genpd_init(&drv->pd, NULL, true); + if (ret) + return ret; + + ret = of_genpd_add_provider_simple(dev->of_node, &drv->pd); + if (ret) + goto err_remove_genpd; + + platform_set_drvdata(pdev, drv); + cpr_debugfs_init(drv); + + return 0; + +err_remove_genpd: + pm_genpd_remove(&drv->pd); + return ret; +} + +static int cpr_remove(struct platform_device *pdev) +{ + struct cpr_drv *drv = platform_get_drvdata(pdev); + + if (cpr_is_allowed(drv)) { + cpr_ctl_disable(drv); + cpr_irq_set(drv, 0); + } + + of_genpd_del_provider(pdev->dev.of_node); + pm_genpd_remove(&drv->pd); + + debugfs_remove_recursive(drv->debugfs); + + return 0; +} + +static const struct of_device_id cpr_match_table[] = { + { .compatible = "qcom,qcs404-cpr", .data = &qcs404_cpr_acc_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, cpr_match_table); + +static struct platform_driver cpr_driver = { + .probe = cpr_probe, + .remove = cpr_remove, + .driver = { + .name = "qcom-cpr", + .of_match_table = cpr_match_table, + }, +}; +module_platform_driver(cpr_driver); + +MODULE_DESCRIPTION("Core Power Reduction (CPR) driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/qcom/rpmhpd.c b/drivers/pmdomain/qcom/rpmhpd.c new file mode 100644 index 000000000000..a87e336d5e33 --- /dev/null +++ b/drivers/pmdomain/qcom/rpmhpd.c @@ -0,0 +1,886 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2018, The Linux Foundation. All rights reserved.*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define domain_to_rpmhpd(domain) container_of(domain, struct rpmhpd, pd) + +#define RPMH_ARC_MAX_LEVELS 16 + +/** + * struct rpmhpd - top level RPMh power domain resource data structure + * @dev: rpmh power domain controller device + * @pd: generic_pm_domain corresponding to the power domain + * @parent: generic_pm_domain corresponding to the parent's power domain + * @peer: A peer power domain in case Active only Voting is + * supported + * @active_only: True if it represents an Active only peer + * @corner: current corner + * @active_corner: current active corner + * @enable_corner: lowest non-zero corner + * @level: An array of level (vlvl) to corner (hlvl) mappings + * derived from cmd-db + * @level_count: Number of levels supported by the power domain. max + * being 16 (0 - 15) + * @enabled: true if the power domain is enabled + * @res_name: Resource name used for cmd-db lookup + * @addr: Resource address as looped up using resource name from + * cmd-db + * @state_synced: Indicator that sync_state has been invoked for the rpmhpd resource + */ +struct rpmhpd { + struct device *dev; + struct generic_pm_domain pd; + struct generic_pm_domain *parent; + struct rpmhpd *peer; + const bool active_only; + unsigned int corner; + unsigned int active_corner; + unsigned int enable_corner; + u32 level[RPMH_ARC_MAX_LEVELS]; + size_t level_count; + bool enabled; + const char *res_name; + u32 addr; + bool state_synced; +}; + +struct rpmhpd_desc { + struct rpmhpd **rpmhpds; + size_t num_pds; +}; + +static DEFINE_MUTEX(rpmhpd_lock); + +/* RPMH powerdomains */ + +static struct rpmhpd cx_ao; +static struct rpmhpd mx; +static struct rpmhpd mx_ao; +static struct rpmhpd cx = { + .pd = { .name = "cx", }, + .peer = &cx_ao, + .res_name = "cx.lvl", +}; + +static struct rpmhpd cx_ao = { + .pd = { .name = "cx_ao", }, + .active_only = true, + .peer = &cx, + .res_name = "cx.lvl", +}; + +static struct rpmhpd cx_ao_w_mx_parent; +static struct rpmhpd cx_w_mx_parent = { + .pd = { .name = "cx", }, + .peer = &cx_ao_w_mx_parent, + .parent = &mx.pd, + .res_name = "cx.lvl", +}; + +static struct rpmhpd cx_ao_w_mx_parent = { + .pd = { .name = "cx_ao", }, + .active_only = true, + .peer = &cx_w_mx_parent, + .parent = &mx_ao.pd, + .res_name = "cx.lvl", +}; + +static struct rpmhpd ebi = { + .pd = { .name = "ebi", }, + .res_name = "ebi.lvl", +}; + +static struct rpmhpd gfx = { + .pd = { .name = "gfx", }, + .res_name = "gfx.lvl", +}; + +static struct rpmhpd lcx = { + .pd = { .name = "lcx", }, + .res_name = "lcx.lvl", +}; + +static struct rpmhpd lmx = { + .pd = { .name = "lmx", }, + .res_name = "lmx.lvl", +}; + +static struct rpmhpd mmcx_ao; +static struct rpmhpd mmcx = { + .pd = { .name = "mmcx", }, + .peer = &mmcx_ao, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mmcx_ao = { + .pd = { .name = "mmcx_ao", }, + .active_only = true, + .peer = &mmcx, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mmcx_ao_w_cx_parent; +static struct rpmhpd mmcx_w_cx_parent = { + .pd = { .name = "mmcx", }, + .peer = &mmcx_ao_w_cx_parent, + .parent = &cx.pd, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mmcx_ao_w_cx_parent = { + .pd = { .name = "mmcx_ao", }, + .active_only = true, + .peer = &mmcx_w_cx_parent, + .parent = &cx_ao.pd, + .res_name = "mmcx.lvl", +}; + +static struct rpmhpd mss = { + .pd = { .name = "mss", }, + .res_name = "mss.lvl", +}; + +static struct rpmhpd mx_ao; +static struct rpmhpd mx = { + .pd = { .name = "mx", }, + .peer = &mx_ao, + .res_name = "mx.lvl", +}; + +static struct rpmhpd mx_ao = { + .pd = { .name = "mx_ao", }, + .active_only = true, + .peer = &mx, + .res_name = "mx.lvl", +}; + +static struct rpmhpd mxc_ao; +static struct rpmhpd mxc = { + .pd = { .name = "mxc", }, + .peer = &mxc_ao, + .res_name = "mxc.lvl", +}; + +static struct rpmhpd mxc_ao = { + .pd = { .name = "mxc_ao", }, + .active_only = true, + .peer = &mxc, + .res_name = "mxc.lvl", +}; + +static struct rpmhpd nsp = { + .pd = { .name = "nsp", }, + .res_name = "nsp.lvl", +}; + +static struct rpmhpd nsp0 = { + .pd = { .name = "nsp0", }, + .res_name = "nsp0.lvl", +}; + +static struct rpmhpd nsp1 = { + .pd = { .name = "nsp1", }, + .res_name = "nsp1.lvl", +}; + +static struct rpmhpd qphy = { + .pd = { .name = "qphy", }, + .res_name = "qphy.lvl", +}; + +/* SA8540P RPMH powerdomains */ +static struct rpmhpd *sa8540p_rpmhpds[] = { + [SC8280XP_CX] = &cx, + [SC8280XP_CX_AO] = &cx_ao, + [SC8280XP_EBI] = &ebi, + [SC8280XP_GFX] = &gfx, + [SC8280XP_LCX] = &lcx, + [SC8280XP_LMX] = &lmx, + [SC8280XP_MMCX] = &mmcx, + [SC8280XP_MMCX_AO] = &mmcx_ao, + [SC8280XP_MX] = &mx, + [SC8280XP_MX_AO] = &mx_ao, + [SC8280XP_NSP] = &nsp, +}; + +static const struct rpmhpd_desc sa8540p_desc = { + .rpmhpds = sa8540p_rpmhpds, + .num_pds = ARRAY_SIZE(sa8540p_rpmhpds), +}; + +/* SA8775P RPMH power domains */ +static struct rpmhpd *sa8775p_rpmhpds[] = { + [SA8775P_CX] = &cx, + [SA8775P_CX_AO] = &cx_ao, + [SA8775P_EBI] = &ebi, + [SA8775P_GFX] = &gfx, + [SA8775P_LCX] = &lcx, + [SA8775P_LMX] = &lmx, + [SA8775P_MMCX] = &mmcx, + [SA8775P_MMCX_AO] = &mmcx_ao, + [SA8775P_MXC] = &mxc, + [SA8775P_MXC_AO] = &mxc_ao, + [SA8775P_MX] = &mx, + [SA8775P_MX_AO] = &mx_ao, + [SA8775P_NSP0] = &nsp0, + [SA8775P_NSP1] = &nsp1, +}; + +static const struct rpmhpd_desc sa8775p_desc = { + .rpmhpds = sa8775p_rpmhpds, + .num_pds = ARRAY_SIZE(sa8775p_rpmhpds), +}; + +/* SDM670 RPMH powerdomains */ +static struct rpmhpd *sdm670_rpmhpds[] = { + [SDM670_CX] = &cx_w_mx_parent, + [SDM670_CX_AO] = &cx_ao_w_mx_parent, + [SDM670_GFX] = &gfx, + [SDM670_LCX] = &lcx, + [SDM670_LMX] = &lmx, + [SDM670_MSS] = &mss, + [SDM670_MX] = &mx, + [SDM670_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sdm670_desc = { + .rpmhpds = sdm670_rpmhpds, + .num_pds = ARRAY_SIZE(sdm670_rpmhpds), +}; + +/* SDM845 RPMH powerdomains */ +static struct rpmhpd *sdm845_rpmhpds[] = { + [SDM845_CX] = &cx_w_mx_parent, + [SDM845_CX_AO] = &cx_ao_w_mx_parent, + [SDM845_EBI] = &ebi, + [SDM845_GFX] = &gfx, + [SDM845_LCX] = &lcx, + [SDM845_LMX] = &lmx, + [SDM845_MSS] = &mss, + [SDM845_MX] = &mx, + [SDM845_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sdm845_desc = { + .rpmhpds = sdm845_rpmhpds, + .num_pds = ARRAY_SIZE(sdm845_rpmhpds), +}; + +/* SDX55 RPMH powerdomains */ +static struct rpmhpd *sdx55_rpmhpds[] = { + [SDX55_CX] = &cx_w_mx_parent, + [SDX55_MSS] = &mss, + [SDX55_MX] = &mx, +}; + +static const struct rpmhpd_desc sdx55_desc = { + .rpmhpds = sdx55_rpmhpds, + .num_pds = ARRAY_SIZE(sdx55_rpmhpds), +}; + +/* SDX65 RPMH powerdomains */ +static struct rpmhpd *sdx65_rpmhpds[] = { + [SDX65_CX] = &cx_w_mx_parent, + [SDX65_CX_AO] = &cx_ao_w_mx_parent, + [SDX65_MSS] = &mss, + [SDX65_MX] = &mx, + [SDX65_MX_AO] = &mx_ao, + [SDX65_MXC] = &mxc, +}; + +static const struct rpmhpd_desc sdx65_desc = { + .rpmhpds = sdx65_rpmhpds, + .num_pds = ARRAY_SIZE(sdx65_rpmhpds), +}; + +/* SDX75 RPMH powerdomains */ +static struct rpmhpd *sdx75_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, +}; + +static const struct rpmhpd_desc sdx75_desc = { + .rpmhpds = sdx75_rpmhpds, + .num_pds = ARRAY_SIZE(sdx75_rpmhpds), +}; + +/* SM6350 RPMH powerdomains */ +static struct rpmhpd *sm6350_rpmhpds[] = { + [SM6350_CX] = &cx_w_mx_parent, + [SM6350_GFX] = &gfx, + [SM6350_LCX] = &lcx, + [SM6350_LMX] = &lmx, + [SM6350_MSS] = &mss, + [SM6350_MX] = &mx, +}; + +static const struct rpmhpd_desc sm6350_desc = { + .rpmhpds = sm6350_rpmhpds, + .num_pds = ARRAY_SIZE(sm6350_rpmhpds), +}; + +/* SM8150 RPMH powerdomains */ +static struct rpmhpd *sm8150_rpmhpds[] = { + [SM8150_CX] = &cx_w_mx_parent, + [SM8150_CX_AO] = &cx_ao_w_mx_parent, + [SM8150_EBI] = &ebi, + [SM8150_GFX] = &gfx, + [SM8150_LCX] = &lcx, + [SM8150_LMX] = &lmx, + [SM8150_MMCX] = &mmcx, + [SM8150_MMCX_AO] = &mmcx_ao, + [SM8150_MSS] = &mss, + [SM8150_MX] = &mx, + [SM8150_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sm8150_desc = { + .rpmhpds = sm8150_rpmhpds, + .num_pds = ARRAY_SIZE(sm8150_rpmhpds), +}; + +static struct rpmhpd *sa8155p_rpmhpds[] = { + [SA8155P_CX] = &cx_w_mx_parent, + [SA8155P_CX_AO] = &cx_ao_w_mx_parent, + [SA8155P_EBI] = &ebi, + [SA8155P_GFX] = &gfx, + [SA8155P_MSS] = &mss, + [SA8155P_MX] = &mx, + [SA8155P_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sa8155p_desc = { + .rpmhpds = sa8155p_rpmhpds, + .num_pds = ARRAY_SIZE(sa8155p_rpmhpds), +}; + +/* SM8250 RPMH powerdomains */ +static struct rpmhpd *sm8250_rpmhpds[] = { + [RPMHPD_CX] = &cx_w_mx_parent, + [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx, + [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sm8250_desc = { + .rpmhpds = sm8250_rpmhpds, + .num_pds = ARRAY_SIZE(sm8250_rpmhpds), +}; + +/* SM8350 Power domains */ +static struct rpmhpd *sm8350_rpmhpds[] = { + [RPMHPD_CX] = &cx_w_mx_parent, + [RPMHPD_CX_AO] = &cx_ao_w_mx_parent, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx, + [RPMHPD_MMCX_AO] = &mmcx_ao, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, +}; + +static const struct rpmhpd_desc sm8350_desc = { + .rpmhpds = sm8350_rpmhpds, + .num_pds = ARRAY_SIZE(sm8350_rpmhpds), +}; + +/* SM8450 RPMH powerdomains */ +static struct rpmhpd *sm8450_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, +}; + +static const struct rpmhpd_desc sm8450_desc = { + .rpmhpds = sm8450_rpmhpds, + .num_pds = ARRAY_SIZE(sm8450_rpmhpds), +}; + +/* SM8550 RPMH powerdomains */ +static struct rpmhpd *sm8550_rpmhpds[] = { + [RPMHPD_CX] = &cx, + [RPMHPD_CX_AO] = &cx_ao, + [RPMHPD_EBI] = &ebi, + [RPMHPD_GFX] = &gfx, + [RPMHPD_LCX] = &lcx, + [RPMHPD_LMX] = &lmx, + [RPMHPD_MMCX] = &mmcx_w_cx_parent, + [RPMHPD_MMCX_AO] = &mmcx_ao_w_cx_parent, + [RPMHPD_MSS] = &mss, + [RPMHPD_MX] = &mx, + [RPMHPD_MX_AO] = &mx_ao, + [RPMHPD_MXC] = &mxc, + [RPMHPD_MXC_AO] = &mxc_ao, + [RPMHPD_NSP] = &nsp, +}; + +static const struct rpmhpd_desc sm8550_desc = { + .rpmhpds = sm8550_rpmhpds, + .num_pds = ARRAY_SIZE(sm8550_rpmhpds), +}; + +/* QDU1000/QRU1000 RPMH powerdomains */ +static struct rpmhpd *qdu1000_rpmhpds[] = { + [QDU1000_CX] = &cx, + [QDU1000_EBI] = &ebi, + [QDU1000_MSS] = &mss, + [QDU1000_MX] = &mx, +}; + +static const struct rpmhpd_desc qdu1000_desc = { + .rpmhpds = qdu1000_rpmhpds, + .num_pds = ARRAY_SIZE(qdu1000_rpmhpds), +}; + +/* SC7180 RPMH powerdomains */ +static struct rpmhpd *sc7180_rpmhpds[] = { + [SC7180_CX] = &cx_w_mx_parent, + [SC7180_CX_AO] = &cx_ao_w_mx_parent, + [SC7180_GFX] = &gfx, + [SC7180_LCX] = &lcx, + [SC7180_LMX] = &lmx, + [SC7180_MSS] = &mss, + [SC7180_MX] = &mx, + [SC7180_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sc7180_desc = { + .rpmhpds = sc7180_rpmhpds, + .num_pds = ARRAY_SIZE(sc7180_rpmhpds), +}; + +/* SC7280 RPMH powerdomains */ +static struct rpmhpd *sc7280_rpmhpds[] = { + [SC7280_CX] = &cx, + [SC7280_CX_AO] = &cx_ao, + [SC7280_EBI] = &ebi, + [SC7280_GFX] = &gfx, + [SC7280_LCX] = &lcx, + [SC7280_LMX] = &lmx, + [SC7280_MSS] = &mss, + [SC7280_MX] = &mx, + [SC7280_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sc7280_desc = { + .rpmhpds = sc7280_rpmhpds, + .num_pds = ARRAY_SIZE(sc7280_rpmhpds), +}; + +/* SC8180x RPMH powerdomains */ +static struct rpmhpd *sc8180x_rpmhpds[] = { + [SC8180X_CX] = &cx_w_mx_parent, + [SC8180X_CX_AO] = &cx_ao_w_mx_parent, + [SC8180X_EBI] = &ebi, + [SC8180X_GFX] = &gfx, + [SC8180X_LCX] = &lcx, + [SC8180X_LMX] = &lmx, + [SC8180X_MMCX] = &mmcx, + [SC8180X_MMCX_AO] = &mmcx_ao, + [SC8180X_MSS] = &mss, + [SC8180X_MX] = &mx, + [SC8180X_MX_AO] = &mx_ao, +}; + +static const struct rpmhpd_desc sc8180x_desc = { + .rpmhpds = sc8180x_rpmhpds, + .num_pds = ARRAY_SIZE(sc8180x_rpmhpds), +}; + +/* SC8280xp RPMH powerdomains */ +static struct rpmhpd *sc8280xp_rpmhpds[] = { + [SC8280XP_CX] = &cx, + [SC8280XP_CX_AO] = &cx_ao, + [SC8280XP_EBI] = &ebi, + [SC8280XP_GFX] = &gfx, + [SC8280XP_LCX] = &lcx, + [SC8280XP_LMX] = &lmx, + [SC8280XP_MMCX] = &mmcx, + [SC8280XP_MMCX_AO] = &mmcx_ao, + [SC8280XP_MX] = &mx, + [SC8280XP_MX_AO] = &mx_ao, + [SC8280XP_NSP] = &nsp, + [SC8280XP_QPHY] = &qphy, +}; + +static const struct rpmhpd_desc sc8280xp_desc = { + .rpmhpds = sc8280xp_rpmhpds, + .num_pds = ARRAY_SIZE(sc8280xp_rpmhpds), +}; + +static const struct of_device_id rpmhpd_match_table[] = { + { .compatible = "qcom,qdu1000-rpmhpd", .data = &qdu1000_desc }, + { .compatible = "qcom,sa8155p-rpmhpd", .data = &sa8155p_desc }, + { .compatible = "qcom,sa8540p-rpmhpd", .data = &sa8540p_desc }, + { .compatible = "qcom,sa8775p-rpmhpd", .data = &sa8775p_desc }, + { .compatible = "qcom,sc7180-rpmhpd", .data = &sc7180_desc }, + { .compatible = "qcom,sc7280-rpmhpd", .data = &sc7280_desc }, + { .compatible = "qcom,sc8180x-rpmhpd", .data = &sc8180x_desc }, + { .compatible = "qcom,sc8280xp-rpmhpd", .data = &sc8280xp_desc }, + { .compatible = "qcom,sdm670-rpmhpd", .data = &sdm670_desc }, + { .compatible = "qcom,sdm845-rpmhpd", .data = &sdm845_desc }, + { .compatible = "qcom,sdx55-rpmhpd", .data = &sdx55_desc}, + { .compatible = "qcom,sdx65-rpmhpd", .data = &sdx65_desc}, + { .compatible = "qcom,sdx75-rpmhpd", .data = &sdx75_desc}, + { .compatible = "qcom,sm6350-rpmhpd", .data = &sm6350_desc }, + { .compatible = "qcom,sm8150-rpmhpd", .data = &sm8150_desc }, + { .compatible = "qcom,sm8250-rpmhpd", .data = &sm8250_desc }, + { .compatible = "qcom,sm8350-rpmhpd", .data = &sm8350_desc }, + { .compatible = "qcom,sm8450-rpmhpd", .data = &sm8450_desc }, + { .compatible = "qcom,sm8550-rpmhpd", .data = &sm8550_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, rpmhpd_match_table); + +static int rpmhpd_send_corner(struct rpmhpd *pd, int state, + unsigned int corner, bool sync) +{ + struct tcs_cmd cmd = { + .addr = pd->addr, + .data = corner, + }; + + /* + * Wait for an ack only when we are increasing the + * perf state of the power domain + */ + if (sync) + return rpmh_write(pd->dev, state, &cmd, 1); + else + return rpmh_write_async(pd->dev, state, &cmd, 1); +} + +static void to_active_sleep(struct rpmhpd *pd, unsigned int corner, + unsigned int *active, unsigned int *sleep) +{ + *active = corner; + + if (pd->active_only) + *sleep = 0; + else + *sleep = *active; +} + +/* + * This function is used to aggregate the votes across the active only + * resources and its peers. The aggregated votes are sent to RPMh as + * ACTIVE_ONLY votes (which take effect immediately), as WAKE_ONLY votes + * (applied by RPMh on system wakeup) and as SLEEP votes (applied by RPMh + * on system sleep). + * We send ACTIVE_ONLY votes for resources without any peers. For others, + * which have an active only peer, all 3 votes are sent. + */ +static int rpmhpd_aggregate_corner(struct rpmhpd *pd, unsigned int corner) +{ + int ret; + struct rpmhpd *peer = pd->peer; + unsigned int active_corner, sleep_corner; + unsigned int this_active_corner = 0, this_sleep_corner = 0; + unsigned int peer_active_corner = 0, peer_sleep_corner = 0; + + if (pd->state_synced) { + to_active_sleep(pd, corner, &this_active_corner, &this_sleep_corner); + } else { + /* Clamp to highest corner if sync_state hasn't happened */ + this_active_corner = pd->level_count - 1; + this_sleep_corner = pd->level_count - 1; + } + + if (peer && peer->enabled) + to_active_sleep(peer, peer->corner, &peer_active_corner, + &peer_sleep_corner); + + active_corner = max(this_active_corner, peer_active_corner); + + ret = rpmhpd_send_corner(pd, RPMH_ACTIVE_ONLY_STATE, active_corner, + active_corner > pd->active_corner); + if (ret) + return ret; + + pd->active_corner = active_corner; + + if (peer) { + peer->active_corner = active_corner; + + ret = rpmhpd_send_corner(pd, RPMH_WAKE_ONLY_STATE, + active_corner, false); + if (ret) + return ret; + + sleep_corner = max(this_sleep_corner, peer_sleep_corner); + + return rpmhpd_send_corner(pd, RPMH_SLEEP_STATE, sleep_corner, + false); + } + + return ret; +} + +static int rpmhpd_power_on(struct generic_pm_domain *domain) +{ + struct rpmhpd *pd = domain_to_rpmhpd(domain); + unsigned int corner; + int ret; + + mutex_lock(&rpmhpd_lock); + + corner = max(pd->corner, pd->enable_corner); + ret = rpmhpd_aggregate_corner(pd, corner); + if (!ret) + pd->enabled = true; + + mutex_unlock(&rpmhpd_lock); + + return ret; +} + +static int rpmhpd_power_off(struct generic_pm_domain *domain) +{ + struct rpmhpd *pd = domain_to_rpmhpd(domain); + int ret; + + mutex_lock(&rpmhpd_lock); + + ret = rpmhpd_aggregate_corner(pd, 0); + if (!ret) + pd->enabled = false; + + mutex_unlock(&rpmhpd_lock); + + return ret; +} + +static int rpmhpd_set_performance_state(struct generic_pm_domain *domain, + unsigned int level) +{ + struct rpmhpd *pd = domain_to_rpmhpd(domain); + int ret = 0, i; + + mutex_lock(&rpmhpd_lock); + + for (i = 0; i < pd->level_count; i++) + if (level <= pd->level[i]) + break; + + /* + * If the level requested is more than that supported by the + * max corner, just set it to max anyway. + */ + if (i == pd->level_count) + i--; + + if (pd->enabled) { + /* Ensure that the domain isn't turn off */ + if (i < pd->enable_corner) + i = pd->enable_corner; + + ret = rpmhpd_aggregate_corner(pd, i); + if (ret) + goto out; + } + + pd->corner = i; +out: + mutex_unlock(&rpmhpd_lock); + + return ret; +} + +static unsigned int rpmhpd_get_performance_state(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_level(opp); +} + +static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd) +{ + int i; + const u16 *buf; + + buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count); + if (IS_ERR(buf)) + return PTR_ERR(buf); + + /* 2 bytes used for each command DB aux data entry */ + rpmhpd->level_count >>= 1; + + if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS) + return -EINVAL; + + for (i = 0; i < rpmhpd->level_count; i++) { + rpmhpd->level[i] = buf[i]; + + /* Remember the first corner with non-zero level */ + if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i]) + rpmhpd->enable_corner = i; + + /* + * The AUX data may be zero padded. These 0 valued entries at + * the end of the map must be ignored. + */ + if (i > 0 && rpmhpd->level[i] == 0) { + rpmhpd->level_count = i; + break; + } + pr_debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i, + rpmhpd->level[i]); + } + + return 0; +} + +static int rpmhpd_probe(struct platform_device *pdev) +{ + int i, ret; + size_t num_pds; + struct device *dev = &pdev->dev; + struct genpd_onecell_data *data; + struct rpmhpd **rpmhpds; + const struct rpmhpd_desc *desc; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + rpmhpds = desc->rpmhpds; + num_pds = desc->num_pds; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domains = devm_kcalloc(dev, num_pds, sizeof(*data->domains), + GFP_KERNEL); + if (!data->domains) + return -ENOMEM; + + data->num_domains = num_pds; + + for (i = 0; i < num_pds; i++) { + if (!rpmhpds[i]) + continue; + + rpmhpds[i]->dev = dev; + rpmhpds[i]->addr = cmd_db_read_addr(rpmhpds[i]->res_name); + if (!rpmhpds[i]->addr) { + dev_err(dev, "Could not find RPMh address for resource %s\n", + rpmhpds[i]->res_name); + return -ENODEV; + } + + ret = cmd_db_read_slave_id(rpmhpds[i]->res_name); + if (ret != CMD_DB_HW_ARC) { + dev_err(dev, "RPMh slave ID mismatch\n"); + return -EINVAL; + } + + ret = rpmhpd_update_level_mapping(rpmhpds[i]); + if (ret) + return ret; + + rpmhpds[i]->pd.power_off = rpmhpd_power_off; + rpmhpds[i]->pd.power_on = rpmhpd_power_on; + rpmhpds[i]->pd.set_performance_state = rpmhpd_set_performance_state; + rpmhpds[i]->pd.opp_to_performance_state = rpmhpd_get_performance_state; + pm_genpd_init(&rpmhpds[i]->pd, NULL, true); + + data->domains[i] = &rpmhpds[i]->pd; + } + + /* Add subdomains */ + for (i = 0; i < num_pds; i++) { + if (!rpmhpds[i]) + continue; + if (rpmhpds[i]->parent) + pm_genpd_add_subdomain(rpmhpds[i]->parent, + &rpmhpds[i]->pd); + } + + return of_genpd_add_provider_onecell(pdev->dev.of_node, data); +} + +static void rpmhpd_sync_state(struct device *dev) +{ + const struct rpmhpd_desc *desc = of_device_get_match_data(dev); + struct rpmhpd **rpmhpds = desc->rpmhpds; + unsigned int corner; + struct rpmhpd *pd; + unsigned int i; + int ret; + + mutex_lock(&rpmhpd_lock); + for (i = 0; i < desc->num_pds; i++) { + pd = rpmhpds[i]; + if (!pd) + continue; + + pd->state_synced = true; + if (pd->enabled) + corner = max(pd->corner, pd->enable_corner); + else + corner = 0; + + ret = rpmhpd_aggregate_corner(pd, corner); + if (ret) + dev_err(dev, "failed to sync %s\n", pd->res_name); + } + mutex_unlock(&rpmhpd_lock); +} + +static struct platform_driver rpmhpd_driver = { + .driver = { + .name = "qcom-rpmhpd", + .of_match_table = rpmhpd_match_table, + .suppress_bind_attrs = true, + .sync_state = rpmhpd_sync_state, + }, + .probe = rpmhpd_probe, +}; + +static int __init rpmhpd_init(void) +{ + return platform_driver_register(&rpmhpd_driver); +} +core_initcall(rpmhpd_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPMh Power Domain Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/qcom/rpmpd.c b/drivers/pmdomain/qcom/rpmpd.c new file mode 100644 index 000000000000..3135dd1dafe0 --- /dev/null +++ b/drivers/pmdomain/qcom/rpmpd.c @@ -0,0 +1,1023 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define domain_to_rpmpd(domain) container_of(domain, struct rpmpd, pd) + +/* Resource types: + * RPMPD_X is X encoded as a little-endian, lower-case, ASCII string */ +#define RPMPD_SMPA 0x61706d73 +#define RPMPD_LDOA 0x616f646c +#define RPMPD_SMPB 0x62706d73 +#define RPMPD_LDOB 0x626f646c +#define RPMPD_RWCX 0x78637772 +#define RPMPD_RWMX 0x786d7772 +#define RPMPD_RWLC 0x636c7772 +#define RPMPD_RWLM 0x6d6c7772 +#define RPMPD_RWSC 0x63737772 +#define RPMPD_RWSM 0x6d737772 +#define RPMPD_RWGX 0x78677772 + +/* Operation Keys */ +#define KEY_CORNER 0x6e726f63 /* corn */ +#define KEY_ENABLE 0x6e657773 /* swen */ +#define KEY_FLOOR_CORNER 0x636676 /* vfc */ +#define KEY_FLOOR_LEVEL 0x6c6676 /* vfl */ +#define KEY_LEVEL 0x6c766c76 /* vlvl */ + +#define MAX_CORNER_RPMPD_STATE 6 + +struct rpmpd_req { + __le32 key; + __le32 nbytes; + __le32 value; +}; + +struct rpmpd { + struct generic_pm_domain pd; + struct generic_pm_domain *parent; + struct rpmpd *peer; + const bool active_only; + unsigned int corner; + bool enabled; + const int res_type; + const int res_id; + struct qcom_smd_rpm *rpm; + unsigned int max_state; + __le32 key; + bool state_synced; +}; + +struct rpmpd_desc { + struct rpmpd **rpmpds; + size_t num_pds; + unsigned int max_state; +}; + +static DEFINE_MUTEX(rpmpd_lock); + +/* CX */ +static struct rpmpd cx_rwcx0_lvl_ao; +static struct rpmpd cx_rwcx0_lvl = { + .pd = { .name = "cx", }, + .peer = &cx_rwcx0_lvl_ao, + .res_type = RPMPD_RWCX, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd cx_rwcx0_lvl_ao = { + .pd = { .name = "cx_ao", }, + .peer = &cx_rwcx0_lvl, + .active_only = true, + .res_type = RPMPD_RWCX, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd cx_s1a_corner_ao; +static struct rpmpd cx_s1a_corner = { + .pd = { .name = "cx", }, + .peer = &cx_s1a_corner_ao, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_CORNER, +}; + +static struct rpmpd cx_s1a_corner_ao = { + .pd = { .name = "cx_ao", }, + .peer = &cx_s1a_corner, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_CORNER, +}; + +static struct rpmpd cx_s2a_corner_ao; +static struct rpmpd cx_s2a_corner = { + .pd = { .name = "cx", }, + .peer = &cx_s2a_corner_ao, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd cx_s2a_corner_ao = { + .pd = { .name = "cx_ao", }, + .peer = &cx_s2a_corner, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd cx_s2a_lvl_ao; +static struct rpmpd cx_s2a_lvl = { + .pd = { .name = "cx", }, + .peer = &cx_s2a_lvl_ao, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_LEVEL, +}; + +static struct rpmpd cx_s2a_lvl_ao = { + .pd = { .name = "cx_ao", }, + .peer = &cx_s2a_lvl, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_LEVEL, +}; + +static struct rpmpd cx_s3a_lvl_ao; +static struct rpmpd cx_s3a_lvl = { + .pd = { .name = "cx", }, + .peer = &cx_s3a_lvl_ao, + .res_type = RPMPD_SMPA, + .res_id = 3, + .key = KEY_LEVEL, +}; + +static struct rpmpd cx_s3a_lvl_ao = { + .pd = { .name = "cx_ao", }, + .peer = &cx_s3a_lvl, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 3, + .key = KEY_LEVEL, +}; + +static struct rpmpd cx_rwcx0_vfl = { + .pd = { .name = "cx_vfl", }, + .res_type = RPMPD_RWCX, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd cx_rwsc2_vfl = { + .pd = { .name = "cx_vfl", }, + .res_type = RPMPD_RWSC, + .res_id = 2, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd cx_s1a_vfc = { + .pd = { .name = "cx_vfc", }, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_FLOOR_CORNER, +}; + +static struct rpmpd cx_s2a_vfc = { + .pd = { .name = "cx_vfc", }, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_FLOOR_CORNER, +}; + +static struct rpmpd cx_s2a_vfl = { + .pd = { .name = "cx_vfl", }, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd cx_s3a_vfl = { + .pd = { .name = "cx_vfl", }, + .res_type = RPMPD_SMPA, + .res_id = 3, + .key = KEY_FLOOR_LEVEL, +}; + +/* G(F)X */ +static struct rpmpd gfx_s2b_corner = { + .pd = { .name = "gfx", }, + .res_type = RPMPD_SMPB, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd gfx_s2b_vfc = { + .pd = { .name = "gfx_vfc", }, + .res_type = RPMPD_SMPB, + .res_id = 2, + .key = KEY_FLOOR_CORNER, +}; + +static struct rpmpd mx_rwmx0_lvl; +static struct rpmpd gx_rwgx0_lvl_ao; +static struct rpmpd gx_rwgx0_lvl = { + .pd = { .name = "gx", }, + .peer = &gx_rwgx0_lvl_ao, + .res_type = RPMPD_RWGX, + .parent = &mx_rwmx0_lvl.pd, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_rwmx0_lvl_ao; +static struct rpmpd gx_rwgx0_lvl_ao = { + .pd = { .name = "gx_ao", }, + .peer = &gx_rwgx0_lvl, + .parent = &mx_rwmx0_lvl_ao.pd, + .active_only = true, + .res_type = RPMPD_RWGX, + .res_id = 0, + .key = KEY_LEVEL, +}; + +/* MX */ +static struct rpmpd mx_l3a_corner_ao; +static struct rpmpd mx_l3a_corner = { + .pd = { .name = "mx", }, + .peer = &mx_l3a_corner_ao, + .res_type = RPMPD_LDOA, + .res_id = 3, + .key = KEY_CORNER, +}; + +static struct rpmpd mx_l3a_corner_ao = { + .pd = { .name = "mx_ao", }, + .peer = &mx_l3a_corner, + .active_only = true, + .res_type = RPMPD_LDOA, + .res_id = 3, + .key = KEY_CORNER, +}; + +static struct rpmpd mx_l12a_lvl_ao; +static struct rpmpd mx_l12a_lvl = { + .pd = { .name = "mx", }, + .peer = &mx_l12a_lvl_ao, + .res_type = RPMPD_LDOA, + .res_id = 12, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_l12a_lvl_ao = { + .pd = { .name = "mx_ao", }, + .peer = &mx_l12a_lvl, + .active_only = true, + .res_type = RPMPD_LDOA, + .res_id = 12, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_s2a_corner_ao; +static struct rpmpd mx_s2a_corner = { + .pd = { .name = "mx", }, + .peer = &mx_s2a_corner_ao, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd mx_s2a_corner_ao = { + .pd = { .name = "mx_ao", }, + .peer = &mx_s2a_corner, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 2, + .key = KEY_CORNER, +}; + +static struct rpmpd mx_rwmx0_lvl_ao; +static struct rpmpd mx_rwmx0_lvl = { + .pd = { .name = "mx", }, + .peer = &mx_rwmx0_lvl_ao, + .res_type = RPMPD_RWMX, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_rwmx0_lvl_ao = { + .pd = { .name = "mx_ao", }, + .peer = &mx_rwmx0_lvl, + .active_only = true, + .res_type = RPMPD_RWMX, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_s6a_lvl_ao; +static struct rpmpd mx_s6a_lvl = { + .pd = { .name = "mx", }, + .peer = &mx_s6a_lvl_ao, + .res_type = RPMPD_SMPA, + .res_id = 6, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_s6a_lvl_ao = { + .pd = { .name = "mx_ao", }, + .peer = &mx_s6a_lvl, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 6, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_s7a_lvl_ao; +static struct rpmpd mx_s7a_lvl = { + .pd = { .name = "mx", }, + .peer = &mx_s7a_lvl_ao, + .res_type = RPMPD_SMPA, + .res_id = 7, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_s7a_lvl_ao = { + .pd = { .name = "mx_ao", }, + .peer = &mx_s7a_lvl, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 7, + .key = KEY_LEVEL, +}; + +static struct rpmpd mx_l12a_vfl = { + .pd = { .name = "mx_vfl", }, + .res_type = RPMPD_LDOA, + .res_id = 12, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd mx_rwmx0_vfl = { + .pd = { .name = "mx_vfl", }, + .res_type = RPMPD_RWMX, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd mx_rwsm6_vfl = { + .pd = { .name = "mx_vfl", }, + .res_type = RPMPD_RWSM, + .res_id = 6, + .key = KEY_FLOOR_LEVEL, +}; + +/* MD */ +static struct rpmpd md_s1a_corner_ao; +static struct rpmpd md_s1a_corner = { + .pd = { .name = "md", }, + .peer = &md_s1a_corner_ao, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_CORNER, +}; + +static struct rpmpd md_s1a_corner_ao = { + .pd = { .name = "md_ao", }, + .peer = &md_s1a_corner, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_CORNER, +}; + +static struct rpmpd md_s1a_lvl_ao; +static struct rpmpd md_s1a_lvl = { + .pd = { .name = "md", }, + .peer = &md_s1a_lvl_ao, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_LEVEL, +}; + +static struct rpmpd md_s1a_lvl_ao = { + .pd = { .name = "md_ao", }, + .peer = &md_s1a_lvl, + .active_only = true, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_LEVEL, +}; + +static struct rpmpd md_s1a_vfc = { + .pd = { .name = "md_vfc", }, + .res_type = RPMPD_SMPA, + .res_id = 1, + .key = KEY_FLOOR_CORNER, +}; + +/* LPI_CX */ +static struct rpmpd lpi_cx_rwlc0_lvl = { + .pd = { .name = "lpi_cx", }, + .res_type = RPMPD_RWLC, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd lpi_cx_rwlc0_vfl = { + .pd = { .name = "lpi_cx_vfl", }, + .res_type = RPMPD_RWLC, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +/* LPI_MX */ +static struct rpmpd lpi_mx_rwlm0_lvl = { + .pd = { .name = "lpi_mx", }, + .res_type = RPMPD_RWLM, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd lpi_mx_rwlm0_vfl = { + .pd = { .name = "lpi_mx_vfl", }, + .res_type = RPMPD_RWLM, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +/* SSC_CX */ +static struct rpmpd ssc_cx_l26a_corner = { + .pd = { .name = "ssc_cx", }, + .res_type = RPMPD_LDOA, + .res_id = 26, + .key = KEY_CORNER, +}; + +static struct rpmpd ssc_cx_rwlc0_lvl = { + .pd = { .name = "ssc_cx", }, + .res_type = RPMPD_RWLC, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd ssc_cx_rwsc0_lvl = { + .pd = { .name = "ssc_cx", }, + .res_type = RPMPD_RWSC, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd ssc_cx_l26a_vfc = { + .pd = { .name = "ssc_cx_vfc", }, + .res_type = RPMPD_LDOA, + .res_id = 26, + .key = KEY_FLOOR_CORNER, +}; + +static struct rpmpd ssc_cx_rwlc0_vfl = { + .pd = { .name = "ssc_cx_vfl", }, + .res_type = RPMPD_RWLC, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd ssc_cx_rwsc0_vfl = { + .pd = { .name = "ssc_cx_vfl", }, + .res_type = RPMPD_RWSC, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +/* SSC_MX */ +static struct rpmpd ssc_mx_rwlm0_lvl = { + .pd = { .name = "ssc_mx", }, + .res_type = RPMPD_RWLM, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd ssc_mx_rwsm0_lvl = { + .pd = { .name = "ssc_mx", }, + .res_type = RPMPD_RWSM, + .res_id = 0, + .key = KEY_LEVEL, +}; + +static struct rpmpd ssc_mx_rwlm0_vfl = { + .pd = { .name = "ssc_mx_vfl", }, + .res_type = RPMPD_RWLM, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd ssc_mx_rwsm0_vfl = { + .pd = { .name = "ssc_mx_vfl", }, + .res_type = RPMPD_RWSM, + .res_id = 0, + .key = KEY_FLOOR_LEVEL, +}; + +static struct rpmpd *mdm9607_rpmpds[] = { + [MDM9607_VDDCX] = &cx_s3a_lvl, + [MDM9607_VDDCX_AO] = &cx_s3a_lvl_ao, + [MDM9607_VDDCX_VFL] = &cx_s3a_vfl, + [MDM9607_VDDMX] = &mx_l12a_lvl, + [MDM9607_VDDMX_AO] = &mx_l12a_lvl_ao, + [MDM9607_VDDMX_VFL] = &mx_l12a_vfl, +}; + +static const struct rpmpd_desc mdm9607_desc = { + .rpmpds = mdm9607_rpmpds, + .num_pds = ARRAY_SIZE(mdm9607_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO, +}; + +static struct rpmpd *msm8226_rpmpds[] = { + [MSM8226_VDDCX] = &cx_s1a_corner, + [MSM8226_VDDCX_AO] = &cx_s1a_corner_ao, + [MSM8226_VDDCX_VFC] = &cx_s1a_vfc, +}; + +static const struct rpmpd_desc msm8226_desc = { + .rpmpds = msm8226_rpmpds, + .num_pds = ARRAY_SIZE(msm8226_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + +static struct rpmpd *msm8939_rpmpds[] = { + [MSM8939_VDDMDCX] = &md_s1a_corner, + [MSM8939_VDDMDCX_AO] = &md_s1a_corner_ao, + [MSM8939_VDDMDCX_VFC] = &md_s1a_vfc, + [MSM8939_VDDCX] = &cx_s2a_corner, + [MSM8939_VDDCX_AO] = &cx_s2a_corner_ao, + [MSM8939_VDDCX_VFC] = &cx_s2a_vfc, + [MSM8939_VDDMX] = &mx_l3a_corner, + [MSM8939_VDDMX_AO] = &mx_l3a_corner_ao, +}; + +static const struct rpmpd_desc msm8939_desc = { + .rpmpds = msm8939_rpmpds, + .num_pds = ARRAY_SIZE(msm8939_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + +static struct rpmpd *msm8916_rpmpds[] = { + [MSM8916_VDDCX] = &cx_s1a_corner, + [MSM8916_VDDCX_AO] = &cx_s1a_corner_ao, + [MSM8916_VDDCX_VFC] = &cx_s1a_vfc, + [MSM8916_VDDMX] = &mx_l3a_corner, + [MSM8916_VDDMX_AO] = &mx_l3a_corner_ao, +}; + +static const struct rpmpd_desc msm8916_desc = { + .rpmpds = msm8916_rpmpds, + .num_pds = ARRAY_SIZE(msm8916_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + +static struct rpmpd *msm8953_rpmpds[] = { + [MSM8953_VDDMD] = &md_s1a_lvl, + [MSM8953_VDDMD_AO] = &md_s1a_lvl_ao, + [MSM8953_VDDCX] = &cx_s2a_lvl, + [MSM8953_VDDCX_AO] = &cx_s2a_lvl_ao, + [MSM8953_VDDCX_VFL] = &cx_s2a_vfl, + [MSM8953_VDDMX] = &mx_s7a_lvl, + [MSM8953_VDDMX_AO] = &mx_s7a_lvl_ao, +}; + +static const struct rpmpd_desc msm8953_desc = { + .rpmpds = msm8953_rpmpds, + .num_pds = ARRAY_SIZE(msm8953_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO, +}; + +static struct rpmpd *msm8976_rpmpds[] = { + [MSM8976_VDDCX] = &cx_s2a_lvl, + [MSM8976_VDDCX_AO] = &cx_s2a_lvl_ao, + [MSM8976_VDDCX_VFL] = &cx_rwsc2_vfl, + [MSM8976_VDDMX] = &mx_s6a_lvl, + [MSM8976_VDDMX_AO] = &mx_s6a_lvl_ao, + [MSM8976_VDDMX_VFL] = &mx_rwsm6_vfl, +}; + +static const struct rpmpd_desc msm8976_desc = { + .rpmpds = msm8976_rpmpds, + .num_pds = ARRAY_SIZE(msm8976_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_HIGH, +}; + +static struct rpmpd *msm8994_rpmpds[] = { + [MSM8994_VDDCX] = &cx_s1a_corner, + [MSM8994_VDDCX_AO] = &cx_s1a_corner_ao, + [MSM8994_VDDCX_VFC] = &cx_s1a_vfc, + [MSM8994_VDDMX] = &mx_s2a_corner, + [MSM8994_VDDMX_AO] = &mx_s2a_corner_ao, + + /* Attention! *Some* 8994 boards with pm8004 may use SMPC here! */ + [MSM8994_VDDGFX] = &gfx_s2b_corner, + [MSM8994_VDDGFX_VFC] = &gfx_s2b_vfc, +}; + +static const struct rpmpd_desc msm8994_desc = { + .rpmpds = msm8994_rpmpds, + .num_pds = ARRAY_SIZE(msm8994_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + +static struct rpmpd *msm8996_rpmpds[] = { + [MSM8996_VDDCX] = &cx_s1a_corner, + [MSM8996_VDDCX_AO] = &cx_s1a_corner_ao, + [MSM8996_VDDCX_VFC] = &cx_s1a_vfc, + [MSM8996_VDDMX] = &mx_s2a_corner, + [MSM8996_VDDMX_AO] = &mx_s2a_corner_ao, + [MSM8996_VDDSSCX] = &ssc_cx_l26a_corner, + [MSM8996_VDDSSCX_VFC] = &ssc_cx_l26a_vfc, +}; + +static const struct rpmpd_desc msm8996_desc = { + .rpmpds = msm8996_rpmpds, + .num_pds = ARRAY_SIZE(msm8996_rpmpds), + .max_state = MAX_CORNER_RPMPD_STATE, +}; + +static struct rpmpd *msm8998_rpmpds[] = { + [MSM8998_VDDCX] = &cx_rwcx0_lvl, + [MSM8998_VDDCX_AO] = &cx_rwcx0_lvl_ao, + [MSM8998_VDDCX_VFL] = &cx_rwcx0_vfl, + [MSM8998_VDDMX] = &mx_rwmx0_lvl, + [MSM8998_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [MSM8998_VDDMX_VFL] = &mx_rwmx0_vfl, + [MSM8998_SSCCX] = &ssc_cx_rwsc0_lvl, + [MSM8998_SSCCX_VFL] = &ssc_cx_rwsc0_vfl, + [MSM8998_SSCMX] = &ssc_mx_rwsm0_lvl, + [MSM8998_SSCMX_VFL] = &ssc_mx_rwsm0_vfl, +}; + +static const struct rpmpd_desc msm8998_desc = { + .rpmpds = msm8998_rpmpds, + .num_pds = ARRAY_SIZE(msm8998_rpmpds), + .max_state = RPM_SMD_LEVEL_BINNING, +}; + +static struct rpmpd *qcs404_rpmpds[] = { + [QCS404_VDDMX] = &mx_rwmx0_lvl, + [QCS404_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [QCS404_VDDMX_VFL] = &mx_rwmx0_vfl, + [QCS404_LPICX] = &lpi_cx_rwlc0_lvl, + [QCS404_LPICX_VFL] = &lpi_cx_rwlc0_vfl, + [QCS404_LPIMX] = &lpi_mx_rwlm0_lvl, + [QCS404_LPIMX_VFL] = &lpi_mx_rwlm0_vfl, +}; + +static const struct rpmpd_desc qcs404_desc = { + .rpmpds = qcs404_rpmpds, + .num_pds = ARRAY_SIZE(qcs404_rpmpds), + .max_state = RPM_SMD_LEVEL_BINNING, +}; + +static struct rpmpd *sdm660_rpmpds[] = { + [SDM660_VDDCX] = &cx_rwcx0_lvl, + [SDM660_VDDCX_AO] = &cx_rwcx0_lvl_ao, + [SDM660_VDDCX_VFL] = &cx_rwcx0_vfl, + [SDM660_VDDMX] = &mx_rwmx0_lvl, + [SDM660_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [SDM660_VDDMX_VFL] = &mx_rwmx0_vfl, + [SDM660_SSCCX] = &ssc_cx_rwlc0_lvl, + [SDM660_SSCCX_VFL] = &ssc_cx_rwlc0_vfl, + [SDM660_SSCMX] = &ssc_mx_rwlm0_lvl, + [SDM660_SSCMX_VFL] = &ssc_mx_rwlm0_vfl, +}; + +static const struct rpmpd_desc sdm660_desc = { + .rpmpds = sdm660_rpmpds, + .num_pds = ARRAY_SIZE(sdm660_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO, +}; + +static struct rpmpd *sm6115_rpmpds[] = { + [SM6115_VDDCX] = &cx_rwcx0_lvl, + [SM6115_VDDCX_AO] = &cx_rwcx0_lvl_ao, + [SM6115_VDDCX_VFL] = &cx_rwcx0_vfl, + [SM6115_VDDMX] = &mx_rwmx0_lvl, + [SM6115_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [SM6115_VDDMX_VFL] = &mx_rwmx0_vfl, + [SM6115_VDD_LPI_CX] = &lpi_cx_rwlc0_lvl, + [SM6115_VDD_LPI_MX] = &lpi_mx_rwlm0_lvl, +}; + +static const struct rpmpd_desc sm6115_desc = { + .rpmpds = sm6115_rpmpds, + .num_pds = ARRAY_SIZE(sm6115_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, +}; + +static struct rpmpd *sm6125_rpmpds[] = { + [SM6125_VDDCX] = &cx_rwcx0_lvl, + [SM6125_VDDCX_AO] = &cx_rwcx0_lvl_ao, + [SM6125_VDDCX_VFL] = &cx_rwcx0_vfl, + [SM6125_VDDMX] = &mx_rwmx0_lvl, + [SM6125_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [SM6125_VDDMX_VFL] = &mx_rwmx0_vfl, +}; + +static const struct rpmpd_desc sm6125_desc = { + .rpmpds = sm6125_rpmpds, + .num_pds = ARRAY_SIZE(sm6125_rpmpds), + .max_state = RPM_SMD_LEVEL_BINNING, +}; + +static struct rpmpd *sm6375_rpmpds[] = { + [SM6375_VDDCX] = &cx_rwcx0_lvl, + [SM6375_VDDCX_AO] = &cx_rwcx0_lvl_ao, + [SM6375_VDDCX_VFL] = &cx_rwcx0_vfl, + [SM6375_VDDMX] = &mx_rwmx0_lvl, + [SM6375_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [SM6375_VDDMX_VFL] = &mx_rwmx0_vfl, + [SM6375_VDDGX] = &gx_rwgx0_lvl, + [SM6375_VDDGX_AO] = &gx_rwgx0_lvl_ao, + [SM6375_VDD_LPI_CX] = &lpi_cx_rwlc0_lvl, + [SM6375_VDD_LPI_MX] = &lpi_mx_rwlm0_lvl, +}; + +static const struct rpmpd_desc sm6375_desc = { + .rpmpds = sm6375_rpmpds, + .num_pds = ARRAY_SIZE(sm6375_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, +}; + +static struct rpmpd *qcm2290_rpmpds[] = { + [QCM2290_VDDCX] = &cx_rwcx0_lvl, + [QCM2290_VDDCX_AO] = &cx_rwcx0_lvl_ao, + [QCM2290_VDDCX_VFL] = &cx_rwcx0_vfl, + [QCM2290_VDDMX] = &mx_rwmx0_lvl, + [QCM2290_VDDMX_AO] = &mx_rwmx0_lvl_ao, + [QCM2290_VDDMX_VFL] = &mx_rwmx0_vfl, + [QCM2290_VDD_LPI_CX] = &lpi_cx_rwlc0_lvl, + [QCM2290_VDD_LPI_MX] = &lpi_mx_rwlm0_lvl, +}; + +static const struct rpmpd_desc qcm2290_desc = { + .rpmpds = qcm2290_rpmpds, + .num_pds = ARRAY_SIZE(qcm2290_rpmpds), + .max_state = RPM_SMD_LEVEL_TURBO_NO_CPR, +}; + +static const struct of_device_id rpmpd_match_table[] = { + { .compatible = "qcom,mdm9607-rpmpd", .data = &mdm9607_desc }, + { .compatible = "qcom,msm8226-rpmpd", .data = &msm8226_desc }, + { .compatible = "qcom,msm8909-rpmpd", .data = &msm8916_desc }, + { .compatible = "qcom,msm8916-rpmpd", .data = &msm8916_desc }, + { .compatible = "qcom,msm8939-rpmpd", .data = &msm8939_desc }, + { .compatible = "qcom,msm8953-rpmpd", .data = &msm8953_desc }, + { .compatible = "qcom,msm8976-rpmpd", .data = &msm8976_desc }, + { .compatible = "qcom,msm8994-rpmpd", .data = &msm8994_desc }, + { .compatible = "qcom,msm8996-rpmpd", .data = &msm8996_desc }, + { .compatible = "qcom,msm8998-rpmpd", .data = &msm8998_desc }, + { .compatible = "qcom,qcm2290-rpmpd", .data = &qcm2290_desc }, + { .compatible = "qcom,qcs404-rpmpd", .data = &qcs404_desc }, + { .compatible = "qcom,sdm660-rpmpd", .data = &sdm660_desc }, + { .compatible = "qcom,sm6115-rpmpd", .data = &sm6115_desc }, + { .compatible = "qcom,sm6125-rpmpd", .data = &sm6125_desc }, + { .compatible = "qcom,sm6375-rpmpd", .data = &sm6375_desc }, + { } +}; +MODULE_DEVICE_TABLE(of, rpmpd_match_table); + +static int rpmpd_send_enable(struct rpmpd *pd, bool enable) +{ + struct rpmpd_req req = { + .key = KEY_ENABLE, + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(enable), + }; + + return qcom_rpm_smd_write(pd->rpm, QCOM_SMD_RPM_ACTIVE_STATE, + pd->res_type, pd->res_id, &req, sizeof(req)); +} + +static int rpmpd_send_corner(struct rpmpd *pd, int state, unsigned int corner) +{ + struct rpmpd_req req = { + .key = pd->key, + .nbytes = cpu_to_le32(sizeof(u32)), + .value = cpu_to_le32(corner), + }; + + return qcom_rpm_smd_write(pd->rpm, state, pd->res_type, pd->res_id, + &req, sizeof(req)); +}; + +static void to_active_sleep(struct rpmpd *pd, unsigned int corner, + unsigned int *active, unsigned int *sleep) +{ + *active = corner; + + if (pd->active_only) + *sleep = 0; + else + *sleep = *active; +} + +static int rpmpd_aggregate_corner(struct rpmpd *pd) +{ + int ret; + struct rpmpd *peer = pd->peer; + unsigned int active_corner, sleep_corner; + unsigned int this_active_corner = 0, this_sleep_corner = 0; + unsigned int peer_active_corner = 0, peer_sleep_corner = 0; + + /* Clamp to the highest corner/level if sync_state isn't done yet */ + if (!pd->state_synced) + this_active_corner = this_sleep_corner = pd->max_state - 1; + else + to_active_sleep(pd, pd->corner, &this_active_corner, &this_sleep_corner); + + if (peer && peer->enabled) + to_active_sleep(peer, peer->corner, &peer_active_corner, + &peer_sleep_corner); + + active_corner = max(this_active_corner, peer_active_corner); + + ret = rpmpd_send_corner(pd, QCOM_SMD_RPM_ACTIVE_STATE, active_corner); + if (ret) + return ret; + + sleep_corner = max(this_sleep_corner, peer_sleep_corner); + + return rpmpd_send_corner(pd, QCOM_SMD_RPM_SLEEP_STATE, sleep_corner); +} + +static int rpmpd_power_on(struct generic_pm_domain *domain) +{ + int ret; + struct rpmpd *pd = domain_to_rpmpd(domain); + + mutex_lock(&rpmpd_lock); + + ret = rpmpd_send_enable(pd, true); + if (ret) + goto out; + + pd->enabled = true; + + if (pd->corner) + ret = rpmpd_aggregate_corner(pd); + +out: + mutex_unlock(&rpmpd_lock); + + return ret; +} + +static int rpmpd_power_off(struct generic_pm_domain *domain) +{ + int ret; + struct rpmpd *pd = domain_to_rpmpd(domain); + + mutex_lock(&rpmpd_lock); + + ret = rpmpd_send_enable(pd, false); + if (!ret) + pd->enabled = false; + + mutex_unlock(&rpmpd_lock); + + return ret; +} + +static int rpmpd_set_performance(struct generic_pm_domain *domain, + unsigned int state) +{ + int ret = 0; + struct rpmpd *pd = domain_to_rpmpd(domain); + + if (state > pd->max_state) + state = pd->max_state; + + mutex_lock(&rpmpd_lock); + + pd->corner = state; + + /* Always send updates for vfc and vfl */ + if (!pd->enabled && pd->key != cpu_to_le32(KEY_FLOOR_CORNER) && + pd->key != cpu_to_le32(KEY_FLOOR_LEVEL)) + goto out; + + ret = rpmpd_aggregate_corner(pd); + +out: + mutex_unlock(&rpmpd_lock); + + return ret; +} + +static unsigned int rpmpd_get_performance(struct generic_pm_domain *genpd, + struct dev_pm_opp *opp) +{ + return dev_pm_opp_get_level(opp); +} + +static int rpmpd_probe(struct platform_device *pdev) +{ + int i; + size_t num; + struct genpd_onecell_data *data; + struct qcom_smd_rpm *rpm; + struct rpmpd **rpmpds; + const struct rpmpd_desc *desc; + + rpm = dev_get_drvdata(pdev->dev.parent); + if (!rpm) { + dev_err(&pdev->dev, "Unable to retrieve handle to RPM\n"); + return -ENODEV; + } + + desc = of_device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + rpmpds = desc->rpmpds; + num = desc->num_pds; + + data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->domains = devm_kcalloc(&pdev->dev, num, sizeof(*data->domains), + GFP_KERNEL); + if (!data->domains) + return -ENOMEM; + + data->num_domains = num; + + for (i = 0; i < num; i++) { + if (!rpmpds[i]) { + dev_warn(&pdev->dev, "rpmpds[] with empty entry at index=%d\n", + i); + continue; + } + + rpmpds[i]->rpm = rpm; + rpmpds[i]->max_state = desc->max_state; + rpmpds[i]->pd.power_off = rpmpd_power_off; + rpmpds[i]->pd.power_on = rpmpd_power_on; + rpmpds[i]->pd.set_performance_state = rpmpd_set_performance; + rpmpds[i]->pd.opp_to_performance_state = rpmpd_get_performance; + pm_genpd_init(&rpmpds[i]->pd, NULL, true); + + data->domains[i] = &rpmpds[i]->pd; + } + + /* Add subdomains */ + for (i = 0; i < num; i++) { + if (!rpmpds[i]) + continue; + + if (rpmpds[i]->parent) + pm_genpd_add_subdomain(rpmpds[i]->parent, &rpmpds[i]->pd); + } + + return of_genpd_add_provider_onecell(pdev->dev.of_node, data); +} + +static void rpmpd_sync_state(struct device *dev) +{ + const struct rpmpd_desc *desc = of_device_get_match_data(dev); + struct rpmpd **rpmpds = desc->rpmpds; + struct rpmpd *pd; + unsigned int i; + int ret; + + mutex_lock(&rpmpd_lock); + for (i = 0; i < desc->num_pds; i++) { + pd = rpmpds[i]; + if (!pd) + continue; + + pd->state_synced = true; + + if (!pd->enabled) + pd->corner = 0; + + ret = rpmpd_aggregate_corner(pd); + if (ret) + dev_err(dev, "failed to sync %s: %d\n", pd->pd.name, ret); + } + mutex_unlock(&rpmpd_lock); +} + +static struct platform_driver rpmpd_driver = { + .driver = { + .name = "qcom-rpmpd", + .of_match_table = rpmpd_match_table, + .suppress_bind_attrs = true, + .sync_state = rpmpd_sync_state, + }, + .probe = rpmpd_probe, +}; + +static int __init rpmpd_init(void) +{ + return platform_driver_register(&rpmpd_driver); +} +core_initcall(rpmpd_init); + +MODULE_DESCRIPTION("Qualcomm Technologies, Inc. RPM Power Domain Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pmdomain/renesas/Makefile b/drivers/pmdomain/renesas/Makefile new file mode 100644 index 000000000000..e306e396fc8c --- /dev/null +++ b/drivers/pmdomain/renesas/Makefile @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0 +# SoC +obj-$(CONFIG_SYSC_R8A7742) += r8a7742-sysc.o +obj-$(CONFIG_SYSC_R8A7743) += r8a7743-sysc.o +obj-$(CONFIG_SYSC_R8A7745) += r8a7745-sysc.o +obj-$(CONFIG_SYSC_R8A77470) += r8a77470-sysc.o +obj-$(CONFIG_SYSC_R8A774A1) += r8a774a1-sysc.o +obj-$(CONFIG_SYSC_R8A774B1) += r8a774b1-sysc.o +obj-$(CONFIG_SYSC_R8A774C0) += r8a774c0-sysc.o +obj-$(CONFIG_SYSC_R8A774E1) += r8a774e1-sysc.o +obj-$(CONFIG_SYSC_R8A7779) += r8a7779-sysc.o +obj-$(CONFIG_SYSC_R8A7790) += r8a7790-sysc.o +obj-$(CONFIG_SYSC_R8A7791) += r8a7791-sysc.o +obj-$(CONFIG_SYSC_R8A7792) += r8a7792-sysc.o +obj-$(CONFIG_SYSC_R8A7794) += r8a7794-sysc.o +obj-$(CONFIG_SYSC_R8A7795) += r8a7795-sysc.o +obj-$(CONFIG_SYSC_R8A77960) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77961) += r8a7796-sysc.o +obj-$(CONFIG_SYSC_R8A77965) += r8a77965-sysc.o +obj-$(CONFIG_SYSC_R8A77970) += r8a77970-sysc.o +obj-$(CONFIG_SYSC_R8A77980) += r8a77980-sysc.o +obj-$(CONFIG_SYSC_R8A77990) += r8a77990-sysc.o +obj-$(CONFIG_SYSC_R8A77995) += r8a77995-sysc.o +obj-$(CONFIG_SYSC_R8A779A0) += r8a779a0-sysc.o +obj-$(CONFIG_SYSC_R8A779F0) += r8a779f0-sysc.o +obj-$(CONFIG_SYSC_R8A779G0) += r8a779g0-sysc.o +# Family +obj-$(CONFIG_SYSC_RCAR) += rcar-sysc.o +obj-$(CONFIG_SYSC_RCAR_GEN4) += rcar-gen4-sysc.o +obj-$(CONFIG_SYSC_RMOBILE) += rmobile-sysc.o diff --git a/drivers/pmdomain/renesas/r8a7742-sysc.c b/drivers/pmdomain/renesas/r8a7742-sysc.c new file mode 100644 index 000000000000..219a675f83f4 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7742-sysc.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G1H System Controller + * + * Copyright (C) 2020 Renesas Electronics Corp. + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7742_areas[] __initconst = { + { "always-on", 0, 0, R8A7742_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7742_PD_CA15_SCU, R8A7742_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7742_PD_CA15_CPU0, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7742_PD_CA15_CPU1, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu2", 0x40, 2, R8A7742_PD_CA15_CPU2, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu3", 0x40, 3, R8A7742_PD_CA15_CPU3, R8A7742_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca7-scu", 0x100, 0, R8A7742_PD_CA7_SCU, R8A7742_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7742_PD_CA7_CPU0, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7742_PD_CA7_CPU1, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu2", 0x1c0, 2, R8A7742_PD_CA7_CPU2, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu3", 0x1c0, 3, R8A7742_PD_CA7_CPU3, R8A7742_PD_CA7_SCU, + PD_CPU_NOCR }, + { "rgx", 0xc0, 0, R8A7742_PD_RGX, R8A7742_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7742_sysc_info __initconst = { + .areas = r8a7742_areas, + .num_areas = ARRAY_SIZE(r8a7742_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7743-sysc.c b/drivers/pmdomain/renesas/r8a7743-sysc.c new file mode 100644 index 000000000000..4e2c0ab951b3 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7743-sysc.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G1M System Controller + * + * Copyright (C) 2016 Cogent Embedded Inc. + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7743_areas[] __initconst = { + { "always-on", 0, 0, R8A7743_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7743_PD_CA15_SCU, R8A7743_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7743_PD_CA15_CPU0, R8A7743_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7743_PD_CA15_CPU1, R8A7743_PD_CA15_SCU, + PD_CPU_NOCR }, + { "sgx", 0xc0, 0, R8A7743_PD_SGX, R8A7743_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7743_sysc_info __initconst = { + .areas = r8a7743_areas, + .num_areas = ARRAY_SIZE(r8a7743_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7745-sysc.c b/drivers/pmdomain/renesas/r8a7745-sysc.c new file mode 100644 index 000000000000..865821a2f0c6 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7745-sysc.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G1E System Controller + * + * Copyright (C) 2016 Cogent Embedded Inc. + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7745_areas[] __initconst = { + { "always-on", 0, 0, R8A7745_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca7-scu", 0x100, 0, R8A7745_PD_CA7_SCU, R8A7745_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7745_PD_CA7_CPU0, R8A7745_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7745_PD_CA7_CPU1, R8A7745_PD_CA7_SCU, + PD_CPU_NOCR }, + { "sgx", 0xc0, 0, R8A7745_PD_SGX, R8A7745_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7745_sysc_info __initconst = { + .areas = r8a7745_areas, + .num_areas = ARRAY_SIZE(r8a7745_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a77470-sysc.c b/drivers/pmdomain/renesas/r8a77470-sysc.c new file mode 100644 index 000000000000..1eeb8018df50 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77470-sysc.c @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G1C System Controller + * + * Copyright (C) 2018 Renesas Electronics Corp. + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77470_areas[] __initconst = { + { "always-on", 0, 0, R8A77470_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca7-scu", 0x100, 0, R8A77470_PD_CA7_SCU, R8A77470_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A77470_PD_CA7_CPU0, R8A77470_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A77470_PD_CA7_CPU1, R8A77470_PD_CA7_SCU, + PD_CPU_NOCR }, + { "sgx", 0xc0, 0, R8A77470_PD_SGX, R8A77470_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a77470_sysc_info __initconst = { + .areas = r8a77470_areas, + .num_areas = ARRAY_SIZE(r8a77470_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a774a1-sysc.c b/drivers/pmdomain/renesas/r8a774a1-sysc.c new file mode 100644 index 000000000000..38ac2c689ff0 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a774a1-sysc.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2M System Controller + * Copyright (C) 2018 Renesas Electronics Corp. + * + * Based on Renesas R-Car M3-W System Controller + * Copyright (C) 2016 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a774a1_areas[] __initconst = { + { "always-on", 0, 0, R8A774A1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A774A1_PD_CA57_SCU, R8A774A1_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A774A1_PD_CA57_CPU0, R8A774A1_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A774A1_PD_CA57_CPU1, R8A774A1_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A774A1_PD_CA53_SCU, R8A774A1_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A774A1_PD_CA53_CPU0, R8A774A1_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A774A1_PD_CA53_CPU1, R8A774A1_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A774A1_PD_CA53_CPU2, R8A774A1_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A774A1_PD_CA53_CPU3, R8A774A1_PD_CA53_SCU, + PD_CPU_NOCR }, + { "a3vc", 0x380, 0, R8A774A1_PD_A3VC, R8A774A1_PD_ALWAYS_ON }, + { "a2vc0", 0x3c0, 0, R8A774A1_PD_A2VC0, R8A774A1_PD_A3VC }, + { "a2vc1", 0x3c0, 1, R8A774A1_PD_A2VC1, R8A774A1_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A774A1_PD_3DG_A, R8A774A1_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A774A1_PD_3DG_B, R8A774A1_PD_3DG_A }, +}; + +const struct rcar_sysc_info r8a774a1_sysc_info __initconst = { + .areas = r8a774a1_areas, + .num_areas = ARRAY_SIZE(r8a774a1_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a774b1-sysc.c b/drivers/pmdomain/renesas/r8a774b1-sysc.c new file mode 100644 index 000000000000..5f97ff26f3f8 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a774b1-sysc.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2N System Controller + * Copyright (C) 2019 Renesas Electronics Corp. + * + * Based on Renesas R-Car M3-W System Controller + * Copyright (C) 2016 Glider bvba + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a774b1_areas[] __initconst = { + { "always-on", 0, 0, R8A774B1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A774B1_PD_CA57_SCU, R8A774B1_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A774B1_PD_CA57_CPU0, R8A774B1_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A774B1_PD_CA57_CPU1, R8A774B1_PD_CA57_SCU, + PD_CPU_NOCR }, + { "a3vc", 0x380, 0, R8A774B1_PD_A3VC, R8A774B1_PD_ALWAYS_ON }, + { "a3vp", 0x340, 0, R8A774B1_PD_A3VP, R8A774B1_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A774B1_PD_A2VC1, R8A774B1_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A774B1_PD_3DG_A, R8A774B1_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A774B1_PD_3DG_B, R8A774B1_PD_3DG_A }, +}; + +const struct rcar_sysc_info r8a774b1_sysc_info __initconst = { + .areas = r8a774b1_areas, + .num_areas = ARRAY_SIZE(r8a774b1_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a774c0-sysc.c b/drivers/pmdomain/renesas/r8a774c0-sysc.c new file mode 100644 index 000000000000..c1c216f7d073 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a774c0-sysc.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2E System Controller + * Copyright (C) 2018 Renesas Electronics Corp. + * + * Based on Renesas R-Car E3 System Controller + */ + +#include +#include +#include + +#include + +#include "rcar-sysc.h" + +static struct rcar_sysc_area r8a774c0_areas[] __initdata = { + { "always-on", 0, 0, R8A774C0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A774C0_PD_CA53_SCU, R8A774C0_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A774C0_PD_CA53_CPU0, R8A774C0_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A774C0_PD_CA53_CPU1, R8A774C0_PD_CA53_SCU, + PD_CPU_NOCR }, + { "a3vc", 0x380, 0, R8A774C0_PD_A3VC, R8A774C0_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A774C0_PD_A2VC1, R8A774C0_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A774C0_PD_3DG_A, R8A774C0_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A774C0_PD_3DG_B, R8A774C0_PD_3DG_A }, +}; + +/* Fixups for RZ/G2E ES1.0 revision */ +static const struct soc_device_attribute r8a774c0[] __initconst = { + { .soc_id = "r8a774c0", .revision = "ES1.0" }, + { /* sentinel */ } +}; + +static int __init r8a774c0_sysc_init(void) +{ + if (soc_device_match(r8a774c0)) { + /* Fix incorrect 3DG hierarchy */ + swap(r8a774c0_areas[6], r8a774c0_areas[7]); + r8a774c0_areas[6].parent = R8A774C0_PD_ALWAYS_ON; + r8a774c0_areas[7].parent = R8A774C0_PD_3DG_B; + } + + return 0; +} + +const struct rcar_sysc_info r8a774c0_sysc_info __initconst = { + .init = r8a774c0_sysc_init, + .areas = r8a774c0_areas, + .num_areas = ARRAY_SIZE(r8a774c0_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a774e1-sysc.c b/drivers/pmdomain/renesas/r8a774e1-sysc.c new file mode 100644 index 000000000000..18449f746455 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a774e1-sysc.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G2H System Controller + * Copyright (C) 2020 Renesas Electronics Corp. + * + * Based on Renesas R-Car H3 System Controller + * Copyright (C) 2016-2017 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a774e1_areas[] __initconst = { + { "always-on", 0, 0, R8A774E1_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A774E1_PD_CA57_SCU, R8A774E1_PD_ALWAYS_ON, PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A774E1_PD_CA57_CPU0, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A774E1_PD_CA57_CPU1, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca57-cpu2", 0x80, 2, R8A774E1_PD_CA57_CPU2, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca57-cpu3", 0x80, 3, R8A774E1_PD_CA57_CPU3, R8A774E1_PD_CA57_SCU, PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A774E1_PD_CA53_SCU, R8A774E1_PD_ALWAYS_ON, PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A774E1_PD_CA53_CPU0, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A774E1_PD_CA53_CPU1, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A774E1_PD_CA53_CPU2, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A774E1_PD_CA53_CPU3, R8A774E1_PD_CA53_SCU, PD_CPU_NOCR }, + { "a3vp", 0x340, 0, R8A774E1_PD_A3VP, R8A774E1_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A774E1_PD_A3VC, R8A774E1_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A774E1_PD_A2VC1, R8A774E1_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A774E1_PD_3DG_A, R8A774E1_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A774E1_PD_3DG_B, R8A774E1_PD_3DG_A }, + { "3dg-c", 0x100, 2, R8A774E1_PD_3DG_C, R8A774E1_PD_3DG_B }, + { "3dg-d", 0x100, 3, R8A774E1_PD_3DG_D, R8A774E1_PD_3DG_C }, + { "3dg-e", 0x100, 4, R8A774E1_PD_3DG_E, R8A774E1_PD_3DG_D }, +}; + +const struct rcar_sysc_info r8a774e1_sysc_info __initconst = { + .areas = r8a774e1_areas, + .num_areas = ARRAY_SIZE(r8a774e1_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a7779-sysc.c b/drivers/pmdomain/renesas/r8a7779-sysc.c new file mode 100644 index 000000000000..e24a7151d55f --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7779-sysc.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car H1 System Controller + * + * Copyright (C) 2016 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7779_areas[] __initconst = { + { "always-on", 0, 0, R8A7779_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "arm1", 0x40, 1, R8A7779_PD_ARM1, R8A7779_PD_ALWAYS_ON, + PD_CPU_CR }, + { "arm2", 0x40, 2, R8A7779_PD_ARM2, R8A7779_PD_ALWAYS_ON, + PD_CPU_CR }, + { "arm3", 0x40, 3, R8A7779_PD_ARM3, R8A7779_PD_ALWAYS_ON, + PD_CPU_CR }, + { "sgx", 0xc0, 0, R8A7779_PD_SGX, R8A7779_PD_ALWAYS_ON }, + { "vdp", 0x100, 0, R8A7779_PD_VDP, R8A7779_PD_ALWAYS_ON }, + { "imp", 0x140, 0, R8A7779_PD_IMP, R8A7779_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7779_sysc_info __initconst = { + .areas = r8a7779_areas, + .num_areas = ARRAY_SIZE(r8a7779_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7790-sysc.c b/drivers/pmdomain/renesas/r8a7790-sysc.c new file mode 100644 index 000000000000..b9afe7f6245b --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7790-sysc.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car H2 System Controller + * + * Copyright (C) 2016 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7790_areas[] __initconst = { + { "always-on", 0, 0, R8A7790_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7790_PD_CA15_SCU, R8A7790_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7790_PD_CA15_CPU0, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7790_PD_CA15_CPU1, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu2", 0x40, 2, R8A7790_PD_CA15_CPU2, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu3", 0x40, 3, R8A7790_PD_CA15_CPU3, R8A7790_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca7-scu", 0x100, 0, R8A7790_PD_CA7_SCU, R8A7790_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7790_PD_CA7_CPU0, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7790_PD_CA7_CPU1, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu2", 0x1c0, 2, R8A7790_PD_CA7_CPU2, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu3", 0x1c0, 3, R8A7790_PD_CA7_CPU3, R8A7790_PD_CA7_SCU, + PD_CPU_NOCR }, + { "sh-4a", 0x80, 0, R8A7790_PD_SH_4A, R8A7790_PD_ALWAYS_ON }, + { "rgx", 0xc0, 0, R8A7790_PD_RGX, R8A7790_PD_ALWAYS_ON }, + { "imp", 0x140, 0, R8A7790_PD_IMP, R8A7790_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7790_sysc_info __initconst = { + .areas = r8a7790_areas, + .num_areas = ARRAY_SIZE(r8a7790_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7791-sysc.c b/drivers/pmdomain/renesas/r8a7791-sysc.c new file mode 100644 index 000000000000..f00fa24522a3 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7791-sysc.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car M2-W/N System Controller + * + * Copyright (C) 2016 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7791_areas[] __initconst = { + { "always-on", 0, 0, R8A7791_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7791_PD_CA15_SCU, R8A7791_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7791_PD_CA15_CPU0, R8A7791_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7791_PD_CA15_CPU1, R8A7791_PD_CA15_SCU, + PD_CPU_NOCR }, + { "sh-4a", 0x80, 0, R8A7791_PD_SH_4A, R8A7791_PD_ALWAYS_ON }, + { "sgx", 0xc0, 0, R8A7791_PD_SGX, R8A7791_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7791_sysc_info __initconst = { + .areas = r8a7791_areas, + .num_areas = ARRAY_SIZE(r8a7791_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7792-sysc.c b/drivers/pmdomain/renesas/r8a7792-sysc.c new file mode 100644 index 000000000000..60aae242c43f --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7792-sysc.c @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V2H (R8A7792) System Controller + * + * Copyright (C) 2016 Cogent Embedded Inc. + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7792_areas[] __initconst = { + { "always-on", 0, 0, R8A7792_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca15-scu", 0x180, 0, R8A7792_PD_CA15_SCU, R8A7792_PD_ALWAYS_ON, + PD_SCU }, + { "ca15-cpu0", 0x40, 0, R8A7792_PD_CA15_CPU0, R8A7792_PD_CA15_SCU, + PD_CPU_NOCR }, + { "ca15-cpu1", 0x40, 1, R8A7792_PD_CA15_CPU1, R8A7792_PD_CA15_SCU, + PD_CPU_NOCR }, + { "sgx", 0xc0, 0, R8A7792_PD_SGX, R8A7792_PD_ALWAYS_ON }, + { "imp", 0x140, 0, R8A7792_PD_IMP, R8A7792_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7792_sysc_info __initconst = { + .areas = r8a7792_areas, + .num_areas = ARRAY_SIZE(r8a7792_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7794-sysc.c b/drivers/pmdomain/renesas/r8a7794-sysc.c new file mode 100644 index 000000000000..72ef4e85458f --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7794-sysc.c @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car E2 System Controller + * + * Copyright (C) 2016 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a7794_areas[] __initconst = { + { "always-on", 0, 0, R8A7794_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca7-scu", 0x100, 0, R8A7794_PD_CA7_SCU, R8A7794_PD_ALWAYS_ON, + PD_SCU }, + { "ca7-cpu0", 0x1c0, 0, R8A7794_PD_CA7_CPU0, R8A7794_PD_CA7_SCU, + PD_CPU_NOCR }, + { "ca7-cpu1", 0x1c0, 1, R8A7794_PD_CA7_CPU1, R8A7794_PD_CA7_SCU, + PD_CPU_NOCR }, + { "sh-4a", 0x80, 0, R8A7794_PD_SH_4A, R8A7794_PD_ALWAYS_ON }, + { "sgx", 0xc0, 0, R8A7794_PD_SGX, R8A7794_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a7794_sysc_info __initconst = { + .areas = r8a7794_areas, + .num_areas = ARRAY_SIZE(r8a7794_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a7795-sysc.c b/drivers/pmdomain/renesas/r8a7795-sysc.c new file mode 100644 index 000000000000..cbe1ff0fc583 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7795-sysc.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car H3 System Controller + * + * Copyright (C) 2016-2017 Glider bvba + */ + +#include +#include +#include + +#include + +#include "rcar-sysc.h" + +static struct rcar_sysc_area r8a7795_areas[] __initdata = { + { "always-on", 0, 0, R8A7795_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A7795_PD_CA57_SCU, R8A7795_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A7795_PD_CA57_CPU0, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A7795_PD_CA57_CPU1, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu2", 0x80, 2, R8A7795_PD_CA57_CPU2, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu3", 0x80, 3, R8A7795_PD_CA57_CPU3, R8A7795_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A7795_PD_CA53_SCU, R8A7795_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A7795_PD_CA53_CPU0, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A7795_PD_CA53_CPU1, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A7795_PD_CA53_CPU2, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A7795_PD_CA53_CPU3, R8A7795_PD_CA53_SCU, + PD_CPU_NOCR }, + { "a3vp", 0x340, 0, R8A7795_PD_A3VP, R8A7795_PD_ALWAYS_ON }, + { "cr7", 0x240, 0, R8A7795_PD_CR7, R8A7795_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A7795_PD_A3VC, R8A7795_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A7795_PD_A2VC1, R8A7795_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A7795_PD_3DG_A, R8A7795_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A7795_PD_3DG_B, R8A7795_PD_3DG_A }, + { "3dg-c", 0x100, 2, R8A7795_PD_3DG_C, R8A7795_PD_3DG_B }, + { "3dg-d", 0x100, 3, R8A7795_PD_3DG_D, R8A7795_PD_3DG_C }, + { "3dg-e", 0x100, 4, R8A7795_PD_3DG_E, R8A7795_PD_3DG_D }, + { "a3ir", 0x180, 0, R8A7795_PD_A3IR, R8A7795_PD_ALWAYS_ON }, +}; + + + /* + * Fixups for R-Car H3 revisions + */ + +#define NO_EXTMASK BIT(1) /* Missing SYSCEXTMASK register */ + +static const struct soc_device_attribute r8a7795_quirks_match[] __initconst = { + { + .soc_id = "r8a7795", .revision = "ES2.*", + .data = (void *)(NO_EXTMASK), + }, + { /* sentinel */ } +}; + +static int __init r8a7795_sysc_init(void) +{ + const struct soc_device_attribute *attr; + u32 quirks = 0; + + attr = soc_device_match(r8a7795_quirks_match); + if (attr) + quirks = (uintptr_t)attr->data; + + if (quirks & NO_EXTMASK) + r8a7795_sysc_info.extmask_val = 0; + + return 0; +} + +struct rcar_sysc_info r8a7795_sysc_info __initdata = { + .init = r8a7795_sysc_init, + .areas = r8a7795_areas, + .num_areas = ARRAY_SIZE(r8a7795_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a7796-sysc.c b/drivers/pmdomain/renesas/r8a7796-sysc.c new file mode 100644 index 000000000000..471bd5b3b6ad --- /dev/null +++ b/drivers/pmdomain/renesas/r8a7796-sysc.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car M3-W/W+ System Controller + * + * Copyright (C) 2016 Glider bvba + * Copyright (C) 2018-2019 Renesas Electronics Corporation + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static struct rcar_sysc_area r8a7796_areas[] __initdata = { + { "always-on", 0, 0, R8A7796_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A7796_PD_CA57_SCU, R8A7796_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A7796_PD_CA57_CPU0, R8A7796_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A7796_PD_CA57_CPU1, R8A7796_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca53-scu", 0x140, 0, R8A7796_PD_CA53_SCU, R8A7796_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A7796_PD_CA53_CPU0, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A7796_PD_CA53_CPU1, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A7796_PD_CA53_CPU2, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A7796_PD_CA53_CPU3, R8A7796_PD_CA53_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A7796_PD_CR7, R8A7796_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A7796_PD_A3VC, R8A7796_PD_ALWAYS_ON }, + { "a2vc0", 0x3c0, 0, R8A7796_PD_A2VC0, R8A7796_PD_A3VC }, + { "a2vc1", 0x3c0, 1, R8A7796_PD_A2VC1, R8A7796_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A7796_PD_3DG_A, R8A7796_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A7796_PD_3DG_B, R8A7796_PD_3DG_A }, + { "a3ir", 0x180, 0, R8A7796_PD_A3IR, R8A7796_PD_ALWAYS_ON }, +}; + + +#ifdef CONFIG_SYSC_R8A77960 +const struct rcar_sysc_info r8a77960_sysc_info __initconst = { + .areas = r8a7796_areas, + .num_areas = ARRAY_SIZE(r8a7796_areas), +}; +#endif /* CONFIG_SYSC_R8A77960 */ + +#ifdef CONFIG_SYSC_R8A77961 +static int __init r8a77961_sysc_init(void) +{ + rcar_sysc_nullify(r8a7796_areas, ARRAY_SIZE(r8a7796_areas), + R8A7796_PD_A2VC0); + + return 0; +} + +const struct rcar_sysc_info r8a77961_sysc_info __initconst = { + .init = r8a77961_sysc_init, + .areas = r8a7796_areas, + .num_areas = ARRAY_SIZE(r8a7796_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; +#endif /* CONFIG_SYSC_R8A77961 */ diff --git a/drivers/pmdomain/renesas/r8a77965-sysc.c b/drivers/pmdomain/renesas/r8a77965-sysc.c new file mode 100644 index 000000000000..ff0b0d116992 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77965-sysc.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car M3-N System Controller + * Copyright (C) 2018 Jacopo Mondi + * + * Based on Renesas R-Car M3-W System Controller + * Copyright (C) 2016 Glider bvba + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77965_areas[] __initconst = { + { "always-on", 0, 0, R8A77965_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca57-scu", 0x1c0, 0, R8A77965_PD_CA57_SCU, R8A77965_PD_ALWAYS_ON, + PD_SCU }, + { "ca57-cpu0", 0x80, 0, R8A77965_PD_CA57_CPU0, R8A77965_PD_CA57_SCU, + PD_CPU_NOCR }, + { "ca57-cpu1", 0x80, 1, R8A77965_PD_CA57_CPU1, R8A77965_PD_CA57_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A77965_PD_CR7, R8A77965_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A77965_PD_A3VC, R8A77965_PD_ALWAYS_ON }, + { "a3vp", 0x340, 0, R8A77965_PD_A3VP, R8A77965_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A77965_PD_A2VC1, R8A77965_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A77965_PD_3DG_A, R8A77965_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A77965_PD_3DG_B, R8A77965_PD_3DG_A }, +}; + +const struct rcar_sysc_info r8a77965_sysc_info __initconst = { + .areas = r8a77965_areas, + .num_areas = ARRAY_SIZE(r8a77965_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a77970-sysc.c b/drivers/pmdomain/renesas/r8a77970-sysc.c new file mode 100644 index 000000000000..706258250600 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77970-sysc.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V3M System Controller + * + * Copyright (C) 2017 Cogent Embedded Inc. + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77970_areas[] __initconst = { + { "always-on", 0, 0, R8A77970_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77970_PD_CA53_SCU, R8A77970_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77970_PD_CA53_CPU0, R8A77970_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A77970_PD_CA53_CPU1, R8A77970_PD_CA53_SCU, + PD_CPU_NOCR }, + { "a3ir", 0x180, 0, R8A77970_PD_A3IR, R8A77970_PD_ALWAYS_ON }, + { "a2ir0", 0x400, 0, R8A77970_PD_A2IR0, R8A77970_PD_A3IR }, + { "a2ir1", 0x400, 1, R8A77970_PD_A2IR1, R8A77970_PD_A3IR }, + { "a2dp", 0x400, 2, R8A77970_PD_A2DP, R8A77970_PD_A3IR }, + { "a2cn", 0x400, 3, R8A77970_PD_A2CN, R8A77970_PD_A3IR }, + { "a2sc0", 0x400, 4, R8A77970_PD_A2SC0, R8A77970_PD_A3IR }, + { "a2sc1", 0x400, 5, R8A77970_PD_A2SC1, R8A77970_PD_A3IR }, +}; + +const struct rcar_sysc_info r8a77970_sysc_info __initconst = { + .areas = r8a77970_areas, + .num_areas = ARRAY_SIZE(r8a77970_areas), + .extmask_offs = 0x1b0, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a77980-sysc.c b/drivers/pmdomain/renesas/r8a77980-sysc.c new file mode 100644 index 000000000000..39ca84a67daa --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77980-sysc.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V3H System Controller + * + * Copyright (C) 2018 Renesas Electronics Corp. + * Copyright (C) 2018 Cogent Embedded, Inc. + */ + +#include +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77980_areas[] __initconst = { + { "always-on", 0, 0, R8A77980_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77980_PD_CA53_SCU, R8A77980_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77980_PD_CA53_CPU0, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A77980_PD_CA53_CPU1, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu2", 0x200, 2, R8A77980_PD_CA53_CPU2, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu3", 0x200, 3, R8A77980_PD_CA53_CPU3, R8A77980_PD_CA53_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A77980_PD_CR7, R8A77980_PD_ALWAYS_ON }, + { "a3ir", 0x180, 0, R8A77980_PD_A3IR, R8A77980_PD_ALWAYS_ON }, + { "a2ir0", 0x400, 0, R8A77980_PD_A2IR0, R8A77980_PD_A3IR }, + { "a2ir1", 0x400, 1, R8A77980_PD_A2IR1, R8A77980_PD_A3IR }, + { "a2ir2", 0x400, 2, R8A77980_PD_A2IR2, R8A77980_PD_A3IR }, + { "a2ir3", 0x400, 3, R8A77980_PD_A2IR3, R8A77980_PD_A3IR }, + { "a2ir4", 0x400, 4, R8A77980_PD_A2IR4, R8A77980_PD_A3IR }, + { "a2ir5", 0x400, 5, R8A77980_PD_A2IR5, R8A77980_PD_A3IR }, + { "a2sc0", 0x400, 6, R8A77980_PD_A2SC0, R8A77980_PD_A3IR }, + { "a2sc1", 0x400, 7, R8A77980_PD_A2SC1, R8A77980_PD_A3IR }, + { "a2sc2", 0x400, 8, R8A77980_PD_A2SC2, R8A77980_PD_A3IR }, + { "a2sc3", 0x400, 9, R8A77980_PD_A2SC3, R8A77980_PD_A3IR }, + { "a2sc4", 0x400, 10, R8A77980_PD_A2SC4, R8A77980_PD_A3IR }, + { "a2dp0", 0x400, 11, R8A77980_PD_A2DP0, R8A77980_PD_A3IR }, + { "a2dp1", 0x400, 12, R8A77980_PD_A2DP1, R8A77980_PD_A3IR }, + { "a2cn", 0x400, 13, R8A77980_PD_A2CN, R8A77980_PD_A3IR }, + { "a3vip0", 0x2c0, 0, R8A77980_PD_A3VIP0, R8A77980_PD_ALWAYS_ON }, + { "a3vip1", 0x300, 0, R8A77980_PD_A3VIP1, R8A77980_PD_ALWAYS_ON }, + { "a3vip2", 0x280, 0, R8A77980_PD_A3VIP2, R8A77980_PD_ALWAYS_ON }, +}; + +const struct rcar_sysc_info r8a77980_sysc_info __initconst = { + .areas = r8a77980_areas, + .num_areas = ARRAY_SIZE(r8a77980_areas), + .extmask_offs = 0x138, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a77990-sysc.c b/drivers/pmdomain/renesas/r8a77990-sysc.c new file mode 100644 index 000000000000..9f92737dc352 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77990-sysc.c @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car E3 System Controller + * + * Copyright (C) 2018 Renesas Electronics Corp. + */ + +#include +#include +#include + +#include + +#include "rcar-sysc.h" + +static struct rcar_sysc_area r8a77990_areas[] __initdata = { + { "always-on", 0, 0, R8A77990_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77990_PD_CA53_SCU, R8A77990_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77990_PD_CA53_CPU0, R8A77990_PD_CA53_SCU, + PD_CPU_NOCR }, + { "ca53-cpu1", 0x200, 1, R8A77990_PD_CA53_CPU1, R8A77990_PD_CA53_SCU, + PD_CPU_NOCR }, + { "cr7", 0x240, 0, R8A77990_PD_CR7, R8A77990_PD_ALWAYS_ON }, + { "a3vc", 0x380, 0, R8A77990_PD_A3VC, R8A77990_PD_ALWAYS_ON }, + { "a2vc1", 0x3c0, 1, R8A77990_PD_A2VC1, R8A77990_PD_A3VC }, + { "3dg-a", 0x100, 0, R8A77990_PD_3DG_A, R8A77990_PD_ALWAYS_ON }, + { "3dg-b", 0x100, 1, R8A77990_PD_3DG_B, R8A77990_PD_3DG_A }, +}; + +/* Fixups for R-Car E3 ES1.0 revision */ +static const struct soc_device_attribute r8a77990[] __initconst = { + { .soc_id = "r8a77990", .revision = "ES1.0" }, + { /* sentinel */ } +}; + +static int __init r8a77990_sysc_init(void) +{ + if (soc_device_match(r8a77990)) { + /* Fix incorrect 3DG hierarchy */ + swap(r8a77990_areas[7], r8a77990_areas[8]); + r8a77990_areas[7].parent = R8A77990_PD_ALWAYS_ON; + r8a77990_areas[8].parent = R8A77990_PD_3DG_B; + } + + return 0; +} + +const struct rcar_sysc_info r8a77990_sysc_info __initconst = { + .init = r8a77990_sysc_init, + .areas = r8a77990_areas, + .num_areas = ARRAY_SIZE(r8a77990_areas), + .extmask_offs = 0x2f8, + .extmask_val = BIT(0), +}; diff --git a/drivers/pmdomain/renesas/r8a77995-sysc.c b/drivers/pmdomain/renesas/r8a77995-sysc.c new file mode 100644 index 000000000000..efcc67e3d76d --- /dev/null +++ b/drivers/pmdomain/renesas/r8a77995-sysc.c @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car D3 System Controller + * + * Copyright (C) 2017 Glider bvba + */ + +#include + +#include + +#include "rcar-sysc.h" + +static const struct rcar_sysc_area r8a77995_areas[] __initconst = { + { "always-on", 0, 0, R8A77995_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "ca53-scu", 0x140, 0, R8A77995_PD_CA53_SCU, R8A77995_PD_ALWAYS_ON, + PD_SCU }, + { "ca53-cpu0", 0x200, 0, R8A77995_PD_CA53_CPU0, R8A77995_PD_CA53_SCU, + PD_CPU_NOCR }, +}; + + +const struct rcar_sysc_info r8a77995_sysc_info __initconst = { + .areas = r8a77995_areas, + .num_areas = ARRAY_SIZE(r8a77995_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a779a0-sysc.c b/drivers/pmdomain/renesas/r8a779a0-sysc.c new file mode 100644 index 000000000000..04f1bc322ae7 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a779a0-sysc.c @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V3U System Controller + * + * Copyright (C) 2020 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rcar-gen4-sysc.h" + +static struct rcar_gen4_sysc_area r8a779a0_areas[] __initdata = { + { "always-on", R8A779A0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "a3e0", R8A779A0_PD_A3E0, R8A779A0_PD_ALWAYS_ON, PD_SCU }, + { "a3e1", R8A779A0_PD_A3E1, R8A779A0_PD_ALWAYS_ON, PD_SCU }, + { "a2e0d0", R8A779A0_PD_A2E0D0, R8A779A0_PD_A3E0, PD_SCU }, + { "a2e0d1", R8A779A0_PD_A2E0D1, R8A779A0_PD_A3E0, PD_SCU }, + { "a2e1d0", R8A779A0_PD_A2E1D0, R8A779A0_PD_A3E1, PD_SCU }, + { "a2e1d1", R8A779A0_PD_A2E1D1, R8A779A0_PD_A3E1, PD_SCU }, + { "a1e0d0c0", R8A779A0_PD_A1E0D0C0, R8A779A0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c1", R8A779A0_PD_A1E0D0C1, R8A779A0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d1c0", R8A779A0_PD_A1E0D1C0, R8A779A0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e0d1c1", R8A779A0_PD_A1E0D1C1, R8A779A0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e1d0c0", R8A779A0_PD_A1E1D0C0, R8A779A0_PD_A2E1D0, PD_CPU_NOCR }, + { "a1e1d0c1", R8A779A0_PD_A1E1D0C1, R8A779A0_PD_A2E1D0, PD_CPU_NOCR }, + { "a1e1d1c0", R8A779A0_PD_A1E1D1C0, R8A779A0_PD_A2E1D1, PD_CPU_NOCR }, + { "a1e1d1c1", R8A779A0_PD_A1E1D1C1, R8A779A0_PD_A2E1D1, PD_CPU_NOCR }, + { "3dg-a", R8A779A0_PD_3DG_A, R8A779A0_PD_ALWAYS_ON }, + { "3dg-b", R8A779A0_PD_3DG_B, R8A779A0_PD_3DG_A }, + { "a3vip0", R8A779A0_PD_A3VIP0, R8A779A0_PD_ALWAYS_ON }, + { "a3vip1", R8A779A0_PD_A3VIP1, R8A779A0_PD_ALWAYS_ON }, + { "a3vip3", R8A779A0_PD_A3VIP3, R8A779A0_PD_ALWAYS_ON }, + { "a3vip2", R8A779A0_PD_A3VIP2, R8A779A0_PD_ALWAYS_ON }, + { "a3isp01", R8A779A0_PD_A3ISP01, R8A779A0_PD_ALWAYS_ON }, + { "a3isp23", R8A779A0_PD_A3ISP23, R8A779A0_PD_ALWAYS_ON }, + { "a3ir", R8A779A0_PD_A3IR, R8A779A0_PD_ALWAYS_ON }, + { "a2cn0", R8A779A0_PD_A2CN0, R8A779A0_PD_A3IR }, + { "a2imp01", R8A779A0_PD_A2IMP01, R8A779A0_PD_A3IR }, + { "a2dp0", R8A779A0_PD_A2DP0, R8A779A0_PD_A3IR }, + { "a2cv0", R8A779A0_PD_A2CV0, R8A779A0_PD_A3IR }, + { "a2cv1", R8A779A0_PD_A2CV1, R8A779A0_PD_A3IR }, + { "a2cv4", R8A779A0_PD_A2CV4, R8A779A0_PD_A3IR }, + { "a2cv6", R8A779A0_PD_A2CV6, R8A779A0_PD_A3IR }, + { "a2cn2", R8A779A0_PD_A2CN2, R8A779A0_PD_A3IR }, + { "a2imp23", R8A779A0_PD_A2IMP23, R8A779A0_PD_A3IR }, + { "a2dp1", R8A779A0_PD_A2DP1, R8A779A0_PD_A3IR }, + { "a2cv2", R8A779A0_PD_A2CV2, R8A779A0_PD_A3IR }, + { "a2cv3", R8A779A0_PD_A2CV3, R8A779A0_PD_A3IR }, + { "a2cv5", R8A779A0_PD_A2CV5, R8A779A0_PD_A3IR }, + { "a2cv7", R8A779A0_PD_A2CV7, R8A779A0_PD_A3IR }, + { "a2cn1", R8A779A0_PD_A2CN1, R8A779A0_PD_A3IR }, + { "a1cnn0", R8A779A0_PD_A1CNN0, R8A779A0_PD_A2CN0 }, + { "a1cnn2", R8A779A0_PD_A1CNN2, R8A779A0_PD_A2CN2 }, + { "a1dsp0", R8A779A0_PD_A1DSP0, R8A779A0_PD_A2CN2 }, + { "a1cnn1", R8A779A0_PD_A1CNN1, R8A779A0_PD_A2CN1 }, + { "a1dsp1", R8A779A0_PD_A1DSP1, R8A779A0_PD_A2CN1 }, +}; + +const struct rcar_gen4_sysc_info r8a779a0_sysc_info __initconst = { + .areas = r8a779a0_areas, + .num_areas = ARRAY_SIZE(r8a779a0_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a779f0-sysc.c b/drivers/pmdomain/renesas/r8a779f0-sysc.c new file mode 100644 index 000000000000..5602aa6bd7ed --- /dev/null +++ b/drivers/pmdomain/renesas/r8a779f0-sysc.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car S4-8 System Controller + * + * Copyright (C) 2021 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rcar-gen4-sysc.h" + +static struct rcar_gen4_sysc_area r8a779f0_areas[] __initdata = { + { "always-on", R8A779F0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "a3e0", R8A779F0_PD_A3E0, R8A779F0_PD_ALWAYS_ON, PD_SCU }, + { "a3e1", R8A779F0_PD_A3E1, R8A779F0_PD_ALWAYS_ON, PD_SCU }, + { "a2e0d0", R8A779F0_PD_A2E0D0, R8A779F0_PD_A3E0, PD_SCU }, + { "a2e0d1", R8A779F0_PD_A2E0D1, R8A779F0_PD_A3E0, PD_SCU }, + { "a2e1d0", R8A779F0_PD_A2E1D0, R8A779F0_PD_A3E1, PD_SCU }, + { "a2e1d1", R8A779F0_PD_A2E1D1, R8A779F0_PD_A3E1, PD_SCU }, + { "a1e0d0c0", R8A779F0_PD_A1E0D0C0, R8A779F0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c1", R8A779F0_PD_A1E0D0C1, R8A779F0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d1c0", R8A779F0_PD_A1E0D1C0, R8A779F0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e0d1c1", R8A779F0_PD_A1E0D1C1, R8A779F0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e1d0c0", R8A779F0_PD_A1E1D0C0, R8A779F0_PD_A2E1D0, PD_CPU_NOCR }, + { "a1e1d0c1", R8A779F0_PD_A1E1D0C1, R8A779F0_PD_A2E1D0, PD_CPU_NOCR }, + { "a1e1d1c0", R8A779F0_PD_A1E1D1C0, R8A779F0_PD_A2E1D1, PD_CPU_NOCR }, + { "a1e1d1c1", R8A779F0_PD_A1E1D1C1, R8A779F0_PD_A2E1D1, PD_CPU_NOCR }, +}; + +const struct rcar_gen4_sysc_info r8a779f0_sysc_info __initconst = { + .areas = r8a779f0_areas, + .num_areas = ARRAY_SIZE(r8a779f0_areas), +}; diff --git a/drivers/pmdomain/renesas/r8a779g0-sysc.c b/drivers/pmdomain/renesas/r8a779g0-sysc.c new file mode 100644 index 000000000000..b932eba1b804 --- /dev/null +++ b/drivers/pmdomain/renesas/r8a779g0-sysc.c @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas R-Car V4H System Controller + * + * Copyright (C) 2022 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "rcar-gen4-sysc.h" + +static struct rcar_gen4_sysc_area r8a779g0_areas[] __initdata = { + { "always-on", R8A779G0_PD_ALWAYS_ON, -1, PD_ALWAYS_ON }, + { "a3e0", R8A779G0_PD_A3E0, R8A779G0_PD_ALWAYS_ON, PD_SCU }, + { "a2e0d0", R8A779G0_PD_A2E0D0, R8A779G0_PD_A3E0, PD_SCU }, + { "a2e0d1", R8A779G0_PD_A2E0D1, R8A779G0_PD_A3E0, PD_SCU }, + { "a1e0d0c0", R8A779G0_PD_A1E0D0C0, R8A779G0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d0c1", R8A779G0_PD_A1E0D0C1, R8A779G0_PD_A2E0D0, PD_CPU_NOCR }, + { "a1e0d1c0", R8A779G0_PD_A1E0D1C0, R8A779G0_PD_A2E0D1, PD_CPU_NOCR }, + { "a1e0d1c1", R8A779G0_PD_A1E0D1C1, R8A779G0_PD_A2E0D1, PD_CPU_NOCR }, + { "a33dga", R8A779G0_PD_A33DGA, R8A779G0_PD_ALWAYS_ON }, + { "a23dgb", R8A779G0_PD_A23DGB, R8A779G0_PD_A33DGA }, + { "a3vip0", R8A779G0_PD_A3VIP0, R8A779G0_PD_ALWAYS_ON }, + { "a3vip1", R8A779G0_PD_A3VIP1, R8A779G0_PD_ALWAYS_ON }, + { "a3vip2", R8A779G0_PD_A3VIP2, R8A779G0_PD_ALWAYS_ON }, + { "a3dul", R8A779G0_PD_A3DUL, R8A779G0_PD_ALWAYS_ON }, + { "a3isp0", R8A779G0_PD_A3ISP0, R8A779G0_PD_ALWAYS_ON }, + { "a3isp1", R8A779G0_PD_A3ISP1, R8A779G0_PD_ALWAYS_ON }, + { "a3ir", R8A779G0_PD_A3IR, R8A779G0_PD_ALWAYS_ON }, + { "a2cn0", R8A779G0_PD_A2CN0, R8A779G0_PD_A3IR }, + { "a1cnn0", R8A779G0_PD_A1CNN0, R8A779G0_PD_A2CN0 }, + { "a1dsp0", R8A779G0_PD_A1DSP0, R8A779G0_PD_A2CN0 }, + { "a1dsp1", R8A779G0_PD_A1DSP1, R8A779G0_PD_A2CN0 }, + { "a1dsp2", R8A779G0_PD_A1DSP2, R8A779G0_PD_A2CN0 }, + { "a1dsp3", R8A779G0_PD_A1DSP3, R8A779G0_PD_A2CN0 }, + { "a2imp01", R8A779G0_PD_A2IMP01, R8A779G0_PD_A3IR }, + { "a2imp23", R8A779G0_PD_A2IMP23, R8A779G0_PD_A3IR }, + { "a2psc", R8A779G0_PD_A2PSC, R8A779G0_PD_A3IR }, + { "a2dma", R8A779G0_PD_A2DMA, R8A779G0_PD_A3IR }, + { "a2cv0", R8A779G0_PD_A2CV0, R8A779G0_PD_A3IR }, + { "a2cv1", R8A779G0_PD_A2CV1, R8A779G0_PD_A3IR }, + { "a2cv2", R8A779G0_PD_A2CV2, R8A779G0_PD_A3IR }, + { "a2cv3", R8A779G0_PD_A2CV3, R8A779G0_PD_A3IR }, +}; + +const struct rcar_gen4_sysc_info r8a779g0_sysc_info __initconst = { + .areas = r8a779g0_areas, + .num_areas = ARRAY_SIZE(r8a779g0_areas), +}; diff --git a/drivers/pmdomain/renesas/rcar-gen4-sysc.c b/drivers/pmdomain/renesas/rcar-gen4-sysc.c new file mode 100644 index 000000000000..9e5e6e077abc --- /dev/null +++ b/drivers/pmdomain/renesas/rcar-gen4-sysc.c @@ -0,0 +1,379 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car Gen4 SYSC Power management support + * + * Copyright (C) 2021 Renesas Electronics Corp. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rcar-gen4-sysc.h" + +/* SYSC Common */ +#define SYSCSR 0x000 /* SYSC Status Register */ +#define SYSCPONSR(x) (0x800 + ((x) * 0x4)) /* Power-ON Status Register 0 */ +#define SYSCPOFFSR(x) (0x808 + ((x) * 0x4)) /* Power-OFF Status Register */ +#define SYSCISCR(x) (0x810 + ((x) * 0x4)) /* Interrupt Status/Clear Register */ +#define SYSCIER(x) (0x820 + ((x) * 0x4)) /* Interrupt Enable Register */ +#define SYSCIMR(x) (0x830 + ((x) * 0x4)) /* Interrupt Mask Register */ + +/* Power Domain Registers */ +#define PDRSR(n) (0x1000 + ((n) * 0x40)) +#define PDRONCR(n) (0x1004 + ((n) * 0x40)) +#define PDROFFCR(n) (0x1008 + ((n) * 0x40)) +#define PDRESR(n) (0x100C + ((n) * 0x40)) + +/* PWRON/PWROFF */ +#define PWRON_PWROFF BIT(0) /* Power-ON/OFF request */ + +/* PDRESR */ +#define PDRESR_ERR BIT(0) + +/* PDRSR */ +#define PDRSR_OFF BIT(0) /* Power-OFF state */ +#define PDRSR_ON BIT(4) /* Power-ON state */ +#define PDRSR_OFF_STATE BIT(8) /* Processing Power-OFF sequence */ +#define PDRSR_ON_STATE BIT(12) /* Processing Power-ON sequence */ + +#define SYSCSR_BUSY GENMASK(1, 0) /* All bit sets is not busy */ + +#define SYSCSR_TIMEOUT 10000 +#define SYSCSR_DELAY_US 10 + +#define PDRESR_RETRIES 1000 +#define PDRESR_DELAY_US 10 + +#define SYSCISR_TIMEOUT 10000 +#define SYSCISR_DELAY_US 10 + +#define RCAR_GEN4_PD_ALWAYS_ON 64 +#define NUM_DOMAINS_EACH_REG BITS_PER_TYPE(u32) + +static void __iomem *rcar_gen4_sysc_base; +static DEFINE_SPINLOCK(rcar_gen4_sysc_lock); /* SMP CPUs + I/O devices */ + +static int rcar_gen4_sysc_pwr_on_off(u8 pdr, bool on) +{ + unsigned int reg_offs; + u32 val; + int ret; + + if (on) + reg_offs = PDRONCR(pdr); + else + reg_offs = PDROFFCR(pdr); + + /* Wait until SYSC is ready to accept a power request */ + ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCSR, val, + (val & SYSCSR_BUSY) == SYSCSR_BUSY, + SYSCSR_DELAY_US, SYSCSR_TIMEOUT); + if (ret < 0) + return -EAGAIN; + + /* Submit power shutoff or power resume request */ + iowrite32(PWRON_PWROFF, rcar_gen4_sysc_base + reg_offs); + + return 0; +} + +static int clear_irq_flags(unsigned int reg_idx, unsigned int isr_mask) +{ + u32 val; + int ret; + + iowrite32(isr_mask, rcar_gen4_sysc_base + SYSCISCR(reg_idx)); + + ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), + val, !(val & isr_mask), + SYSCISR_DELAY_US, SYSCISR_TIMEOUT); + if (ret < 0) { + pr_err("\n %s : Can not clear IRQ flags in SYSCISCR", __func__); + return -EIO; + } + + return 0; +} + +static int rcar_gen4_sysc_power(u8 pdr, bool on) +{ + unsigned int isr_mask; + unsigned int reg_idx, bit_idx; + unsigned int status; + unsigned long flags; + int ret = 0; + u32 val; + int k; + + spin_lock_irqsave(&rcar_gen4_sysc_lock, flags); + + reg_idx = pdr / NUM_DOMAINS_EACH_REG; + bit_idx = pdr % NUM_DOMAINS_EACH_REG; + + isr_mask = BIT(bit_idx); + + /* + * The interrupt source needs to be enabled, but masked, to prevent the + * CPU from receiving it. + */ + iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIER(reg_idx)) | isr_mask, + rcar_gen4_sysc_base + SYSCIER(reg_idx)); + iowrite32(ioread32(rcar_gen4_sysc_base + SYSCIMR(reg_idx)) | isr_mask, + rcar_gen4_sysc_base + SYSCIMR(reg_idx)); + + ret = clear_irq_flags(reg_idx, isr_mask); + if (ret) + goto out; + + /* Submit power shutoff or resume request until it was accepted */ + for (k = 0; k < PDRESR_RETRIES; k++) { + ret = rcar_gen4_sysc_pwr_on_off(pdr, on); + if (ret) + goto out; + + status = ioread32(rcar_gen4_sysc_base + PDRESR(pdr)); + if (!(status & PDRESR_ERR)) + break; + + udelay(PDRESR_DELAY_US); + } + + if (k == PDRESR_RETRIES) { + ret = -EIO; + goto out; + } + + /* Wait until the power shutoff or resume request has completed * */ + ret = readl_poll_timeout_atomic(rcar_gen4_sysc_base + SYSCISCR(reg_idx), + val, (val & isr_mask), + SYSCISR_DELAY_US, SYSCISR_TIMEOUT); + if (ret < 0) { + ret = -EIO; + goto out; + } + + /* Clear interrupt flags */ + ret = clear_irq_flags(reg_idx, isr_mask); + if (ret) + goto out; + + out: + spin_unlock_irqrestore(&rcar_gen4_sysc_lock, flags); + + pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", + pdr, ioread32(rcar_gen4_sysc_base + SYSCISCR(reg_idx)), ret); + return ret; +} + +static bool rcar_gen4_sysc_power_is_off(u8 pdr) +{ + unsigned int st; + + st = ioread32(rcar_gen4_sysc_base + PDRSR(pdr)); + + if (st & PDRSR_OFF) + return true; + + return false; +} + +struct rcar_gen4_sysc_pd { + struct generic_pm_domain genpd; + u8 pdr; + unsigned int flags; + char name[]; +}; + +static inline struct rcar_gen4_sysc_pd *to_rcar_gen4_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct rcar_gen4_sysc_pd, genpd); +} + +static int rcar_gen4_sysc_pd_power_off(struct generic_pm_domain *genpd) +{ + struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + return rcar_gen4_sysc_power(pd->pdr, false); +} + +static int rcar_gen4_sysc_pd_power_on(struct generic_pm_domain *genpd) +{ + struct rcar_gen4_sysc_pd *pd = to_rcar_gen4_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + return rcar_gen4_sysc_power(pd->pdr, true); +} + +static int __init rcar_gen4_sysc_pd_setup(struct rcar_gen4_sysc_pd *pd) +{ + struct generic_pm_domain *genpd = &pd->genpd; + const char *name = pd->genpd.name; + int error; + + if (pd->flags & PD_CPU) { + /* + * This domain contains a CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "CPU"); + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } else if (pd->flags & PD_SCU) { + /* + * This domain contains an SCU and cache-controller, and + * therefore it should only be turned off if the CPU cores are + * not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "SCU"); + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } else if (pd->flags & PD_NO_CR) { + /* + * This domain cannot be turned off. + */ + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } + + if (!(pd->flags & (PD_CPU | PD_SCU))) { + /* Enable Clock Domain for I/O devices */ + genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; + genpd->attach_dev = cpg_mssr_attach_dev; + genpd->detach_dev = cpg_mssr_detach_dev; + } + + genpd->power_off = rcar_gen4_sysc_pd_power_off; + genpd->power_on = rcar_gen4_sysc_pd_power_on; + + if (pd->flags & (PD_CPU | PD_NO_CR)) { + /* Skip CPUs (handled by SMP code) and areas without control */ + pr_debug("%s: Not touching %s\n", __func__, genpd->name); + goto finalize; + } + + if (!rcar_gen4_sysc_power_is_off(pd->pdr)) { + pr_debug("%s: %s is already powered\n", __func__, genpd->name); + goto finalize; + } + + rcar_gen4_sysc_power(pd->pdr, true); + +finalize: + error = pm_genpd_init(genpd, &simple_qos_governor, false); + if (error) + pr_err("Failed to init PM domain %s: %d\n", name, error); + + return error; +} + +static const struct of_device_id rcar_gen4_sysc_matches[] __initconst = { +#ifdef CONFIG_SYSC_R8A779A0 + { .compatible = "renesas,r8a779a0-sysc", .data = &r8a779a0_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A779F0 + { .compatible = "renesas,r8a779f0-sysc", .data = &r8a779f0_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A779G0 + { .compatible = "renesas,r8a779g0-sysc", .data = &r8a779g0_sysc_info }, +#endif + { /* sentinel */ } +}; + +struct rcar_gen4_pm_domains { + struct genpd_onecell_data onecell_data; + struct generic_pm_domain *domains[RCAR_GEN4_PD_ALWAYS_ON + 1]; +}; + +static struct genpd_onecell_data *rcar_gen4_sysc_onecell_data; + +static int __init rcar_gen4_sysc_pd_init(void) +{ + const struct rcar_gen4_sysc_info *info; + const struct of_device_id *match; + struct rcar_gen4_pm_domains *domains; + struct device_node *np; + void __iomem *base; + unsigned int i; + int error; + + np = of_find_matching_node_and_match(NULL, rcar_gen4_sysc_matches, &match); + if (!np) + return -ENODEV; + + info = match->data; + + base = of_iomap(np, 0); + if (!base) { + pr_warn("%pOF: Cannot map regs\n", np); + error = -ENOMEM; + goto out_put; + } + + rcar_gen4_sysc_base = base; + + domains = kzalloc(sizeof(*domains), GFP_KERNEL); + if (!domains) { + error = -ENOMEM; + goto out_put; + } + + domains->onecell_data.domains = domains->domains; + domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); + rcar_gen4_sysc_onecell_data = &domains->onecell_data; + + for (i = 0; i < info->num_areas; i++) { + const struct rcar_gen4_sysc_area *area = &info->areas[i]; + struct rcar_gen4_sysc_pd *pd; + size_t n; + + if (!area->name) { + /* Skip NULLified area */ + continue; + } + + n = strlen(area->name) + 1; + pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); + if (!pd) { + error = -ENOMEM; + goto out_put; + } + + memcpy(pd->name, area->name, n); + pd->genpd.name = pd->name; + pd->pdr = area->pdr; + pd->flags = area->flags; + + error = rcar_gen4_sysc_pd_setup(pd); + if (error) + goto out_put; + + domains->domains[area->pdr] = &pd->genpd; + + if (area->parent < 0) + continue; + + error = pm_genpd_add_subdomain(domains->domains[area->parent], + &pd->genpd); + if (error) { + pr_warn("Failed to add PM subdomain %s to parent %u\n", + area->name, area->parent); + goto out_put; + } + } + + error = of_genpd_add_provider_onecell(np, &domains->onecell_data); + +out_put: + of_node_put(np); + return error; +} +early_initcall(rcar_gen4_sysc_pd_init); diff --git a/drivers/pmdomain/renesas/rcar-gen4-sysc.h b/drivers/pmdomain/renesas/rcar-gen4-sysc.h new file mode 100644 index 000000000000..388cfa8f8f9f --- /dev/null +++ b/drivers/pmdomain/renesas/rcar-gen4-sysc.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * R-Car Gen4 System Controller + * + * Copyright (C) 2021 Renesas Electronics Corp. + */ +#ifndef __SOC_RENESAS_RCAR_GEN4_SYSC_H__ +#define __SOC_RENESAS_RCAR_GEN4_SYSC_H__ + +#include + +/* + * Power Domain flags + */ +#define PD_CPU BIT(0) /* Area contains main CPU core */ +#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ +#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ + +#define PD_CPU_NOCR (PD_CPU | PD_NO_CR) /* CPU area lacks CR */ +#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ + +/* + * Description of a Power Area + */ +struct rcar_gen4_sysc_area { + const char *name; + u8 pdr; /* PDRn */ + s8 parent; /* -1 if none */ + u8 flags; /* See PD_* */ +}; + +/* + * SoC-specific Power Area Description + */ +struct rcar_gen4_sysc_info { + const struct rcar_gen4_sysc_area *areas; + unsigned int num_areas; +}; + +extern const struct rcar_gen4_sysc_info r8a779a0_sysc_info; +extern const struct rcar_gen4_sysc_info r8a779f0_sysc_info; +extern const struct rcar_gen4_sysc_info r8a779g0_sysc_info; + +#endif /* __SOC_RENESAS_RCAR_GEN4_SYSC_H__ */ diff --git a/drivers/pmdomain/renesas/rcar-sysc.c b/drivers/pmdomain/renesas/rcar-sysc.c new file mode 100644 index 000000000000..eed47696e825 --- /dev/null +++ b/drivers/pmdomain/renesas/rcar-sysc.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * R-Car SYSC Power management support + * + * Copyright (C) 2014 Magnus Damm + * Copyright (C) 2015-2017 Glider bvba + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rcar-sysc.h" + +/* SYSC Common */ +#define SYSCSR 0x00 /* SYSC Status Register */ +#define SYSCISR 0x04 /* Interrupt Status Register */ +#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ +#define SYSCIER 0x0c /* Interrupt Enable Register */ +#define SYSCIMR 0x10 /* Interrupt Mask Register */ + +/* SYSC Status Register */ +#define SYSCSR_PONENB 1 /* Ready for power resume requests */ +#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ + +/* + * Power Control Register Offsets inside the register block for each domain + * Note: The "CR" registers for ARM cores exist on H1 only + * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 + * Use PSCI on R-Car Gen3 + */ +#define PWRSR_OFFS 0x00 /* Power Status Register */ +#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ +#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ +#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ +#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ +#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ + + +#define SYSCSR_TIMEOUT 100 +#define SYSCSR_DELAY_US 1 + +#define PWRER_RETRIES 100 +#define PWRER_DELAY_US 1 + +#define SYSCISR_TIMEOUT 1000 +#define SYSCISR_DELAY_US 1 + +#define RCAR_PD_ALWAYS_ON 32 /* Always-on power area */ + +struct rcar_sysc_ch { + u16 chan_offs; + u8 chan_bit; + u8 isr_bit; +}; + +static void __iomem *rcar_sysc_base; +static DEFINE_SPINLOCK(rcar_sysc_lock); /* SMP CPUs + I/O devices */ +static u32 rcar_sysc_extmask_offs, rcar_sysc_extmask_val; + +static int rcar_sysc_pwr_on_off(const struct rcar_sysc_ch *sysc_ch, bool on) +{ + unsigned int sr_bit, reg_offs; + u32 val; + int ret; + + if (on) { + sr_bit = SYSCSR_PONENB; + reg_offs = PWRONCR_OFFS; + } else { + sr_bit = SYSCSR_POFFENB; + reg_offs = PWROFFCR_OFFS; + } + + /* Wait until SYSC is ready to accept a power request */ + ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCSR, val, + val & BIT(sr_bit), SYSCSR_DELAY_US, + SYSCSR_TIMEOUT); + if (ret) + return -EAGAIN; + + /* Submit power shutoff or power resume request */ + iowrite32(BIT(sysc_ch->chan_bit), + rcar_sysc_base + sysc_ch->chan_offs + reg_offs); + + return 0; +} + +static int rcar_sysc_power(const struct rcar_sysc_ch *sysc_ch, bool on) +{ + unsigned int isr_mask = BIT(sysc_ch->isr_bit); + unsigned int chan_mask = BIT(sysc_ch->chan_bit); + unsigned int status, k; + unsigned long flags; + int ret; + + spin_lock_irqsave(&rcar_sysc_lock, flags); + + /* + * Mask external power requests for CPU or 3DG domains + */ + if (rcar_sysc_extmask_val) { + iowrite32(rcar_sysc_extmask_val, + rcar_sysc_base + rcar_sysc_extmask_offs); + } + + /* + * The interrupt source needs to be enabled, but masked, to prevent the + * CPU from receiving it. + */ + iowrite32(ioread32(rcar_sysc_base + SYSCIMR) | isr_mask, + rcar_sysc_base + SYSCIMR); + iowrite32(ioread32(rcar_sysc_base + SYSCIER) | isr_mask, + rcar_sysc_base + SYSCIER); + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + /* Submit power shutoff or resume request until it was accepted */ + for (k = 0; k < PWRER_RETRIES; k++) { + ret = rcar_sysc_pwr_on_off(sysc_ch, on); + if (ret) + goto out; + + status = ioread32(rcar_sysc_base + + sysc_ch->chan_offs + PWRER_OFFS); + if (!(status & chan_mask)) + break; + + udelay(PWRER_DELAY_US); + } + + if (k == PWRER_RETRIES) { + ret = -EIO; + goto out; + } + + /* Wait until the power shutoff or resume request has completed * */ + ret = readl_poll_timeout_atomic(rcar_sysc_base + SYSCISR, status, + status & isr_mask, SYSCISR_DELAY_US, + SYSCISR_TIMEOUT); + if (ret) + ret = -EIO; + + iowrite32(isr_mask, rcar_sysc_base + SYSCISCR); + + out: + if (rcar_sysc_extmask_val) + iowrite32(0, rcar_sysc_base + rcar_sysc_extmask_offs); + + spin_unlock_irqrestore(&rcar_sysc_lock, flags); + + pr_debug("sysc power %s domain %d: %08x -> %d\n", on ? "on" : "off", + sysc_ch->isr_bit, ioread32(rcar_sysc_base + SYSCISR), ret); + return ret; +} + +static bool rcar_sysc_power_is_off(const struct rcar_sysc_ch *sysc_ch) +{ + unsigned int st; + + st = ioread32(rcar_sysc_base + sysc_ch->chan_offs + PWRSR_OFFS); + if (st & BIT(sysc_ch->chan_bit)) + return true; + + return false; +} + +struct rcar_sysc_pd { + struct generic_pm_domain genpd; + struct rcar_sysc_ch ch; + unsigned int flags; + char name[]; +}; + +static inline struct rcar_sysc_pd *to_rcar_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct rcar_sysc_pd, genpd); +} + +static int rcar_sysc_pd_power_off(struct generic_pm_domain *genpd) +{ + struct rcar_sysc_pd *pd = to_rcar_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + return rcar_sysc_power(&pd->ch, false); +} + +static int rcar_sysc_pd_power_on(struct generic_pm_domain *genpd) +{ + struct rcar_sysc_pd *pd = to_rcar_pd(genpd); + + pr_debug("%s: %s\n", __func__, genpd->name); + return rcar_sysc_power(&pd->ch, true); +} + +static bool has_cpg_mstp; + +static int __init rcar_sysc_pd_setup(struct rcar_sysc_pd *pd) +{ + struct generic_pm_domain *genpd = &pd->genpd; + const char *name = pd->genpd.name; + int error; + + if (pd->flags & PD_CPU) { + /* + * This domain contains a CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "CPU"); + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } else if (pd->flags & PD_SCU) { + /* + * This domain contains an SCU and cache-controller, and + * therefore it should only be turned off if the CPU cores are + * not in use. + */ + pr_debug("PM domain %s contains %s\n", name, "SCU"); + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } else if (pd->flags & PD_NO_CR) { + /* + * This domain cannot be turned off. + */ + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + } + + if (!(pd->flags & (PD_CPU | PD_SCU))) { + /* Enable Clock Domain for I/O devices */ + genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; + if (has_cpg_mstp) { + genpd->attach_dev = cpg_mstp_attach_dev; + genpd->detach_dev = cpg_mstp_detach_dev; + } else { + genpd->attach_dev = cpg_mssr_attach_dev; + genpd->detach_dev = cpg_mssr_detach_dev; + } + } + + genpd->power_off = rcar_sysc_pd_power_off; + genpd->power_on = rcar_sysc_pd_power_on; + + if (pd->flags & (PD_CPU | PD_NO_CR)) { + /* Skip CPUs (handled by SMP code) and areas without control */ + pr_debug("%s: Not touching %s\n", __func__, genpd->name); + goto finalize; + } + + if (!rcar_sysc_power_is_off(&pd->ch)) { + pr_debug("%s: %s is already powered\n", __func__, genpd->name); + goto finalize; + } + + rcar_sysc_power(&pd->ch, true); + +finalize: + error = pm_genpd_init(genpd, &simple_qos_governor, false); + if (error) + pr_err("Failed to init PM domain %s: %d\n", name, error); + + return error; +} + +static const struct of_device_id rcar_sysc_matches[] __initconst = { +#ifdef CONFIG_SYSC_R8A7742 + { .compatible = "renesas,r8a7742-sysc", .data = &r8a7742_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7743 + { .compatible = "renesas,r8a7743-sysc", .data = &r8a7743_sysc_info }, + /* RZ/G1N is identical to RZ/G2M w.r.t. power domains. */ + { .compatible = "renesas,r8a7744-sysc", .data = &r8a7743_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7745 + { .compatible = "renesas,r8a7745-sysc", .data = &r8a7745_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77470 + { .compatible = "renesas,r8a77470-sysc", .data = &r8a77470_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A774A1 + { .compatible = "renesas,r8a774a1-sysc", .data = &r8a774a1_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A774B1 + { .compatible = "renesas,r8a774b1-sysc", .data = &r8a774b1_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A774C0 + { .compatible = "renesas,r8a774c0-sysc", .data = &r8a774c0_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A774E1 + { .compatible = "renesas,r8a774e1-sysc", .data = &r8a774e1_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7779 + { .compatible = "renesas,r8a7779-sysc", .data = &r8a7779_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7790 + { .compatible = "renesas,r8a7790-sysc", .data = &r8a7790_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7791 + { .compatible = "renesas,r8a7791-sysc", .data = &r8a7791_sysc_info }, + /* R-Car M2-N is identical to R-Car M2-W w.r.t. power domains. */ + { .compatible = "renesas,r8a7793-sysc", .data = &r8a7791_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7792 + { .compatible = "renesas,r8a7792-sysc", .data = &r8a7792_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7794 + { .compatible = "renesas,r8a7794-sysc", .data = &r8a7794_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A7795 + { .compatible = "renesas,r8a7795-sysc", .data = &r8a7795_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77960 + { .compatible = "renesas,r8a7796-sysc", .data = &r8a77960_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77961 + { .compatible = "renesas,r8a77961-sysc", .data = &r8a77961_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77965 + { .compatible = "renesas,r8a77965-sysc", .data = &r8a77965_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77970 + { .compatible = "renesas,r8a77970-sysc", .data = &r8a77970_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77980 + { .compatible = "renesas,r8a77980-sysc", .data = &r8a77980_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77990 + { .compatible = "renesas,r8a77990-sysc", .data = &r8a77990_sysc_info }, +#endif +#ifdef CONFIG_SYSC_R8A77995 + { .compatible = "renesas,r8a77995-sysc", .data = &r8a77995_sysc_info }, +#endif + { /* sentinel */ } +}; + +struct rcar_pm_domains { + struct genpd_onecell_data onecell_data; + struct generic_pm_domain *domains[RCAR_PD_ALWAYS_ON + 1]; +}; + +static struct genpd_onecell_data *rcar_sysc_onecell_data; + +static int __init rcar_sysc_pd_init(void) +{ + const struct rcar_sysc_info *info; + const struct of_device_id *match; + struct rcar_pm_domains *domains; + struct device_node *np; + void __iomem *base; + unsigned int i; + int error; + + np = of_find_matching_node_and_match(NULL, rcar_sysc_matches, &match); + if (!np) + return -ENODEV; + + info = match->data; + + if (info->init) { + error = info->init(); + if (error) + goto out_put; + } + + has_cpg_mstp = of_find_compatible_node(NULL, NULL, + "renesas,cpg-mstp-clocks"); + + base = of_iomap(np, 0); + if (!base) { + pr_warn("%pOF: Cannot map regs\n", np); + error = -ENOMEM; + goto out_put; + } + + rcar_sysc_base = base; + + /* Optional External Request Mask Register */ + rcar_sysc_extmask_offs = info->extmask_offs; + rcar_sysc_extmask_val = info->extmask_val; + + domains = kzalloc(sizeof(*domains), GFP_KERNEL); + if (!domains) { + error = -ENOMEM; + goto out_put; + } + + domains->onecell_data.domains = domains->domains; + domains->onecell_data.num_domains = ARRAY_SIZE(domains->domains); + rcar_sysc_onecell_data = &domains->onecell_data; + + for (i = 0; i < info->num_areas; i++) { + const struct rcar_sysc_area *area = &info->areas[i]; + struct rcar_sysc_pd *pd; + size_t n; + + if (!area->name) { + /* Skip NULLified area */ + continue; + } + + n = strlen(area->name) + 1; + pd = kzalloc(sizeof(*pd) + n, GFP_KERNEL); + if (!pd) { + error = -ENOMEM; + goto out_put; + } + + memcpy(pd->name, area->name, n); + pd->genpd.name = pd->name; + pd->ch.chan_offs = area->chan_offs; + pd->ch.chan_bit = area->chan_bit; + pd->ch.isr_bit = area->isr_bit; + pd->flags = area->flags; + + error = rcar_sysc_pd_setup(pd); + if (error) + goto out_put; + + domains->domains[area->isr_bit] = &pd->genpd; + + if (area->parent < 0) + continue; + + error = pm_genpd_add_subdomain(domains->domains[area->parent], + &pd->genpd); + if (error) { + pr_warn("Failed to add PM subdomain %s to parent %u\n", + area->name, area->parent); + goto out_put; + } + } + + error = of_genpd_add_provider_onecell(np, &domains->onecell_data); + if (!error) + fwnode_dev_initialized(of_fwnode_handle(np), true); + +out_put: + of_node_put(np); + return error; +} +early_initcall(rcar_sysc_pd_init); + +void __init rcar_sysc_nullify(struct rcar_sysc_area *areas, + unsigned int num_areas, u8 id) +{ + unsigned int i; + + for (i = 0; i < num_areas; i++) + if (areas[i].isr_bit == id) { + areas[i].name = NULL; + return; + } +} + +#ifdef CONFIG_ARCH_R8A7779 +static int rcar_sysc_power_cpu(unsigned int idx, bool on) +{ + struct generic_pm_domain *genpd; + struct rcar_sysc_pd *pd; + unsigned int i; + + if (!rcar_sysc_onecell_data) + return -ENODEV; + + for (i = 0; i < rcar_sysc_onecell_data->num_domains; i++) { + genpd = rcar_sysc_onecell_data->domains[i]; + if (!genpd) + continue; + + pd = to_rcar_pd(genpd); + if (!(pd->flags & PD_CPU) || pd->ch.chan_bit != idx) + continue; + + return rcar_sysc_power(&pd->ch, on); + } + + return -ENOENT; +} + +int rcar_sysc_power_down_cpu(unsigned int cpu) +{ + return rcar_sysc_power_cpu(cpu, false); +} + +int rcar_sysc_power_up_cpu(unsigned int cpu) +{ + return rcar_sysc_power_cpu(cpu, true); +} +#endif /* CONFIG_ARCH_R8A7779 */ diff --git a/drivers/pmdomain/renesas/rcar-sysc.h b/drivers/pmdomain/renesas/rcar-sysc.h new file mode 100644 index 000000000000..266c599a0a9b --- /dev/null +++ b/drivers/pmdomain/renesas/rcar-sysc.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Renesas R-Car System Controller + * + * Copyright (C) 2016 Glider bvba + */ +#ifndef __SOC_RENESAS_RCAR_SYSC_H__ +#define __SOC_RENESAS_RCAR_SYSC_H__ + +#include + + +/* + * Power Domain flags + */ +#define PD_CPU BIT(0) /* Area contains main CPU core */ +#define PD_SCU BIT(1) /* Area contains SCU and L2 cache */ +#define PD_NO_CR BIT(2) /* Area lacks PWR{ON,OFF}CR registers */ + +#define PD_CPU_CR PD_CPU /* CPU area has CR (R-Car H1) */ +#define PD_CPU_NOCR PD_CPU | PD_NO_CR /* CPU area lacks CR (R-Car Gen2/3) */ +#define PD_ALWAYS_ON PD_NO_CR /* Always-on area */ + + +/* + * Description of a Power Area + */ + +struct rcar_sysc_area { + const char *name; + u16 chan_offs; /* Offset of PWRSR register for this area */ + u8 chan_bit; /* Bit in PWR* (except for PWRUP in PWRSR) */ + u8 isr_bit; /* Bit in SYSCI*R */ + s8 parent; /* -1 if none */ + u8 flags; /* See PD_* */ +}; + + +/* + * SoC-specific Power Area Description + */ + +struct rcar_sysc_info { + int (*init)(void); /* Optional */ + const struct rcar_sysc_area *areas; + unsigned int num_areas; + /* Optional External Request Mask Register */ + u32 extmask_offs; /* SYSCEXTMASK register offset */ + u32 extmask_val; /* SYSCEXTMASK register mask value */ +}; + +extern const struct rcar_sysc_info r8a7742_sysc_info; +extern const struct rcar_sysc_info r8a7743_sysc_info; +extern const struct rcar_sysc_info r8a7745_sysc_info; +extern const struct rcar_sysc_info r8a77470_sysc_info; +extern const struct rcar_sysc_info r8a774a1_sysc_info; +extern const struct rcar_sysc_info r8a774b1_sysc_info; +extern const struct rcar_sysc_info r8a774c0_sysc_info; +extern const struct rcar_sysc_info r8a774e1_sysc_info; +extern const struct rcar_sysc_info r8a7779_sysc_info; +extern const struct rcar_sysc_info r8a7790_sysc_info; +extern const struct rcar_sysc_info r8a7791_sysc_info; +extern const struct rcar_sysc_info r8a7792_sysc_info; +extern const struct rcar_sysc_info r8a7794_sysc_info; +extern struct rcar_sysc_info r8a7795_sysc_info; +extern const struct rcar_sysc_info r8a77960_sysc_info; +extern const struct rcar_sysc_info r8a77961_sysc_info; +extern const struct rcar_sysc_info r8a77965_sysc_info; +extern const struct rcar_sysc_info r8a77970_sysc_info; +extern const struct rcar_sysc_info r8a77980_sysc_info; +extern const struct rcar_sysc_info r8a77990_sysc_info; +extern const struct rcar_sysc_info r8a77995_sysc_info; + + + /* + * Helpers for fixing up power area tables depending on SoC revision + */ + +extern void rcar_sysc_nullify(struct rcar_sysc_area *areas, + unsigned int num_areas, u8 id); + +#endif /* __SOC_RENESAS_RCAR_SYSC_H__ */ diff --git a/drivers/pmdomain/renesas/rmobile-sysc.c b/drivers/pmdomain/renesas/rmobile-sysc.c new file mode 100644 index 000000000000..912daadaa10d --- /dev/null +++ b/drivers/pmdomain/renesas/rmobile-sysc.c @@ -0,0 +1,343 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * rmobile power management support + * + * Copyright (C) 2012 Renesas Solutions Corp. + * Copyright (C) 2012 Kuninori Morimoto + * Copyright (C) 2014 Glider bvba + * + * based on pm-sh7372.c + * Copyright (C) 2011 Magnus Damm + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* SYSC */ +#define SPDCR 0x08 /* SYS Power Down Control Register */ +#define SWUCR 0x14 /* SYS Wakeup Control Register */ +#define PSTR 0x80 /* Power Status Register */ + +#define PSTR_RETRIES 100 +#define PSTR_DELAY_US 10 + +struct rmobile_pm_domain { + struct generic_pm_domain genpd; + struct dev_power_governor *gov; + int (*suspend)(void); + void __iomem *base; + unsigned int bit_shift; +}; + +static inline +struct rmobile_pm_domain *to_rmobile_pd(struct generic_pm_domain *d) +{ + return container_of(d, struct rmobile_pm_domain, genpd); +} + +static int rmobile_pd_power_down(struct generic_pm_domain *genpd) +{ + struct rmobile_pm_domain *rmobile_pd = to_rmobile_pd(genpd); + unsigned int mask = BIT(rmobile_pd->bit_shift); + u32 val; + + if (rmobile_pd->suspend) { + int ret = rmobile_pd->suspend(); + + if (ret) + return ret; + } + + if (readl(rmobile_pd->base + PSTR) & mask) { + writel(mask, rmobile_pd->base + SPDCR); + + readl_poll_timeout_atomic(rmobile_pd->base + SPDCR, val, + !(val & mask), 0, PSTR_RETRIES); + } + + pr_debug("%s: Power off, 0x%08x -> PSTR = 0x%08x\n", genpd->name, mask, + readl(rmobile_pd->base + PSTR)); + + return 0; +} + +static int __rmobile_pd_power_up(struct rmobile_pm_domain *rmobile_pd) +{ + unsigned int val, mask = BIT(rmobile_pd->bit_shift); + int ret = 0; + + if (readl(rmobile_pd->base + PSTR) & mask) + return ret; + + writel(mask, rmobile_pd->base + SWUCR); + + ret = readl_poll_timeout_atomic(rmobile_pd->base + SWUCR, val, + (val & mask), PSTR_DELAY_US, + PSTR_RETRIES * PSTR_DELAY_US); + + pr_debug("%s: Power on, 0x%08x -> PSTR = 0x%08x\n", + rmobile_pd->genpd.name, mask, + readl(rmobile_pd->base + PSTR)); + + return ret; +} + +static int rmobile_pd_power_up(struct generic_pm_domain *genpd) +{ + return __rmobile_pd_power_up(to_rmobile_pd(genpd)); +} + +static void rmobile_init_pm_domain(struct rmobile_pm_domain *rmobile_pd) +{ + struct generic_pm_domain *genpd = &rmobile_pd->genpd; + struct dev_power_governor *gov = rmobile_pd->gov; + + genpd->flags |= GENPD_FLAG_PM_CLK | GENPD_FLAG_ACTIVE_WAKEUP; + genpd->attach_dev = cpg_mstp_attach_dev; + genpd->detach_dev = cpg_mstp_detach_dev; + + if (!(genpd->flags & GENPD_FLAG_ALWAYS_ON)) { + genpd->power_off = rmobile_pd_power_down; + genpd->power_on = rmobile_pd_power_up; + __rmobile_pd_power_up(rmobile_pd); + } + + pm_genpd_init(genpd, gov ? : &simple_qos_governor, false); +} + +static int rmobile_pd_suspend_console(void) +{ + /* + * Serial consoles make use of SCIF hardware located in this domain, + * hence keep the power domain on if "no_console_suspend" is set. + */ + return console_suspend_enabled ? 0 : -EBUSY; +} + +enum pd_types { + PD_NORMAL, + PD_CPU, + PD_CONSOLE, + PD_DEBUG, + PD_MEMCTL, +}; + +#define MAX_NUM_SPECIAL_PDS 16 + +static struct special_pd { + struct device_node *pd; + enum pd_types type; +} special_pds[MAX_NUM_SPECIAL_PDS] __initdata; + +static unsigned int num_special_pds __initdata; + +static const struct of_device_id special_ids[] __initconst = { + { .compatible = "arm,coresight-etm3x", .data = (void *)PD_DEBUG }, + { .compatible = "renesas,dbsc-r8a73a4", .data = (void *)PD_MEMCTL, }, + { .compatible = "renesas,dbsc3-r8a7740", .data = (void *)PD_MEMCTL, }, + { .compatible = "renesas,sbsc-sh73a0", .data = (void *)PD_MEMCTL, }, + { /* sentinel */ }, +}; + +static void __init add_special_pd(struct device_node *np, enum pd_types type) +{ + unsigned int i; + struct device_node *pd; + + pd = of_parse_phandle(np, "power-domains", 0); + if (!pd) + return; + + for (i = 0; i < num_special_pds; i++) + if (pd == special_pds[i].pd && type == special_pds[i].type) { + of_node_put(pd); + return; + } + + if (num_special_pds == ARRAY_SIZE(special_pds)) { + pr_warn("Too many special PM domains\n"); + of_node_put(pd); + return; + } + + pr_debug("Special PM domain %pOFn type %d for %pOF\n", pd, type, np); + + special_pds[num_special_pds].pd = pd; + special_pds[num_special_pds].type = type; + num_special_pds++; +} + +static void __init get_special_pds(void) +{ + struct device_node *np; + const struct of_device_id *id; + + /* PM domains containing CPUs */ + for_each_of_cpu_node(np) + add_special_pd(np, PD_CPU); + + /* PM domain containing console */ + if (of_stdout) + add_special_pd(of_stdout, PD_CONSOLE); + + /* PM domains containing other special devices */ + for_each_matching_node_and_match(np, special_ids, &id) + add_special_pd(np, (enum pd_types)id->data); +} + +static void __init put_special_pds(void) +{ + unsigned int i; + + for (i = 0; i < num_special_pds; i++) + of_node_put(special_pds[i].pd); +} + +static enum pd_types __init pd_type(const struct device_node *pd) +{ + unsigned int i; + + for (i = 0; i < num_special_pds; i++) + if (pd == special_pds[i].pd) + return special_pds[i].type; + + return PD_NORMAL; +} + +static void __init rmobile_setup_pm_domain(struct device_node *np, + struct rmobile_pm_domain *pd) +{ + const char *name = pd->genpd.name; + + switch (pd_type(np)) { + case PD_CPU: + /* + * This domain contains the CPU core and therefore it should + * only be turned off if the CPU is not in use. + */ + pr_debug("PM domain %s contains CPU\n", name); + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + break; + + case PD_CONSOLE: + pr_debug("PM domain %s contains serial console\n", name); + pd->gov = &pm_domain_always_on_gov; + pd->suspend = rmobile_pd_suspend_console; + break; + + case PD_DEBUG: + /* + * This domain contains the Coresight-ETM hardware block and + * therefore it should only be turned off if the debug module + * is not in use. + */ + pr_debug("PM domain %s contains Coresight-ETM\n", name); + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + break; + + case PD_MEMCTL: + /* + * This domain contains a memory-controller and therefore it + * should only be turned off if memory is not in use. + */ + pr_debug("PM domain %s contains MEMCTL\n", name); + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + break; + + case PD_NORMAL: + if (pd->bit_shift == ~0) { + /* Top-level always-on domain */ + pr_debug("PM domain %s is always-on domain\n", name); + pd->genpd.flags |= GENPD_FLAG_ALWAYS_ON; + } + break; + } + + rmobile_init_pm_domain(pd); +} + +static int __init rmobile_add_pm_domains(void __iomem *base, + struct device_node *parent, + struct generic_pm_domain *genpd_parent) +{ + struct device_node *np; + + for_each_child_of_node(parent, np) { + struct rmobile_pm_domain *pd; + u32 idx = ~0; + + if (of_property_read_u32(np, "reg", &idx)) { + /* always-on domain */ + } + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) { + of_node_put(np); + return -ENOMEM; + } + + pd->genpd.name = np->name; + pd->base = base; + pd->bit_shift = idx; + + rmobile_setup_pm_domain(np, pd); + if (genpd_parent) + pm_genpd_add_subdomain(genpd_parent, &pd->genpd); + of_genpd_add_provider_simple(np, &pd->genpd); + + rmobile_add_pm_domains(base, np, &pd->genpd); + } + return 0; +} + +static int __init rmobile_init_pm_domains(void) +{ + struct device_node *np, *pmd; + bool scanned = false; + void __iomem *base; + int ret = 0; + + for_each_compatible_node(np, NULL, "renesas,sysc-rmobile") { + base = of_iomap(np, 0); + if (!base) { + pr_warn("%pOF cannot map reg 0\n", np); + continue; + } + + pmd = of_get_child_by_name(np, "pm-domains"); + if (!pmd) { + iounmap(base); + pr_warn("%pOF lacks pm-domains node\n", np); + continue; + } + + if (!scanned) { + /* Find PM domains containing special blocks */ + get_special_pds(); + scanned = true; + } + + ret = rmobile_add_pm_domains(base, pmd, NULL); + of_node_put(pmd); + if (ret) { + of_node_put(np); + break; + } + + fwnode_dev_initialized(of_fwnode_handle(np), true); + } + + put_special_pds(); + + return ret; +} + +core_initcall(rmobile_init_pm_domains); diff --git a/drivers/pmdomain/rockchip/Makefile b/drivers/pmdomain/rockchip/Makefile new file mode 100644 index 000000000000..8fb9d88a3492 --- /dev/null +++ b/drivers/pmdomain/rockchip/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm-domains.o diff --git a/drivers/pmdomain/rockchip/pm-domains.c b/drivers/pmdomain/rockchip/pm-domains.c new file mode 100644 index 000000000000..d5d3ecb38283 --- /dev/null +++ b/drivers/pmdomain/rockchip/pm-domains.c @@ -0,0 +1,1396 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Rockchip Generic power domain support. + * + * Copyright (c) 2015 ROCKCHIP, Co. Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct rockchip_domain_info { + const char *name; + int pwr_mask; + int status_mask; + int req_mask; + int idle_mask; + int ack_mask; + bool active_wakeup; + int pwr_w_mask; + int req_w_mask; + int mem_status_mask; + int repair_status_mask; + u32 pwr_offset; + u32 mem_offset; + u32 req_offset; +}; + +struct rockchip_pmu_info { + u32 pwr_offset; + u32 status_offset; + u32 req_offset; + u32 idle_offset; + u32 ack_offset; + u32 mem_pwr_offset; + u32 chain_status_offset; + u32 mem_status_offset; + u32 repair_status_offset; + + u32 core_pwrcnt_offset; + u32 gpu_pwrcnt_offset; + + unsigned int core_power_transition_time; + unsigned int gpu_power_transition_time; + + int num_domains; + const struct rockchip_domain_info *domain_info; +}; + +#define MAX_QOS_REGS_NUM 5 +#define QOS_PRIORITY 0x08 +#define QOS_MODE 0x0c +#define QOS_BANDWIDTH 0x10 +#define QOS_SATURATION 0x14 +#define QOS_EXTCONTROL 0x18 + +struct rockchip_pm_domain { + struct generic_pm_domain genpd; + const struct rockchip_domain_info *info; + struct rockchip_pmu *pmu; + int num_qos; + struct regmap **qos_regmap; + u32 *qos_save_regs[MAX_QOS_REGS_NUM]; + int num_clks; + struct clk_bulk_data *clks; +}; + +struct rockchip_pmu { + struct device *dev; + struct regmap *regmap; + const struct rockchip_pmu_info *info; + struct mutex mutex; /* mutex lock for pmu */ + struct genpd_onecell_data genpd_data; + struct generic_pm_domain *domains[]; +}; + +#define to_rockchip_pd(gpd) container_of(gpd, struct rockchip_pm_domain, genpd) + +#define DOMAIN(_name, pwr, status, req, idle, ack, wakeup) \ +{ \ + .name = _name, \ + .pwr_mask = (pwr), \ + .status_mask = (status), \ + .req_mask = (req), \ + .idle_mask = (idle), \ + .ack_mask = (ack), \ + .active_wakeup = (wakeup), \ +} + +#define DOMAIN_M(_name, pwr, status, req, idle, ack, wakeup) \ +{ \ + .name = _name, \ + .pwr_w_mask = (pwr) << 16, \ + .pwr_mask = (pwr), \ + .status_mask = (status), \ + .req_w_mask = (req) << 16, \ + .req_mask = (req), \ + .idle_mask = (idle), \ + .ack_mask = (ack), \ + .active_wakeup = wakeup, \ +} + +#define DOMAIN_M_O_R(_name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, ack, wakeup) \ +{ \ + .name = _name, \ + .pwr_offset = p_offset, \ + .pwr_w_mask = (pwr) << 16, \ + .pwr_mask = (pwr), \ + .status_mask = (status), \ + .mem_offset = m_offset, \ + .mem_status_mask = (m_status), \ + .repair_status_mask = (r_status), \ + .req_offset = r_offset, \ + .req_w_mask = (req) << 16, \ + .req_mask = (req), \ + .idle_mask = (idle), \ + .ack_mask = (ack), \ + .active_wakeup = wakeup, \ +} + +#define DOMAIN_RK3036(_name, req, ack, idle, wakeup) \ +{ \ + .name = _name, \ + .req_mask = (req), \ + .req_w_mask = (req) << 16, \ + .ack_mask = (ack), \ + .idle_mask = (idle), \ + .active_wakeup = wakeup, \ +} + +#define DOMAIN_PX30(name, pwr, status, req, wakeup) \ + DOMAIN_M(name, pwr, status, req, (req) << 16, req, wakeup) + +#define DOMAIN_RV1126(name, pwr, req, idle, wakeup) \ + DOMAIN_M(name, pwr, pwr, req, idle, idle, wakeup) + +#define DOMAIN_RK3288(name, pwr, status, req, wakeup) \ + DOMAIN(name, pwr, status, req, req, (req) << 16, wakeup) + +#define DOMAIN_RK3328(name, pwr, status, req, wakeup) \ + DOMAIN_M(name, pwr, pwr, req, (req) << 10, req, wakeup) + +#define DOMAIN_RK3368(name, pwr, status, req, wakeup) \ + DOMAIN(name, pwr, status, req, (req) << 16, req, wakeup) + +#define DOMAIN_RK3399(name, pwr, status, req, wakeup) \ + DOMAIN(name, pwr, status, req, req, req, wakeup) + +#define DOMAIN_RK3568(name, pwr, req, wakeup) \ + DOMAIN_M(name, pwr, pwr, req, req, req, wakeup) + +/* + * Dynamic Memory Controller may need to coordinate with us -- see + * rockchip_pmu_block(). + * + * dmc_pmu_mutex protects registration-time races, so DMC driver doesn't try to + * block() while we're initializing the PMU. + */ +static DEFINE_MUTEX(dmc_pmu_mutex); +static struct rockchip_pmu *dmc_pmu; + +/* + * Block PMU transitions and make sure they don't interfere with ARM Trusted + * Firmware operations. There are two conflicts, noted in the comments below. + * + * Caller must unblock PMU transitions via rockchip_pmu_unblock(). + */ +int rockchip_pmu_block(void) +{ + struct rockchip_pmu *pmu; + struct generic_pm_domain *genpd; + struct rockchip_pm_domain *pd; + int i, ret; + + mutex_lock(&dmc_pmu_mutex); + + /* No PMU (yet)? Then we just block rockchip_pmu_probe(). */ + if (!dmc_pmu) + return 0; + pmu = dmc_pmu; + + /* + * mutex blocks all idle transitions: we can't touch the + * PMU_BUS_IDLE_REQ (our ".idle_offset") register while ARM Trusted + * Firmware might be using it. + */ + mutex_lock(&pmu->mutex); + + /* + * Power domain clocks: Per Rockchip, we *must* keep certain clocks + * enabled for the duration of power-domain transitions. Most + * transitions are handled by this driver, but some cases (in + * particular, DRAM DVFS / memory-controller idle) must be handled by + * firmware. Firmware can handle most clock management via a special + * "ungate" register (PMU_CRU_GATEDIS_CON0), but unfortunately, this + * doesn't handle PLLs. We can assist this transition by doing the + * clock management on behalf of firmware. + */ + for (i = 0; i < pmu->genpd_data.num_domains; i++) { + genpd = pmu->genpd_data.domains[i]; + if (genpd) { + pd = to_rockchip_pd(genpd); + ret = clk_bulk_enable(pd->num_clks, pd->clks); + if (ret < 0) { + dev_err(pmu->dev, + "failed to enable clks for domain '%s': %d\n", + genpd->name, ret); + goto err; + } + } + } + + return 0; + +err: + for (i = i - 1; i >= 0; i--) { + genpd = pmu->genpd_data.domains[i]; + if (genpd) { + pd = to_rockchip_pd(genpd); + clk_bulk_disable(pd->num_clks, pd->clks); + } + } + mutex_unlock(&pmu->mutex); + mutex_unlock(&dmc_pmu_mutex); + + return ret; +} +EXPORT_SYMBOL_GPL(rockchip_pmu_block); + +/* Unblock PMU transitions. */ +void rockchip_pmu_unblock(void) +{ + struct rockchip_pmu *pmu; + struct generic_pm_domain *genpd; + struct rockchip_pm_domain *pd; + int i; + + if (dmc_pmu) { + pmu = dmc_pmu; + for (i = 0; i < pmu->genpd_data.num_domains; i++) { + genpd = pmu->genpd_data.domains[i]; + if (genpd) { + pd = to_rockchip_pd(genpd); + clk_bulk_disable(pd->num_clks, pd->clks); + } + } + + mutex_unlock(&pmu->mutex); + } + + mutex_unlock(&dmc_pmu_mutex); +} +EXPORT_SYMBOL_GPL(rockchip_pmu_unblock); + +#define DOMAIN_RK3588(name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, wakeup) \ + DOMAIN_M_O_R(name, p_offset, pwr, status, m_offset, m_status, r_status, r_offset, req, idle, idle, wakeup) + +static bool rockchip_pmu_domain_is_idle(struct rockchip_pm_domain *pd) +{ + struct rockchip_pmu *pmu = pd->pmu; + const struct rockchip_domain_info *pd_info = pd->info; + unsigned int val; + + regmap_read(pmu->regmap, pmu->info->idle_offset, &val); + return (val & pd_info->idle_mask) == pd_info->idle_mask; +} + +static unsigned int rockchip_pmu_read_ack(struct rockchip_pmu *pmu) +{ + unsigned int val; + + regmap_read(pmu->regmap, pmu->info->ack_offset, &val); + return val; +} + +static int rockchip_pmu_set_idle_request(struct rockchip_pm_domain *pd, + bool idle) +{ + const struct rockchip_domain_info *pd_info = pd->info; + struct generic_pm_domain *genpd = &pd->genpd; + struct rockchip_pmu *pmu = pd->pmu; + u32 pd_req_offset = pd_info->req_offset; + unsigned int target_ack; + unsigned int val; + bool is_idle; + int ret; + + if (pd_info->req_mask == 0) + return 0; + else if (pd_info->req_w_mask) + regmap_write(pmu->regmap, pmu->info->req_offset + pd_req_offset, + idle ? (pd_info->req_mask | pd_info->req_w_mask) : + pd_info->req_w_mask); + else + regmap_update_bits(pmu->regmap, pmu->info->req_offset + pd_req_offset, + pd_info->req_mask, idle ? -1U : 0); + + wmb(); + + /* Wait util idle_ack = 1 */ + target_ack = idle ? pd_info->ack_mask : 0; + ret = readx_poll_timeout_atomic(rockchip_pmu_read_ack, pmu, val, + (val & pd_info->ack_mask) == target_ack, + 0, 10000); + if (ret) { + dev_err(pmu->dev, + "failed to get ack on domain '%s', val=0x%x\n", + genpd->name, val); + return ret; + } + + ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_idle, pd, + is_idle, is_idle == idle, 0, 10000); + if (ret) { + dev_err(pmu->dev, + "failed to set idle on domain '%s', val=%d\n", + genpd->name, is_idle); + return ret; + } + + return 0; +} + +static int rockchip_pmu_save_qos(struct rockchip_pm_domain *pd) +{ + int i; + + for (i = 0; i < pd->num_qos; i++) { + regmap_read(pd->qos_regmap[i], + QOS_PRIORITY, + &pd->qos_save_regs[0][i]); + regmap_read(pd->qos_regmap[i], + QOS_MODE, + &pd->qos_save_regs[1][i]); + regmap_read(pd->qos_regmap[i], + QOS_BANDWIDTH, + &pd->qos_save_regs[2][i]); + regmap_read(pd->qos_regmap[i], + QOS_SATURATION, + &pd->qos_save_regs[3][i]); + regmap_read(pd->qos_regmap[i], + QOS_EXTCONTROL, + &pd->qos_save_regs[4][i]); + } + return 0; +} + +static int rockchip_pmu_restore_qos(struct rockchip_pm_domain *pd) +{ + int i; + + for (i = 0; i < pd->num_qos; i++) { + regmap_write(pd->qos_regmap[i], + QOS_PRIORITY, + pd->qos_save_regs[0][i]); + regmap_write(pd->qos_regmap[i], + QOS_MODE, + pd->qos_save_regs[1][i]); + regmap_write(pd->qos_regmap[i], + QOS_BANDWIDTH, + pd->qos_save_regs[2][i]); + regmap_write(pd->qos_regmap[i], + QOS_SATURATION, + pd->qos_save_regs[3][i]); + regmap_write(pd->qos_regmap[i], + QOS_EXTCONTROL, + pd->qos_save_regs[4][i]); + } + + return 0; +} + +static bool rockchip_pmu_domain_is_on(struct rockchip_pm_domain *pd) +{ + struct rockchip_pmu *pmu = pd->pmu; + unsigned int val; + + if (pd->info->repair_status_mask) { + regmap_read(pmu->regmap, pmu->info->repair_status_offset, &val); + /* 1'b1: power on, 1'b0: power off */ + return val & pd->info->repair_status_mask; + } + + /* check idle status for idle-only domains */ + if (pd->info->status_mask == 0) + return !rockchip_pmu_domain_is_idle(pd); + + regmap_read(pmu->regmap, pmu->info->status_offset, &val); + + /* 1'b0: power on, 1'b1: power off */ + return !(val & pd->info->status_mask); +} + +static bool rockchip_pmu_domain_is_mem_on(struct rockchip_pm_domain *pd) +{ + struct rockchip_pmu *pmu = pd->pmu; + unsigned int val; + + regmap_read(pmu->regmap, + pmu->info->mem_status_offset + pd->info->mem_offset, &val); + + /* 1'b0: power on, 1'b1: power off */ + return !(val & pd->info->mem_status_mask); +} + +static bool rockchip_pmu_domain_is_chain_on(struct rockchip_pm_domain *pd) +{ + struct rockchip_pmu *pmu = pd->pmu; + unsigned int val; + + regmap_read(pmu->regmap, + pmu->info->chain_status_offset + pd->info->mem_offset, &val); + + /* 1'b1: power on, 1'b0: power off */ + return val & pd->info->mem_status_mask; +} + +static int rockchip_pmu_domain_mem_reset(struct rockchip_pm_domain *pd) +{ + struct rockchip_pmu *pmu = pd->pmu; + struct generic_pm_domain *genpd = &pd->genpd; + bool is_on; + int ret = 0; + + ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_chain_on, pd, is_on, + is_on == true, 0, 10000); + if (ret) { + dev_err(pmu->dev, + "failed to get chain status '%s', target_on=1, val=%d\n", + genpd->name, is_on); + goto error; + } + + udelay(20); + + regmap_write(pmu->regmap, pmu->info->mem_pwr_offset + pd->info->pwr_offset, + (pd->info->pwr_mask | pd->info->pwr_w_mask)); + wmb(); + + ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_mem_on, pd, is_on, + is_on == false, 0, 10000); + if (ret) { + dev_err(pmu->dev, + "failed to get mem status '%s', target_on=0, val=%d\n", + genpd->name, is_on); + goto error; + } + + regmap_write(pmu->regmap, pmu->info->mem_pwr_offset + pd->info->pwr_offset, + pd->info->pwr_w_mask); + wmb(); + + ret = readx_poll_timeout_atomic(rockchip_pmu_domain_is_mem_on, pd, is_on, + is_on == true, 0, 10000); + if (ret) { + dev_err(pmu->dev, + "failed to get mem status '%s', target_on=1, val=%d\n", + genpd->name, is_on); + } + +error: + return ret; +} + +static void rockchip_do_pmu_set_power_domain(struct rockchip_pm_domain *pd, + bool on) +{ + struct rockchip_pmu *pmu = pd->pmu; + struct generic_pm_domain *genpd = &pd->genpd; + u32 pd_pwr_offset = pd->info->pwr_offset; + bool is_on, is_mem_on = false; + + if (pd->info->pwr_mask == 0) + return; + + if (on && pd->info->mem_status_mask) + is_mem_on = rockchip_pmu_domain_is_mem_on(pd); + + if (pd->info->pwr_w_mask) + regmap_write(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset, + on ? pd->info->pwr_w_mask : + (pd->info->pwr_mask | pd->info->pwr_w_mask)); + else + regmap_update_bits(pmu->regmap, pmu->info->pwr_offset + pd_pwr_offset, + pd->info->pwr_mask, on ? 0 : -1U); + + wmb(); + + if (is_mem_on && rockchip_pmu_domain_mem_reset(pd)) + return; + + if (readx_poll_timeout_atomic(rockchip_pmu_domain_is_on, pd, is_on, + is_on == on, 0, 10000)) { + dev_err(pmu->dev, + "failed to set domain '%s', val=%d\n", + genpd->name, is_on); + return; + } +} + +static int rockchip_pd_power(struct rockchip_pm_domain *pd, bool power_on) +{ + struct rockchip_pmu *pmu = pd->pmu; + int ret; + + mutex_lock(&pmu->mutex); + + if (rockchip_pmu_domain_is_on(pd) != power_on) { + ret = clk_bulk_enable(pd->num_clks, pd->clks); + if (ret < 0) { + dev_err(pmu->dev, "failed to enable clocks\n"); + mutex_unlock(&pmu->mutex); + return ret; + } + + if (!power_on) { + rockchip_pmu_save_qos(pd); + + /* if powering down, idle request to NIU first */ + rockchip_pmu_set_idle_request(pd, true); + } + + rockchip_do_pmu_set_power_domain(pd, power_on); + + if (power_on) { + /* if powering up, leave idle mode */ + rockchip_pmu_set_idle_request(pd, false); + + rockchip_pmu_restore_qos(pd); + } + + clk_bulk_disable(pd->num_clks, pd->clks); + } + + mutex_unlock(&pmu->mutex); + return 0; +} + +static int rockchip_pd_power_on(struct generic_pm_domain *domain) +{ + struct rockchip_pm_domain *pd = to_rockchip_pd(domain); + + return rockchip_pd_power(pd, true); +} + +static int rockchip_pd_power_off(struct generic_pm_domain *domain) +{ + struct rockchip_pm_domain *pd = to_rockchip_pd(domain); + + return rockchip_pd_power(pd, false); +} + +static int rockchip_pd_attach_dev(struct generic_pm_domain *genpd, + struct device *dev) +{ + struct clk *clk; + int i; + int error; + + dev_dbg(dev, "attaching to power domain '%s'\n", genpd->name); + + error = pm_clk_create(dev); + if (error) { + dev_err(dev, "pm_clk_create failed %d\n", error); + return error; + } + + i = 0; + while ((clk = of_clk_get(dev->of_node, i++)) && !IS_ERR(clk)) { + dev_dbg(dev, "adding clock '%pC' to list of PM clocks\n", clk); + error = pm_clk_add_clk(dev, clk); + if (error) { + dev_err(dev, "pm_clk_add_clk failed %d\n", error); + clk_put(clk); + pm_clk_destroy(dev); + return error; + } + } + + return 0; +} + +static void rockchip_pd_detach_dev(struct generic_pm_domain *genpd, + struct device *dev) +{ + dev_dbg(dev, "detaching from power domain '%s'\n", genpd->name); + + pm_clk_destroy(dev); +} + +static int rockchip_pm_add_one_domain(struct rockchip_pmu *pmu, + struct device_node *node) +{ + const struct rockchip_domain_info *pd_info; + struct rockchip_pm_domain *pd; + struct device_node *qos_node; + int i, j; + u32 id; + int error; + + error = of_property_read_u32(node, "reg", &id); + if (error) { + dev_err(pmu->dev, + "%pOFn: failed to retrieve domain id (reg): %d\n", + node, error); + return -EINVAL; + } + + if (id >= pmu->info->num_domains) { + dev_err(pmu->dev, "%pOFn: invalid domain id %d\n", + node, id); + return -EINVAL; + } + /* RK3588 has domains with two parents (RKVDEC0/RKVDEC1) */ + if (pmu->genpd_data.domains[id]) + return 0; + + pd_info = &pmu->info->domain_info[id]; + if (!pd_info) { + dev_err(pmu->dev, "%pOFn: undefined domain id %d\n", + node, id); + return -EINVAL; + } + + pd = devm_kzalloc(pmu->dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->info = pd_info; + pd->pmu = pmu; + + pd->num_clks = of_clk_get_parent_count(node); + if (pd->num_clks > 0) { + pd->clks = devm_kcalloc(pmu->dev, pd->num_clks, + sizeof(*pd->clks), GFP_KERNEL); + if (!pd->clks) + return -ENOMEM; + } else { + dev_dbg(pmu->dev, "%pOFn: doesn't have clocks: %d\n", + node, pd->num_clks); + pd->num_clks = 0; + } + + for (i = 0; i < pd->num_clks; i++) { + pd->clks[i].clk = of_clk_get(node, i); + if (IS_ERR(pd->clks[i].clk)) { + error = PTR_ERR(pd->clks[i].clk); + dev_err(pmu->dev, + "%pOFn: failed to get clk at index %d: %d\n", + node, i, error); + return error; + } + } + + error = clk_bulk_prepare(pd->num_clks, pd->clks); + if (error) + goto err_put_clocks; + + pd->num_qos = of_count_phandle_with_args(node, "pm_qos", + NULL); + + if (pd->num_qos > 0) { + pd->qos_regmap = devm_kcalloc(pmu->dev, pd->num_qos, + sizeof(*pd->qos_regmap), + GFP_KERNEL); + if (!pd->qos_regmap) { + error = -ENOMEM; + goto err_unprepare_clocks; + } + + for (j = 0; j < MAX_QOS_REGS_NUM; j++) { + pd->qos_save_regs[j] = devm_kcalloc(pmu->dev, + pd->num_qos, + sizeof(u32), + GFP_KERNEL); + if (!pd->qos_save_regs[j]) { + error = -ENOMEM; + goto err_unprepare_clocks; + } + } + + for (j = 0; j < pd->num_qos; j++) { + qos_node = of_parse_phandle(node, "pm_qos", j); + if (!qos_node) { + error = -ENODEV; + goto err_unprepare_clocks; + } + pd->qos_regmap[j] = syscon_node_to_regmap(qos_node); + if (IS_ERR(pd->qos_regmap[j])) { + error = -ENODEV; + of_node_put(qos_node); + goto err_unprepare_clocks; + } + of_node_put(qos_node); + } + } + + if (pd->info->name) + pd->genpd.name = pd->info->name; + else + pd->genpd.name = kbasename(node->full_name); + pd->genpd.power_off = rockchip_pd_power_off; + pd->genpd.power_on = rockchip_pd_power_on; + pd->genpd.attach_dev = rockchip_pd_attach_dev; + pd->genpd.detach_dev = rockchip_pd_detach_dev; + pd->genpd.flags = GENPD_FLAG_PM_CLK; + if (pd_info->active_wakeup) + pd->genpd.flags |= GENPD_FLAG_ACTIVE_WAKEUP; + pm_genpd_init(&pd->genpd, NULL, + !rockchip_pmu_domain_is_on(pd) || + (pd->info->mem_status_mask && !rockchip_pmu_domain_is_mem_on(pd))); + + pmu->genpd_data.domains[id] = &pd->genpd; + return 0; + +err_unprepare_clocks: + clk_bulk_unprepare(pd->num_clks, pd->clks); +err_put_clocks: + clk_bulk_put(pd->num_clks, pd->clks); + return error; +} + +static void rockchip_pm_remove_one_domain(struct rockchip_pm_domain *pd) +{ + int ret; + + /* + * We're in the error cleanup already, so we only complain, + * but won't emit another error on top of the original one. + */ + ret = pm_genpd_remove(&pd->genpd); + if (ret < 0) + dev_err(pd->pmu->dev, "failed to remove domain '%s' : %d - state may be inconsistent\n", + pd->genpd.name, ret); + + clk_bulk_unprepare(pd->num_clks, pd->clks); + clk_bulk_put(pd->num_clks, pd->clks); + + /* protect the zeroing of pm->num_clks */ + mutex_lock(&pd->pmu->mutex); + pd->num_clks = 0; + mutex_unlock(&pd->pmu->mutex); + + /* devm will free our memory */ +} + +static void rockchip_pm_domain_cleanup(struct rockchip_pmu *pmu) +{ + struct generic_pm_domain *genpd; + struct rockchip_pm_domain *pd; + int i; + + for (i = 0; i < pmu->genpd_data.num_domains; i++) { + genpd = pmu->genpd_data.domains[i]; + if (genpd) { + pd = to_rockchip_pd(genpd); + rockchip_pm_remove_one_domain(pd); + } + } + + /* devm will free our memory */ +} + +static void rockchip_configure_pd_cnt(struct rockchip_pmu *pmu, + u32 domain_reg_offset, + unsigned int count) +{ + /* First configure domain power down transition count ... */ + regmap_write(pmu->regmap, domain_reg_offset, count); + /* ... and then power up count. */ + regmap_write(pmu->regmap, domain_reg_offset + 4, count); +} + +static int rockchip_pm_add_subdomain(struct rockchip_pmu *pmu, + struct device_node *parent) +{ + struct device_node *np; + struct generic_pm_domain *child_domain, *parent_domain; + int error; + + for_each_child_of_node(parent, np) { + u32 idx; + + error = of_property_read_u32(parent, "reg", &idx); + if (error) { + dev_err(pmu->dev, + "%pOFn: failed to retrieve domain id (reg): %d\n", + parent, error); + goto err_out; + } + parent_domain = pmu->genpd_data.domains[idx]; + + error = rockchip_pm_add_one_domain(pmu, np); + if (error) { + dev_err(pmu->dev, "failed to handle node %pOFn: %d\n", + np, error); + goto err_out; + } + + error = of_property_read_u32(np, "reg", &idx); + if (error) { + dev_err(pmu->dev, + "%pOFn: failed to retrieve domain id (reg): %d\n", + np, error); + goto err_out; + } + child_domain = pmu->genpd_data.domains[idx]; + + error = pm_genpd_add_subdomain(parent_domain, child_domain); + if (error) { + dev_err(pmu->dev, "%s failed to add subdomain %s: %d\n", + parent_domain->name, child_domain->name, error); + goto err_out; + } else { + dev_dbg(pmu->dev, "%s add subdomain: %s\n", + parent_domain->name, child_domain->name); + } + + rockchip_pm_add_subdomain(pmu, np); + } + + return 0; + +err_out: + of_node_put(np); + return error; +} + +static int rockchip_pm_domain_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *node; + struct device *parent; + struct rockchip_pmu *pmu; + const struct of_device_id *match; + const struct rockchip_pmu_info *pmu_info; + int error; + + if (!np) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + match = of_match_device(dev->driver->of_match_table, dev); + if (!match || !match->data) { + dev_err(dev, "missing pmu data\n"); + return -EINVAL; + } + + pmu_info = match->data; + + pmu = devm_kzalloc(dev, + struct_size(pmu, domains, pmu_info->num_domains), + GFP_KERNEL); + if (!pmu) + return -ENOMEM; + + pmu->dev = &pdev->dev; + mutex_init(&pmu->mutex); + + pmu->info = pmu_info; + + pmu->genpd_data.domains = pmu->domains; + pmu->genpd_data.num_domains = pmu_info->num_domains; + + parent = dev->parent; + if (!parent) { + dev_err(dev, "no parent for syscon devices\n"); + return -ENODEV; + } + + pmu->regmap = syscon_node_to_regmap(parent->of_node); + if (IS_ERR(pmu->regmap)) { + dev_err(dev, "no regmap available\n"); + return PTR_ERR(pmu->regmap); + } + + /* + * Configure power up and down transition delays for CORE + * and GPU domains. + */ + if (pmu_info->core_power_transition_time) + rockchip_configure_pd_cnt(pmu, pmu_info->core_pwrcnt_offset, + pmu_info->core_power_transition_time); + if (pmu_info->gpu_pwrcnt_offset) + rockchip_configure_pd_cnt(pmu, pmu_info->gpu_pwrcnt_offset, + pmu_info->gpu_power_transition_time); + + error = -ENODEV; + + /* + * Prevent any rockchip_pmu_block() from racing with the remainder of + * setup (clocks, register initialization). + */ + mutex_lock(&dmc_pmu_mutex); + + for_each_available_child_of_node(np, node) { + error = rockchip_pm_add_one_domain(pmu, node); + if (error) { + dev_err(dev, "failed to handle node %pOFn: %d\n", + node, error); + of_node_put(node); + goto err_out; + } + + error = rockchip_pm_add_subdomain(pmu, node); + if (error < 0) { + dev_err(dev, "failed to handle subdomain node %pOFn: %d\n", + node, error); + of_node_put(node); + goto err_out; + } + } + + if (error) { + dev_dbg(dev, "no power domains defined\n"); + goto err_out; + } + + error = of_genpd_add_provider_onecell(np, &pmu->genpd_data); + if (error) { + dev_err(dev, "failed to add provider: %d\n", error); + goto err_out; + } + + /* We only expect one PMU. */ + if (!WARN_ON_ONCE(dmc_pmu)) + dmc_pmu = pmu; + + mutex_unlock(&dmc_pmu_mutex); + + return 0; + +err_out: + rockchip_pm_domain_cleanup(pmu); + mutex_unlock(&dmc_pmu_mutex); + return error; +} + +static const struct rockchip_domain_info px30_pm_domains[] = { + [PX30_PD_USB] = DOMAIN_PX30("usb", BIT(5), BIT(5), BIT(10), false), + [PX30_PD_SDCARD] = DOMAIN_PX30("sdcard", BIT(8), BIT(8), BIT(9), false), + [PX30_PD_GMAC] = DOMAIN_PX30("gmac", BIT(10), BIT(10), BIT(6), false), + [PX30_PD_MMC_NAND] = DOMAIN_PX30("mmc_nand", BIT(11), BIT(11), BIT(5), false), + [PX30_PD_VPU] = DOMAIN_PX30("vpu", BIT(12), BIT(12), BIT(14), false), + [PX30_PD_VO] = DOMAIN_PX30("vo", BIT(13), BIT(13), BIT(7), false), + [PX30_PD_VI] = DOMAIN_PX30("vi", BIT(14), BIT(14), BIT(8), false), + [PX30_PD_GPU] = DOMAIN_PX30("gpu", BIT(15), BIT(15), BIT(2), false), +}; + +static const struct rockchip_domain_info rv1126_pm_domains[] = { + [RV1126_PD_VEPU] = DOMAIN_RV1126("vepu", BIT(2), BIT(9), BIT(9), false), + [RV1126_PD_VI] = DOMAIN_RV1126("vi", BIT(4), BIT(6), BIT(6), false), + [RV1126_PD_VO] = DOMAIN_RV1126("vo", BIT(5), BIT(7), BIT(7), false), + [RV1126_PD_ISPP] = DOMAIN_RV1126("ispp", BIT(1), BIT(8), BIT(8), false), + [RV1126_PD_VDPU] = DOMAIN_RV1126("vdpu", BIT(3), BIT(10), BIT(10), false), + [RV1126_PD_NVM] = DOMAIN_RV1126("nvm", BIT(7), BIT(11), BIT(11), false), + [RV1126_PD_SDIO] = DOMAIN_RV1126("sdio", BIT(8), BIT(13), BIT(13), false), + [RV1126_PD_USB] = DOMAIN_RV1126("usb", BIT(9), BIT(15), BIT(15), false), +}; + +static const struct rockchip_domain_info rk3036_pm_domains[] = { + [RK3036_PD_MSCH] = DOMAIN_RK3036("msch", BIT(14), BIT(23), BIT(30), true), + [RK3036_PD_CORE] = DOMAIN_RK3036("core", BIT(13), BIT(17), BIT(24), false), + [RK3036_PD_PERI] = DOMAIN_RK3036("peri", BIT(12), BIT(18), BIT(25), false), + [RK3036_PD_VIO] = DOMAIN_RK3036("vio", BIT(11), BIT(19), BIT(26), false), + [RK3036_PD_VPU] = DOMAIN_RK3036("vpu", BIT(10), BIT(20), BIT(27), false), + [RK3036_PD_GPU] = DOMAIN_RK3036("gpu", BIT(9), BIT(21), BIT(28), false), + [RK3036_PD_SYS] = DOMAIN_RK3036("sys", BIT(8), BIT(22), BIT(29), false), +}; + +static const struct rockchip_domain_info rk3066_pm_domains[] = { + [RK3066_PD_GPU] = DOMAIN("gpu", BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false), + [RK3066_PD_VIDEO] = DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false), + [RK3066_PD_VIO] = DOMAIN("vio", BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false), + [RK3066_PD_PERI] = DOMAIN("peri", BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false), + [RK3066_PD_CPU] = DOMAIN("cpu", 0, BIT(5), BIT(1), BIT(26), BIT(31), false), +}; + +static const struct rockchip_domain_info rk3128_pm_domains[] = { + [RK3128_PD_CORE] = DOMAIN_RK3288("core", BIT(0), BIT(0), BIT(4), false), + [RK3128_PD_MSCH] = DOMAIN_RK3288("msch", 0, 0, BIT(6), true), + [RK3128_PD_VIO] = DOMAIN_RK3288("vio", BIT(3), BIT(3), BIT(2), false), + [RK3128_PD_VIDEO] = DOMAIN_RK3288("video", BIT(2), BIT(2), BIT(1), false), + [RK3128_PD_GPU] = DOMAIN_RK3288("gpu", BIT(1), BIT(1), BIT(3), false), +}; + +static const struct rockchip_domain_info rk3188_pm_domains[] = { + [RK3188_PD_GPU] = DOMAIN("gpu", BIT(9), BIT(9), BIT(3), BIT(24), BIT(29), false), + [RK3188_PD_VIDEO] = DOMAIN("video", BIT(8), BIT(8), BIT(4), BIT(23), BIT(28), false), + [RK3188_PD_VIO] = DOMAIN("vio", BIT(7), BIT(7), BIT(5), BIT(22), BIT(27), false), + [RK3188_PD_PERI] = DOMAIN("peri", BIT(6), BIT(6), BIT(2), BIT(25), BIT(30), false), + [RK3188_PD_CPU] = DOMAIN("cpu", BIT(5), BIT(5), BIT(1), BIT(26), BIT(31), false), +}; + +static const struct rockchip_domain_info rk3228_pm_domains[] = { + [RK3228_PD_CORE] = DOMAIN_RK3036("core", BIT(0), BIT(0), BIT(16), true), + [RK3228_PD_MSCH] = DOMAIN_RK3036("msch", BIT(1), BIT(1), BIT(17), true), + [RK3228_PD_BUS] = DOMAIN_RK3036("bus", BIT(2), BIT(2), BIT(18), true), + [RK3228_PD_SYS] = DOMAIN_RK3036("sys", BIT(3), BIT(3), BIT(19), true), + [RK3228_PD_VIO] = DOMAIN_RK3036("vio", BIT(4), BIT(4), BIT(20), false), + [RK3228_PD_VOP] = DOMAIN_RK3036("vop", BIT(5), BIT(5), BIT(21), false), + [RK3228_PD_VPU] = DOMAIN_RK3036("vpu", BIT(6), BIT(6), BIT(22), false), + [RK3228_PD_RKVDEC] = DOMAIN_RK3036("vdec", BIT(7), BIT(7), BIT(23), false), + [RK3228_PD_GPU] = DOMAIN_RK3036("gpu", BIT(8), BIT(8), BIT(24), false), + [RK3228_PD_PERI] = DOMAIN_RK3036("peri", BIT(9), BIT(9), BIT(25), true), + [RK3228_PD_GMAC] = DOMAIN_RK3036("gmac", BIT(10), BIT(10), BIT(26), false), +}; + +static const struct rockchip_domain_info rk3288_pm_domains[] = { + [RK3288_PD_VIO] = DOMAIN_RK3288("vio", BIT(7), BIT(7), BIT(4), false), + [RK3288_PD_HEVC] = DOMAIN_RK3288("hevc", BIT(14), BIT(10), BIT(9), false), + [RK3288_PD_VIDEO] = DOMAIN_RK3288("video", BIT(8), BIT(8), BIT(3), false), + [RK3288_PD_GPU] = DOMAIN_RK3288("gpu", BIT(9), BIT(9), BIT(2), false), +}; + +static const struct rockchip_domain_info rk3328_pm_domains[] = { + [RK3328_PD_CORE] = DOMAIN_RK3328("core", 0, BIT(0), BIT(0), false), + [RK3328_PD_GPU] = DOMAIN_RK3328("gpu", 0, BIT(1), BIT(1), false), + [RK3328_PD_BUS] = DOMAIN_RK3328("bus", 0, BIT(2), BIT(2), true), + [RK3328_PD_MSCH] = DOMAIN_RK3328("msch", 0, BIT(3), BIT(3), true), + [RK3328_PD_PERI] = DOMAIN_RK3328("peri", 0, BIT(4), BIT(4), true), + [RK3328_PD_VIDEO] = DOMAIN_RK3328("video", 0, BIT(5), BIT(5), false), + [RK3328_PD_HEVC] = DOMAIN_RK3328("hevc", 0, BIT(6), BIT(6), false), + [RK3328_PD_VIO] = DOMAIN_RK3328("vio", 0, BIT(8), BIT(8), false), + [RK3328_PD_VPU] = DOMAIN_RK3328("vpu", 0, BIT(9), BIT(9), false), +}; + +static const struct rockchip_domain_info rk3366_pm_domains[] = { + [RK3366_PD_PERI] = DOMAIN_RK3368("peri", BIT(10), BIT(10), BIT(6), true), + [RK3366_PD_VIO] = DOMAIN_RK3368("vio", BIT(14), BIT(14), BIT(8), false), + [RK3366_PD_VIDEO] = DOMAIN_RK3368("video", BIT(13), BIT(13), BIT(7), false), + [RK3366_PD_RKVDEC] = DOMAIN_RK3368("vdec", BIT(11), BIT(11), BIT(7), false), + [RK3366_PD_WIFIBT] = DOMAIN_RK3368("wifibt", BIT(8), BIT(8), BIT(9), false), + [RK3366_PD_VPU] = DOMAIN_RK3368("vpu", BIT(12), BIT(12), BIT(7), false), + [RK3366_PD_GPU] = DOMAIN_RK3368("gpu", BIT(15), BIT(15), BIT(2), false), +}; + +static const struct rockchip_domain_info rk3368_pm_domains[] = { + [RK3368_PD_PERI] = DOMAIN_RK3368("peri", BIT(13), BIT(12), BIT(6), true), + [RK3368_PD_VIO] = DOMAIN_RK3368("vio", BIT(15), BIT(14), BIT(8), false), + [RK3368_PD_VIDEO] = DOMAIN_RK3368("video", BIT(14), BIT(13), BIT(7), false), + [RK3368_PD_GPU_0] = DOMAIN_RK3368("gpu_0", BIT(16), BIT(15), BIT(2), false), + [RK3368_PD_GPU_1] = DOMAIN_RK3368("gpu_1", BIT(17), BIT(16), BIT(2), false), +}; + +static const struct rockchip_domain_info rk3399_pm_domains[] = { + [RK3399_PD_TCPD0] = DOMAIN_RK3399("tcpd0", BIT(8), BIT(8), 0, false), + [RK3399_PD_TCPD1] = DOMAIN_RK3399("tcpd1", BIT(9), BIT(9), 0, false), + [RK3399_PD_CCI] = DOMAIN_RK3399("cci", BIT(10), BIT(10), 0, true), + [RK3399_PD_CCI0] = DOMAIN_RK3399("cci0", 0, 0, BIT(15), true), + [RK3399_PD_CCI1] = DOMAIN_RK3399("cci1", 0, 0, BIT(16), true), + [RK3399_PD_PERILP] = DOMAIN_RK3399("perilp", BIT(11), BIT(11), BIT(1), true), + [RK3399_PD_PERIHP] = DOMAIN_RK3399("perihp", BIT(12), BIT(12), BIT(2), true), + [RK3399_PD_CENTER] = DOMAIN_RK3399("center", BIT(13), BIT(13), BIT(14), true), + [RK3399_PD_VIO] = DOMAIN_RK3399("vio", BIT(14), BIT(14), BIT(17), false), + [RK3399_PD_GPU] = DOMAIN_RK3399("gpu", BIT(15), BIT(15), BIT(0), false), + [RK3399_PD_VCODEC] = DOMAIN_RK3399("vcodec", BIT(16), BIT(16), BIT(3), false), + [RK3399_PD_VDU] = DOMAIN_RK3399("vdu", BIT(17), BIT(17), BIT(4), false), + [RK3399_PD_RGA] = DOMAIN_RK3399("rga", BIT(18), BIT(18), BIT(5), false), + [RK3399_PD_IEP] = DOMAIN_RK3399("iep", BIT(19), BIT(19), BIT(6), false), + [RK3399_PD_VO] = DOMAIN_RK3399("vo", BIT(20), BIT(20), 0, false), + [RK3399_PD_VOPB] = DOMAIN_RK3399("vopb", 0, 0, BIT(7), false), + [RK3399_PD_VOPL] = DOMAIN_RK3399("vopl", 0, 0, BIT(8), false), + [RK3399_PD_ISP0] = DOMAIN_RK3399("isp0", BIT(22), BIT(22), BIT(9), false), + [RK3399_PD_ISP1] = DOMAIN_RK3399("isp1", BIT(23), BIT(23), BIT(10), false), + [RK3399_PD_HDCP] = DOMAIN_RK3399("hdcp", BIT(24), BIT(24), BIT(11), false), + [RK3399_PD_GMAC] = DOMAIN_RK3399("gmac", BIT(25), BIT(25), BIT(23), true), + [RK3399_PD_EMMC] = DOMAIN_RK3399("emmc", BIT(26), BIT(26), BIT(24), true), + [RK3399_PD_USB3] = DOMAIN_RK3399("usb3", BIT(27), BIT(27), BIT(12), true), + [RK3399_PD_EDP] = DOMAIN_RK3399("edp", BIT(28), BIT(28), BIT(22), false), + [RK3399_PD_GIC] = DOMAIN_RK3399("gic", BIT(29), BIT(29), BIT(27), true), + [RK3399_PD_SD] = DOMAIN_RK3399("sd", BIT(30), BIT(30), BIT(28), true), + [RK3399_PD_SDIOAUDIO] = DOMAIN_RK3399("sdioaudio", BIT(31), BIT(31), BIT(29), true), +}; + +static const struct rockchip_domain_info rk3568_pm_domains[] = { + [RK3568_PD_NPU] = DOMAIN_RK3568("npu", BIT(1), BIT(2), false), + [RK3568_PD_GPU] = DOMAIN_RK3568("gpu", BIT(0), BIT(1), false), + [RK3568_PD_VI] = DOMAIN_RK3568("vi", BIT(6), BIT(3), false), + [RK3568_PD_VO] = DOMAIN_RK3568("vo", BIT(7), BIT(4), false), + [RK3568_PD_RGA] = DOMAIN_RK3568("rga", BIT(5), BIT(5), false), + [RK3568_PD_VPU] = DOMAIN_RK3568("vpu", BIT(2), BIT(6), false), + [RK3568_PD_RKVDEC] = DOMAIN_RK3568("vdec", BIT(4), BIT(8), false), + [RK3568_PD_RKVENC] = DOMAIN_RK3568("venc", BIT(3), BIT(7), false), + [RK3568_PD_PIPE] = DOMAIN_RK3568("pipe", BIT(8), BIT(11), false), +}; + +static const struct rockchip_domain_info rk3588_pm_domains[] = { + [RK3588_PD_GPU] = DOMAIN_RK3588("gpu", 0x0, BIT(0), 0, 0x0, 0, BIT(1), 0x0, BIT(0), BIT(0), false), + [RK3588_PD_NPU] = DOMAIN_RK3588("npu", 0x0, BIT(1), BIT(1), 0x0, 0, 0, 0x0, 0, 0, false), + [RK3588_PD_VCODEC] = DOMAIN_RK3588("vcodec", 0x0, BIT(2), BIT(2), 0x0, 0, 0, 0x0, 0, 0, false), + [RK3588_PD_NPUTOP] = DOMAIN_RK3588("nputop", 0x0, BIT(3), 0, 0x0, BIT(11), BIT(2), 0x0, BIT(1), BIT(1), false), + [RK3588_PD_NPU1] = DOMAIN_RK3588("npu1", 0x0, BIT(4), 0, 0x0, BIT(12), BIT(3), 0x0, BIT(2), BIT(2), false), + [RK3588_PD_NPU2] = DOMAIN_RK3588("npu2", 0x0, BIT(5), 0, 0x0, BIT(13), BIT(4), 0x0, BIT(3), BIT(3), false), + [RK3588_PD_VENC0] = DOMAIN_RK3588("venc0", 0x0, BIT(6), 0, 0x0, BIT(14), BIT(5), 0x0, BIT(4), BIT(4), false), + [RK3588_PD_VENC1] = DOMAIN_RK3588("venc1", 0x0, BIT(7), 0, 0x0, BIT(15), BIT(6), 0x0, BIT(5), BIT(5), false), + [RK3588_PD_RKVDEC0] = DOMAIN_RK3588("rkvdec0", 0x0, BIT(8), 0, 0x0, BIT(16), BIT(7), 0x0, BIT(6), BIT(6), false), + [RK3588_PD_RKVDEC1] = DOMAIN_RK3588("rkvdec1", 0x0, BIT(9), 0, 0x0, BIT(17), BIT(8), 0x0, BIT(7), BIT(7), false), + [RK3588_PD_VDPU] = DOMAIN_RK3588("vdpu", 0x0, BIT(10), 0, 0x0, BIT(18), BIT(9), 0x0, BIT(8), BIT(8), false), + [RK3588_PD_RGA30] = DOMAIN_RK3588("rga30", 0x0, BIT(11), 0, 0x0, BIT(19), BIT(10), 0x0, 0, 0, false), + [RK3588_PD_AV1] = DOMAIN_RK3588("av1", 0x0, BIT(12), 0, 0x0, BIT(20), BIT(11), 0x0, BIT(9), BIT(9), false), + [RK3588_PD_VI] = DOMAIN_RK3588("vi", 0x0, BIT(13), 0, 0x0, BIT(21), BIT(12), 0x0, BIT(10), BIT(10), false), + [RK3588_PD_FEC] = DOMAIN_RK3588("fec", 0x0, BIT(14), 0, 0x0, BIT(22), BIT(13), 0x0, 0, 0, false), + [RK3588_PD_ISP1] = DOMAIN_RK3588("isp1", 0x0, BIT(15), 0, 0x0, BIT(23), BIT(14), 0x0, BIT(11), BIT(11), false), + [RK3588_PD_RGA31] = DOMAIN_RK3588("rga31", 0x4, BIT(0), 0, 0x0, BIT(24), BIT(15), 0x0, BIT(12), BIT(12), false), + [RK3588_PD_VOP] = DOMAIN_RK3588("vop", 0x4, BIT(1), 0, 0x0, BIT(25), BIT(16), 0x0, BIT(13) | BIT(14), BIT(13) | BIT(14), false), + [RK3588_PD_VO0] = DOMAIN_RK3588("vo0", 0x4, BIT(2), 0, 0x0, BIT(26), BIT(17), 0x0, BIT(15), BIT(15), false), + [RK3588_PD_VO1] = DOMAIN_RK3588("vo1", 0x4, BIT(3), 0, 0x0, BIT(27), BIT(18), 0x4, BIT(0), BIT(16), false), + [RK3588_PD_AUDIO] = DOMAIN_RK3588("audio", 0x4, BIT(4), 0, 0x0, BIT(28), BIT(19), 0x4, BIT(1), BIT(17), false), + [RK3588_PD_PHP] = DOMAIN_RK3588("php", 0x4, BIT(5), 0, 0x0, BIT(29), BIT(20), 0x4, BIT(5), BIT(21), false), + [RK3588_PD_GMAC] = DOMAIN_RK3588("gmac", 0x4, BIT(6), 0, 0x0, BIT(30), BIT(21), 0x0, 0, 0, false), + [RK3588_PD_PCIE] = DOMAIN_RK3588("pcie", 0x4, BIT(7), 0, 0x0, BIT(31), BIT(22), 0x0, 0, 0, true), + [RK3588_PD_NVM] = DOMAIN_RK3588("nvm", 0x4, BIT(8), BIT(24), 0x4, 0, 0, 0x4, BIT(2), BIT(18), false), + [RK3588_PD_NVM0] = DOMAIN_RK3588("nvm0", 0x4, BIT(9), 0, 0x4, BIT(1), BIT(23), 0x0, 0, 0, false), + [RK3588_PD_SDIO] = DOMAIN_RK3588("sdio", 0x4, BIT(10), 0, 0x4, BIT(2), BIT(24), 0x4, BIT(3), BIT(19), false), + [RK3588_PD_USB] = DOMAIN_RK3588("usb", 0x4, BIT(11), 0, 0x4, BIT(3), BIT(25), 0x4, BIT(4), BIT(20), true), + [RK3588_PD_SDMMC] = DOMAIN_RK3588("sdmmc", 0x4, BIT(13), 0, 0x4, BIT(5), BIT(26), 0x0, 0, 0, false), +}; + +static const struct rockchip_pmu_info px30_pmu = { + .pwr_offset = 0x18, + .status_offset = 0x20, + .req_offset = 0x64, + .idle_offset = 0x6c, + .ack_offset = 0x6c, + + .num_domains = ARRAY_SIZE(px30_pm_domains), + .domain_info = px30_pm_domains, +}; + +static const struct rockchip_pmu_info rk3036_pmu = { + .req_offset = 0x148, + .idle_offset = 0x14c, + .ack_offset = 0x14c, + + .num_domains = ARRAY_SIZE(rk3036_pm_domains), + .domain_info = rk3036_pm_domains, +}; + +static const struct rockchip_pmu_info rk3066_pmu = { + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x38, /* PMU_MISC_CON1 */ + .idle_offset = 0x0c, + .ack_offset = 0x0c, + + .num_domains = ARRAY_SIZE(rk3066_pm_domains), + .domain_info = rk3066_pm_domains, +}; + +static const struct rockchip_pmu_info rk3128_pmu = { + .pwr_offset = 0x04, + .status_offset = 0x08, + .req_offset = 0x0c, + .idle_offset = 0x10, + .ack_offset = 0x10, + + .num_domains = ARRAY_SIZE(rk3128_pm_domains), + .domain_info = rk3128_pm_domains, +}; + +static const struct rockchip_pmu_info rk3188_pmu = { + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x38, /* PMU_MISC_CON1 */ + .idle_offset = 0x0c, + .ack_offset = 0x0c, + + .num_domains = ARRAY_SIZE(rk3188_pm_domains), + .domain_info = rk3188_pm_domains, +}; + +static const struct rockchip_pmu_info rk3228_pmu = { + .req_offset = 0x40c, + .idle_offset = 0x488, + .ack_offset = 0x488, + + .num_domains = ARRAY_SIZE(rk3228_pm_domains), + .domain_info = rk3228_pm_domains, +}; + +static const struct rockchip_pmu_info rk3288_pmu = { + .pwr_offset = 0x08, + .status_offset = 0x0c, + .req_offset = 0x10, + .idle_offset = 0x14, + .ack_offset = 0x14, + + .core_pwrcnt_offset = 0x34, + .gpu_pwrcnt_offset = 0x3c, + + .core_power_transition_time = 24, /* 1us */ + .gpu_power_transition_time = 24, /* 1us */ + + .num_domains = ARRAY_SIZE(rk3288_pm_domains), + .domain_info = rk3288_pm_domains, +}; + +static const struct rockchip_pmu_info rk3328_pmu = { + .req_offset = 0x414, + .idle_offset = 0x484, + .ack_offset = 0x484, + + .num_domains = ARRAY_SIZE(rk3328_pm_domains), + .domain_info = rk3328_pm_domains, +}; + +static const struct rockchip_pmu_info rk3366_pmu = { + .pwr_offset = 0x0c, + .status_offset = 0x10, + .req_offset = 0x3c, + .idle_offset = 0x40, + .ack_offset = 0x40, + + .core_pwrcnt_offset = 0x48, + .gpu_pwrcnt_offset = 0x50, + + .core_power_transition_time = 24, + .gpu_power_transition_time = 24, + + .num_domains = ARRAY_SIZE(rk3366_pm_domains), + .domain_info = rk3366_pm_domains, +}; + +static const struct rockchip_pmu_info rk3368_pmu = { + .pwr_offset = 0x0c, + .status_offset = 0x10, + .req_offset = 0x3c, + .idle_offset = 0x40, + .ack_offset = 0x40, + + .core_pwrcnt_offset = 0x48, + .gpu_pwrcnt_offset = 0x50, + + .core_power_transition_time = 24, + .gpu_power_transition_time = 24, + + .num_domains = ARRAY_SIZE(rk3368_pm_domains), + .domain_info = rk3368_pm_domains, +}; + +static const struct rockchip_pmu_info rk3399_pmu = { + .pwr_offset = 0x14, + .status_offset = 0x18, + .req_offset = 0x60, + .idle_offset = 0x64, + .ack_offset = 0x68, + + /* ARM Trusted Firmware manages power transition times */ + + .num_domains = ARRAY_SIZE(rk3399_pm_domains), + .domain_info = rk3399_pm_domains, +}; + +static const struct rockchip_pmu_info rk3568_pmu = { + .pwr_offset = 0xa0, + .status_offset = 0x98, + .req_offset = 0x50, + .idle_offset = 0x68, + .ack_offset = 0x60, + + .num_domains = ARRAY_SIZE(rk3568_pm_domains), + .domain_info = rk3568_pm_domains, +}; + +static const struct rockchip_pmu_info rk3588_pmu = { + .pwr_offset = 0x14c, + .status_offset = 0x180, + .req_offset = 0x10c, + .idle_offset = 0x120, + .ack_offset = 0x118, + .mem_pwr_offset = 0x1a0, + .chain_status_offset = 0x1f0, + .mem_status_offset = 0x1f8, + .repair_status_offset = 0x290, + + .num_domains = ARRAY_SIZE(rk3588_pm_domains), + .domain_info = rk3588_pm_domains, +}; + +static const struct rockchip_pmu_info rv1126_pmu = { + .pwr_offset = 0x110, + .status_offset = 0x108, + .req_offset = 0xc0, + .idle_offset = 0xd8, + .ack_offset = 0xd0, + + .num_domains = ARRAY_SIZE(rv1126_pm_domains), + .domain_info = rv1126_pm_domains, +}; + +static const struct of_device_id rockchip_pm_domain_dt_match[] = { + { + .compatible = "rockchip,px30-power-controller", + .data = (void *)&px30_pmu, + }, + { + .compatible = "rockchip,rk3036-power-controller", + .data = (void *)&rk3036_pmu, + }, + { + .compatible = "rockchip,rk3066-power-controller", + .data = (void *)&rk3066_pmu, + }, + { + .compatible = "rockchip,rk3128-power-controller", + .data = (void *)&rk3128_pmu, + }, + { + .compatible = "rockchip,rk3188-power-controller", + .data = (void *)&rk3188_pmu, + }, + { + .compatible = "rockchip,rk3228-power-controller", + .data = (void *)&rk3228_pmu, + }, + { + .compatible = "rockchip,rk3288-power-controller", + .data = (void *)&rk3288_pmu, + }, + { + .compatible = "rockchip,rk3328-power-controller", + .data = (void *)&rk3328_pmu, + }, + { + .compatible = "rockchip,rk3366-power-controller", + .data = (void *)&rk3366_pmu, + }, + { + .compatible = "rockchip,rk3368-power-controller", + .data = (void *)&rk3368_pmu, + }, + { + .compatible = "rockchip,rk3399-power-controller", + .data = (void *)&rk3399_pmu, + }, + { + .compatible = "rockchip,rk3568-power-controller", + .data = (void *)&rk3568_pmu, + }, + { + .compatible = "rockchip,rk3588-power-controller", + .data = (void *)&rk3588_pmu, + }, + { + .compatible = "rockchip,rv1126-power-controller", + .data = (void *)&rv1126_pmu, + }, + { /* sentinel */ }, +}; + +static struct platform_driver rockchip_pm_domain_driver = { + .probe = rockchip_pm_domain_probe, + .driver = { + .name = "rockchip-pm-domain", + .of_match_table = rockchip_pm_domain_dt_match, + /* + * We can't forcibly eject devices from the power + * domain, so we can't really remove power domains + * once they were added. + */ + .suppress_bind_attrs = true, + }, +}; + +static int __init rockchip_pm_domain_drv_register(void) +{ + return platform_driver_register(&rockchip_pm_domain_driver); +} +postcore_initcall(rockchip_pm_domain_drv_register); diff --git a/drivers/pmdomain/samsung/Makefile b/drivers/pmdomain/samsung/Makefile new file mode 100644 index 000000000000..397aa5908c1d --- /dev/null +++ b/drivers/pmdomain/samsung/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_EXYNOS_PM_DOMAINS) += exynos-pm-domains.o diff --git a/drivers/pmdomain/samsung/exynos-pm-domains.c b/drivers/pmdomain/samsung/exynos-pm-domains.c new file mode 100644 index 000000000000..9b502e8751d1 --- /dev/null +++ b/drivers/pmdomain/samsung/exynos-pm-domains.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Exynos Generic power domain support. +// +// Copyright (c) 2012 Samsung Electronics Co., Ltd. +// http://www.samsung.com +// +// Implementation of Exynos specific power domain control which is used in +// conjunction with runtime-pm. Support for both device-tree and non-device-tree +// based power domain support is included. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct exynos_pm_domain_config { + /* Value for LOCAL_PWR_CFG and STATUS fields for each domain */ + u32 local_pwr_cfg; +}; + +/* + * Exynos specific wrapper around the generic power domain + */ +struct exynos_pm_domain { + void __iomem *base; + struct generic_pm_domain pd; + u32 local_pwr_cfg; +}; + +static int exynos_pd_power(struct generic_pm_domain *domain, bool power_on) +{ + struct exynos_pm_domain *pd; + void __iomem *base; + u32 timeout, pwr; + char *op; + + pd = container_of(domain, struct exynos_pm_domain, pd); + base = pd->base; + + pwr = power_on ? pd->local_pwr_cfg : 0; + writel_relaxed(pwr, base); + + /* Wait max 1ms */ + timeout = 10; + + while ((readl_relaxed(base + 0x4) & pd->local_pwr_cfg) != pwr) { + if (!timeout) { + op = (power_on) ? "enable" : "disable"; + pr_err("Power domain %s %s failed\n", domain->name, op); + return -ETIMEDOUT; + } + timeout--; + cpu_relax(); + usleep_range(80, 100); + } + + return 0; +} + +static int exynos_pd_power_on(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, true); +} + +static int exynos_pd_power_off(struct generic_pm_domain *domain) +{ + return exynos_pd_power(domain, false); +} + +static const struct exynos_pm_domain_config exynos4210_cfg = { + .local_pwr_cfg = 0x7, +}; + +static const struct exynos_pm_domain_config exynos5433_cfg = { + .local_pwr_cfg = 0xf, +}; + +static const struct of_device_id exynos_pm_domain_of_match[] = { + { + .compatible = "samsung,exynos4210-pd", + .data = &exynos4210_cfg, + }, { + .compatible = "samsung,exynos5433-pd", + .data = &exynos5433_cfg, + }, + { }, +}; + +static const char *exynos_get_domain_name(struct device_node *node) +{ + const char *name; + + if (of_property_read_string(node, "label", &name) < 0) + name = kbasename(node->full_name); + return kstrdup_const(name, GFP_KERNEL); +} + +static int exynos_pd_probe(struct platform_device *pdev) +{ + const struct exynos_pm_domain_config *pm_domain_cfg; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct of_phandle_args child, parent; + struct exynos_pm_domain *pd; + int on, ret; + + pm_domain_cfg = of_device_get_match_data(dev); + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->pd.name = exynos_get_domain_name(np); + if (!pd->pd.name) + return -ENOMEM; + + pd->base = of_iomap(np, 0); + if (!pd->base) { + kfree_const(pd->pd.name); + return -ENODEV; + } + + pd->pd.power_off = exynos_pd_power_off; + pd->pd.power_on = exynos_pd_power_on; + pd->local_pwr_cfg = pm_domain_cfg->local_pwr_cfg; + + on = readl_relaxed(pd->base + 0x4) & pd->local_pwr_cfg; + + pm_genpd_init(&pd->pd, NULL, !on); + ret = of_genpd_add_provider_simple(np, &pd->pd); + + if (ret == 0 && of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &parent) == 0) { + child.np = np; + child.args_count = 0; + + if (of_genpd_add_subdomain(&parent, &child)) + pr_warn("%pOF failed to add subdomain: %pOF\n", + parent.np, child.np); + else + pr_info("%pOF has as child subdomain: %pOF.\n", + parent.np, child.np); + } + + pm_runtime_enable(dev); + return ret; +} + +static struct platform_driver exynos_pd_driver = { + .probe = exynos_pd_probe, + .driver = { + .name = "exynos-pd", + .of_match_table = exynos_pm_domain_of_match, + .suppress_bind_attrs = true, + } +}; + +static __init int exynos4_pm_init_power_domain(void) +{ + return platform_driver_register(&exynos_pd_driver); +} +core_initcall(exynos4_pm_init_power_domain); diff --git a/drivers/pmdomain/st/Makefile b/drivers/pmdomain/st/Makefile new file mode 100644 index 000000000000..8fa5f9855460 --- /dev/null +++ b/drivers/pmdomain/st/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_ARCH_U8500) += ste-ux500-pm-domain.o diff --git a/drivers/pmdomain/st/ste-ux500-pm-domain.c b/drivers/pmdomain/st/ste-ux500-pm-domain.c new file mode 100644 index 000000000000..3d4f111ed156 --- /dev/null +++ b/drivers/pmdomain/st/ste-ux500-pm-domain.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2014 Linaro Ltd. + * + * Author: Ulf Hansson + * + * Implements PM domains using the generic PM domain for ux500. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static int pd_power_off(struct generic_pm_domain *domain) +{ + /* + * Handle the gating of the PM domain regulator here. + * + * Drivers/subsystems handling devices in the PM domain needs to perform + * register context save/restore from their respective runtime PM + * callbacks, to be able to enable PM domain gating/ungating. + */ + return 0; +} + +static int pd_power_on(struct generic_pm_domain *domain) +{ + /* + * Handle the ungating of the PM domain regulator here. + * + * Drivers/subsystems handling devices in the PM domain needs to perform + * register context save/restore from their respective runtime PM + * callbacks, to be able to enable PM domain gating/ungating. + */ + return 0; +} + +static struct generic_pm_domain ux500_pm_domain_vape = { + .name = "VAPE", + .power_off = pd_power_off, + .power_on = pd_power_on, +}; + +static struct generic_pm_domain *ux500_pm_domains[NR_DOMAINS] = { + [DOMAIN_VAPE] = &ux500_pm_domain_vape, +}; + +static const struct of_device_id ux500_pm_domain_matches[] = { + { .compatible = "stericsson,ux500-pm-domains", }, + { }, +}; + +static int ux500_pm_domains_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct genpd_onecell_data *genpd_data; + int i; + + if (!np) + return -ENODEV; + + genpd_data = kzalloc(sizeof(*genpd_data), GFP_KERNEL); + if (!genpd_data) + return -ENOMEM; + + genpd_data->domains = ux500_pm_domains; + genpd_data->num_domains = ARRAY_SIZE(ux500_pm_domains); + + for (i = 0; i < ARRAY_SIZE(ux500_pm_domains); ++i) + pm_genpd_init(ux500_pm_domains[i], NULL, false); + + of_genpd_add_provider_onecell(np, genpd_data); + return 0; +} + +static struct platform_driver ux500_pm_domains_driver = { + .probe = ux500_pm_domains_probe, + .driver = { + .name = "ux500_pm_domains", + .of_match_table = ux500_pm_domain_matches, + }, +}; + +static int __init ux500_pm_domains_init(void) +{ + return platform_driver_register(&ux500_pm_domains_driver); +} +arch_initcall(ux500_pm_domains_init); diff --git a/drivers/pmdomain/starfive/Makefile b/drivers/pmdomain/starfive/Makefile new file mode 100644 index 000000000000..975bba2a29a9 --- /dev/null +++ b/drivers/pmdomain/starfive/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_JH71XX_PMU) += jh71xx-pmu.o diff --git a/drivers/pmdomain/starfive/jh71xx-pmu.c b/drivers/pmdomain/starfive/jh71xx-pmu.c new file mode 100644 index 000000000000..7d5f50d71c0d --- /dev/null +++ b/drivers/pmdomain/starfive/jh71xx-pmu.c @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * StarFive JH71XX PMU (Power Management Unit) Controller Driver + * + * Copyright (C) 2022 StarFive Technology Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* register offset */ +#define JH71XX_PMU_SW_TURN_ON_POWER 0x0C +#define JH71XX_PMU_SW_TURN_OFF_POWER 0x10 +#define JH71XX_PMU_SW_ENCOURAGE 0x44 +#define JH71XX_PMU_TIMER_INT_MASK 0x48 +#define JH71XX_PMU_CURR_POWER_MODE 0x80 +#define JH71XX_PMU_EVENT_STATUS 0x88 +#define JH71XX_PMU_INT_STATUS 0x8C + +/* sw encourage cfg */ +#define JH71XX_PMU_SW_ENCOURAGE_EN_LO 0x05 +#define JH71XX_PMU_SW_ENCOURAGE_EN_HI 0x50 +#define JH71XX_PMU_SW_ENCOURAGE_DIS_LO 0x0A +#define JH71XX_PMU_SW_ENCOURAGE_DIS_HI 0xA0 +#define JH71XX_PMU_SW_ENCOURAGE_ON 0xFF + +/* pmu int status */ +#define JH71XX_PMU_INT_SEQ_DONE BIT(0) +#define JH71XX_PMU_INT_HW_REQ BIT(1) +#define JH71XX_PMU_INT_SW_FAIL GENMASK(3, 2) +#define JH71XX_PMU_INT_HW_FAIL GENMASK(5, 4) +#define JH71XX_PMU_INT_PCH_FAIL GENMASK(8, 6) +#define JH71XX_PMU_INT_ALL_MASK GENMASK(8, 0) + +/* + * The time required for switching power status is based on the time + * to turn on the largest domain's power, which is at microsecond level + */ +#define JH71XX_PMU_TIMEOUT_US 100 + +struct jh71xx_domain_info { + const char * const name; + unsigned int flags; + u8 bit; +}; + +struct jh71xx_pmu_match_data { + const struct jh71xx_domain_info *domain_info; + int num_domains; +}; + +struct jh71xx_pmu { + struct device *dev; + const struct jh71xx_pmu_match_data *match_data; + void __iomem *base; + struct generic_pm_domain **genpd; + struct genpd_onecell_data genpd_data; + int irq; + spinlock_t lock; /* protects pmu reg */ +}; + +struct jh71xx_pmu_dev { + const struct jh71xx_domain_info *domain_info; + struct jh71xx_pmu *pmu; + struct generic_pm_domain genpd; +}; + +static int jh71xx_pmu_get_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool *is_on) +{ + struct jh71xx_pmu *pmu = pmd->pmu; + + if (!mask) + return -EINVAL; + + *is_on = readl(pmu->base + JH71XX_PMU_CURR_POWER_MODE) & mask; + + return 0; +} + +static int jh71xx_pmu_set_state(struct jh71xx_pmu_dev *pmd, u32 mask, bool on) +{ + struct jh71xx_pmu *pmu = pmd->pmu; + unsigned long flags; + u32 val; + u32 mode; + u32 encourage_lo; + u32 encourage_hi; + bool is_on; + int ret; + + ret = jh71xx_pmu_get_state(pmd, mask, &is_on); + if (ret) { + dev_dbg(pmu->dev, "unable to get current state for %s\n", + pmd->genpd.name); + return ret; + } + + if (is_on == on) { + dev_dbg(pmu->dev, "pm domain [%s] is already %sable status.\n", + pmd->genpd.name, on ? "en" : "dis"); + return 0; + } + + spin_lock_irqsave(&pmu->lock, flags); + + /* + * The PMU accepts software encourage to switch power mode in the following 2 steps: + * + * 1.Configure the register SW_TURN_ON_POWER (offset 0x0c) by writing 1 to + * the bit corresponding to the power domain that will be turned on + * and writing 0 to the others. + * Likewise, configure the register SW_TURN_OFF_POWER (offset 0x10) by + * writing 1 to the bit corresponding to the power domain that will be + * turned off and writing 0 to the others. + */ + if (on) { + mode = JH71XX_PMU_SW_TURN_ON_POWER; + encourage_lo = JH71XX_PMU_SW_ENCOURAGE_EN_LO; + encourage_hi = JH71XX_PMU_SW_ENCOURAGE_EN_HI; + } else { + mode = JH71XX_PMU_SW_TURN_OFF_POWER; + encourage_lo = JH71XX_PMU_SW_ENCOURAGE_DIS_LO; + encourage_hi = JH71XX_PMU_SW_ENCOURAGE_DIS_HI; + } + + writel(mask, pmu->base + mode); + + /* + * 2.Write SW encourage command sequence to the Software Encourage Reg (offset 0x44) + * First write SW_MODE_ENCOURAGE_ON to JH71XX_PMU_SW_ENCOURAGE. This will reset + * the state machine which parses the command sequence. This register must be + * written every time software wants to power on/off a domain. + * Then write the lower bits of the command sequence, followed by the upper + * bits. The sequence differs between powering on & off a domain. + */ + writel(JH71XX_PMU_SW_ENCOURAGE_ON, pmu->base + JH71XX_PMU_SW_ENCOURAGE); + writel(encourage_lo, pmu->base + JH71XX_PMU_SW_ENCOURAGE); + writel(encourage_hi, pmu->base + JH71XX_PMU_SW_ENCOURAGE); + + spin_unlock_irqrestore(&pmu->lock, flags); + + /* Wait for the power domain bit to be enabled / disabled */ + if (on) { + ret = readl_poll_timeout_atomic(pmu->base + JH71XX_PMU_CURR_POWER_MODE, + val, val & mask, + 1, JH71XX_PMU_TIMEOUT_US); + } else { + ret = readl_poll_timeout_atomic(pmu->base + JH71XX_PMU_CURR_POWER_MODE, + val, !(val & mask), + 1, JH71XX_PMU_TIMEOUT_US); + } + + if (ret) { + dev_err(pmu->dev, "%s: failed to power %s\n", + pmd->genpd.name, on ? "on" : "off"); + return -ETIMEDOUT; + } + + return 0; +} + +static int jh71xx_pmu_on(struct generic_pm_domain *genpd) +{ + struct jh71xx_pmu_dev *pmd = container_of(genpd, + struct jh71xx_pmu_dev, genpd); + u32 pwr_mask = BIT(pmd->domain_info->bit); + + return jh71xx_pmu_set_state(pmd, pwr_mask, true); +} + +static int jh71xx_pmu_off(struct generic_pm_domain *genpd) +{ + struct jh71xx_pmu_dev *pmd = container_of(genpd, + struct jh71xx_pmu_dev, genpd); + u32 pwr_mask = BIT(pmd->domain_info->bit); + + return jh71xx_pmu_set_state(pmd, pwr_mask, false); +} + +static void jh71xx_pmu_int_enable(struct jh71xx_pmu *pmu, u32 mask, bool enable) +{ + u32 val; + unsigned long flags; + + spin_lock_irqsave(&pmu->lock, flags); + val = readl(pmu->base + JH71XX_PMU_TIMER_INT_MASK); + + if (enable) + val &= ~mask; + else + val |= mask; + + writel(val, pmu->base + JH71XX_PMU_TIMER_INT_MASK); + spin_unlock_irqrestore(&pmu->lock, flags); +} + +static irqreturn_t jh71xx_pmu_interrupt(int irq, void *data) +{ + struct jh71xx_pmu *pmu = data; + u32 val; + + val = readl(pmu->base + JH71XX_PMU_INT_STATUS); + + if (val & JH71XX_PMU_INT_SEQ_DONE) + dev_dbg(pmu->dev, "sequence done.\n"); + if (val & JH71XX_PMU_INT_HW_REQ) + dev_dbg(pmu->dev, "hardware encourage requestion.\n"); + if (val & JH71XX_PMU_INT_SW_FAIL) + dev_err(pmu->dev, "software encourage fail.\n"); + if (val & JH71XX_PMU_INT_HW_FAIL) + dev_err(pmu->dev, "hardware encourage fail.\n"); + if (val & JH71XX_PMU_INT_PCH_FAIL) + dev_err(pmu->dev, "p-channel fail event.\n"); + + /* clear interrupts */ + writel(val, pmu->base + JH71XX_PMU_INT_STATUS); + writel(val, pmu->base + JH71XX_PMU_EVENT_STATUS); + + return IRQ_HANDLED; +} + +static int jh71xx_pmu_init_domain(struct jh71xx_pmu *pmu, int index) +{ + struct jh71xx_pmu_dev *pmd; + u32 pwr_mask; + int ret; + bool is_on = false; + + pmd = devm_kzalloc(pmu->dev, sizeof(*pmd), GFP_KERNEL); + if (!pmd) + return -ENOMEM; + + pmd->domain_info = &pmu->match_data->domain_info[index]; + pmd->pmu = pmu; + pwr_mask = BIT(pmd->domain_info->bit); + + pmd->genpd.name = pmd->domain_info->name; + pmd->genpd.flags = pmd->domain_info->flags; + + ret = jh71xx_pmu_get_state(pmd, pwr_mask, &is_on); + if (ret) + dev_warn(pmu->dev, "unable to get current state for %s\n", + pmd->genpd.name); + + pmd->genpd.power_on = jh71xx_pmu_on; + pmd->genpd.power_off = jh71xx_pmu_off; + pm_genpd_init(&pmd->genpd, NULL, !is_on); + + pmu->genpd_data.domains[index] = &pmd->genpd; + + return 0; +} + +static int jh71xx_pmu_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + const struct jh71xx_pmu_match_data *match_data; + struct jh71xx_pmu *pmu; + unsigned int i; + int ret; + + pmu = devm_kzalloc(dev, sizeof(*pmu), GFP_KERNEL); + if (!pmu) + return -ENOMEM; + + pmu->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pmu->base)) + return PTR_ERR(pmu->base); + + pmu->irq = platform_get_irq(pdev, 0); + if (pmu->irq < 0) + return pmu->irq; + + ret = devm_request_irq(dev, pmu->irq, jh71xx_pmu_interrupt, + 0, pdev->name, pmu); + if (ret) + dev_err(dev, "failed to request irq\n"); + + match_data = of_device_get_match_data(dev); + if (!match_data) + return -EINVAL; + + pmu->genpd = devm_kcalloc(dev, match_data->num_domains, + sizeof(struct generic_pm_domain *), + GFP_KERNEL); + if (!pmu->genpd) + return -ENOMEM; + + pmu->dev = dev; + pmu->match_data = match_data; + pmu->genpd_data.domains = pmu->genpd; + pmu->genpd_data.num_domains = match_data->num_domains; + + for (i = 0; i < match_data->num_domains; i++) { + ret = jh71xx_pmu_init_domain(pmu, i); + if (ret) { + dev_err(dev, "failed to initialize power domain\n"); + return ret; + } + } + + spin_lock_init(&pmu->lock); + jh71xx_pmu_int_enable(pmu, JH71XX_PMU_INT_ALL_MASK & ~JH71XX_PMU_INT_PCH_FAIL, true); + + ret = of_genpd_add_provider_onecell(np, &pmu->genpd_data); + if (ret) { + dev_err(dev, "failed to register genpd driver: %d\n", ret); + return ret; + } + + dev_dbg(dev, "registered %u power domains\n", i); + + return 0; +} + +static const struct jh71xx_domain_info jh7110_power_domains[] = { + [JH7110_PD_SYSTOP] = { + .name = "SYSTOP", + .bit = 0, + .flags = GENPD_FLAG_ALWAYS_ON, + }, + [JH7110_PD_CPU] = { + .name = "CPU", + .bit = 1, + .flags = GENPD_FLAG_ALWAYS_ON, + }, + [JH7110_PD_GPUA] = { + .name = "GPUA", + .bit = 2, + }, + [JH7110_PD_VDEC] = { + .name = "VDEC", + .bit = 3, + }, + [JH7110_PD_VOUT] = { + .name = "VOUT", + .bit = 4, + }, + [JH7110_PD_ISP] = { + .name = "ISP", + .bit = 5, + }, + [JH7110_PD_VENC] = { + .name = "VENC", + .bit = 6, + }, +}; + +static const struct jh71xx_pmu_match_data jh7110_pmu = { + .num_domains = ARRAY_SIZE(jh7110_power_domains), + .domain_info = jh7110_power_domains, +}; + +static const struct of_device_id jh71xx_pmu_of_match[] = { + { + .compatible = "starfive,jh7110-pmu", + .data = (void *)&jh7110_pmu, + }, { + /* sentinel */ + } +}; + +static struct platform_driver jh71xx_pmu_driver = { + .probe = jh71xx_pmu_probe, + .driver = { + .name = "jh71xx-pmu", + .of_match_table = jh71xx_pmu_of_match, + .suppress_bind_attrs = true, + }, +}; +builtin_platform_driver(jh71xx_pmu_driver); + +MODULE_AUTHOR("Walker Chen "); +MODULE_DESCRIPTION("StarFive JH71XX PMU Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/sunxi/Makefile b/drivers/pmdomain/sunxi/Makefile new file mode 100644 index 000000000000..ec1d7a2fb21d --- /dev/null +++ b/drivers/pmdomain/sunxi/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SUN20I_PPU) += sun20i-ppu.o diff --git a/drivers/pmdomain/sunxi/sun20i-ppu.c b/drivers/pmdomain/sunxi/sun20i-ppu.c new file mode 100644 index 000000000000..8700f9dd5f75 --- /dev/null +++ b/drivers/pmdomain/sunxi/sun20i-ppu.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define PD_STATE_ON 1 +#define PD_STATE_OFF 2 + +#define PD_RSTN_REG 0x00 +#define PD_CLK_GATE_REG 0x04 +#define PD_PWROFF_GATE_REG 0x08 +#define PD_PSW_ON_REG 0x0c +#define PD_PSW_OFF_REG 0x10 +#define PD_PSW_DELAY_REG 0x14 +#define PD_OFF_DELAY_REG 0x18 +#define PD_ON_DELAY_REG 0x1c +#define PD_COMMAND_REG 0x20 +#define PD_STATUS_REG 0x24 +#define PD_STATUS_COMPLETE BIT(1) +#define PD_STATUS_BUSY BIT(3) +#define PD_STATUS_STATE GENMASK(17, 16) +#define PD_ACTIVE_CTRL_REG 0x2c +#define PD_GATE_STATUS_REG 0x30 +#define PD_RSTN_STATUS BIT(0) +#define PD_CLK_GATE_STATUS BIT(1) +#define PD_PWROFF_GATE_STATUS BIT(2) +#define PD_PSW_STATUS_REG 0x34 + +#define PD_REGS_SIZE 0x80 + +struct sun20i_ppu_desc { + const char *const *names; + unsigned int num_domains; +}; + +struct sun20i_ppu_pd { + struct generic_pm_domain genpd; + void __iomem *base; +}; + +#define to_sun20i_ppu_pd(_genpd) \ + container_of(_genpd, struct sun20i_ppu_pd, genpd) + +static bool sun20i_ppu_pd_is_on(const struct sun20i_ppu_pd *pd) +{ + u32 status = readl(pd->base + PD_STATUS_REG); + + return FIELD_GET(PD_STATUS_STATE, status) == PD_STATE_ON; +} + +static int sun20i_ppu_pd_set_power(const struct sun20i_ppu_pd *pd, bool power_on) +{ + u32 state, status; + int ret; + + if (sun20i_ppu_pd_is_on(pd) == power_on) + return 0; + + /* Wait for the power controller to be idle. */ + ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status, + !(status & PD_STATUS_BUSY), 100, 1000); + if (ret) + return ret; + + state = power_on ? PD_STATE_ON : PD_STATE_OFF; + writel(state, pd->base + PD_COMMAND_REG); + + /* Wait for the state transition to complete. */ + ret = readl_poll_timeout(pd->base + PD_STATUS_REG, status, + FIELD_GET(PD_STATUS_STATE, status) == state && + (status & PD_STATUS_COMPLETE), 100, 1000); + if (ret) + return ret; + + /* Clear the completion flag. */ + writel(status, pd->base + PD_STATUS_REG); + + return 0; +} + +static int sun20i_ppu_pd_power_on(struct generic_pm_domain *genpd) +{ + const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd); + + return sun20i_ppu_pd_set_power(pd, true); +} + +static int sun20i_ppu_pd_power_off(struct generic_pm_domain *genpd) +{ + const struct sun20i_ppu_pd *pd = to_sun20i_ppu_pd(genpd); + + return sun20i_ppu_pd_set_power(pd, false); +} + +static int sun20i_ppu_probe(struct platform_device *pdev) +{ + const struct sun20i_ppu_desc *desc; + struct device *dev = &pdev->dev; + struct genpd_onecell_data *ppu; + struct sun20i_ppu_pd *pds; + struct reset_control *rst; + void __iomem *base; + struct clk *clk; + int ret; + + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; + + pds = devm_kcalloc(dev, desc->num_domains, sizeof(*pds), GFP_KERNEL); + if (!pds) + return -ENOMEM; + + ppu = devm_kzalloc(dev, sizeof(*ppu), GFP_KERNEL); + if (!ppu) + return -ENOMEM; + + ppu->domains = devm_kcalloc(dev, desc->num_domains, + sizeof(*ppu->domains), GFP_KERNEL); + if (!ppu->domains) + return -ENOMEM; + + ppu->num_domains = desc->num_domains; + platform_set_drvdata(pdev, ppu); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + ret = reset_control_deassert(rst); + if (ret) + return ret; + + for (unsigned int i = 0; i < ppu->num_domains; ++i) { + struct sun20i_ppu_pd *pd = &pds[i]; + + pd->genpd.name = desc->names[i]; + pd->genpd.power_off = sun20i_ppu_pd_power_off; + pd->genpd.power_on = sun20i_ppu_pd_power_on; + pd->base = base + PD_REGS_SIZE * i; + + ret = pm_genpd_init(&pd->genpd, NULL, sun20i_ppu_pd_is_on(pd)); + if (ret) { + dev_warn(dev, "Failed to add '%s' domain: %d\n", + pd->genpd.name, ret); + continue; + } + + ppu->domains[i] = &pd->genpd; + } + + ret = of_genpd_add_provider_onecell(dev->of_node, ppu); + if (ret) + dev_warn(dev, "Failed to add provider: %d\n", ret); + + return 0; +} + +static const char *const sun20i_d1_ppu_pd_names[] = { + "CPU", + "VE", + "DSP", +}; + +static const struct sun20i_ppu_desc sun20i_d1_ppu_desc = { + .names = sun20i_d1_ppu_pd_names, + .num_domains = ARRAY_SIZE(sun20i_d1_ppu_pd_names), +}; + +static const struct of_device_id sun20i_ppu_of_match[] = { + { + .compatible = "allwinner,sun20i-d1-ppu", + .data = &sun20i_d1_ppu_desc, + }, + { } +}; +MODULE_DEVICE_TABLE(of, sun20i_ppu_of_match); + +static struct platform_driver sun20i_ppu_driver = { + .probe = sun20i_ppu_probe, + .driver = { + .name = "sun20i-ppu", + .of_match_table = sun20i_ppu_of_match, + /* Power domains cannot be removed while they are in use. */ + .suppress_bind_attrs = true, + }, +}; +module_platform_driver(sun20i_ppu_driver); + +MODULE_AUTHOR("Samuel Holland "); +MODULE_DESCRIPTION("Allwinner D1 PPU power domain driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pmdomain/tegra/Makefile b/drivers/pmdomain/tegra/Makefile new file mode 100644 index 000000000000..ec8acfd2c77c --- /dev/null +++ b/drivers/pmdomain/tegra/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_SOC_TEGRA_POWERGATE_BPMP) += powergate-bpmp.o diff --git a/drivers/pmdomain/tegra/powergate-bpmp.c b/drivers/pmdomain/tegra/powergate-bpmp.c new file mode 100644 index 000000000000..179ed895c279 --- /dev/null +++ b/drivers/pmdomain/tegra/powergate-bpmp.c @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved + */ + +#include +#include +#include +#include + +#include +#include + +struct tegra_powergate_info { + unsigned int id; + char *name; +}; + +struct tegra_powergate { + struct generic_pm_domain genpd; + struct tegra_bpmp *bpmp; + unsigned int id; +}; + +static inline struct tegra_powergate * +to_tegra_powergate(struct generic_pm_domain *genpd) +{ + return container_of(genpd, struct tegra_powergate, genpd); +} + +static int tegra_bpmp_powergate_set_state(struct tegra_bpmp *bpmp, + unsigned int id, u32 state) +{ + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_SET_STATE; + request.id = id; + request.set_state.state = state; + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + else if (msg.rx.ret < 0) + return -EINVAL; + + return 0; +} + +static int tegra_bpmp_powergate_get_state(struct tegra_bpmp *bpmp, + unsigned int id) +{ + struct mrq_pg_response response; + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_GET_STATE; + request.id = id; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return PG_STATE_OFF; + else if (msg.rx.ret < 0) + return -EINVAL; + + return response.get_state.state; +} + +static int tegra_bpmp_powergate_get_max_id(struct tegra_bpmp *bpmp) +{ + struct mrq_pg_response response; + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_GET_MAX_ID; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0) + return err; + else if (msg.rx.ret < 0) + return -EINVAL; + + return response.get_max_id.max_id; +} + +static char *tegra_bpmp_powergate_get_name(struct tegra_bpmp *bpmp, + unsigned int id) +{ + struct mrq_pg_response response; + struct mrq_pg_request request; + struct tegra_bpmp_message msg; + int err; + + memset(&request, 0, sizeof(request)); + request.cmd = CMD_PG_GET_NAME; + request.id = id; + + memset(&response, 0, sizeof(response)); + + memset(&msg, 0, sizeof(msg)); + msg.mrq = MRQ_PG; + msg.tx.data = &request; + msg.tx.size = sizeof(request); + msg.rx.data = &response; + msg.rx.size = sizeof(response); + + err = tegra_bpmp_transfer(bpmp, &msg); + if (err < 0 || msg.rx.ret < 0) + return NULL; + + return kstrdup(response.get_name.name, GFP_KERNEL); +} + +static inline bool tegra_bpmp_powergate_is_powered(struct tegra_bpmp *bpmp, + unsigned int id) +{ + return tegra_bpmp_powergate_get_state(bpmp, id) != PG_STATE_OFF; +} + +static int tegra_powergate_power_on(struct generic_pm_domain *domain) +{ + struct tegra_powergate *powergate = to_tegra_powergate(domain); + struct tegra_bpmp *bpmp = powergate->bpmp; + + return tegra_bpmp_powergate_set_state(bpmp, powergate->id, + PG_STATE_ON); +} + +static int tegra_powergate_power_off(struct generic_pm_domain *domain) +{ + struct tegra_powergate *powergate = to_tegra_powergate(domain); + struct tegra_bpmp *bpmp = powergate->bpmp; + + return tegra_bpmp_powergate_set_state(bpmp, powergate->id, + PG_STATE_OFF); +} + +static struct tegra_powergate * +tegra_powergate_add(struct tegra_bpmp *bpmp, + const struct tegra_powergate_info *info) +{ + struct tegra_powergate *powergate; + bool off; + int err; + + off = !tegra_bpmp_powergate_is_powered(bpmp, info->id); + + powergate = devm_kzalloc(bpmp->dev, sizeof(*powergate), GFP_KERNEL); + if (!powergate) + return ERR_PTR(-ENOMEM); + + powergate->id = info->id; + powergate->bpmp = bpmp; + + powergate->genpd.name = kstrdup(info->name, GFP_KERNEL); + powergate->genpd.power_on = tegra_powergate_power_on; + powergate->genpd.power_off = tegra_powergate_power_off; + + err = pm_genpd_init(&powergate->genpd, NULL, off); + if (err < 0) { + kfree(powergate->genpd.name); + return ERR_PTR(err); + } + + return powergate; +} + +static void tegra_powergate_remove(struct tegra_powergate *powergate) +{ + struct generic_pm_domain *genpd = &powergate->genpd; + struct tegra_bpmp *bpmp = powergate->bpmp; + int err; + + err = pm_genpd_remove(genpd); + if (err < 0) + dev_err(bpmp->dev, "failed to remove power domain %s: %d\n", + genpd->name, err); + + kfree(genpd->name); +} + +static int +tegra_bpmp_probe_powergates(struct tegra_bpmp *bpmp, + struct tegra_powergate_info **powergatesp) +{ + struct tegra_powergate_info *powergates; + unsigned int max_id, id, count = 0; + unsigned int num_holes = 0; + int err; + + err = tegra_bpmp_powergate_get_max_id(bpmp); + if (err < 0) + return err; + + max_id = err; + + dev_dbg(bpmp->dev, "maximum powergate ID: %u\n", max_id); + + powergates = kcalloc(max_id + 1, sizeof(*powergates), GFP_KERNEL); + if (!powergates) + return -ENOMEM; + + for (id = 0; id <= max_id; id++) { + struct tegra_powergate_info *info = &powergates[count]; + + info->name = tegra_bpmp_powergate_get_name(bpmp, id); + if (!info->name || info->name[0] == '\0') { + num_holes++; + continue; + } + + info->id = id; + count++; + } + + dev_dbg(bpmp->dev, "holes: %u\n", num_holes); + + *powergatesp = powergates; + + return count; +} + +static int tegra_bpmp_add_powergates(struct tegra_bpmp *bpmp, + struct tegra_powergate_info *powergates, + unsigned int count) +{ + struct genpd_onecell_data *genpd = &bpmp->genpd; + struct generic_pm_domain **domains; + struct tegra_powergate *powergate; + unsigned int i; + int err; + + domains = kcalloc(count, sizeof(*domains), GFP_KERNEL); + if (!domains) + return -ENOMEM; + + for (i = 0; i < count; i++) { + powergate = tegra_powergate_add(bpmp, &powergates[i]); + if (IS_ERR(powergate)) { + err = PTR_ERR(powergate); + goto remove; + } + + dev_dbg(bpmp->dev, "added power domain %s\n", + powergate->genpd.name); + domains[i] = &powergate->genpd; + } + + genpd->num_domains = count; + genpd->domains = domains; + + return 0; + +remove: + while (i--) { + powergate = to_tegra_powergate(domains[i]); + tegra_powergate_remove(powergate); + } + + kfree(domains); + return err; +} + +static void tegra_bpmp_remove_powergates(struct tegra_bpmp *bpmp) +{ + struct genpd_onecell_data *genpd = &bpmp->genpd; + unsigned int i = genpd->num_domains; + struct tegra_powergate *powergate; + + while (i--) { + dev_dbg(bpmp->dev, "removing power domain %s\n", + genpd->domains[i]->name); + powergate = to_tegra_powergate(genpd->domains[i]); + tegra_powergate_remove(powergate); + } +} + +static struct generic_pm_domain * +tegra_powergate_xlate(struct of_phandle_args *spec, void *data) +{ + struct generic_pm_domain *domain = ERR_PTR(-ENOENT); + struct genpd_onecell_data *genpd = data; + unsigned int i; + + for (i = 0; i < genpd->num_domains; i++) { + struct tegra_powergate *powergate; + + powergate = to_tegra_powergate(genpd->domains[i]); + if (powergate->id == spec->args[0]) { + domain = &powergate->genpd; + break; + } + } + + return domain; +} + +int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp) +{ + struct device_node *np = bpmp->dev->of_node; + struct tegra_powergate_info *powergates; + struct device *dev = bpmp->dev; + unsigned int count, i; + int err; + + err = tegra_bpmp_probe_powergates(bpmp, &powergates); + if (err < 0) + return err; + + count = err; + + dev_dbg(dev, "%u power domains probed\n", count); + + err = tegra_bpmp_add_powergates(bpmp, powergates, count); + if (err < 0) + goto free; + + bpmp->genpd.xlate = tegra_powergate_xlate; + + err = of_genpd_add_provider_onecell(np, &bpmp->genpd); + if (err < 0) { + dev_err(dev, "failed to add power domain provider: %d\n", err); + tegra_bpmp_remove_powergates(bpmp); + } + +free: + for (i = 0; i < count; i++) + kfree(powergates[i].name); + + kfree(powergates); + return err; +} diff --git a/drivers/pmdomain/ti/Makefile b/drivers/pmdomain/ti/Makefile new file mode 100644 index 000000000000..69580afbb436 --- /dev/null +++ b/drivers/pmdomain/ti/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_ARCH_OMAP2PLUS) += omap_prm.o +obj-$(CONFIG_TI_SCI_PM_DOMAINS) += ti_sci_pm_domains.o diff --git a/drivers/pmdomain/ti/omap_prm.c b/drivers/pmdomain/ti/omap_prm.c new file mode 100644 index 000000000000..c2feae3a634c --- /dev/null +++ b/drivers/pmdomain/ti/omap_prm.c @@ -0,0 +1,989 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP2+ PRM driver + * + * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/ + * Tero Kristo + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +enum omap_prm_domain_mode { + OMAP_PRMD_OFF, + OMAP_PRMD_RETENTION, + OMAP_PRMD_ON_INACTIVE, + OMAP_PRMD_ON_ACTIVE, +}; + +struct omap_prm_domain_map { + unsigned int usable_modes; /* Mask of hardware supported modes */ + unsigned long statechange:1; /* Optional low-power state change */ + unsigned long logicretstate:1; /* Optional logic off mode */ +}; + +struct omap_prm_domain { + struct device *dev; + struct omap_prm *prm; + struct generic_pm_domain pd; + u16 pwrstctrl; + u16 pwrstst; + const struct omap_prm_domain_map *cap; + u32 pwrstctrl_saved; + unsigned int uses_pm_clk:1; +}; + +struct omap_rst_map { + s8 rst; + s8 st; +}; + +struct omap_prm_data { + u32 base; + const char *name; + const char *clkdm_name; + u16 pwrstctrl; + u16 pwrstst; + const struct omap_prm_domain_map *dmap; + u16 rstctrl; + u16 rstst; + const struct omap_rst_map *rstmap; + u8 flags; +}; + +struct omap_prm { + const struct omap_prm_data *data; + void __iomem *base; + struct omap_prm_domain *prmd; +}; + +struct omap_reset_data { + struct reset_controller_dev rcdev; + struct omap_prm *prm; + u32 mask; + spinlock_t lock; + struct clockdomain *clkdm; + struct device *dev; +}; + +#define genpd_to_prm_domain(gpd) container_of(gpd, struct omap_prm_domain, pd) +#define to_omap_reset_data(p) container_of((p), struct omap_reset_data, rcdev) + +#define OMAP_MAX_RESETS 8 +#define OMAP_RESET_MAX_WAIT 10000 + +#define OMAP_PRM_HAS_RSTCTRL BIT(0) +#define OMAP_PRM_HAS_RSTST BIT(1) +#define OMAP_PRM_HAS_NO_CLKDM BIT(2) +#define OMAP_PRM_RET_WHEN_IDLE BIT(3) + +#define OMAP_PRM_HAS_RESETS (OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_RSTST) + +#define PRM_STATE_MAX_WAIT 10000 +#define PRM_LOGICRETSTATE BIT(2) +#define PRM_LOWPOWERSTATECHANGE BIT(4) +#define PRM_POWERSTATE_MASK OMAP_PRMD_ON_ACTIVE + +#define PRM_ST_INTRANSITION BIT(20) + +static const struct omap_prm_domain_map omap_prm_all = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | + BIT(OMAP_PRMD_RETENTION) | BIT(OMAP_PRMD_OFF), + .statechange = 1, + .logicretstate = 1, +}; + +static const struct omap_prm_domain_map omap_prm_noinact = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION) | + BIT(OMAP_PRMD_OFF), + .statechange = 1, + .logicretstate = 1, +}; + +static const struct omap_prm_domain_map omap_prm_nooff = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_ON_INACTIVE) | + BIT(OMAP_PRMD_RETENTION), + .statechange = 1, + .logicretstate = 1, +}; + +static const struct omap_prm_domain_map omap_prm_onoff_noauto = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_OFF), + .statechange = 1, +}; + +static const struct omap_prm_domain_map omap_prm_alwon = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE), +}; + +static const struct omap_prm_domain_map omap_prm_reton = { + .usable_modes = BIT(OMAP_PRMD_ON_ACTIVE) | BIT(OMAP_PRMD_RETENTION), + .statechange = 1, + .logicretstate = 1, +}; + +static const struct omap_rst_map rst_map_0[] = { + { .rst = 0, .st = 0 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map rst_map_01[] = { + { .rst = 0, .st = 0 }, + { .rst = 1, .st = 1 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map rst_map_012[] = { + { .rst = 0, .st = 0 }, + { .rst = 1, .st = 1 }, + { .rst = 2, .st = 2 }, + { .rst = -1 }, +}; + +static const struct omap_prm_data omap4_prm_data[] = { + { + .name = "mpu", .base = 0x4a306300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, + }, + { + .name = "tesla", .base = 0x4a306400, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "abe", .base = 0x4a306500, + .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_all, + }, + { + .name = "always_on_core", .base = 0x4a306600, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "core", .base = 0x4a306700, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, + .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ducati", + .rstmap = rst_map_012, + .flags = OMAP_PRM_RET_WHEN_IDLE, + }, + { + .name = "ivahd", .base = 0x4a306f00, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 + }, + { + .name = "cam", .base = 0x4a307000, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "dss", .base = 0x4a307100, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact + }, + { + .name = "gfx", .base = 0x4a307200, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "l3init", .base = 0x4a307300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton + }, + { + .name = "l4per", .base = 0x4a307400, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, + .flags = OMAP_PRM_RET_WHEN_IDLE, + }, + { + .name = "cefuse", .base = 0x4a307600, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "wkup", .base = 0x4a307700, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon + }, + { + .name = "emu", .base = 0x4a307900, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "device", .base = 0x4a307b00, + .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, + .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM + }, + { }, +}; + +static const struct omap_prm_data omap5_prm_data[] = { + { + .name = "mpu", .base = 0x4ae06300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, + }, + { + .name = "dsp", .base = 0x4ae06400, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "abe", .base = 0x4ae06500, + .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_nooff, + }, + { + .name = "coreaon", .base = 0x4ae06600, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon + }, + { + .name = "core", .base = 0x4ae06700, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, + .rstctrl = 0x210, .rstst = 0x214, .clkdm_name = "ipu", + .rstmap = rst_map_012 + }, + { + .name = "iva", .base = 0x4ae07200, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012 + }, + { + .name = "cam", .base = 0x4ae07300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "dss", .base = 0x4ae07400, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact + }, + { + .name = "gpu", .base = 0x4ae07500, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "l3init", .base = 0x4ae07600, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton + }, + { + .name = "custefuse", .base = 0x4ae07700, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "wkupaon", .base = 0x4ae07800, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon + }, + { + .name = "emu", .base = 0x4ae07a00, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto + }, + { + .name = "device", .base = 0x4ae07c00, + .rstctrl = 0x0, .rstst = 0x4, .rstmap = rst_map_01, + .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM + }, + { }, +}; + +static const struct omap_prm_data dra7_prm_data[] = { + { + .name = "mpu", .base = 0x4ae06300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_reton, + }, + { + .name = "dsp1", .base = 0x4ae06400, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01, + }, + { + .name = "ipu", .base = 0x4ae06500, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012, + .clkdm_name = "ipu1" + }, + { + .name = "coreaon", .base = 0x4ae06628, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "core", .base = 0x4ae06700, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + .rstctrl = 0x210, .rstst = 0x214, .rstmap = rst_map_012, + .clkdm_name = "ipu2" + }, + { + .name = "iva", .base = 0x4ae06f00, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_012, + }, + { + .name = "cam", .base = 0x4ae07000, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "dss", .base = 0x4ae07100, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "gpu", .base = 0x4ae07200, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "l3init", .base = 0x4ae07300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01, + .clkdm_name = "pcie" + }, + { + .name = "l4per", .base = 0x4ae07400, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "custefuse", .base = 0x4ae07600, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "wkupaon", .base = 0x4ae07724, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "emu", .base = 0x4ae07900, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "dsp2", .base = 0x4ae07b00, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "eve1", .base = 0x4ae07b40, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "eve2", .base = 0x4ae07b80, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "eve3", .base = 0x4ae07bc0, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "eve4", .base = 0x4ae07c00, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_01 + }, + { + .name = "rtc", .base = 0x4ae07c60, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "vpe", .base = 0x4ae07c80, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { }, +}; + +static const struct omap_rst_map am3_per_rst_map[] = { + { .rst = 1 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map am3_wkup_rst_map[] = { + { .rst = 3, .st = 5 }, + { .rst = -1 }, +}; + +static const struct omap_prm_data am3_prm_data[] = { + { + .name = "per", .base = 0x44e00c00, + .pwrstctrl = 0xc, .pwrstst = 0x8, .dmap = &omap_prm_noinact, + .rstctrl = 0x0, .rstmap = am3_per_rst_map, + .flags = OMAP_PRM_HAS_RSTCTRL, .clkdm_name = "pruss_ocp" + }, + { + .name = "wkup", .base = 0x44e00d00, + .pwrstctrl = 0x4, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + .rstctrl = 0x0, .rstst = 0xc, .rstmap = am3_wkup_rst_map, + .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM + }, + { + .name = "mpu", .base = 0x44e00e00, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + }, + { + .name = "device", .base = 0x44e00f00, + .rstctrl = 0x0, .rstst = 0x8, .rstmap = rst_map_01, + .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM + }, + { + .name = "rtc", .base = 0x44e01000, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "gfx", .base = 0x44e01100, + .pwrstctrl = 0, .pwrstst = 0x10, .dmap = &omap_prm_noinact, + .rstctrl = 0x4, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", + }, + { + .name = "cefuse", .base = 0x44e01200, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { }, +}; + +static const struct omap_rst_map am4_per_rst_map[] = { + { .rst = 1, .st = 0 }, + { .rst = -1 }, +}; + +static const struct omap_rst_map am4_device_rst_map[] = { + { .rst = 0, .st = 1 }, + { .rst = 1, .st = 0 }, + { .rst = -1 }, +}; + +static const struct omap_prm_data am4_prm_data[] = { + { + .name = "mpu", .base = 0x44df0300, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + }, + { + .name = "gfx", .base = 0x44df0400, + .pwrstctrl = 0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = rst_map_0, .clkdm_name = "gfx_l3", + }, + { + .name = "rtc", .base = 0x44df0500, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "tamper", .base = 0x44df0600, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + }, + { + .name = "cefuse", .base = 0x44df0700, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_onoff_noauto, + }, + { + .name = "per", .base = 0x44df0800, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_noinact, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = am4_per_rst_map, + .clkdm_name = "pruss_ocp" + }, + { + .name = "wkup", .base = 0x44df2000, + .pwrstctrl = 0x0, .pwrstst = 0x4, .dmap = &omap_prm_alwon, + .rstctrl = 0x10, .rstst = 0x14, .rstmap = am3_wkup_rst_map, + .flags = OMAP_PRM_HAS_NO_CLKDM + }, + { + .name = "device", .base = 0x44df4000, + .rstctrl = 0x0, .rstst = 0x4, .rstmap = am4_device_rst_map, + .flags = OMAP_PRM_HAS_RSTCTRL | OMAP_PRM_HAS_NO_CLKDM + }, + { }, +}; + +static const struct of_device_id omap_prm_id_table[] = { + { .compatible = "ti,omap4-prm-inst", .data = omap4_prm_data }, + { .compatible = "ti,omap5-prm-inst", .data = omap5_prm_data }, + { .compatible = "ti,dra7-prm-inst", .data = dra7_prm_data }, + { .compatible = "ti,am3-prm-inst", .data = am3_prm_data }, + { .compatible = "ti,am4-prm-inst", .data = am4_prm_data }, + { }, +}; + +#ifdef DEBUG +static void omap_prm_domain_show_state(struct omap_prm_domain *prmd, + const char *desc) +{ + dev_dbg(prmd->dev, "%s %s: %08x/%08x\n", + prmd->pd.name, desc, + readl_relaxed(prmd->prm->base + prmd->pwrstctrl), + readl_relaxed(prmd->prm->base + prmd->pwrstst)); +} +#else +static inline void omap_prm_domain_show_state(struct omap_prm_domain *prmd, + const char *desc) +{ +} +#endif + +static int omap_prm_domain_power_on(struct generic_pm_domain *domain) +{ + struct omap_prm_domain *prmd; + int ret; + u32 v, mode; + + prmd = genpd_to_prm_domain(domain); + if (!prmd->cap) + return 0; + + omap_prm_domain_show_state(prmd, "on: previous state"); + + if (prmd->pwrstctrl_saved) + v = prmd->pwrstctrl_saved; + else + v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); + + if (prmd->prm->data->flags & OMAP_PRM_RET_WHEN_IDLE) + mode = OMAP_PRMD_RETENTION; + else + mode = OMAP_PRMD_ON_ACTIVE; + + writel_relaxed((v & ~PRM_POWERSTATE_MASK) | mode, + prmd->prm->base + prmd->pwrstctrl); + + /* wait for the transition bit to get cleared */ + ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, + v, !(v & PRM_ST_INTRANSITION), 1, + PRM_STATE_MAX_WAIT); + if (ret) + dev_err(prmd->dev, "%s: %s timed out\n", + prmd->pd.name, __func__); + + omap_prm_domain_show_state(prmd, "on: new state"); + + return ret; +} + +/* No need to check for holes in the mask for the lowest mode */ +static int omap_prm_domain_find_lowest(struct omap_prm_domain *prmd) +{ + return __ffs(prmd->cap->usable_modes); +} + +static int omap_prm_domain_power_off(struct generic_pm_domain *domain) +{ + struct omap_prm_domain *prmd; + int ret; + u32 v; + + prmd = genpd_to_prm_domain(domain); + if (!prmd->cap) + return 0; + + omap_prm_domain_show_state(prmd, "off: previous state"); + + v = readl_relaxed(prmd->prm->base + prmd->pwrstctrl); + prmd->pwrstctrl_saved = v; + + v &= ~PRM_POWERSTATE_MASK; + v |= omap_prm_domain_find_lowest(prmd); + + if (prmd->cap->statechange) + v |= PRM_LOWPOWERSTATECHANGE; + if (prmd->cap->logicretstate) + v &= ~PRM_LOGICRETSTATE; + else + v |= PRM_LOGICRETSTATE; + + writel_relaxed(v, prmd->prm->base + prmd->pwrstctrl); + + /* wait for the transition bit to get cleared */ + ret = readl_relaxed_poll_timeout(prmd->prm->base + prmd->pwrstst, + v, !(v & PRM_ST_INTRANSITION), 1, + PRM_STATE_MAX_WAIT); + if (ret) + dev_warn(prmd->dev, "%s: %s timed out\n", + __func__, prmd->pd.name); + + omap_prm_domain_show_state(prmd, "off: new state"); + + return 0; +} + +/* + * Note that ti-sysc already manages the module clocks separately so + * no need to manage those. Interconnect instances need clocks managed + * for simple-pm-bus. + */ +static int omap_prm_domain_attach_clock(struct device *dev, + struct omap_prm_domain *prmd) +{ + struct device_node *np = dev->of_node; + int error; + + if (!of_device_is_compatible(np, "simple-pm-bus")) + return 0; + + if (!of_property_read_bool(np, "clocks")) + return 0; + + error = pm_clk_create(dev); + if (error) + return error; + + error = of_pm_clk_add_clks(dev); + if (error < 0) { + pm_clk_destroy(dev); + return error; + } + + prmd->uses_pm_clk = 1; + + return 0; +} + +static int omap_prm_domain_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct generic_pm_domain_data *genpd_data; + struct of_phandle_args pd_args; + struct omap_prm_domain *prmd; + struct device_node *np; + int ret; + + prmd = genpd_to_prm_domain(domain); + np = dev->of_node; + + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", 0, &pd_args); + if (ret < 0) + return ret; + + if (pd_args.args_count != 0) + dev_warn(dev, "%s: unusupported #power-domain-cells: %i\n", + prmd->pd.name, pd_args.args_count); + + genpd_data = dev_gpd_data(dev); + genpd_data->data = NULL; + + ret = omap_prm_domain_attach_clock(dev, prmd); + if (ret) + return ret; + + return 0; +} + +static void omap_prm_domain_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct generic_pm_domain_data *genpd_data; + struct omap_prm_domain *prmd; + + prmd = genpd_to_prm_domain(domain); + if (prmd->uses_pm_clk) + pm_clk_destroy(dev); + genpd_data = dev_gpd_data(dev); + genpd_data->data = NULL; +} + +static int omap_prm_domain_init(struct device *dev, struct omap_prm *prm) +{ + struct omap_prm_domain *prmd; + struct device_node *np = dev->of_node; + const struct omap_prm_data *data; + const char *name; + int error; + + if (!of_property_present(dev->of_node, "#power-domain-cells")) + return 0; + + of_node_put(dev->of_node); + + prmd = devm_kzalloc(dev, sizeof(*prmd), GFP_KERNEL); + if (!prmd) + return -ENOMEM; + + data = prm->data; + name = devm_kasprintf(dev, GFP_KERNEL, "prm_%s", + data->name); + + prmd->dev = dev; + prmd->prm = prm; + prmd->cap = prmd->prm->data->dmap; + prmd->pwrstctrl = prmd->prm->data->pwrstctrl; + prmd->pwrstst = prmd->prm->data->pwrstst; + + prmd->pd.name = name; + prmd->pd.power_on = omap_prm_domain_power_on; + prmd->pd.power_off = omap_prm_domain_power_off; + prmd->pd.attach_dev = omap_prm_domain_attach_dev; + prmd->pd.detach_dev = omap_prm_domain_detach_dev; + prmd->pd.flags = GENPD_FLAG_PM_CLK; + + pm_genpd_init(&prmd->pd, NULL, true); + error = of_genpd_add_provider_simple(np, &prmd->pd); + if (error) + pm_genpd_remove(&prmd->pd); + else + prm->prmd = prmd; + + return error; +} + +static bool _is_valid_reset(struct omap_reset_data *reset, unsigned long id) +{ + if (reset->mask & BIT(id)) + return true; + + return false; +} + +static int omap_reset_get_st_bit(struct omap_reset_data *reset, + unsigned long id) +{ + const struct omap_rst_map *map = reset->prm->data->rstmap; + + while (map->rst >= 0) { + if (map->rst == id) + return map->st; + + map++; + } + + return id; +} + +static int omap_reset_status(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + u32 v; + int st_bit = omap_reset_get_st_bit(reset, id); + bool has_rstst = reset->prm->data->rstst || + (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); + + /* Check if we have rstst */ + if (!has_rstst) + return -ENOTSUPP; + + /* Check if hw reset line is asserted */ + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + if (v & BIT(id)) + return 1; + + /* + * Check reset status, high value means reset sequence has been + * completed successfully so we can return 0 here (reset deasserted) + */ + v = readl_relaxed(reset->prm->base + reset->prm->data->rstst); + v >>= st_bit; + v &= 1; + + return !v; +} + +static int omap_reset_assert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + u32 v; + unsigned long flags; + + /* assert the reset control line */ + spin_lock_irqsave(&reset->lock, flags); + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + v |= 1 << id; + writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); + spin_unlock_irqrestore(&reset->lock, flags); + + return 0; +} + +static int omap_reset_deassert(struct reset_controller_dev *rcdev, + unsigned long id) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + u32 v; + int st_bit; + bool has_rstst; + unsigned long flags; + struct ti_prm_platform_data *pdata = dev_get_platdata(reset->dev); + int ret = 0; + + /* Nothing to do if the reset is already deasserted */ + if (!omap_reset_status(rcdev, id)) + return 0; + + has_rstst = reset->prm->data->rstst || + (reset->prm->data->flags & OMAP_PRM_HAS_RSTST); + + if (has_rstst) { + st_bit = omap_reset_get_st_bit(reset, id); + + /* Clear the reset status by writing 1 to the status bit */ + v = 1 << st_bit; + writel_relaxed(v, reset->prm->base + reset->prm->data->rstst); + } + + if (reset->clkdm) + pdata->clkdm_deny_idle(reset->clkdm); + + /* de-assert the reset control line */ + spin_lock_irqsave(&reset->lock, flags); + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + v &= ~(1 << id); + writel_relaxed(v, reset->prm->base + reset->prm->data->rstctrl); + spin_unlock_irqrestore(&reset->lock, flags); + + /* wait for the reset bit to clear */ + ret = readl_relaxed_poll_timeout_atomic(reset->prm->base + + reset->prm->data->rstctrl, + v, !(v & BIT(id)), 1, + OMAP_RESET_MAX_WAIT); + if (ret) + pr_err("%s: timedout waiting for %s:%lu\n", __func__, + reset->prm->data->name, id); + + /* wait for the status to be set */ + if (has_rstst) { + ret = readl_relaxed_poll_timeout_atomic(reset->prm->base + + reset->prm->data->rstst, + v, v & BIT(st_bit), 1, + OMAP_RESET_MAX_WAIT); + if (ret) + pr_err("%s: timedout waiting for %s:%lu\n", __func__, + reset->prm->data->name, id); + } + + if (reset->clkdm) + pdata->clkdm_allow_idle(reset->clkdm); + + return ret; +} + +static const struct reset_control_ops omap_reset_ops = { + .assert = omap_reset_assert, + .deassert = omap_reset_deassert, + .status = omap_reset_status, +}; + +static int omap_prm_reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct omap_reset_data *reset = to_omap_reset_data(rcdev); + + if (!_is_valid_reset(reset, reset_spec->args[0])) + return -EINVAL; + + return reset_spec->args[0]; +} + +static int omap_prm_reset_init(struct platform_device *pdev, + struct omap_prm *prm) +{ + struct omap_reset_data *reset; + const struct omap_rst_map *map; + struct ti_prm_platform_data *pdata = dev_get_platdata(&pdev->dev); + char buf[32]; + u32 v; + + /* + * Check if we have controllable resets. If either rstctrl is non-zero + * or OMAP_PRM_HAS_RSTCTRL flag is set, we have reset control register + * for the domain. + */ + if (!prm->data->rstctrl && !(prm->data->flags & OMAP_PRM_HAS_RSTCTRL)) + return 0; + + /* Check if we have the pdata callbacks in place */ + if (!pdata || !pdata->clkdm_lookup || !pdata->clkdm_deny_idle || + !pdata->clkdm_allow_idle) + return -EINVAL; + + map = prm->data->rstmap; + if (!map) + return -EINVAL; + + reset = devm_kzalloc(&pdev->dev, sizeof(*reset), GFP_KERNEL); + if (!reset) + return -ENOMEM; + + reset->rcdev.owner = THIS_MODULE; + reset->rcdev.ops = &omap_reset_ops; + reset->rcdev.of_node = pdev->dev.of_node; + reset->rcdev.nr_resets = OMAP_MAX_RESETS; + reset->rcdev.of_xlate = omap_prm_reset_xlate; + reset->rcdev.of_reset_n_cells = 1; + reset->dev = &pdev->dev; + spin_lock_init(&reset->lock); + + reset->prm = prm; + + sprintf(buf, "%s_clkdm", prm->data->clkdm_name ? prm->data->clkdm_name : + prm->data->name); + + if (!(prm->data->flags & OMAP_PRM_HAS_NO_CLKDM)) { + reset->clkdm = pdata->clkdm_lookup(buf); + if (!reset->clkdm) + return -EINVAL; + } + + while (map->rst >= 0) { + reset->mask |= BIT(map->rst); + map++; + } + + /* Quirk handling to assert rst_map_012 bits on reset and avoid errors */ + if (prm->data->rstmap == rst_map_012) { + v = readl_relaxed(reset->prm->base + reset->prm->data->rstctrl); + if ((v & reset->mask) != reset->mask) { + dev_dbg(&pdev->dev, "Asserting all resets: %08x\n", v); + writel_relaxed(reset->mask, reset->prm->base + + reset->prm->data->rstctrl); + } + } + + return devm_reset_controller_register(&pdev->dev, &reset->rcdev); +} + +static int omap_prm_probe(struct platform_device *pdev) +{ + struct resource *res; + const struct omap_prm_data *data; + struct omap_prm *prm; + int ret; + + data = of_device_get_match_data(&pdev->dev); + if (!data) + return -ENOTSUPP; + + prm = devm_kzalloc(&pdev->dev, sizeof(*prm), GFP_KERNEL); + if (!prm) + return -ENOMEM; + + prm->base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); + if (IS_ERR(prm->base)) + return PTR_ERR(prm->base); + + while (data->base != res->start) { + if (!data->base) + return -EINVAL; + data++; + } + + prm->data = data; + + ret = omap_prm_domain_init(&pdev->dev, prm); + if (ret) + return ret; + + ret = omap_prm_reset_init(pdev, prm); + if (ret) + goto err_domain; + + return 0; + +err_domain: + of_genpd_del_provider(pdev->dev.of_node); + pm_genpd_remove(&prm->prmd->pd); + + return ret; +} + +static struct platform_driver omap_prm_driver = { + .probe = omap_prm_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = omap_prm_id_table, + }, +}; +builtin_platform_driver(omap_prm_driver); diff --git a/drivers/pmdomain/ti/ti_sci_pm_domains.c b/drivers/pmdomain/ti/ti_sci_pm_domains.c new file mode 100644 index 000000000000..34645104fe45 --- /dev/null +++ b/drivers/pmdomain/ti/ti_sci_pm_domains.c @@ -0,0 +1,204 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * TI SCI Generic Power Domain Driver + * + * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/ + * J Keerthy + * Dave Gerlach + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * struct ti_sci_genpd_provider: holds common TI SCI genpd provider data + * @ti_sci: handle to TI SCI protocol driver that provides ops to + * communicate with system control processor. + * @dev: pointer to dev for the driver for devm allocs + * @pd_list: list of all the power domains on the device + * @data: onecell data for genpd core + */ +struct ti_sci_genpd_provider { + const struct ti_sci_handle *ti_sci; + struct device *dev; + struct list_head pd_list; + struct genpd_onecell_data data; +}; + +/** + * struct ti_sci_pm_domain: TI specific data needed for power domain + * @idx: index of the device that identifies it with the system + * control processor. + * @exclusive: Permissions for exclusive request or shared request of the + * device. + * @pd: generic_pm_domain for use with the genpd framework + * @node: link for the genpd list + * @parent: link to the parent TI SCI genpd provider + */ +struct ti_sci_pm_domain { + int idx; + u8 exclusive; + struct generic_pm_domain pd; + struct list_head node; + struct ti_sci_genpd_provider *parent; +}; + +#define genpd_to_ti_sci_pd(gpd) container_of(gpd, struct ti_sci_pm_domain, pd) + +/* + * ti_sci_pd_power_off(): genpd power down hook + * @domain: pointer to the powerdomain to power off + */ +static int ti_sci_pd_power_off(struct generic_pm_domain *domain) +{ + struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); + const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; + + return ti_sci->ops.dev_ops.put_device(ti_sci, pd->idx); +} + +/* + * ti_sci_pd_power_on(): genpd power up hook + * @domain: pointer to the powerdomain to power on + */ +static int ti_sci_pd_power_on(struct generic_pm_domain *domain) +{ + struct ti_sci_pm_domain *pd = genpd_to_ti_sci_pd(domain); + const struct ti_sci_handle *ti_sci = pd->parent->ti_sci; + + if (pd->exclusive) + return ti_sci->ops.dev_ops.get_device_exclusive(ti_sci, + pd->idx); + else + return ti_sci->ops.dev_ops.get_device(ti_sci, pd->idx); +} + +/* + * ti_sci_pd_xlate(): translation service for TI SCI genpds + * @genpdspec: DT identification data for the genpd + * @data: genpd core data for all the powerdomains on the device + */ +static struct generic_pm_domain *ti_sci_pd_xlate( + struct of_phandle_args *genpdspec, + void *data) +{ + struct genpd_onecell_data *genpd_data = data; + unsigned int idx = genpdspec->args[0]; + + if (genpdspec->args_count != 1 && genpdspec->args_count != 2) + return ERR_PTR(-EINVAL); + + if (idx >= genpd_data->num_domains) { + pr_err("%s: invalid domain index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + if (!genpd_data->domains[idx]) + return ERR_PTR(-ENOENT); + + genpd_to_ti_sci_pd(genpd_data->domains[idx])->exclusive = + genpdspec->args[1]; + + return genpd_data->domains[idx]; +} + +static const struct of_device_id ti_sci_pm_domain_matches[] = { + { .compatible = "ti,sci-pm-domain", }, + { }, +}; +MODULE_DEVICE_TABLE(of, ti_sci_pm_domain_matches); + +static int ti_sci_pm_domain_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct ti_sci_genpd_provider *pd_provider; + struct ti_sci_pm_domain *pd; + struct device_node *np; + struct of_phandle_args args; + int ret; + u32 max_id = 0; + int index; + + pd_provider = devm_kzalloc(dev, sizeof(*pd_provider), GFP_KERNEL); + if (!pd_provider) + return -ENOMEM; + + pd_provider->ti_sci = devm_ti_sci_get_handle(dev); + if (IS_ERR(pd_provider->ti_sci)) + return PTR_ERR(pd_provider->ti_sci); + + pd_provider->dev = dev; + + INIT_LIST_HEAD(&pd_provider->pd_list); + + /* Find highest device ID used for power domains */ + for_each_node_with_property(np, "power-domains") { + index = 0; + + while (1) { + ret = of_parse_phandle_with_args(np, "power-domains", + "#power-domain-cells", + index, &args); + if (ret) + break; + + if (args.args_count >= 1 && args.np == dev->of_node) { + if (args.args[0] > max_id) + max_id = args.args[0]; + + pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + pd->pd.name = devm_kasprintf(dev, GFP_KERNEL, + "pd:%d", + args.args[0]); + if (!pd->pd.name) + return -ENOMEM; + + pd->pd.power_off = ti_sci_pd_power_off; + pd->pd.power_on = ti_sci_pd_power_on; + pd->idx = args.args[0]; + pd->parent = pd_provider; + + pm_genpd_init(&pd->pd, NULL, true); + + list_add(&pd->node, &pd_provider->pd_list); + } + index++; + } + } + + pd_provider->data.domains = + devm_kcalloc(dev, max_id + 1, + sizeof(*pd_provider->data.domains), + GFP_KERNEL); + if (!pd_provider->data.domains) + return -ENOMEM; + + pd_provider->data.num_domains = max_id + 1; + pd_provider->data.xlate = ti_sci_pd_xlate; + + list_for_each_entry(pd, &pd_provider->pd_list, node) + pd_provider->data.domains[pd->idx] = &pd->pd; + + return of_genpd_add_provider_onecell(dev->of_node, &pd_provider->data); +} + +static struct platform_driver ti_sci_pm_domains_driver = { + .probe = ti_sci_pm_domain_probe, + .driver = { + .name = "ti_sci_pm_domains", + .of_match_table = ti_sci_pm_domain_matches, + }, +}; +module_platform_driver(ti_sci_pm_domains_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("TI System Control Interface (SCI) Power Domain driver"); +MODULE_AUTHOR("Dave Gerlach"); diff --git a/drivers/pmdomain/xilinx/Makefile b/drivers/pmdomain/xilinx/Makefile new file mode 100644 index 000000000000..a706ab699cfa --- /dev/null +++ b/drivers/pmdomain/xilinx/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += zynqmp-pm-domains.o diff --git a/drivers/pmdomain/xilinx/zynqmp-pm-domains.c b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c new file mode 100644 index 000000000000..69d03ad4cf1e --- /dev/null +++ b/drivers/pmdomain/xilinx/zynqmp-pm-domains.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ZynqMP Generic PM domain support + * + * Copyright (C) 2015-2019 Xilinx, Inc. + * + * Davorin Mista + * Jolly Shah + * Rajan Vaja + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#define ZYNQMP_NUM_DOMAINS (100) + +static int min_capability; + +/** + * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain + * @gpd: Generic power domain + * @node_id: PM node ID corresponding to device inside PM domain + * @requested: The PM node mapped to the PM domain has been requested + */ +struct zynqmp_pm_domain { + struct generic_pm_domain gpd; + u32 node_id; + bool requested; +}; + +#define to_zynqmp_pm_domain(pm_domain) \ + container_of(pm_domain, struct zynqmp_pm_domain, gpd) + +/** + * zynqmp_gpd_is_active_wakeup_path() - Check if device is in wakeup source + * path + * @dev: Device to check for wakeup source path + * @not_used: Data member (not required) + * + * This function is checks device's child hierarchy and checks if any device is + * set as wakeup source. + * + * Return: 1 if device is in wakeup source path else 0 + */ +static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used) +{ + int may_wakeup; + + may_wakeup = device_may_wakeup(dev); + if (may_wakeup) + return may_wakeup; + + return device_for_each_child(dev, NULL, + zynqmp_gpd_is_active_wakeup_path); +} + +/** + * zynqmp_gpd_power_on() - Power on PM domain + * @domain: Generic PM domain + * + * This function is called before devices inside a PM domain are resumed, to + * power on PM domain. + * + * Return: 0 on success, error code otherwise + */ +static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) +{ + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); + int ret; + + ret = zynqmp_pm_set_requirement(pd->node_id, + ZYNQMP_PM_CAPABILITY_ACCESS, + ZYNQMP_PM_MAX_QOS, + ZYNQMP_PM_REQUEST_ACK_BLOCKING); + if (ret) { + dev_err(&domain->dev, + "failed to set requirement to 0x%x for PM node id %d: %d\n", + ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id, ret); + return ret; + } + + dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n", + ZYNQMP_PM_CAPABILITY_ACCESS, pd->node_id); + + return 0; +} + +/** + * zynqmp_gpd_power_off() - Power off PM domain + * @domain: Generic PM domain + * + * This function is called after devices inside a PM domain are suspended, to + * power off PM domain. + * + * Return: 0 on success, error code otherwise + */ +static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) +{ + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); + int ret; + struct pm_domain_data *pdd, *tmp; + u32 capabilities = min_capability; + bool may_wakeup; + + /* If domain is already released there is nothing to be done */ + if (!pd->requested) { + dev_dbg(&domain->dev, "PM node id %d is already released\n", + pd->node_id); + return 0; + } + + list_for_each_entry_safe(pdd, tmp, &domain->dev_list, list_node) { + /* If device is in wakeup path, set capability to WAKEUP */ + may_wakeup = zynqmp_gpd_is_active_wakeup_path(pdd->dev, NULL); + if (may_wakeup) { + dev_dbg(pdd->dev, "device is in wakeup path in %s\n", + domain->name); + capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP; + break; + } + } + + ret = zynqmp_pm_set_requirement(pd->node_id, capabilities, 0, + ZYNQMP_PM_REQUEST_ACK_NO); + if (ret) { + dev_err(&domain->dev, + "failed to set requirement to 0x%x for PM node id %d: %d\n", + capabilities, pd->node_id, ret); + return ret; + } + + dev_dbg(&domain->dev, "set requirement to 0x%x for PM node id %d\n", + capabilities, pd->node_id); + + return 0; +} + +/** + * zynqmp_gpd_attach_dev() - Attach device to the PM domain + * @domain: Generic PM domain + * @dev: Device to attach + * + * Return: 0 on success, error code otherwise + */ +static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); + struct device_link *link; + int ret; + + link = device_link_add(dev, &domain->dev, DL_FLAG_SYNC_STATE_ONLY); + if (!link) + dev_dbg(&domain->dev, "failed to create device link for %s\n", + dev_name(dev)); + + /* If this is not the first device to attach there is nothing to do */ + if (domain->device_count) + return 0; + + ret = zynqmp_pm_request_node(pd->node_id, 0, 0, + ZYNQMP_PM_REQUEST_ACK_BLOCKING); + if (ret) { + dev_err(&domain->dev, "%s request failed for node %d: %d\n", + domain->name, pd->node_id, ret); + return ret; + } + + pd->requested = true; + + dev_dbg(&domain->dev, "%s requested PM node id %d\n", + dev_name(dev), pd->node_id); + + return 0; +} + +/** + * zynqmp_gpd_detach_dev() - Detach device from the PM domain + * @domain: Generic PM domain + * @dev: Device to detach + */ +static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, + struct device *dev) +{ + struct zynqmp_pm_domain *pd = to_zynqmp_pm_domain(domain); + int ret; + + /* If this is not the last device to detach there is nothing to do */ + if (domain->device_count) + return; + + ret = zynqmp_pm_release_node(pd->node_id); + if (ret) { + dev_err(&domain->dev, "failed to release PM node id %d: %d\n", + pd->node_id, ret); + return; + } + + pd->requested = false; + + dev_dbg(&domain->dev, "%s released PM node id %d\n", + dev_name(dev), pd->node_id); +} + +static struct generic_pm_domain *zynqmp_gpd_xlate + (struct of_phandle_args *genpdspec, void *data) +{ + struct genpd_onecell_data *genpd_data = data; + unsigned int i, idx = genpdspec->args[0]; + struct zynqmp_pm_domain *pd; + + pd = to_zynqmp_pm_domain(genpd_data->domains[0]); + + if (genpdspec->args_count != 1) + return ERR_PTR(-EINVAL); + + /* Check for existing pm domains */ + for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) { + if (pd[i].node_id == idx) + goto done; + } + + /* + * Add index in empty node_id of power domain list as no existing + * power domain found for current index. + */ + for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++) { + if (pd[i].node_id == 0) { + pd[i].node_id = idx; + break; + } + } + +done: + if (!genpd_data->domains[i] || i == ZYNQMP_NUM_DOMAINS) + return ERR_PTR(-ENOENT); + + return genpd_data->domains[i]; +} + +static int zynqmp_gpd_probe(struct platform_device *pdev) +{ + int i; + struct genpd_onecell_data *zynqmp_pd_data; + struct generic_pm_domain **domains; + struct zynqmp_pm_domain *pd; + struct device *dev = &pdev->dev; + + pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + + zynqmp_pd_data = devm_kzalloc(dev, sizeof(*zynqmp_pd_data), GFP_KERNEL); + if (!zynqmp_pd_data) + return -ENOMEM; + + zynqmp_pd_data->xlate = zynqmp_gpd_xlate; + + domains = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*domains), + GFP_KERNEL); + if (!domains) + return -ENOMEM; + + if (!of_device_is_compatible(dev->parent->of_node, + "xlnx,zynqmp-firmware")) + min_capability = ZYNQMP_PM_CAPABILITY_UNUSABLE; + + for (i = 0; i < ZYNQMP_NUM_DOMAINS; i++, pd++) { + pd->node_id = 0; + pd->gpd.name = kasprintf(GFP_KERNEL, "domain%d", i); + pd->gpd.power_off = zynqmp_gpd_power_off; + pd->gpd.power_on = zynqmp_gpd_power_on; + pd->gpd.attach_dev = zynqmp_gpd_attach_dev; + pd->gpd.detach_dev = zynqmp_gpd_detach_dev; + + domains[i] = &pd->gpd; + + /* Mark all PM domains as initially powered off */ + pm_genpd_init(&pd->gpd, NULL, true); + } + + zynqmp_pd_data->domains = domains; + zynqmp_pd_data->num_domains = ZYNQMP_NUM_DOMAINS; + of_genpd_add_provider_onecell(dev->parent->of_node, zynqmp_pd_data); + + return 0; +} + +static int zynqmp_gpd_remove(struct platform_device *pdev) +{ + of_genpd_del_provider(pdev->dev.parent->of_node); + + return 0; +} + +static void zynqmp_gpd_sync_state(struct device *dev) +{ + int ret; + + ret = zynqmp_pm_init_finalize(); + if (ret) + dev_warn(dev, "failed to release power management to firmware\n"); +} + +static struct platform_driver zynqmp_power_domain_driver = { + .driver = { + .name = "zynqmp_power_controller", + .sync_state = zynqmp_gpd_sync_state, + }, + .probe = zynqmp_gpd_probe, + .remove = zynqmp_gpd_remove, +}; +module_platform_driver(zynqmp_power_domain_driver); + +MODULE_ALIAS("platform:zynqmp_power_controller"); -- cgit v1.2.3 From 637f33a4fe864ac8636e22766d67210e801fcd0d Mon Sep 17 00:00:00 2001 From: Shubhrajyoti Datta Date: Thu, 31 Aug 2023 14:32:53 +0530 Subject: i2c: cadence: Fix the kernel-doc warnings This fixes the below warnings drivers/i2c/busses/i2c-cadence.c:221: warning: Function parameter or member 'rinfo' not described in 'cdns_i2c' Reviewed-by: Andi Shyti Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202308171510.bKHBcZQW-lkp@intel.com/ Signed-off-by: Shubhrajyoti Datta Signed-off-by: Wolfram Sang --- drivers/i2c/busses/i2c-cadence.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/i2c/busses/i2c-cadence.c b/drivers/i2c/busses/i2c-cadence.c index 9849f4502570..de3f58b60dce 100644 --- a/drivers/i2c/busses/i2c-cadence.c +++ b/drivers/i2c/busses/i2c-cadence.c @@ -182,6 +182,7 @@ enum cdns_i2c_slave_state { * @reset: Reset control for the device * @quirks: flag for broken hold bit usage in r1p10 * @ctrl_reg: Cached value of the control register. + * @rinfo: I2C GPIO recovery information * @ctrl_reg_diva_divb: value of fields DIV_A and DIV_B from CR register * @slave: Registered slave instance. * @dev_mode: I2C operating role(master/slave). -- cgit v1.2.3 From 3c44191dd76cf9c0cc49adaf34384cbd42ef8ad2 Mon Sep 17 00:00:00 2001 From: Vadim Fedorenko Date: Mon, 11 Sep 2023 13:28:14 -0700 Subject: ixgbe: fix timestamp configuration code The commit in fixes introduced flags to control the status of hardware configuration while processing packets. At the same time another structure is used to provide configuration of timestamper to user-space applications. The way it was coded makes this structures go out of sync easily. The repro is easy for 82599 chips: [root@hostname ~]# hwstamp_ctl -i eth0 -r 12 -t 1 current settings: tx_type 0 rx_filter 0 new settings: tx_type 1 rx_filter 12 The eth0 device is properly configured to timestamp any PTPv2 events. [root@hostname ~]# hwstamp_ctl -i eth0 -r 1 -t 1 current settings: tx_type 1 rx_filter 12 SIOCSHWTSTAMP failed: Numerical result out of range The requested time stamping mode is not supported by the hardware. The error is properly returned because HW doesn't support all packets timestamping. But the adapter->flags is cleared of timestamp flags even though no HW configuration was done. From that point no RX timestamps are received by user-space application. But configuration shows good values: [root@hostname ~]# hwstamp_ctl -i eth0 current settings: tx_type 1 rx_filter 12 Fix the issue by applying new flags only when the HW was actually configured. Fixes: a9763f3cb54c ("ixgbe: Update PTP to support X550EM_x devices") Signed-off-by: Vadim Fedorenko Reviewed-by: Simon Horman Tested-by: Pucha Himasekhar Reddy (A Contingent worker at Intel) Signed-off-by: Tony Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c index 0310af851086..9339edbd9082 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c @@ -979,6 +979,7 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, u32 tsync_tx_ctl = IXGBE_TSYNCTXCTL_ENABLED; u32 tsync_rx_ctl = IXGBE_TSYNCRXCTL_ENABLED; u32 tsync_rx_mtrl = PTP_EV_PORT << 16; + u32 aflags = adapter->flags; bool is_l2 = false; u32 regval; @@ -996,20 +997,20 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, case HWTSTAMP_FILTER_NONE: tsync_rx_ctl = 0; tsync_rx_mtrl = 0; - adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_SYNC: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_mtrl |= IXGBE_RXMTRL_V1_SYNC_MSG; - adapter->flags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ: tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_L4_V1; tsync_rx_mtrl |= IXGBE_RXMTRL_V1_DELAY_REQ_MSG; - adapter->flags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V2_EVENT: case HWTSTAMP_FILTER_PTP_V2_L2_EVENT: @@ -1023,8 +1024,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_EVENT_V2; is_l2 = true; config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT; - adapter->flags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); + aflags |= (IXGBE_FLAG_RX_HWTSTAMP_ENABLED | + IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); break; case HWTSTAMP_FILTER_PTP_V1_L4_EVENT: case HWTSTAMP_FILTER_NTP_ALL: @@ -1035,7 +1036,7 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, if (hw->mac.type >= ixgbe_mac_X550) { tsync_rx_ctl |= IXGBE_TSYNCRXCTL_TYPE_ALL; config->rx_filter = HWTSTAMP_FILTER_ALL; - adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; + aflags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; break; } fallthrough; @@ -1046,8 +1047,6 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, * Delay_Req messages and hardware does not support * timestamping all packets => return error */ - adapter->flags &= ~(IXGBE_FLAG_RX_HWTSTAMP_ENABLED | - IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER); config->rx_filter = HWTSTAMP_FILTER_NONE; return -ERANGE; } @@ -1079,8 +1078,8 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, IXGBE_TSYNCRXCTL_TYPE_ALL | IXGBE_TSYNCRXCTL_TSIP_UT_EN; config->rx_filter = HWTSTAMP_FILTER_ALL; - adapter->flags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; - adapter->flags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER; + aflags |= IXGBE_FLAG_RX_HWTSTAMP_ENABLED; + aflags &= ~IXGBE_FLAG_RX_HWTSTAMP_IN_REGISTER; is_l2 = true; break; default: @@ -1113,6 +1112,9 @@ static int ixgbe_ptp_set_timestamp_mode(struct ixgbe_adapter *adapter, IXGBE_WRITE_FLUSH(hw); + /* configure adapter flags only when HW is actually configured */ + adapter->flags = aflags; + /* clear TX/RX time stamp registers, just to be sure */ ixgbe_ptp_clear_tx_timestamp(adapter); IXGBE_READ_REG(hw, IXGBE_RXSTMPH); -- cgit v1.2.3 From bc6ed2fa24b14e40e1005488bbe11268ce7108fa Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Mon, 11 Sep 2023 13:28:49 -0700 Subject: igb: clean up in all error paths when enabling SR-IOV After commit 50f303496d92 ("igb: Enable SR-IOV after reinit"), removing the igb module could hang or crash (depending on the machine) when the module has been loaded with the max_vfs parameter set to some value != 0. In case of one test machine with a dual port 82580, this hang occurred: [ 232.480687] igb 0000:41:00.1: removed PHC on enp65s0f1 [ 233.093257] igb 0000:41:00.1: IOV Disabled [ 233.329969] pcieport 0000:40:01.0: AER: Multiple Uncorrected (Non-Fatal) err0 [ 233.340302] igb 0000:41:00.0: PCIe Bus Error: severity=Uncorrected (Non-Fata) [ 233.352248] igb 0000:41:00.0: device [8086:1516] error status/mask=00100000 [ 233.361088] igb 0000:41:00.0: [20] UnsupReq (First) [ 233.368183] igb 0000:41:00.0: AER: TLP Header: 40000001 0000040f cdbfc00c c [ 233.376846] igb 0000:41:00.1: PCIe Bus Error: severity=Uncorrected (Non-Fata) [ 233.388779] igb 0000:41:00.1: device [8086:1516] error status/mask=00100000 [ 233.397629] igb 0000:41:00.1: [20] UnsupReq (First) [ 233.404736] igb 0000:41:00.1: AER: TLP Header: 40000001 0000040f cdbfc00c c [ 233.538214] pci 0000:41:00.1: AER: can't recover (no error_detected callback) [ 233.538401] igb 0000:41:00.0: removed PHC on enp65s0f0 [ 233.546197] pcieport 0000:40:01.0: AER: device recovery failed [ 234.157244] igb 0000:41:00.0: IOV Disabled [ 371.619705] INFO: task irq/35-aerdrv:257 blocked for more than 122 seconds. [ 371.627489] Not tainted 6.4.0-dirty #2 [ 371.632257] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this. [ 371.641000] task:irq/35-aerdrv state:D stack:0 pid:257 ppid:2 f0 [ 371.650330] Call Trace: [ 371.653061] [ 371.655407] __schedule+0x20e/0x660 [ 371.659313] schedule+0x5a/0xd0 [ 371.662824] schedule_preempt_disabled+0x11/0x20 [ 371.667983] __mutex_lock.constprop.0+0x372/0x6c0 [ 371.673237] ? __pfx_aer_root_reset+0x10/0x10 [ 371.678105] report_error_detected+0x25/0x1c0 [ 371.682974] ? __pfx_report_normal_detected+0x10/0x10 [ 371.688618] pci_walk_bus+0x72/0x90 [ 371.692519] pcie_do_recovery+0xb2/0x330 [ 371.696899] aer_process_err_devices+0x117/0x170 [ 371.702055] aer_isr+0x1c0/0x1e0 [ 371.705661] ? __set_cpus_allowed_ptr+0x54/0xa0 [ 371.710723] ? __pfx_irq_thread_fn+0x10/0x10 [ 371.715496] irq_thread_fn+0x20/0x60 [ 371.719491] irq_thread+0xe6/0x1b0 [ 371.723291] ? __pfx_irq_thread_dtor+0x10/0x10 [ 371.728255] ? __pfx_irq_thread+0x10/0x10 [ 371.732731] kthread+0xe2/0x110 [ 371.736243] ? __pfx_kthread+0x10/0x10 [ 371.740430] ret_from_fork+0x2c/0x50 [ 371.744428] The reproducer was a simple script: #!/bin/sh for i in `seq 1 5`; do modprobe -rv igb modprobe -v igb max_vfs=1 sleep 1 modprobe -rv igb done It turned out that this could only be reproduce on 82580 (quad and dual-port), but not on 82576, i350 and i210. Further debugging showed that igb_enable_sriov()'s call to pci_enable_sriov() is failing, because dev->is_physfn is 0 on 82580. Prior to commit 50f303496d92 ("igb: Enable SR-IOV after reinit"), igb_enable_sriov() jumped into the "err_out" cleanup branch. After this commit it only returned the error code. So the cleanup didn't take place, and the incorrect VF setup in the igb_adapter structure fooled the igb driver into assuming that VFs have been set up where no VF actually existed. Fix this problem by cleaning up again if pci_enable_sriov() fails. Fixes: 50f303496d92 ("igb: Enable SR-IOV after reinit") Signed-off-by: Corinna Vinschen Reviewed-by: Akihiko Odaki Tested-by: Rafal Romanowski Signed-off-by: Tony Nguyen Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/igb/igb_main.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 13ba9c74bd84..76b34cee1da3 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -3827,8 +3827,11 @@ static int igb_enable_sriov(struct pci_dev *pdev, int num_vfs, bool reinit) } /* only call pci_enable_sriov() if no VFs are allocated already */ - if (!old_vfs) + if (!old_vfs) { err = pci_enable_sriov(pdev, adapter->vfs_allocated_count); + if (err) + goto err_out; + } goto out; -- cgit v1.2.3 From 450e749707bc1755f22b505d9cd942d4869dc535 Mon Sep 17 00:00:00 2001 From: Tim Chen Date: Thu, 7 Sep 2023 10:42:21 -0700 Subject: sched/fair: Fix SMT4 group_smt_balance handling For SMT4, any group with more than 2 tasks will be marked as group_smt_balance. Retain the behaviour of group_has_spare by marking the busiest group as the group which has the least number of idle_cpus. Also, handle rounding effect of adding (ncores_local + ncores_busy) when the local is fully idle and busy group imbalance is less than 2 tasks. Local group should try to pull at least 1 task in this case so imbalance should be set to 2 instead. Fixes: fee1759e4f04 ("sched/fair: Determine active load balance for SMT sched groups") Acked-by: Shrikanth Hegde Signed-off-by: Tim Chen Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Link: http://lkml.kernel.org/r/6cd1633036bb6b651af575c32c2a9608a106702c.camel@linux.intel.com --- kernel/sched/fair.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/kernel/sched/fair.c b/kernel/sched/fair.c index 33a2b6bba676..cb225921bbca 100644 --- a/kernel/sched/fair.c +++ b/kernel/sched/fair.c @@ -9580,7 +9580,7 @@ static inline long sibling_imbalance(struct lb_env *env, imbalance /= ncores_local + ncores_busiest; /* Take advantage of resource in an empty sched group */ - if (imbalance == 0 && local->sum_nr_running == 0 && + if (imbalance <= 1 && local->sum_nr_running == 0 && busiest->sum_nr_running > 1) imbalance = 2; @@ -9768,6 +9768,15 @@ static bool update_sd_pick_busiest(struct lb_env *env, break; case group_smt_balance: + /* + * Check if we have spare CPUs on either SMT group to + * choose has spare or fully busy handling. + */ + if (sgs->idle_cpus != 0 || busiest->idle_cpus != 0) + goto has_spare; + + fallthrough; + case group_fully_busy: /* * Select the fully busy group with highest avg_load. In @@ -9807,6 +9816,7 @@ static bool update_sd_pick_busiest(struct lb_env *env, else return true; } +has_spare: /* * Select not overloaded group with lowest number of idle cpus -- cgit v1.2.3 From 108af4b4bd3813610701379a58538e3339b162e4 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Mon, 14 Aug 2023 20:57:47 -0700 Subject: x86/sched: Restore the SD_ASYM_PACKING flag in the DIE domain Commit 8f2d6c41e5a6 ("x86/sched: Rewrite topology setup") dropped the SD_ASYM_PACKING flag in the DIE domain added in commit 044f0e27dec6 ("x86/sched: Add the SD_ASYM_PACKING flag to the die domain of hybrid processors"). Restore it on hybrid processors. The die-level domain does not depend on any build configuration and now x86_sched_itmt_flags() is always needed. Remove the build dependency on CONFIG_SCHED_[SMT|CLUSTER|MC]. Fixes: 8f2d6c41e5a6 ("x86/sched: Rewrite topology setup") Signed-off-by: Ricardo Neri Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Reviewed-by: Chen Yu Tested-by: Caleb Callaway Link: https://lkml.kernel.org/r/20230815035747.11529-1-ricardo.neri-calderon@linux.intel.com --- arch/x86/kernel/smpboot.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index d40ed3a7dc23..266d05e22ac3 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c @@ -579,7 +579,6 @@ static bool match_llc(struct cpuinfo_x86 *c, struct cpuinfo_x86 *o) } -#if defined(CONFIG_SCHED_SMT) || defined(CONFIG_SCHED_CLUSTER) || defined(CONFIG_SCHED_MC) static inline int x86_sched_itmt_flags(void) { return sysctl_sched_itmt_enabled ? SD_ASYM_PACKING : 0; @@ -603,7 +602,14 @@ static int x86_cluster_flags(void) return cpu_cluster_flags() | x86_sched_itmt_flags(); } #endif -#endif + +static int x86_die_flags(void) +{ + if (cpu_feature_enabled(X86_FEATURE_HYBRID_CPU)) + return x86_sched_itmt_flags(); + + return 0; +} /* * Set if a package/die has multiple NUMA nodes inside. @@ -640,7 +646,7 @@ static void __init build_sched_topology(void) */ if (!x86_has_numa_in_package) { x86_topology[i++] = (struct sched_domain_topology_level){ - cpu_cpu_mask, SD_INIT_NAME(DIE) + cpu_cpu_mask, x86_die_flags, SD_INIT_NAME(DIE) }; } -- cgit v1.2.3 From e4e14095cc68a2efefba6f77d95efe1137e751d4 Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 12 Sep 2023 23:28:25 +0900 Subject: ksmbd: remove unneeded mark_inode_dirty in set_info_sec() mark_inode_dirty will be called in notify_change(). This patch remove unneeded mark_inode_dirty in set_info_sec(). Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smbacl.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/smb/server/smbacl.c b/fs/smb/server/smbacl.c index e5e438bf5499..6c0305be895e 100644 --- a/fs/smb/server/smbacl.c +++ b/fs/smb/server/smbacl.c @@ -1420,7 +1420,6 @@ int set_info_sec(struct ksmbd_conn *conn, struct ksmbd_tree_connect *tcon, out: posix_acl_release(fattr.cf_acls); posix_acl_release(fattr.cf_dacls); - mark_inode_dirty(inode); return rc; } -- cgit v1.2.3 From 59d8d24f4610333560cf2e8fe3f44cafe30322eb Mon Sep 17 00:00:00 2001 From: Namjae Jeon Date: Tue, 12 Sep 2023 23:29:10 +0900 Subject: ksmbd: fix passing freed memory 'aux_payload_buf' The patch e2b76ab8b5c9: "ksmbd: add support for read compound" leads to the following Smatch static checker warning: fs/smb/server/smb2pdu.c:6329 smb2_read() warn: passing freed memory 'aux_payload_buf' It doesn't matter that we're passing a freed variable because nbytes is zero. This patch set "aux_payload_buf = NULL" to make smatch silence. Fixes: e2b76ab8b5c9 ("ksmbd: add support for read compound") Reported-by: Dan Carpenter Signed-off-by: Namjae Jeon Signed-off-by: Steve French --- fs/smb/server/smb2pdu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c index 749660110878..544022dd6d20 100644 --- a/fs/smb/server/smb2pdu.c +++ b/fs/smb/server/smb2pdu.c @@ -6312,7 +6312,7 @@ int smb2_read(struct ksmbd_work *work) aux_payload_buf, nbytes); kvfree(aux_payload_buf); - + aux_payload_buf = NULL; nbytes = 0; if (remain_bytes < 0) { err = (int)remain_bytes; -- cgit v1.2.3 From 71996bb835aed58c7ec4967be1d05190a27339ec Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Wed, 13 Sep 2023 08:56:10 -0700 Subject: scsi: pm80xx: Use phy-specific SAS address when sending PHY_START command Some cards have more than one SAS address. Using an incorrect address causes communication issues with some devices like expanders. Closes: https://lore.kernel.org/linux-kernel/A57AEA84-5CA0-403E-8053-106033C73C70@fb.com/ Signed-off-by: Michal Grzedzicki Link: https://lore.kernel.org/r/20230913155611.3183612-1-mge@meta.com Acked-by: Jack Wang Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm8001_hwi.c | 2 +- drivers/scsi/pm8001/pm80xx_hwi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 33053db5a713..90069c7b1642 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -4180,7 +4180,7 @@ pm8001_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) payload.sas_identify.dev_type = SAS_END_DEVICE; payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL; memcpy(payload.sas_identify.sas_addr, - pm8001_ha->sas_addr, SAS_ADDR_SIZE); + &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE); payload.sas_identify.phy_id = phy_id; return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index f6857632dc7c..1b2c40b1381c 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -4671,7 +4671,7 @@ pm80xx_chip_phy_start_req(struct pm8001_hba_info *pm8001_ha, u8 phy_id) payload.sas_identify.dev_type = SAS_END_DEVICE; payload.sas_identify.initiator_bits = SAS_PROTOCOL_ALL; memcpy(payload.sas_identify.sas_addr, - &pm8001_ha->sas_addr, SAS_ADDR_SIZE); + &pm8001_ha->phy[phy_id].dev_sas_addr, SAS_ADDR_SIZE); payload.sas_identify.phy_id = phy_id; return pm8001_mpi_build_cmd(pm8001_ha, 0, opcode, &payload, -- cgit v1.2.3 From c13e7331745852d0dd7c35eabbe181cbd5b01172 Mon Sep 17 00:00:00 2001 From: Michal Grzedzicki Date: Mon, 11 Sep 2023 10:03:40 -0700 Subject: scsi: pm80xx: Avoid leaking tags when processing OPC_INB_SET_CONTROLLER_CONFIG command Tags allocated for OPC_INB_SET_CONTROLLER_CONFIG command need to be freed when we receive the response. Signed-off-by: Michal Grzedzicki Link: https://lore.kernel.org/r/20230911170340.699533-2-mge@meta.com Acked-by: Jack Wang Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm80xx_hwi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 1b2c40b1381c..3afd9443c425 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -3671,10 +3671,12 @@ static int mpi_set_controller_config_resp(struct pm8001_hba_info *pm8001_ha, (struct set_ctrl_cfg_resp *)(piomb + 4); u32 status = le32_to_cpu(pPayload->status); u32 err_qlfr_pgcd = le32_to_cpu(pPayload->err_qlfr_pgcd); + u32 tag = le32_to_cpu(pPayload->tag); pm8001_dbg(pm8001_ha, MSG, "SET CONTROLLER RESP: status 0x%x qlfr_pgcd 0x%x\n", status, err_qlfr_pgcd); + pm8001_tag_free(pm8001_ha, tag); return 0; } -- cgit v1.2.3 From c91774818b041ed290df29fb1dc0725be9b12e83 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Tue, 12 Sep 2023 08:27:36 +0900 Subject: scsi: pm8001: Setup IRQs on resume The function pm8001_pci_resume() only calls pm8001_request_irq() without calling pm8001_setup_irq(). This causes the IRQ allocation to fail, which leads all drives being removed from the system. Fix this issue by integrating the code for pm8001_setup_irq() directly inside pm8001_request_irq() so that MSI-X setup is performed both during normal initialization and resume operations. Fixes: dbf9bfe61571 ("[SCSI] pm8001: add SAS/SATA HBA driver") Cc: stable@vger.kernel.org Signed-off-by: Damien Le Moal Link: https://lore.kernel.org/r/20230911232745.325149-2-dlemoal@kernel.org Acked-by: Jack Wang Signed-off-by: Martin K. Petersen --- drivers/scsi/pm8001/pm8001_init.c | 51 +++++++++++++-------------------------- 1 file changed, 17 insertions(+), 34 deletions(-) diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 5e5ce1e74c3b..443a3176c6c0 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -273,7 +273,6 @@ static irqreturn_t pm8001_interrupt_handler_intx(int irq, void *dev_id) return ret; } -static u32 pm8001_setup_irq(struct pm8001_hba_info *pm8001_ha); static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha); /** @@ -294,13 +293,6 @@ static int pm8001_alloc(struct pm8001_hba_info *pm8001_ha, pm8001_dbg(pm8001_ha, INIT, "pm8001_alloc: PHY:%x\n", pm8001_ha->chip->n_phy); - /* Setup Interrupt */ - rc = pm8001_setup_irq(pm8001_ha); - if (rc) { - pm8001_dbg(pm8001_ha, FAIL, - "pm8001_setup_irq failed [ret: %d]\n", rc); - goto err_out; - } /* Request Interrupt */ rc = pm8001_request_irq(pm8001_ha); if (rc) @@ -1031,47 +1023,38 @@ static u32 pm8001_request_msix(struct pm8001_hba_info *pm8001_ha) } #endif -static u32 pm8001_setup_irq(struct pm8001_hba_info *pm8001_ha) -{ - struct pci_dev *pdev; - - pdev = pm8001_ha->pdev; - -#ifdef PM8001_USE_MSIX - if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) - return pm8001_setup_msix(pm8001_ha); - pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n"); -#endif - return 0; -} - /** * pm8001_request_irq - register interrupt * @pm8001_ha: our ha struct. */ static u32 pm8001_request_irq(struct pm8001_hba_info *pm8001_ha) { - struct pci_dev *pdev; + struct pci_dev *pdev = pm8001_ha->pdev; +#ifdef PM8001_USE_MSIX int rc; - pdev = pm8001_ha->pdev; + if (pci_find_capability(pdev, PCI_CAP_ID_MSIX)) { + rc = pm8001_setup_msix(pm8001_ha); + if (rc) { + pm8001_dbg(pm8001_ha, FAIL, + "pm8001_setup_irq failed [ret: %d]\n", rc); + return rc; + } -#ifdef PM8001_USE_MSIX - if (pdev->msix_cap && pci_msi_enabled()) - return pm8001_request_msix(pm8001_ha); - else { - pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n"); - goto intx; + if (pdev->msix_cap && pci_msi_enabled()) + return pm8001_request_msix(pm8001_ha); } + + pm8001_dbg(pm8001_ha, INIT, "MSIX not supported!!!\n"); #endif -intx: /* initialize the INT-X interrupt */ pm8001_ha->irq_vector[0].irq_id = 0; pm8001_ha->irq_vector[0].drv_inst = pm8001_ha; - rc = request_irq(pdev->irq, pm8001_interrupt_handler_intx, IRQF_SHARED, - pm8001_ha->name, SHOST_TO_SAS_HA(pm8001_ha->shost)); - return rc; + + return request_irq(pdev->irq, pm8001_interrupt_handler_intx, + IRQF_SHARED, pm8001_ha->name, + SHOST_TO_SAS_HA(pm8001_ha->shost)); } /** -- cgit v1.2.3 From d14e3e553e05cb763964c991fe6acb0a6a1c6f9c Mon Sep 17 00:00:00 2001 From: David Disseldorp Date: Thu, 31 Aug 2023 20:34:59 +0200 Subject: scsi: target: core: Fix target_cmd_counter leak The target_cmd_counter struct allocated via target_alloc_cmd_counter() is never freed, resulting in leaks across various transport types, e.g.: unreferenced object 0xffff88801f920120 (size 96): comm "sh", pid 102, jiffies 4294892535 (age 713.412s) hex dump (first 32 bytes): 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ 00 00 00 00 00 00 00 00 38 01 92 1f 80 88 ff ff ........8....... backtrace: [<00000000e58a6252>] kmalloc_trace+0x11/0x20 [<0000000043af4b2f>] target_alloc_cmd_counter+0x17/0x90 [target_core_mod] [<000000007da2dfa7>] target_setup_session+0x2d/0x140 [target_core_mod] [<0000000068feef86>] tcm_loop_tpg_nexus_store+0x19b/0x350 [tcm_loop] [<000000006a80e021>] configfs_write_iter+0xb1/0x120 [<00000000e9f4d860>] vfs_write+0x2e4/0x3c0 [<000000008143433b>] ksys_write+0x80/0xb0 [<00000000a7df29b2>] do_syscall_64+0x42/0x90 [<0000000053f45fb8>] entry_SYSCALL_64_after_hwframe+0x6e/0xd8 Free the structure alongside the corresponding iscsit_conn / se_sess parent. Signed-off-by: David Disseldorp Link: https://lore.kernel.org/r/20230831183459.6938-1-ddiss@suse.de Fixes: becd9be6069e ("scsi: target: Move sess cmd counter to new struct") Reviewed-by: Mike Christie Signed-off-by: Martin K. Petersen --- drivers/target/target_core_transport.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/target/target_core_transport.c b/drivers/target/target_core_transport.c index 687adc9e086c..0686882bcbda 100644 --- a/drivers/target/target_core_transport.c +++ b/drivers/target/target_core_transport.c @@ -264,6 +264,7 @@ void target_free_cmd_counter(struct target_cmd_counter *cmd_cnt) percpu_ref_put(&cmd_cnt->refcnt); percpu_ref_exit(&cmd_cnt->refcnt); + kfree(cmd_cnt); } EXPORT_SYMBOL_GPL(target_free_cmd_counter); -- cgit v1.2.3 From 7dcc683db3639eadd11bf0d59a09088a43de5e22 Mon Sep 17 00:00:00 2001 From: Jinjie Ruan Date: Wed, 6 Sep 2023 11:08:09 +0800 Subject: scsi: lpfc: Fix the NULL vs IS_ERR() bug for debugfs_create_file() Since debugfs_create_file() returns ERR_PTR and never NULL, use IS_ERR() to check the return value. Fixes: 2fcbc569b9f5 ("scsi: lpfc: Make debugfs ktime stats generic for NVME and SCSI") Fixes: 4c47efc140fa ("scsi: lpfc: Move SCSI and NVME Stats to hardware queue structures") Fixes: 6a828b0f6192 ("scsi: lpfc: Support non-uniform allocation of MSIX vectors to hardware queues") Fixes: 95bfc6d8ad86 ("scsi: lpfc: Make FW logging dynamically configurable") Fixes: 9f77870870d8 ("scsi: lpfc: Add debugfs support for cm framework buffers") Fixes: c490850a0947 ("scsi: lpfc: Adapt partitioned XRI lists to efficient sharing") Signed-off-by: Jinjie Ruan Link: https://lore.kernel.org/r/20230906030809.2847970-1-ruanjinjie@huawei.com Reviewed-by: Justin Tee Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_debugfs.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 7f9b221e7c34..ea9b42225e62 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -6073,7 +6073,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) phba->hba_debugfs_root, phba, &lpfc_debugfs_op_multixripools); - if (!phba->debug_multixri_pools) { + if (IS_ERR(phba->debug_multixri_pools)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "0527 Cannot create debugfs multixripools\n"); goto debug_failed; @@ -6085,7 +6085,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, phba, &lpfc_cgn_buffer_op); - if (!phba->debug_cgn_buffer) { + if (IS_ERR(phba->debug_cgn_buffer)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "6527 Cannot create debugfs " "cgn_buffer\n"); @@ -6098,7 +6098,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, phba, &lpfc_rx_monitor_op); - if (!phba->debug_rx_monitor) { + if (IS_ERR(phba->debug_rx_monitor)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "6528 Cannot create debugfs " "rx_monitor\n"); @@ -6111,7 +6111,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, 0644, phba->hba_debugfs_root, phba, &lpfc_debugfs_ras_log); - if (!phba->debug_ras_log) { + if (IS_ERR(phba->debug_ras_log)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "6148 Cannot create debugfs" " ras_log\n"); @@ -6132,7 +6132,7 @@ lpfc_debugfs_initialize(struct lpfc_vport *vport) debugfs_create_file(name, S_IFREG | 0644, phba->hba_debugfs_root, phba, &lpfc_debugfs_op_lockstat); - if (!phba->debug_lockstat) { + if (IS_ERR(phba->debug_lockstat)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "4610 Can't create debugfs lockstat\n"); goto debug_failed; @@ -6358,7 +6358,7 @@ nvmeio_off: debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_scsistat); - if (!vport->debug_scsistat) { + if (IS_ERR(vport->debug_scsistat)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "4611 Cannot create debugfs scsistat\n"); goto debug_failed; @@ -6369,7 +6369,7 @@ nvmeio_off: debugfs_create_file(name, 0644, vport->vport_debugfs_root, vport, &lpfc_debugfs_op_ioktime); - if (!vport->debug_ioktime) { + if (IS_ERR(vport->debug_ioktime)) { lpfc_printf_vlog(vport, KERN_ERR, LOG_INIT, "0815 Cannot create debugfs ioktime\n"); goto debug_failed; -- cgit v1.2.3 From 9c3034968ed0feeaf72e5b549b19c7767a1a04f2 Mon Sep 17 00:00:00 2001 From: Justin Tee Date: Fri, 8 Sep 2023 14:18:52 -0700 Subject: scsi: lpfc: Early return after marking final NLP_DROPPED flag in dev_loss_tmo When a dev_loss_tmo event occurs, an ndlp lock is taken before checking nlp_flag for NLP_DROPPED. There is an attempt to restore the ndlp lock when exiting the if statement, but the nlp_put kref could be the final decrement causing a use-after-free memory access on a released ndlp object. Instead of trying to reacquire the ndlp lock after checking nlp_flag, just return after calling nlp_put. Signed-off-by: Justin Tee Link: https://lore.kernel.org/r/20230908211852.37576-1-justintee8345@gmail.com Reviewed-by: "Ewan D. Milne" Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hbadisc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 51afb60859eb..674dd07aae72 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -203,7 +203,7 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) ndlp->nlp_flag |= NLP_DROPPED; spin_unlock_irqrestore(&ndlp->lock, iflags); lpfc_nlp_put(ndlp); - spin_lock_irqsave(&ndlp->lock, iflags); + return; } spin_unlock_irqrestore(&ndlp->lock, iflags); -- cgit v1.2.3 From dae40be7a1a72474e225795c0d6f43a4ac596a3f Mon Sep 17 00:00:00 2001 From: Justin Tee Date: Fri, 8 Sep 2023 14:19:23 -0700 Subject: scsi: lpfc: Prevent use-after-free during rmmod with mapped NVMe rports During rmmod, when dev_loss_tmo callback is called, an ndlp kref count is decremented twice. Once for SCSI transport registration and second to remove the initial node allocation kref. If there is also an NVMe transport registration, another reference count decrement is expected in lpfc_nvme_unregister_port(). Race conditions between the NVMe transport remoteport_delete and dev_loss_tmo callbacks sometimes results in premature ndlp object release resulting in use-after-free issues. Fix by not dropping the ndlp object in dev_loss_tmo callback with an outstanding NVMe transport registration. Inversely, mark the final NLP_DROPPED flag in lpfc_nvme_unregister_port when rmmod flag is set. Signed-off-by: Justin Tee Link: https://lore.kernel.org/r/20230908211923.37603-1-justintee8345@gmail.com Signed-off-by: Martin K. Petersen --- drivers/scsi/lpfc/lpfc_hbadisc.c | 3 ++- drivers/scsi/lpfc/lpfc_nvme.c | 24 +++++++++++++++++------- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index 674dd07aae72..5154eeaee0ec 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -199,7 +199,8 @@ lpfc_dev_loss_tmo_callbk(struct fc_rport *rport) /* Only 1 thread can drop the initial node reference. If * another thread has set NLP_DROPPED, this thread is done. */ - if (!(ndlp->nlp_flag & NLP_DROPPED)) { + if (!(ndlp->fc4_xpt_flags & NVME_XPT_REGD) && + !(ndlp->nlp_flag & NLP_DROPPED)) { ndlp->nlp_flag |= NLP_DROPPED; spin_unlock_irqrestore(&ndlp->lock, iflags); lpfc_nlp_put(ndlp); diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index 39acbcb7ec66..96e11a26c297 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -228,8 +228,7 @@ lpfc_nvme_remoteport_delete(struct nvme_fc_remote_port *remoteport) spin_unlock_irq(&ndlp->lock); /* On a devloss timeout event, one more put is executed provided the - * NVME and SCSI rport unregister requests are complete. If the vport - * is unloading, this extra put is executed by lpfc_drop_node. + * NVME and SCSI rport unregister requests are complete. */ if (!(ndlp->fc4_xpt_flags & fc4_xpt_flags)) lpfc_disc_state_machine(vport, ndlp, NULL, NLP_EVT_DEVICE_RM); @@ -2567,11 +2566,7 @@ lpfc_nvme_rescan_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) * nvme_transport perspective. Loss of an rport just means IO cannot * be sent and recovery is completely up to the initator. * For now, the driver just unbinds the DID and port_role so that - * no further IO can be issued. Changes are planned for later. - * - * Notes - the ndlp reference count is not decremented here since - * since there is no nvme_transport api for devloss. Node ref count - * is only adjusted in driver unload. + * no further IO can be issued. */ void lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) @@ -2646,6 +2641,21 @@ lpfc_nvme_unregister_port(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp) "6167 NVME unregister failed %d " "port_state x%x\n", ret, remoteport->port_state); + + if (vport->load_flag & FC_UNLOADING) { + /* Only 1 thread can drop the initial node + * reference. Check if another thread has set + * NLP_DROPPED. + */ + spin_lock_irq(&ndlp->lock); + if (!(ndlp->nlp_flag & NLP_DROPPED)) { + ndlp->nlp_flag |= NLP_DROPPED; + spin_unlock_irq(&ndlp->lock); + lpfc_nlp_put(ndlp); + return; + } + spin_unlock_irq(&ndlp->lock); + } } } return; -- cgit v1.2.3 From e7b1ef29420fe52c2c1a273a9b4b36103a522625 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Sep 2023 10:49:35 +0900 Subject: net: renesas: rswitch: Fix unmasking irq condition Fix unmasking irq condition by using napi_complete_done(). Otherwise, redundant interrupts happen. Fixes: 3590918b5d07 ("net: ethernet: renesas: Add support for "Ethernet Switch"") Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Paolo Abeni --- drivers/net/ethernet/renesas/rswitch.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index 6083b1c8e4fb..26c8807d7dea 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -816,10 +816,10 @@ retry: netif_wake_subqueue(ndev, 0); - napi_complete(napi); - - rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); - rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); + if (napi_complete_done(napi, budget - quota)) { + rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); + rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); + } out: return budget - quota; -- cgit v1.2.3 From c4f922e86c8e0f7c5fe94e0547e9835fc9711f08 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Tue, 12 Sep 2023 10:49:36 +0900 Subject: net: renesas: rswitch: Add spin lock protection for irq {un}mask Add spin lock protection for irq {un}mask registers' control. After napi_complete_done() and this protection were applied, a lot of redundant interrupts no longer occur. For example: when "iperf3 -c -R" on R-Car S4-8 Spider Before the patches are applied: about 800,000 times happened After the patches were applied: about 100,000 times happened Fixes: 3590918b5d07 ("net: ethernet: renesas: Add support for "Ethernet Switch"") Signed-off-by: Yoshihiro Shimoda Reviewed-by: Simon Horman Signed-off-by: Paolo Abeni --- drivers/net/ethernet/renesas/rswitch.c | 12 ++++++++++++ drivers/net/ethernet/renesas/rswitch.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/drivers/net/ethernet/renesas/rswitch.c b/drivers/net/ethernet/renesas/rswitch.c index 26c8807d7dea..ea9186178091 100644 --- a/drivers/net/ethernet/renesas/rswitch.c +++ b/drivers/net/ethernet/renesas/rswitch.c @@ -799,6 +799,7 @@ static int rswitch_poll(struct napi_struct *napi, int budget) struct net_device *ndev = napi->dev; struct rswitch_private *priv; struct rswitch_device *rdev; + unsigned long flags; int quota = budget; rdev = netdev_priv(ndev); @@ -817,8 +818,10 @@ retry: netif_wake_subqueue(ndev, 0); if (napi_complete_done(napi, budget - quota)) { + spin_lock_irqsave(&priv->lock, flags); rswitch_enadis_data_irq(priv, rdev->tx_queue->index, true); rswitch_enadis_data_irq(priv, rdev->rx_queue->index, true); + spin_unlock_irqrestore(&priv->lock, flags); } out: @@ -835,8 +838,10 @@ static void rswitch_queue_interrupt(struct net_device *ndev) struct rswitch_device *rdev = netdev_priv(ndev); if (napi_schedule_prep(&rdev->napi)) { + spin_lock(&rdev->priv->lock); rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); + spin_unlock(&rdev->priv->lock); __napi_schedule(&rdev->napi); } } @@ -1440,14 +1445,17 @@ static void rswitch_ether_port_deinit_all(struct rswitch_private *priv) static int rswitch_open(struct net_device *ndev) { struct rswitch_device *rdev = netdev_priv(ndev); + unsigned long flags; phy_start(ndev->phydev); napi_enable(&rdev->napi); netif_start_queue(ndev); + spin_lock_irqsave(&rdev->priv->lock, flags); rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, true); rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, true); + spin_unlock_irqrestore(&rdev->priv->lock, flags); if (bitmap_empty(rdev->priv->opened_ports, RSWITCH_NUM_PORTS)) iowrite32(GWCA_TS_IRQ_BIT, rdev->priv->addr + GWTSDIE); @@ -1461,6 +1469,7 @@ static int rswitch_stop(struct net_device *ndev) { struct rswitch_device *rdev = netdev_priv(ndev); struct rswitch_gwca_ts_info *ts_info, *ts_info2; + unsigned long flags; netif_tx_stop_all_queues(ndev); bitmap_clear(rdev->priv->opened_ports, rdev->port, 1); @@ -1476,8 +1485,10 @@ static int rswitch_stop(struct net_device *ndev) kfree(ts_info); } + spin_lock_irqsave(&rdev->priv->lock, flags); rswitch_enadis_data_irq(rdev->priv, rdev->tx_queue->index, false); rswitch_enadis_data_irq(rdev->priv, rdev->rx_queue->index, false); + spin_unlock_irqrestore(&rdev->priv->lock, flags); phy_stop(ndev->phydev); napi_disable(&rdev->napi); @@ -1887,6 +1898,7 @@ static int renesas_eth_sw_probe(struct platform_device *pdev) priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + spin_lock_init(&priv->lock); attr = soc_device_match(rswitch_soc_no_speed_change); if (attr) diff --git a/drivers/net/ethernet/renesas/rswitch.h b/drivers/net/ethernet/renesas/rswitch.h index 54f397effbc6..f0c16a37ea55 100644 --- a/drivers/net/ethernet/renesas/rswitch.h +++ b/drivers/net/ethernet/renesas/rswitch.h @@ -1011,6 +1011,8 @@ struct rswitch_private { struct rswitch_etha etha[RSWITCH_NUM_PORTS]; struct rswitch_mfwd mfwd; + spinlock_t lock; /* lock interrupt registers' control */ + bool etha_no_runtime_change; bool gwca_halt; }; -- cgit v1.2.3 From a22730b1b4bf437c6bbfdeff5feddf54be4aeada Mon Sep 17 00:00:00 2001 From: Kuniyuki Iwashima Date: Mon, 11 Sep 2023 19:27:53 -0700 Subject: kcm: Fix error handling for SOCK_DGRAM in kcm_sendmsg(). syzkaller found a memory leak in kcm_sendmsg(), and commit c821a88bd720 ("kcm: Fix memory leak in error path of kcm_sendmsg()") suppressed it by updating kcm_tx_msg(head)->last_skb if partial data is copied so that the following sendmsg() will resume from the skb. However, we cannot know how many bytes were copied when we get the error. Thus, we could mess up the MSG_MORE queue. When kcm_sendmsg() fails for SOCK_DGRAM, we should purge the queue as we do so for UDP by udp_flush_pending_frames(). Even without this change, when the error occurred, the following sendmsg() resumed from a wrong skb and the queue was messed up. However, we have yet to get such a report, and only syzkaller stumbled on it. So, this can be changed safely. Note this does not change SOCK_SEQPACKET behaviour. Fixes: c821a88bd720 ("kcm: Fix memory leak in error path of kcm_sendmsg()") Fixes: ab7ac4eb9832 ("kcm: Kernel Connection Multiplexor module") Signed-off-by: Kuniyuki Iwashima Link: https://lore.kernel.org/r/20230912022753.33327-1-kuniyu@amazon.com Signed-off-by: Paolo Abeni --- net/kcm/kcmsock.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 740539a218b7..dd1d8ffd5f59 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -930,17 +930,18 @@ partial_message: out_error: kcm_push(kcm); - if (copied && sock->type == SOCK_SEQPACKET) { + if (sock->type == SOCK_SEQPACKET) { /* Wrote some bytes before encountering an * error, return partial success. */ - goto partial_message; - } - - if (head != kcm->seq_skb) + if (copied) + goto partial_message; + if (head != kcm->seq_skb) + kfree_skb(head); + } else { kfree_skb(head); - else if (copied) - kcm_tx_msg(head)->last_skb = skb; + kcm->seq_skb = NULL; + } err = sk_stream_error(sk, msg->msg_flags, err); -- cgit v1.2.3 From 7908632f2927b65f7486ae6b67c24071666ba43f Mon Sep 17 00:00:00 2001 From: Maíra Canal Date: Thu, 14 Sep 2023 07:19:02 -0300 Subject: Revert "drm/vkms: Fix race-condition between the hrtimer and the atomic commit" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit a0e6a017ab56936c0405fe914a793b241ed25ee0. Unlocking a mutex in the context of a hrtimer callback is violating mutex locking rules, as mutex_unlock() from interrupt context is not permitted. Link: https://lore.kernel.org/dri-devel/ZQLAc%2FFwkv%2FGiVoK@phenom.ffwll.local/T/#t Acked-by: Daniel Vetter Signed-off-by: Maíra Canal Signed-off-by: Maíra Canal Link: https://patchwork.freedesktop.org/patch/msgid/20230914102024.1789154-1-mcanal@igalia.com --- drivers/gpu/drm/vkms/vkms_composer.c | 9 ++------- drivers/gpu/drm/vkms/vkms_crtc.c | 9 ++++----- drivers/gpu/drm/vkms/vkms_drv.h | 4 +--- 3 files changed, 7 insertions(+), 15 deletions(-) diff --git a/drivers/gpu/drm/vkms/vkms_composer.c b/drivers/gpu/drm/vkms/vkms_composer.c index d5d4f642d367..3c99fb8b54e2 100644 --- a/drivers/gpu/drm/vkms/vkms_composer.c +++ b/drivers/gpu/drm/vkms/vkms_composer.c @@ -408,15 +408,10 @@ void vkms_set_composer(struct vkms_output *out, bool enabled) if (enabled) drm_crtc_vblank_get(&out->crtc); - mutex_lock(&out->enabled_lock); + spin_lock_irq(&out->lock); old_enabled = out->composer_enabled; out->composer_enabled = enabled; - - /* the composition wasn't enabled, so unlock the lock to make sure the lock - * will be balanced even if we have a failed commit - */ - if (!out->composer_enabled) - mutex_unlock(&out->enabled_lock); + spin_unlock_irq(&out->lock); if (old_enabled) drm_crtc_vblank_put(&out->crtc); diff --git a/drivers/gpu/drm/vkms/vkms_crtc.c b/drivers/gpu/drm/vkms/vkms_crtc.c index 3c5ebf106b66..61e500b8c9da 100644 --- a/drivers/gpu/drm/vkms/vkms_crtc.c +++ b/drivers/gpu/drm/vkms/vkms_crtc.c @@ -16,7 +16,7 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) struct drm_crtc *crtc = &output->crtc; struct vkms_crtc_state *state; u64 ret_overrun; - bool ret, fence_cookie, composer_enabled; + bool ret, fence_cookie; fence_cookie = dma_fence_begin_signalling(); @@ -25,15 +25,15 @@ static enum hrtimer_restart vkms_vblank_simulate(struct hrtimer *timer) if (ret_overrun != 1) pr_warn("%s: vblank timer overrun\n", __func__); + spin_lock(&output->lock); ret = drm_crtc_handle_vblank(crtc); if (!ret) DRM_ERROR("vkms failure on handling vblank"); state = output->composer_state; - composer_enabled = output->composer_enabled; - mutex_unlock(&output->enabled_lock); + spin_unlock(&output->lock); - if (state && composer_enabled) { + if (state && output->composer_enabled) { u64 frame = drm_crtc_accurate_vblank_count(crtc); /* update frame_start only if a queued vkms_composer_worker() @@ -295,7 +295,6 @@ int vkms_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, spin_lock_init(&vkms_out->lock); spin_lock_init(&vkms_out->composer_lock); - mutex_init(&vkms_out->enabled_lock); vkms_out->composer_workq = alloc_ordered_workqueue("vkms_composer", 0); if (!vkms_out->composer_workq) diff --git a/drivers/gpu/drm/vkms/vkms_drv.h b/drivers/gpu/drm/vkms/vkms_drv.h index c7ae6c2ba1df..8f5710debb1e 100644 --- a/drivers/gpu/drm/vkms/vkms_drv.h +++ b/drivers/gpu/drm/vkms/vkms_drv.h @@ -108,10 +108,8 @@ struct vkms_output { struct workqueue_struct *composer_workq; /* protects concurrent access to composer */ spinlock_t lock; - /* guarantees that if the composer is enabled, a job will be queued */ - struct mutex enabled_lock; - /* protected by @enabled_lock */ + /* protected by @lock */ bool composer_enabled; struct vkms_crtc_state *composer_state; -- cgit v1.2.3 From 139a27854bf5ce93ff9805f9f7683b88c13074dc Mon Sep 17 00:00:00 2001 From: Thomas Hellström Date: Thu, 7 Sep 2023 15:53:38 +0200 Subject: drm/tests: helpers: Avoid a driver uaf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit when using __drm_kunit_helper_alloc_drm_device() the driver may be dereferenced by device-managed resources up until the device is freed, which is typically later than the kunit-managed resource code frees it. Fix this by simply make the driver device-managed as well. In short, the sequence leading to the UAF is as follows: INIT: Code allocates a struct device as a kunit-managed resource. Code allocates a drm driver as a kunit-managed resource. Code allocates a drm device as a device-managed resource. EXIT: Kunit resource cleanup frees the drm driver Kunit resource cleanup puts the struct device, which starts a device-managed resource cleanup device-managed cleanup calls drm_dev_put() drm_dev_put() dereferences the (now freed) drm driver -> Boom. Related KASAN message: [55272.551542] ================================================================== [55272.551551] BUG: KASAN: slab-use-after-free in drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551603] Read of size 8 at addr ffff888127502828 by task kunit_try_catch/10353 [55272.551612] CPU: 4 PID: 10353 Comm: kunit_try_catch Tainted: G U N 6.5.0-rc7+ #155 [55272.551620] Hardware name: ASUS System Product Name/PRIME B560M-A AC, BIOS 0403 01/26/2021 [55272.551626] Call Trace: [55272.551629] [55272.551633] dump_stack_lvl+0x57/0x90 [55272.551639] print_report+0xcf/0x630 [55272.551645] ? _raw_spin_lock_irqsave+0x5f/0x70 [55272.551652] ? drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551694] kasan_report+0xd7/0x110 [55272.551699] ? drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551742] drm_dev_put.part.0+0xd4/0xe0 [drm] [55272.551783] devres_release_all+0x15d/0x1f0 [55272.551790] ? __pfx_devres_release_all+0x10/0x10 [55272.551797] device_unbind_cleanup+0x16/0x1a0 [55272.551802] device_release_driver_internal+0x3e5/0x540 [55272.551808] ? kobject_put+0x5d/0x4b0 [55272.551814] bus_remove_device+0x1f1/0x3f0 [55272.551819] device_del+0x342/0x910 [55272.551826] ? __pfx_device_del+0x10/0x10 [55272.551830] ? lock_release+0x339/0x5e0 [55272.551836] ? kunit_remove_resource+0x128/0x290 [kunit] [55272.551845] ? __pfx_lock_release+0x10/0x10 [55272.551851] platform_device_del.part.0+0x1f/0x1e0 [55272.551856] ? _raw_spin_unlock_irqrestore+0x30/0x60 [55272.551863] kunit_remove_resource+0x195/0x290 [kunit] [55272.551871] ? _raw_spin_unlock_irqrestore+0x30/0x60 [55272.551877] kunit_cleanup+0x78/0x120 [kunit] [55272.551885] ? __kthread_parkme+0xc1/0x1f0 [55272.551891] ? __pfx_kunit_try_run_case_cleanup+0x10/0x10 [kunit] [55272.551900] ? __pfx_kunit_generic_run_threadfn_adapter+0x10/0x10 [kunit] [55272.551909] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [55272.551919] kthread+0x2e7/0x3c0 [55272.551924] ? __pfx_kthread+0x10/0x10 [55272.551929] ret_from_fork+0x2d/0x70 [55272.551935] ? __pfx_kthread+0x10/0x10 [55272.551940] ret_from_fork_asm+0x1b/0x30 [55272.551948] [55272.551953] Allocated by task 10351: [55272.551956] kasan_save_stack+0x1c/0x40 [55272.551962] kasan_set_track+0x21/0x30 [55272.551966] __kasan_kmalloc+0x8b/0x90 [55272.551970] __kmalloc+0x5e/0x160 [55272.551976] kunit_kmalloc_array+0x1c/0x50 [kunit] [55272.551984] drm_exec_test_init+0xfa/0x2c0 [drm_exec_test] [55272.551991] kunit_try_run_case+0xdd/0x250 [kunit] [55272.551999] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [55272.552008] kthread+0x2e7/0x3c0 [55272.552012] ret_from_fork+0x2d/0x70 [55272.552017] ret_from_fork_asm+0x1b/0x30 [55272.552024] Freed by task 10353: [55272.552027] kasan_save_stack+0x1c/0x40 [55272.552032] kasan_set_track+0x21/0x30 [55272.552036] kasan_save_free_info+0x27/0x40 [55272.552041] __kasan_slab_free+0x106/0x180 [55272.552046] slab_free_freelist_hook+0xb3/0x160 [55272.552051] __kmem_cache_free+0xb2/0x290 [55272.552056] kunit_remove_resource+0x195/0x290 [kunit] [55272.552064] kunit_cleanup+0x78/0x120 [kunit] [55272.552072] kunit_generic_run_threadfn_adapter+0x4a/0x90 [kunit] [55272.552080] kthread+0x2e7/0x3c0 [55272.552085] ret_from_fork+0x2d/0x70 [55272.552089] ret_from_fork_asm+0x1b/0x30 [55272.552096] The buggy address belongs to the object at ffff888127502800 which belongs to the cache kmalloc-512 of size 512 [55272.552105] The buggy address is located 40 bytes inside of freed 512-byte region [ffff888127502800, ffff888127502a00) [55272.552115] The buggy address belongs to the physical page: [55272.552119] page:00000000af6c70ff refcount:1 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x127500 [55272.552127] head:00000000af6c70ff order:3 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [55272.552133] anon flags: 0x17ffffc0010200(slab|head|node=0|zone=2|lastcpupid=0x1fffff) [55272.552141] page_type: 0xffffffff() [55272.552145] raw: 0017ffffc0010200 ffff888100042c80 0000000000000000 dead000000000001 [55272.552152] raw: 0000000000000000 0000000080200020 00000001ffffffff 0000000000000000 [55272.552157] page dumped because: kasan: bad access detected [55272.552163] Memory state around the buggy address: [55272.552167] ffff888127502700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [55272.552173] ffff888127502780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc [55272.552178] >ffff888127502800: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [55272.552184] ^ [55272.552187] ffff888127502880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [55272.552193] ffff888127502900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [55272.552198] ================================================================== [55272.552203] Disabling lock debugging due to kernel taint v2: - Update commit message, add Fixes: tag and Cc stable. v3: - Further commit message updates (Maxime Ripard). Cc: Maarten Lankhorst Cc: Maxime Ripard Cc: Thomas Zimmermann Cc: David Airlie Cc: Daniel Vetter Cc: dri-devel@lists.freedesktop.org Cc: stable@vger.kernel.org # v6.3+ Fixes: d98780310719 ("drm/tests: helpers: Allow to pass a custom drm_driver") Signed-off-by: Thomas Hellström Reviewed-by: Francois Dugast Acked-by: Maxime Ripard Link: https://lore.kernel.org/r/20230907135339.7971-2-thomas.hellstrom@linux.intel.com Signed-off-by: Maxime Ripard --- include/drm/drm_kunit_helpers.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/drm/drm_kunit_helpers.h b/include/drm/drm_kunit_helpers.h index 514c8a7a32f0..ba483c87f0e7 100644 --- a/include/drm/drm_kunit_helpers.h +++ b/include/drm/drm_kunit_helpers.h @@ -3,6 +3,8 @@ #ifndef DRM_KUNIT_HELPERS_H_ #define DRM_KUNIT_HELPERS_H_ +#include + #include struct drm_device; @@ -51,7 +53,7 @@ __drm_kunit_helper_alloc_drm_device(struct kunit *test, { struct drm_driver *driver; - driver = kunit_kzalloc(test, sizeof(*driver), GFP_KERNEL); + driver = devm_kzalloc(dev, sizeof(*driver), GFP_KERNEL); KUNIT_ASSERT_NOT_NULL(test, driver); driver->driver_features = features; -- cgit v1.2.3 From f6007dce0cd35d634d9be91ef3515a6385dcee16 Mon Sep 17 00:00:00 2001 From: Mikulas Patocka Date: Wed, 9 Aug 2023 12:44:20 +0200 Subject: dm: fix a race condition in retrieve_deps There's a race condition in the multipath target when retrieve_deps races with multipath_message calling dm_get_device and dm_put_device. retrieve_deps walks the list of open devices without holding any lock but multipath may add or remove devices to the list while it is running. The end result may be memory corruption or use-after-free memory access. See this description of a UAF with multipath_message(): https://listman.redhat.com/archives/dm-devel/2022-October/052373.html Fix this bug by introducing a new rw semaphore "devices_lock". We grab devices_lock for read in retrieve_deps and we grab it for write in dm_get_device and dm_put_device. Reported-by: Luo Meng Signed-off-by: Mikulas Patocka Cc: stable@vger.kernel.org Tested-by: Li Lingfeng Signed-off-by: Mike Snitzer --- drivers/md/dm-core.h | 1 + drivers/md/dm-ioctl.c | 7 ++++++- drivers/md/dm-table.c | 32 ++++++++++++++++++++++++-------- 3 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/md/dm-core.h b/drivers/md/dm-core.h index 0d93661f88d3..095b9b49aa82 100644 --- a/drivers/md/dm-core.h +++ b/drivers/md/dm-core.h @@ -214,6 +214,7 @@ struct dm_table { /* a list of devices used by this table */ struct list_head devices; + struct rw_semaphore devices_lock; /* events get handed up using this callback */ void (*event_fn)(void *data); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index f5ed729a8e0c..21ebb6c39394 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1630,6 +1630,8 @@ static void retrieve_deps(struct dm_table *table, struct dm_dev_internal *dd; struct dm_target_deps *deps; + down_read(&table->devices_lock); + deps = get_result_buffer(param, param_size, &len); /* @@ -1644,7 +1646,7 @@ static void retrieve_deps(struct dm_table *table, needed = struct_size(deps, dev, count); if (len < needed) { param->flags |= DM_BUFFER_FULL_FLAG; - return; + goto out; } /* @@ -1656,6 +1658,9 @@ static void retrieve_deps(struct dm_table *table, deps->dev[count++] = huge_encode_dev(dd->dm_dev->bdev->bd_dev); param->data_size = param->data_start + needed; + +out: + up_read(&table->devices_lock); } static int table_deps(struct file *filp, struct dm_ioctl *param, size_t param_size) diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 7d208b2b1a19..37b48f63ae6a 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -135,6 +135,7 @@ int dm_table_create(struct dm_table **result, blk_mode_t mode, return -ENOMEM; INIT_LIST_HEAD(&t->devices); + init_rwsem(&t->devices_lock); if (!num_targets) num_targets = KEYS_PER_NODE; @@ -359,16 +360,20 @@ int __ref dm_get_device(struct dm_target *ti, const char *path, blk_mode_t mode, if (dev == disk_devt(t->md->disk)) return -EINVAL; + down_write(&t->devices_lock); + dd = find_device(&t->devices, dev); if (!dd) { dd = kmalloc(sizeof(*dd), GFP_KERNEL); - if (!dd) - return -ENOMEM; + if (!dd) { + r = -ENOMEM; + goto unlock_ret_r; + } r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev); if (r) { kfree(dd); - return r; + goto unlock_ret_r; } refcount_set(&dd->count, 1); @@ -378,12 +383,17 @@ int __ref dm_get_device(struct dm_target *ti, const char *path, blk_mode_t mode, } else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) { r = upgrade_mode(dd, mode, t->md); if (r) - return r; + goto unlock_ret_r; } refcount_inc(&dd->count); out: + up_write(&t->devices_lock); *result = dd->dm_dev; return 0; + +unlock_ret_r: + up_write(&t->devices_lock); + return r; } EXPORT_SYMBOL(dm_get_device); @@ -419,9 +429,12 @@ static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev, void dm_put_device(struct dm_target *ti, struct dm_dev *d) { int found = 0; - struct list_head *devices = &ti->table->devices; + struct dm_table *t = ti->table; + struct list_head *devices = &t->devices; struct dm_dev_internal *dd; + down_write(&t->devices_lock); + list_for_each_entry(dd, devices, list) { if (dd->dm_dev == d) { found = 1; @@ -430,14 +443,17 @@ void dm_put_device(struct dm_target *ti, struct dm_dev *d) } if (!found) { DMERR("%s: device %s not in table devices list", - dm_device_name(ti->table->md), d->name); - return; + dm_device_name(t->md), d->name); + goto unlock_ret; } if (refcount_dec_and_test(&dd->count)) { - dm_put_table_device(ti->table->md, d); + dm_put_table_device(t->md, d); list_del(&dd->list); kfree(dd); } + +unlock_ret: + up_write(&t->devices_lock); } EXPORT_SYMBOL(dm_put_device); -- cgit v1.2.3 From 1bb0763f1eb7dd015989fdc77dea17a349df2ea9 Mon Sep 17 00:00:00 2001 From: Li Zetao Date: Mon, 11 Sep 2023 10:51:38 +0800 Subject: jbd2: Fix memory leak in journal_init_common() There is a memory leak reported by kmemleak: unreferenced object 0xff11000105903b80 (size 64): comm "mount", pid 3382, jiffies 4295032021 (age 27.826s) hex dump (first 32 bytes): 04 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 ................ ff ff ff ff 00 00 00 00 00 00 00 00 00 00 00 00 ................ backtrace: [] __kmalloc_node+0x50/0x160 [] crypto_alloc_tfmmem.isra.0+0x38/0x110 [] crypto_create_tfm_node+0x85/0x2f0 [] crypto_alloc_tfm_node+0xfc/0x210 [] journal_init_common+0x727/0x1ad0 [] jbd2_journal_init_inode+0x2b5/0x500 [] ext4_load_and_init_journal+0x255/0x2440 [] ext4_fill_super+0x8823/0xa330 ... The root cause was traced to an error handing path in journal_init_common() when malloc memory failed in register_shrinker(). The checksum driver is used to reference to checksum algorithm via cryptoapi and the user should release the memory when the driver is no longer needed or the journal initialization failed. Fix it by calling crypto_free_shash() on the "err_cleanup" error handing path in journal_init_common(). Fixes: c30713084ba5 ("jbd2: move load_superblock() into journal_init_common()") Signed-off-by: Li Zetao Reviewed-by: Jan Kara Reviewed-by: Zhang Yi Reviewed-by: Ritesh Harjani (IBM) Link: https://lore.kernel.org/r/20230911025138.983101-1-lizetao1@huawei.com Signed-off-by: Theodore Ts'o --- fs/jbd2/journal.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 15e33c26c6cd..3ffda557dbdf 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -1604,6 +1604,8 @@ static journal_t *journal_init_common(struct block_device *bdev, err_cleanup: percpu_counter_destroy(&journal->j_checkpoint_jh_count); + if (journal->j_chksum_driver) + crypto_free_shash(journal->j_chksum_driver); kfree(journal->j_wbuf); jbd2_journal_destroy_revoke(journal); journal_fail_superblock(journal); -- cgit v1.2.3 From 45e4ab320c9b5fa67b1fc3b6a9b381cfcc0c8488 Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 13 Sep 2023 17:04:54 +0200 Subject: ext4: move setting of trimmed bit into ext4_try_to_trim_range() Currently we set the group's trimmed bit in ext4_trim_all_free() based on return value of ext4_try_to_trim_range(). However when we will want to abort trimming because of suspend attempt, we want to return success from ext4_try_to_trim_range() but not set the trimmed bit. Instead implementing awkward propagation of this information, just move setting of trimmed bit into ext4_try_to_trim_range() when the whole group is trimmed. Cc: stable@kernel.org Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20230913150504.9054-1-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index c91db9f57524..09091adfde64 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -6906,6 +6906,16 @@ __acquires(bitlock) return ret; } +static ext4_grpblk_t ext4_last_grp_cluster(struct super_block *sb, + ext4_group_t grp) +{ + if (grp < ext4_get_groups_count(sb)) + return EXT4_CLUSTERS_PER_GROUP(sb) - 1; + return (ext4_blocks_count(EXT4_SB(sb)->s_es) - + ext4_group_first_block_no(sb, grp) - 1) >> + EXT4_CLUSTER_BITS(sb); +} + static int ext4_try_to_trim_range(struct super_block *sb, struct ext4_buddy *e4b, ext4_grpblk_t start, ext4_grpblk_t max, ext4_grpblk_t minblocks) @@ -6913,9 +6923,12 @@ __acquires(ext4_group_lock_ptr(sb, e4b->bd_group)) __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) { ext4_grpblk_t next, count, free_count; + bool set_trimmed = false; void *bitmap; bitmap = e4b->bd_bitmap; + if (start == 0 && max >= ext4_last_grp_cluster(sb, e4b->bd_group)) + set_trimmed = true; start = max(e4b->bd_info->bb_first_free, start); count = 0; free_count = 0; @@ -6930,16 +6943,14 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) int ret = ext4_trim_extent(sb, start, next - start, e4b); if (ret && ret != -EOPNOTSUPP) - break; + return count; count += next - start; } free_count += next - start; start = next + 1; - if (fatal_signal_pending(current)) { - count = -ERESTARTSYS; - break; - } + if (fatal_signal_pending(current)) + return -ERESTARTSYS; if (need_resched()) { ext4_unlock_group(sb, e4b->bd_group); @@ -6951,6 +6962,9 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) break; } + if (set_trimmed) + EXT4_MB_GRP_SET_TRIMMED(e4b->bd_info); + return count; } @@ -6961,7 +6975,6 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) * @start: first group block to examine * @max: last group block to examine * @minblocks: minimum extent block count - * @set_trimmed: set the trimmed flag if at least one block is trimmed * * ext4_trim_all_free walks through group's block bitmap searching for free * extents. When the free extent is found, mark it as used in group buddy @@ -6971,7 +6984,7 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) static ext4_grpblk_t ext4_trim_all_free(struct super_block *sb, ext4_group_t group, ext4_grpblk_t start, ext4_grpblk_t max, - ext4_grpblk_t minblocks, bool set_trimmed) + ext4_grpblk_t minblocks) { struct ext4_buddy e4b; int ret; @@ -6988,13 +7001,10 @@ ext4_trim_all_free(struct super_block *sb, ext4_group_t group, ext4_lock_group(sb, group); if (!EXT4_MB_GRP_WAS_TRIMMED(e4b.bd_info) || - minblocks < EXT4_SB(sb)->s_last_trim_minblks) { + minblocks < EXT4_SB(sb)->s_last_trim_minblks) ret = ext4_try_to_trim_range(sb, &e4b, start, max, minblocks); - if (ret >= 0 && set_trimmed) - EXT4_MB_GRP_SET_TRIMMED(e4b.bd_info); - } else { + else ret = 0; - } ext4_unlock_group(sb, group); ext4_mb_unload_buddy(&e4b); @@ -7027,7 +7037,6 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) ext4_fsblk_t first_data_blk = le32_to_cpu(EXT4_SB(sb)->s_es->s_first_data_block); ext4_fsblk_t max_blks = ext4_blocks_count(EXT4_SB(sb)->s_es); - bool whole_group, eof = false; int ret = 0; start = range->start >> sb->s_blocksize_bits; @@ -7046,10 +7055,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) if (minlen > EXT4_CLUSTERS_PER_GROUP(sb)) goto out; } - if (end >= max_blks - 1) { + if (end >= max_blks - 1) end = max_blks - 1; - eof = true; - } if (end <= first_data_blk) goto out; if (start < first_data_blk) @@ -7063,7 +7070,6 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) /* end now represents the last cluster to discard in this group */ end = EXT4_CLUSTERS_PER_GROUP(sb) - 1; - whole_group = true; for (group = first_group; group <= last_group; group++) { grp = ext4_get_group_info(sb, group); @@ -7082,13 +7088,11 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) * change it for the last group, note that last_cluster is * already computed earlier by ext4_get_group_no_and_offset() */ - if (group == last_group) { + if (group == last_group) end = last_cluster; - whole_group = eof ? true : end == EXT4_CLUSTERS_PER_GROUP(sb) - 1; - } if (grp->bb_free >= minlen) { cnt = ext4_trim_all_free(sb, group, first_cluster, - end, minlen, whole_group); + end, minlen); if (cnt < 0) { ret = cnt; break; -- cgit v1.2.3 From 5229a658f6453362fbb9da6bf96872ef25a7097e Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 13 Sep 2023 17:04:55 +0200 Subject: ext4: do not let fstrim block system suspend Len Brown has reported that system suspend sometimes fail due to inability to freeze a task working in ext4_trim_fs() for one minute. Trimming a large filesystem on a disk that slowly processes discard requests can indeed take a long time. Since discard is just an advisory call, it is perfectly fine to interrupt it at any time and the return number of discarded blocks until that moment. Do that when we detect the task is being frozen. Cc: stable@kernel.org Reported-by: Len Brown Suggested-by: Dave Chinner References: https://bugzilla.kernel.org/show_bug.cgi?id=216322 Signed-off-by: Jan Kara Link: https://lore.kernel.org/r/20230913150504.9054-2-jack@suse.cz Signed-off-by: Theodore Ts'o --- fs/ext4/mballoc.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 09091adfde64..1e599305d85f 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -16,6 +16,7 @@ #include #include #include +#include #include /* @@ -6916,6 +6917,11 @@ static ext4_grpblk_t ext4_last_grp_cluster(struct super_block *sb, EXT4_CLUSTER_BITS(sb); } +static bool ext4_trim_interrupted(void) +{ + return fatal_signal_pending(current) || freezing(current); +} + static int ext4_try_to_trim_range(struct super_block *sb, struct ext4_buddy *e4b, ext4_grpblk_t start, ext4_grpblk_t max, ext4_grpblk_t minblocks) @@ -6949,8 +6955,8 @@ __releases(ext4_group_lock_ptr(sb, e4b->bd_group)) free_count += next - start; start = next + 1; - if (fatal_signal_pending(current)) - return -ERESTARTSYS; + if (ext4_trim_interrupted()) + return count; if (need_resched()) { ext4_unlock_group(sb, e4b->bd_group); @@ -7072,6 +7078,8 @@ int ext4_trim_fs(struct super_block *sb, struct fstrim_range *range) end = EXT4_CLUSTERS_PER_GROUP(sb) - 1; for (group = first_group; group <= last_group; group++) { + if (ext4_trim_interrupted()) + break; grp = ext4_get_group_info(sb, group); if (!grp) continue; -- cgit v1.2.3 From 7fda67e8c3ab6069f75888f67958a6d30454a9f6 Mon Sep 17 00:00:00 2001 From: Shida Zhang Date: Thu, 3 Aug 2023 14:09:38 +0800 Subject: ext4: fix rec_len verify error With the configuration PAGE_SIZE 64k and filesystem blocksize 64k, a problem occurred when more than 13 million files were directly created under a directory: EXT4-fs error (device xx): ext4_dx_csum_set:492: inode #xxxx: comm xxxxx: dir seems corrupt? Run e2fsck -D. EXT4-fs error (device xx): ext4_dx_csum_verify:463: inode #xxxx: comm xxxxx: dir seems corrupt? Run e2fsck -D. EXT4-fs error (device xx): dx_probe:856: inode #xxxx: block 8188: comm xxxxx: Directory index failed checksum When enough files are created, the fake_dirent->reclen will be 0xffff. it doesn't equal to the blocksize 65536, i.e. 0x10000. But it is not the same condition when blocksize equals to 4k. when enough files are created, the fake_dirent->reclen will be 0x1000. it equals to the blocksize 4k, i.e. 0x1000. The problem seems to be related to the limitation of the 16-bit field when the blocksize is set to 64k. To address this, helpers like ext4_rec_len_{from,to}_disk has already been introduced to complete the conversion between the encoded and the plain form of rec_len. So fix this one by using the helper, and all the other in this file too. Cc: stable@kernel.org Fixes: dbe89444042a ("ext4: Calculate and verify checksums for htree nodes") Suggested-by: Andreas Dilger Suggested-by: Darrick J. Wong Signed-off-by: Shida Zhang Reviewed-by: Andreas Dilger Reviewed-by: Darrick J. Wong Link: https://lore.kernel.org/r/20230803060938.1929759-1-zhangshida@kylinos.cn Signed-off-by: Theodore Ts'o --- fs/ext4/namei.c | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index c0f0b4e2413b..c1ceccab05f5 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -343,17 +343,17 @@ static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, struct buffer_head *bh) { struct ext4_dir_entry_tail *t; + int blocksize = EXT4_BLOCK_SIZE(inode->i_sb); #ifdef PARANOID struct ext4_dir_entry *d, *top; d = (struct ext4_dir_entry *)bh->b_data; top = (struct ext4_dir_entry *)(bh->b_data + - (EXT4_BLOCK_SIZE(inode->i_sb) - - sizeof(struct ext4_dir_entry_tail))); - while (d < top && d->rec_len) + (blocksize - sizeof(struct ext4_dir_entry_tail))); + while (d < top && ext4_rec_len_from_disk(d->rec_len, blocksize)) d = (struct ext4_dir_entry *)(((void *)d) + - le16_to_cpu(d->rec_len)); + ext4_rec_len_from_disk(d->rec_len, blocksize)); if (d != top) return NULL; @@ -364,7 +364,8 @@ static struct ext4_dir_entry_tail *get_dirent_tail(struct inode *inode, #endif if (t->det_reserved_zero1 || - le16_to_cpu(t->det_rec_len) != sizeof(struct ext4_dir_entry_tail) || + (ext4_rec_len_from_disk(t->det_rec_len, blocksize) != + sizeof(struct ext4_dir_entry_tail)) || t->det_reserved_zero2 || t->det_reserved_ft != EXT4_FT_DIR_CSUM) return NULL; @@ -445,13 +446,14 @@ static struct dx_countlimit *get_dx_countlimit(struct inode *inode, struct ext4_dir_entry *dp; struct dx_root_info *root; int count_offset; + int blocksize = EXT4_BLOCK_SIZE(inode->i_sb); + unsigned int rlen = ext4_rec_len_from_disk(dirent->rec_len, blocksize); - if (le16_to_cpu(dirent->rec_len) == EXT4_BLOCK_SIZE(inode->i_sb)) + if (rlen == blocksize) count_offset = 8; - else if (le16_to_cpu(dirent->rec_len) == 12) { + else if (rlen == 12) { dp = (struct ext4_dir_entry *)(((void *)dirent) + 12); - if (le16_to_cpu(dp->rec_len) != - EXT4_BLOCK_SIZE(inode->i_sb) - 12) + if (ext4_rec_len_from_disk(dp->rec_len, blocksize) != blocksize - 12) return NULL; root = (struct dx_root_info *)(((void *)dp + 12)); if (root->reserved_zero || @@ -1315,6 +1317,7 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, unsigned int buflen = bh->b_size; char *base = bh->b_data; struct dx_hash_info h = *hinfo; + int blocksize = EXT4_BLOCK_SIZE(dir->i_sb); if (ext4_has_metadata_csum(dir->i_sb)) buflen -= sizeof(struct ext4_dir_entry_tail); @@ -1335,11 +1338,12 @@ static int dx_make_map(struct inode *dir, struct buffer_head *bh, map_tail--; map_tail->hash = h.hash; map_tail->offs = ((char *) de - base)>>2; - map_tail->size = le16_to_cpu(de->rec_len); + map_tail->size = ext4_rec_len_from_disk(de->rec_len, + blocksize); count++; cond_resched(); } - de = ext4_next_entry(de, dir->i_sb->s_blocksize); + de = ext4_next_entry(de, blocksize); } return count; } -- cgit v1.2.3 From c21a8027ad8a68c340d0d58bf1cc61dcb0bc4d2f Mon Sep 17 00:00:00 2001 From: Pavel Begunkov Date: Thu, 14 Sep 2023 16:51:09 +0100 Subject: io_uring/net: fix iter retargeting for selected buf When using selected buffer feature, io_uring delays data iter setup until later. If io_setup_async_msg() is called before that it might see not correctly setup iterator. Pre-init nr_segs and judge from its state whether we repointing. Cc: stable@vger.kernel.org Reported-by: syzbot+a4c6e5ef999b68b26ed1@syzkaller.appspotmail.com Fixes: 0455d4ccec548 ("io_uring: add POLL_FIRST support for send/sendmsg and recv/recvmsg") Signed-off-by: Pavel Begunkov Link: https://lore.kernel.org/r/0000000000002770be06053c7757@google.com Signed-off-by: Jens Axboe --- io_uring/net.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/io_uring/net.c b/io_uring/net.c index 3d07bf79c1e0..7a8e298af81b 100644 --- a/io_uring/net.c +++ b/io_uring/net.c @@ -183,6 +183,10 @@ static int io_setup_async_msg(struct io_kiocb *req, memcpy(async_msg, kmsg, sizeof(*kmsg)); if (async_msg->msg.msg_name) async_msg->msg.msg_name = &async_msg->addr; + + if ((req->flags & REQ_F_BUFFER_SELECT) && !async_msg->msg.msg_iter.nr_segs) + return -EAGAIN; + /* if were using fast_iov, set it to the new one */ if (iter_is_iovec(&kmsg->msg.msg_iter) && !kmsg->free_iov) { size_t fast_idx = iter_iov(&kmsg->msg.msg_iter) - kmsg->fast_iov; @@ -542,6 +546,7 @@ static int io_recvmsg_copy_hdr(struct io_kiocb *req, struct io_async_msghdr *iomsg) { iomsg->msg.msg_name = &iomsg->addr; + iomsg->msg.msg_iter.nr_segs = 0; #ifdef CONFIG_COMPAT if (req->ctx->compat) -- cgit v1.2.3 From c8870379a21fbd9ad14ca36204ccfbe9d25def43 Mon Sep 17 00:00:00 2001 From: Mariusz Tkaczyk Date: Thu, 14 Sep 2023 17:24:16 +0200 Subject: md: Put the right device in md_seq_next If there are multiple arrays in system and one mddevice is marked with MD_DELETED and md_seq_next() is called in the middle of removal then it _get()s proper device but it may _put() deleted one. As a result, active counter may never be zeroed for mddevice and it cannot be removed. Put the device which has been _get with previous md_seq_next() call. Cc: stable@vger.kernel.org Fixes: 12a6caf27324 ("md: only delete entries from all_mddevs when the disk is freed") Reported-by: AceLan Kao Closes: https://bugzilla.kernel.org/show_bug.cgi?id=217798 Cc: Yu Kuai Signed-off-by: Mariusz Tkaczyk Signed-off-by: Song Liu Link: https://lore.kernel.org/r/20230914152416.10819-1-mariusz.tkaczyk@linux.intel.com --- drivers/md/md.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/md/md.c b/drivers/md/md.c index 73758b754127..a104a025084d 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -8265,7 +8265,7 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) spin_unlock(&all_mddevs_lock); if (to_put) - mddev_put(mddev); + mddev_put(to_put); return next_mddev; } -- cgit v1.2.3 From c86e9ae5e3ad82969fe395414d1d9f173f8e9fd4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Sun, 10 Sep 2023 21:44:13 +0900 Subject: kbuild: fix kernel-devel RPM package and linux-headers Deb package Since commit fe66b5d2ae72 ("kbuild: refactor kernel-devel RPM package and linux-headers Deb package"), the kernel-devel RPM package and linux-headers Deb package are broken. I double-quoted the $(find ... -type d), which resulted in newlines being included in the argument to the outer find comment. find: 'arch/arm64/include\narch/arm64/kvm/hyp/include': No such file or directory The outer find command is unneeded. Fixes: fe66b5d2ae72 ("kbuild: refactor kernel-devel RPM package and linux-headers Deb package") Reported-by: Karolis M Signed-off-by: Masahiro Yamada Reviewed-by: Nicolas Schier --- scripts/package/install-extmod-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/package/install-extmod-build b/scripts/package/install-extmod-build index af7fe9f5b1e4..8a7051fad087 100755 --- a/scripts/package/install-extmod-build +++ b/scripts/package/install-extmod-build @@ -20,7 +20,7 @@ mkdir -p "${destdir}" find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*' find include scripts -type f -o -type l find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform - find "$(find "arch/${SRCARCH}" -name include -o -name scripts -type d)" -type f + find "arch/${SRCARCH}" -name include -o -name scripts -type d ) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}" { -- cgit v1.2.3 From 552c5013f2bc648611395ea80df6250aa4fe28f6 Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Mon, 11 Sep 2023 10:01:29 +0200 Subject: kbuild: avoid long argument lists in make modules_install Running "make modules_install" may fail with make[2]: execvp: /bin/sh: Argument list too long if many modules are built and INSTALL_MOD_PATH is long. This is because scripts/Makefile.modinst creates all directories with one mkdir command. Use $(foreach ...) instead to prevent an excessive argument list. Fixes: 2dfec887c0fd ("kbuild: reduce the number of mkdir calls during modules_install") Signed-off-by: Michal Kubecek Signed-off-by: Masahiro Yamada --- scripts/Makefile.modinst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/Makefile.modinst b/scripts/Makefile.modinst index c59cc57286ba..346f5ec50682 100644 --- a/scripts/Makefile.modinst +++ b/scripts/Makefile.modinst @@ -113,7 +113,7 @@ quiet_cmd_sign := endif # Create necessary directories -$(shell mkdir -p $(sort $(dir $(install-y)))) +$(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir))) $(dst)/%.ko: $(extmod_prefix)%.ko FORCE $(call cmd,install) -- cgit v1.2.3 From fb2c10245f201278804a6f28e196e95436059d6d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 14 Sep 2023 21:42:20 +0200 Subject: thermal: core: Fix disabled trip point check in handle_thermal_trip() Commit bc840ea5f9a9 ("thermal: core: Do not handle trip points with invalid temperature") added a check for invalid temperature to the disabled trip point check in handle_thermal_trip(), but that check was added at a point when the trip structure has not been initialized yet. This may cause handle_thermal_trip() to skip a valid trip point in some cases, so fix it by moving the check to a suitable place, after __thermal_zone_get_trip() has been called to populate the trip structure. Fixes: bc840ea5f9a9 ("thermal: core: Do not handle trip points with invalid temperature") Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_core.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/thermal/thermal_core.c b/drivers/thermal/thermal_core.c index 8717a3343512..58533ea75cd9 100644 --- a/drivers/thermal/thermal_core.c +++ b/drivers/thermal/thermal_core.c @@ -348,12 +348,14 @@ static void handle_thermal_trip(struct thermal_zone_device *tz, int trip_id) struct thermal_trip trip; /* Ignore disabled trip points */ - if (test_bit(trip_id, &tz->trips_disabled) || - trip.temperature == THERMAL_TEMP_INVALID) + if (test_bit(trip_id, &tz->trips_disabled)) return; __thermal_zone_get_trip(tz, trip_id, &trip); + if (trip.temperature == THERMAL_TEMP_INVALID) + return; + if (tz->last_temperature != THERMAL_TEMP_INVALID) { if (tz->last_temperature < trip.temperature && tz->temperature >= trip.temperature) -- cgit v1.2.3 From 6cc834ba62998c65c42d0c63499bdd35067151ec Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Tue, 12 Sep 2023 14:38:58 -0700 Subject: nvme: avoid bogus CRTO values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices are reporting controller ready mode support, but return 0 for CRTO. These devices require a much higher time to ready than that, so they are failing to initialize after the driver starter preferring that value over CAP.TO. The spec requires that CAP.TO match the appropritate CRTO value, or be set to 0xff if CRTO is larger than that. This means that CAP.TO can be used to validate if CRTO is reliable, and provides an appropriate fallback for setting the timeout value if not. Use whichever is larger. Link: https://bugzilla.kernel.org/show_bug.cgi?id=217863 Reported-by: Cláudio Sampaio Reported-by: Felix Yan Tested-by: Felix Yan Based-on-a-patch-by: Felix Yan Cc: stable@vger.kernel.org Signed-off-by: Keith Busch --- drivers/nvme/host/core.c | 54 +++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 19 deletions(-) diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 37b6fa746662..0685ed4f2dc4 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -2245,25 +2245,8 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl) else ctrl->ctrl_config = NVME_CC_CSS_NVM; - if (ctrl->cap & NVME_CAP_CRMS_CRWMS) { - u32 crto; - - ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CRTO, &crto); - if (ret) { - dev_err(ctrl->device, "Reading CRTO failed (%d)\n", - ret); - return ret; - } - - if (ctrl->cap & NVME_CAP_CRMS_CRIMS) { - ctrl->ctrl_config |= NVME_CC_CRIME; - timeout = NVME_CRTO_CRIMT(crto); - } else { - timeout = NVME_CRTO_CRWMT(crto); - } - } else { - timeout = NVME_CAP_TIMEOUT(ctrl->cap); - } + if (ctrl->cap & NVME_CAP_CRMS_CRWMS && ctrl->cap & NVME_CAP_CRMS_CRIMS) + ctrl->ctrl_config |= NVME_CC_CRIME; ctrl->ctrl_config |= (NVME_CTRL_PAGE_SHIFT - 12) << NVME_CC_MPS_SHIFT; ctrl->ctrl_config |= NVME_CC_AMS_RR | NVME_CC_SHN_NONE; @@ -2277,6 +2260,39 @@ int nvme_enable_ctrl(struct nvme_ctrl *ctrl) if (ret) return ret; + /* CAP value may change after initial CC write */ + ret = ctrl->ops->reg_read64(ctrl, NVME_REG_CAP, &ctrl->cap); + if (ret) + return ret; + + timeout = NVME_CAP_TIMEOUT(ctrl->cap); + if (ctrl->cap & NVME_CAP_CRMS_CRWMS) { + u32 crto, ready_timeout; + + ret = ctrl->ops->reg_read32(ctrl, NVME_REG_CRTO, &crto); + if (ret) { + dev_err(ctrl->device, "Reading CRTO failed (%d)\n", + ret); + return ret; + } + + /* + * CRTO should always be greater or equal to CAP.TO, but some + * devices are known to get this wrong. Use the larger of the + * two values. + */ + if (ctrl->ctrl_config & NVME_CC_CRIME) + ready_timeout = NVME_CRTO_CRIMT(crto); + else + ready_timeout = NVME_CRTO_CRWMT(crto); + + if (ready_timeout < timeout) + dev_warn_once(ctrl->device, "bad crto:%x cap:%llx\n", + crto, ctrl->cap); + else + timeout = ready_timeout; + } + ctrl->ctrl_config |= NVME_CC_ENABLE; ret = ctrl->ops->reg_write32(ctrl, NVME_REG_CC, ctrl->ctrl_config); if (ret) -- cgit v1.2.3 From 737dd811a3dbfd7edd4ad2ba5152e93d99074f83 Mon Sep 17 00:00:00 2001 From: Szuying Chen Date: Thu, 7 Sep 2023 16:17:10 +0800 Subject: ata: libahci: clear pending interrupt status When a CRC error occurs, the HBA asserts an interrupt to indicate an interface fatal error (PxIS.IFS). The ISR clears PxIE and PxIS, then does error recovery. If the adapter receives another SDB FIS with an error (PxIS.TFES) from the device before the start of the EH recovery process, the interrupt signaling the new SDB cannot be serviced as PxIE was cleared already. This in turn results in the HBA inability to issue any command during the error recovery process after setting PxCMD.ST to 1 because PxIS.TFES is still set. According to AHCI 1.3.1 specifications section 6.2.2, fatal errors notified by setting PxIS.HBFS, PxIS.HBDS, PxIS.IFS or PxIS.TFES will cause the HBA to enter the ERR:Fatal state. In this state, the HBA shall not issue any new commands. To avoid this situation, introduce the function ahci_port_clear_pending_irq() to clear pending interrupts before executing a COMRESET. This follows the AHCI 1.3.1 - section 6.2.2.2 specification. Signed-off-by: Szuying Chen Fixes: e0bfd149973d ("[PATCH] ahci: stop engine during hard reset") Cc: stable@vger.kernel.org Reviewed-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libahci.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/drivers/ata/libahci.c b/drivers/ata/libahci.c index e2bacedf28ef..f1263364fa97 100644 --- a/drivers/ata/libahci.c +++ b/drivers/ata/libahci.c @@ -1256,6 +1256,26 @@ static ssize_t ahci_activity_show(struct ata_device *dev, char *buf) return sprintf(buf, "%d\n", emp->blink_policy); } +static void ahci_port_clear_pending_irq(struct ata_port *ap) +{ + struct ahci_host_priv *hpriv = ap->host->private_data; + void __iomem *port_mmio = ahci_port_base(ap); + u32 tmp; + + /* clear SError */ + tmp = readl(port_mmio + PORT_SCR_ERR); + dev_dbg(ap->host->dev, "PORT_SCR_ERR 0x%x\n", tmp); + writel(tmp, port_mmio + PORT_SCR_ERR); + + /* clear port IRQ */ + tmp = readl(port_mmio + PORT_IRQ_STAT); + dev_dbg(ap->host->dev, "PORT_IRQ_STAT 0x%x\n", tmp); + if (tmp) + writel(tmp, port_mmio + PORT_IRQ_STAT); + + writel(1 << ap->port_no, hpriv->mmio + HOST_IRQ_STAT); +} + static void ahci_port_init(struct device *dev, struct ata_port *ap, int port_no, void __iomem *mmio, void __iomem *port_mmio) @@ -1270,18 +1290,7 @@ static void ahci_port_init(struct device *dev, struct ata_port *ap, if (rc) dev_warn(dev, "%s (%d)\n", emsg, rc); - /* clear SError */ - tmp = readl(port_mmio + PORT_SCR_ERR); - dev_dbg(dev, "PORT_SCR_ERR 0x%x\n", tmp); - writel(tmp, port_mmio + PORT_SCR_ERR); - - /* clear port IRQ */ - tmp = readl(port_mmio + PORT_IRQ_STAT); - dev_dbg(dev, "PORT_IRQ_STAT 0x%x\n", tmp); - if (tmp) - writel(tmp, port_mmio + PORT_IRQ_STAT); - - writel(1 << port_no, mmio + HOST_IRQ_STAT); + ahci_port_clear_pending_irq(ap); /* mark esata ports */ tmp = readl(port_mmio + PORT_CMD); @@ -1603,6 +1612,8 @@ int ahci_do_hardreset(struct ata_link *link, unsigned int *class, tf.status = ATA_BUSY; ata_tf_to_fis(&tf, 0, 0, d2h_fis); + ahci_port_clear_pending_irq(ap); + rc = sata_link_hardreset(link, timing, deadline, online, ahci_check_ready); -- cgit v1.2.3 From e3da4c401f2d088cf049769eb1e39c299867ee9d Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Fri, 15 Sep 2023 11:33:12 +0900 Subject: ata: pata_parport: Fix code style issues Fix indentation and other code style issues in the comm.c file. Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202309150646.n3iBvbPj-lkp@intel.com/ Signed-off-by: Damien Le Moal --- drivers/ata/pata_parport/comm.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/ata/pata_parport/comm.c b/drivers/ata/pata_parport/comm.c index 4839becbbd56..94b8d352102e 100644 --- a/drivers/ata/pata_parport/comm.c +++ b/drivers/ata/pata_parport/comm.c @@ -37,7 +37,7 @@ static int comm_read_regr(struct pi_adapter *pi, int cont, int regr) { int l, h, r; - r = regr + cont_map[cont]; + r = regr + cont_map[cont]; switch (pi->mode) { case 0: @@ -90,7 +90,6 @@ static void comm_connect(struct pi_adapter *pi) } static void comm_disconnect(struct pi_adapter *pi) - { w2(0); w2(0); w2(0); w2(4); w0(pi->saved_r0); @@ -172,12 +171,12 @@ static void comm_write_block(struct pi_adapter *pi, char *buf, int count) w4l(swab16(((u16 *)buf)[2 * k]) | swab16(((u16 *)buf)[2 * k + 1]) << 16); break; - } + } } static void comm_log_adapter(struct pi_adapter *pi) - -{ char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" }; +{ + char *mode_string[5] = { "4-bit", "8-bit", "EPP-8", "EPP-16", "EPP-32" }; dev_info(&pi->dev, "DataStor Commuter at 0x%x, mode %d (%s), delay %d\n", -- cgit v1.2.3 From ebc3d4e44a7e05457825e03d0560153687265523 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 15 Sep 2023 01:10:40 -0500 Subject: smb3: correct places where ENOTSUPP is used instead of preferred EOPNOTSUPP checkpatch flagged a few places with: WARNING: ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP Also fixed minor typo Signed-off-by: Steve French --- fs/smb/client/inode.c | 2 +- fs/smb/client/smb2ops.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c index de2dfbaae821..d7c302442c1e 100644 --- a/fs/smb/client/inode.c +++ b/fs/smb/client/inode.c @@ -2680,7 +2680,7 @@ int cifs_fiemap(struct inode *inode, struct fiemap_extent_info *fei, u64 start, } cifsFileInfo_put(cfile); - return -ENOTSUPP; + return -EOPNOTSUPP; } int cifs_truncate_page(struct address_space *mapping, loff_t from) diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c index d9eda2e958b4..9aeecee6b91b 100644 --- a/fs/smb/client/smb2ops.c +++ b/fs/smb/client/smb2ops.c @@ -297,7 +297,7 @@ smb2_adjust_credits(struct TCP_Server_Info *server, cifs_server_dbg(VFS, "request has less credits (%d) than required (%d)", credits->value, new_val); - return -ENOTSUPP; + return -EOPNOTSUPP; } spin_lock(&server->req_lock); @@ -1161,7 +1161,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon, /* Use a fudge factor of 256 bytes in case we collide * with a different set_EAs command. */ - if(CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - + if (CIFSMaxBufSize - MAX_SMB2_CREATE_RESPONSE_SIZE - MAX_SMB2_CLOSE_RESPONSE_SIZE - 256 < used_len + ea_name_len + ea_value_len + 1) { rc = -ENOSPC; @@ -4591,7 +4591,7 @@ handle_read_data(struct TCP_Server_Info *server, struct mid_q_entry *mid, if (shdr->Command != SMB2_READ) { cifs_server_dbg(VFS, "only big read responses are supported\n"); - return -ENOTSUPP; + return -EOPNOTSUPP; } if (server->ops->is_session_expired && -- cgit v1.2.3 From 2c75426c1fea591bb338ba072068f83d2f6be088 Mon Sep 17 00:00:00 2001 From: Steve French Date: Fri, 15 Sep 2023 01:37:33 -0500 Subject: smb3: fix some minor typos and repeated words Minor cleanup pointed out by checkpatch (repeated words, missing blank lines) in smb2pdu.c and old header location referred to in transport.c Signed-off-by: Steve French --- fs/smb/client/smb2pdu.c | 6 ++++-- fs/smb/client/transport.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c index 3403188e3100..44d4943e9c56 100644 --- a/fs/smb/client/smb2pdu.c +++ b/fs/smb/client/smb2pdu.c @@ -89,6 +89,7 @@ smb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd, struct TCP_Server_Info *server) { struct smb3_hdr_req *smb3_hdr; + shdr->ProtocolId = SMB2_PROTO_NUMBER; shdr->StructureSize = cpu_to_le16(64); shdr->Command = smb2_cmd; @@ -2239,7 +2240,7 @@ create_durable_v2_buf(struct cifs_open_parms *oparms) * (most servers default to 120 seconds) and most clients default to 0. * This can be overridden at mount ("handletimeout=") if the user wants * a different persistent (or resilient) handle timeout for all opens - * opens on a particular SMB3 mount. + * on a particular SMB3 mount. */ buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout); buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT); @@ -2384,7 +2385,7 @@ add_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp) return 0; } -/* See See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ +/* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */ static void setup_owner_group_sids(char *buf) { struct owner_group_sids *sids = (struct owner_group_sids *)buf; @@ -3129,6 +3130,7 @@ void SMB2_ioctl_free(struct smb_rqst *rqst) { int i; + if (rqst && rqst->rq_iov) { cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */ for (i = 1; i < rqst->rq_nvec; i++) diff --git a/fs/smb/client/transport.c b/fs/smb/client/transport.c index 1b5d9794ed5b..d52057a511ee 100644 --- a/fs/smb/client/transport.c +++ b/fs/smb/client/transport.c @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include #include -- cgit v1.2.3 From cccd32816506cbac3a4c65d9dff51b3125ef1a03 Mon Sep 17 00:00:00 2001 From: Lukas Wunner Date: Fri, 15 Sep 2023 09:55:39 +0200 Subject: panic: Reenable preemption in WARN slowpath Commit: 5a5d7e9badd2 ("cpuidle: lib/bug: Disable rcu_is_watching() during WARN/BUG") amended warn_slowpath_fmt() to disable preemption until the WARN splat has been emitted. However the commit neglected to reenable preemption in the !fmt codepath, i.e. when a WARN splat is emitted without additional format string. One consequence is that users may see more splats than intended. E.g. a WARN splat emitted in a work item results in at least two extra splats: BUG: workqueue leaked lock or atomic (emitted by process_one_work()) BUG: scheduling while atomic (emitted by worker_thread() -> schedule()) Ironically the point of the commit was to *avoid* extra splats. ;) Fix it. Fixes: 5a5d7e9badd2 ("cpuidle: lib/bug: Disable rcu_is_watching() during WARN/BUG") Signed-off-by: Lukas Wunner Signed-off-by: Ingo Molnar Cc: Linus Torvalds Cc: Thomas Gleixner Cc: Paul E. McKenney Link: https://lore.kernel.org/r/3ec48fde01e4ee6505f77908ba351bad200ae3d1.1694763684.git.lukas@wunner.de --- kernel/panic.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/panic.c b/kernel/panic.c index 07239d4ad81e..ffa037fa777d 100644 --- a/kernel/panic.c +++ b/kernel/panic.c @@ -697,6 +697,7 @@ void warn_slowpath_fmt(const char *file, int line, unsigned taint, if (!fmt) { __warn(file, line, __builtin_return_address(0), taint, NULL, NULL); + warn_rcu_exit(rcu); return; } -- cgit v1.2.3 From 3c70de9b580998e5d644f4e80a9944c30aa1197b Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Fri, 15 Sep 2023 18:33:59 +0900 Subject: Revert "firewire: core: obsolete usage of GFP_ATOMIC at building node tree" This reverts commit 06f45435d985d60d7d2fe2424fbb9909d177a63d. John Ogness reports the case that the allocation is in atomic context under acquired spin-lock. [ 12.555784] BUG: sleeping function called from invalid context at include/linux/sched/mm.h:306 [ 12.555808] in_atomic(): 1, irqs_disabled(): 1, non_block: 0, pid: 70, name: kworker/1:2 [ 12.555814] preempt_count: 1, expected: 0 [ 12.555820] INFO: lockdep is turned off. [ 12.555824] irq event stamp: 208 [ 12.555828] hardirqs last enabled at (207): [] ._raw_spin_unlock_irq+0x44/0x80 [ 12.555850] hardirqs last disabled at (208): [] .__schedule+0x854/0xfe0 [ 12.555859] softirqs last enabled at (188): [] .addrconf_verify_rtnl+0x2c4/0xb70 [ 12.555872] softirqs last disabled at (182): [] .addrconf_verify_rtnl+0x70/0xb70 [ 12.555884] CPU: 1 PID: 70 Comm: kworker/1:2 Tainted: G S 6.6.0-rc1 #1 [ 12.555893] Hardware name: PowerMac7,2 PPC970 0x390202 PowerMac [ 12.555898] Workqueue: firewire_ohci .bus_reset_work [firewire_ohci] [ 12.555939] Call Trace: [ 12.555944] [c000000009677830] [c0000000010d83c0] .dump_stack_lvl+0x8c/0xd0 (unreliable) [ 12.555963] [c0000000096778b0] [c000000000140270] .__might_resched+0x320/0x340 [ 12.555978] [c000000009677940] [c000000000497600] .__kmem_cache_alloc_node+0x390/0x460 [ 12.555993] [c000000009677a10] [c0000000003fe620] .__kmalloc+0x70/0x310 [ 12.556007] [c000000009677ac0] [c0003d00004e2268] .fw_core_handle_bus_reset+0x2c8/0xba0 [firewire_core] [ 12.556060] [c000000009677c20] [c0003d0000491190] .bus_reset_work+0x330/0x9b0 [firewire_ohci] [ 12.556079] [c000000009677d10] [c00000000011d0d0] .process_one_work+0x280/0x6f0 [ 12.556094] [c000000009677e10] [c00000000011d8a0] .worker_thread+0x360/0x500 [ 12.556107] [c000000009677ef0] [c00000000012e3b4] .kthread+0x154/0x160 [ 12.556120] [c000000009677f90] [c00000000000bfa8] .start_kernel_thread+0x10/0x14 Cc: stable@kernel.org Reported-by: John Ogness Link: https://lore.kernel.org/lkml/87jzsuv1xk.fsf@jogness.linutronix.de/raw Signed-off-by: Takashi Sakamoto --- drivers/firewire/core-device.c | 2 +- drivers/firewire/core-topology.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/firewire/core-device.c b/drivers/firewire/core-device.c index a3104e35412c..aa597cda0d88 100644 --- a/drivers/firewire/core-device.c +++ b/drivers/firewire/core-device.c @@ -1211,7 +1211,7 @@ void fw_node_event(struct fw_card *card, struct fw_node *node, int event) * without actually having a link. */ create: - device = kzalloc(sizeof(*device), GFP_KERNEL); + device = kzalloc(sizeof(*device), GFP_ATOMIC); if (device == NULL) break; diff --git a/drivers/firewire/core-topology.c b/drivers/firewire/core-topology.c index 88466b663482..f40c81534381 100644 --- a/drivers/firewire/core-topology.c +++ b/drivers/firewire/core-topology.c @@ -101,7 +101,7 @@ static struct fw_node *fw_node_create(u32 sid, int port_count, int color) { struct fw_node *node; - node = kzalloc(struct_size(node, ports, port_count), GFP_KERNEL); + node = kzalloc(struct_size(node, ports, port_count), GFP_ATOMIC); if (node == NULL) return NULL; -- cgit v1.2.3 From a9ce385344f916cd1c36a33905e564f5581beae9 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Fri, 15 Sep 2023 13:14:23 -0600 Subject: dm: don't attempt to queue IO under RCU protection dm looks up the table for IO based on the request type, with an assumption that if the request is marked REQ_NOWAIT, it's fine to attempt to submit that IO while under RCU read lock protection. This is not OK, as REQ_NOWAIT just means that we should not be sleeping waiting on other IO, it does not mean that we can't potentially schedule. A simple test case demonstrates this quite nicely: int main(int argc, char *argv[]) { struct iovec iov; int fd; fd = open("/dev/dm-0", O_RDONLY | O_DIRECT); posix_memalign(&iov.iov_base, 4096, 4096); iov.iov_len = 4096; preadv2(fd, &iov, 1, 0, RWF_NOWAIT); return 0; } which will instantly spew: BUG: sleeping function called from invalid context at include/linux/sched/mm.h:306 in_atomic(): 0, irqs_disabled(): 0, non_block: 0, pid: 5580, name: dm-nowait preempt_count: 0, expected: 0 RCU nest depth: 1, expected: 0 INFO: lockdep is turned off. CPU: 7 PID: 5580 Comm: dm-nowait Not tainted 6.6.0-rc1-g39956d2dcd81 #132 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.16.2-debian-1.16.2-1 04/01/2014 Call Trace: dump_stack_lvl+0x11d/0x1b0 __might_resched+0x3c3/0x5e0 ? preempt_count_sub+0x150/0x150 mempool_alloc+0x1e2/0x390 ? mempool_resize+0x7d0/0x7d0 ? lock_sync+0x190/0x190 ? lock_release+0x4b7/0x670 ? internal_get_user_pages_fast+0x868/0x2d40 bio_alloc_bioset+0x417/0x8c0 ? bvec_alloc+0x200/0x200 ? internal_get_user_pages_fast+0xb8c/0x2d40 bio_alloc_clone+0x53/0x100 dm_submit_bio+0x27f/0x1a20 ? lock_release+0x4b7/0x670 ? blk_try_enter_queue+0x1a0/0x4d0 ? dm_dax_direct_access+0x260/0x260 ? rcu_is_watching+0x12/0xb0 ? blk_try_enter_queue+0x1cc/0x4d0 __submit_bio+0x239/0x310 ? __bio_queue_enter+0x700/0x700 ? kvm_clock_get_cycles+0x40/0x60 ? ktime_get+0x285/0x470 submit_bio_noacct_nocheck+0x4d9/0xb80 ? should_fail_request+0x80/0x80 ? preempt_count_sub+0x150/0x150 ? lock_release+0x4b7/0x670 ? __bio_add_page+0x143/0x2d0 ? iov_iter_revert+0x27/0x360 submit_bio_noacct+0x53e/0x1b30 submit_bio_wait+0x10a/0x230 ? submit_bio_wait_endio+0x40/0x40 __blkdev_direct_IO_simple+0x4f8/0x780 ? blkdev_bio_end_io+0x4c0/0x4c0 ? stack_trace_save+0x90/0xc0 ? __bio_clone+0x3c0/0x3c0 ? lock_release+0x4b7/0x670 ? lock_sync+0x190/0x190 ? atime_needs_update+0x3bf/0x7e0 ? timestamp_truncate+0x21b/0x2d0 ? inode_owner_or_capable+0x240/0x240 blkdev_direct_IO.part.0+0x84a/0x1810 ? rcu_is_watching+0x12/0xb0 ? lock_release+0x4b7/0x670 ? blkdev_read_iter+0x40d/0x530 ? reacquire_held_locks+0x4e0/0x4e0 ? __blkdev_direct_IO_simple+0x780/0x780 ? rcu_is_watching+0x12/0xb0 ? __mark_inode_dirty+0x297/0xd50 ? preempt_count_add+0x72/0x140 blkdev_read_iter+0x2a4/0x530 do_iter_readv_writev+0x2f2/0x3c0 ? generic_copy_file_range+0x1d0/0x1d0 ? fsnotify_perm.part.0+0x25d/0x630 ? security_file_permission+0xd8/0x100 do_iter_read+0x31b/0x880 ? import_iovec+0x10b/0x140 vfs_readv+0x12d/0x1a0 ? vfs_iter_read+0xb0/0xb0 ? rcu_is_watching+0x12/0xb0 ? rcu_is_watching+0x12/0xb0 ? lock_release+0x4b7/0x670 do_preadv+0x1b3/0x260 ? do_readv+0x370/0x370 __x64_sys_preadv2+0xef/0x150 do_syscall_64+0x39/0xb0 entry_SYSCALL_64_after_hwframe+0x63/0xcd RIP: 0033:0x7f5af41ad806 Code: 41 54 41 89 fc 55 44 89 c5 53 48 89 cb 48 83 ec 18 80 3d e4 dd 0d 00 00 74 7a 45 89 c1 49 89 ca 45 31 c0 b8 47 01 00 00 0f 05 <48> 3d 00 f0 ff ff 0f 87 be 00 00 00 48 85 c0 79 4a 48 8b 0d da 55 RSP: 002b:00007ffd3145c7f0 EFLAGS: 00000246 ORIG_RAX: 0000000000000147 RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f5af41ad806 RDX: 0000000000000001 RSI: 00007ffd3145c850 RDI: 0000000000000003 RBP: 0000000000000008 R08: 0000000000000000 R09: 0000000000000008 R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000003 R13: 00007ffd3145c850 R14: 000055f5f0431dd8 R15: 0000000000000001 where in fact it is dm itself that attempts to allocate a bio clone with GFP_NOIO under the rcu read lock, regardless of the request type. Fix this by getting rid of the special casing for REQ_NOWAIT, and just use the normal SRCU protected table lookup. Get rid of the bio based table locking helpers at the same time, as they are now unused. Cc: stable@vger.kernel.org Fixes: 563a225c9fd2 ("dm: introduce dm_{get,put}_live_table_bio called from dm_submit_bio") Signed-off-by: Jens Axboe Signed-off-by: Mike Snitzer --- drivers/md/dm.c | 23 ++--------------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index f0f118ab20fa..64a1f306c96c 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -715,24 +715,6 @@ static void dm_put_live_table_fast(struct mapped_device *md) __releases(RCU) rcu_read_unlock(); } -static inline struct dm_table *dm_get_live_table_bio(struct mapped_device *md, - int *srcu_idx, blk_opf_t bio_opf) -{ - if (bio_opf & REQ_NOWAIT) - return dm_get_live_table_fast(md); - else - return dm_get_live_table(md, srcu_idx); -} - -static inline void dm_put_live_table_bio(struct mapped_device *md, int srcu_idx, - blk_opf_t bio_opf) -{ - if (bio_opf & REQ_NOWAIT) - dm_put_live_table_fast(md); - else - dm_put_live_table(md, srcu_idx); -} - static char *_dm_claim_ptr = "I belong to device-mapper"; /* @@ -1833,9 +1815,8 @@ static void dm_submit_bio(struct bio *bio) struct mapped_device *md = bio->bi_bdev->bd_disk->private_data; int srcu_idx; struct dm_table *map; - blk_opf_t bio_opf = bio->bi_opf; - map = dm_get_live_table_bio(md, &srcu_idx, bio_opf); + map = dm_get_live_table(md, &srcu_idx); /* If suspended, or map not yet available, queue this IO for later */ if (unlikely(test_bit(DMF_BLOCK_IO_FOR_SUSPEND, &md->flags)) || @@ -1851,7 +1832,7 @@ static void dm_submit_bio(struct bio *bio) dm_split_and_process_bio(md, map, bio); out: - dm_put_live_table_bio(md, srcu_idx, bio_opf); + dm_put_live_table(md, srcu_idx); } static bool dm_poll_dm_io(struct dm_io *io, struct io_comp_batch *iob, -- cgit v1.2.3 From 80cc944eca4f0baa9c381d0706f3160e491437f2 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 Sep 2023 00:19:16 +0200 Subject: ata: libata-eh: do not clear ATA_PFLAG_EH_PENDING in ata_eh_reset() ata_scsi_port_error_handler() starts off by clearing ATA_PFLAG_EH_PENDING, before calling ap->ops->error_handler() (without holding the ap->lock). If an error IRQ is received while ap->ops->error_handler() is running, the irq handler will set ATA_PFLAG_EH_PENDING. Once ap->ops->error_handler() returns, ata_scsi_port_error_handler() checks if ATA_PFLAG_EH_PENDING is set, and if it is, another iteration of ATA EH is performed. The problem is that ATA_PFLAG_EH_PENDING is not only cleared by ata_scsi_port_error_handler(), it is also cleared by ata_eh_reset(). ata_eh_reset() is called by ap->ops->error_handler(). This additional clearing done by ata_eh_reset() breaks the whole retry logic in ata_scsi_port_error_handler(). Thus, if an error IRQ is received while ap->ops->error_handler() is running, the port will currently remain frozen and will never get re-enabled. The additional clearing in ata_eh_reset() was introduced in commit 1e641060c4b5 ("libata: clear eh_info on reset completion"). Looking at the original error report: https://marc.info/?l=linux-ide&m=124765325828495&w=2 We can see the following happening: [ 1.074659] ata3: XXX port freeze [ 1.074700] ata3: XXX hardresetting link, stopping engine [ 1.074746] ata3: XXX flipping SControl [ 1.411471] ata3: XXX irq_stat=400040 CONN|PHY [ 1.411475] ata3: XXX port freeze [ 1.420049] ata3: XXX starting engine [ 1.420096] ata3: XXX rc=0, class=1 [ 1.420142] ata3: XXX clearing IRQs for thawing [ 1.420188] ata3: XXX port thawed [ 1.420234] ata3: SATA link up 3.0 Gbps (SStatus 123 SControl 300) We are not supposed to be able to receive an error IRQ while the port is frozen (PxIE is set to 0, i.e. all IRQs for the port are disabled). AHCI 1.3.1 section 10.7.1.1 First Tier (IS Register) states: "Each bit location can be thought of as reporting a '1' if the virtual "interrupt line" for that port is indicating it wishes to generate an interrupt. That is, if a port has one or more interrupt status bit set, and the enables for those status bits are set, then this bit shall be set." Additionally, AHCI state P:ComInit clearly shows that the state machine will only jump to P:ComInitSetIS (which sets IS.IPS(x) to '1'), if PxIE.PCE is set to '1'. In our case, PxIE is set to 0, so IS.IPS(x) won't get set. So IS.IPS(x) only gets set if PxIS and PxIE is set. AHCI 1.3.1 section 10.7.1.1 First Tier (IS Register) also states: "The bits in this register are read/write clear. It is set by the level of the virtual interrupt line being a set, and cleared by a write of '1' from the software." So if IS.IPS(x) is set, you need to explicitly clear it by writing a 1 to IS.IPS(x) for that port. Since PxIE is cleared, the only way to get an interrupt while the port is frozen, is if IS.IPS(x) is set, and the only way IS.IPS(x) can be set when the port is frozen, is if it was set before the port was frozen. However, since commit 737dd811a3db ("ata: libahci: clear pending interrupt status"), we clear both PxIS and IS.IPS(x) after freezing the port, but before the COMRESET, so the problem that commit 1e641060c4b5 ("libata: clear eh_info on reset completion") fixed can no longer happen. Thus, revert commit 1e641060c4b5 ("libata: clear eh_info on reset completion"), so that the retry logic in ata_scsi_port_error_handler() works once again. (The retry logic is still needed, since we can still get an error IRQ _after_ the port has been thawed, but before ata_scsi_port_error_handler() takes the ap->lock in order to check if ATA_PFLAG_EH_PENDING is set.) Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 159ba6ba19eb..5c493b6316eb 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2796,18 +2796,11 @@ int ata_eh_reset(struct ata_link *link, int classify, } } - /* - * Some controllers can't be frozen very well and may set spurious - * error conditions during reset. Clear accumulated error - * information and re-thaw the port if frozen. As reset is the - * final recovery action and we cross check link onlineness against - * device classification later, no hotplug event is lost by this. - */ + /* clear cached SError */ spin_lock_irqsave(link->ap->lock, flags); - memset(&link->eh_info, 0, sizeof(link->eh_info)); + link->eh_info.serror = 0; if (slave) - memset(&slave->eh_info, 0, sizeof(link->eh_info)); - ap->pflags &= ~ATA_PFLAG_EH_PENDING; + slave->eh_info.serror = 0; spin_unlock_irqrestore(link->ap->lock, flags); if (ata_port_is_frozen(ap)) -- cgit v1.2.3 From 7a3bc2b3989e05bbaa904a63279049a401491c84 Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Thu, 14 Sep 2023 00:19:17 +0200 Subject: ata: libata-eh: do not thaw the port twice in ata_eh_reset() commit 1e641060c4b5 ("libata: clear eh_info on reset completion") added a workaround that broke the retry mechanism in ATA EH. Tejun himself suggested to remove this workaround when it was identified to cause additional problems: https://lore.kernel.org/linux-ide/20110426135027.GI878@htj.dyndns.org/ He even said: "Hmm... it seems I wasn't thinking straight when I added that work around." https://lore.kernel.org/linux-ide/20110426155229.GM878@htj.dyndns.org/ While removing the workaround solved the issue, however, the workaround was kept to avoid "spurious hotplug events during reset", and instead another workaround was added on top of the existing workaround in commit 8c56cacc724c ("libata: fix unexpectedly frozen port after ata_eh_reset()"). Because these IRQs happened when the port was frozen, we know that they were actually a side effect of PxIS and IS.IPS(x) not being cleared before the COMRESET. This is now done in commit 94152042eaa9 ("ata: libahci: clear pending interrupt status"), so these workarounds can now be removed. Since commit 1e641060c4b5 ("libata: clear eh_info on reset completion") has now been reverted, the ATA EH retry mechanism is functional again, so there is once again no need to thaw the port more than once in ata_eh_reset(). This reverts "the workaround on top of the workaround" introduced in commit 8c56cacc724c ("libata: fix unexpectedly frozen port after ata_eh_reset()"). Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-eh.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 5c493b6316eb..4cf4f57e57b8 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -2803,9 +2803,6 @@ int ata_eh_reset(struct ata_link *link, int classify, slave->eh_info.serror = 0; spin_unlock_irqrestore(link->ap->lock, flags); - if (ata_port_is_frozen(ap)) - ata_eh_thaw_port(ap); - /* * Make sure onlineness and classification result correspond. * Hotplug could have happened during reset and some -- cgit v1.2.3 From 5e35a9ac3fe3a0d571b899a16ca84253e53dc70c Mon Sep 17 00:00:00 2001 From: Niklas Cassel Date: Wed, 13 Sep 2023 17:04:43 +0200 Subject: ata: libata-core: fetch sense data for successful commands iff CDL enabled Currently, we fetch sense data for a _successful_ command if either: 1) Command was NCQ and ATA_DFLAG_CDL_ENABLED flag set (flag ATA_DFLAG_CDL_ENABLED will only be set if the Successful NCQ command sense data supported bit is set); or 2) Command was non-NCQ and regular sense data reporting is enabled. This means that case 2) will trigger for a non-NCQ command which has ATA_SENSE bit set, regardless if CDL is enabled or not. This decision was by design. If the device reports that it has sense data available, it makes sense to fetch that sense data, since the sk/asc/ascq could be important information regardless if CDL is enabled or not. However, the fetching of sense data for a successful command is done via ATA EH. Considering how intricate the ATA EH is, we really do not want to invoke ATA EH unless absolutely needed. Before commit 18bd7718b5c4 ("scsi: ata: libata: Handle completion of CDL commands using policy 0xD") we never fetched sense data for successful commands. In order to not invoke the ATA EH unless absolutely necessary, even if the device claims support for sense data reporting, only fetch sense data for successful (NCQ and non-NCQ commands) commands that are using CDL. [Damien] Modified the check to test the qc flag ATA_QCFLAG_HAS_CDL instead of the device support for CDL, which is implied for commands using CDL. Fixes: 3ac873c76d79 ("ata: libata-core: fix when to fetch sense data for successful commands") Signed-off-by: Niklas Cassel Signed-off-by: Damien Le Moal --- drivers/ata/libata-core.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index 74314311295f..0072e0f9ad39 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4783,11 +4783,8 @@ void ata_qc_complete(struct ata_queued_cmd *qc) * been aborted by the device due to a limit timeout using the policy * 0xD. For these commands, invoke EH to get the command sense data. */ - if (qc->result_tf.status & ATA_SENSE && - ((ata_is_ncq(qc->tf.protocol) && - dev->flags & ATA_DFLAG_CDL_ENABLED) || - (!ata_is_ncq(qc->tf.protocol) && - ata_id_sense_reporting_enabled(dev->id)))) { + if (qc->flags & ATA_QCFLAG_HAS_CDL && + qc->result_tf.status & ATA_SENSE) { /* * Tell SCSI EH to not overwrite scmd->result even if this * command is finished with result SAM_STAT_GOOD. -- cgit v1.2.3 From 3cec50490969afd4a76ccee441f747d869ccff77 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 16 Sep 2023 12:31:42 -0700 Subject: vm: fix move_vma() memory accounting being off Commit 408579cd627a ("mm: Update do_vmi_align_munmap() return semantics") seems to have updated one of the callers of do_vmi_munmap() incorrectly: it used to check for the error case (which didn't change: negative means error). That commit changed the check to the success case (which did change: before that commit, 0 was success, and 1 was "success and lock downgraded". After the change, it's always 0 for success, and the lock will have been released if requested). This didn't change any actual VM behavior _except_ for memory accounting when 'VM_ACCOUNT' was set on the vma. Which made the wrong return value test fairly subtle, since everything continues to work. Or rather - it continues to work but the "Committed memory" accounting goes all wonky (Committed_AS value in /proc/meminfo), and depending on settings that then causes problems much much later as the VM relies on bogus statistics for its heuristics. Revert that one line of the change back to the original logic. Fixes: 408579cd627a ("mm: Update do_vmi_align_munmap() return semantics") Reported-by: Christoph Biedl Reported-bisected-and-tested-by: Michael Labiuk Cc: Bagas Sanjaya Cc: Liam R. Howlett Link: https://lore.kernel.org/all/1694366957@msgid.manchmal.in-ulm.de/ Signed-off-by: Linus Torvalds --- mm/mremap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/mremap.c b/mm/mremap.c index 056478c106ee..382e81c33fc4 100644 --- a/mm/mremap.c +++ b/mm/mremap.c @@ -715,7 +715,7 @@ static unsigned long move_vma(struct vm_area_struct *vma, } vma_iter_init(&vmi, mm, old_addr); - if (!do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false)) { + if (do_vmi_munmap(&vmi, mm, old_addr, old_len, uf_unmap, false) < 0) { /* OOM: unable to split vma, just get accounts right */ if (vm_flags & VM_ACCOUNT && !(flags & MREMAP_DONTUNMAP)) vm_acct_memory(old_len >> PAGE_SHIFT); -- cgit v1.2.3 From f530ee95b72e77b09c141c4b1a4b94d1199ffbd9 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Fri, 15 Sep 2023 10:02:21 +0300 Subject: x86/boot/compressed: Reserve more memory for page tables The decompressor has a hard limit on the number of page tables it can allocate. This limit is defined at compile-time and will cause boot failure if it is reached. The kernel is very strict and calculates the limit precisely for the worst-case scenario based on the current configuration. However, it is easy to forget to adjust the limit when a new use-case arises. The worst-case scenario is rarely encountered during sanity checks. In the case of enabling 5-level paging, a use-case was overlooked. The limit needs to be increased by one to accommodate the additional level. This oversight went unnoticed until Aaron attempted to run the kernel via kexec with 5-level paging and unaccepted memory enabled. Update wost-case calculations to include 5-level paging. To address this issue, let's allocate some extra space for page tables. 128K should be sufficient for any use-case. The logic can be simplified by using a single value for all kernel configurations. [ Also add a warning, should this memory run low - by Dave Hansen. ] Fixes: 34bbb0009f3b ("x86/boot/compressed: Enable 5-level paging during decompression stage") Reported-by: Aaron Lu Signed-off-by: Kirill A. Shutemov Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/20230915070221.10266-1-kirill.shutemov@linux.intel.com --- arch/x86/boot/compressed/ident_map_64.c | 8 ++++++ arch/x86/include/asm/boot.h | 45 +++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/arch/x86/boot/compressed/ident_map_64.c b/arch/x86/boot/compressed/ident_map_64.c index bcc956c17872..08f93b0401bb 100644 --- a/arch/x86/boot/compressed/ident_map_64.c +++ b/arch/x86/boot/compressed/ident_map_64.c @@ -59,6 +59,14 @@ static void *alloc_pgt_page(void *context) return NULL; } + /* Consumed more tables than expected? */ + if (pages->pgt_buf_offset == BOOT_PGT_SIZE_WARN) { + debug_putstr("pgt_buf running low in " __FILE__ "\n"); + debug_putstr("Need to raise BOOT_PGT_SIZE?\n"); + debug_putaddr(pages->pgt_buf_offset); + debug_putaddr(pages->pgt_buf_size); + } + entry = pages->pgt_buf + pages->pgt_buf_offset; pages->pgt_buf_offset += PAGE_SIZE; diff --git a/arch/x86/include/asm/boot.h b/arch/x86/include/asm/boot.h index 4ae14339cb8c..b3a7cfb0d99e 100644 --- a/arch/x86/include/asm/boot.h +++ b/arch/x86/include/asm/boot.h @@ -40,23 +40,40 @@ #ifdef CONFIG_X86_64 # define BOOT_STACK_SIZE 0x4000 +/* + * Used by decompressor's startup_32() to allocate page tables for identity + * mapping of the 4G of RAM in 4-level paging mode: + * - 1 level4 table; + * - 1 level3 table; + * - 4 level2 table that maps everything with 2M pages; + * + * The additional level5 table needed for 5-level paging is allocated from + * trampoline_32bit memory. + */ # define BOOT_INIT_PGT_SIZE (6*4096) -# ifdef CONFIG_RANDOMIZE_BASE + /* - * Assuming all cross the 512GB boundary: - * 1 page for level4 - * (2+2)*4 pages for kernel, param, cmd_line, and randomized kernel - * 2 pages for first 2M (video RAM: CONFIG_X86_VERBOSE_BOOTUP). - * Total is 19 pages. + * Total number of page tables kernel_add_identity_map() can allocate, + * including page tables consumed by startup_32(). + * + * Worst-case scenario: + * - 5-level paging needs 1 level5 table; + * - KASLR needs to map kernel, boot_params, cmdline and randomized kernel, + * assuming all of them cross 256T boundary: + * + 4*2 level4 table; + * + 4*2 level3 table; + * + 4*2 level2 table; + * - X86_VERBOSE_BOOTUP needs to map the first 2M (video RAM): + * + 1 level4 table; + * + 1 level3 table; + * + 1 level2 table; + * Total: 28 tables + * + * Add 4 spare table in case decompressor touches anything beyond what is + * accounted above. Warn if it happens. */ -# ifdef CONFIG_X86_VERBOSE_BOOTUP -# define BOOT_PGT_SIZE (19*4096) -# else /* !CONFIG_X86_VERBOSE_BOOTUP */ -# define BOOT_PGT_SIZE (17*4096) -# endif -# else /* !CONFIG_RANDOMIZE_BASE */ -# define BOOT_PGT_SIZE BOOT_INIT_PGT_SIZE -# endif +# define BOOT_PGT_SIZE_WARN (28*4096) +# define BOOT_PGT_SIZE (32*4096) #else /* !CONFIG_X86_64 */ # define BOOT_STACK_SIZE 0x1000 -- cgit v1.2.3 From 75b2f7e4c9e0fd750a5a27ca9736d1daa7a3762a Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 14 Sep 2023 10:01:38 -0700 Subject: x86/purgatory: Remove LTO flags -flto* implies -ffunction-sections. With LTO enabled, ld.lld generates multiple .text sections for purgatory.ro: $ readelf -S purgatory.ro | grep " .text" [ 1] .text PROGBITS 0000000000000000 00000040 [ 7] .text.purgatory PROGBITS 0000000000000000 000020e0 [ 9] .text.warn PROGBITS 0000000000000000 000021c0 [13] .text.sha256_upda PROGBITS 0000000000000000 000022f0 [15] .text.sha224_upda PROGBITS 0000000000000000 00002be0 [17] .text.sha256_fina PROGBITS 0000000000000000 00002bf0 [19] .text.sha224_fina PROGBITS 0000000000000000 00002cc0 This causes WARNING from kexec_purgatory_setup_sechdrs(): WARNING: CPU: 26 PID: 110894 at kernel/kexec_file.c:919 kexec_load_purgatory+0x37f/0x390 Fix this by disabling LTO for purgatory. [ AFAICT, x86 is the only arch that supports LTO and purgatory. ] We could also fix this with an explicit linker script to rejoin .text.* sections back into .text. However, given the benefit of LTOing purgatory is small, simply disable the production of more .text.* sections for now. Fixes: b33fff07e3e3 ("x86, build: allow LTO to be selected") Signed-off-by: Song Liu Signed-off-by: Ingo Molnar Reviewed-by: Nick Desaulniers Reviewed-by: Sami Tolvanen Link: https://lore.kernel.org/r/20230914170138.995606-1-song@kernel.org --- arch/x86/purgatory/Makefile | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile index c2a29be35c01..08aa0f25f12a 100644 --- a/arch/x86/purgatory/Makefile +++ b/arch/x86/purgatory/Makefile @@ -19,6 +19,10 @@ CFLAGS_sha256.o := -D__DISABLE_EXPORTS -D__NO_FORTIFY # optimization flags. KBUILD_CFLAGS := $(filter-out -fprofile-sample-use=% -fprofile-use=%,$(KBUILD_CFLAGS)) +# When LTO is enabled, llvm emits many text sections, which is not supported +# by kexec. Remove -flto=* flags. +KBUILD_CFLAGS := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS)) + # When linking purgatory.ro with -r unresolved symbols are not checked, # also link a purgatory.chk binary without -r to check for unresolved symbols. PURGATORY_LDFLAGS := -e purgatory_start -z nodefaultlib -- cgit v1.2.3 From 42aadec8c739727fce8e2c1ee71e72cb0f82ed3f Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 3 Sep 2023 11:09:56 -0700 Subject: stat: remove no-longer-used helper macros The choose_32_64() macros were added to deal with an odd inconsistency between the 32-bit and 64-bit layout of 'struct stat' way back when in commit a52dd971f947 ("vfs: de-crapify "cp_new_stat()" function"). Then a decade later Mikulas noticed that said inconsistency had been a mistake in the early x86-64 port, and shouldn't have existed in the first place. So commit 932aba1e1690 ("stat: fix inconsistency between struct stat and struct compat_stat") removed the uses of the helpers. But the helpers remained around, unused. Get rid of them. Signed-off-by: Linus Torvalds --- fs/stat.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/fs/stat.c b/fs/stat.c index 6822ac77aec2..6e60389d6a15 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -419,12 +419,6 @@ SYSCALL_DEFINE2(fstat, unsigned int, fd, struct __old_kernel_stat __user *, stat #ifdef __ARCH_WANT_NEW_STAT -#if BITS_PER_LONG == 32 -# define choose_32_64(a,b) a -#else -# define choose_32_64(a,b) b -#endif - #ifndef INIT_STRUCT_STAT_PADDING # define INIT_STRUCT_STAT_PADDING(st) memset(&st, 0, sizeof(st)) #endif -- cgit v1.2.3 From ce9ecca0238b140b88f43859b211c9fdfd8e5b70 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sun, 17 Sep 2023 14:40:24 -0700 Subject: Linux 6.6-rc2 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ceb23eed4dce..57698d048e2c 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ VERSION = 6 PATCHLEVEL = 6 SUBLEVEL = 0 -EXTRAVERSION = -rc1 +EXTRAVERSION = -rc2 NAME = Hurr durr I'ma ninja sloth # *DOCUMENTATION* -- cgit v1.2.3 From ef4d48368587f27cb1f690691518889d8fb3510b Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 18 Sep 2023 11:48:29 +0530 Subject: RISC-V: KVM: Fix KVM_GET_REG_LIST API for ISA_EXT registers The ISA_EXT registers to enabled/disable ISA extensions for VCPU are always available when underlying host has the corresponding ISA extension. The copy_isa_ext_reg_indices() called by the KVM_GET_REG_LIST API does not align with this expectation so let's fix it. Fixes: 031f9efafc08 ("KVM: riscv: Add KVM_GET_REG_LIST API support") Signed-off-by: Anup Patel Reviewed-by: Atish Patra Reviewed-by: Andrew Jones Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_onereg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index 1b7e9fa265cb..e7e833ced91b 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -842,7 +842,7 @@ static int copy_isa_ext_reg_indices(const struct kvm_vcpu *vcpu, u64 reg = KVM_REG_RISCV | size | KVM_REG_RISCV_ISA_EXT | i; isa_ext = kvm_isa_ext_arr[i]; - if (!__riscv_isa_extension_available(vcpu->arch.isa, isa_ext)) + if (!__riscv_isa_extension_available(NULL, isa_ext)) continue; if (uindices) { -- cgit v1.2.3 From 17f71a2a340f1dcd397a66110005722177d5927c Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 18 Sep 2023 11:58:29 +0530 Subject: RISC-V: KVM: Fix riscv_vcpu_get_isa_ext_single() for missing extensions The riscv_vcpu_get_isa_ext_single() should fail with -ENOENT error when corresponding ISA extension is not available on the host. Fixes: e98b1085be79 ("RISC-V: KVM: Factor-out ONE_REG related code to its own source file") Signed-off-by: Anup Patel Reviewed-by: Atish Patra Reviewed-by: Andrew Jones Signed-off-by: Anup Patel --- arch/riscv/kvm/vcpu_onereg.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/arch/riscv/kvm/vcpu_onereg.c b/arch/riscv/kvm/vcpu_onereg.c index e7e833ced91b..b7e0e03c69b1 100644 --- a/arch/riscv/kvm/vcpu_onereg.c +++ b/arch/riscv/kvm/vcpu_onereg.c @@ -460,8 +460,11 @@ static int riscv_vcpu_get_isa_ext_single(struct kvm_vcpu *vcpu, reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) return -ENOENT; - *reg_val = 0; host_isa_ext = kvm_isa_ext_arr[reg_num]; + if (!__riscv_isa_extension_available(NULL, host_isa_ext)) + return -ENOENT; + + *reg_val = 0; if (__riscv_isa_extension_available(vcpu->arch.isa, host_isa_ext)) *reg_val = 1; /* Mark the given extension as available */ -- cgit v1.2.3 From ba1af6e2e0f0e814c0f6be6ef64917c212f9fa96 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 18 Sep 2023 14:29:19 +0530 Subject: KVM: riscv: selftests: Fix ISA_EXT register handling in get-reg-list Same set of ISA_EXT registers are not present on all host because ISA_EXT registers are visible to the KVM user space based on the ISA extensions available on the host. Also, disabling an ISA extension using corresponding ISA_EXT register does not affect the visibility of the ISA_EXT register itself. Based on the above, we should filter-out all ISA_EXT registers. Fixes: 477069398ed6 ("KVM: riscv: selftests: Add get-reg-list test") Signed-off-by: Anup Patel Reviewed-by: Andrew Jones Signed-off-by: Anup Patel --- tools/testing/selftests/kvm/riscv/get-reg-list.c | 35 ++++++++++++++---------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/tools/testing/selftests/kvm/riscv/get-reg-list.c b/tools/testing/selftests/kvm/riscv/get-reg-list.c index d8ecacd03ecf..76c0ad11e423 100644 --- a/tools/testing/selftests/kvm/riscv/get-reg-list.c +++ b/tools/testing/selftests/kvm/riscv/get-reg-list.c @@ -14,17 +14,33 @@ bool filter_reg(__u64 reg) { + switch (reg & ~REG_MASK) { /* - * Some ISA extensions are optional and not present on all host, - * but they can't be disabled through ISA_EXT registers when present. - * So, to make life easy, just filtering out these kind of registers. + * Same set of ISA_EXT registers are not present on all host because + * ISA_EXT registers are visible to the KVM user space based on the + * ISA extensions available on the host. Also, disabling an ISA + * extension using corresponding ISA_EXT register does not affect + * the visibility of the ISA_EXT register itself. + * + * Based on above, we should filter-out all ISA_EXT registers. */ - switch (reg & ~REG_MASK) { + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_A: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_C: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_D: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_F: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_H: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_I: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_M: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVPBMT: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSTC: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVINVAL: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHINTPAUSE: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOM: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICBOZ: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBB: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SSAIA: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_V: + case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_SVNAPOT: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBA: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZBS: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZICNTR: @@ -50,12 +66,7 @@ static inline bool vcpu_has_ext(struct kvm_vcpu *vcpu, int ext) unsigned long value; ret = __vcpu_get_reg(vcpu, RISCV_ISA_EXT_REG(ext), &value); - if (ret) { - printf("Failed to get ext %d", ext); - return false; - } - - return !!value; + return (ret) ? false : !!value; } void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) @@ -506,10 +517,6 @@ static __u64 base_regs[] = { KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(time), KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(compare), KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_TIMER | KVM_REG_RISCV_TIMER_REG(state), - KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_A, - KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_C, - KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_I, - KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_M, KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_V01, KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_TIME, KVM_REG_RISCV | KVM_REG_SIZE_ULONG | KVM_REG_RISCV_SBI_EXT | KVM_REG_RISCV_SBI_SINGLE | KVM_RISCV_SBI_EXT_IPI, -- cgit v1.2.3 From 071ef070ca77e6dfe33fd78afa293e83422f0411 Mon Sep 17 00:00:00 2001 From: Anup Patel Date: Mon, 18 Sep 2023 10:55:55 +0530 Subject: KVM: riscv: selftests: Selectively filter-out AIA registers Currently the AIA ONE_REG registers are reported by get-reg-list as new registers for various vcpu_reg_list configs whenever Ssaia is available on the host because Ssaia extension can only be disabled by Smstateen extension which is not always available. To tackle this, we should filter-out AIA ONE_REG registers only when Ssaia can't be disabled for a VCPU. Fixes: 477069398ed6 ("KVM: riscv: selftests: Add get-reg-list test") Signed-off-by: Anup Patel Reviewed-by: Atish Patra Reviewed-by: Andrew Jones Signed-off-by: Anup Patel --- tools/testing/selftests/kvm/riscv/get-reg-list.c | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/kvm/riscv/get-reg-list.c b/tools/testing/selftests/kvm/riscv/get-reg-list.c index 76c0ad11e423..9f99ea42f45f 100644 --- a/tools/testing/selftests/kvm/riscv/get-reg-list.c +++ b/tools/testing/selftests/kvm/riscv/get-reg-list.c @@ -12,6 +12,8 @@ #define REG_MASK (KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK) +static bool isa_ext_cant_disable[KVM_RISCV_ISA_EXT_MAX]; + bool filter_reg(__u64 reg) { switch (reg & ~REG_MASK) { @@ -48,6 +50,15 @@ bool filter_reg(__u64 reg) case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIFENCEI: case KVM_REG_RISCV_ISA_EXT | KVM_RISCV_ISA_EXT_ZIHPM: return true; + /* AIA registers are always available when Ssaia can't be disabled */ + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(siselect): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(sieh): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(siph): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio1h): + case KVM_REG_RISCV_CSR | KVM_REG_RISCV_CSR_AIA | KVM_REG_RISCV_CSR_AIA_REG(iprio2h): + return isa_ext_cant_disable[KVM_RISCV_ISA_EXT_SSAIA]; default: break; } @@ -71,14 +82,22 @@ static inline bool vcpu_has_ext(struct kvm_vcpu *vcpu, int ext) void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_reg_list *c) { + unsigned long isa_ext_state[KVM_RISCV_ISA_EXT_MAX] = { 0 }; struct vcpu_reg_sublist *s; + int rc; + + for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) + __vcpu_get_reg(vcpu, RISCV_ISA_EXT_REG(i), &isa_ext_state[i]); /* * Disable all extensions which were enabled by default * if they were available in the risc-v host. */ - for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) - __vcpu_set_reg(vcpu, RISCV_ISA_EXT_REG(i), 0); + for (int i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + rc = __vcpu_set_reg(vcpu, RISCV_ISA_EXT_REG(i), 0); + if (rc && isa_ext_state[i]) + isa_ext_cant_disable[i] = true; + } for_each_sublist(c, s) { if (!s->feature) -- cgit v1.2.3