From b15c90153fd906af6e70821a301e78d379bd482d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Fri, 8 Jan 2021 12:22:33 +0100 Subject: gnss: drop stray semicolons Drop semicolons after function definitions that have managed to sneak in and get reproduced. Signed-off-by: Johan Hovold --- drivers/gnss/mtk.c | 2 +- drivers/gnss/serial.c | 2 +- drivers/gnss/sirf.c | 2 +- drivers/gnss/ubx.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gnss/mtk.c b/drivers/gnss/mtk.c index d1fc55560daf..c62b1211f4fe 100644 --- a/drivers/gnss/mtk.c +++ b/drivers/gnss/mtk.c @@ -126,7 +126,7 @@ static void mtk_remove(struct serdev_device *serdev) if (data->vbackup) regulator_disable(data->vbackup); gnss_serial_free(gserial); -}; +} #ifdef CONFIG_OF static const struct of_device_id mtk_of_match[] = { diff --git a/drivers/gnss/serial.c b/drivers/gnss/serial.c index def64b36d994..5d8e9bfb24d0 100644 --- a/drivers/gnss/serial.c +++ b/drivers/gnss/serial.c @@ -165,7 +165,7 @@ void gnss_serial_free(struct gnss_serial *gserial) { gnss_put_device(gserial->gdev); kfree(gserial); -}; +} EXPORT_SYMBOL_GPL(gnss_serial_free); int gnss_serial_register(struct gnss_serial *gserial) diff --git a/drivers/gnss/sirf.c b/drivers/gnss/sirf.c index 2ecb1d3e8eeb..bcb53ccfee4d 100644 --- a/drivers/gnss/sirf.c +++ b/drivers/gnss/sirf.c @@ -551,7 +551,7 @@ static void sirf_remove(struct serdev_device *serdev) regulator_disable(data->vcc); gnss_put_device(data->gdev); -}; +} #ifdef CONFIG_OF static const struct of_device_id sirf_of_match[] = { diff --git a/drivers/gnss/ubx.c b/drivers/gnss/ubx.c index 7b05bc40532e..c951be202ca2 100644 --- a/drivers/gnss/ubx.c +++ b/drivers/gnss/ubx.c @@ -126,7 +126,7 @@ static void ubx_remove(struct serdev_device *serdev) if (data->v_bckp) regulator_disable(data->v_bckp); gnss_serial_free(gserial); -}; +} #ifdef CONFIG_OF static const struct of_device_id ubx_of_match[] = { -- cgit v1.2.3 From 934a5dc1546b1c637999bfcebcdd1598eaab4818 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Wed, 3 Nov 2021 19:39:18 +0100 Subject: coresight: Use devm_bitmap_zalloc when applicable 'drvdata->chs.guaranteed' is a bitmap. So use 'devm_bitmap_kzalloc()' to simplify code, improve the semantic and avoid some open-coded arithmetic in allocator arguments. Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/a4b8454f560b70cedf0e4d06275787f08d576ee5.1635964610.git.christophe.jaillet@wanadoo.fr Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-stm.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-stm.c b/drivers/hwtracing/coresight/coresight-stm.c index 58062a5a8238..bb14a3a8a921 100644 --- a/drivers/hwtracing/coresight/coresight-stm.c +++ b/drivers/hwtracing/coresight/coresight-stm.c @@ -856,13 +856,11 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) { int ret; void __iomem *base; - unsigned long *guaranteed; struct device *dev = &adev->dev; struct coresight_platform_data *pdata = NULL; struct stm_drvdata *drvdata; struct resource *res = &adev->res; struct resource ch_res; - size_t bitmap_size; struct coresight_desc desc = { 0 }; desc.name = coresight_alloc_device_name(&stm_devs, dev); @@ -904,12 +902,10 @@ static int stm_probe(struct amba_device *adev, const struct amba_id *id) else drvdata->numsp = stm_num_stimulus_port(drvdata); - bitmap_size = BITS_TO_LONGS(drvdata->numsp) * sizeof(long); - - guaranteed = devm_kzalloc(dev, bitmap_size, GFP_KERNEL); - if (!guaranteed) + drvdata->chs.guaranteed = devm_bitmap_zalloc(dev, drvdata->numsp, + GFP_KERNEL); + if (!drvdata->chs.guaranteed) return -ENOMEM; - drvdata->chs.guaranteed = guaranteed; spin_lock_init(&drvdata->spinlock); -- cgit v1.2.3 From dc74e8cf2324ad61b050a55ec0ffa9db6f4fce33 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 12 Nov 2021 18:09:54 +0100 Subject: nitro_enclaves: Remove redundant 'flush_workqueue()' calls 'destroy_workqueue()' already drains the queue before destroying it, so there is no need to flush it explicitly. Remove the redundant 'flush_workqueue()' calls. This was generated with coccinelle: @@ expression E; @@ - flush_workqueue(E); destroy_workqueue(E); Reviewed-by: Andra Paraschiv Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/d57f5c7e362837a8dfcde0d726a76b56f114e619.1636736947.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_pci_dev.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/virt/nitro_enclaves/ne_pci_dev.c b/drivers/virt/nitro_enclaves/ne_pci_dev.c index 40b49ec8e30b..6b81e8f3a5dc 100644 --- a/drivers/virt/nitro_enclaves/ne_pci_dev.c +++ b/drivers/virt/nitro_enclaves/ne_pci_dev.c @@ -376,7 +376,6 @@ static void ne_teardown_msix(struct pci_dev *pdev) free_irq(pci_irq_vector(pdev, NE_VEC_EVENT), ne_pci_dev); flush_work(&ne_pci_dev->notify_work); - flush_workqueue(ne_pci_dev->event_wq); destroy_workqueue(ne_pci_dev->event_wq); free_irq(pci_irq_vector(pdev, NE_VEC_REPLY), ne_pci_dev); -- cgit v1.2.3 From f6bdc0aafe88cf4c727e7bb00da1f480ecd80bee Mon Sep 17 00:00:00 2001 From: Longpeng Date: Sun, 7 Nov 2021 22:09:14 +0800 Subject: nitro_enclaves: Merge contiguous physical memory regions There can be cases when there are more memory regions that need to be set for an enclave than the maximum supported number of memory regions per enclave. One example can be when the memory regions are backed by 2 MiB hugepages (the minimum supported hugepage size). Let's merge the adjacent regions if they are physically contiguous. This way the final number of memory regions is less than before merging and could potentially avoid reaching maximum. Reviewed-by: Andra Paraschiv Signed-off-by: Longpeng Link: https://lore.kernel.org/r/20211107140918.2106-2-longpeng2@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_misc_dev.c | 84 ++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 28 deletions(-) diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c index 8939612ee0e0..ced58de9a0b1 100644 --- a/drivers/virt/nitro_enclaves/ne_misc_dev.c +++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,16 @@ struct ne_cpu_pool { static struct ne_cpu_pool ne_cpu_pool; +/** + * struct ne_phys_contig_mem_regions - Contiguous physical memory regions. + * @num: The number of regions that currently has. + * @regions: The array of physical memory regions. + */ +struct ne_phys_contig_mem_regions { + unsigned long num; + struct range *regions; +}; + /** * ne_check_enclaves_created() - Verify if at least one enclave has been created. * @void: No parameters provided. @@ -824,6 +835,33 @@ static int ne_sanity_check_user_mem_region_page(struct ne_enclave *ne_enclave, return 0; } +/** + * ne_merge_phys_contig_memory_regions() - Add a memory region and merge the adjacent + * regions if they are physically contiguous. + * @phys_contig_regions : Private data associated with the contiguous physical memory regions. + * @page_paddr : Physical start address of the region to be added. + * @page_size : Length of the region to be added. + * + * Context: Process context. This function is called with the ne_enclave mutex held. + */ +static void +ne_merge_phys_contig_memory_regions(struct ne_phys_contig_mem_regions *phys_contig_regions, + u64 page_paddr, u64 page_size) +{ + unsigned long num = phys_contig_regions->num; + + /* Physically contiguous, just merge */ + if (num && (phys_contig_regions->regions[num - 1].end + 1) == page_paddr) { + phys_contig_regions->regions[num - 1].end += page_size; + + return; + } + + phys_contig_regions->regions[num].start = page_paddr; + phys_contig_regions->regions[num].end = page_paddr + page_size - 1; + phys_contig_regions->num++; +} + /** * ne_set_user_memory_region_ioctl() - Add user space memory region to the slot * associated with the current enclave. @@ -843,9 +881,8 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, unsigned long max_nr_pages = 0; unsigned long memory_size = 0; struct ne_mem_region *ne_mem_region = NULL; - unsigned long nr_phys_contig_mem_regions = 0; struct pci_dev *pdev = ne_devs.ne_pci_dev->pdev; - struct page **phys_contig_mem_regions = NULL; + struct ne_phys_contig_mem_regions phys_contig_mem_regions = {}; int rc = -EINVAL; rc = ne_sanity_check_user_mem_region(ne_enclave, mem_region); @@ -866,9 +903,10 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, goto free_mem_region; } - phys_contig_mem_regions = kcalloc(max_nr_pages, sizeof(*phys_contig_mem_regions), - GFP_KERNEL); - if (!phys_contig_mem_regions) { + phys_contig_mem_regions.regions = kcalloc(max_nr_pages, + sizeof(*phys_contig_mem_regions.regions), + GFP_KERNEL); + if (!phys_contig_mem_regions.regions) { rc = -ENOMEM; goto free_mem_region; @@ -901,26 +939,16 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, if (rc < 0) goto put_pages; - /* - * TODO: Update once handled non-contiguous memory regions - * received from user space or contiguous physical memory regions - * larger than 2 MiB e.g. 8 MiB. - */ - phys_contig_mem_regions[i] = ne_mem_region->pages[i]; + ne_merge_phys_contig_memory_regions(&phys_contig_mem_regions, + page_to_phys(ne_mem_region->pages[i]), + page_size(ne_mem_region->pages[i])); memory_size += page_size(ne_mem_region->pages[i]); ne_mem_region->nr_pages++; } while (memory_size < mem_region.memory_size); - /* - * TODO: Update once handled non-contiguous memory regions received - * from user space or contiguous physical memory regions larger than - * 2 MiB e.g. 8 MiB. - */ - nr_phys_contig_mem_regions = ne_mem_region->nr_pages; - - if ((ne_enclave->nr_mem_regions + nr_phys_contig_mem_regions) > + if ((ne_enclave->nr_mem_regions + phys_contig_mem_regions.num) > ne_enclave->max_mem_regions) { dev_err_ratelimited(ne_misc_dev.this_device, "Reached max memory regions %lld\n", @@ -931,9 +959,9 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, goto put_pages; } - for (i = 0; i < nr_phys_contig_mem_regions; i++) { - u64 phys_region_addr = page_to_phys(phys_contig_mem_regions[i]); - u64 phys_region_size = page_size(phys_contig_mem_regions[i]); + for (i = 0; i < phys_contig_mem_regions.num; i++) { + u64 phys_region_addr = phys_contig_mem_regions.regions[i].start; + u64 phys_region_size = range_len(&phys_contig_mem_regions.regions[i]); if (phys_region_size & (NE_MIN_MEM_REGION_SIZE - 1)) { dev_err_ratelimited(ne_misc_dev.this_device, @@ -959,13 +987,13 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, list_add(&ne_mem_region->mem_region_list_entry, &ne_enclave->mem_regions_list); - for (i = 0; i < nr_phys_contig_mem_regions; i++) { + for (i = 0; i < phys_contig_mem_regions.num; i++) { struct ne_pci_dev_cmd_reply cmd_reply = {}; struct slot_add_mem_req slot_add_mem_req = {}; slot_add_mem_req.slot_uid = ne_enclave->slot_uid; - slot_add_mem_req.paddr = page_to_phys(phys_contig_mem_regions[i]); - slot_add_mem_req.size = page_size(phys_contig_mem_regions[i]); + slot_add_mem_req.paddr = phys_contig_mem_regions.regions[i].start; + slot_add_mem_req.size = range_len(&phys_contig_mem_regions.regions[i]); rc = ne_do_request(pdev, SLOT_ADD_MEM, &slot_add_mem_req, sizeof(slot_add_mem_req), @@ -974,7 +1002,7 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, dev_err_ratelimited(ne_misc_dev.this_device, "Error in slot add mem [rc=%d]\n", rc); - kfree(phys_contig_mem_regions); + kfree(phys_contig_mem_regions.regions); /* * Exit here without put pages as memory regions may @@ -987,7 +1015,7 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, ne_enclave->nr_mem_regions++; } - kfree(phys_contig_mem_regions); + kfree(phys_contig_mem_regions.regions); return 0; @@ -995,7 +1023,7 @@ put_pages: for (i = 0; i < ne_mem_region->nr_pages; i++) put_page(ne_mem_region->pages[i]); free_mem_region: - kfree(phys_contig_mem_regions); + kfree(phys_contig_mem_regions.regions); kfree(ne_mem_region->pages); kfree(ne_mem_region); -- cgit v1.2.3 From 090ce7831d340e8be92e5f2a90617cca6e92156e Mon Sep 17 00:00:00 2001 From: Longpeng Date: Sun, 7 Nov 2021 22:09:15 +0800 Subject: nitro_enclaves: Sanity check physical memory regions during merging Sanity check the physical memory regions during the merge of contiguous regions. Thus we can test the physical memory regions setup logic individually, including the error cases coming from the sanity checks. Reviewed-by: Andra Paraschiv Signed-off-by: Longpeng Link: https://lore.kernel.org/r/20211107140918.2106-3-longpeng2@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_misc_dev.c | 77 +++++++++++++++++++++---------- 1 file changed, 52 insertions(+), 25 deletions(-) diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c index ced58de9a0b1..83ed9b5fad56 100644 --- a/drivers/virt/nitro_enclaves/ne_misc_dev.c +++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c @@ -835,6 +835,37 @@ static int ne_sanity_check_user_mem_region_page(struct ne_enclave *ne_enclave, return 0; } +/** + * ne_sanity_check_phys_mem_region() - Sanity check the start address and the size + * of a physical memory region. + * @phys_mem_region_paddr : Physical start address of the region to be sanity checked. + * @phys_mem_region_size : Length of the region to be sanity checked. + * + * Context: Process context. This function is called with the ne_enclave mutex held. + * Return: + * * 0 on success. + * * Negative return value on failure. + */ +static int ne_sanity_check_phys_mem_region(u64 phys_mem_region_paddr, + u64 phys_mem_region_size) +{ + if (phys_mem_region_size & (NE_MIN_MEM_REGION_SIZE - 1)) { + dev_err_ratelimited(ne_misc_dev.this_device, + "Physical mem region size is not multiple of 2 MiB\n"); + + return -EINVAL; + } + + if (!IS_ALIGNED(phys_mem_region_paddr, NE_MIN_MEM_REGION_SIZE)) { + dev_err_ratelimited(ne_misc_dev.this_device, + "Physical mem region address is not 2 MiB aligned\n"); + + return -EINVAL; + } + + return 0; +} + /** * ne_merge_phys_contig_memory_regions() - Add a memory region and merge the adjacent * regions if they are physically contiguous. @@ -843,23 +874,31 @@ static int ne_sanity_check_user_mem_region_page(struct ne_enclave *ne_enclave, * @page_size : Length of the region to be added. * * Context: Process context. This function is called with the ne_enclave mutex held. + * Return: + * * 0 on success. + * * Negative return value on failure. */ -static void +static int ne_merge_phys_contig_memory_regions(struct ne_phys_contig_mem_regions *phys_contig_regions, u64 page_paddr, u64 page_size) { unsigned long num = phys_contig_regions->num; + int rc = 0; + + rc = ne_sanity_check_phys_mem_region(page_paddr, page_size); + if (rc < 0) + return rc; /* Physically contiguous, just merge */ if (num && (phys_contig_regions->regions[num - 1].end + 1) == page_paddr) { phys_contig_regions->regions[num - 1].end += page_size; - - return; + } else { + phys_contig_regions->regions[num].start = page_paddr; + phys_contig_regions->regions[num].end = page_paddr + page_size - 1; + phys_contig_regions->num++; } - phys_contig_regions->regions[num].start = page_paddr; - phys_contig_regions->regions[num].end = page_paddr + page_size - 1; - phys_contig_regions->num++; + return 0; } /** @@ -939,9 +978,11 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, if (rc < 0) goto put_pages; - ne_merge_phys_contig_memory_regions(&phys_contig_mem_regions, - page_to_phys(ne_mem_region->pages[i]), - page_size(ne_mem_region->pages[i])); + rc = ne_merge_phys_contig_memory_regions(&phys_contig_mem_regions, + page_to_phys(ne_mem_region->pages[i]), + page_size(ne_mem_region->pages[i])); + if (rc < 0) + goto put_pages; memory_size += page_size(ne_mem_region->pages[i]); @@ -963,23 +1004,9 @@ static int ne_set_user_memory_region_ioctl(struct ne_enclave *ne_enclave, u64 phys_region_addr = phys_contig_mem_regions.regions[i].start; u64 phys_region_size = range_len(&phys_contig_mem_regions.regions[i]); - if (phys_region_size & (NE_MIN_MEM_REGION_SIZE - 1)) { - dev_err_ratelimited(ne_misc_dev.this_device, - "Physical mem region size is not multiple of 2 MiB\n"); - - rc = -EINVAL; - - goto put_pages; - } - - if (!IS_ALIGNED(phys_region_addr, NE_MIN_MEM_REGION_SIZE)) { - dev_err_ratelimited(ne_misc_dev.this_device, - "Physical mem region address is not 2 MiB aligned\n"); - - rc = -EINVAL; - + rc = ne_sanity_check_phys_mem_region(phys_region_addr, phys_region_size); + if (rc < 0) goto put_pages; - } } ne_mem_region->memory_size = mem_region.memory_size; -- cgit v1.2.3 From 07503b3c1e13fdeb66d4531c5dcba335eed9602a Mon Sep 17 00:00:00 2001 From: Longpeng Date: Sun, 7 Nov 2021 22:09:16 +0800 Subject: nitro_enclaves: Add KUnit tests setup for the misc device functionality Add the initial setup for the KUnit tests that will target the Nitro Enclaves misc device functionality. Reviewed-by: Andra Paraschiv Signed-off-by: Longpeng Link: https://lore.kernel.org/r/20211107140918.2106-4-longpeng2@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/Kconfig | 9 ++++++++ drivers/virt/nitro_enclaves/ne_misc_dev.c | 31 ++++++++++++++++++++++++++ drivers/virt/nitro_enclaves/ne_misc_dev_test.c | 17 ++++++++++++++ 3 files changed, 57 insertions(+) create mode 100644 drivers/virt/nitro_enclaves/ne_misc_dev_test.c diff --git a/drivers/virt/nitro_enclaves/Kconfig b/drivers/virt/nitro_enclaves/Kconfig index f53740b941c0..2d3d98158121 100644 --- a/drivers/virt/nitro_enclaves/Kconfig +++ b/drivers/virt/nitro_enclaves/Kconfig @@ -14,3 +14,12 @@ config NITRO_ENCLAVES To compile this driver as a module, choose M here. The module will be called nitro_enclaves. + +config NITRO_ENCLAVES_MISC_DEV_TEST + bool "Tests for the misc device functionality of the Nitro Enclaves" + depends on NITRO_ENCLAVES && KUNIT=y + help + Enable KUnit tests for the misc device functionality of the Nitro + Enclaves. Select this option only if you will boot the kernel for + the purpose of running unit tests (e.g. under UML or qemu). If + unsure, say N. diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev.c b/drivers/virt/nitro_enclaves/ne_misc_dev.c index 83ed9b5fad56..51ba4caeef23 100644 --- a/drivers/virt/nitro_enclaves/ne_misc_dev.c +++ b/drivers/virt/nitro_enclaves/ne_misc_dev.c @@ -1756,8 +1756,37 @@ static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg) return 0; } +#if defined(CONFIG_NITRO_ENCLAVES_MISC_DEV_TEST) +#include "ne_misc_dev_test.c" + +static inline int ne_misc_dev_test_init(void) +{ + return __kunit_test_suites_init(ne_misc_dev_test_suites); +} + +static inline void ne_misc_dev_test_exit(void) +{ + __kunit_test_suites_exit(ne_misc_dev_test_suites); +} +#else +static inline int ne_misc_dev_test_init(void) +{ + return 0; +} + +static inline void ne_misc_dev_test_exit(void) +{ +} +#endif + static int __init ne_init(void) { + int rc = 0; + + rc = ne_misc_dev_test_init(); + if (rc < 0) + return rc; + mutex_init(&ne_cpu_pool.mutex); return pci_register_driver(&ne_pci_driver); @@ -1768,6 +1797,8 @@ static void __exit ne_exit(void) pci_unregister_driver(&ne_pci_driver); ne_teardown_cpu_pool(); + + ne_misc_dev_test_exit(); } module_init(ne_init); diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev_test.c b/drivers/virt/nitro_enclaves/ne_misc_dev_test.c new file mode 100644 index 000000000000..6862e990d2be --- /dev/null +++ b/drivers/virt/nitro_enclaves/ne_misc_dev_test.c @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include + +static struct kunit_case ne_misc_dev_test_cases[] = { + {} +}; + +static struct kunit_suite ne_misc_dev_test_suite = { + .name = "ne_misc_dev_test", + .test_cases = ne_misc_dev_test_cases, +}; + +static struct kunit_suite *ne_misc_dev_test_suites[] = { + &ne_misc_dev_test_suite, + NULL +}; -- cgit v1.2.3 From fbf3443f77503f68f244cd7afa050c19ac78511c Mon Sep 17 00:00:00 2001 From: Longpeng Date: Sun, 7 Nov 2021 22:09:17 +0800 Subject: nitro_enclaves: Add KUnit tests for contiguous physical memory regions merging Add KUnit tests for the contiguous physical memory regions merging functionality from the Nitro Enclaves misc device logic. We can build the test binary with the following configuration: CONFIG_KUNIT=y CONFIG_NITRO_ENCLAVES=m CONFIG_NITRO_ENCLAVES_MISC_DEV_TEST=y and install the nitro_enclaves module to run the testcases. We'll see the following message using dmesg if everything goes well: [...] # Subtest: ne_misc_dev_test [...] 1..1 [...] (NULL device *): Physical mem region address is not 2 MiB aligned [...] (NULL device *): Physical mem region size is not multiple of 2 MiB [...] (NULL device *): Physical mem region address is not 2 MiB aligned [...] ok 1 - ne_misc_dev_test_merge_phys_contig_memory_regions [...] ok 1 - ne_misc_dev_test Reviewed-by: Andra Paraschiv Signed-off-by: Longpeng Link: https://lore.kernel.org/r/20211107140918.2106-5-longpeng2@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/virt/nitro_enclaves/ne_misc_dev_test.c | 140 +++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/drivers/virt/nitro_enclaves/ne_misc_dev_test.c b/drivers/virt/nitro_enclaves/ne_misc_dev_test.c index 6862e990d2be..265797bed0ea 100644 --- a/drivers/virt/nitro_enclaves/ne_misc_dev_test.c +++ b/drivers/virt/nitro_enclaves/ne_misc_dev_test.c @@ -2,7 +2,147 @@ #include +#define MAX_PHYS_REGIONS 16 +#define INVALID_VALUE (~0ull) + +struct ne_phys_regions_test { + u64 paddr; + u64 size; + int expect_rc; + unsigned long expect_num; + u64 expect_last_paddr; + u64 expect_last_size; +} phys_regions_test_cases[] = { + /* + * Add the region from 0x1000 to (0x1000 + 0x200000 - 1): + * Expected result: + * Failed, start address is not 2M-aligned + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 0 + * regions = {} + */ + {0x1000, 0x200000, -EINVAL, 0, INVALID_VALUE, INVALID_VALUE}, + + /* + * Add the region from 0x200000 to (0x200000 + 0x1000 - 1): + * Expected result: + * Failed, size is not 2M-aligned + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 0 + * regions = {} + */ + {0x200000, 0x1000, -EINVAL, 0, INVALID_VALUE, INVALID_VALUE}, + + /* + * Add the region from 0x200000 to (0x200000 + 0x200000 - 1): + * Expected result: + * Successful + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 1 + * regions = { + * {start=0x200000, end=0x3fffff}, // len=0x200000 + * } + */ + {0x200000, 0x200000, 0, 1, 0x200000, 0x200000}, + + /* + * Add the region from 0x0 to (0x0 + 0x200000 - 1): + * Expected result: + * Successful + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 2 + * regions = { + * {start=0x200000, end=0x3fffff}, // len=0x200000 + * {start=0x0, end=0x1fffff}, // len=0x200000 + * } + */ + {0x0, 0x200000, 0, 2, 0x0, 0x200000}, + + /* + * Add the region from 0x600000 to (0x600000 + 0x400000 - 1): + * Expected result: + * Successful + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 3 + * regions = { + * {start=0x200000, end=0x3fffff}, // len=0x200000 + * {start=0x0, end=0x1fffff}, // len=0x200000 + * {start=0x600000, end=0x9fffff}, // len=0x400000 + * } + */ + {0x600000, 0x400000, 0, 3, 0x600000, 0x400000}, + + /* + * Add the region from 0xa00000 to (0xa00000 + 0x400000 - 1): + * Expected result: + * Successful, merging case! + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 3 + * regions = { + * {start=0x200000, end=0x3fffff}, // len=0x200000 + * {start=0x0, end=0x1fffff}, // len=0x200000 + * {start=0x600000, end=0xdfffff}, // len=0x800000 + * } + */ + {0xa00000, 0x400000, 0, 3, 0x600000, 0x800000}, + + /* + * Add the region from 0x1000 to (0x1000 + 0x200000 - 1): + * Expected result: + * Failed, start address is not 2M-aligned + * + * Now the instance of struct ne_phys_contig_mem_regions is: + * num = 3 + * regions = { + * {start=0x200000, end=0x3fffff}, // len=0x200000 + * {start=0x0, end=0x1fffff}, // len=0x200000 + * {start=0x600000, end=0xdfffff}, // len=0x800000 + * } + */ + {0x1000, 0x200000, -EINVAL, 3, 0x600000, 0x800000}, +}; + +static void ne_misc_dev_test_merge_phys_contig_memory_regions(struct kunit *test) +{ + struct ne_phys_contig_mem_regions phys_contig_mem_regions = {}; + int rc = 0; + int i = 0; + + phys_contig_mem_regions.regions = kunit_kcalloc(test, MAX_PHYS_REGIONS, + sizeof(*phys_contig_mem_regions.regions), + GFP_KERNEL); + KUNIT_ASSERT_TRUE(test, phys_contig_mem_regions.regions); + + for (i = 0; i < ARRAY_SIZE(phys_regions_test_cases); i++) { + struct ne_phys_regions_test *test_case = &phys_regions_test_cases[i]; + unsigned long num = 0; + + rc = ne_merge_phys_contig_memory_regions(&phys_contig_mem_regions, + test_case->paddr, test_case->size); + KUNIT_EXPECT_EQ(test, rc, test_case->expect_rc); + KUNIT_EXPECT_EQ(test, phys_contig_mem_regions.num, test_case->expect_num); + + if (test_case->expect_last_paddr == INVALID_VALUE) + continue; + + num = phys_contig_mem_regions.num; + KUNIT_EXPECT_EQ(test, phys_contig_mem_regions.regions[num - 1].start, + test_case->expect_last_paddr); + KUNIT_EXPECT_EQ(test, range_len(&phys_contig_mem_regions.regions[num - 1]), + test_case->expect_last_size); + } + + kunit_kfree(test, phys_contig_mem_regions.regions); +} + static struct kunit_case ne_misc_dev_test_cases[] = { + KUNIT_CASE(ne_misc_dev_test_merge_phys_contig_memory_regions), {} }; -- cgit v1.2.3 From 1881eadb2041889d74d60c074eb04189c4a07dad Mon Sep 17 00:00:00 2001 From: Abhyuday Godhasara Date: Mon, 25 Oct 2021 21:25:20 -0700 Subject: firmware: xilinx: add register notifier in zynqmp firmware In zynqmp-firmware, register notifier is not supported, add support of register notifier in zynqmp-firmware. Acked-by: Michal Simek Signed-off-by: Tejas Patel Signed-off-by: Abhyuday Godhasara Link: https://lore.kernel.org/r/20211026042525.26612-2-abhyuday.godhasara@xilinx.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 23 +++++++++++++++++++++++ include/linux/firmware/xlnx-zynqmp.h | 11 ++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 3dd45a7420dc..30edcd233786 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -1116,6 +1116,29 @@ int zynqmp_pm_aes_engine(const u64 address, u32 *out) } EXPORT_SYMBOL_GPL(zynqmp_pm_aes_engine); +/** + * zynqmp_pm_register_notifier() - PM API for register a subsystem + * to be notified about specific + * event/error. + * @node: Node ID to which the event is related. + * @event: Event Mask of Error events for which wants to get notified. + * @wake: Wake subsystem upon capturing the event if value 1 + * @enable: Enable the registration for value 1, disable for value 0 + * + * This function is used to register/un-register for particular node-event + * combination in firmware. + * + * Return: Returns status, either success or error+reason + */ + +int zynqmp_pm_register_notifier(const u32 node, const u32 event, + const u32 wake, const u32 enable) +{ + return zynqmp_pm_invoke_fn(PM_REGISTER_NOTIFIER, node, event, + wake, enable, NULL); +} +EXPORT_SYMBOL_GPL(zynqmp_pm_register_notifier); + /** * zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart * @type: Shutdown or restart? 0 for shutdown, 1 for restart diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 47fd4e52a423..d30d39dc8cb4 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -2,7 +2,7 @@ /* * Xilinx Zynq MPSoC Firmware layer * - * Copyright (C) 2014-2019 Xilinx + * Copyright (C) 2014-2021 Xilinx * * Michal Simek * Davorin Mista @@ -66,6 +66,7 @@ enum pm_api_id { PM_GET_API_VERSION = 1, + PM_REGISTER_NOTIFIER = 5, PM_SYSTEM_SHUTDOWN = 12, PM_REQUEST_NODE = 13, PM_RELEASE_NODE = 14, @@ -427,6 +428,8 @@ int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param, int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, u32 value); int zynqmp_pm_load_pdi(const u32 src, const u64 address); +int zynqmp_pm_register_notifier(const u32 node, const u32 event, + const u32 wake, const u32 enable); #else static inline int zynqmp_pm_get_api_version(u32 *version) { @@ -658,6 +661,12 @@ static inline int zynqmp_pm_load_pdi(const u32 src, const u64 address) { return -ENODEV; } + +static inline int zynqmp_pm_register_notifier(const u32 node, const u32 event, + const u32 wake, const u32 enable) +{ + return -ENODEV; +} #endif #endif /* __FIRMWARE_ZYNQMP_H__ */ -- cgit v1.2.3 From fbce9f14055e547d270046f61758c29c957e675d Mon Sep 17 00:00:00 2001 From: Abhyuday Godhasara Date: Mon, 25 Oct 2021 21:25:21 -0700 Subject: firmware: xilinx: add macros of node ids for error event Add macros for the Node-Id of Error events. Move supported api callback ids from zynqmp-power to zynqmp-firmware. Acked-by: Michal Simek Signed-off-by: Rajan Vaja Signed-off-by: Abhyuday Godhasara Link: https://lore.kernel.org/r/20211026042525.26612-3-abhyuday.godhasara@xilinx.com Signed-off-by: Greg Kroah-Hartman --- drivers/soc/xilinx/zynqmp_power.c | 6 ------ include/linux/firmware/xlnx-zynqmp.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index c556623dae02..76478fe2301f 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -46,12 +46,6 @@ static const char *const suspend_modes[] = { static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; -enum pm_api_cb_id { - PM_INIT_SUSPEND_CB = 30, - PM_ACKNOWLEDGE_CB, - PM_NOTIFY_CB, -}; - static void zynqmp_pm_get_callback_data(u32 *buf) { zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index d30d39dc8cb4..b0a38091db71 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -64,6 +64,20 @@ #define XILINX_ZYNQMP_PM_FPGA_FULL 0x0U #define XILINX_ZYNQMP_PM_FPGA_PARTIAL BIT(0) +/* + * Node IDs for the Error Events. + */ +#define EVENT_ERROR_PMC_ERR1 (0x28100000U) +#define EVENT_ERROR_PMC_ERR2 (0x28104000U) +#define EVENT_ERROR_PSM_ERR1 (0x28108000U) +#define EVENT_ERROR_PSM_ERR2 (0x2810C000U) + +enum pm_api_cb_id { + PM_INIT_SUSPEND_CB = 30, + PM_ACKNOWLEDGE_CB = 31, + PM_NOTIFY_CB = 32, +}; + enum pm_api_id { PM_GET_API_VERSION = 1, PM_REGISTER_NOTIFIER = 5, -- cgit v1.2.3 From f4d77525679e289d4976ca03b620ac4cc5403205 Mon Sep 17 00:00:00 2001 From: Abhyuday Godhasara Date: Mon, 25 Oct 2021 21:25:22 -0700 Subject: firmware: xilinx: export the feature check of zynqmp firmware Export the zynqmp_pm_feature(), so it can be use by other as to get API version available in firmware. Acked-by: Michal Simek Signed-off-by: Rajan Vaja Signed-off-by: Abhyuday Godhasara Link: https://lore.kernel.org/r/20211026042525.26612-4-abhyuday.godhasara@xilinx.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 3 ++- include/linux/firmware/xlnx-zynqmp.h | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 30edcd233786..c2828ee6d4cf 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -160,7 +160,7 @@ static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2, * * Return: Returns status, either success or error+reason */ -static int zynqmp_pm_feature(u32 api_id) +int zynqmp_pm_feature(const u32 api_id) { int ret; u32 ret_payload[PAYLOAD_ARG_CNT]; @@ -197,6 +197,7 @@ static int zynqmp_pm_feature(u32 api_id) return ret; } +EXPORT_SYMBOL_GPL(zynqmp_pm_feature); /** * zynqmp_pm_invoke_fn() - Invoke the system-level platform management layer diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index b0a38091db71..077e894bb340 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -444,6 +444,7 @@ int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param, int zynqmp_pm_load_pdi(const u32 src, const u64 address); int zynqmp_pm_register_notifier(const u32 node, const u32 event, const u32 wake, const u32 enable); +int zynqmp_pm_feature(const u32 api_id); #else static inline int zynqmp_pm_get_api_version(u32 *version) { @@ -681,6 +682,11 @@ static inline int zynqmp_pm_register_notifier(const u32 node, const u32 event, { return -ENODEV; } + +static inline int zynqmp_pm_feature(const u32 api_id) +{ + return -ENODEV; +} #endif #endif /* __FIRMWARE_ZYNQMP_H__ */ -- cgit v1.2.3 From 8eebe6281ac1062764db23d181e3feb3305a3690 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Gustavo=20Nakagomi=20Lopez?= Date: Mon, 25 Oct 2021 09:19:50 -0300 Subject: iio: adc: lpc18xx_adc: Reorder clk_get_rate() function call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clk_get_rate() is not guaranteed to work if called before clk_prepare_enable(). Reorder clk_get_rate(), so it's called after clk_prepare_enable() and after devm_add_action_or_reset() of lpc18xx_clk_disable(). Not that this is not a problem on this particular device, but it is good to remove a case that might get copied elsewhere. Suggested-by: Jonathan Cameron Acked-by: Vladimir Zapolskiy Signed-off-by: André Gustavo Nakagomi Lopez Link: https://lore.kernel.org/r/YXag5l4xBkGQH3tq@Andryuu.br Signed-off-by: Jonathan Cameron --- drivers/iio/adc/lpc18xx_adc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c index ceefa4d793cf..ae9c9384f23e 100644 --- a/drivers/iio/adc/lpc18xx_adc.c +++ b/drivers/iio/adc/lpc18xx_adc.c @@ -157,9 +157,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk), "error getting clock\n"); - rate = clk_get_rate(adc->clk); - clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); - adc->vref = devm_regulator_get(&pdev->dev, "vref"); if (IS_ERR(adc->vref)) return dev_err_probe(&pdev->dev, PTR_ERR(adc->vref), @@ -192,6 +189,9 @@ static int lpc18xx_adc_probe(struct platform_device *pdev) if (ret) return ret; + rate = clk_get_rate(adc->clk); + clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET); + adc->cr_reg = (clkdiv << LPC18XX_ADC_CR_CLKDIV_SHIFT) | LPC18XX_ADC_CR_PDN; writel(adc->cr_reg, adc->base + LPC18XX_ADC_CR); -- cgit v1.2.3 From e12653eb77b90fc33ff3e0b9caf21b02b026f552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Mon, 25 Oct 2021 21:50:07 +0200 Subject: iio: accel: mma7660: Warn about failure to put device in stand-by in .remove() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whan an i2c driver's remove function returns a non-zero error code nothing happens apart from emitting a generic error message. Make this error message more device specific and return zero instead. Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211025195007.84541-1-u.kleine-koenig@pengutronix.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma7660.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index cd6cdf2c51b0..24b83ccdb950 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -210,10 +210,16 @@ static int mma7660_probe(struct i2c_client *client, static int mma7660_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + int ret; iio_device_unregister(indio_dev); - return mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); + ret = mma7660_set_mode(iio_priv(indio_dev), MMA7660_MODE_STANDBY); + if (ret) + dev_warn(&client->dev, "Failed to put device in stand-by mode (%pe), ignoring\n", + ERR_PTR(ret)); + + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3 From fb45c7a31ec1f772502867ea87a2315b57a9f439 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 21 Oct 2021 14:59:50 +0200 Subject: iio: xilinx-xadc: Make IRQ optional In some setups the IRQ signal of the XADC might not be wired to the host system. The driver currently requires that an interrupt is specified. Make the interrupt optional so the driver can be used in such setups where the interrupt is not connected. Since both the internal triggers as well as events depend on the interrupt being connected both are not available when the interrupt is not connected. Buffered access is still supported even without an interrupt since an external trigger can be used. The IRQ is only optional when using the AXI interface, since the PCAP interface needs the IRQ for reading and writing registers. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211021125950.28707-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 62 ++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 83bea5ef765d..2aa4278ecba7 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -107,6 +107,7 @@ static const unsigned int XADC_ZYNQ_UNMASK_TIMEOUT = 500; #define XADC_AXI_INT_ALARM_MASK 0x3c0f #define XADC_FLAGS_BUFFERED BIT(0) +#define XADC_FLAGS_IRQ_OPTIONAL BIT(1) /* * The XADC hardware supports a samplerate of up to 1MSPS. Unfortunately it does @@ -562,7 +563,7 @@ static const struct xadc_ops xadc_7s_axi_ops = { .get_dclk_rate = xadc_axi_get_dclk, .update_alarm = xadc_axi_update_alarm, .interrupt_handler = xadc_axi_interrupt_handler, - .flags = XADC_FLAGS_BUFFERED, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, .type = XADC_TYPE_S7, }; @@ -573,7 +574,7 @@ static const struct xadc_ops xadc_us_axi_ops = { .get_dclk_rate = xadc_axi_get_dclk, .update_alarm = xadc_axi_update_alarm, .interrupt_handler = xadc_axi_interrupt_handler, - .flags = XADC_FLAGS_BUFFERED, + .flags = XADC_FLAGS_BUFFERED | XADC_FLAGS_IRQ_OPTIONAL, .type = XADC_TYPE_US, }; @@ -1182,7 +1183,7 @@ static const struct of_device_id xadc_of_match_table[] = { MODULE_DEVICE_TABLE(of, xadc_of_match_table); static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, - unsigned int *conf) + unsigned int *conf, int irq) { struct device *dev = indio_dev->dev.parent; struct xadc *xadc = iio_priv(indio_dev); @@ -1195,6 +1196,7 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, u32 ext_mux_chan; u32 reg; int ret; + int i; *conf = 0; @@ -1273,6 +1275,14 @@ static int xadc_parse_dt(struct iio_dev *indio_dev, struct device_node *np, } of_node_put(chan_node); + /* No IRQ => no events */ + if (irq <= 0) { + for (i = 0; i < num_channels; i++) { + channels[i].event_spec = NULL; + channels[i].num_event_specs = 0; + } + } + indio_dev->num_channels = num_channels; indio_dev->channels = devm_krealloc(dev, channels, sizeof(*channels) * num_channels, @@ -1307,6 +1317,7 @@ static int xadc_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct of_device_id *id; + const struct xadc_ops *ops; struct iio_dev *indio_dev; unsigned int bipolar_mask; unsigned int conf0; @@ -1322,9 +1333,12 @@ static int xadc_probe(struct platform_device *pdev) if (!id) return -EINVAL; - irq = platform_get_irq(pdev, 0); - if (irq <= 0) - return -ENXIO; + ops = id->data; + + irq = platform_get_irq_optional(pdev, 0); + if (irq < 0 && + (irq != -ENXIO || !(ops->flags & XADC_FLAGS_IRQ_OPTIONAL))) + return irq; indio_dev = devm_iio_device_alloc(dev, sizeof(*xadc)); if (!indio_dev) @@ -1345,7 +1359,7 @@ static int xadc_probe(struct platform_device *pdev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &xadc_info; - ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0); + ret = xadc_parse_dt(indio_dev, dev->of_node, &conf0, irq); if (ret) return ret; @@ -1357,14 +1371,16 @@ static int xadc_probe(struct platform_device *pdev) if (ret) return ret; - xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); - if (IS_ERR(xadc->convst_trigger)) - return PTR_ERR(xadc->convst_trigger); + if (irq > 0) { + xadc->convst_trigger = xadc_alloc_trigger(indio_dev, "convst"); + if (IS_ERR(xadc->convst_trigger)) + return PTR_ERR(xadc->convst_trigger); - xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, - "samplerate"); - if (IS_ERR(xadc->samplerate_trigger)) - return PTR_ERR(xadc->samplerate_trigger); + xadc->samplerate_trigger = xadc_alloc_trigger(indio_dev, + "samplerate"); + if (IS_ERR(xadc->samplerate_trigger)) + return PTR_ERR(xadc->samplerate_trigger); + } } xadc->clk = devm_clk_get(dev, NULL); @@ -1396,15 +1412,17 @@ static int xadc_probe(struct platform_device *pdev) } } - ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, 0, - dev_name(dev), indio_dev); - if (ret) - return ret; + if (irq > 0) { + ret = devm_request_irq(dev, irq, xadc->ops->interrupt_handler, + 0, dev_name(dev), indio_dev); + if (ret) + return ret; - ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, - &xadc->zynq_unmask_work); - if (ret) - return ret; + ret = devm_add_action_or_reset(dev, xadc_cancel_delayed_work, + &xadc->zynq_unmask_work); + if (ret) + return ret; + } ret = xadc->ops->setup(pdev, indio_dev, irq); if (ret) -- cgit v1.2.3 From 8cf524be72fa8205754ed0ddc11a32aaf156c39f Mon Sep 17 00:00:00 2001 From: Wan Jiabing Date: Thu, 21 Oct 2021 08:18:23 -0400 Subject: iio: adc: stm32-adc: Fix of_node_put() issue in stm32-adc Fix following coccicheck warning: ./drivers/iio/adc/stm32-adc.c:2014:1-33: WARNING: Function for_each_available_child_of_node should have of_node_put() before return. Early exits from for_each_available_child_of_node should decrement the node reference counter. Replace return by goto here. Reviewed-by: Fabrice Gasnier Signed-off-by: Wan Jiabing Link: https://lore.kernel.org/r/20211021121826.6339-1-wanjiabing@vivo.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stm32-adc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 6245434f8377..7f1fb36c747c 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -2024,7 +2024,8 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, if (strlen(name) >= STM32_ADC_CH_SZ) { dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n", name, STM32_ADC_CH_SZ); - return -EINVAL; + ret = -EINVAL; + goto err; } strncpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); ret = stm32_adc_populate_int_ch(indio_dev, name, val); -- cgit v1.2.3 From 4498863cad7befbbd14006e033ae84784a33ae53 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Oct 2021 10:53:49 +0200 Subject: iio: st-sensors: Use dev_to_iio_dev() in sysfs callbacks Using `dev_get_drvdata()` in IIO sysfs callbacks to get a pointer to the IIO device is a relic from the very early days of IIO. The IIO core as well as most other drivers have switched over to using `dev_to_iio_dev()` instead. This driver is one of the last few drivers remaining that uses the outdated idiom, update it. This will allow to eventually update the IIO core to no longer set the drvdata for the IIO device and free it up for driver usage. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211020085349.16178-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/common/st_sensors/st_sensors_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c index 1de395bda03e..eb452d0c423c 100644 --- a/drivers/iio/common/st_sensors/st_sensors_core.c +++ b/drivers/iio/common/st_sensors/st_sensors_core.c @@ -638,7 +638,7 @@ ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0; - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct st_sensor_data *sdata = iio_priv(indio_dev); mutex_lock(&indio_dev->mlock); @@ -660,7 +660,7 @@ ssize_t st_sensors_sysfs_scale_avail(struct device *dev, struct device_attribute *attr, char *buf) { int i, len = 0, q, r; - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct st_sensor_data *sdata = iio_priv(indio_dev); mutex_lock(&indio_dev->mlock); -- cgit v1.2.3 From ba1287e73182f2521d4fc5c0809620ed06652796 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Oct 2021 10:57:54 +0200 Subject: iio: imx7d_adc: Don't pass IIO device to imx7d_adc_{enable,disable}() The `imx7d_adc_enable()` and `imx7d_adc_disable()` functions are used as the suspend and resume callbacks for the device. When called as suspend/resume functions they are called with the platform_device's device as their parameter. In addition the functions are called on device probe and remove. In this case they are passed the struct device of the IIO device that the driver registers. This works because in the `imx7d_adc_{enable,disable}()` functions the passed struct device is only ever used as a parameter to `dev_get_drvdata()` and `dev_get_drvdata()` returns the same value for the platform device and the IIO device. But for consistency we should pass the same struct device to the `imx7d_adc_{enable,disable}()` in all cases. This will avoid accidental breakage if the device is ever used for something more than `dev_get_drvdata()`. Another motivation is that `dev_get_drvdata()` on the IIO device relies on the IIO core calling `dev_set_drvdata()`. Something we want to remove. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211020085754.16654-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/imx7d_adc.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c index 092f8d296527..12f5b8e34c84 100644 --- a/drivers/iio/adc/imx7d_adc.c +++ b/drivers/iio/adc/imx7d_adc.c @@ -522,12 +522,11 @@ static int imx7d_adc_probe(struct platform_device *pdev) imx7d_adc_feature_config(info); - ret = imx7d_adc_enable(&indio_dev->dev); + ret = imx7d_adc_enable(dev); if (ret) return ret; - ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, - &indio_dev->dev); + ret = devm_add_action_or_reset(dev, __imx7d_adc_disable, dev); if (ret) return ret; -- cgit v1.2.3 From dc19fa63ad80a636fdbc1a02153d1ab140cb901f Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Wed, 20 Oct 2021 16:21:10 +0200 Subject: iio: ms5611: Simplify IO callback parameters The ms5611 passes &indio_dev->dev as a parameter to all its IO callbacks only to directly cast the struct device back to struct iio_dev. And the struct iio_dev is then only used to get the drivers state struct. Simplify this a bit by passing the state struct directly. This makes it a bit easier to follow what the code is doing. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211020142110.7060-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 6 +++--- drivers/iio/pressure/ms5611_core.c | 7 +++---- drivers/iio/pressure/ms5611_i2c.c | 11 ++++------- drivers/iio/pressure/ms5611_spi.c | 17 +++++++---------- 4 files changed, 17 insertions(+), 24 deletions(-) diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index 86b1c4b1820d..cbc9349c342a 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -50,9 +50,9 @@ struct ms5611_state { const struct ms5611_osr *pressure_osr; const struct ms5611_osr *temp_osr; - int (*reset)(struct device *dev); - int (*read_prom_word)(struct device *dev, int index, u16 *word); - int (*read_adc_temp_and_pressure)(struct device *dev, + int (*reset)(struct ms5611_state *st); + int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word); + int (*read_adc_temp_and_pressure)(struct ms5611_state *st, s32 *temp, s32 *pressure); struct ms5611_chip_info *chip_info; diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index ee75f08655c9..a4d0b54cde9b 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -85,8 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) struct ms5611_state *st = iio_priv(indio_dev); for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { - ret = st->read_prom_word(&indio_dev->dev, - i, &st->chip_info->prom[i]); + ret = st->read_prom_word(st, i, &st->chip_info->prom[i]); if (ret < 0) { dev_err(&indio_dev->dev, "failed to read prom at %d\n", i); @@ -108,7 +107,7 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, int ret; struct ms5611_state *st = iio_priv(indio_dev); - ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure); + ret = st->read_adc_temp_and_pressure(st, temp, pressure); if (ret < 0) { dev_err(&indio_dev->dev, "failed to read temperature and pressure\n"); @@ -196,7 +195,7 @@ static int ms5611_reset(struct iio_dev *indio_dev) int ret; struct ms5611_state *st = iio_priv(indio_dev); - ret = st->reset(&indio_dev->dev); + ret = st->reset(st); if (ret < 0) { dev_err(&indio_dev->dev, "failed to reset device\n"); return ret; diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c index 5c82d80f85b6..1047a85527a9 100644 --- a/drivers/iio/pressure/ms5611_i2c.c +++ b/drivers/iio/pressure/ms5611_i2c.c @@ -20,17 +20,15 @@ #include "ms5611.h" -static int ms5611_i2c_reset(struct device *dev) +static int ms5611_i2c_reset(struct ms5611_state *st) { - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); - return i2c_smbus_write_byte(st->client, MS5611_RESET); } -static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word) +static int ms5611_i2c_read_prom_word(struct ms5611_state *st, int index, + u16 *word) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); ret = i2c_smbus_read_word_swapped(st->client, MS5611_READ_PROM_WORD + (index << 1)); @@ -57,11 +55,10 @@ static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val) return 0; } -static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev, +static int ms5611_i2c_read_adc_temp_and_pressure(struct ms5611_state *st, s32 *temp, s32 *pressure) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); const struct ms5611_osr *osr = st->temp_osr; ret = i2c_smbus_write_byte(st->client, osr->cmd); diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c index 79bed64c9b68..9fa2dcd71760 100644 --- a/drivers/iio/pressure/ms5611_spi.c +++ b/drivers/iio/pressure/ms5611_spi.c @@ -15,18 +15,17 @@ #include "ms5611.h" -static int ms5611_spi_reset(struct device *dev) +static int ms5611_spi_reset(struct ms5611_state *st) { u8 cmd = MS5611_RESET; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); return spi_write_then_read(st->client, &cmd, 1, NULL, 0); } -static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) +static int ms5611_spi_read_prom_word(struct ms5611_state *st, int index, + u16 *word) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1)); if (ret < 0) @@ -37,11 +36,10 @@ static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) return 0; } -static int ms5611_spi_read_adc(struct device *dev, s32 *val) +static int ms5611_spi_read_adc(struct ms5611_state *st, s32 *val) { int ret; u8 buf[3] = { MS5611_READ_ADC }; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); ret = spi_write_then_read(st->client, buf, 1, buf, 3); if (ret < 0) @@ -52,11 +50,10 @@ static int ms5611_spi_read_adc(struct device *dev, s32 *val) return 0; } -static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, +static int ms5611_spi_read_adc_temp_and_pressure(struct ms5611_state *st, s32 *temp, s32 *pressure) { int ret; - struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); const struct ms5611_osr *osr = st->temp_osr; /* @@ -68,7 +65,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, return ret; usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL)); - ret = ms5611_spi_read_adc(dev, temp); + ret = ms5611_spi_read_adc(st, temp); if (ret < 0) return ret; @@ -78,7 +75,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, return ret; usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL)); - return ms5611_spi_read_adc(dev, pressure); + return ms5611_spi_read_adc(st, pressure); } static int ms5611_spi_probe(struct spi_device *spi) -- cgit v1.2.3 From 4bdc3e967dc6c22e32da0ddba099828415ac4b0e Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 21 Oct 2021 20:42:53 +0800 Subject: iio: adc: ina2xx: Make use of the helper macro kthread_run() Repalce kthread_create/wake_up_process() with kthread_run() to simplify the code. Reviewed-by: Lars-Peter Clausen Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211021124254.3247-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index a4b2ff9e0dd5..360d7a00f60d 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -842,15 +842,14 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) dev_dbg(&indio_dev->dev, "Async readout mode: %d\n", chip->allow_async_readout); - task = kthread_create(ina2xx_capture_thread, (void *)indio_dev, - "%s:%d-%uus", indio_dev->name, - iio_device_id(indio_dev), - sampling_us); + task = kthread_run(ina2xx_capture_thread, (void *)indio_dev, + "%s:%d-%uus", indio_dev->name, + iio_device_id(indio_dev), + sampling_us); if (IS_ERR(task)) return PTR_ERR(task); get_task_struct(task); - wake_up_process(task); chip->task = task; return 0; -- cgit v1.2.3 From 2c4ce5041cd5d66875137a854b5e19672dce19a5 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Thu, 21 Oct 2021 20:42:54 +0800 Subject: iio: adc: ina2xx: Avoid double reference counting from get_task_struct/put_task_struct() kthread_run() and kthread_stop() already do reference counting of the task, so remove get_task_struct/put_task_struct() to avoid double reference counting. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211021124254.3247-2-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 360d7a00f60d..352f27657238 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -849,7 +849,6 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) if (IS_ERR(task)) return PTR_ERR(task); - get_task_struct(task); chip->task = task; return 0; @@ -861,7 +860,6 @@ static int ina2xx_buffer_disable(struct iio_dev *indio_dev) if (chip->task) { kthread_stop(chip->task); - put_task_struct(chip->task); chip->task = NULL; } -- cgit v1.2.3 From 6bb835f3d00467c9a5e35f4955afa29df96a404e Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Sun, 24 Oct 2021 12:16:26 +0300 Subject: iio: core: Introduce IIO_VAL_INT_64. Introduce IIO_VAL_INT_64 to read 64-bit value for channel attribute. Val is used as lower 32 bits. Signed-off-by: Andriy Tryshnivskyy Link: https://lore.kernel.org/r/20211024091627.28031-2-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 3 +++ include/linux/iio/types.h | 1 + 2 files changed, 4 insertions(+) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 463a63d5bf56..d94d26b11473 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -702,6 +702,9 @@ static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, } case IIO_VAL_CHAR: return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); + case IIO_VAL_INT_64: + tmp2 = (s64)((((u64)vals[1]) << 32) | (u32)vals[0]); + return sysfs_emit_at(buf, offset, "%lld", tmp2); default: return 0; } diff --git a/include/linux/iio/types.h b/include/linux/iio/types.h index 84b3f8175cc6..a7aa91f3a8dc 100644 --- a/include/linux/iio/types.h +++ b/include/linux/iio/types.h @@ -24,6 +24,7 @@ enum iio_event_info { #define IIO_VAL_INT_PLUS_NANO 3 #define IIO_VAL_INT_PLUS_MICRO_DB 4 #define IIO_VAL_INT_MULTIPLE 5 +#define IIO_VAL_INT_64 6 /* 64-bit data, val is lower 32 bits */ #define IIO_VAL_FRACTIONAL 10 #define IIO_VAL_FRACTIONAL_LOG2 11 #define IIO_VAL_CHAR 12 -- cgit v1.2.3 From 1fd85607e1e52dc6f3ac1a993f9ab57e416aa9ab Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Sun, 24 Oct 2021 12:16:27 +0300 Subject: iio/scmi: Add reading "raw" attribute. Add IIO_CHAN_INFO_RAW to the mask and implement corresponding reading "raw" attribute in scmi_iio_read_raw. Signed-off-by: Andriy Tryshnivskyy Acked-by: Jyoti Bhayana Link: https://lore.kernel.org/r/20211024091627.28031-3-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/common/scmi_sensors/scmi_iio.c | 57 +++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 7cf2bf282cef..d538bf3ab1ef 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -279,6 +279,52 @@ static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2) return 0; } +static int scmi_iio_read_channel_data(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, int *val, int *val2) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_config; + struct scmi_sensor_reading readings[SCMI_IIO_NUM_OF_AXIS]; + int err; + + sensor_config = FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_ENABLE); + err = sensor->sensor_ops->config_set( + sensor->ph, sensor->sensor_info->id, sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in enabling sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + err = sensor->sensor_ops->reading_get_timestamped( + sensor->ph, sensor->sensor_info->id, + sensor->sensor_info->num_axis, readings); + if (err) { + dev_err(&iio_dev->dev, + "Error in reading raw attribute for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + sensor_config = FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_DISABLE); + err = sensor->sensor_ops->config_set( + sensor->ph, sensor->sensor_info->id, sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in disabling sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + *val = lower_32_bits(readings[ch->scan_index].value); + *val2 = upper_32_bits(readings[ch->scan_index].value); + + return IIO_VAL_INT_64; +} + static int scmi_iio_read_raw(struct iio_dev *iio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -300,6 +346,14 @@ static int scmi_iio_read_raw(struct iio_dev *iio_dev, case IIO_CHAN_INFO_SAMP_FREQ: ret = scmi_iio_get_odr_val(iio_dev, val, val2); return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + return ret; + + ret = scmi_iio_read_channel_data(iio_dev, ch, val, val2); + iio_device_release_direct_mode(iio_dev); + return ret; default: return -EINVAL; } @@ -381,7 +435,8 @@ static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan, iio_chan->type = type; iio_chan->modified = 1; iio_chan->channel2 = mod; - iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE); + iio_chan->info_mask_separate = + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_RAW); iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); iio_chan->info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ); -- cgit v1.2.3 From 3c33b7b8267f0795d74f407e97f3eeec2acb0165 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 09:04:21 +0100 Subject: iio: Mark iio_device_type as const The iio_device_type struct is never modified, mark it as const. This allows it to be placed in a read-only memory section, which will protect against accidental or deliberate modification. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031080421.2086-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/iio_core.h | 2 +- drivers/iio/industrialio-core.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index 61e318431de9..501e286702ef 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -16,7 +16,7 @@ struct iio_buffer; struct iio_chan_spec; struct iio_dev; -extern struct device_type iio_device_type; +extern const struct device_type iio_device_type; struct iio_dev_buffer_pair { struct iio_dev *indio_dev; diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index d94d26b11473..20d5178ca073 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1622,7 +1622,7 @@ static void iio_dev_release(struct device *device) kfree(iio_dev_opaque); } -struct device_type iio_device_type = { +const struct device_type iio_device_type = { .name = "iio_device", .release = iio_dev_release, }; -- cgit v1.2.3 From 2d323927519c3ffbf4b0700459333bcc5528bb96 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:22 +0100 Subject: iio: interrupt-trigger: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-interrupt.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index f746c460bf2a..5f49cd105fae 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -25,9 +25,6 @@ static irqreturn_t iio_interrupt_trigger_poll(int irq, void *private) return IRQ_HANDLED; } -static const struct iio_trigger_ops iio_interrupt_trigger_ops = { -}; - static int iio_interrupt_trigger_probe(struct platform_device *pdev) { struct iio_interrupt_trigger_info *trig_info; @@ -58,7 +55,6 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) } iio_trigger_set_drvdata(trig, trig_info); trig_info->irq = irq; - trig->ops = &iio_interrupt_trigger_ops; ret = request_irq(irq, iio_interrupt_trigger_poll, irqflags, trig->name, trig); if (ret) { -- cgit v1.2.3 From e28309ad8a06da6b5bdd210b3c98efc3149f862c Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:23 +0100 Subject: iio: sysfs-trigger: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-2-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-sysfs.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index e9adfff45b39..2a4b75897910 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -124,9 +124,6 @@ static const struct attribute_group *iio_sysfs_trigger_attr_groups[] = { NULL }; -static const struct iio_trigger_ops iio_sysfs_trigger_ops = { -}; - static int iio_sysfs_trigger_probe(int id) { struct iio_sysfs_trig *t; @@ -156,7 +153,6 @@ static int iio_sysfs_trigger_probe(int id) } t->trig->dev.groups = iio_sysfs_trigger_attr_groups; - t->trig->ops = &iio_sysfs_trigger_ops; iio_trigger_set_drvdata(t->trig, t); t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work); -- cgit v1.2.3 From a3ab9c0622511bc8330ba9da0b406de6c7a0d645 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:24 +0100 Subject: iio: ad_sigma_delta: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-3-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 1d652d9b2f5c..cd418bd8bd87 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -467,9 +467,6 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig) } EXPORT_SYMBOL_GPL(ad_sd_validate_trigger); -static const struct iio_trigger_ops ad_sd_trigger_ops = { -}; - static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); @@ -486,7 +483,6 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de if (sigma_delta->trig == NULL) return -ENOMEM; - sigma_delta->trig->ops = &ad_sd_trigger_ops; init_completion(&sigma_delta->completion); sigma_delta->irq_dis = true; -- cgit v1.2.3 From 26ae5ed3fcda509ca46e28447bf0aa0fbff5bb88 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:25 +0100 Subject: iio: afe4403: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-4-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4403.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 97b82f9a8e45..273f16dcaff8 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -345,9 +345,6 @@ err: return IRQ_HANDLED; } -static const struct iio_trigger_ops afe4403_trigger_ops = { -}; - #define AFE4403_TIMING_PAIRS \ { AFE440X_LED2STC, 0x000050 }, \ { AFE440X_LED2ENDC, 0x0003e7 }, \ @@ -530,8 +527,6 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_set_drvdata(afe->trig, indio_dev); - afe->trig->ops = &afe4403_trigger_ops; - ret = iio_trigger_register(afe->trig); if (ret) { dev_err(afe->dev, "Unable to register IIO trigger\n"); -- cgit v1.2.3 From 35ce398a554c9851f83730c186c7c325bb127e40 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:26 +0100 Subject: iio: afe4404: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-5-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/health/afe4404.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 7ef3f5e34de5..aa9311e1e655 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -347,9 +347,6 @@ err: return IRQ_HANDLED; } -static const struct iio_trigger_ops afe4404_trigger_ops = { -}; - /* Default timings from data-sheet */ #define AFE4404_TIMING_PAIRS \ { AFE440X_PRPCOUNT, 39999 }, \ @@ -537,8 +534,6 @@ static int afe4404_probe(struct i2c_client *client, iio_trigger_set_drvdata(afe->trig, indio_dev); - afe->trig->ops = &afe4404_trigger_ops; - ret = iio_trigger_register(afe->trig); if (ret) { dev_err(afe->dev, "Unable to register IIO trigger\n"); -- cgit v1.2.3 From 44c3bf8c1a4838115f5de5b66f84370b7aff2e21 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:27 +0100 Subject: iio: as3935: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-6-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index 3797a8f54276..d62766b6b39e 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -238,9 +238,6 @@ err_read: return IRQ_HANDLED; } -static const struct iio_trigger_ops iio_interrupt_trigger_ops = { -}; - static void as3935_event_work(struct work_struct *work) { struct as3935_state *st; @@ -417,7 +414,6 @@ static int as3935_probe(struct spi_device *spi) st->trig = trig; st->noise_tripped = jiffies - HZ; iio_trigger_set_drvdata(trig, indio_dev); - trig->ops = &iio_interrupt_trigger_ops; ret = devm_iio_trigger_register(dev, trig); if (ret) { -- cgit v1.2.3 From f3df6c739a8513ab81dd9d49c0329933620cfaa7 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:28 +0100 Subject: iio: atlas-sensor: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-7-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/atlas-sensor.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 9cb99585b6ff..04b44a327614 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -434,9 +434,6 @@ static int atlas_buffer_predisable(struct iio_dev *indio_dev) return 0; } -static const struct iio_trigger_ops atlas_interrupt_trigger_ops = { -}; - static const struct iio_buffer_setup_ops atlas_buffer_setup_ops = { .postenable = atlas_buffer_postenable, .predisable = atlas_buffer_predisable, @@ -645,7 +642,6 @@ static int atlas_probe(struct i2c_client *client, data->client = client; data->trig = trig; data->chip = chip; - trig->ops = &atlas_interrupt_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); i2c_set_clientdata(client, indio_dev); -- cgit v1.2.3 From 9662afc9059b79ed4efb8b90b2865f9c919c7fe4 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:29 +0100 Subject: iio: gp2ap020a00f: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-8-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/light/gp2ap020a00f.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index d1d9f2d319e4..b820041159f7 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1467,9 +1467,6 @@ static const struct iio_buffer_setup_ops gp2ap020a00f_buffer_setup_ops = { .predisable = &gp2ap020a00f_buffer_predisable, }; -static const struct iio_trigger_ops gp2ap020a00f_trigger_ops = { -}; - static int gp2ap020a00f_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -1550,8 +1547,6 @@ static int gp2ap020a00f_probe(struct i2c_client *client, goto error_uninit_buffer; } - data->trig->ops = &gp2ap020a00f_trigger_ops; - init_irq_work(&data->work, gp2ap020a00f_iio_trigger_work); err = iio_trigger_register(data->trig); -- cgit v1.2.3 From 6a9a90364914d41a1a7456dd964af8dc2ab3ed4b Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 31 Oct 2021 15:21:30 +0100 Subject: iio: lmp91000: Remove no-op trigger ops The IIO core handles a trigger ops with all NULL callbacks the same as if the trigger ops itself was NULL. Remove the empty trigger ops from the interrupt trigger driver to slightly reduce the boilerplate code. Object size of the driver module is also slightly reduced. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211031142130.20791-9-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/potentiostat/lmp91000.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index ed30bdaa10ec..fe514f0b5506 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -271,9 +271,6 @@ static int lmp91000_buffer_cb(const void *val, void *private) return 0; } -static const struct iio_trigger_ops lmp91000_trigger_ops = { -}; - static int lmp91000_buffer_postenable(struct iio_dev *indio_dev) { struct lmp91000_data *data = iio_priv(indio_dev); @@ -330,7 +327,6 @@ static int lmp91000_probe(struct i2c_client *client, return -ENOMEM; } - data->trig->ops = &lmp91000_trigger_ops; init_completion(&data->completion); ret = lmp91000_read_config(data); -- cgit v1.2.3 From eb0469894ba788ffdc81097b7dea822432e479d9 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 1 Nov 2021 11:27:34 +0100 Subject: iio: mma8452: Use correct type for return variable in IRQ handler The IRQ handler's return type is irqreturn_t. The mma8452 uses a variable to store the return value, but the variable is of type int. Change this to irqreturn_t. This makes it easier to verify that the code is correct. Signed-off-by: Lars-Peter Clausen Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211101102734.32291-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma8452.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 715b8138fb71..4ac4c06e9707 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -1053,7 +1053,7 @@ static irqreturn_t mma8452_interrupt(int irq, void *p) { struct iio_dev *indio_dev = p; struct mma8452_data *data = iio_priv(indio_dev); - int ret = IRQ_NONE; + irqreturn_t ret = IRQ_NONE; int src; src = i2c_smbus_read_byte_data(data->client, MMA8452_INT_SRC); -- cgit v1.2.3 From 907b2ad8c9acad39ac1f0ccdbbe66c63856055e3 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Oct 2021 10:29:28 +0200 Subject: iio: at91-sama5d2: Fix incorrect cast to platform_device The at91-sama5d2 driver calls `to_platform_device()` on a struct device that is part of a IIO device. This is incorrect since `to_platform_device()` must only be called on a struct device that is part of a platform device. The code still works by accident because non of the struct platform_device specific fields are accessed. Refactor the code a bit so that it behaves identically, but does not use the incorrect cast. This avoids accidentally adding undefined behavior in the future by assuming the `struct platform_device` is actually valid. Signed-off-by: Lars-Peter Clausen Tested-by: Eugen Hristev Link: https://lore.kernel.org/r/20211019082929.30503-1-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 4c922ef634f8..3841e7b6c81d 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1661,10 +1661,9 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev, } } -static void at91_adc_dma_init(struct platform_device *pdev) +static void at91_adc_dma_init(struct at91_adc_state *st) { - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct at91_adc_state *st = iio_priv(indio_dev); + struct device *dev = &st->indio_dev->dev; struct dma_slave_config config = {0}; /* we have 2 bytes for each channel */ unsigned int sample_size = st->soc_info.platform->nr_channels * 2; @@ -1679,9 +1678,9 @@ static void at91_adc_dma_init(struct platform_device *pdev) if (st->dma_st.dma_chan) return; - st->dma_st.dma_chan = dma_request_chan(&pdev->dev, "rx"); + st->dma_st.dma_chan = dma_request_chan(dev, "rx"); if (IS_ERR(st->dma_st.dma_chan)) { - dev_info(&pdev->dev, "can't get DMA channel\n"); + dev_info(dev, "can't get DMA channel\n"); st->dma_st.dma_chan = NULL; goto dma_exit; } @@ -1691,7 +1690,7 @@ static void at91_adc_dma_init(struct platform_device *pdev) &st->dma_st.rx_dma_buf, GFP_KERNEL); if (!st->dma_st.rx_buf) { - dev_info(&pdev->dev, "can't allocate coherent DMA area\n"); + dev_info(dev, "can't allocate coherent DMA area\n"); goto dma_chan_disable; } @@ -1704,11 +1703,11 @@ static void at91_adc_dma_init(struct platform_device *pdev) config.dst_maxburst = 1; if (dmaengine_slave_config(st->dma_st.dma_chan, &config)) { - dev_info(&pdev->dev, "can't configure DMA slave\n"); + dev_info(dev, "can't configure DMA slave\n"); goto dma_free_area; } - dev_info(&pdev->dev, "using %s for rx DMA transfers\n", + dev_info(dev, "using %s for rx DMA transfers\n", dma_chan_name(st->dma_st.dma_chan)); return; @@ -1720,13 +1719,12 @@ dma_chan_disable: dma_release_channel(st->dma_st.dma_chan); st->dma_st.dma_chan = NULL; dma_exit: - dev_info(&pdev->dev, "continuing without DMA support\n"); + dev_info(dev, "continuing without DMA support\n"); } -static void at91_adc_dma_disable(struct platform_device *pdev) +static void at91_adc_dma_disable(struct at91_adc_state *st) { - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct at91_adc_state *st = iio_priv(indio_dev); + struct device *dev = &st->indio_dev->dev; /* we have 2 bytes for each channel */ unsigned int sample_size = st->soc_info.platform->nr_channels * 2; unsigned int pages = DIV_ROUND_UP(AT91_HWFIFO_MAX_SIZE * @@ -1744,7 +1742,7 @@ static void at91_adc_dma_disable(struct platform_device *pdev) dma_release_channel(st->dma_st.dma_chan); st->dma_st.dma_chan = NULL; - dev_info(&pdev->dev, "continuing without DMA support\n"); + dev_info(dev, "continuing without DMA support\n"); } static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) @@ -1770,9 +1768,9 @@ static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) */ if (val == 1) - at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + at91_adc_dma_disable(st); else if (val > 1) - at91_adc_dma_init(to_platform_device(&indio_dev->dev)); + at91_adc_dma_init(st); /* * We can start the DMA only after setting the watermark and @@ -1780,7 +1778,7 @@ static int at91_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val) */ ret = at91_adc_buffer_prepare(indio_dev); if (ret) - at91_adc_dma_disable(to_platform_device(&indio_dev->dev)); + at91_adc_dma_disable(st); return ret; } @@ -2077,7 +2075,7 @@ static int at91_adc_probe(struct platform_device *pdev) return 0; dma_disable: - at91_adc_dma_disable(pdev); + at91_adc_dma_disable(st); per_clk_disable_unprepare: clk_disable_unprepare(st->per_clk); vref_disable: @@ -2094,7 +2092,7 @@ static int at91_adc_remove(struct platform_device *pdev) iio_device_unregister(indio_dev); - at91_adc_dma_disable(pdev); + at91_adc_dma_disable(st); clk_disable_unprepare(st->per_clk); -- cgit v1.2.3 From 0d376dc9febb78eda0bc3121f66d4e4d868880c0 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Tue, 19 Oct 2021 10:29:29 +0200 Subject: iio: at91-sama5d2: Use dev_to_iio_dev() in sysfs callbacks Using `dev_get_drvdata()` in IIO sysfs callbacks to get a pointer to the IIO device is a relic from the very early days of IIO. The IIO core as well as most other drivers have switched over to using `dev_to_iio_dev()` instead. This driver is one of the last few drivers remaining that uses the outdated idiom, update it. This will allow to eventually update the IIO core to no longer set the drvdata for the IIO device and free it up for driver usage. Signed-off-by: Lars-Peter Clausen Link: https://lore.kernel.org/r/20211019082929.30503-2-lars@metafoo.de Signed-off-by: Jonathan Cameron --- drivers/iio/adc/at91-sama5d2_adc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index 3841e7b6c81d..a2c406276329 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -1825,7 +1825,7 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev) static ssize_t at91_adc_get_fifo_state(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan); @@ -1834,7 +1834,7 @@ static ssize_t at91_adc_get_fifo_state(struct device *dev, static ssize_t at91_adc_get_watermark(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct at91_adc_state *st = iio_priv(indio_dev); return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark); -- cgit v1.2.3 From f905772e8b16cde9858b9d775b215757d4d8db27 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:01 -0700 Subject: iio: bma220: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-2-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma220_spi.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index bc4c626e454d..74024d7ce5ac 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -27,7 +27,6 @@ #define BMA220_CHIP_ID 0xDD #define BMA220_READ_MASK BIT(7) #define BMA220_RANGE_MASK GENMASK(1, 0) -#define BMA220_DATA_SHIFT 2 #define BMA220_SUSPEND_SLEEP 0xFF #define BMA220_SUSPEND_WAKE 0x00 @@ -45,7 +44,7 @@ .sign = 's', \ .realbits = 6, \ .storagebits = 8, \ - .shift = BMA220_DATA_SHIFT, \ + .shift = 2, \ .endianness = IIO_CPU, \ }, \ } @@ -125,7 +124,8 @@ static int bma220_read_raw(struct iio_dev *indio_dev, ret = bma220_read_reg(data->spi_device, chan->address); if (ret < 0) return -EINVAL; - *val = sign_extend32(ret >> BMA220_DATA_SHIFT, 5); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: ret = bma220_read_reg(data->spi_device, BMA220_REG_RANGE); -- cgit v1.2.3 From 9105079db67a64fa58c10e699aabfe4703f5ac3f Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:02 -0700 Subject: iio: kxcjk-1013: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-3-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/kxcjk-1013.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index a51fdd3c9b5b..88cf0c276893 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -927,7 +927,8 @@ static int kxcjk1013_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->mutex); return ret; } - *val = sign_extend32(ret >> 4, 11); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = kxcjk1013_set_power_state(data, false); } mutex_unlock(&data->mutex); -- cgit v1.2.3 From 1aa2f96abbcc7e68a13ce53deaf41cb2fd2debfa Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:03 -0700 Subject: iio: mma7455: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-4-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/mma7455_core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c index 777c6c384b09..e6739ba74edf 100644 --- a/drivers/iio/accel/mma7455_core.c +++ b/drivers/iio/accel/mma7455_core.c @@ -134,7 +134,8 @@ static int mma7455_read_raw(struct iio_dev *indio_dev, if (ret) return ret; - *val = sign_extend32(le16_to_cpu(data), 9); + *val = sign_extend32(le16_to_cpu(data), + chan->scan_type.realbits - 1); return IIO_VAL_INT; -- cgit v1.2.3 From 5405c9b4074a93f01977f8b070c4d999aea00754 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:04 -0700 Subject: iio: sca3000: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-5-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/sca3000.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index c6b75308148a..43ecacbdc95a 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -534,6 +534,13 @@ static const struct iio_chan_spec sca3000_channels_with_temp[] = { BIT(IIO_CHAN_INFO_OFFSET), /* No buffer support */ .scan_index = -1, + .scan_type = { + .sign = 'u', + .realbits = 9, + .storagebits = 16, + .shift = 5, + .endianness = IIO_BE, + }, }, { .type = IIO_ACCEL, @@ -730,8 +737,9 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, mutex_unlock(&st->lock); return ret; } - *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF; - *val = sign_extend32(*val, 12); + *val = sign_extend32(be16_to_cpup((__be16 *)st->rx) >> + chan->scan_type.shift, + chan->scan_type.realbits - 1); } else { /* get the temperature when available */ ret = sca3000_read_data_short(st, @@ -741,8 +749,9 @@ static int sca3000_read_raw(struct iio_dev *indio_dev, mutex_unlock(&st->lock); return ret; } - *val = ((st->rx[0] & 0x3F) << 3) | - ((st->rx[1] & 0xE0) >> 5); + *val = (be16_to_cpup((__be16 *)st->rx) >> + chan->scan_type.shift) & + GENMASK(chan->scan_type.realbits - 1, 0); } mutex_unlock(&st->lock); return IIO_VAL_INT; -- cgit v1.2.3 From 571f8d006f39d8159d80f65eeb15603e649f6611 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:05 -0700 Subject: iio: stk8312: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-6-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/stk8312.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 43c621d0f11e..de0cdf8c1f94 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -355,7 +355,7 @@ static int stk8312_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return ret; } - *val = sign_extend32(ret, 7); + *val = sign_extend32(ret, chan->scan_type.realbits - 1); ret = stk8312_set_mode(data, data->mode & (~STK8312_MODE_ACTIVE)); mutex_unlock(&data->lock); -- cgit v1.2.3 From ded408b1135437540d27012da7b6f1afb4f4bf65 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:06 -0700 Subject: iio: stk8ba50: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-7-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/accel/stk8ba50.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index e137a34b5c9a..517c57ed9e94 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -227,7 +227,8 @@ static int stk8ba50_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); return -EINVAL; } - *val = sign_extend32(ret >> STK8BA50_DATA_SHIFT, 9); + *val = sign_extend32(ret >> chan->scan_type.shift, + chan->scan_type.realbits - 1); stk8ba50_set_power(data, STK8BA50_MODE_SUSPEND); mutex_unlock(&data->lock); return IIO_VAL_INT; -- cgit v1.2.3 From 4e9f4c12f1863b890965bfbf81d8d9bc85c12edb Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:07 -0700 Subject: iio: ad7266: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-8-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7266.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index a8ec3efd659e..1d345d66742d 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -159,7 +159,8 @@ static int ad7266_read_raw(struct iio_dev *indio_dev, *val = (*val >> 2) & 0xfff; if (chan->scan_type.sign == 's') - *val = sign_extend32(*val, 11); + *val = sign_extend32(*val, + chan->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: -- cgit v1.2.3 From a5cd0e7f5b3cda94b9f4029b8baef817a7a97226 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:09 -0700 Subject: iio: ti-adc12138: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-10-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc12138.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index fcd5d39dd03e..5b5d45210539 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -239,7 +239,8 @@ static int adc12138_read_raw(struct iio_dev *iio, if (ret) return ret; - *value = sign_extend32(be16_to_cpu(data) >> 3, 12); + *value = sign_extend32(be16_to_cpu(data) >> channel->scan_type.shift, + channel->scan_type.realbits - 1); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: -- cgit v1.2.3 From 4d57fb548a1b086fc216c94cc186fba03c396190 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:10 -0700 Subject: iio: mag3110: Use scan_type when processing raw data Use channel definition as root of trust and replace constant when reading elements directly using the raw sysfs attributes. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-11-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mag3110.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index c96415a1aead..17c62d806218 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -291,7 +291,8 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, if (ret < 0) goto release; *val = sign_extend32( - be16_to_cpu(buffer[chan->scan_index]), 15); + be16_to_cpu(buffer[chan->scan_index]), + chan->scan_type.realbits - 1); ret = IIO_VAL_INT; break; case IIO_TEMP: /* in 1 C / LSB */ @@ -306,7 +307,8 @@ static int mag3110_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); if (ret < 0) goto release; - *val = sign_extend32(ret, 7); + *val = sign_extend32(ret, + chan->scan_type.realbits - 1); ret = IIO_VAL_INT; break; default: -- cgit v1.2.3 From aad54091e1b50d725baa31c11358e6d6dcf44cf0 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:11 -0700 Subject: iio: ti-ads1015: Remove shift variable ads1015_read_raw By using scan_type.realbits when processing raw data, we use scan_type.shit only once, thus we don't need to define a local variable for it anymore. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-12-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index b0352e91ac16..b92d4cd1b823 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -464,9 +464,7 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, mutex_lock(&data->lock); switch (mask) { - case IIO_CHAN_INFO_RAW: { - int shift = chan->scan_type.shift; - + case IIO_CHAN_INFO_RAW: ret = iio_device_claim_direct_mode(indio_dev); if (ret) break; @@ -487,7 +485,8 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, goto release_direct; } - *val = sign_extend32(*val >> shift, 15 - shift); + *val = sign_extend32(*val >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = ads1015_set_power_state(data, false); if (ret < 0) @@ -497,7 +496,6 @@ static int ads1015_read_raw(struct iio_dev *indio_dev, release_direct: iio_device_release_direct_mode(indio_dev); break; - } case IIO_CHAN_INFO_SCALE: idx = data->channel_data[chan->address].pga; *val = ads1015_fullscale_range[idx]; -- cgit v1.2.3 From fb3e8bb47806a3e41d200841518726a9e700e283 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:12 -0700 Subject: iio: xilinx-xadc-core: Use local variable in xadc_read_raw Minor cleanup: bit is already defined as chan->scan_type.realbits, use bit when needed. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-13-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/xilinx-xadc-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 2aa4278ecba7..823c8e5f9809 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -944,7 +944,7 @@ static int xadc_read_raw(struct iio_dev *indio_dev, *val = 1000; break; } - *val2 = chan->scan_type.realbits; + *val2 = bits; return IIO_VAL_FRACTIONAL_LOG2; case IIO_TEMP: /* Temp in C = (val * 503.975) / 2**bits - 273.15 */ -- cgit v1.2.3 From 7721c73d8018ee8a8588ab165a34032bec27de4d Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Thu, 4 Nov 2021 01:24:13 -0700 Subject: iio: mpl3115: Use scan_type.shift and realbit in mpl3115_read_raw When processing raw data using channel scan_type.shift as source of trust to shift data appropriately. When processing the temperature channel, use a 16bit big endian variable as buffer to increase conversion readability. Signed-off-by: Gwendal Grignou Link: https://lore.kernel.org/r/20211104082413.3681212-14-gwendal@chromium.org Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/mpl3115.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c index 1eb9e7b29e05..e95b9a5475b4 100644 --- a/drivers/iio/pressure/mpl3115.c +++ b/drivers/iio/pressure/mpl3115.c @@ -74,7 +74,6 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct mpl3115_data *data = iio_priv(indio_dev); - __be32 tmp = 0; int ret; switch (mask) { @@ -84,7 +83,9 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, return ret; switch (chan->type) { - case IIO_PRESSURE: /* in 0.25 pascal / LSB */ + case IIO_PRESSURE: { /* in 0.25 pascal / LSB */ + __be32 tmp = 0; + mutex_lock(&data->lock); ret = mpl3115_request(data); if (ret < 0) { @@ -96,10 +97,13 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); if (ret < 0) break; - *val = be32_to_cpu(tmp) >> 12; + *val = be32_to_cpu(tmp) >> chan->scan_type.shift; ret = IIO_VAL_INT; break; - case IIO_TEMP: /* in 0.0625 celsius / LSB */ + } + case IIO_TEMP: { /* in 0.0625 celsius / LSB */ + __be16 tmp; + mutex_lock(&data->lock); ret = mpl3115_request(data); if (ret < 0) { @@ -111,9 +115,11 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev, mutex_unlock(&data->lock); if (ret < 0) break; - *val = sign_extend32(be32_to_cpu(tmp) >> 20, 11); + *val = sign_extend32(be16_to_cpu(tmp) >> chan->scan_type.shift, + chan->scan_type.realbits - 1); ret = IIO_VAL_INT; break; + } default: ret = -EINVAL; break; -- cgit v1.2.3 From 471d040defb243e59a2cee42069ca4e8d6d3e94b Mon Sep 17 00:00:00 2001 From: Xu Wang Date: Fri, 5 Nov 2021 01:55:04 +0000 Subject: iio: adc: rzg2l_adc: Remove unnecessary print function dev_err() The print function dev_err() is redundant because platform_get_irq() already prints an error. Signed-off-by: Xu Wang Reviewed-by: Geert Uytterhoeven Reviewed-by: Lad Prabhakar Link: https://lore.kernel.org/r/20211105015504.39226-1-vulab@iscas.ac.cn Signed-off-by: Jonathan Cameron --- drivers/iio/adc/rzg2l_adc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index 32fbf57c362f..9d5be52bd948 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -506,10 +506,8 @@ static int rzg2l_adc_probe(struct platform_device *pdev) } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(dev, "no irq resource\n"); + if (irq < 0) return irq; - } ret = devm_request_irq(dev, irq, rzg2l_adc_isr, 0, dev_name(dev), adc); -- cgit v1.2.3 From 7d71d289e1ba86838bc908d5ce216a208815fd01 Mon Sep 17 00:00:00 2001 From: Maslov Dmitry Date: Sat, 6 Nov 2021 18:41:37 +0100 Subject: iio: light: ltr501: Added ltr303 driver support Previously ltr501 driver supported a number of light and, proximity sensors including ltr501, ltr559 and ltr301. This adds support for another light sensor ltr303 used in Seeed Studio reTerminal, a carrier board for Raspberry Pi 4 CM. Signed-off-by: Maslov Dmitry Link: https://lore.kernel.org/r/20211106174137.6783-1-maslovdmitry@seeed.cc Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index 7e51aaac0bf8..bab5b78f2e30 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor + * Support for Lite-On LTR501 and similar ambient light and proximity sensors. * * Copyright 2014 Peter Meerwald * @@ -98,6 +98,7 @@ enum { ltr501 = 0, ltr559, ltr301, + ltr303, }; struct ltr501_gain { @@ -1231,6 +1232,18 @@ static const struct ltr501_chip_info ltr501_chip_info_tbl[] = { .channels = ltr301_channels, .no_channels = ARRAY_SIZE(ltr301_channels), }, + [ltr303] = { + .partid = 0x0A, + .als_gain = ltr559_als_gain_tbl, + .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), + .als_mode_active = BIT(0), + .als_gain_mask = BIT(2) | BIT(3) | BIT(4), + .als_gain_shift = 2, + .info = <r301_info, + .info_no_irq = <r301_info_no_irq, + .channels = ltr301_channels, + .no_channels = ARRAY_SIZE(ltr301_channels), + }, }; static int ltr501_write_contr(struct ltr501_data *data, u8 als_val, u8 ps_val) @@ -1605,6 +1618,7 @@ static const struct i2c_device_id ltr501_id[] = { { "ltr501", ltr501}, { "ltr559", ltr559}, { "ltr301", ltr301}, + { "ltr303", ltr303}, { } }; MODULE_DEVICE_TABLE(i2c, ltr501_id); @@ -1613,6 +1627,7 @@ static const struct of_device_id ltr501_of_match[] = { { .compatible = "liteon,ltr501", }, { .compatible = "liteon,ltr559", }, { .compatible = "liteon,ltr301", }, + { .compatible = "liteon,ltr303", }, {} }; MODULE_DEVICE_TABLE(of, ltr501_of_match); -- cgit v1.2.3 From 66bd1333abd7fa191f13b929c9119d6cd3df27b0 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 17 Nov 2021 16:42:20 +0000 Subject: Documentation: coresight: Fix documentation issue Fix the description of the directories and attributes used in cs_etm as used by perf. Drop the references to the 'configurations' sub-directory which had been removed in an earlier version of the patchset. Fixes: f71cd93d5ea4 ("Documentation: coresight: Add documentation for CoreSight config") Reported-by: German Gomex Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211117164220.14883-1-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- Documentation/trace/coresight/coresight-config.rst | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/Documentation/trace/coresight/coresight-config.rst b/Documentation/trace/coresight/coresight-config.rst index a4e3ef295240..6ed13398ca2c 100644 --- a/Documentation/trace/coresight/coresight-config.rst +++ b/Documentation/trace/coresight/coresight-config.rst @@ -211,19 +211,13 @@ also declared in the perf 'cs_etm' event infrastructure so that they can be selected when running trace under perf:: $ ls /sys/devices/cs_etm - configurations format perf_event_mux_interval_ms sinks type - events nr_addr_filters power + cpu0 cpu2 events nr_addr_filters power subsystem uevent + cpu1 cpu3 format perf_event_mux_interval_ms sinks type -Key directories here are 'configurations' - which lists the loaded -configurations, and 'events' - a generic perf directory which allows -selection on the perf command line.:: +The key directory here is 'events' - a generic perf directory which allows +selection on the perf command line. As with the sinks entries, this provides +a hash of the configuration name. - $ ls configurations/ - autofdo - $ cat configurations/autofdo - 0xa7c3dddd - -As with the sinks entries, this provides a hash of the configuration name. The entry in the 'events' directory uses perfs built in syntax generator to substitute the syntax for the name when evaluating the command:: -- cgit v1.2.3 From e5cc9840f08be46c701d88b81f06d37db516fe32 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 13 Oct 2021 12:49:23 +0300 Subject: iio: buffer: Use dedicated variable in iio_buffers_alloc_sysfs_and_mask() Use dedicated variable for index in the loop in the iio_buffers_alloc_sysfs_and_mask(). This will make code cleaner and less error prone as proved by previous changes done in this function. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211013094923.2473-3-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index e180728914c0..94eb9f6cf128 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1727,8 +1727,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); const struct iio_chan_spec *channels; struct iio_buffer *buffer; - int unwind_idx; - int ret, i; + int ret, i, idx; size_t sz; channels = indio_dev->channels; @@ -1743,15 +1742,12 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) if (!iio_dev_opaque->attached_buffers_cnt) return 0; - for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { - buffer = iio_dev_opaque->attached_buffers[i]; - ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i); - if (ret) { - unwind_idx = i - 1; + for (idx = 0; idx < iio_dev_opaque->attached_buffers_cnt; idx++) { + buffer = iio_dev_opaque->attached_buffers[idx]; + ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, idx); + if (ret) goto error_unwind_sysfs_and_mask; - } } - unwind_idx = iio_dev_opaque->attached_buffers_cnt - 1; sz = sizeof(*(iio_dev_opaque->buffer_ioctl_handler)); iio_dev_opaque->buffer_ioctl_handler = kzalloc(sz, GFP_KERNEL); @@ -1767,9 +1763,9 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) return 0; error_unwind_sysfs_and_mask: - for (; unwind_idx >= 0; unwind_idx--) { - buffer = iio_dev_opaque->attached_buffers[unwind_idx]; - __iio_buffer_free_sysfs_and_mask(buffer, indio_dev, unwind_idx); + while (idx--) { + buffer = iio_dev_opaque->attached_buffers[idx]; + __iio_buffer_free_sysfs_and_mask(buffer, indio_dev, idx); } return ret; } -- cgit v1.2.3 From 3b47746cd787d7130dda700e96a4503f7f315cbd Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Thu, 21 Oct 2021 16:10:55 +0530 Subject: dt-bindings: interconnect: Add EPSS L3 DT binding on SC7280 Add Epoch Subsystem (EPSS) L3 interconnect provider binding on SC7280 SoCs. Signed-off-by: Odelu Kukatla Acked-by: Rob Herring Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/1634812857-10676-2-git-send-email-okukatla@codeaurora.org Signed-off-by: Georgi Djakov --- Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml index e701524ee811..116e434d0daa 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,osm-l3.yaml @@ -18,6 +18,7 @@ properties: compatible: enum: - qcom,sc7180-osm-l3 + - qcom,sc7280-epss-l3 - qcom,sc8180x-osm-l3 - qcom,sdm845-osm-l3 - qcom,sm8150-osm-l3 -- cgit v1.2.3 From 6a61d1d1491eea268c07c5a623b08d6d1d7ec237 Mon Sep 17 00:00:00 2001 From: Odelu Kukatla Date: Thu, 21 Oct 2021 16:10:56 +0530 Subject: interconnect: qcom: Add EPSS L3 support on SC7280 Add Epoch Subsystem (EPSS) L3 interconnect provider support on SC7280 SoCs. Signed-off-by: Odelu Kukatla Reviewed-by: Stephen Boyd Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/1634812857-10676-3-git-send-email-okukatla@codeaurora.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/osm-l3.c | 20 +++++++++++++++++++- drivers/interconnect/qcom/sc7280.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/osm-l3.c b/drivers/interconnect/qcom/osm-l3.c index c7af143980de..eec13099a6a3 100644 --- a/drivers/interconnect/qcom/osm-l3.c +++ b/drivers/interconnect/qcom/osm-l3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. */ #include @@ -15,6 +15,7 @@ #include #include "sc7180.h" +#include "sc7280.h" #include "sc8180x.h" #include "sdm845.h" #include "sm8150.h" @@ -114,6 +115,22 @@ static const struct qcom_osm_l3_desc sc7180_icc_osm_l3 = { .reg_perf_state = OSM_REG_PERF_STATE, }; +DEFINE_QNODE(sc7280_epss_apps_l3, SC7280_MASTER_EPSS_L3_APPS, 32, SC7280_SLAVE_EPSS_L3); +DEFINE_QNODE(sc7280_epss_l3, SC7280_SLAVE_EPSS_L3, 32); + +static const struct qcom_osm_l3_node *sc7280_epss_l3_nodes[] = { + [MASTER_EPSS_L3_APPS] = &sc7280_epss_apps_l3, + [SLAVE_EPSS_L3_SHARED] = &sc7280_epss_l3, +}; + +static const struct qcom_osm_l3_desc sc7280_icc_epss_l3 = { + .nodes = sc7280_epss_l3_nodes, + .num_nodes = ARRAY_SIZE(sc7280_epss_l3_nodes), + .lut_row_size = EPSS_LUT_ROW_SIZE, + .reg_freq_lut = EPSS_REG_FREQ_LUT, + .reg_perf_state = EPSS_REG_PERF_STATE, +}; + DEFINE_QNODE(sc8180x_osm_apps_l3, SC8180X_MASTER_OSM_L3_APPS, 32, SC8180X_SLAVE_OSM_L3); DEFINE_QNODE(sc8180x_osm_l3, SC8180X_SLAVE_OSM_L3, 32); @@ -326,6 +343,7 @@ err: static const struct of_device_id osm_l3_of_match[] = { { .compatible = "qcom,sc7180-osm-l3", .data = &sc7180_icc_osm_l3 }, + { .compatible = "qcom,sc7280-epss-l3", .data = &sc7280_icc_epss_l3 }, { .compatible = "qcom,sdm845-osm-l3", .data = &sdm845_icc_osm_l3 }, { .compatible = "qcom,sm8150-osm-l3", .data = &sm8150_icc_osm_l3 }, { .compatible = "qcom,sc8180x-osm-l3", .data = &sc8180x_icc_osm_l3 }, diff --git a/drivers/interconnect/qcom/sc7280.h b/drivers/interconnect/qcom/sc7280.h index 175e400305c5..1fb9839b2c14 100644 --- a/drivers/interconnect/qcom/sc7280.h +++ b/drivers/interconnect/qcom/sc7280.h @@ -150,5 +150,7 @@ #define SC7280_SLAVE_PCIE_1 139 #define SC7280_SLAVE_QDSS_STM 140 #define SC7280_SLAVE_TCU 141 +#define SC7280_MASTER_EPSS_L3_APPS 142 +#define SC7280_SLAVE_EPSS_L3 143 #endif -- cgit v1.2.3 From 45c548cc5baa047e59865bec5dfa0bd36b48ff17 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 21 Oct 2021 13:24:25 +0000 Subject: dt-bindings: interconnect: Combine SDM660 bindings into RPM schema SDM660 interconnect bindings are similar to other RPM interconnect providers, and now it shares the same common driver with them, so it is better to combine them into qcom,rpm.yaml. Signed-off-by: Yassine Oudjana Reviewed-by: Rob Herring Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov #db820c Link: https://lore.kernel.org/r/20211021132329.234942-2-y.oudjana@protonmail.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,rpm.yaml | 103 +++++++++++- .../bindings/interconnect/qcom,sdm660.yaml | 185 --------------------- 2 files changed, 95 insertions(+), 193 deletions(-) delete mode 100644 Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml index 983d71fb5399..6c39c0529e36 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml @@ -30,19 +30,23 @@ properties: - qcom,qcs404-bimc - qcom,qcs404-pcnoc - qcom,qcs404-snoc + - qcom,sdm660-a2noc + - qcom,sdm660-bimc + - qcom,sdm660-cnoc + - qcom,sdm660-gnoc + - qcom,sdm660-mnoc + - qcom,sdm660-snoc '#interconnect-cells': const: 1 - clock-names: - items: - - const: bus - - const: bus_a - clocks: - items: - - description: Bus Clock - - description: Bus A Clock + minItems: 2 + maxItems: 7 + + clock-names: + minItems: 2 + maxItems: 7 required: - compatible @@ -53,6 +57,89 @@ required: additionalProperties: false +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8916-bimc + - qcom,msm8916-pcnoc + - qcom,msm8916-snoc + - qcom,msm8939-bimc + - qcom,msm8939-pcnoc + - qcom,msm8939-snoc + - qcom,msm8939-snoc-mm + - qcom,qcs404-bimc + - qcom,qcs404-pcnoc + - qcom,qcs404-snoc + - qcom,sdm660-bimc + - qcom,sdm660-cnoc + - qcom,sdm660-gnoc + - qcom,sdm660-snoc + + then: + properties: + clock-names: + items: + - const: bus + - const: bus_a + + clocks: + items: + - description: Bus Clock + - description: Bus A Clock + + - if: + properties: + compatible: + contains: + enum: + - qcom,sdm660-mnoc + + then: + properties: + clock-names: + items: + - const: bus + - const: bus_a + - const: iface + + clocks: + items: + - description: Bus Clock. + - description: Bus A Clock. + - description: CPU-NoC High-performance Bus Clock. + + - if: + properties: + compatible: + contains: + enum: + - qcom,sdm660-a2noc + + then: + properties: + clock-names: + items: + - const: bus + - const: bus_a + - const: ipa + - const: ufs_axi + - const: aggre2_ufs_axi + - const: aggre2_usb3_axi + - const: cfg_noc_usb2_axi + + clocks: + items: + - description: Bus Clock. + - description: Bus A Clock. + - description: IPA Clock. + - description: UFS AXI Clock. + - description: Aggregate2 UFS AXI Clock. + - description: Aggregate2 USB3 AXI Clock. + - description: Config NoC USB2 AXI Clock. + examples: - | #include diff --git a/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml b/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml deleted file mode 100644 index bcd41e491f1d..000000000000 --- a/Documentation/devicetree/bindings/interconnect/qcom,sdm660.yaml +++ /dev/null @@ -1,185 +0,0 @@ -# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) -%YAML 1.2 ---- -$id: http://devicetree.org/schemas/interconnect/qcom,sdm660.yaml# -$schema: http://devicetree.org/meta-schemas/core.yaml# - -title: Qualcomm SDM660 Network-On-Chip interconnect - -maintainers: - - AngeloGioacchino Del Regno - -description: | - The Qualcomm SDM660 interconnect providers support adjusting the - bandwidth requirements between the various NoC fabrics. - -properties: - reg: - maxItems: 1 - - compatible: - enum: - - qcom,sdm660-a2noc - - qcom,sdm660-bimc - - qcom,sdm660-cnoc - - qcom,sdm660-gnoc - - qcom,sdm660-mnoc - - qcom,sdm660-snoc - - '#interconnect-cells': - const: 1 - - clocks: - minItems: 1 - maxItems: 7 - - clock-names: - minItems: 1 - maxItems: 7 - -required: - - compatible - - reg - - '#interconnect-cells' - - clock-names - - clocks - -additionalProperties: false - -allOf: - - if: - properties: - compatible: - contains: - enum: - - qcom,sdm660-mnoc - then: - properties: - clocks: - items: - - description: Bus Clock. - - description: Bus A Clock. - - description: CPU-NoC High-performance Bus Clock. - clock-names: - items: - - const: bus - - const: bus_a - - const: iface - - - if: - properties: - compatible: - contains: - enum: - - qcom,sdm660-a2noc - then: - properties: - clocks: - items: - - description: Bus Clock. - - description: Bus A Clock. - - description: IPA Clock. - - description: UFS AXI Clock. - - description: Aggregate2 UFS AXI Clock. - - description: Aggregate2 USB3 AXI Clock. - - description: Config NoC USB2 AXI Clock. - clock-names: - items: - - const: bus - - const: bus_a - - const: ipa - - const: ufs_axi - - const: aggre2_ufs_axi - - const: aggre2_usb3_axi - - const: cfg_noc_usb2_axi - - - if: - properties: - compatible: - contains: - enum: - - qcom,sdm660-bimc - - qcom,sdm660-cnoc - - qcom,sdm660-gnoc - - qcom,sdm660-snoc - then: - properties: - clocks: - items: - - description: Bus Clock. - - description: Bus A Clock. - clock-names: - items: - - const: bus - - const: bus_a - -examples: - - | - #include - #include - #include - - bimc: interconnect@1008000 { - compatible = "qcom,sdm660-bimc"; - reg = <0x01008000 0x78000>; - #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_BIMC_CLK>, - <&rpmcc RPM_SMD_BIMC_A_CLK>; - }; - - cnoc: interconnect@1500000 { - compatible = "qcom,sdm660-cnoc"; - reg = <0x01500000 0x10000>; - #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_CNOC_CLK>, - <&rpmcc RPM_SMD_CNOC_A_CLK>; - }; - - snoc: interconnect@1626000 { - compatible = "qcom,sdm660-snoc"; - reg = <0x01626000 0x7090>; - #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&rpmcc RPM_SMD_SNOC_CLK>, - <&rpmcc RPM_SMD_SNOC_A_CLK>; - }; - - a2noc: interconnect@1704000 { - compatible = "qcom,sdm660-a2noc"; - reg = <0x01704000 0xc100>; - #interconnect-cells = <1>; - clock-names = "bus", - "bus_a", - "ipa", - "ufs_axi", - "aggre2_ufs_axi", - "aggre2_usb3_axi", - "cfg_noc_usb2_axi"; - clocks = <&rpmcc RPM_SMD_AGGR2_NOC_CLK>, - <&rpmcc RPM_SMD_AGGR2_NOC_A_CLK>, - <&rpmcc RPM_SMD_IPA_CLK>, - <&gcc GCC_UFS_AXI_CLK>, - <&gcc GCC_AGGRE2_UFS_AXI_CLK>, - <&gcc GCC_AGGRE2_USB3_AXI_CLK>, - <&gcc GCC_CFG_NOC_USB2_AXI_CLK>; - }; - - mnoc: interconnect@1745000 { - compatible = "qcom,sdm660-mnoc"; - reg = <0x01745000 0xa010>; - #interconnect-cells = <1>; - clock-names = "bus", "bus_a", "iface"; - clocks = <&rpmcc RPM_SMD_MMSSNOC_AXI_CLK>, - <&rpmcc RPM_SMD_MMSSNOC_AXI_CLK_A>, - <&mmcc AHB_CLK_SRC>; - }; - - gnoc: interconnect@17900000 { - compatible = "qcom,sdm660-gnoc"; - reg = <0x17900000 0xe000>; - #interconnect-cells = <1>; - clock-names = "bus", "bus_a"; - clocks = <&xo_board>, <&xo_board>; - }; -- cgit v1.2.3 From 7de109c0abe9bb3f03b3500f3e1840b06c5fd853 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 21 Oct 2021 13:24:42 +0000 Subject: interconnect: icc-rpm: Add support for bus power domain Add support for attaching to a power domain. This is required for Aggregate 0 NoC on MSM8996, which is powered by a GDSC. Signed-off-by: Yassine Oudjana Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov #db820c Link: https://lore.kernel.org/r/20211021132329.234942-3-y.oudjana@protonmail.com Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 7 +++++++ drivers/interconnect/qcom/icc-rpm.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index ef7999a08c8b..6b918d082ab6 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -340,6 +341,12 @@ int qnoc_probe(struct platform_device *pdev) if (ret) return ret; + if (desc->has_bus_pd) { + ret = dev_pm_domain_attach(dev, true); + if (ret) + return ret; + } + provider = &qp->provider; INIT_LIST_HEAD(&provider->nodes); provider->dev = dev; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index f5744de4da19..fd06a3b9e3f7 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -77,6 +77,7 @@ struct qcom_icc_desc { size_t num_nodes; const char * const *clocks; size_t num_clocks; + bool has_bus_pd; bool is_bimc_node; const struct regmap_config *regmap_cfg; unsigned int qos_offset; -- cgit v1.2.3 From 3e9fdc6b73ca862e72ea8a563638cecdc11d26e2 Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 21 Oct 2021 13:24:54 +0000 Subject: dt-bindings: interconnect: Add Qualcomm MSM8996 DT bindings Add bindings for interconnects on Qualcomm MSM8996. Signed-off-by: Yassine Oudjana Reviewed-by: Rob Herring Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov #db820c Link: https://lore.kernel.org/r/20211021132329.234942-4-y.oudjana@protonmail.com Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/qcom,rpm.yaml | 42 ++++++ include/dt-bindings/interconnect/qcom,msm8996.h | 163 +++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 include/dt-bindings/interconnect/qcom,msm8996.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml index 6c39c0529e36..e4c3c2818119 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,rpm.yaml @@ -27,6 +27,14 @@ properties: - qcom,msm8939-pcnoc - qcom,msm8939-snoc - qcom,msm8939-snoc-mm + - qcom,msm8996-a0noc + - qcom,msm8996-a1noc + - qcom,msm8996-a2noc + - qcom,msm8996-bimc + - qcom,msm8996-cnoc + - qcom,msm8996-mnoc + - qcom,msm8996-pnoc + - qcom,msm8996-snoc - qcom,qcs404-bimc - qcom,qcs404-pcnoc - qcom,qcs404-snoc @@ -48,6 +56,9 @@ properties: minItems: 2 maxItems: 7 + power-domains: + maxItems: 1 + required: - compatible - reg @@ -70,6 +81,12 @@ allOf: - qcom,msm8939-pcnoc - qcom,msm8939-snoc - qcom,msm8939-snoc-mm + - qcom,msm8996-a1noc + - qcom,msm8996-a2noc + - qcom,msm8996-bimc + - qcom,msm8996-cnoc + - qcom,msm8996-pnoc + - qcom,msm8996-snoc - qcom,qcs404-bimc - qcom,qcs404-pcnoc - qcom,qcs404-snoc @@ -95,6 +112,7 @@ allOf: compatible: contains: enum: + - qcom,msm8996-mnoc - qcom,sdm660-mnoc then: @@ -111,6 +129,30 @@ allOf: - description: Bus A Clock. - description: CPU-NoC High-performance Bus Clock. + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8996-a0noc + + then: + properties: + clock-names: + items: + - const: aggre0_snoc_axi + - const: aggre0_cnoc_ahb + - const: aggre0_noc_mpu_cfg + + clocks: + items: + - description: Aggregate0 System NoC AXI Clock. + - description: Aggregate0 Config NoC AHB Clock. + - description: Aggregate0 NoC MPU Clock. + + required: + - power-domains + - if: properties: compatible: diff --git a/include/dt-bindings/interconnect/qcom,msm8996.h b/include/dt-bindings/interconnect/qcom,msm8996.h new file mode 100644 index 000000000000..a0b7c0ec7bed --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8996.h @@ -0,0 +1,163 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-3-Clause) */ +/* + * Qualcomm MSM8996 interconnect IDs + * + * Copyright (c) 2021 Yassine Oudjana + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8996_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8996_H + +/* A0NOC */ +#define MASTER_PCIE_0 0 +#define MASTER_PCIE_1 1 +#define MASTER_PCIE_2 2 + +/* A1NOC */ +#define MASTER_CNOC_A1NOC 0 +#define MASTER_CRYPTO_CORE0 1 +#define MASTER_PNOC_A1NOC 2 + +/* A2NOC */ +#define MASTER_USB3 0 +#define MASTER_IPA 1 +#define MASTER_UFS 2 + +/* BIMC */ +#define MASTER_AMPSS_M0 0 +#define MASTER_GRAPHICS_3D 1 +#define MASTER_MNOC_BIMC 2 +#define MASTER_SNOC_BIMC 3 +#define SLAVE_EBI_CH0 4 +#define SLAVE_HMSS_L3 5 +#define SLAVE_BIMC_SNOC_0 6 +#define SLAVE_BIMC_SNOC_1 7 + +/* CNOC */ +#define MASTER_SNOC_CNOC 0 +#define MASTER_QDSS_DAP 1 +#define SLAVE_CNOC_A1NOC 2 +#define SLAVE_CLK_CTL 3 +#define SLAVE_TCSR 4 +#define SLAVE_TLMM 5 +#define SLAVE_CRYPTO_0_CFG 6 +#define SLAVE_MPM 7 +#define SLAVE_PIMEM_CFG 8 +#define SLAVE_IMEM_CFG 9 +#define SLAVE_MESSAGE_RAM 10 +#define SLAVE_BIMC_CFG 11 +#define SLAVE_PMIC_ARB 12 +#define SLAVE_PRNG 13 +#define SLAVE_DCC_CFG 14 +#define SLAVE_RBCPR_MX 15 +#define SLAVE_QDSS_CFG 16 +#define SLAVE_RBCPR_CX 17 +#define SLAVE_QDSS_RBCPR_APU 18 +#define SLAVE_CNOC_MNOC_CFG 19 +#define SLAVE_SNOC_CFG 20 +#define SLAVE_SNOC_MPU_CFG 21 +#define SLAVE_EBI1_PHY_CFG 22 +#define SLAVE_A0NOC_CFG 23 +#define SLAVE_PCIE_1_CFG 24 +#define SLAVE_PCIE_2_CFG 25 +#define SLAVE_PCIE_0_CFG 26 +#define SLAVE_PCIE20_AHB2PHY 27 +#define SLAVE_A0NOC_MPU_CFG 28 +#define SLAVE_UFS_CFG 29 +#define SLAVE_A1NOC_CFG 30 +#define SLAVE_A1NOC_MPU_CFG 31 +#define SLAVE_A2NOC_CFG 32 +#define SLAVE_A2NOC_MPU_CFG 33 +#define SLAVE_SSC_CFG 34 +#define SLAVE_A0NOC_SMMU_CFG 35 +#define SLAVE_A1NOC_SMMU_CFG 36 +#define SLAVE_A2NOC_SMMU_CFG 37 +#define SLAVE_LPASS_SMMU_CFG 38 +#define SLAVE_CNOC_MNOC_MMSS_CFG 39 + +/* MNOC */ +#define MASTER_CNOC_MNOC_CFG 0 +#define MASTER_CPP 1 +#define MASTER_JPEG 2 +#define MASTER_MDP_PORT0 3 +#define MASTER_MDP_PORT1 4 +#define MASTER_ROTATOR 5 +#define MASTER_VIDEO_P0 6 +#define MASTER_VFE 7 +#define MASTER_SNOC_VMEM 8 +#define MASTER_VIDEO_P0_OCMEM 9 +#define MASTER_CNOC_MNOC_MMSS_CFG 10 +#define SLAVE_MNOC_BIMC 11 +#define SLAVE_VMEM 12 +#define SLAVE_SERVICE_MNOC 13 +#define SLAVE_MMAGIC_CFG 14 +#define SLAVE_CPR_CFG 15 +#define SLAVE_MISC_CFG 16 +#define SLAVE_VENUS_THROTTLE_CFG 17 +#define SLAVE_VENUS_CFG 18 +#define SLAVE_VMEM_CFG 19 +#define SLAVE_DSA_CFG 20 +#define SLAVE_MMSS_CLK_CFG 21 +#define SLAVE_DSA_MPU_CFG 22 +#define SLAVE_MNOC_MPU_CFG 23 +#define SLAVE_DISPLAY_CFG 24 +#define SLAVE_DISPLAY_THROTTLE_CFG 25 +#define SLAVE_CAMERA_CFG 26 +#define SLAVE_CAMERA_THROTTLE_CFG 27 +#define SLAVE_GRAPHICS_3D_CFG 28 +#define SLAVE_SMMU_MDP_CFG 29 +#define SLAVE_SMMU_ROT_CFG 30 +#define SLAVE_SMMU_VENUS_CFG 31 +#define SLAVE_SMMU_CPP_CFG 32 +#define SLAVE_SMMU_JPEG_CFG 33 +#define SLAVE_SMMU_VFE_CFG 34 + +/* PNOC */ +#define MASTER_SNOC_PNOC 0 +#define MASTER_SDCC_1 1 +#define MASTER_SDCC_2 2 +#define MASTER_SDCC_4 3 +#define MASTER_USB_HS 4 +#define MASTER_BLSP_1 5 +#define MASTER_BLSP_2 6 +#define MASTER_TSIF 7 +#define SLAVE_PNOC_A1NOC 8 +#define SLAVE_USB_HS 9 +#define SLAVE_SDCC_2 10 +#define SLAVE_SDCC_4 11 +#define SLAVE_TSIF 12 +#define SLAVE_BLSP_2 13 +#define SLAVE_SDCC_1 14 +#define SLAVE_BLSP_1 15 +#define SLAVE_PDM 16 +#define SLAVE_AHB2PHY 17 + +/* SNOC */ +#define MASTER_HMSS 0 +#define MASTER_QDSS_BAM 1 +#define MASTER_SNOC_CFG 2 +#define MASTER_BIMC_SNOC_0 3 +#define MASTER_BIMC_SNOC_1 4 +#define MASTER_A0NOC_SNOC 5 +#define MASTER_A1NOC_SNOC 6 +#define MASTER_A2NOC_SNOC 7 +#define MASTER_QDSS_ETR 8 +#define SLAVE_A0NOC_SNOC 9 +#define SLAVE_A1NOC_SNOC 10 +#define SLAVE_A2NOC_SNOC 11 +#define SLAVE_HMSS 12 +#define SLAVE_LPASS 13 +#define SLAVE_USB3 14 +#define SLAVE_SNOC_BIMC 15 +#define SLAVE_SNOC_CNOC 16 +#define SLAVE_IMEM 17 +#define SLAVE_PIMEM 18 +#define SLAVE_SNOC_VMEM 19 +#define SLAVE_SNOC_PNOC 20 +#define SLAVE_QDSS_STM 21 +#define SLAVE_PCIE_0 22 +#define SLAVE_PCIE_1 23 +#define SLAVE_PCIE_2 24 +#define SLAVE_SERVICE_SNOC 25 + +#endif -- cgit v1.2.3 From 7add937f5222fe9a04a2ca3c43a322985219711f Mon Sep 17 00:00:00 2001 From: Yassine Oudjana Date: Thu, 21 Oct 2021 13:25:07 +0000 Subject: interconnect: qcom: Add MSM8996 interconnect provider driver Add a driver for the MSM8996 NoCs. This chip is similar to SDM660 where some busses are controlled by RPM, while others directly by the AP with writes to QoS registers. Signed-off-by: Yassine Oudjana Link: https://lore.kernel.org/r/20211021132329.234942-5-y.oudjana@protonmail.com Reviewed-by: Dmitry Baryshkov Tested-by: Dmitry Baryshkov #db820c Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/msm8996.c | 2103 +++++++++++++++++++++++++++++++++++ drivers/interconnect/qcom/msm8996.h | 149 +++ 4 files changed, 2263 insertions(+) create mode 100644 drivers/interconnect/qcom/msm8996.c create mode 100644 drivers/interconnect/qcom/msm8996.h diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index daf1e25f6042..d0ed6f570355 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -35,6 +35,15 @@ config INTERCONNECT_QCOM_MSM8974 This is a driver for the Qualcomm Network-on-Chip on msm8974-based platforms. +config INTERCONNECT_QCOM_MSM8996 + tristate "Qualcomm MSM8996 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on msm8996-based + platforms. + config INTERCONNECT_QCOM_OSM_L3 tristate "Qualcomm OSM L3 interconnect driver" depends on INTERCONNECT_QCOM || COMPILE_TEST diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 69300b1d48ef..750e42ab82ac 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -4,6 +4,7 @@ icc-bcm-voter-objs := bcm-voter.o qnoc-msm8916-objs := msm8916.o qnoc-msm8939-objs := msm8939.o qnoc-msm8974-objs := msm8974.o +qnoc-msm8996-objs := msm8996.o icc-osm-l3-objs := osm-l3.o qnoc-qcs404-objs := qcs404.o icc-rpmh-obj := icc-rpmh.o @@ -22,6 +23,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8916) += qnoc-msm8916.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o +obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c new file mode 100644 index 000000000000..d8248ebdf6b3 --- /dev/null +++ b/drivers/interconnect/qcom/msm8996.c @@ -0,0 +1,2103 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Qualcomm MSM8996 Network-on-Chip (NoC) QoS driver + * + * Copyright (c) 2021 Yassine Oudjana + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "icc-rpm.h" +#include "smd-rpm.h" +#include "msm8996.h" + +static const char * const bus_mm_clocks[] = { + "bus", + "bus_a", + "iface" +}; + +static const char * const bus_a0noc_clocks[] = { + "aggre0_snoc_axi", + "aggre0_cnoc_ahb", + "aggre0_noc_mpu_cfg" +}; + +static const u16 mas_a0noc_common_links[] = { + MSM8996_SLAVE_A0NOC_SNOC +}; + +static struct qcom_icc_node mas_pcie_0 = { + .name = "mas_pcie_0", + .id = MSM8996_MASTER_PCIE_0, + .buswidth = 8, + .mas_rpm_id = 65, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_a0noc_common_links), + .links = mas_a0noc_common_links +}; + +static struct qcom_icc_node mas_pcie_1 = { + .name = "mas_pcie_1", + .id = MSM8996_MASTER_PCIE_1, + .buswidth = 8, + .mas_rpm_id = 66, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_a0noc_common_links), + .links = mas_a0noc_common_links +}; + +static struct qcom_icc_node mas_pcie_2 = { + .name = "mas_pcie_2", + .id = MSM8996_MASTER_PCIE_2, + .buswidth = 8, + .mas_rpm_id = 119, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_a0noc_common_links), + .links = mas_a0noc_common_links +}; + +static const u16 mas_a1noc_common_links[] = { + MSM8996_SLAVE_A1NOC_SNOC +}; + +static struct qcom_icc_node mas_cnoc_a1noc = { + .name = "mas_cnoc_a1noc", + .id = MSM8996_MASTER_CNOC_A1NOC, + .buswidth = 8, + .mas_rpm_id = 116, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_a1noc_common_links), + .links = mas_a1noc_common_links +}; + +static struct qcom_icc_node mas_crypto_c0 = { + .name = "mas_crypto_c0", + .id = MSM8996_MASTER_CRYPTO_CORE0, + .buswidth = 8, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_a1noc_common_links), + .links = mas_a1noc_common_links +}; + +static struct qcom_icc_node mas_pnoc_a1noc = { + .name = "mas_pnoc_a1noc", + .id = MSM8996_MASTER_PNOC_A1NOC, + .buswidth = 8, + .mas_rpm_id = 117, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_a1noc_common_links), + .links = mas_a1noc_common_links +}; + +static const u16 mas_a2noc_common_links[] = { + MSM8996_SLAVE_A2NOC_SNOC +}; + +static struct qcom_icc_node mas_usb3 = { + .name = "mas_usb3", + .id = MSM8996_MASTER_USB3, + .buswidth = 8, + .mas_rpm_id = 32, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 3, + .num_links = ARRAY_SIZE(mas_a2noc_common_links), + .links = mas_a2noc_common_links +}; + +static struct qcom_icc_node mas_ipa = { + .name = "mas_ipa", + .id = MSM8996_MASTER_IPA, + .buswidth = 8, + .mas_rpm_id = 59, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_a2noc_common_links), + .links = mas_a2noc_common_links +}; + +static struct qcom_icc_node mas_ufs = { + .name = "mas_ufs", + .id = MSM8996_MASTER_UFS, + .buswidth = 8, + .mas_rpm_id = 68, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_a2noc_common_links), + .links = mas_a2noc_common_links +}; + +static const u16 mas_apps_proc_links[] = { + MSM8996_SLAVE_BIMC_SNOC_1, + MSM8996_SLAVE_EBI_CH0, + MSM8996_SLAVE_BIMC_SNOC_0 +}; + +static struct qcom_icc_node mas_apps_proc = { + .name = "mas_apps_proc", + .id = MSM8996_MASTER_AMPSS_M0, + .buswidth = 8, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_apps_proc_links), + .links = mas_apps_proc_links +}; + +static const u16 mas_oxili_common_links[] = { + MSM8996_SLAVE_BIMC_SNOC_1, + MSM8996_SLAVE_HMSS_L3, + MSM8996_SLAVE_EBI_CH0, + MSM8996_SLAVE_BIMC_SNOC_0 +}; + +static struct qcom_icc_node mas_oxili = { + .name = "mas_oxili", + .id = MSM8996_MASTER_GRAPHICS_3D, + .buswidth = 8, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_oxili_common_links), + .links = mas_oxili_common_links +}; + +static struct qcom_icc_node mas_mnoc_bimc = { + .name = "mas_mnoc_bimc", + .id = MSM8996_MASTER_MNOC_BIMC, + .buswidth = 8, + .mas_rpm_id = 2, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_oxili_common_links), + .links = mas_oxili_common_links +}; + +static const u16 mas_snoc_bimc_links[] = { + MSM8996_SLAVE_HMSS_L3, + MSM8996_SLAVE_EBI_CH0 +}; + +static struct qcom_icc_node mas_snoc_bimc = { + .name = "mas_snoc_bimc", + .id = MSM8996_MASTER_SNOC_BIMC, + .buswidth = 8, + .mas_rpm_id = 3, + .slv_rpm_id = -1, + .qos.ap_owned = false, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = -1, + .num_links = ARRAY_SIZE(mas_snoc_bimc_links), + .links = mas_snoc_bimc_links +}; + +static const u16 mas_snoc_cnoc_links[] = { + MSM8996_SLAVE_CLK_CTL, + MSM8996_SLAVE_RBCPR_CX, + MSM8996_SLAVE_A2NOC_SMMU_CFG, + MSM8996_SLAVE_A0NOC_MPU_CFG, + MSM8996_SLAVE_MESSAGE_RAM, + MSM8996_SLAVE_CNOC_MNOC_MMSS_CFG, + MSM8996_SLAVE_PCIE_0_CFG, + MSM8996_SLAVE_TLMM, + MSM8996_SLAVE_MPM, + MSM8996_SLAVE_A0NOC_SMMU_CFG, + MSM8996_SLAVE_EBI1_PHY_CFG, + MSM8996_SLAVE_BIMC_CFG, + MSM8996_SLAVE_PIMEM_CFG, + MSM8996_SLAVE_RBCPR_MX, + MSM8996_SLAVE_PRNG, + MSM8996_SLAVE_PCIE20_AHB2PHY, + MSM8996_SLAVE_A2NOC_MPU_CFG, + MSM8996_SLAVE_QDSS_CFG, + MSM8996_SLAVE_A2NOC_CFG, + MSM8996_SLAVE_A0NOC_CFG, + MSM8996_SLAVE_UFS_CFG, + MSM8996_SLAVE_CRYPTO_0_CFG, + MSM8996_SLAVE_PCIE_1_CFG, + MSM8996_SLAVE_SNOC_CFG, + MSM8996_SLAVE_SNOC_MPU_CFG, + MSM8996_SLAVE_A1NOC_MPU_CFG, + MSM8996_SLAVE_A1NOC_SMMU_CFG, + MSM8996_SLAVE_PCIE_2_CFG, + MSM8996_SLAVE_CNOC_MNOC_CFG, + MSM8996_SLAVE_QDSS_RBCPR_APU_CFG, + MSM8996_SLAVE_PMIC_ARB, + MSM8996_SLAVE_IMEM_CFG, + MSM8996_SLAVE_A1NOC_CFG, + MSM8996_SLAVE_SSC_CFG, + MSM8996_SLAVE_TCSR, + MSM8996_SLAVE_LPASS_SMMU_CFG, + MSM8996_SLAVE_DCC_CFG +}; + +static struct qcom_icc_node mas_snoc_cnoc = { + .name = "mas_snoc_cnoc", + .id = MSM8996_MASTER_SNOC_CNOC, + .buswidth = 8, + .mas_rpm_id = 52, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cnoc_links), + .links = mas_snoc_cnoc_links +}; + +static const u16 mas_qdss_dap_links[] = { + MSM8996_SLAVE_QDSS_RBCPR_APU_CFG, + MSM8996_SLAVE_RBCPR_CX, + MSM8996_SLAVE_A2NOC_SMMU_CFG, + MSM8996_SLAVE_A0NOC_MPU_CFG, + MSM8996_SLAVE_MESSAGE_RAM, + MSM8996_SLAVE_PCIE_0_CFG, + MSM8996_SLAVE_TLMM, + MSM8996_SLAVE_MPM, + MSM8996_SLAVE_A0NOC_SMMU_CFG, + MSM8996_SLAVE_EBI1_PHY_CFG, + MSM8996_SLAVE_BIMC_CFG, + MSM8996_SLAVE_PIMEM_CFG, + MSM8996_SLAVE_RBCPR_MX, + MSM8996_SLAVE_CLK_CTL, + MSM8996_SLAVE_PRNG, + MSM8996_SLAVE_PCIE20_AHB2PHY, + MSM8996_SLAVE_A2NOC_MPU_CFG, + MSM8996_SLAVE_QDSS_CFG, + MSM8996_SLAVE_A2NOC_CFG, + MSM8996_SLAVE_A0NOC_CFG, + MSM8996_SLAVE_UFS_CFG, + MSM8996_SLAVE_CRYPTO_0_CFG, + MSM8996_SLAVE_CNOC_A1NOC, + MSM8996_SLAVE_PCIE_1_CFG, + MSM8996_SLAVE_SNOC_CFG, + MSM8996_SLAVE_SNOC_MPU_CFG, + MSM8996_SLAVE_A1NOC_MPU_CFG, + MSM8996_SLAVE_A1NOC_SMMU_CFG, + MSM8996_SLAVE_PCIE_2_CFG, + MSM8996_SLAVE_CNOC_MNOC_CFG, + MSM8996_SLAVE_CNOC_MNOC_MMSS_CFG, + MSM8996_SLAVE_PMIC_ARB, + MSM8996_SLAVE_IMEM_CFG, + MSM8996_SLAVE_A1NOC_CFG, + MSM8996_SLAVE_SSC_CFG, + MSM8996_SLAVE_TCSR, + MSM8996_SLAVE_LPASS_SMMU_CFG, + MSM8996_SLAVE_DCC_CFG +}; + +static struct qcom_icc_node mas_qdss_dap = { + .name = "mas_qdss_dap", + .id = MSM8996_MASTER_QDSS_DAP, + .buswidth = 8, + .mas_rpm_id = 49, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_qdss_dap_links), + .links = mas_qdss_dap_links +}; + +static const u16 mas_cnoc_mnoc_mmss_cfg_links[] = { + MSM8996_SLAVE_MMAGIC_CFG, + MSM8996_SLAVE_DSA_MPU_CFG, + MSM8996_SLAVE_MMSS_CLK_CFG, + MSM8996_SLAVE_CAMERA_THROTTLE_CFG, + MSM8996_SLAVE_VENUS_CFG, + MSM8996_SLAVE_SMMU_VFE_CFG, + MSM8996_SLAVE_MISC_CFG, + MSM8996_SLAVE_SMMU_CPP_CFG, + MSM8996_SLAVE_GRAPHICS_3D_CFG, + MSM8996_SLAVE_DISPLAY_THROTTLE_CFG, + MSM8996_SLAVE_VENUS_THROTTLE_CFG, + MSM8996_SLAVE_CAMERA_CFG, + MSM8996_SLAVE_DISPLAY_CFG, + MSM8996_SLAVE_CPR_CFG, + MSM8996_SLAVE_SMMU_ROTATOR_CFG, + MSM8996_SLAVE_DSA_CFG, + MSM8996_SLAVE_SMMU_VENUS_CFG, + MSM8996_SLAVE_VMEM_CFG, + MSM8996_SLAVE_SMMU_JPEG_CFG, + MSM8996_SLAVE_SMMU_MDP_CFG, + MSM8996_SLAVE_MNOC_MPU_CFG +}; + +static struct qcom_icc_node mas_cnoc_mnoc_mmss_cfg = { + .name = "mas_cnoc_mnoc_mmss_cfg", + .id = MSM8996_MASTER_CNOC_MNOC_MMSS_CFG, + .buswidth = 8, + .mas_rpm_id = 4, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_cnoc_mnoc_mmss_cfg_links), + .links = mas_cnoc_mnoc_mmss_cfg_links +}; + +static const u16 mas_cnoc_mnoc_cfg_links[] = { + MSM8996_SLAVE_SERVICE_MNOC +}; + +static struct qcom_icc_node mas_cnoc_mnoc_cfg = { + .name = "mas_cnoc_mnoc_cfg", + .id = MSM8996_MASTER_CNOC_MNOC_CFG, + .buswidth = 8, + .mas_rpm_id = 5, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_cnoc_mnoc_cfg_links), + .links = mas_cnoc_mnoc_cfg_links +}; + +static const u16 mas_mnoc_bimc_common_links[] = { + MSM8996_SLAVE_MNOC_BIMC +}; + +static struct qcom_icc_node mas_cpp = { + .name = "mas_cpp", + .id = MSM8996_MASTER_CPP, + .buswidth = 32, + .mas_rpm_id = 115, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 5, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static struct qcom_icc_node mas_jpeg = { + .name = "mas_jpeg", + .id = MSM8996_MASTER_JPEG, + .buswidth = 32, + .mas_rpm_id = 7, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 7, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static struct qcom_icc_node mas_mdp_p0 = { + .name = "mas_mdp_p0", + .id = MSM8996_MASTER_MDP_PORT0, + .buswidth = 32, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 1, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static struct qcom_icc_node mas_mdp_p1 = { + .name = "mas_mdp_p1", + .id = MSM8996_MASTER_MDP_PORT1, + .buswidth = 32, + .mas_rpm_id = 61, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static struct qcom_icc_node mas_rotator = { + .name = "mas_rotator", + .id = MSM8996_MASTER_ROTATOR, + .buswidth = 32, + .mas_rpm_id = 120, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 0, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static struct qcom_icc_node mas_venus = { + .name = "mas_venus", + .id = MSM8996_MASTER_VIDEO_P0, + .buswidth = 32, + .mas_rpm_id = 9, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 3, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static struct qcom_icc_node mas_vfe = { + .name = "mas_vfe", + .id = MSM8996_MASTER_VFE, + .buswidth = 32, + .mas_rpm_id = 11, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .qos.areq_prio = 0, + .qos.prio_level = 0, + .qos.qos_port = 6, + .num_links = ARRAY_SIZE(mas_mnoc_bimc_common_links), + .links = mas_mnoc_bimc_common_links +}; + +static const u16 mas_vmem_common_links[] = { + MSM8996_SLAVE_VMEM +}; + +static struct qcom_icc_node mas_snoc_vmem = { + .name = "mas_snoc_vmem", + .id = MSM8996_MASTER_SNOC_VMEM, + .buswidth = 32, + .mas_rpm_id = 114, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_vmem_common_links), + .links = mas_vmem_common_links +}; + +static struct qcom_icc_node mas_venus_vmem = { + .name = "mas_venus_vmem", + .id = MSM8996_MASTER_VIDEO_P0_OCMEM, + .buswidth = 32, + .mas_rpm_id = 121, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_vmem_common_links), + .links = mas_vmem_common_links +}; + +static const u16 mas_snoc_pnoc_links[] = { + MSM8996_SLAVE_BLSP_1, + MSM8996_SLAVE_BLSP_2, + MSM8996_SLAVE_SDCC_1, + MSM8996_SLAVE_SDCC_2, + MSM8996_SLAVE_SDCC_4, + MSM8996_SLAVE_TSIF, + MSM8996_SLAVE_PDM, + MSM8996_SLAVE_AHB2PHY +}; + +static struct qcom_icc_node mas_snoc_pnoc = { + .name = "mas_snoc_pnoc", + .id = MSM8996_MASTER_SNOC_PNOC, + .buswidth = 8, + .mas_rpm_id = 44, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_pnoc_links), + .links = mas_snoc_pnoc_links +}; + +static const u16 mas_pnoc_a1noc_common_links[] = { + MSM8996_SLAVE_PNOC_A1NOC +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .name = "mas_sdcc_1", + .id = MSM8996_MASTER_SDCC_1, + .buswidth = 8, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .name = "mas_sdcc_2", + .id = MSM8996_MASTER_SDCC_2, + .buswidth = 8, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static struct qcom_icc_node mas_sdcc_4 = { + .name = "mas_sdcc_4", + .id = MSM8996_MASTER_SDCC_4, + .buswidth = 8, + .mas_rpm_id = 36, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static struct qcom_icc_node mas_usb_hs = { + .name = "mas_usb_hs", + .id = MSM8996_MASTER_USB_HS, + .buswidth = 8, + .mas_rpm_id = 42, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static struct qcom_icc_node mas_blsp_1 = { + .name = "mas_blsp_1", + .id = MSM8996_MASTER_BLSP_1, + .buswidth = 4, + .mas_rpm_id = 41, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static struct qcom_icc_node mas_blsp_2 = { + .name = "mas_blsp_2", + .id = MSM8996_MASTER_BLSP_2, + .buswidth = 4, + .mas_rpm_id = 39, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static struct qcom_icc_node mas_tsif = { + .name = "mas_tsif", + .id = MSM8996_MASTER_TSIF, + .buswidth = 4, + .mas_rpm_id = 37, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pnoc_a1noc_common_links), + .links = mas_pnoc_a1noc_common_links +}; + +static const u16 mas_hmss_links[] = { + MSM8996_SLAVE_PIMEM, + MSM8996_SLAVE_OCIMEM, + MSM8996_SLAVE_SNOC_BIMC +}; + +static struct qcom_icc_node mas_hmss = { + .name = "mas_hmss", + .id = MSM8996_MASTER_HMSS, + .buswidth = 8, + .mas_rpm_id = 118, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 4, + .num_links = ARRAY_SIZE(mas_hmss_links), + .links = mas_hmss_links +}; + +static const u16 mas_qdss_common_links[] = { + MSM8996_SLAVE_PIMEM, + MSM8996_SLAVE_USB3, + MSM8996_SLAVE_OCIMEM, + MSM8996_SLAVE_SNOC_BIMC, + MSM8996_SLAVE_SNOC_PNOC +}; + +static struct qcom_icc_node mas_qdss_bam = { + .name = "mas_qdss_bam", + .id = MSM8996_MASTER_QDSS_BAM, + .buswidth = 16, + .mas_rpm_id = 19, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 2, + .num_links = ARRAY_SIZE(mas_qdss_common_links), + .links = mas_qdss_common_links +}; + +static const u16 mas_snoc_cfg_links[] = { + MSM8996_SLAVE_SERVICE_SNOC +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .name = "mas_snoc_cfg", + .id = MSM8996_MASTER_SNOC_CFG, + .buswidth = 16, + .mas_rpm_id = 20, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_snoc_cfg_links), + .links = mas_snoc_cfg_links +}; + +static const u16 mas_bimc_snoc_0_links[] = { + MSM8996_SLAVE_SNOC_VMEM, + MSM8996_SLAVE_USB3, + MSM8996_SLAVE_PIMEM, + MSM8996_SLAVE_LPASS, + MSM8996_SLAVE_APPSS, + MSM8996_SLAVE_SNOC_CNOC, + MSM8996_SLAVE_SNOC_PNOC, + MSM8996_SLAVE_OCIMEM, + MSM8996_SLAVE_QDSS_STM +}; + +static struct qcom_icc_node mas_bimc_snoc_0 = { + .name = "mas_bimc_snoc_0", + .id = MSM8996_MASTER_BIMC_SNOC_0, + .buswidth = 16, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_bimc_snoc_0_links), + .links = mas_bimc_snoc_0_links +}; + +static const u16 mas_bimc_snoc_1_links[] = { + MSM8996_SLAVE_PCIE_2, + MSM8996_SLAVE_PCIE_1, + MSM8996_SLAVE_PCIE_0 +}; + +static struct qcom_icc_node mas_bimc_snoc_1 = { + .name = "mas_bimc_snoc_1", + .id = MSM8996_MASTER_BIMC_SNOC_1, + .buswidth = 16, + .mas_rpm_id = 109, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_bimc_snoc_1_links), + .links = mas_bimc_snoc_1_links +}; + +static const u16 mas_a0noc_snoc_links[] = { + MSM8996_SLAVE_SNOC_PNOC, + MSM8996_SLAVE_OCIMEM, + MSM8996_SLAVE_APPSS, + MSM8996_SLAVE_SNOC_BIMC, + MSM8996_SLAVE_PIMEM +}; + +static struct qcom_icc_node mas_a0noc_snoc = { + .name = "mas_a0noc_snoc", + .id = MSM8996_MASTER_A0NOC_SNOC, + .buswidth = 16, + .mas_rpm_id = 110, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(mas_a0noc_snoc_links), + .links = mas_a0noc_snoc_links +}; + +static const u16 mas_a1noc_snoc_links[] = { + MSM8996_SLAVE_SNOC_VMEM, + MSM8996_SLAVE_USB3, + MSM8996_SLAVE_PCIE_0, + MSM8996_SLAVE_PIMEM, + MSM8996_SLAVE_PCIE_2, + MSM8996_SLAVE_LPASS, + MSM8996_SLAVE_PCIE_1, + MSM8996_SLAVE_APPSS, + MSM8996_SLAVE_SNOC_BIMC, + MSM8996_SLAVE_SNOC_CNOC, + MSM8996_SLAVE_SNOC_PNOC, + MSM8996_SLAVE_OCIMEM, + MSM8996_SLAVE_QDSS_STM +}; + +static struct qcom_icc_node mas_a1noc_snoc = { + .name = "mas_a1noc_snoc", + .id = MSM8996_MASTER_A1NOC_SNOC, + .buswidth = 16, + .mas_rpm_id = 111, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_a1noc_snoc_links), + .links = mas_a1noc_snoc_links +}; + +static const u16 mas_a2noc_snoc_links[] = { + MSM8996_SLAVE_SNOC_VMEM, + MSM8996_SLAVE_USB3, + MSM8996_SLAVE_PCIE_1, + MSM8996_SLAVE_PIMEM, + MSM8996_SLAVE_PCIE_2, + MSM8996_SLAVE_QDSS_STM, + MSM8996_SLAVE_LPASS, + MSM8996_SLAVE_SNOC_BIMC, + MSM8996_SLAVE_SNOC_CNOC, + MSM8996_SLAVE_SNOC_PNOC, + MSM8996_SLAVE_OCIMEM, + MSM8996_SLAVE_PCIE_0 +}; + +static struct qcom_icc_node mas_a2noc_snoc = { + .name = "mas_a2noc_snoc", + .id = MSM8996_MASTER_A2NOC_SNOC, + .buswidth = 16, + .mas_rpm_id = 112, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_a2noc_snoc_links), + .links = mas_a2noc_snoc_links +}; + +static struct qcom_icc_node mas_qdss_etr = { + .name = "mas_qdss_etr", + .id = MSM8996_MASTER_QDSS_ETR, + .buswidth = 16, + .mas_rpm_id = 31, + .slv_rpm_id = -1, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 1, + .qos.prio_level = 1, + .qos.qos_port = 3, + .num_links = ARRAY_SIZE(mas_qdss_common_links), + .links = mas_qdss_common_links +}; + +static const u16 slv_a0noc_snoc_links[] = { + MSM8996_MASTER_A0NOC_SNOC +}; + +static struct qcom_icc_node slv_a0noc_snoc = { + .name = "slv_a0noc_snoc", + .id = MSM8996_SLAVE_A0NOC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 141, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_a0noc_snoc_links), + .links = slv_a0noc_snoc_links +}; + +static const u16 slv_a1noc_snoc_links[] = { + MSM8996_MASTER_A1NOC_SNOC +}; + +static struct qcom_icc_node slv_a1noc_snoc = { + .name = "slv_a1noc_snoc", + .id = MSM8996_SLAVE_A1NOC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 142, + .num_links = ARRAY_SIZE(slv_a1noc_snoc_links), + .links = slv_a1noc_snoc_links +}; + +static const u16 slv_a2noc_snoc_links[] = { + MSM8996_MASTER_A2NOC_SNOC +}; + +static struct qcom_icc_node slv_a2noc_snoc = { + .name = "slv_a2noc_snoc", + .id = MSM8996_SLAVE_A2NOC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 143, + .num_links = ARRAY_SIZE(slv_a2noc_snoc_links), + .links = slv_a2noc_snoc_links +}; + +static struct qcom_icc_node slv_ebi = { + .name = "slv_ebi", + .id = MSM8996_SLAVE_EBI_CH0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0 +}; + +static struct qcom_icc_node slv_hmss_l3 = { + .name = "slv_hmss_l3", + .id = MSM8996_SLAVE_HMSS_L3, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 160 +}; + +static const u16 slv_bimc_snoc_0_links[] = { + MSM8996_MASTER_BIMC_SNOC_0 +}; + +static struct qcom_icc_node slv_bimc_snoc_0 = { + .name = "slv_bimc_snoc_0", + .id = MSM8996_SLAVE_BIMC_SNOC_0, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_bimc_snoc_0_links), + .links = slv_bimc_snoc_0_links +}; + +static const u16 slv_bimc_snoc_1_links[] = { + MSM8996_MASTER_BIMC_SNOC_1 +}; + +static struct qcom_icc_node slv_bimc_snoc_1 = { + .name = "slv_bimc_snoc_1", + .id = MSM8996_SLAVE_BIMC_SNOC_1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 138, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_bimc_snoc_1_links), + .links = slv_bimc_snoc_1_links +}; + +static const u16 slv_cnoc_a1noc_links[] = { + MSM8996_MASTER_CNOC_A1NOC +}; + +static struct qcom_icc_node slv_cnoc_a1noc = { + .name = "slv_cnoc_a1noc", + .id = MSM8996_SLAVE_CNOC_A1NOC, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 75, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_cnoc_a1noc_links), + .links = slv_cnoc_a1noc_links +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = MSM8996_SLAVE_CLK_CTL, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 47 +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = MSM8996_SLAVE_TCSR, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 50 +}; + +static struct qcom_icc_node slv_tlmm = { + .name = "slv_tlmm", + .id = MSM8996_SLAVE_TLMM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 51 +}; + +static struct qcom_icc_node slv_crypto0_cfg = { + .name = "slv_crypto0_cfg", + .id = MSM8996_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 52, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_mpm = { + .name = "slv_mpm", + .id = MSM8996_SLAVE_MPM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 62, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pimem_cfg = { + .name = "slv_pimem_cfg", + .id = MSM8996_SLAVE_PIMEM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 167, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = MSM8996_SLAVE_IMEM_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 54, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = MSM8996_SLAVE_MESSAGE_RAM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 55 +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = MSM8996_SLAVE_BIMC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 56, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = MSM8996_SLAVE_PMIC_ARB, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 59 +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = MSM8996_SLAVE_PRNG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 127, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_dcc_cfg = { + .name = "slv_dcc_cfg", + .id = MSM8996_SLAVE_DCC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 155, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_rbcpr_mx = { + .name = "slv_rbcpr_mx", + .id = MSM8996_SLAVE_RBCPR_MX, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 170, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = MSM8996_SLAVE_QDSS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 63, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_rbcpr_cx = { + .name = "slv_rbcpr_cx", + .id = MSM8996_SLAVE_RBCPR_CX, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 169, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_cpu_apu_cfg = { + .name = "slv_cpu_apu_cfg", + .id = MSM8996_SLAVE_QDSS_RBCPR_APU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 168, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static const u16 slv_cnoc_mnoc_cfg_links[] = { + MSM8996_MASTER_CNOC_MNOC_CFG +}; + +static struct qcom_icc_node slv_cnoc_mnoc_cfg = { + .name = "slv_cnoc_mnoc_cfg", + .id = MSM8996_SLAVE_CNOC_MNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 66, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_cnoc_mnoc_cfg_links), + .links = slv_cnoc_mnoc_cfg_links +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = MSM8996_SLAVE_SNOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 70, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_snoc_mpu_cfg = { + .name = "slv_snoc_mpu_cfg", + .id = MSM8996_SLAVE_SNOC_MPU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 67, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_ebi1_phy_cfg = { + .name = "slv_ebi1_phy_cfg", + .id = MSM8996_SLAVE_EBI1_PHY_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 73, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a0noc_cfg = { + .name = "slv_a0noc_cfg", + .id = MSM8996_SLAVE_A0NOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 144, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pcie_1_cfg = { + .name = "slv_pcie_1_cfg", + .id = MSM8996_SLAVE_PCIE_1_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 89, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pcie_2_cfg = { + .name = "slv_pcie_2_cfg", + .id = MSM8996_SLAVE_PCIE_2_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 165, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pcie_0_cfg = { + .name = "slv_pcie_0_cfg", + .id = MSM8996_SLAVE_PCIE_0_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 88, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pcie20_ahb2phy = { + .name = "slv_pcie20_ahb2phy", + .id = MSM8996_SLAVE_PCIE20_AHB2PHY, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 163, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a0noc_mpu_cfg = { + .name = "slv_a0noc_mpu_cfg", + .id = MSM8996_SLAVE_A0NOC_MPU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 145, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_ufs_cfg = { + .name = "slv_ufs_cfg", + .id = MSM8996_SLAVE_UFS_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 92, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a1noc_cfg = { + .name = "slv_a1noc_cfg", + .id = MSM8996_SLAVE_A1NOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 147, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a1noc_mpu_cfg = { + .name = "slv_a1noc_mpu_cfg", + .id = MSM8996_SLAVE_A1NOC_MPU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 148, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a2noc_cfg = { + .name = "slv_a2noc_cfg", + .id = MSM8996_SLAVE_A2NOC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 150, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a2noc_mpu_cfg = { + .name = "slv_a2noc_mpu_cfg", + .id = MSM8996_SLAVE_A2NOC_MPU_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 151, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_ssc_cfg = { + .name = "slv_ssc_cfg", + .id = MSM8996_SLAVE_SSC_CFG, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 177, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a0noc_smmu_cfg = { + .name = "slv_a0noc_smmu_cfg", + .id = MSM8996_SLAVE_A0NOC_SMMU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 146, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a1noc_smmu_cfg = { + .name = "slv_a1noc_smmu_cfg", + .id = MSM8996_SLAVE_A1NOC_SMMU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 149, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_a2noc_smmu_cfg = { + .name = "slv_a2noc_smmu_cfg", + .id = MSM8996_SLAVE_A2NOC_SMMU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 152, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_lpass_smmu_cfg = { + .name = "slv_lpass_smmu_cfg", + .id = MSM8996_SLAVE_LPASS_SMMU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 161, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static const u16 slv_cnoc_mnoc_mmss_cfg_links[] = { + MSM8996_MASTER_CNOC_MNOC_MMSS_CFG +}; + +static struct qcom_icc_node slv_cnoc_mnoc_mmss_cfg = { + .name = "slv_cnoc_mnoc_mmss_cfg", + .id = MSM8996_SLAVE_CNOC_MNOC_MMSS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 58, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_cnoc_mnoc_mmss_cfg_links), + .links = slv_cnoc_mnoc_mmss_cfg_links +}; + +static struct qcom_icc_node slv_mmagic_cfg = { + .name = "slv_mmagic_cfg", + .id = MSM8996_SLAVE_MMAGIC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 162, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_cpr_cfg = { + .name = "slv_cpr_cfg", + .id = MSM8996_SLAVE_CPR_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 6, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_misc_cfg = { + .name = "slv_misc_cfg", + .id = MSM8996_SLAVE_MISC_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_venus_throttle_cfg = { + .name = "slv_venus_throttle_cfg", + .id = MSM8996_SLAVE_VENUS_THROTTLE_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 178, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = MSM8996_SLAVE_VENUS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 10, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_vmem_cfg = { + .name = "slv_vmem_cfg", + .id = MSM8996_SLAVE_VMEM_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 180, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_dsa_cfg = { + .name = "slv_dsa_cfg", + .id = MSM8996_SLAVE_DSA_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 157, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_mnoc_clocks_cfg = { + .name = "slv_mnoc_clocks_cfg", + .id = MSM8996_SLAVE_MMSS_CLK_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 12, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_dsa_mpu_cfg = { + .name = "slv_dsa_mpu_cfg", + .id = MSM8996_SLAVE_DSA_MPU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 158, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_mnoc_mpu_cfg = { + .name = "slv_mnoc_mpu_cfg", + .id = MSM8996_SLAVE_MNOC_MPU_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 14, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = MSM8996_SLAVE_DISPLAY_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_display_throttle_cfg = { + .name = "slv_display_throttle_cfg", + .id = MSM8996_SLAVE_DISPLAY_THROTTLE_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 156, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = MSM8996_SLAVE_CAMERA_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 3, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_camera_throttle_cfg = { + .name = "slv_camera_throttle_cfg", + .id = MSM8996_SLAVE_CAMERA_THROTTLE_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 154, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_oxili_cfg = { + .name = "slv_oxili_cfg", + .id = MSM8996_SLAVE_GRAPHICS_3D_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 11, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_smmu_mdp_cfg = { + .name = "slv_smmu_mdp_cfg", + .id = MSM8996_SLAVE_SMMU_MDP_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 173, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_smmu_rot_cfg = { + .name = "slv_smmu_rot_cfg", + .id = MSM8996_SLAVE_SMMU_ROTATOR_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 174, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_smmu_venus_cfg = { + .name = "slv_smmu_venus_cfg", + .id = MSM8996_SLAVE_SMMU_VENUS_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 175, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_smmu_cpp_cfg = { + .name = "slv_smmu_cpp_cfg", + .id = MSM8996_SLAVE_SMMU_CPP_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 171, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_smmu_jpeg_cfg = { + .name = "slv_smmu_jpeg_cfg", + .id = MSM8996_SLAVE_SMMU_JPEG_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 172, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_smmu_vfe_cfg = { + .name = "slv_smmu_vfe_cfg", + .id = MSM8996_SLAVE_SMMU_VFE_CFG, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 176, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static const u16 slv_mnoc_bimc_links[] = { + MSM8996_MASTER_MNOC_BIMC +}; + +static struct qcom_icc_node slv_mnoc_bimc = { + .name = "slv_mnoc_bimc", + .id = MSM8996_SLAVE_MNOC_BIMC, + .buswidth = 32, + .mas_rpm_id = -1, + .slv_rpm_id = 16, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_mnoc_bimc_links), + .links = slv_mnoc_bimc_links +}; + +static struct qcom_icc_node slv_vmem = { + .name = "slv_vmem", + .id = MSM8996_SLAVE_VMEM, + .buswidth = 32, + .mas_rpm_id = -1, + .slv_rpm_id = 179, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_srvc_mnoc = { + .name = "slv_srvc_mnoc", + .id = MSM8996_SLAVE_SERVICE_MNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 17, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static const u16 slv_pnoc_a1noc_links[] = { + MSM8996_MASTER_PNOC_A1NOC +}; + +static struct qcom_icc_node slv_pnoc_a1noc = { + .name = "slv_pnoc_a1noc", + .id = MSM8996_SLAVE_PNOC_A1NOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 139, + .num_links = ARRAY_SIZE(slv_pnoc_a1noc_links), + .links = slv_pnoc_a1noc_links +}; + +static struct qcom_icc_node slv_usb_hs = { + .name = "slv_usb_hs", + .id = MSM8996_SLAVE_USB_HS, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 40 +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = MSM8996_SLAVE_SDCC_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 33 +}; + +static struct qcom_icc_node slv_sdcc_4 = { + .name = "slv_sdcc_4", + .id = MSM8996_SLAVE_SDCC_4, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 34 +}; + +static struct qcom_icc_node slv_tsif = { + .name = "slv_tsif", + .id = MSM8996_SLAVE_TSIF, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 35 +}; + +static struct qcom_icc_node slv_blsp_2 = { + .name = "slv_blsp_2", + .id = MSM8996_SLAVE_BLSP_2, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 37 +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = MSM8996_SLAVE_SDCC_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 31 +}; + +static struct qcom_icc_node slv_blsp_1 = { + .name = "slv_blsp_1", + .id = MSM8996_SLAVE_BLSP_1, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 39 +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = MSM8996_SLAVE_PDM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 41 +}; + +static struct qcom_icc_node slv_ahb2phy = { + .name = "slv_ahb2phy", + .id = MSM8996_SLAVE_AHB2PHY, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 153, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_hmss = { + .name = "slv_hmss", + .id = MSM8996_SLAVE_APPSS, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 20, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = MSM8996_SLAVE_LPASS, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 21, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_usb3 = { + .name = "slv_usb3", + .id = MSM8996_SLAVE_USB3, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 22, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static const u16 slv_snoc_bimc_links[] = { + MSM8996_MASTER_SNOC_BIMC +}; + +static struct qcom_icc_node slv_snoc_bimc = { + .name = "slv_snoc_bimc", + .id = MSM8996_SLAVE_SNOC_BIMC, + .buswidth = 32, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .num_links = ARRAY_SIZE(slv_snoc_bimc_links), + .links = slv_snoc_bimc_links +}; + +static const u16 slv_snoc_cnoc_links[] = { + MSM8996_MASTER_SNOC_CNOC +}; + +static struct qcom_icc_node slv_snoc_cnoc = { + .name = "slv_snoc_cnoc", + .id = MSM8996_SLAVE_SNOC_CNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 25, + .num_links = ARRAY_SIZE(slv_snoc_cnoc_links), + .links = slv_snoc_cnoc_links +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = MSM8996_SLAVE_OCIMEM, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 26 +}; + +static struct qcom_icc_node slv_pimem = { + .name = "slv_pimem", + .id = MSM8996_SLAVE_PIMEM, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 166 +}; + +static const u16 slv_snoc_vmem_links[] = { + MSM8996_MASTER_SNOC_VMEM +}; + +static struct qcom_icc_node slv_snoc_vmem = { + .name = "slv_snoc_vmem", + .id = MSM8996_SLAVE_SNOC_VMEM, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 140, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .num_links = ARRAY_SIZE(slv_snoc_vmem_links), + .links = slv_snoc_vmem_links +}; + +static const u16 slv_snoc_pnoc_links[] = { + MSM8996_MASTER_SNOC_PNOC +}; + +static struct qcom_icc_node slv_snoc_pnoc = { + .name = "slv_snoc_pnoc", + .id = MSM8996_SLAVE_SNOC_PNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 28, + .num_links = ARRAY_SIZE(slv_snoc_pnoc_links), + .links = slv_snoc_pnoc_links +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = MSM8996_SLAVE_QDSS_STM, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 30 +}; + +static struct qcom_icc_node slv_pcie_0 = { + .name = "slv_pcie_0", + .id = MSM8996_SLAVE_PCIE_0, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 84, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pcie_1 = { + .name = "slv_pcie_1", + .id = MSM8996_SLAVE_PCIE_1, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 85, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_pcie_2 = { + .name = "slv_pcie_2", + .id = MSM8996_SLAVE_PCIE_2, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 164, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node slv_srvc_snoc = { + .name = "slv_srvc_snoc", + .id = MSM8996_SLAVE_SERVICE_SNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 29, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID +}; + +static struct qcom_icc_node *a0noc_nodes[] = { + [MASTER_PCIE_0] = &mas_pcie_0, + [MASTER_PCIE_1] = &mas_pcie_1, + [MASTER_PCIE_2] = &mas_pcie_2 +}; + +static const struct regmap_config msm8996_a0noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x9000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_a0noc = { + .nodes = a0noc_nodes, + .num_nodes = ARRAY_SIZE(a0noc_nodes), + .clocks = bus_a0noc_clocks, + .num_clocks = ARRAY_SIZE(bus_a0noc_clocks), + .has_bus_pd = true, + .regmap_cfg = &msm8996_a0noc_regmap_config +}; + +static struct qcom_icc_node *a1noc_nodes[] = { + [MASTER_CNOC_A1NOC] = &mas_cnoc_a1noc, + [MASTER_CRYPTO_CORE0] = &mas_crypto_c0, + [MASTER_PNOC_A1NOC] = &mas_pnoc_a1noc +}; + +static const struct regmap_config msm8996_a1noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x7000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_a1noc = { + .nodes = a1noc_nodes, + .num_nodes = ARRAY_SIZE(a1noc_nodes), + .regmap_cfg = &msm8996_a1noc_regmap_config +}; + +static struct qcom_icc_node *a2noc_nodes[] = { + [MASTER_USB3] = &mas_usb3, + [MASTER_IPA] = &mas_ipa, + [MASTER_UFS] = &mas_ufs +}; + +static const struct regmap_config msm8996_a2noc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0xa000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_a2noc = { + .nodes = a2noc_nodes, + .num_nodes = ARRAY_SIZE(a2noc_nodes), + .regmap_cfg = &msm8996_a2noc_regmap_config +}; + +static struct qcom_icc_node *bimc_nodes[] = { + [MASTER_AMPSS_M0] = &mas_apps_proc, + [MASTER_GRAPHICS_3D] = &mas_oxili, + [MASTER_MNOC_BIMC] = &mas_mnoc_bimc, + [MASTER_SNOC_BIMC] = &mas_snoc_bimc, + [SLAVE_EBI_CH0] = &slv_ebi, + [SLAVE_HMSS_L3] = &slv_hmss_l3, + [SLAVE_BIMC_SNOC_0] = &slv_bimc_snoc_0, + [SLAVE_BIMC_SNOC_1] = &slv_bimc_snoc_1 +}; + +static const struct regmap_config msm8996_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x62000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_bimc = { + .nodes = bimc_nodes, + .num_nodes = ARRAY_SIZE(bimc_nodes), + .is_bimc_node = true, + .regmap_cfg = &msm8996_bimc_regmap_config +}; + +static struct qcom_icc_node *cnoc_nodes[] = { + [MASTER_SNOC_CNOC] = &mas_snoc_cnoc, + [MASTER_QDSS_DAP] = &mas_qdss_dap, + [SLAVE_CNOC_A1NOC] = &slv_cnoc_a1noc, + [SLAVE_CLK_CTL] = &slv_clk_ctl, + [SLAVE_TCSR] = &slv_tcsr, + [SLAVE_TLMM] = &slv_tlmm, + [SLAVE_CRYPTO_0_CFG] = &slv_crypto0_cfg, + [SLAVE_MPM] = &slv_mpm, + [SLAVE_PIMEM_CFG] = &slv_pimem_cfg, + [SLAVE_IMEM_CFG] = &slv_imem_cfg, + [SLAVE_MESSAGE_RAM] = &slv_message_ram, + [SLAVE_BIMC_CFG] = &slv_bimc_cfg, + [SLAVE_PMIC_ARB] = &slv_pmic_arb, + [SLAVE_PRNG] = &slv_prng, + [SLAVE_DCC_CFG] = &slv_dcc_cfg, + [SLAVE_RBCPR_MX] = &slv_rbcpr_mx, + [SLAVE_QDSS_CFG] = &slv_qdss_cfg, + [SLAVE_RBCPR_CX] = &slv_rbcpr_cx, + [SLAVE_QDSS_RBCPR_APU] = &slv_cpu_apu_cfg, + [SLAVE_CNOC_MNOC_CFG] = &slv_cnoc_mnoc_cfg, + [SLAVE_SNOC_CFG] = &slv_snoc_cfg, + [SLAVE_SNOC_MPU_CFG] = &slv_snoc_mpu_cfg, + [SLAVE_EBI1_PHY_CFG] = &slv_ebi1_phy_cfg, + [SLAVE_A0NOC_CFG] = &slv_a0noc_cfg, + [SLAVE_PCIE_1_CFG] = &slv_pcie_1_cfg, + [SLAVE_PCIE_2_CFG] = &slv_pcie_2_cfg, + [SLAVE_PCIE_0_CFG] = &slv_pcie_0_cfg, + [SLAVE_PCIE20_AHB2PHY] = &slv_pcie20_ahb2phy, + [SLAVE_A0NOC_MPU_CFG] = &slv_a0noc_mpu_cfg, + [SLAVE_UFS_CFG] = &slv_ufs_cfg, + [SLAVE_A1NOC_CFG] = &slv_a1noc_cfg, + [SLAVE_A1NOC_MPU_CFG] = &slv_a1noc_mpu_cfg, + [SLAVE_A2NOC_CFG] = &slv_a2noc_cfg, + [SLAVE_A2NOC_MPU_CFG] = &slv_a2noc_mpu_cfg, + [SLAVE_SSC_CFG] = &slv_ssc_cfg, + [SLAVE_A0NOC_SMMU_CFG] = &slv_a0noc_smmu_cfg, + [SLAVE_A1NOC_SMMU_CFG] = &slv_a1noc_smmu_cfg, + [SLAVE_A2NOC_SMMU_CFG] = &slv_a2noc_smmu_cfg, + [SLAVE_LPASS_SMMU_CFG] = &slv_lpass_smmu_cfg, + [SLAVE_CNOC_MNOC_MMSS_CFG] = &slv_cnoc_mnoc_mmss_cfg +}; + +static const struct regmap_config msm8996_cnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_cnoc = { + .nodes = cnoc_nodes, + .num_nodes = ARRAY_SIZE(cnoc_nodes), + .regmap_cfg = &msm8996_cnoc_regmap_config +}; + +static struct qcom_icc_node *mnoc_nodes[] = { + [MASTER_CNOC_MNOC_CFG] = &mas_cnoc_mnoc_cfg, + [MASTER_CPP] = &mas_cpp, + [MASTER_JPEG] = &mas_jpeg, + [MASTER_MDP_PORT0] = &mas_mdp_p0, + [MASTER_MDP_PORT1] = &mas_mdp_p1, + [MASTER_ROTATOR] = &mas_rotator, + [MASTER_VIDEO_P0] = &mas_venus, + [MASTER_VFE] = &mas_vfe, + [MASTER_SNOC_VMEM] = &mas_snoc_vmem, + [MASTER_VIDEO_P0_OCMEM] = &mas_venus_vmem, + [MASTER_CNOC_MNOC_MMSS_CFG] = &mas_cnoc_mnoc_mmss_cfg, + [SLAVE_MNOC_BIMC] = &slv_mnoc_bimc, + [SLAVE_VMEM] = &slv_vmem, + [SLAVE_SERVICE_MNOC] = &slv_srvc_mnoc, + [SLAVE_MMAGIC_CFG] = &slv_mmagic_cfg, + [SLAVE_CPR_CFG] = &slv_cpr_cfg, + [SLAVE_MISC_CFG] = &slv_misc_cfg, + [SLAVE_VENUS_THROTTLE_CFG] = &slv_venus_throttle_cfg, + [SLAVE_VENUS_CFG] = &slv_venus_cfg, + [SLAVE_VMEM_CFG] = &slv_vmem_cfg, + [SLAVE_DSA_CFG] = &slv_dsa_cfg, + [SLAVE_MMSS_CLK_CFG] = &slv_mnoc_clocks_cfg, + [SLAVE_DSA_MPU_CFG] = &slv_dsa_mpu_cfg, + [SLAVE_MNOC_MPU_CFG] = &slv_mnoc_mpu_cfg, + [SLAVE_DISPLAY_CFG] = &slv_display_cfg, + [SLAVE_DISPLAY_THROTTLE_CFG] = &slv_display_throttle_cfg, + [SLAVE_CAMERA_CFG] = &slv_camera_cfg, + [SLAVE_CAMERA_THROTTLE_CFG] = &slv_camera_throttle_cfg, + [SLAVE_GRAPHICS_3D_CFG] = &slv_oxili_cfg, + [SLAVE_SMMU_MDP_CFG] = &slv_smmu_mdp_cfg, + [SLAVE_SMMU_ROT_CFG] = &slv_smmu_rot_cfg, + [SLAVE_SMMU_VENUS_CFG] = &slv_smmu_venus_cfg, + [SLAVE_SMMU_CPP_CFG] = &slv_smmu_cpp_cfg, + [SLAVE_SMMU_JPEG_CFG] = &slv_smmu_jpeg_cfg, + [SLAVE_SMMU_VFE_CFG] = &slv_smmu_vfe_cfg +}; + +static const struct regmap_config msm8996_mnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_mnoc = { + .nodes = mnoc_nodes, + .num_nodes = ARRAY_SIZE(mnoc_nodes), + .clocks = bus_mm_clocks, + .num_clocks = ARRAY_SIZE(bus_mm_clocks), + .regmap_cfg = &msm8996_mnoc_regmap_config +}; + +static struct qcom_icc_node *pnoc_nodes[] = { + [MASTER_SNOC_PNOC] = &mas_snoc_pnoc, + [MASTER_SDCC_1] = &mas_sdcc_1, + [MASTER_SDCC_2] = &mas_sdcc_2, + [MASTER_SDCC_4] = &mas_sdcc_4, + [MASTER_USB_HS] = &mas_usb_hs, + [MASTER_BLSP_1] = &mas_blsp_1, + [MASTER_BLSP_2] = &mas_blsp_2, + [MASTER_TSIF] = &mas_tsif, + [SLAVE_PNOC_A1NOC] = &slv_pnoc_a1noc, + [SLAVE_USB_HS] = &slv_usb_hs, + [SLAVE_SDCC_2] = &slv_sdcc_2, + [SLAVE_SDCC_4] = &slv_sdcc_4, + [SLAVE_TSIF] = &slv_tsif, + [SLAVE_BLSP_2] = &slv_blsp_2, + [SLAVE_SDCC_1] = &slv_sdcc_1, + [SLAVE_BLSP_1] = &slv_blsp_1, + [SLAVE_PDM] = &slv_pdm, + [SLAVE_AHB2PHY] = &slv_ahb2phy +}; + +static const struct regmap_config msm8996_pnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x3000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_pnoc = { + .nodes = pnoc_nodes, + .num_nodes = ARRAY_SIZE(pnoc_nodes), + .regmap_cfg = &msm8996_pnoc_regmap_config +}; + +static struct qcom_icc_node *snoc_nodes[] = { + [MASTER_HMSS] = &mas_hmss, + [MASTER_QDSS_BAM] = &mas_qdss_bam, + [MASTER_SNOC_CFG] = &mas_snoc_cfg, + [MASTER_BIMC_SNOC_0] = &mas_bimc_snoc_0, + [MASTER_BIMC_SNOC_1] = &mas_bimc_snoc_1, + [MASTER_A0NOC_SNOC] = &mas_a0noc_snoc, + [MASTER_A1NOC_SNOC] = &mas_a1noc_snoc, + [MASTER_A2NOC_SNOC] = &mas_a2noc_snoc, + [MASTER_QDSS_ETR] = &mas_qdss_etr, + [SLAVE_A0NOC_SNOC] = &slv_a0noc_snoc, + [SLAVE_A1NOC_SNOC] = &slv_a1noc_snoc, + [SLAVE_A2NOC_SNOC] = &slv_a2noc_snoc, + [SLAVE_HMSS] = &slv_hmss, + [SLAVE_LPASS] = &slv_lpass, + [SLAVE_USB3] = &slv_usb3, + [SLAVE_SNOC_BIMC] = &slv_snoc_bimc, + [SLAVE_SNOC_CNOC] = &slv_snoc_cnoc, + [SLAVE_IMEM] = &slv_imem, + [SLAVE_PIMEM] = &slv_pimem, + [SLAVE_SNOC_VMEM] = &slv_snoc_vmem, + [SLAVE_SNOC_PNOC] = &slv_snoc_pnoc, + [SLAVE_QDSS_STM] = &slv_qdss_stm, + [SLAVE_PCIE_0] = &slv_pcie_0, + [SLAVE_PCIE_1] = &slv_pcie_1, + [SLAVE_PCIE_2] = &slv_pcie_2, + [SLAVE_SERVICE_SNOC] = &slv_srvc_snoc +}; + +static const struct regmap_config msm8996_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x20000, + .fast_io = true +}; + +static const struct qcom_icc_desc msm8996_snoc = { + .nodes = snoc_nodes, + .num_nodes = ARRAY_SIZE(snoc_nodes), + .regmap_cfg = &msm8996_snoc_regmap_config +}; + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,msm8996-a0noc", .data = &msm8996_a0noc}, + { .compatible = "qcom,msm8996-a1noc", .data = &msm8996_a1noc}, + { .compatible = "qcom,msm8996-a2noc", .data = &msm8996_a2noc}, + { .compatible = "qcom,msm8996-bimc", .data = &msm8996_bimc}, + { .compatible = "qcom,msm8996-cnoc", .data = &msm8996_cnoc}, + { .compatible = "qcom,msm8996-mnoc", .data = &msm8996_mnoc}, + { .compatible = "qcom,msm8996-pnoc", .data = &msm8996_pnoc}, + { .compatible = "qcom,msm8996-snoc", .data = &msm8996_snoc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-msm8996", + .of_match_table = qnoc_of_match, + .sync_state = icc_sync_state, + } +}; +module_platform_driver(qnoc_driver); + +MODULE_AUTHOR("Yassine Oudjana "); +MODULE_DESCRIPTION("Qualcomm MSM8996 NoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/interconnect/qcom/msm8996.h b/drivers/interconnect/qcom/msm8996.h new file mode 100644 index 000000000000..42b54ffcaa7b --- /dev/null +++ b/drivers/interconnect/qcom/msm8996.h @@ -0,0 +1,149 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Qualcomm MSM8996 interconnect IDs + * + * Copyright (c) 2021 Yassine Oudjana + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_MSM8996_H__ +#define __DRIVERS_INTERCONNECT_QCOM_MSM8996_H__ + +#define MSM8996_MASTER_PCIE_0 1 +#define MSM8996_MASTER_PCIE_1 2 +#define MSM8996_MASTER_PCIE_2 3 +#define MSM8996_MASTER_CNOC_A1NOC 4 +#define MSM8996_MASTER_CRYPTO_CORE0 5 +#define MSM8996_MASTER_PNOC_A1NOC 6 +#define MSM8996_MASTER_USB3 7 +#define MSM8996_MASTER_IPA 8 +#define MSM8996_MASTER_UFS 9 +#define MSM8996_MASTER_AMPSS_M0 10 +#define MSM8996_MASTER_GRAPHICS_3D 11 +#define MSM8996_MASTER_MNOC_BIMC 12 +#define MSM8996_MASTER_SNOC_BIMC 13 +#define MSM8996_MASTER_SNOC_CNOC 14 +#define MSM8996_MASTER_QDSS_DAP 15 +#define MSM8996_MASTER_CNOC_MNOC_MMSS_CFG 16 +#define MSM8996_MASTER_CNOC_MNOC_CFG 17 +#define MSM8996_MASTER_CPP 18 +#define MSM8996_MASTER_JPEG 19 +#define MSM8996_MASTER_MDP_PORT0 20 +#define MSM8996_MASTER_MDP_PORT1 21 +#define MSM8996_MASTER_ROTATOR 22 +#define MSM8996_MASTER_VIDEO_P0 23 +#define MSM8996_MASTER_VFE 24 +#define MSM8996_MASTER_SNOC_VMEM 25 +#define MSM8996_MASTER_VIDEO_P0_OCMEM 26 +#define MSM8996_MASTER_SNOC_PNOC 27 +#define MSM8996_MASTER_SDCC_1 28 +#define MSM8996_MASTER_SDCC_2 29 +#define MSM8996_MASTER_SDCC_4 30 +#define MSM8996_MASTER_USB_HS 31 +#define MSM8996_MASTER_BLSP_1 32 +#define MSM8996_MASTER_BLSP_2 33 +#define MSM8996_MASTER_TSIF 34 +#define MSM8996_MASTER_HMSS 35 +#define MSM8996_MASTER_QDSS_BAM 36 +#define MSM8996_MASTER_SNOC_CFG 37 +#define MSM8996_MASTER_BIMC_SNOC_0 38 +#define MSM8996_MASTER_BIMC_SNOC_1 39 +#define MSM8996_MASTER_A0NOC_SNOC 40 +#define MSM8996_MASTER_A1NOC_SNOC 41 +#define MSM8996_MASTER_A2NOC_SNOC 42 +#define MSM8996_MASTER_QDSS_ETR 43 + +#define MSM8996_SLAVE_A0NOC_SNOC 44 +#define MSM8996_SLAVE_A1NOC_SNOC 45 +#define MSM8996_SLAVE_A2NOC_SNOC 46 +#define MSM8996_SLAVE_EBI_CH0 47 +#define MSM8996_SLAVE_HMSS_L3 48 +#define MSM8996_SLAVE_BIMC_SNOC_0 49 +#define MSM8996_SLAVE_BIMC_SNOC_1 50 +#define MSM8996_SLAVE_CNOC_A1NOC 51 +#define MSM8996_SLAVE_CLK_CTL 52 +#define MSM8996_SLAVE_TCSR 53 +#define MSM8996_SLAVE_TLMM 54 +#define MSM8996_SLAVE_CRYPTO_0_CFG 55 +#define MSM8996_SLAVE_MPM 56 +#define MSM8996_SLAVE_PIMEM_CFG 57 +#define MSM8996_SLAVE_IMEM_CFG 58 +#define MSM8996_SLAVE_MESSAGE_RAM 59 +#define MSM8996_SLAVE_BIMC_CFG 60 +#define MSM8996_SLAVE_PMIC_ARB 61 +#define MSM8996_SLAVE_PRNG 62 +#define MSM8996_SLAVE_DCC_CFG 63 +#define MSM8996_SLAVE_RBCPR_MX 64 +#define MSM8996_SLAVE_QDSS_CFG 65 +#define MSM8996_SLAVE_RBCPR_CX 66 +#define MSM8996_SLAVE_QDSS_RBCPR_APU_CFG 67 +#define MSM8996_SLAVE_CNOC_MNOC_CFG 68 +#define MSM8996_SLAVE_SNOC_CFG 69 +#define MSM8996_SLAVE_SNOC_MPU_CFG 70 +#define MSM8996_SLAVE_EBI1_PHY_CFG 71 +#define MSM8996_SLAVE_A0NOC_CFG 72 +#define MSM8996_SLAVE_PCIE_1_CFG 73 +#define MSM8996_SLAVE_PCIE_2_CFG 74 +#define MSM8996_SLAVE_PCIE_0_CFG 75 +#define MSM8996_SLAVE_PCIE20_AHB2PHY 76 +#define MSM8996_SLAVE_A0NOC_MPU_CFG 77 +#define MSM8996_SLAVE_UFS_CFG 78 +#define MSM8996_SLAVE_A1NOC_CFG 79 +#define MSM8996_SLAVE_A1NOC_MPU_CFG 80 +#define MSM8996_SLAVE_A2NOC_CFG 81 +#define MSM8996_SLAVE_A2NOC_MPU_CFG 82 +#define MSM8996_SLAVE_SSC_CFG 83 +#define MSM8996_SLAVE_A0NOC_SMMU_CFG 84 +#define MSM8996_SLAVE_A1NOC_SMMU_CFG 85 +#define MSM8996_SLAVE_A2NOC_SMMU_CFG 86 +#define MSM8996_SLAVE_LPASS_SMMU_CFG 87 +#define MSM8996_SLAVE_CNOC_MNOC_MMSS_CFG 88 +#define MSM8996_SLAVE_MMAGIC_CFG 89 +#define MSM8996_SLAVE_CPR_CFG 90 +#define MSM8996_SLAVE_MISC_CFG 91 +#define MSM8996_SLAVE_VENUS_THROTTLE_CFG 92 +#define MSM8996_SLAVE_VENUS_CFG 93 +#define MSM8996_SLAVE_VMEM_CFG 94 +#define MSM8996_SLAVE_DSA_CFG 95 +#define MSM8996_SLAVE_MMSS_CLK_CFG 96 +#define MSM8996_SLAVE_DSA_MPU_CFG 97 +#define MSM8996_SLAVE_MNOC_MPU_CFG 98 +#define MSM8996_SLAVE_DISPLAY_CFG 99 +#define MSM8996_SLAVE_DISPLAY_THROTTLE_CFG 100 +#define MSM8996_SLAVE_CAMERA_CFG 101 +#define MSM8996_SLAVE_CAMERA_THROTTLE_CFG 102 +#define MSM8996_SLAVE_GRAPHICS_3D_CFG 103 +#define MSM8996_SLAVE_SMMU_MDP_CFG 104 +#define MSM8996_SLAVE_SMMU_ROTATOR_CFG 105 +#define MSM8996_SLAVE_SMMU_VENUS_CFG 106 +#define MSM8996_SLAVE_SMMU_CPP_CFG 107 +#define MSM8996_SLAVE_SMMU_JPEG_CFG 108 +#define MSM8996_SLAVE_SMMU_VFE_CFG 109 +#define MSM8996_SLAVE_MNOC_BIMC 110 +#define MSM8996_SLAVE_VMEM 111 +#define MSM8996_SLAVE_SERVICE_MNOC 112 +#define MSM8996_SLAVE_PNOC_A1NOC 113 +#define MSM8996_SLAVE_USB_HS 114 +#define MSM8996_SLAVE_SDCC_2 115 +#define MSM8996_SLAVE_SDCC_4 116 +#define MSM8996_SLAVE_TSIF 117 +#define MSM8996_SLAVE_BLSP_2 118 +#define MSM8996_SLAVE_SDCC_1 119 +#define MSM8996_SLAVE_BLSP_1 120 +#define MSM8996_SLAVE_PDM 121 +#define MSM8996_SLAVE_AHB2PHY 122 +#define MSM8996_SLAVE_APPSS 123 +#define MSM8996_SLAVE_LPASS 124 +#define MSM8996_SLAVE_USB3 125 +#define MSM8996_SLAVE_SNOC_BIMC 126 +#define MSM8996_SLAVE_SNOC_CNOC 127 +#define MSM8996_SLAVE_OCIMEM 128 +#define MSM8996_SLAVE_PIMEM 129 +#define MSM8996_SLAVE_SNOC_VMEM 130 +#define MSM8996_SLAVE_SNOC_PNOC 131 +#define MSM8996_SLAVE_QDSS_STM 132 +#define MSM8996_SLAVE_PCIE_0 133 +#define MSM8996_SLAVE_PCIE_1 134 +#define MSM8996_SLAVE_PCIE_2 135 +#define MSM8996_SLAVE_SERVICE_SNOC 136 + +#endif /* __DRIVERS_INTERCONNECT_QCOM_MSM8996_H__ */ -- cgit v1.2.3 From d3bc6269e21fc474763708e79c7a118740befb94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 26 Oct 2021 11:37:16 +0200 Subject: phy: bcm-ns-usb2: support updated DT binding with PHY reg space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Updated DT binding maps just a PHY's register space instead of the whole DMU block. Accessing a common CRU reg is handled using syscon & regmap. The old binding has been deprecated and remains supported as a fallback method. Signed-off-by: Rafał Miłecki Link: https://lore.kernel.org/r/20211026093716.5567-1-zajec5@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/phy-bcm-ns-usb2.c | 52 ++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c index 4b015b8a71c3..98d32729a45d 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb2.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c @@ -9,17 +9,23 @@ #include #include #include +#include #include #include #include #include #include +#include #include struct bcm_ns_usb2 { struct device *dev; struct clk *ref_clk; struct phy *phy; + struct regmap *clkset; + void __iomem *base; + + /* Deprecated binding */ void __iomem *dmu; }; @@ -27,7 +33,6 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) { struct bcm_ns_usb2 *usb2 = phy_get_drvdata(phy); struct device *dev = usb2->dev; - void __iomem *dmu = usb2->dmu; u32 ref_clk_rate, usb2ctl, usb_pll_ndiv, usb_pll_pdiv; int err = 0; @@ -44,7 +49,10 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) goto err_clk_off; } - usb2ctl = readl(dmu + BCMA_DMU_CRU_USB2_CONTROL); + if (usb2->base) + usb2ctl = readl(usb2->base); + else + usb2ctl = readl(usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL); if (usb2ctl & BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_PDIV_MASK) { usb_pll_pdiv = usb2ctl; @@ -58,15 +66,24 @@ static int bcm_ns_usb2_phy_init(struct phy *phy) usb_pll_ndiv = (1920000000 * usb_pll_pdiv) / ref_clk_rate; /* Unlock DMU PLL settings with some magic value */ - writel(0x0000ea68, dmu + BCMA_DMU_CRU_CLKSET_KEY); + if (usb2->clkset) + regmap_write(usb2->clkset, 0, 0x0000ea68); + else + writel(0x0000ea68, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY); /* Write USB 2.0 PLL control setting */ usb2ctl &= ~BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_MASK; usb2ctl |= usb_pll_ndiv << BCMA_DMU_CRU_USB2_CONTROL_USB_PLL_NDIV_SHIFT; - writel(usb2ctl, dmu + BCMA_DMU_CRU_USB2_CONTROL); + if (usb2->base) + writel(usb2ctl, usb2->base); + else + writel(usb2ctl, usb2->dmu + BCMA_DMU_CRU_USB2_CONTROL); /* Lock DMU PLL settings */ - writel(0x00000000, dmu + BCMA_DMU_CRU_CLKSET_KEY); + if (usb2->clkset) + regmap_write(usb2->clkset, 0, 0x00000000); + else + writel(0x00000000, usb2->dmu + BCMA_DMU_CRU_CLKSET_KEY); err_clk_off: clk_disable_unprepare(usb2->ref_clk); @@ -90,10 +107,27 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev) return -ENOMEM; usb2->dev = dev; - usb2->dmu = devm_platform_ioremap_resource_byname(pdev, "dmu"); - if (IS_ERR(usb2->dmu)) { - dev_err(dev, "Failed to map DMU regs\n"); - return PTR_ERR(usb2->dmu); + if (of_find_property(dev->of_node, "brcm,syscon-clkset", NULL)) { + usb2->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(usb2->base)) { + dev_err(dev, "Failed to map control reg\n"); + return PTR_ERR(usb2->base); + } + + usb2->clkset = syscon_regmap_lookup_by_phandle(dev->of_node, + "brcm,syscon-clkset"); + if (IS_ERR(usb2->clkset)) { + dev_err(dev, "Failed to lookup clkset regmap\n"); + return PTR_ERR(usb2->clkset); + } + } else { + usb2->dmu = devm_platform_ioremap_resource_byname(pdev, "dmu"); + if (IS_ERR(usb2->dmu)) { + dev_err(dev, "Failed to map DMU regs\n"); + return PTR_ERR(usb2->dmu); + } + + dev_warn(dev, "using deprecated DT binding\n"); } usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); -- cgit v1.2.3 From 26379667d26f33083484f0df814afec3a955b974 Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 3 Nov 2021 16:44:09 -0700 Subject: dt-bindings: phy: Introduce Qualcomm eDP PHY binding Introduce a binding for the eDP PHY hardware block found in several different Qualcomm platforms. Signed-off-by: Bjorn Andersson Reviewed-by: Stephen Boyd Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211103234410.1352424-1-bjorn.andersson@linaro.org Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/qcom,edp-phy.yaml | 67 ++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml new file mode 100644 index 000000000000..9076e19b6417 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/qcom,edp-phy.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) + +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/qcom,edp-phy.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Qualcomm eDP PHY + +maintainers: + - Bjorn Andersson + +description: + The Qualcomm eDP PHY is found in a number of Qualcomm platform and provides + the physical interface for Embedded Display Port. + +properties: + compatible: + const: qcom,sc8180x-edp-phy + + reg: + items: + - description: PHY base register block + - description: tx0 register block + - description: tx1 register block + - description: PLL register block + + clocks: + maxItems: 2 + + clock-names: + items: + - const: aux + - const: cfg_ahb + + "#clock-cells": + const: 1 + + "#phy-cells": + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - "#clock-cells" + - "#phy-cells" + +additionalProperties: false + +examples: + - | + phy@aec2a00 { + compatible = "qcom,sc8180x-edp-phy"; + reg = <0x0aec2a00 0x1c0>, + <0x0aec2200 0xa0>, + <0x0aec2600 0xa0>, + <0x0aec2000 0x19c>; + + clocks = <&dispcc 0>, <&dispcc 1>; + clock-names = "aux", "cfg_ahb"; + + #clock-cells = <1>; + #phy-cells = <0>; + }; +... -- cgit v1.2.3 From f199223cb490be108e3e44a6577fb76bc6ca8bbe Mon Sep 17 00:00:00 2001 From: Bjorn Andersson Date: Wed, 3 Nov 2021 16:44:10 -0700 Subject: phy: qcom: Introduce new eDP PHY driver Many recent Qualcomm platforms comes with native DP and eDP support. This consists of a controller in the MDSS and a QMP-like PHY. While similar to the well known QMP block, the eDP PHY only has TX lanes and the programming sequences are slightly different. Rather than continuing the trend of parameterize the QMP driver to pieces, this introduces the support as a new driver. The registration of link and pixel clocks are borrowed from the QMP driver. The non-DP link frequencies are omitted for now. The eDP PHY is very similar to the dedicated (non-USB) DP PHY, but only the prior is supported for now. Signed-off-by: Bjorn Andersson Reviewed-by: Stephen Boyd Link: https://lore.kernel.org/r/20211103234410.1352424-2-bjorn.andersson@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/Kconfig | 10 + drivers/phy/qualcomm/Makefile | 1 + drivers/phy/qualcomm/phy-qcom-edp.c | 674 ++++++++++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 1 + 4 files changed, 686 insertions(+) create mode 100644 drivers/phy/qualcomm/phy-qcom-edp.c diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 7f6fcb8ec5ba..5c98850f5a36 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -18,6 +18,16 @@ config PHY_QCOM_APQ8064_SATA depends on OF select GENERIC_PHY +config PHY_QCOM_EDP + tristate "Qualcomm eDP PHY driver" + depends on ARCH_QCOM || COMPILE_TEST + depends on OF + depends on COMMON_CLK + select GENERIC_PHY + help + Enable this driver to support the Qualcomm eDP PHY found in various + Qualcomm chipsets. + config PHY_QCOM_IPQ4019_USB tristate "Qualcomm IPQ4019 USB PHY driver" depends on OF && (ARCH_QCOM || COMPILE_TEST) diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index 47acbd7daa3a..e9e3b1a4dbb0 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_PHY_ATH79_USB) += phy-ath79-usb.o obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o +obj-$(CONFIG_PHY_QCOM_EDP) += phy-qcom-edp.o obj-$(CONFIG_PHY_QCOM_IPQ4019_USB) += phy-qcom-ipq4019-usb.o obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o obj-$(CONFIG_PHY_QCOM_PCIE2) += phy-qcom-pcie2.o diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c new file mode 100644 index 000000000000..17d5653b661d --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -0,0 +1,674 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2017, 2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "phy-qcom-qmp.h" + +/* EDP_PHY registers */ +#define DP_PHY_CFG 0x0010 +#define DP_PHY_CFG_1 0x0014 +#define DP_PHY_PD_CTL 0x001c +#define DP_PHY_MODE 0x0020 + +#define DP_PHY_AUX_CFG0 0x0024 +#define DP_PHY_AUX_CFG1 0x0028 +#define DP_PHY_AUX_CFG2 0x002C +#define DP_PHY_AUX_CFG3 0x0030 +#define DP_PHY_AUX_CFG4 0x0034 +#define DP_PHY_AUX_CFG5 0x0038 +#define DP_PHY_AUX_CFG6 0x003C +#define DP_PHY_AUX_CFG7 0x0040 +#define DP_PHY_AUX_CFG8 0x0044 +#define DP_PHY_AUX_CFG9 0x0048 + +#define DP_PHY_AUX_INTERRUPT_MASK 0x0058 + +#define DP_PHY_VCO_DIV 0x0074 +#define DP_PHY_TX0_TX1_LANE_CTL 0x007c +#define DP_PHY_TX2_TX3_LANE_CTL 0x00a0 + +#define DP_PHY_STATUS 0x00e0 + +/* LANE_TXn registers */ +#define TXn_CLKBUF_ENABLE 0x0000 +#define TXn_TX_EMP_POST1_LVL 0x0004 + +#define TXn_TX_DRV_LVL 0x0014 +#define TXn_TX_DRV_LVL_OFFSET 0x0018 +#define TXn_RESET_TSYNC_EN 0x001c +#define TXn_LDO_CONFIG 0x0084 +#define TXn_TX_BAND 0x0028 + +#define TXn_RES_CODE_LANE_OFFSET_TX0 0x0044 +#define TXn_RES_CODE_LANE_OFFSET_TX1 0x0048 + +#define TXn_TRANSCEIVER_BIAS_EN 0x0054 +#define TXn_HIGHZ_DRVR_EN 0x0058 +#define TXn_TX_POL_INV 0x005c +#define TXn_LANE_MODE_1 0x0064 + +#define TXn_TRAN_DRVR_EMP_EN 0x0078 + +struct qcom_edp { + struct device *dev; + + struct phy *phy; + + void __iomem *edp; + void __iomem *tx0; + void __iomem *tx1; + void __iomem *pll; + + struct clk_hw dp_link_hw; + struct clk_hw dp_pixel_hw; + + struct phy_configure_opts_dp dp_opts; + + struct clk_bulk_data clks[2]; + struct regulator_bulk_data supplies[2]; +}; + +static int qcom_edp_phy_init(struct phy *phy) +{ + struct qcom_edp *edp = phy_get_drvdata(phy); + int ret; + + ret = regulator_bulk_enable(ARRAY_SIZE(edp->supplies), edp->supplies); + if (ret) + return ret; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(edp->clks), edp->clks); + if (ret) + goto out_disable_supplies; + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + + /* Turn on BIAS current for PHY/PLL */ + writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + + writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL); + msleep(20); + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + + writel(0x00, edp->edp + DP_PHY_AUX_CFG0); + writel(0x13, edp->edp + DP_PHY_AUX_CFG1); + writel(0x24, edp->edp + DP_PHY_AUX_CFG2); + writel(0x00, edp->edp + DP_PHY_AUX_CFG3); + writel(0x0a, edp->edp + DP_PHY_AUX_CFG4); + writel(0x26, edp->edp + DP_PHY_AUX_CFG5); + writel(0x0a, edp->edp + DP_PHY_AUX_CFG6); + writel(0x03, edp->edp + DP_PHY_AUX_CFG7); + writel(0x37, edp->edp + DP_PHY_AUX_CFG8); + writel(0x03, edp->edp + DP_PHY_AUX_CFG9); + + writel(PHY_AUX_STOP_ERR_MASK | PHY_AUX_DEC_ERR_MASK | + PHY_AUX_SYNC_ERR_MASK | PHY_AUX_ALIGN_ERR_MASK | + PHY_AUX_REQ_ERR_MASK, edp->edp + DP_PHY_AUX_INTERRUPT_MASK); + + msleep(20); + + return 0; + +out_disable_supplies: + regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies); + + return ret; +} + +static int qcom_edp_phy_configure(struct phy *phy, union phy_configure_opts *opts) +{ + const struct phy_configure_opts_dp *dp_opts = &opts->dp; + struct qcom_edp *edp = phy_get_drvdata(phy); + + memcpy(&edp->dp_opts, dp_opts, sizeof(*dp_opts)); + + return 0; +} + +static int qcom_edp_configure_ssc(const struct qcom_edp *edp) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 step1; + u32 step2; + + switch (dp_opts->link_rate) { + case 1620: + case 2700: + case 8100: + step1 = 0x45; + step2 = 0x06; + break; + + case 5400: + step1 = 0x5c; + step2 = 0x08; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(0x01, edp->pll + QSERDES_V4_COM_SSC_EN_CENTER); + writel(0x00, edp->pll + QSERDES_V4_COM_SSC_ADJ_PER1); + writel(0x36, edp->pll + QSERDES_V4_COM_SSC_PER1); + writel(0x01, edp->pll + QSERDES_V4_COM_SSC_PER2); + writel(step1, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0); + writel(step2, edp->pll + QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0); + + return 0; +} + +static int qcom_edp_configure_pll(const struct qcom_edp *edp) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + u32 div_frac_start2_mode0; + u32 div_frac_start3_mode0; + u32 dec_start_mode0; + u32 lock_cmp1_mode0; + u32 lock_cmp2_mode0; + u32 hsclk_sel; + + switch (dp_opts->link_rate) { + case 1620: + hsclk_sel = 0x5; + dec_start_mode0 = 0x69; + div_frac_start2_mode0 = 0x80; + div_frac_start3_mode0 = 0x07; + lock_cmp1_mode0 = 0x6f; + lock_cmp2_mode0 = 0x08; + break; + + case 2700: + hsclk_sel = 0x3; + dec_start_mode0 = 0x69; + div_frac_start2_mode0 = 0x80; + div_frac_start3_mode0 = 0x07; + lock_cmp1_mode0 = 0x0f; + lock_cmp2_mode0 = 0x0e; + break; + + case 5400: + hsclk_sel = 0x1; + dec_start_mode0 = 0x8c; + div_frac_start2_mode0 = 0x00; + div_frac_start3_mode0 = 0x0a; + lock_cmp1_mode0 = 0x1f; + lock_cmp2_mode0 = 0x1c; + break; + + case 8100: + hsclk_sel = 0x0; + dec_start_mode0 = 0x69; + div_frac_start2_mode0 = 0x80; + div_frac_start3_mode0 = 0x07; + lock_cmp1_mode0 = 0x2f; + lock_cmp2_mode0 = 0x2a; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(0x01, edp->pll + QSERDES_V4_COM_SVS_MODE_CLK_SEL); + writel(0x0b, edp->pll + QSERDES_V4_COM_SYSCLK_EN_SEL); + writel(0x02, edp->pll + QSERDES_V4_COM_SYS_CLK_CTRL); + writel(0x0c, edp->pll + QSERDES_V4_COM_CLK_ENABLE1); + writel(0x06, edp->pll + QSERDES_V4_COM_SYSCLK_BUF_ENABLE); + writel(0x30, edp->pll + QSERDES_V4_COM_CLK_SELECT); + writel(hsclk_sel, edp->pll + QSERDES_V4_COM_HSCLK_SEL); + writel(0x0f, edp->pll + QSERDES_V4_COM_PLL_IVCO); + writel(0x08, edp->pll + QSERDES_V4_COM_LOCK_CMP_EN); + writel(0x36, edp->pll + QSERDES_V4_COM_PLL_CCTRL_MODE0); + writel(0x16, edp->pll + QSERDES_V4_COM_PLL_RCTRL_MODE0); + writel(0x06, edp->pll + QSERDES_V4_COM_CP_CTRL_MODE0); + writel(dec_start_mode0, edp->pll + QSERDES_V4_COM_DEC_START_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_DIV_FRAC_START1_MODE0); + writel(div_frac_start2_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START2_MODE0); + writel(div_frac_start3_mode0, edp->pll + QSERDES_V4_COM_DIV_FRAC_START3_MODE0); + writel(0x02, edp->pll + QSERDES_V4_COM_CMN_CONFIG); + writel(0x3f, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN0_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_INTEGLOOP_GAIN1_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_MAP); + writel(lock_cmp1_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP1_MODE0); + writel(lock_cmp2_mode0, edp->pll + QSERDES_V4_COM_LOCK_CMP2_MODE0); + + writel(0x0a, edp->pll + QSERDES_V4_COM_BG_TIMER); + writel(0x14, edp->pll + QSERDES_V4_COM_CORECLK_DIV_MODE0); + writel(0x00, edp->pll + QSERDES_V4_COM_VCO_TUNE_CTRL); + writel(0x17, edp->pll + QSERDES_V4_COM_BIAS_EN_CLKBUFLR_EN); + writel(0x0f, edp->pll + QSERDES_V4_COM_CORE_CLK_EN); + writel(0xa0, edp->pll + QSERDES_V4_COM_VCO_TUNE1_MODE0); + writel(0x03, edp->pll + QSERDES_V4_COM_VCO_TUNE2_MODE0); + + return 0; +} + +static int qcom_edp_set_vco_div(const struct qcom_edp *edp) +{ + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + unsigned long pixel_freq; + u32 vco_div; + + switch (dp_opts->link_rate) { + case 1620: + vco_div = 0x1; + pixel_freq = 1620000000UL / 2; + break; + + case 2700: + vco_div = 0x1; + pixel_freq = 2700000000UL / 2; + break; + + case 5400: + vco_div = 0x2; + pixel_freq = 5400000000UL / 4; + break; + + case 8100: + vco_div = 0x0; + pixel_freq = 8100000000UL / 6; + break; + + default: + /* Other link rates aren't supported */ + return -EINVAL; + } + + writel(vco_div, edp->edp + DP_PHY_VCO_DIV); + + clk_set_rate(edp->dp_link_hw.clk, dp_opts->link_rate * 100000); + clk_set_rate(edp->dp_pixel_hw.clk, pixel_freq); + + return 0; +} + +static int qcom_edp_phy_power_on(struct phy *phy) +{ + const struct qcom_edp *edp = phy_get_drvdata(phy); + int timeout; + int ret; + u32 val; + + writel(DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | + DP_PHY_PD_CTL_LANE_0_1_PWRDN | DP_PHY_PD_CTL_LANE_2_3_PWRDN | + DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN, + edp->edp + DP_PHY_PD_CTL); + writel(0xfc, edp->edp + DP_PHY_MODE); + + timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_CMN_STATUS, + val, val & BIT(7), 5, 200); + if (timeout) + return timeout; + + writel(0x01, edp->tx0 + TXn_LDO_CONFIG); + writel(0x01, edp->tx1 + TXn_LDO_CONFIG); + writel(0x00, edp->tx0 + TXn_LANE_MODE_1); + writel(0x00, edp->tx1 + TXn_LANE_MODE_1); + + ret = qcom_edp_configure_ssc(edp); + if (ret) + return ret; + + ret = qcom_edp_configure_pll(edp); + if (ret) + return ret; + + /* TX Lane configuration */ + writel(0x05, edp->edp + DP_PHY_TX0_TX1_LANE_CTL); + writel(0x05, edp->edp + DP_PHY_TX2_TX3_LANE_CTL); + + /* TX-0 register configuration */ + writel(0x03, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN); + writel(0x0f, edp->tx0 + TXn_CLKBUF_ENABLE); + writel(0x03, edp->tx0 + TXn_RESET_TSYNC_EN); + writel(0x01, edp->tx0 + TXn_TRAN_DRVR_EMP_EN); + writel(0x04, edp->tx0 + TXn_TX_BAND); + + /* TX-1 register configuration */ + writel(0x03, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN); + writel(0x0f, edp->tx1 + TXn_CLKBUF_ENABLE); + writel(0x03, edp->tx1 + TXn_RESET_TSYNC_EN); + writel(0x01, edp->tx1 + TXn_TRAN_DRVR_EMP_EN); + writel(0x04, edp->tx1 + TXn_TX_BAND); + + ret = qcom_edp_set_vco_div(edp); + if (ret) + return ret; + + writel(0x01, edp->edp + DP_PHY_CFG); + writel(0x05, edp->edp + DP_PHY_CFG); + writel(0x01, edp->edp + DP_PHY_CFG); + writel(0x09, edp->edp + DP_PHY_CFG); + + writel(0x20, edp->pll + QSERDES_V4_COM_RESETSM_CNTRL); + + timeout = readl_poll_timeout(edp->pll + QSERDES_V4_COM_C_READY_STATUS, + val, val & BIT(0), 500, 10000); + if (timeout) + return timeout; + + writel(0x19, edp->edp + DP_PHY_CFG); + writel(0x1f, edp->tx0 + TXn_HIGHZ_DRVR_EN); + writel(0x04, edp->tx0 + TXn_HIGHZ_DRVR_EN); + writel(0x00, edp->tx0 + TXn_TX_POL_INV); + writel(0x1f, edp->tx1 + TXn_HIGHZ_DRVR_EN); + writel(0x04, edp->tx1 + TXn_HIGHZ_DRVR_EN); + writel(0x00, edp->tx1 + TXn_TX_POL_INV); + writel(0x10, edp->tx0 + TXn_TX_DRV_LVL_OFFSET); + writel(0x10, edp->tx1 + TXn_TX_DRV_LVL_OFFSET); + writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX0); + writel(0x11, edp->tx0 + TXn_RES_CODE_LANE_OFFSET_TX1); + writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX0); + writel(0x11, edp->tx1 + TXn_RES_CODE_LANE_OFFSET_TX1); + + writel(0x10, edp->tx0 + TXn_TX_EMP_POST1_LVL); + writel(0x10, edp->tx1 + TXn_TX_EMP_POST1_LVL); + writel(0x1f, edp->tx0 + TXn_TX_DRV_LVL); + writel(0x1f, edp->tx1 + TXn_TX_DRV_LVL); + + writel(0x4, edp->tx0 + TXn_HIGHZ_DRVR_EN); + writel(0x3, edp->tx0 + TXn_TRANSCEIVER_BIAS_EN); + writel(0x4, edp->tx1 + TXn_HIGHZ_DRVR_EN); + writel(0x0, edp->tx1 + TXn_TRANSCEIVER_BIAS_EN); + writel(0x3, edp->edp + DP_PHY_CFG_1); + + writel(0x18, edp->edp + DP_PHY_CFG); + usleep_range(100, 1000); + + writel(0x19, edp->edp + DP_PHY_CFG); + + return readl_poll_timeout(edp->edp + DP_PHY_STATUS, + val, val & BIT(1), 500, 10000); +} + +static int qcom_edp_phy_power_off(struct phy *phy) +{ + const struct qcom_edp *edp = phy_get_drvdata(phy); + + writel(DP_PHY_PD_CTL_PSR_PWRDN, edp->edp + DP_PHY_PD_CTL); + + return 0; +} + +static int qcom_edp_phy_exit(struct phy *phy) +{ + struct qcom_edp *edp = phy_get_drvdata(phy); + + clk_bulk_disable_unprepare(ARRAY_SIZE(edp->clks), edp->clks); + regulator_bulk_disable(ARRAY_SIZE(edp->supplies), edp->supplies); + + return 0; +} + +static const struct phy_ops qcom_edp_ops = { + .init = qcom_edp_phy_init, + .configure = qcom_edp_phy_configure, + .power_on = qcom_edp_phy_power_on, + .power_off = qcom_edp_phy_power_off, + .exit = qcom_edp_phy_exit, + .owner = THIS_MODULE, +}; + +/* + * Embedded Display Port PLL driver block diagram for branch clocks + * + * +------------------------------+ + * | EDP_VCO_CLK | + * | | + * | +-------------------+ | + * | | (EDP PLL/VCO) | | + * | +---------+---------+ | + * | v | + * | +----------+-----------+ | + * | | hsclk_divsel_clk_src | | + * | +----------+-----------+ | + * +------------------------------+ + * | + * +---------<---------v------------>----------+ + * | | + * +--------v----------------+ | + * | edp_phy_pll_link_clk | | + * | link_clk | | + * +--------+----------------+ | + * | | + * | | + * v v + * Input to DISPCC block | + * for link clk, crypto clk | + * and interface clock | + * | + * | + * +--------<------------+-----------------+---<---+ + * | | | + * +----v---------+ +--------v-----+ +--------v------+ + * | vco_divided | | vco_divided | | vco_divided | + * | _clk_src | | _clk_src | | _clk_src | + * | | | | | | + * |divsel_six | | divsel_two | | divsel_four | + * +-------+------+ +-----+--------+ +--------+------+ + * | | | + * v---->----------v-------------<------v + * | + * +----------+-----------------+ + * | edp_phy_pll_vco_div_clk | + * +---------+------------------+ + * | + * v + * Input to DISPCC block + * for EDP pixel clock + * + */ +static int qcom_edp_dp_pixel_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + switch (req->rate) { + case 1620000000UL / 2: + case 2700000000UL / 2: + /* 5.4 and 8.1 GHz are same link rate as 2.7GHz, i.e. div 4 and div 6 */ + return 0; + + default: + return -EINVAL; + } +} + +static unsigned long +qcom_edp_dp_pixel_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_pixel_hw); + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + + switch (dp_opts->link_rate) { + case 1620: + return 1620000000UL / 2; + case 2700: + return 2700000000UL / 2; + case 5400: + return 5400000000UL / 4; + case 8100: + return 8100000000UL / 6; + default: + return 0; + } +} + +static const struct clk_ops qcom_edp_dp_pixel_clk_ops = { + .determine_rate = qcom_edp_dp_pixel_clk_determine_rate, + .recalc_rate = qcom_edp_dp_pixel_clk_recalc_rate, +}; + +static int qcom_edp_dp_link_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) +{ + switch (req->rate) { + case 162000000: + case 270000000: + case 540000000: + case 810000000: + return 0; + + default: + return -EINVAL; + } +} + +static unsigned long +qcom_edp_dp_link_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) +{ + const struct qcom_edp *edp = container_of(hw, struct qcom_edp, dp_link_hw); + const struct phy_configure_opts_dp *dp_opts = &edp->dp_opts; + + switch (dp_opts->link_rate) { + case 1620: + case 2700: + case 5400: + case 8100: + return dp_opts->link_rate * 100000; + + default: + return 0; + } +} + +static const struct clk_ops qcom_edp_dp_link_clk_ops = { + .determine_rate = qcom_edp_dp_link_clk_determine_rate, + .recalc_rate = qcom_edp_dp_link_clk_recalc_rate, +}; + +static int qcom_edp_clks_register(struct qcom_edp *edp, struct device_node *np) +{ + struct clk_hw_onecell_data *data; + struct clk_init_data init = { }; + int ret; + + data = devm_kzalloc(edp->dev, sizeof(data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + init.ops = &qcom_edp_dp_link_clk_ops; + init.name = "edp_phy_pll_link_clk"; + edp->dp_link_hw.init = &init; + ret = devm_clk_hw_register(edp->dev, &edp->dp_link_hw); + if (ret) + return ret; + + init.ops = &qcom_edp_dp_pixel_clk_ops; + init.name = "edp_phy_pll_vco_div_clk"; + edp->dp_pixel_hw.init = &init; + ret = devm_clk_hw_register(edp->dev, &edp->dp_pixel_hw); + if (ret) + return ret; + + data->hws[0] = &edp->dp_link_hw; + data->hws[1] = &edp->dp_pixel_hw; + data->num = 2; + + return devm_of_clk_add_hw_provider(edp->dev, of_clk_hw_onecell_get, data); +} + +static int qcom_edp_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct qcom_edp *edp; + int ret; + + edp = devm_kzalloc(dev, sizeof(*edp), GFP_KERNEL); + if (!edp) + return -ENOMEM; + + edp->dev = dev; + + edp->edp = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(edp->edp)) + return PTR_ERR(edp->edp); + + edp->tx0 = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(edp->tx0)) + return PTR_ERR(edp->tx0); + + edp->tx1 = devm_platform_ioremap_resource(pdev, 2); + if (IS_ERR(edp->tx1)) + return PTR_ERR(edp->tx1); + + edp->pll = devm_platform_ioremap_resource(pdev, 3); + if (IS_ERR(edp->pll)) + return PTR_ERR(edp->pll); + + edp->clks[0].id = "aux"; + edp->clks[1].id = "cfg_ahb"; + ret = devm_clk_bulk_get(dev, ARRAY_SIZE(edp->clks), edp->clks); + if (ret) + return ret; + + edp->supplies[0].supply = "vdda-phy"; + edp->supplies[1].supply = "vdda-pll"; + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(edp->supplies), edp->supplies); + if (ret) + return ret; + + ret = qcom_edp_clks_register(edp, pdev->dev.of_node); + if (ret) + return ret; + + edp->phy = devm_phy_create(dev, pdev->dev.of_node, &qcom_edp_ops); + if (IS_ERR(edp->phy)) { + dev_err(dev, "failed to register phy\n"); + return PTR_ERR(edp->phy); + } + + phy_set_drvdata(edp->phy, edp); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id qcom_edp_phy_match_table[] = { + { .compatible = "qcom,sc8180x-edp-phy" }, + { } +}; +MODULE_DEVICE_TABLE(of, qcom_edp_phy_match_table); + +static struct platform_driver qcom_edp_phy_driver = { + .probe = qcom_edp_phy_probe, + .driver = { + .name = "qcom-edp-phy", + .of_match_table = qcom_edp_phy_match_table, + }, +}; + +module_platform_driver(qcom_edp_phy_driver); + +MODULE_AUTHOR("Bjorn Andersson "); +MODULE_DESCRIPTION("Qualcomm eDP QMP PHY driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index e15f461065bb..3d123fbe42d2 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -551,6 +551,7 @@ /* Only for QMP V4 PHY - QSERDES COM registers */ #define QSERDES_V4_COM_BG_TIMER 0x00c #define QSERDES_V4_COM_SSC_EN_CENTER 0x010 +#define QSERDES_V4_COM_SSC_ADJ_PER1 0x014 #define QSERDES_V4_COM_SSC_PER1 0x01c #define QSERDES_V4_COM_SSC_PER2 0x020 #define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0 0x024 -- cgit v1.2.3 From 3870a48cd10c216213417e9929d9ffa85edb6ed5 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Wed, 20 Oct 2021 21:51:06 +0200 Subject: dt-bindings: phy: Add the Amlogic Meson8 HDMI TX PHY bindings Amlogic Meson8, Meson8b and Meson8m2 all include an identical (or at least very similar) HDMI TX PHY. The PHY registers are part of the HHI register area. Signed-off-by: Martin Blumenstingl Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211020195107.1564533-2-martin.blumenstingl@googlemail.com Signed-off-by: Vinod Koul --- .../bindings/phy/amlogic,meson8-hdmi-tx-phy.yaml | 65 ++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/amlogic,meson8-hdmi-tx-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson8-hdmi-tx-phy.yaml b/Documentation/devicetree/bindings/phy/amlogic,meson8-hdmi-tx-phy.yaml new file mode 100644 index 000000000000..1f085cdd1c85 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/amlogic,meson8-hdmi-tx-phy.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/amlogic,meson8-hdmi-tx-phy.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Amlogic Meson8, Meson8b and Meson8m2 HDMI TX PHY + +maintainers: + - Martin Blumenstingl + +description: |+ + The HDMI TX PHY node should be the child of a syscon node with the + required property: + + compatible = "amlogic,meson-hhi-sysctrl", "simple-mfd", "syscon" + + Refer to the bindings described in + Documentation/devicetree/bindings/mfd/syscon.yaml + +properties: + $nodename: + pattern: "^hdmi-phy@[0-9a-f]+$" + + compatible: + oneOf: + - items: + - enum: + - amlogic,meson8b-hdmi-tx-phy + - amlogic,meson8m2-hdmi-tx-phy + - const: amlogic,meson8-hdmi-tx-phy + - const: amlogic,meson8-hdmi-tx-phy + + reg: + maxItems: 1 + + clocks: + minItems: 1 + description: + HDMI TMDS clock + + "#phy-cells": + const: 0 + +required: + - compatible + - "#phy-cells" + +additionalProperties: false + +examples: + - | + hdmi-phy@3a0 { + compatible = "amlogic,meson8-hdmi-tx-phy"; + reg = <0x3a0 0xc>; + clocks = <&tmds_clock>; + #phy-cells = <0>; + }; + - | + hdmi-phy@3a0 { + compatible = "amlogic,meson8b-hdmi-tx-phy", "amlogic,meson8-hdmi-tx-phy"; + reg = <0x3a0 0xc>; + clocks = <&tmds_clock>; + #phy-cells = <0>; + }; -- cgit v1.2.3 From e45dbd3a4b1111f622edcbbec00adae81659eba7 Mon Sep 17 00:00:00 2001 From: Martin Blumenstingl Date: Wed, 20 Oct 2021 21:51:07 +0200 Subject: phy: amlogic: Add a new driver for the HDMI TX PHY on Meson8/8b/8m2 Amlogic Meson8/8b/8m2 have a built-in HDMI PHY in the HHI register region. Unfortunately only few register bits are documented. For HHI_HDMI_PHY_CNTL0 the magic numbers are taken from the 3.10 vendor kernel. Signed-off-by: Martin Blumenstingl Link: https://lore.kernel.org/r/20211020195107.1564533-3-martin.blumenstingl@googlemail.com Signed-off-by: Vinod Koul --- drivers/phy/amlogic/Kconfig | 10 ++ drivers/phy/amlogic/Makefile | 1 + drivers/phy/amlogic/phy-meson8-hdmi-tx.c | 160 +++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+) create mode 100644 drivers/phy/amlogic/phy-meson8-hdmi-tx.c diff --git a/drivers/phy/amlogic/Kconfig b/drivers/phy/amlogic/Kconfig index db5d0cd757e3..486ca23aba32 100644 --- a/drivers/phy/amlogic/Kconfig +++ b/drivers/phy/amlogic/Kconfig @@ -2,6 +2,16 @@ # # Phy drivers for Amlogic platforms # +config PHY_MESON8_HDMI_TX + tristate "Meson8, Meson8b and Meson8m2 HDMI TX PHY driver" + depends on (ARCH_MESON && ARM) || COMPILE_TEST + depends on OF + select MFD_SYSCON + help + Enable this to support the HDMI TX PHYs found in Meson8, + Meson8b and Meson8m2 SoCs. + If unsure, say N. + config PHY_MESON8B_USB2 tristate "Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver" default ARCH_MESON diff --git a/drivers/phy/amlogic/Makefile b/drivers/phy/amlogic/Makefile index 8fa07fbd0d92..c0886c850bb0 100644 --- a/drivers/phy/amlogic/Makefile +++ b/drivers/phy/amlogic/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_PHY_MESON8_HDMI_TX) += phy-meson8-hdmi-tx.o obj-$(CONFIG_PHY_MESON8B_USB2) += phy-meson8b-usb2.o obj-$(CONFIG_PHY_MESON_GXL_USB2) += phy-meson-gxl-usb2.o obj-$(CONFIG_PHY_MESON_G12A_USB2) += phy-meson-g12a-usb2.o diff --git a/drivers/phy/amlogic/phy-meson8-hdmi-tx.c b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c new file mode 100644 index 000000000000..f9a6572c27d8 --- /dev/null +++ b/drivers/phy/amlogic/phy-meson8-hdmi-tx.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Meson8, Meson8b and Meson8m2 HDMI TX PHY. + * + * Copyright (C) 2021 Martin Blumenstingl + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Unfortunately there is no detailed documentation available for the + * HHI_HDMI_PHY_CNTL0 register. CTL0 and CTL1 is all we know about. + * Magic register values in the driver below are taken from the vendor + * BSP / kernel. + */ +#define HHI_HDMI_PHY_CNTL0 0x3a0 + #define HHI_HDMI_PHY_CNTL0_HDMI_CTL1 GENMASK(31, 16) + #define HHI_HDMI_PHY_CNTL0_HDMI_CTL0 GENMASK(15, 0) + +#define HHI_HDMI_PHY_CNTL1 0x3a4 + #define HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE BIT(1) + #define HHI_HDMI_PHY_CNTL1_SOFT_RESET BIT(0) + +#define HHI_HDMI_PHY_CNTL2 0x3a8 + +struct phy_meson8_hdmi_tx_priv { + struct regmap *hhi; + struct clk *tmds_clk; +}; + +static int phy_meson8_hdmi_tx_init(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + + return clk_prepare_enable(priv->tmds_clk); +} + +static int phy_meson8_hdmi_tx_exit(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + + clk_disable_unprepare(priv->tmds_clk); + + return 0; +} + +static int phy_meson8_hdmi_tx_power_on(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + unsigned int i; + u16 hdmi_ctl0; + + if (clk_get_rate(priv->tmds_clk) >= 2970UL * 1000 * 1000) + hdmi_ctl0 = 0x1e8b; + else + hdmi_ctl0 = 0x4d0b; + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x08c3) | + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, hdmi_ctl0)); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, 0x0); + + /* Reset three times, just like the vendor driver does */ + for (i = 0; i < 3; i++) { + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, + HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE | + HHI_HDMI_PHY_CNTL1_SOFT_RESET); + usleep_range(1000, 2000); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL1, + HHI_HDMI_PHY_CNTL1_CLOCK_ENABLE); + usleep_range(1000, 2000); + } + + return 0; +} + +static int phy_meson8_hdmi_tx_power_off(struct phy *phy) +{ + struct phy_meson8_hdmi_tx_priv *priv = phy_get_drvdata(phy); + + regmap_write(priv->hhi, HHI_HDMI_PHY_CNTL0, + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL1, 0x0841) | + FIELD_PREP(HHI_HDMI_PHY_CNTL0_HDMI_CTL0, 0x8d00)); + + return 0; +} + +static const struct phy_ops phy_meson8_hdmi_tx_ops = { + .init = phy_meson8_hdmi_tx_init, + .exit = phy_meson8_hdmi_tx_exit, + .power_on = phy_meson8_hdmi_tx_power_on, + .power_off = phy_meson8_hdmi_tx_power_off, + .owner = THIS_MODULE, +}; + +static int phy_meson8_hdmi_tx_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct phy_meson8_hdmi_tx_priv *priv; + struct phy_provider *phy_provider; + struct resource *res; + struct phy *phy; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -EINVAL; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->hhi = syscon_node_to_regmap(np->parent); + if (IS_ERR(priv->hhi)) + return PTR_ERR(priv->hhi); + + priv->tmds_clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(priv->tmds_clk)) + return PTR_ERR(priv->tmds_clk); + + phy = devm_phy_create(&pdev->dev, np, &phy_meson8_hdmi_tx_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + phy_set_drvdata(phy, priv); + + phy_provider = devm_of_phy_provider_register(&pdev->dev, + of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id phy_meson8_hdmi_tx_of_match[] = { + { .compatible = "amlogic,meson8-hdmi-tx-phy" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, phy_meson8_hdmi_tx_of_match); + +static struct platform_driver phy_meson8_hdmi_tx_driver = { + .probe = phy_meson8_hdmi_tx_probe, + .driver = { + .name = "phy-meson8-hdmi-tx", + .of_match_table = phy_meson8_hdmi_tx_of_match, + }, +}; +module_platform_driver(phy_meson8_hdmi_tx_driver); + +MODULE_AUTHOR("Martin Blumenstingl "); +MODULE_DESCRIPTION("Meson8, Meson8b and Meson8m2 HDMI TX PHY driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 5c2ecfce44b28aefeac4053a3680ae3a46e57f39 Mon Sep 17 00:00:00 2001 From: David Heidelberg Date: Sun, 31 Oct 2021 12:32:36 +0100 Subject: dt-bindings: phy: Tegra194 P2U convert to YAML Convert Tegra194 P2U binding to the YAML format. Signed-off-by: David Heidelberg Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211031113236.29712-1-david@ixit.cz Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/phy-tegra194-p2u.txt | 28 -------------- .../devicetree/bindings/phy/phy-tegra194-p2u.yaml | 44 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 28 deletions(-) delete mode 100644 Documentation/devicetree/bindings/phy/phy-tegra194-p2u.txt create mode 100644 Documentation/devicetree/bindings/phy/phy-tegra194-p2u.yaml diff --git a/Documentation/devicetree/bindings/phy/phy-tegra194-p2u.txt b/Documentation/devicetree/bindings/phy/phy-tegra194-p2u.txt deleted file mode 100644 index d23ff90baad5..000000000000 --- a/Documentation/devicetree/bindings/phy/phy-tegra194-p2u.txt +++ /dev/null @@ -1,28 +0,0 @@ -NVIDIA Tegra194 P2U binding - -Tegra194 has two PHY bricks namely HSIO (High Speed IO) and NVHS (NVIDIA High -Speed) each interfacing with 12 and 8 P2U instances respectively. -A P2U instance is a glue logic between Synopsys DesignWare Core PCIe IP's PIPE -interface and PHY of HSIO/NVHS bricks. Each P2U instance represents one PCIe -lane. - -Required properties: -- compatible: For Tegra19x, must contain "nvidia,tegra194-p2u". -- reg: Should be the physical address space and length of respective each P2U - instance. -- reg-names: Must include the entry "ctl". - -Required properties for PHY port node: -- #phy-cells: Defined by generic PHY bindings. Must be 0. - -Refer to phy/phy-bindings.txt for the generic PHY binding properties. - -Example: - -p2u_hsio_0: phy@3e10000 { - compatible = "nvidia,tegra194-p2u"; - reg = <0x03e10000 0x10000>; - reg-names = "ctl"; - - #phy-cells = <0>; -}; diff --git a/Documentation/devicetree/bindings/phy/phy-tegra194-p2u.yaml b/Documentation/devicetree/bindings/phy/phy-tegra194-p2u.yaml new file mode 100644 index 000000000000..9a89d05efbda --- /dev/null +++ b/Documentation/devicetree/bindings/phy/phy-tegra194-p2u.yaml @@ -0,0 +1,44 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/phy/phy-tegra194-p2u.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: NVIDIA Tegra194 P2U binding + +maintainers: + - Thierry Reding + +description: > + Tegra194 has two PHY bricks namely HSIO (High Speed IO) and NVHS (NVIDIA High + Speed) each interfacing with 12 and 8 P2U instances respectively. + A P2U instance is a glue logic between Synopsys DesignWare Core PCIe IP's PIPE + interface and PHY of HSIO/NVHS bricks. Each P2U instance represents one PCIe + lane. + +properties: + compatible: + const: nvidia,tegra194-p2u + + reg: + maxItems: 1 + description: Should be the physical address space and length of respective each P2U instance. + + reg-names: + items: + - const: ctl + + '#phy-cells': + const: 0 + +additionalProperties: false + +examples: + - | + p2u_hsio_0: phy@3e10000 { + compatible = "nvidia,tegra194-p2u"; + reg = <0x03e10000 0x10000>; + reg-names = "ctl"; + + #phy-cells = <0>; + }; -- cgit v1.2.3 From d0cfb865b363e6de30295348fca20d9c6810a0dc Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:00 +0900 Subject: dt-bindings: phy: uniphier-usb3: Add bindings for NX1 SoC Update USB3-PHY binding document for UniPhier NX1 SoC. Add SS-PHY and HS-PHY compatible strings for the SoC to the document. Signed-off-by: Kunihiko Hayashi Acked-by: Rob Herring Link: https://lore.kernel.org/r/1635503947-18250-2-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml | 1 + Documentation/devicetree/bindings/phy/socionext,uniphier-usb3ss-phy.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml index a681cbc3b4ef..33946efcac5e 100644 --- a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml +++ b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3hs-phy.yaml @@ -22,6 +22,7 @@ properties: - socionext,uniphier-pxs2-usb3-hsphy - socionext,uniphier-ld20-usb3-hsphy - socionext,uniphier-pxs3-usb3-hsphy + - socionext,uniphier-nx1-usb3-hsphy reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3ss-phy.yaml b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3ss-phy.yaml index 41c0dd68ee25..92d46eb913a3 100644 --- a/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3ss-phy.yaml +++ b/Documentation/devicetree/bindings/phy/socionext,uniphier-usb3ss-phy.yaml @@ -23,6 +23,7 @@ properties: - socionext,uniphier-pxs2-usb3-ssphy - socionext,uniphier-ld20-usb3-ssphy - socionext,uniphier-pxs3-usb3-ssphy + - socionext,uniphier-nx1-usb3-ssphy reg: maxItems: 1 -- cgit v1.2.3 From 877e8d28bc840f1240852280850cdea5d97c1151 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:01 +0900 Subject: phy: uniphier-usb3: Add compatible string for NX1 SoC Add basic support for UniPhier NX1 SoC. This includes a compatible string and the same SoC-dependent data as LD20/PXs3 SoCs. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1635503947-18250-3-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- drivers/phy/socionext/phy-uniphier-usb3hs.c | 4 ++++ drivers/phy/socionext/phy-uniphier-usb3ss.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/drivers/phy/socionext/phy-uniphier-usb3hs.c b/drivers/phy/socionext/phy-uniphier-usb3hs.c index a9bc74121f38..8c8673df0084 100644 --- a/drivers/phy/socionext/phy-uniphier-usb3hs.c +++ b/drivers/phy/socionext/phy-uniphier-usb3hs.c @@ -447,6 +447,10 @@ static const struct of_device_id uniphier_u3hsphy_match[] = { .compatible = "socionext,uniphier-pxs3-usb3-hsphy", .data = &uniphier_pxs3_data, }, + { + .compatible = "socionext,uniphier-nx1-usb3-hsphy", + .data = &uniphier_pxs3_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, uniphier_u3hsphy_match); diff --git a/drivers/phy/socionext/phy-uniphier-usb3ss.c b/drivers/phy/socionext/phy-uniphier-usb3ss.c index 6700645bcbe6..7ce611c2088b 100644 --- a/drivers/phy/socionext/phy-uniphier-usb3ss.c +++ b/drivers/phy/socionext/phy-uniphier-usb3ss.c @@ -328,6 +328,10 @@ static const struct of_device_id uniphier_u3ssphy_match[] = { .compatible = "socionext,uniphier-pxs3-usb3-ssphy", .data = &uniphier_ld20_data, }, + { + .compatible = "socionext,uniphier-nx1-usb3-ssphy", + .data = &uniphier_ld20_data, + }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, uniphier_u3ssphy_match); -- cgit v1.2.3 From 21db1010cd80763c622d2e5e3f084e2af8a4b682 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:02 +0900 Subject: dt-bindings: phy: uniphier-pcie: Add bindings for NX1 SoC Update PCIe-PHY binding document for UniPhier NX1 SoC. Add a compatible string for the SoC to the document. Signed-off-by: Kunihiko Hayashi Acked-by: Rob Herring Link: https://lore.kernel.org/r/1635503947-18250-4-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/socionext,uniphier-pcie-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/socionext,uniphier-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/socionext,uniphier-pcie-phy.yaml index 3e0566899041..fbb71d6dd531 100644 --- a/Documentation/devicetree/bindings/phy/socionext,uniphier-pcie-phy.yaml +++ b/Documentation/devicetree/bindings/phy/socionext,uniphier-pcie-phy.yaml @@ -19,6 +19,7 @@ properties: - socionext,uniphier-pro5-pcie-phy - socionext,uniphier-ld20-pcie-phy - socionext,uniphier-pxs3-pcie-phy + - socionext,uniphier-nx1-pcie-phy reg: maxItems: 1 -- cgit v1.2.3 From 1c1597c8027aa4a98a56e8b5b341ddc38451f0e8 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:03 +0900 Subject: phy: uniphier-pcie: Add compatible string and SoC-dependent data for NX1 SoC Add basic support for UniPhier NX1 SoC. This includes a compatible string, SoC-dependent data, and a function that set to 2-lane mode. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1635503947-18250-5-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- drivers/phy/socionext/phy-uniphier-pcie.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c index 6bdbd1f214dd..fde8aac5f4b6 100644 --- a/drivers/phy/socionext/phy-uniphier-pcie.c +++ b/drivers/phy/socionext/phy-uniphier-pcie.c @@ -39,6 +39,10 @@ #define SG_USBPCIESEL 0x590 #define SG_USBPCIESEL_PCIE BIT(0) +/* SC */ +#define SC_US3SRCSEL 0x2244 +#define SC_US3SRCSEL_2LANE GENMASK(9, 8) + #define PCL_PHY_R00 0 #define RX_EQ_ADJ_EN BIT(3) /* enable for EQ adjustment */ #define PCL_PHY_R06 6 @@ -261,6 +265,12 @@ static void uniphier_pciephy_ld20_setmode(struct regmap *regmap) SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE); } +static void uniphier_pciephy_nx1_setmode(struct regmap *regmap) +{ + regmap_update_bits(regmap, SC_US3SRCSEL, + SC_US3SRCSEL_2LANE, SC_US3SRCSEL_2LANE); +} + static const struct uniphier_pciephy_soc_data uniphier_pro5_data = { .is_legacy = true, }; @@ -274,6 +284,11 @@ static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = { .is_legacy = false, }; +static const struct uniphier_pciephy_soc_data uniphier_nx1_data = { + .is_legacy = false, + .set_phymode = uniphier_pciephy_nx1_setmode, +}; + static const struct of_device_id uniphier_pciephy_match[] = { { .compatible = "socionext,uniphier-pro5-pcie-phy", @@ -287,6 +302,10 @@ static const struct of_device_id uniphier_pciephy_match[] = { .compatible = "socionext,uniphier-pxs3-pcie-phy", .data = &uniphier_pxs3_data, }, + { + .compatible = "socionext,uniphier-nx1-pcie-phy", + .data = &uniphier_nx1_data, + }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(of, uniphier_pciephy_match); -- cgit v1.2.3 From 25bba42f95f6ad22295c5a0204086ace9bff1e4a Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:04 +0900 Subject: phy: uniphier-pcie: Set VCOPLL clamp mode in PHY register Set VCOPLL clamp mode to mode 0 to avoid hardware unstable issue. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1635503947-18250-6-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- drivers/phy/socionext/phy-uniphier-pcie.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c index fde8aac5f4b6..2bd8df619712 100644 --- a/drivers/phy/socionext/phy-uniphier-pcie.c +++ b/drivers/phy/socionext/phy-uniphier-pcie.c @@ -51,6 +51,9 @@ #define PCL_PHY_R26 26 #define VCO_CTRL GENMASK(7, 4) /* Tx VCO adjustment value */ #define VCO_CTRL_INIT_VAL 5 +#define PCL_PHY_R28 28 +#define VCOPLL_CLMP GENMASK(3, 2) /* Tx VCOPLL clamp mode */ +#define VCOPLL_CLMP_VAL 0 struct uniphier_pciephy_priv { void __iomem *base; @@ -158,6 +161,8 @@ static int uniphier_pciephy_init(struct phy *phy) FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL)); uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL, FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL)); + uniphier_pciephy_set_param(priv, PCL_PHY_R28, VCOPLL_CLMP, + FIELD_PREP(VCOPLL_CLMP, VCOPLL_CLMP_VAL)); usleep_range(1, 10); uniphier_pciephy_deassert(priv); -- cgit v1.2.3 From 7f1abed4e9a5d0e0f565ae6c74bf258a97fa8f86 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:05 +0900 Subject: phy: uniphier-pcie: Add dual-phy support for NX1 SoC NX1 SoC supports 2 lanes and has dual-phy. Should set appropriate configuration values to both PHY registers. Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1635503947-18250-7-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- drivers/phy/socionext/phy-uniphier-pcie.c | 48 ++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/drivers/phy/socionext/phy-uniphier-pcie.c b/drivers/phy/socionext/phy-uniphier-pcie.c index 2bd8df619712..ebca296ef123 100644 --- a/drivers/phy/socionext/phy-uniphier-pcie.c +++ b/drivers/phy/socionext/phy-uniphier-pcie.c @@ -27,6 +27,7 @@ #define TESTI_DAT_MASK GENMASK(13, 6) #define TESTI_ADR_MASK GENMASK(5, 1) #define TESTI_WR_EN BIT(0) +#define TESTIO_PHY_SHIFT 16 #define PCL_PHY_TEST_O 0x2004 #define TESTO_DAT_MASK GENMASK(7, 0) @@ -65,43 +66,57 @@ struct uniphier_pciephy_priv { struct uniphier_pciephy_soc_data { bool is_legacy; + bool is_dual_phy; void (*set_phymode)(struct regmap *regmap); }; static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv, - u32 data) + int id, u32 data) { + if (id) + data <<= TESTIO_PHY_SHIFT; + /* need to read TESTO twice after accessing TESTI */ writel(data, priv->base + PCL_PHY_TEST_I); readl(priv->base + PCL_PHY_TEST_O); readl(priv->base + PCL_PHY_TEST_O); } +static u32 uniphier_pciephy_testio_read(struct uniphier_pciephy_priv *priv, int id) +{ + u32 val = readl(priv->base + PCL_PHY_TEST_O); + + if (id) + val >>= TESTIO_PHY_SHIFT; + + return val & TESTO_DAT_MASK; +} + static void uniphier_pciephy_set_param(struct uniphier_pciephy_priv *priv, - u32 reg, u32 mask, u32 param) + int id, u32 reg, u32 mask, u32 param) { u32 val; /* read previous data */ val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, reg); - uniphier_pciephy_testio_write(priv, val); - val = readl(priv->base + PCL_PHY_TEST_O) & TESTO_DAT_MASK; + uniphier_pciephy_testio_write(priv, id, val); + val = uniphier_pciephy_testio_read(priv, id); /* update value */ val &= ~mask; val |= mask & param; val = FIELD_PREP(TESTI_DAT_MASK, val); val |= FIELD_PREP(TESTI_ADR_MASK, reg); - uniphier_pciephy_testio_write(priv, val); - uniphier_pciephy_testio_write(priv, val | TESTI_WR_EN); - uniphier_pciephy_testio_write(priv, val); + uniphier_pciephy_testio_write(priv, id, val); + uniphier_pciephy_testio_write(priv, id, val | TESTI_WR_EN); + uniphier_pciephy_testio_write(priv, id, val); /* read current data as dummy */ val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, reg); - uniphier_pciephy_testio_write(priv, val); - readl(priv->base + PCL_PHY_TEST_O); + uniphier_pciephy_testio_write(priv, id, val); + uniphier_pciephy_testio_read(priv, id); } static void uniphier_pciephy_assert(struct uniphier_pciephy_priv *priv) @@ -127,7 +142,7 @@ static int uniphier_pciephy_init(struct phy *phy) { struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy); u32 val; - int ret; + int ret, id; ret = clk_prepare_enable(priv->clk); if (ret) @@ -155,14 +170,16 @@ static int uniphier_pciephy_init(struct phy *phy) if (priv->data->is_legacy) return 0; - uniphier_pciephy_set_param(priv, PCL_PHY_R00, + for (id = 0; id < (priv->data->is_dual_phy ? 2 : 1); id++) { + uniphier_pciephy_set_param(priv, id, PCL_PHY_R00, RX_EQ_ADJ_EN, RX_EQ_ADJ_EN); - uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ, + uniphier_pciephy_set_param(priv, id, PCL_PHY_R06, RX_EQ_ADJ, FIELD_PREP(RX_EQ_ADJ, RX_EQ_ADJ_VAL)); - uniphier_pciephy_set_param(priv, PCL_PHY_R26, VCO_CTRL, + uniphier_pciephy_set_param(priv, id, PCL_PHY_R26, VCO_CTRL, FIELD_PREP(VCO_CTRL, VCO_CTRL_INIT_VAL)); - uniphier_pciephy_set_param(priv, PCL_PHY_R28, VCOPLL_CLMP, + uniphier_pciephy_set_param(priv, id, PCL_PHY_R28, VCOPLL_CLMP, FIELD_PREP(VCOPLL_CLMP, VCOPLL_CLMP_VAL)); + } usleep_range(1, 10); uniphier_pciephy_deassert(priv); @@ -282,15 +299,18 @@ static const struct uniphier_pciephy_soc_data uniphier_pro5_data = { static const struct uniphier_pciephy_soc_data uniphier_ld20_data = { .is_legacy = false, + .is_dual_phy = false, .set_phymode = uniphier_pciephy_ld20_setmode, }; static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = { .is_legacy = false, + .is_dual_phy = false, }; static const struct uniphier_pciephy_soc_data uniphier_nx1_data = { .is_legacy = false, + .is_dual_phy = true, .set_phymode = uniphier_pciephy_nx1_setmode, }; -- cgit v1.2.3 From 34f92b67621fe933ed9a3ed5a6f432541d183851 Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:06 +0900 Subject: dt-bindings: phy: uniphier-ahci: Add bindings for Pro4 SoC Update AHCI-PHY binding document for UniPhier Pro4 SoC. Add a compatible string, clock and reset lines for the SoC to the document. Pro4 AHCI-PHY needs to control additional GIO clock line and reset lines ("pm", "tx", and "rx"). Signed-off-by: Kunihiko Hayashi Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1635503947-18250-8-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- .../bindings/phy/socionext,uniphier-ahci-phy.yaml | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/socionext,uniphier-ahci-phy.yaml b/Documentation/devicetree/bindings/phy/socionext,uniphier-ahci-phy.yaml index 745c525ce6b9..3b400a85b44a 100644 --- a/Documentation/devicetree/bindings/phy/socionext,uniphier-ahci-phy.yaml +++ b/Documentation/devicetree/bindings/phy/socionext,uniphier-ahci-phy.yaml @@ -16,6 +16,7 @@ maintainers: properties: compatible: enum: + - socionext,uniphier-pro4-ahci-phy - socionext,uniphier-pxs2-ahci-phy - socionext,uniphier-pxs3-ahci-phy @@ -26,23 +27,35 @@ properties: const: 0 clocks: + minItems: 1 maxItems: 2 clock-names: oneOf: - items: # for PXs2 - const: link + - items: # for Pro4 + - const: link + - const: gio - items: # for others - const: link - const: phy resets: - maxItems: 2 + minItems: 2 + maxItems: 5 reset-names: - items: - - const: link - - const: phy + oneOf: + - items: # for Pro4 + - const: link + - const: gio + - const: pm + - const: tx + - const: rx + - items: # for others + - const: link + - const: phy required: - compatible -- cgit v1.2.3 From b1f9f4541e99a43e3d52bc65408d0b96a340c1df Mon Sep 17 00:00:00 2001 From: Kunihiko Hayashi Date: Fri, 29 Oct 2021 19:39:07 +0900 Subject: phy: uniphier-ahci: Add support for Pro4 SoC Add support for PHY interface built into ahci controller implemented in UniPhier Pro4 SoC. Pro4 SoC distinguishes it from other SoCs as "legacy" SoC, which has GIO clock line. And Pro4 AHCI-PHY needs to control additional reset lines ("pm", "tx", and "rx"). Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1635503947-18250-9-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- drivers/phy/socionext/Kconfig | 2 +- drivers/phy/socionext/phy-uniphier-ahci.c | 201 +++++++++++++++++++++++++++++- 2 files changed, 198 insertions(+), 5 deletions(-) diff --git a/drivers/phy/socionext/Kconfig b/drivers/phy/socionext/Kconfig index a3970e0f89da..8ae644756352 100644 --- a/drivers/phy/socionext/Kconfig +++ b/drivers/phy/socionext/Kconfig @@ -43,4 +43,4 @@ config PHY_UNIPHIER_AHCI select GENERIC_PHY help Enable this to support PHY implemented in AHCI controller - on UniPhier SoCs. This driver supports PXs2 and PXs3 SoCs. + on UniPhier SoCs. This driver supports Pro4, PXs2 and PXs3 SoCs. diff --git a/drivers/phy/socionext/phy-uniphier-ahci.c b/drivers/phy/socionext/phy-uniphier-ahci.c index 7427c40bf4ae..28cf3efe0695 100644 --- a/drivers/phy/socionext/phy-uniphier-ahci.c +++ b/drivers/phy/socionext/phy-uniphier-ahci.c @@ -19,8 +19,9 @@ struct uniphier_ahciphy_priv { struct device *dev; void __iomem *base; - struct clk *clk, *clk_parent; - struct reset_control *rst, *rst_parent; + struct clk *clk, *clk_parent, *clk_parent_gio; + struct reset_control *rst, *rst_parent, *rst_parent_gio; + struct reset_control *rst_pm, *rst_tx, *rst_rx; const struct uniphier_ahciphy_soc_data *data; }; @@ -28,10 +29,30 @@ struct uniphier_ahciphy_soc_data { int (*init)(struct uniphier_ahciphy_priv *priv); int (*power_on)(struct uniphier_ahciphy_priv *priv); int (*power_off)(struct uniphier_ahciphy_priv *priv); + bool is_legacy; bool is_ready_high; bool is_phy_clk; }; +/* for Pro4 */ +#define CKCTRL0 0x0 +#define CKCTRL0_CK_OFF BIT(9) +#define CKCTRL0_NCY_MASK GENMASK(8, 4) +#define CKCTRL0_NCY5_MASK GENMASK(3, 2) +#define CKCTRL0_PRESCALE_MASK GENMASK(1, 0) +#define CKCTRL1 0x4 +#define CKCTRL1_LOS_LVL_MASK GENMASK(20, 16) +#define CKCTRL1_TX_LVL_MASK GENMASK(12, 8) +#define RXTXCTRL 0x8 +#define RXTXCTRL_RX_EQ_VALL_MASK GENMASK(31, 29) +#define RXTXCTRL_RX_DPLL_MODE_MASK GENMASK(28, 26) +#define RXTXCTRL_TX_ATTEN_MASK GENMASK(14, 12) +#define RXTXCTRL_TX_BOOST_MASK GENMASK(11, 8) +#define RXTXCTRL_TX_EDGERATE_MASK GENMASK(3, 2) +#define RXTXCTRL_TX_CKO_EN BIT(0) +#define RSTPWR 0x30 +#define RSTPWR_RX_EN_VAL BIT(18) + /* for PXs2/PXs3 */ #define CKCTRL 0x0 #define CKCTRL_P0_READY BIT(15) @@ -50,6 +71,128 @@ struct uniphier_ahciphy_soc_data { #define RXCTRL_LOS_BIAS_MASK GENMASK(10, 8) #define RXCTRL_RX_EQ_MASK GENMASK(2, 0) +static int uniphier_ahciphy_pro4_init(struct uniphier_ahciphy_priv *priv) +{ + u32 val; + + /* set phy MPLL parameters */ + val = readl(priv->base + CKCTRL0); + val &= ~CKCTRL0_NCY_MASK; + val |= FIELD_PREP(CKCTRL0_NCY_MASK, 0x6); + val &= ~CKCTRL0_NCY5_MASK; + val |= FIELD_PREP(CKCTRL0_NCY5_MASK, 0x2); + val &= ~CKCTRL0_PRESCALE_MASK; + val |= FIELD_PREP(CKCTRL0_PRESCALE_MASK, 0x1); + writel(val, priv->base + CKCTRL0); + + /* setup phy control parameters */ + val = readl(priv->base + CKCTRL1); + val &= ~CKCTRL1_LOS_LVL_MASK; + val |= FIELD_PREP(CKCTRL1_LOS_LVL_MASK, 0x10); + val &= ~CKCTRL1_TX_LVL_MASK; + val |= FIELD_PREP(CKCTRL1_TX_LVL_MASK, 0x06); + writel(val, priv->base + CKCTRL1); + + val = readl(priv->base + RXTXCTRL); + val &= ~RXTXCTRL_RX_EQ_VALL_MASK; + val |= FIELD_PREP(RXTXCTRL_RX_EQ_VALL_MASK, 0x6); + val &= ~RXTXCTRL_RX_DPLL_MODE_MASK; + val |= FIELD_PREP(RXTXCTRL_RX_DPLL_MODE_MASK, 0x3); + val &= ~RXTXCTRL_TX_ATTEN_MASK; + val |= FIELD_PREP(RXTXCTRL_TX_ATTEN_MASK, 0x3); + val &= ~RXTXCTRL_TX_BOOST_MASK; + val |= FIELD_PREP(RXTXCTRL_TX_BOOST_MASK, 0x5); + val &= ~RXTXCTRL_TX_EDGERATE_MASK; + val |= FIELD_PREP(RXTXCTRL_TX_EDGERATE_MASK, 0x0); + writel(val, priv->base + RXTXCTRL); + + return 0; +} + +static int uniphier_ahciphy_pro4_power_on(struct uniphier_ahciphy_priv *priv) +{ + u32 val; + int ret; + + /* enable reference clock for phy */ + val = readl(priv->base + CKCTRL0); + val &= ~CKCTRL0_CK_OFF; + writel(val, priv->base + CKCTRL0); + + /* enable TX clock */ + val = readl(priv->base + RXTXCTRL); + val |= RXTXCTRL_TX_CKO_EN; + writel(val, priv->base + RXTXCTRL); + + /* wait until RX is ready */ + ret = readl_poll_timeout(priv->base + RSTPWR, val, + !(val & RSTPWR_RX_EN_VAL), 200, 2000); + if (ret) { + dev_err(priv->dev, "Failed to check whether Rx is ready\n"); + goto out_disable_clock; + } + + /* release all reset */ + ret = reset_control_deassert(priv->rst_pm); + if (ret) { + dev_err(priv->dev, "Failed to release PM reset\n"); + goto out_disable_clock; + } + + ret = reset_control_deassert(priv->rst_tx); + if (ret) { + dev_err(priv->dev, "Failed to release Tx reset\n"); + goto out_reset_pm_assert; + } + + ret = reset_control_deassert(priv->rst_rx); + if (ret) { + dev_err(priv->dev, "Failed to release Rx reset\n"); + goto out_reset_tx_assert; + } + + return 0; + +out_reset_tx_assert: + reset_control_assert(priv->rst_tx); +out_reset_pm_assert: + reset_control_assert(priv->rst_pm); + +out_disable_clock: + /* disable TX clock */ + val = readl(priv->base + RXTXCTRL); + val &= ~RXTXCTRL_TX_CKO_EN; + writel(val, priv->base + RXTXCTRL); + + /* disable reference clock for phy */ + val = readl(priv->base + CKCTRL0); + val |= CKCTRL0_CK_OFF; + writel(val, priv->base + CKCTRL0); + + return ret; +} + +static int uniphier_ahciphy_pro4_power_off(struct uniphier_ahciphy_priv *priv) +{ + u32 val; + + reset_control_assert(priv->rst_rx); + reset_control_assert(priv->rst_tx); + reset_control_assert(priv->rst_pm); + + /* disable TX clock */ + val = readl(priv->base + RXTXCTRL); + val &= ~RXTXCTRL_TX_CKO_EN; + writel(val, priv->base + RXTXCTRL); + + /* disable reference clock for phy */ + val = readl(priv->base + CKCTRL0); + val |= CKCTRL0_CK_OFF; + writel(val, priv->base + CKCTRL0); + + return 0; +} + static void uniphier_ahciphy_pxs2_enable(struct uniphier_ahciphy_priv *priv, bool enable) { @@ -142,14 +285,22 @@ static int uniphier_ahciphy_init(struct phy *phy) struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy); int ret; - ret = clk_prepare_enable(priv->clk_parent); + ret = clk_prepare_enable(priv->clk_parent_gio); if (ret) return ret; - ret = reset_control_deassert(priv->rst_parent); + ret = clk_prepare_enable(priv->clk_parent); + if (ret) + goto out_clk_gio_disable; + + ret = reset_control_deassert(priv->rst_parent_gio); if (ret) goto out_clk_disable; + ret = reset_control_deassert(priv->rst_parent); + if (ret) + goto out_rst_gio_assert; + if (priv->data->init) { ret = priv->data->init(priv); if (ret) @@ -160,8 +311,12 @@ static int uniphier_ahciphy_init(struct phy *phy) out_rst_assert: reset_control_assert(priv->rst_parent); +out_rst_gio_assert: + reset_control_assert(priv->rst_parent_gio); out_clk_disable: clk_disable_unprepare(priv->clk_parent); +out_clk_gio_disable: + clk_disable_unprepare(priv->clk_parent_gio); return ret; } @@ -171,7 +326,9 @@ static int uniphier_ahciphy_exit(struct phy *phy) struct uniphier_ahciphy_priv *priv = phy_get_drvdata(phy); reset_control_assert(priv->rst_parent); + reset_control_assert(priv->rst_parent_gio); clk_disable_unprepare(priv->clk_parent); + clk_disable_unprepare(priv->clk_parent_gio); return 0; } @@ -265,6 +422,28 @@ static int uniphier_ahciphy_probe(struct platform_device *pdev) if (IS_ERR(priv->rst)) return PTR_ERR(priv->rst); + if (priv->data->is_legacy) { + priv->clk_parent_gio = devm_clk_get(dev, "gio"); + if (IS_ERR(priv->clk_parent_gio)) + return PTR_ERR(priv->clk_parent_gio); + priv->rst_parent_gio = + devm_reset_control_get_shared(dev, "gio"); + if (IS_ERR(priv->rst_parent_gio)) + return PTR_ERR(priv->rst_parent_gio); + + priv->rst_pm = devm_reset_control_get_shared(dev, "pm"); + if (IS_ERR(priv->rst_pm)) + return PTR_ERR(priv->rst_pm); + + priv->rst_tx = devm_reset_control_get_shared(dev, "tx"); + if (IS_ERR(priv->rst_tx)) + return PTR_ERR(priv->rst_tx); + + priv->rst_rx = devm_reset_control_get_shared(dev, "rx"); + if (IS_ERR(priv->rst_rx)) + return PTR_ERR(priv->rst_rx); + } + phy = devm_phy_create(dev, dev->of_node, &uniphier_ahciphy_ops); if (IS_ERR(phy)) { dev_err(dev, "failed to create phy\n"); @@ -279,9 +458,18 @@ static int uniphier_ahciphy_probe(struct platform_device *pdev) return 0; } +static const struct uniphier_ahciphy_soc_data uniphier_pro4_data = { + .init = uniphier_ahciphy_pro4_init, + .power_on = uniphier_ahciphy_pro4_power_on, + .power_off = uniphier_ahciphy_pro4_power_off, + .is_legacy = true, + .is_phy_clk = false, +}; + static const struct uniphier_ahciphy_soc_data uniphier_pxs2_data = { .power_on = uniphier_ahciphy_pxs2_power_on, .power_off = uniphier_ahciphy_pxs2_power_off, + .is_legacy = false, .is_ready_high = false, .is_phy_clk = false, }; @@ -290,11 +478,16 @@ static const struct uniphier_ahciphy_soc_data uniphier_pxs3_data = { .init = uniphier_ahciphy_pxs3_init, .power_on = uniphier_ahciphy_pxs2_power_on, .power_off = uniphier_ahciphy_pxs2_power_off, + .is_legacy = false, .is_ready_high = true, .is_phy_clk = true, }; static const struct of_device_id uniphier_ahciphy_match[] = { + { + .compatible = "socionext,uniphier-pro4-ahci-phy", + .data = &uniphier_pro4_data, + }, { .compatible = "socionext,uniphier-pxs2-ahci-phy", .data = &uniphier_pxs2_data, -- cgit v1.2.3 From a463462998777af31995e309617918552983b890 Mon Sep 17 00:00:00 2001 From: Yang Guang Date: Thu, 4 Nov 2021 14:52:33 +0800 Subject: phy: cadence-torrent: use swap() to make code cleaner Use the macro 'swap()' defined in 'include/linux/minmax.h' to avoid opencoding it. Reported-by: Zeal Robot Signed-off-by: Yang Guang Link: https://lore.kernel.org/r/20211104065233.1833499-1-yang.guang5@zte.com.cn Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-torrent.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 5786166133d3..7c4b8050485f 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -2278,7 +2278,7 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) struct cdns_torrent_vals *cmn_vals, *tx_ln_vals, *rx_ln_vals; enum cdns_torrent_ref_clk ref_clk = cdns_phy->ref_clk_rate; struct cdns_torrent_vals *link_cmn_vals, *xcvr_diag_vals; - enum cdns_torrent_phy_type phy_t1, phy_t2, tmp_phy_type; + enum cdns_torrent_phy_type phy_t1, phy_t2; struct cdns_torrent_vals *pcs_cmn_vals; int i, j, node, mlane, num_lanes, ret; struct cdns_reg_pairs *reg_pairs; @@ -2304,9 +2304,7 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) * configure the PHY for second link with phy_t2. * Get the array values as [phy_t2][phy_t1][ssc]. */ - tmp_phy_type = phy_t1; - phy_t1 = phy_t2; - phy_t2 = tmp_phy_type; + swap(phy_t1, phy_t2); } mlane = cdns_phy->phys[node].mlane; -- cgit v1.2.3 From be24d24840ccb6f35ecd866005bf1b9498cddf97 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Tue, 2 Nov 2021 16:51:20 +0530 Subject: phy: phy-can-transceiver: Make devm_gpiod_get optional In some cases the standby/enable gpio can be pulled low/high and would not be connected to a gpio. The current driver implementation will return an error in these cases. Therefore, make devm_gpiod_get optional. Signed-off-by: Aswath Govindraju Acked-by: Marc Kleine-Budde Link: https://lore.kernel.org/r/20211102112120.23637-1-a-govindraju@ti.com Signed-off-by: Vinod Koul --- drivers/phy/phy-can-transceiver.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/phy/phy-can-transceiver.c b/drivers/phy/phy-can-transceiver.c index c2cb93b4df71..6f3fe37dee0e 100644 --- a/drivers/phy/phy-can-transceiver.c +++ b/drivers/phy/phy-can-transceiver.c @@ -110,14 +110,14 @@ static int can_transceiver_phy_probe(struct platform_device *pdev) can_transceiver_phy->generic_phy = phy; if (drvdata->flags & CAN_TRANSCEIVER_STB_PRESENT) { - standby_gpio = devm_gpiod_get(dev, "standby", GPIOD_OUT_HIGH); + standby_gpio = devm_gpiod_get_optional(dev, "standby", GPIOD_OUT_HIGH); if (IS_ERR(standby_gpio)) return PTR_ERR(standby_gpio); can_transceiver_phy->standby_gpio = standby_gpio; } if (drvdata->flags & CAN_TRANSCEIVER_EN_PRESENT) { - enable_gpio = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW); + enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_LOW); if (IS_ERR(enable_gpio)) return PTR_ERR(enable_gpio); can_transceiver_phy->enable_gpio = enable_gpio; -- cgit v1.2.3 From fd66e57e46a3d1b73912e4a04b1f17d3369f8bfa Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 16 Nov 2021 11:08:16 +0100 Subject: dt-bindings: phy: Add lan966x-serdes binding Document the lan966x ethernet serdes phy driver bindings. Reviewed-by: Rob Herring Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20211116100818.1615762-2-horatiu.vultur@microchip.com Signed-off-by: Vinod Koul --- .../bindings/phy/microchip,lan966x-serdes.yaml | 59 ++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/microchip,lan966x-serdes.yaml diff --git a/Documentation/devicetree/bindings/phy/microchip,lan966x-serdes.yaml b/Documentation/devicetree/bindings/phy/microchip,lan966x-serdes.yaml new file mode 100644 index 000000000000..6e914fbbac56 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/microchip,lan966x-serdes.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/microchip,lan966x-serdes.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Microchip Lan966x Serdes controller + +maintainers: + - Horatiu Vultur + +description: | + Lan966x has 7 interfaces, consisting of 2 copper transceivers(CU), + 3 SERDES6G and 2 RGMII interfaces. Two of the SERDES6G support QSGMII. + Also it has 8 logical Ethernet ports which can be connected to these + interfaces. The Serdes controller will allow to configure these interfaces + and allows to "mux" the interfaces to different ports. + + For simple selection of the interface that is used with a port, the + following macros are defined CU(X), SERDES6G(X), RGMII(X). Where X is a + number that represents the index of that interface type. For example + CU(1) means use interface copper transceivers 1. SERDES6G(2) means use + interface SerDes 2. + +properties: + $nodename: + pattern: "^serdes@[0-9a-f]+$" + + compatible: + const: microchip,lan966x-serdes + + reg: + items: + - description: HSIO registers + - description: HW_STAT register + + '#phy-cells': + const: 2 + description: | + - Input port to use for a given macro. + - The macro to be used. The macros are defined in + dt-bindings/phy/phy-lan966x-serdes. + +required: + - compatible + - reg + - '#phy-cells' + +additionalProperties: false + +examples: + - | + serdes: serdes@e2004010 { + compatible = "microchip,lan966x-serdes"; + reg = <0xe202c000 0x9c>, <0xe2004010 0x4>; + #phy-cells = <2>; + }; + +... -- cgit v1.2.3 From ea8a163e02d6925773129e2dd86e419e491b791d Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 16 Nov 2021 11:08:17 +0100 Subject: dt-bindings: phy: Add constants for lan966x serdes Lan966x has: 2 integrated PHYs, 3 SerDes and 2 RGMII interfaces. Which requires to be muxed based on the HW representation. So add constants for each interface to be able to distinguish them. Reviewed-by: Rob Herring Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20211116100818.1615762-3-horatiu.vultur@microchip.com Signed-off-by: Vinod Koul --- include/dt-bindings/phy/phy-lan966x-serdes.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 include/dt-bindings/phy/phy-lan966x-serdes.h diff --git a/include/dt-bindings/phy/phy-lan966x-serdes.h b/include/dt-bindings/phy/phy-lan966x-serdes.h new file mode 100644 index 000000000000..4330269a901e --- /dev/null +++ b/include/dt-bindings/phy/phy-lan966x-serdes.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ + +#ifndef __PHY_LAN966X_SERDES_H__ +#define __PHY_LAN966X_SERDES_H__ + +#define CU(x) (x) +#define CU_MAX CU(2) +#define SERDES6G(x) (CU_MAX + 1 + (x)) +#define SERDES6G_MAX SERDES6G(3) +#define RGMII(x) (SERDES6G_MAX + 1 + (x)) +#define RGMII_MAX RGMII(2) +#define SERDES_MAX (RGMII_MAX + 1) + +#endif -- cgit v1.2.3 From 305524902a00455b61ddc44800ac5c39198e24f7 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 16 Nov 2021 11:08:18 +0100 Subject: phy: Add lan966x ethernet serdes PHY driver Add the Microchip lan966x ethernet serdes PHY driver for interfaces available in the lan966x SoC. Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20211116100818.1615762-4-horatiu.vultur@microchip.com Signed-off-by: Vinod Koul --- drivers/phy/microchip/Kconfig | 8 + drivers/phy/microchip/Makefile | 1 + drivers/phy/microchip/lan966x_serdes.c | 548 ++++++++++++++++++++++++++++ drivers/phy/microchip/lan966x_serdes_regs.h | 209 +++++++++++ 4 files changed, 766 insertions(+) create mode 100644 drivers/phy/microchip/lan966x_serdes.c create mode 100644 drivers/phy/microchip/lan966x_serdes_regs.h diff --git a/drivers/phy/microchip/Kconfig b/drivers/phy/microchip/Kconfig index 3728a284bf64..38039ed0754c 100644 --- a/drivers/phy/microchip/Kconfig +++ b/drivers/phy/microchip/Kconfig @@ -11,3 +11,11 @@ config PHY_SPARX5_SERDES depends on HAS_IOMEM help Enable this for support of the 10G/25G SerDes on Microchip Sparx5. + +config PHY_LAN966X_SERDES + tristate "SerDes PHY driver for Microchip LAN966X" + select GENERIC_PHY + depends on OF + depends on MFD_SYSCON + help + Enable this for supporting SerDes muxing with Microchip LAN966X diff --git a/drivers/phy/microchip/Makefile b/drivers/phy/microchip/Makefile index 7b98345712aa..fd73b87960a5 100644 --- a/drivers/phy/microchip/Makefile +++ b/drivers/phy/microchip/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_PHY_SPARX5_SERDES) := sparx5_serdes.o +obj-$(CONFIG_PHY_LAN966X_SERDES) := lan966x_serdes.o diff --git a/drivers/phy/microchip/lan966x_serdes.c b/drivers/phy/microchip/lan966x_serdes.c new file mode 100644 index 000000000000..262bb616b4bb --- /dev/null +++ b/drivers/phy/microchip/lan966x_serdes.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +#include +#include +#include +#include +#include +#include +#include + +#include +#include "lan966x_serdes_regs.h" + +#define PLL_CONF_MASK GENMASK(4, 3) +#define PLL_CONF_25MHZ 0 +#define PLL_CONF_125MHZ 1 +#define PLL_CONF_SERDES_125MHZ 2 +#define PLL_CONF_BYPASS 3 + +#define lan_offset_(id, tinst, tcnt, \ + gbase, ginst, gcnt, gwidth, \ + raddr, rinst, rcnt, rwidth) \ + (gbase + ((ginst) * gwidth) + raddr + ((rinst) * rwidth)) +#define lan_offset(...) lan_offset_(__VA_ARGS__) + +#define lan_rmw(val, mask, reg, off) \ + lan_rmw_(val, mask, reg, lan_offset(off)) + +#define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \ + .idx = _idx, \ + .port = _port, \ + .mode = _mode, \ + .submode = _submode, \ + .mask = _mask, \ + .mux = _mux, \ +} + +#define SERDES_MUX_GMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_GMII, m, c) +#define SERDES_MUX_SGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c) +#define SERDES_MUX_QSGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c) +#define SERDES_MUX_RGMII(i, p, m, c) \ + SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII, m, c) + +static void lan_rmw_(u32 val, u32 mask, void __iomem *mem, u32 offset) +{ + u32 v; + + v = readl(mem + offset); + v = (v & ~mask) | (val & mask); + writel(v, mem + offset); +} + +struct serdes_mux { + u8 idx; + u8 port; + enum phy_mode mode; + int submode; + u32 mask; + u32 mux; +}; + +static const struct serdes_mux lan966x_serdes_muxes[] = { + SERDES_MUX_QSGMII(SERDES6G(1), 0, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + SERDES_MUX_QSGMII(SERDES6G(1), 1, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + SERDES_MUX_QSGMII(SERDES6G(1), 2, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + SERDES_MUX_QSGMII(SERDES6G(1), 3, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), + + SERDES_MUX_QSGMII(SERDES6G(2), 4, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + SERDES_MUX_QSGMII(SERDES6G(2), 5, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + SERDES_MUX_QSGMII(SERDES6G(2), 6, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + SERDES_MUX_QSGMII(SERDES6G(2), 7, HSIO_HW_CFG_QSGMII_ENA, + HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), + + SERDES_MUX_GMII(CU(0), 0, HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_GMII_ENA_SET(BIT(0))), + SERDES_MUX_GMII(CU(1), 1, HSIO_HW_CFG_GMII_ENA, + HSIO_HW_CFG_GMII_ENA_SET(BIT(1))), + + SERDES_MUX_SGMII(SERDES6G(0), 0, HSIO_HW_CFG_SD6G_0_CFG, 0), + SERDES_MUX_SGMII(SERDES6G(1), 1, HSIO_HW_CFG_SD6G_1_CFG, 0), + SERDES_MUX_SGMII(SERDES6G(0), 2, HSIO_HW_CFG_SD6G_0_CFG, + HSIO_HW_CFG_SD6G_0_CFG_SET(1)), + SERDES_MUX_SGMII(SERDES6G(1), 3, HSIO_HW_CFG_SD6G_1_CFG, + HSIO_HW_CFG_SD6G_1_CFG_SET(1)), + + SERDES_MUX_RGMII(RGMII(0), 2, HSIO_HW_CFG_RGMII_0_CFG | + HSIO_HW_CFG_RGMII_ENA, + HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(0))), + SERDES_MUX_RGMII(RGMII(1), 3, HSIO_HW_CFG_RGMII_1_CFG | + HSIO_HW_CFG_RGMII_ENA, + HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(1))), + SERDES_MUX_RGMII(RGMII(0), 5, HSIO_HW_CFG_RGMII_0_CFG | + HSIO_HW_CFG_RGMII_ENA, + HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(0))), + SERDES_MUX_RGMII(RGMII(1), 6, HSIO_HW_CFG_RGMII_1_CFG | + HSIO_HW_CFG_RGMII_ENA, + HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) | + HSIO_HW_CFG_RGMII_ENA_SET(BIT(1))), +}; + +struct serdes_ctrl { + void __iomem *regs; + struct device *dev; + struct phy *phys[SERDES_MAX]; + int ref125; +}; + +struct serdes_macro { + u8 idx; + int port; + struct serdes_ctrl *ctrl; + int speed; + phy_interface_t mode; +}; + +enum lan966x_sd6g40_mode { + LAN966X_SD6G40_MODE_QSGMII, + LAN966X_SD6G40_MODE_SGMII, +}; + +enum lan966x_sd6g40_ltx2rx { + LAN966X_SD6G40_TX2RX_LOOP_NONE, + LAN966X_SD6G40_LTX2RX +}; + +struct lan966x_sd6g40_setup_args { + enum lan966x_sd6g40_mode mode; + enum lan966x_sd6g40_ltx2rx tx2rx_loop; + bool txinvert; + bool rxinvert; + bool refclk125M; + bool mute; +}; + +struct lan966x_sd6g40_mode_args { + enum lan966x_sd6g40_mode mode; + u8 lane_10bit_sel; + u8 mpll_multiplier; + u8 ref_clkdiv2; + u8 tx_rate; + u8 rx_rate; +}; + +struct lan966x_sd6g40_setup { + u8 rx_term_en; + u8 lane_10bit_sel; + u8 tx_invert; + u8 rx_invert; + u8 mpll_multiplier; + u8 lane_loopbk_en; + u8 ref_clkdiv2; + u8 tx_rate; + u8 rx_rate; +}; + +static int lan966x_sd6g40_reg_cfg(struct serdes_macro *macro, + struct lan966x_sd6g40_setup *res_struct, + u32 idx) +{ + u32 value; + + /* Note: SerDes HSIO is configured in 1G_LAN mode */ + lan_rmw(HSIO_SD_CFG_LANE_10BIT_SEL_SET(res_struct->lane_10bit_sel) | + HSIO_SD_CFG_RX_RATE_SET(res_struct->rx_rate) | + HSIO_SD_CFG_TX_RATE_SET(res_struct->tx_rate) | + HSIO_SD_CFG_TX_INVERT_SET(res_struct->tx_invert) | + HSIO_SD_CFG_RX_INVERT_SET(res_struct->rx_invert) | + HSIO_SD_CFG_LANE_LOOPBK_EN_SET(res_struct->lane_loopbk_en) | + HSIO_SD_CFG_RX_RESET_SET(0) | + HSIO_SD_CFG_TX_RESET_SET(0), + HSIO_SD_CFG_LANE_10BIT_SEL | + HSIO_SD_CFG_RX_RATE | + HSIO_SD_CFG_TX_RATE | + HSIO_SD_CFG_TX_INVERT | + HSIO_SD_CFG_RX_INVERT | + HSIO_SD_CFG_LANE_LOOPBK_EN | + HSIO_SD_CFG_RX_RESET | + HSIO_SD_CFG_TX_RESET, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + lan_rmw(HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(res_struct->mpll_multiplier) | + HSIO_MPLL_CFG_REF_CLKDIV2_SET(res_struct->ref_clkdiv2), + HSIO_MPLL_CFG_MPLL_MULTIPLIER | + HSIO_MPLL_CFG_REF_CLKDIV2, + macro->ctrl->regs, HSIO_MPLL_CFG(idx)); + + lan_rmw(HSIO_SD_CFG_RX_TERM_EN_SET(res_struct->rx_term_en), + HSIO_SD_CFG_RX_TERM_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + lan_rmw(HSIO_MPLL_CFG_REF_SSP_EN_SET(1), + HSIO_MPLL_CFG_REF_SSP_EN, + macro->ctrl->regs, HSIO_MPLL_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + lan_rmw(HSIO_SD_CFG_PHY_RESET_SET(0), + HSIO_SD_CFG_PHY_RESET, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + lan_rmw(HSIO_MPLL_CFG_MPLL_EN_SET(1), + HSIO_MPLL_CFG_MPLL_EN, + macro->ctrl->regs, HSIO_MPLL_CFG(idx)); + + usleep_range(7 * USEC_PER_MSEC, 8 * USEC_PER_MSEC); + + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_MPLL_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] mpll_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + lan_rmw(HSIO_SD_CFG_TX_CM_EN_SET(1), + HSIO_SD_CFG_TX_CM_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_TX_CM_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] tx_cm_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + lan_rmw(HSIO_SD_CFG_RX_PLL_EN_SET(1) | + HSIO_SD_CFG_TX_EN_SET(1), + HSIO_SD_CFG_RX_PLL_EN | + HSIO_SD_CFG_TX_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); + + /* Waiting for serdes 0 rx DPLL to lock... */ + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_RX_PLL_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] rx_pll_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + /* Waiting for serdes 0 tx operational... */ + value = readl(macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); + value = HSIO_SD_STAT_TX_STATE_GET(value); + if (value != 0x1) { + dev_err(macro->ctrl->dev, + "Unexpected sd_sd_stat[%u] tx_state was 0x1 but is 0x%x\n", + idx, value); + return -EIO; + } + + lan_rmw(HSIO_SD_CFG_TX_DATA_EN_SET(1) | + HSIO_SD_CFG_RX_DATA_EN_SET(1), + HSIO_SD_CFG_TX_DATA_EN | + HSIO_SD_CFG_RX_DATA_EN, + macro->ctrl->regs, HSIO_SD_CFG(idx)); + + return 0; +} + +static int lan966x_sd6g40_get_conf_from_mode(struct serdes_macro *macro, + enum lan966x_sd6g40_mode f_mode, + bool ref125M, + struct lan966x_sd6g40_mode_args *ret_val) +{ + switch (f_mode) { + case LAN966X_SD6G40_MODE_QSGMII: + ret_val->lane_10bit_sel = 0; + if (ref125M) { + ret_val->mpll_multiplier = 40; + ret_val->ref_clkdiv2 = 0x1; + ret_val->tx_rate = 0x0; + ret_val->rx_rate = 0x0; + } else { + ret_val->mpll_multiplier = 100; + ret_val->ref_clkdiv2 = 0x0; + ret_val->tx_rate = 0x0; + ret_val->rx_rate = 0x0; + } + break; + + case LAN966X_SD6G40_MODE_SGMII: + ret_val->lane_10bit_sel = 1; + if (ref125M) { + ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 50 : 40; + ret_val->ref_clkdiv2 = 0x1; + ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + } else { + ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 125 : 100; + ret_val->ref_clkdiv2 = 0x0; + ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; + } + break; + + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static int lan966x_calc_sd6g40_setup_lane(struct serdes_macro *macro, + struct lan966x_sd6g40_setup_args config, + struct lan966x_sd6g40_setup *ret_val) +{ + struct lan966x_sd6g40_mode_args sd6g40_mode; + struct lan966x_sd6g40_mode_args *mode_args = &sd6g40_mode; + int ret; + + ret = lan966x_sd6g40_get_conf_from_mode(macro, config.mode, + config.refclk125M, mode_args); + if (ret) + return ret; + + ret_val->lane_10bit_sel = mode_args->lane_10bit_sel; + ret_val->rx_rate = mode_args->rx_rate; + ret_val->tx_rate = mode_args->tx_rate; + ret_val->mpll_multiplier = mode_args->mpll_multiplier; + ret_val->ref_clkdiv2 = mode_args->ref_clkdiv2; + ret_val->rx_term_en = 0; + + if (config.tx2rx_loop == LAN966X_SD6G40_LTX2RX) + ret_val->lane_loopbk_en = 1; + else + ret_val->lane_loopbk_en = 0; + + ret_val->tx_invert = !!config.txinvert; + ret_val->rx_invert = !!config.rxinvert; + + return 0; +} + +static int lan966x_sd6g40_setup_lane(struct serdes_macro *macro, + struct lan966x_sd6g40_setup_args config, + u32 idx) +{ + struct lan966x_sd6g40_setup calc_results = {}; + int ret; + + ret = lan966x_calc_sd6g40_setup_lane(macro, config, &calc_results); + if (ret) + return ret; + + return lan966x_sd6g40_reg_cfg(macro, &calc_results, idx); +} + +static int lan966x_sd6g40_setup(struct serdes_macro *macro, u32 idx, int mode) +{ + struct lan966x_sd6g40_setup_args conf = {}; + + conf.refclk125M = macro->ctrl->ref125; + + if (mode == PHY_INTERFACE_MODE_QSGMII) + conf.mode = LAN966X_SD6G40_MODE_QSGMII; + else + conf.mode = LAN966X_SD6G40_MODE_SGMII; + + return lan966x_sd6g40_setup_lane(macro, conf, idx); +} + +static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct serdes_macro *macro = phy_get_drvdata(phy); + unsigned int i; + int val; + + /* As of now only PHY_MODE_ETHERNET is supported */ + if (mode != PHY_MODE_ETHERNET) + return -EOPNOTSUPP; + + for (i = 0; i < ARRAY_SIZE(lan966x_serdes_muxes); i++) { + if (macro->idx != lan966x_serdes_muxes[i].idx || + mode != lan966x_serdes_muxes[i].mode || + submode != lan966x_serdes_muxes[i].submode || + macro->port != lan966x_serdes_muxes[i].port) + continue; + + val = readl(macro->ctrl->regs + lan_offset(HSIO_HW_CFG)); + val |= lan966x_serdes_muxes[i].mux; + lan_rmw(val, lan966x_serdes_muxes[i].mask, + macro->ctrl->regs, HSIO_HW_CFG); + + macro->mode = lan966x_serdes_muxes[i].submode; + + if (macro->idx < CU_MAX) + return 0; + + if (macro->idx < SERDES6G_MAX) + return lan966x_sd6g40_setup(macro, + macro->idx - (CU_MAX + 1), + macro->mode); + + if (macro->idx < RGMII_MAX) + return 0; + + return -EOPNOTSUPP; + } + + return -EINVAL; +} + +static int serdes_set_speed(struct phy *phy, int speed) +{ + struct serdes_macro *macro = phy_get_drvdata(phy); + + macro->speed = speed; + + return lan966x_sd6g40_setup(macro, macro->idx - (CU_MAX + 1), + macro->mode); +} + +static const struct phy_ops serdes_ops = { + .set_mode = serdes_set_mode, + .set_speed = serdes_set_speed, + .owner = THIS_MODULE, +}; + +static struct phy *serdes_simple_xlate(struct device *dev, + struct of_phandle_args *args) +{ + struct serdes_ctrl *ctrl = dev_get_drvdata(dev); + unsigned int port, idx, i; + + if (args->args_count != 2) + return ERR_PTR(-EINVAL); + + port = args->args[0]; + idx = args->args[1]; + + for (i = 0; i < SERDES_MAX; i++) { + struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]); + + if (idx != macro->idx) + continue; + + macro->port = port; + return ctrl->phys[i]; + } + + return ERR_PTR(-ENODEV); +} + +static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy) +{ + struct serdes_macro *macro; + + *phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops); + if (IS_ERR(*phy)) + return PTR_ERR(*phy); + + macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL); + if (!macro) + return -ENOMEM; + + macro->idx = idx; + macro->ctrl = ctrl; + macro->speed = SPEED_1000; + macro->port = -1; + + phy_set_drvdata(*phy, macro); + + return 0; +} + +static int serdes_probe(struct platform_device *pdev) +{ + struct phy_provider *provider; + struct serdes_ctrl *ctrl; + void __iomem *hw_stat; + unsigned int i; + u32 val; + int ret; + + ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL); + if (!ctrl) + return -ENOMEM; + + ctrl->dev = &pdev->dev; + ctrl->regs = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); + if (IS_ERR(ctrl->regs)) + return PTR_ERR(ctrl->regs); + + hw_stat = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); + if (IS_ERR(hw_stat)) + return PTR_ERR(hw_stat); + + for (i = 0; i < SERDES_MAX; i++) { + ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]); + if (ret) + return ret; + } + + val = readl(hw_stat); + val = FIELD_GET(PLL_CONF_MASK, val); + ctrl->ref125 = (val == PLL_CONF_125MHZ || + val == PLL_CONF_SERDES_125MHZ); + + dev_set_drvdata(&pdev->dev, ctrl); + + provider = devm_of_phy_provider_register(ctrl->dev, + serdes_simple_xlate); + + return PTR_ERR_OR_ZERO(provider); +} + +static const struct of_device_id serdes_ids[] = { + { .compatible = "microchip,lan966x-serdes", }, + {}, +}; +MODULE_DEVICE_TABLE(of, serdes_ids); + +static struct platform_driver mscc_lan966x_serdes = { + .probe = serdes_probe, + .driver = { + .name = "microchip,lan966x-serdes", + .of_match_table = of_match_ptr(serdes_ids), + }, +}; + +module_platform_driver(mscc_lan966x_serdes); + +MODULE_DESCRIPTION("Microchip lan966x switch serdes driver"); +MODULE_AUTHOR("Horatiu Vultur "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/microchip/lan966x_serdes_regs.h b/drivers/phy/microchip/lan966x_serdes_regs.h new file mode 100644 index 000000000000..ea30f64ffd5c --- /dev/null +++ b/drivers/phy/microchip/lan966x_serdes_regs.h @@ -0,0 +1,209 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef _LAN966X_SERDES_REGS_H_ +#define _LAN966X_SERDES_REGS_H_ + +#include +#include +#include + +enum lan966x_target { + TARGET_HSIO = 32, + NUM_TARGETS = 66 +}; + +#define __REG(...) __VA_ARGS__ + +/* HSIO:SD:SD_CFG */ +#define HSIO_SD_CFG(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 0, 0, 1, 4) + +#define HSIO_SD_CFG_PHY_RESET BIT(27) +#define HSIO_SD_CFG_PHY_RESET_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_PHY_RESET, x) +#define HSIO_SD_CFG_PHY_RESET_GET(x)\ + FIELD_GET(HSIO_SD_CFG_PHY_RESET, x) + +#define HSIO_SD_CFG_TX_RESET BIT(18) +#define HSIO_SD_CFG_TX_RESET_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_RESET, x) +#define HSIO_SD_CFG_TX_RESET_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_RESET, x) + +#define HSIO_SD_CFG_TX_RATE GENMASK(17, 16) +#define HSIO_SD_CFG_TX_RATE_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_RATE, x) +#define HSIO_SD_CFG_TX_RATE_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_RATE, x) + +#define HSIO_SD_CFG_TX_INVERT BIT(15) +#define HSIO_SD_CFG_TX_INVERT_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_INVERT, x) +#define HSIO_SD_CFG_TX_INVERT_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_INVERT, x) + +#define HSIO_SD_CFG_TX_EN BIT(14) +#define HSIO_SD_CFG_TX_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_EN, x) +#define HSIO_SD_CFG_TX_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_EN, x) + +#define HSIO_SD_CFG_TX_DATA_EN BIT(12) +#define HSIO_SD_CFG_TX_DATA_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_DATA_EN, x) +#define HSIO_SD_CFG_TX_DATA_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_DATA_EN, x) + +#define HSIO_SD_CFG_TX_CM_EN BIT(11) +#define HSIO_SD_CFG_TX_CM_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_TX_CM_EN, x) +#define HSIO_SD_CFG_TX_CM_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_TX_CM_EN, x) + +#define HSIO_SD_CFG_LANE_10BIT_SEL BIT(10) +#define HSIO_SD_CFG_LANE_10BIT_SEL_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_LANE_10BIT_SEL, x) +#define HSIO_SD_CFG_LANE_10BIT_SEL_GET(x)\ + FIELD_GET(HSIO_SD_CFG_LANE_10BIT_SEL, x) + +#define HSIO_SD_CFG_RX_TERM_EN BIT(9) +#define HSIO_SD_CFG_RX_TERM_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_TERM_EN, x) +#define HSIO_SD_CFG_RX_TERM_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_TERM_EN, x) + +#define HSIO_SD_CFG_RX_RESET BIT(8) +#define HSIO_SD_CFG_RX_RESET_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_RESET, x) +#define HSIO_SD_CFG_RX_RESET_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_RESET, x) + +#define HSIO_SD_CFG_RX_RATE GENMASK(7, 6) +#define HSIO_SD_CFG_RX_RATE_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_RATE, x) +#define HSIO_SD_CFG_RX_RATE_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_RATE, x) + +#define HSIO_SD_CFG_RX_PLL_EN BIT(5) +#define HSIO_SD_CFG_RX_PLL_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_PLL_EN, x) +#define HSIO_SD_CFG_RX_PLL_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_PLL_EN, x) + +#define HSIO_SD_CFG_RX_INVERT BIT(3) +#define HSIO_SD_CFG_RX_INVERT_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_INVERT, x) +#define HSIO_SD_CFG_RX_INVERT_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_INVERT, x) + +#define HSIO_SD_CFG_RX_DATA_EN BIT(2) +#define HSIO_SD_CFG_RX_DATA_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_RX_DATA_EN, x) +#define HSIO_SD_CFG_RX_DATA_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_RX_DATA_EN, x) + +#define HSIO_SD_CFG_LANE_LOOPBK_EN BIT(0) +#define HSIO_SD_CFG_LANE_LOOPBK_EN_SET(x)\ + FIELD_PREP(HSIO_SD_CFG_LANE_LOOPBK_EN, x) +#define HSIO_SD_CFG_LANE_LOOPBK_EN_GET(x)\ + FIELD_GET(HSIO_SD_CFG_LANE_LOOPBK_EN, x) + +/* HSIO:SD:MPLL_CFG */ +#define HSIO_MPLL_CFG(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 8, 0, 1, 4) + +#define HSIO_MPLL_CFG_REF_SSP_EN BIT(18) +#define HSIO_MPLL_CFG_REF_SSP_EN_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_REF_SSP_EN, x) +#define HSIO_MPLL_CFG_REF_SSP_EN_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_REF_SSP_EN, x) + +#define HSIO_MPLL_CFG_REF_CLKDIV2 BIT(17) +#define HSIO_MPLL_CFG_REF_CLKDIV2_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_REF_CLKDIV2, x) +#define HSIO_MPLL_CFG_REF_CLKDIV2_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_REF_CLKDIV2, x) + +#define HSIO_MPLL_CFG_MPLL_EN BIT(16) +#define HSIO_MPLL_CFG_MPLL_EN_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_MPLL_EN, x) +#define HSIO_MPLL_CFG_MPLL_EN_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_MPLL_EN, x) + +#define HSIO_MPLL_CFG_MPLL_MULTIPLIER GENMASK(6, 0) +#define HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(x)\ + FIELD_PREP(HSIO_MPLL_CFG_MPLL_MULTIPLIER, x) +#define HSIO_MPLL_CFG_MPLL_MULTIPLIER_GET(x)\ + FIELD_GET(HSIO_MPLL_CFG_MPLL_MULTIPLIER, x) + +/* HSIO:SD:SD_STAT */ +#define HSIO_SD_STAT(g) __REG(TARGET_HSIO, 0, 1, 8, g, 3, 32, 12, 0, 1, 4) + +#define HSIO_SD_STAT_MPLL_STATE BIT(6) +#define HSIO_SD_STAT_MPLL_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_MPLL_STATE, x) +#define HSIO_SD_STAT_MPLL_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_MPLL_STATE, x) + +#define HSIO_SD_STAT_TX_STATE BIT(5) +#define HSIO_SD_STAT_TX_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_TX_STATE, x) +#define HSIO_SD_STAT_TX_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_TX_STATE, x) + +#define HSIO_SD_STAT_TX_CM_STATE BIT(2) +#define HSIO_SD_STAT_TX_CM_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_TX_CM_STATE, x) +#define HSIO_SD_STAT_TX_CM_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_TX_CM_STATE, x) + +#define HSIO_SD_STAT_RX_PLL_STATE BIT(0) +#define HSIO_SD_STAT_RX_PLL_STATE_SET(x)\ + FIELD_PREP(HSIO_SD_STAT_RX_PLL_STATE, x) +#define HSIO_SD_STAT_RX_PLL_STATE_GET(x)\ + FIELD_GET(HSIO_SD_STAT_RX_PLL_STATE, x) + +/* HSIO:HW_CFGSTAT:HW_CFG */ +#define HSIO_HW_CFG __REG(TARGET_HSIO, 0, 1, 104, 0, 1, 52, 0, 0, 1, 4) + +#define HSIO_HW_CFG_RGMII_1_CFG BIT(15) +#define HSIO_HW_CFG_RGMII_1_CFG_SET(x)\ + (((x) << 15) & GENMASK(15, 15)) +#define HSIO_HW_CFG_RGMII_1_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_RGMII_1_CFG, x) + +#define HSIO_HW_CFG_RGMII_0_CFG BIT(14) +#define HSIO_HW_CFG_RGMII_0_CFG_SET(x)\ + (((x) << 14) & GENMASK(14, 14)) +#define HSIO_HW_CFG_RGMII_0_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_RGMII_0_CFG, x) + +#define HSIO_HW_CFG_RGMII_ENA GENMASK(13, 12) +#define HSIO_HW_CFG_RGMII_ENA_SET(x)\ + (((x) << 12) & GENMASK(13, 12)) +#define HSIO_HW_CFG_RGMII_ENA_GET(x)\ + FIELD_GET(HSIO_HW_CFG_RGMII_ENA, x) + +#define HSIO_HW_CFG_SD6G_0_CFG BIT(11) +#define HSIO_HW_CFG_SD6G_0_CFG_SET(x)\ + (((x) << 11) & GENMASK(11, 11)) +#define HSIO_HW_CFG_SD6G_0_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_SD6G_0_CFG, x) + +#define HSIO_HW_CFG_SD6G_1_CFG BIT(10) +#define HSIO_HW_CFG_SD6G_1_CFG_SET(x)\ + (((x) << 10) & GENMASK(10, 10)) +#define HSIO_HW_CFG_SD6G_1_CFG_GET(x)\ + FIELD_GET(HSIO_HW_CFG_SD6G_1_CFG, x) + +#define HSIO_HW_CFG_GMII_ENA GENMASK(9, 2) +#define HSIO_HW_CFG_GMII_ENA_SET(x)\ + (((x) << 2) & GENMASK(9, 2)) +#define HSIO_HW_CFG_GMII_ENA_GET(x)\ + FIELD_GET(HSIO_HW_CFG_GMII_ENA, x) + +#define HSIO_HW_CFG_QSGMII_ENA GENMASK(1, 0) +#define HSIO_HW_CFG_QSGMII_ENA_SET(x)\ + ((x) & GENMASK(1, 0)) +#define HSIO_HW_CFG_QSGMII_ENA_GET(x)\ + FIELD_GET(HSIO_HW_CFG_QSGMII_ENA, x) + +#endif /* _LAN966X_HSIO_REGS_H_ */ -- cgit v1.2.3 From efb6935dd786a9d213ee542ed77d47ece700357c Mon Sep 17 00:00:00 2001 From: Rashmi A Date: Wed, 27 Oct 2021 17:25:15 +0530 Subject: dt-bindings: phy: intel: Add Thunder Bay eMMC PHY bindings Binding description for Intel Thunder Bay eMMC PHY. Added the newly introduced files into MAINTAINERS file-list Signed-off-by: Rashmi A Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211027115516.4475-4-rashmi.a@intel.com Signed-off-by: Vinod Koul --- .../bindings/phy/intel,phy-thunderbay-emmc.yaml | 46 ++++++++++++++++++++++ MAINTAINERS | 7 ++++ 2 files changed, 53 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/intel,phy-thunderbay-emmc.yaml diff --git a/Documentation/devicetree/bindings/phy/intel,phy-thunderbay-emmc.yaml b/Documentation/devicetree/bindings/phy/intel,phy-thunderbay-emmc.yaml new file mode 100644 index 000000000000..34bdb5c4cae8 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/intel,phy-thunderbay-emmc.yaml @@ -0,0 +1,46 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/intel,phy-thunderbay-emmc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Intel Thunder Bay eMMC PHY bindings + +maintainers: + - Srikandan Nandhini + +properties: + compatible: + const: intel,thunderbay-emmc-phy + + "#phy-cells": + const: 0 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: emmcclk + +required: + - "#phy-cells" + - compatible + - reg + - clocks + +additionalProperties: false + +examples: + - | + mmc_phy@80440800 { + #phy-cells = <0x0>; + compatible = "intel,thunderbay-emmc-phy"; + status = "okay"; + reg = <0x80440800 0x100>; + clocks = <&emmc>; + clock-names = "emmcclk"; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..a5604132a90f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9686,6 +9686,13 @@ F: drivers/crypto/keembay/keembay-ocs-hcu-core.c F: drivers/crypto/keembay/ocs-hcu.c F: drivers/crypto/keembay/ocs-hcu.h +INTEL THUNDER BAY EMMC PHY DRIVER +M: Nandhini Srikandan +M: Rashmi A +S: Maintained +F: Documentation/devicetree/bindings/phy/intel,phy-thunderbay-emmc.yaml +F: drivers/phy/intel/phy-intel-thunderbay-emmc.c + INTEL MANAGEMENT ENGINE (mei) M: Tomas Winkler L: linux-kernel@vger.kernel.org -- cgit v1.2.3 From 97004c1a4c52b4357169290158a130ca0b7caae1 Mon Sep 17 00:00:00 2001 From: Rashmi A Date: Wed, 27 Oct 2021 17:25:16 +0530 Subject: phy: intel: Add Thunder Bay eMMC PHY support Add support of eMMC PHY for Intel Thunder Bay SoC, uses the Arasan eMMC phy Signed-off-by: Rashmi A Reviewed-by: Adrian Hunter Link: https://lore.kernel.org/r/20211027115516.4475-5-rashmi.a@intel.com Signed-off-by: Vinod Koul --- drivers/phy/intel/Kconfig | 10 + drivers/phy/intel/Makefile | 1 + drivers/phy/intel/phy-intel-thunderbay-emmc.c | 511 ++++++++++++++++++++++++++ 3 files changed, 522 insertions(+) create mode 100644 drivers/phy/intel/phy-intel-thunderbay-emmc.c diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig index ac42bb2fb394..18a3cc5b98c0 100644 --- a/drivers/phy/intel/Kconfig +++ b/drivers/phy/intel/Kconfig @@ -46,3 +46,13 @@ config PHY_INTEL_LGM_EMMC select GENERIC_PHY help Enable this to support the Intel EMMC PHY + +config PHY_INTEL_THUNDERBAY_EMMC + tristate "Intel Thunder Bay eMMC PHY driver" + depends on OF && (ARCH_THUNDERBAY || COMPILE_TEST) + select GENERIC_PHY + help + This option enables support for Intel Thunder Bay SoC eMMC PHY. + + To compile this driver as a module, choose M here: the module + will be called phy-intel-thunderbay-emmc.ko. diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile index 14550981a707..b7321d56b0bb 100644 --- a/drivers/phy/intel/Makefile +++ b/drivers/phy/intel/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_PHY_INTEL_KEEMBAY_EMMC) += phy-intel-keembay-emmc.o obj-$(CONFIG_PHY_INTEL_KEEMBAY_USB) += phy-intel-keembay-usb.o obj-$(CONFIG_PHY_INTEL_LGM_COMBO) += phy-intel-lgm-combo.o obj-$(CONFIG_PHY_INTEL_LGM_EMMC) += phy-intel-lgm-emmc.o +obj-$(CONFIG_PHY_INTEL_THUNDERBAY_EMMC) += phy-intel-thunderbay-emmc.o diff --git a/drivers/phy/intel/phy-intel-thunderbay-emmc.c b/drivers/phy/intel/phy-intel-thunderbay-emmc.c new file mode 100644 index 000000000000..2d6ea84492f2 --- /dev/null +++ b/drivers/phy/intel/phy-intel-thunderbay-emmc.c @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Intel ThunderBay eMMC PHY driver + * + * Copyright (C) 2021 Intel Corporation + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* eMMC/SD/SDIO core/phy configuration registers */ +#define CTRL_CFG_0 0x00 +#define CTRL_CFG_1 0x04 +#define CTRL_PRESET_0 0x08 +#define CTRL_PRESET_1 0x0c +#define CTRL_PRESET_2 0x10 +#define CTRL_PRESET_3 0x14 +#define CTRL_PRESET_4 0x18 +#define CTRL_CFG_2 0x1c +#define CTRL_CFG_3 0x20 +#define PHY_CFG_0 0x24 +#define PHY_CFG_1 0x28 +#define PHY_CFG_2 0x2c +#define PHYBIST_CTRL 0x30 +#define SDHC_STAT3 0x34 +#define PHY_STAT 0x38 +#define PHYBIST_STAT_0 0x3c +#define PHYBIST_STAT_1 0x40 +#define EMMC_AXI 0x44 + +/* CTRL_PRESET_3 */ +#define CTRL_PRESET3_MASK GENMASK(31, 0) +#define CTRL_PRESET3_SHIFT 0 + +/* CTRL_CFG_0 bit fields */ +#define SUPPORT_HS_MASK BIT(26) +#define SUPPORT_HS_SHIFT 26 + +#define SUPPORT_8B_MASK BIT(24) +#define SUPPORT_8B_SHIFT 24 + +/* CTRL_CFG_1 bit fields */ +#define SUPPORT_SDR50_MASK BIT(28) +#define SUPPORT_SDR50_SHIFT 28 +#define SLOT_TYPE_MASK GENMASK(27, 26) +#define SLOT_TYPE_OFFSET 26 +#define SUPPORT_64B_MASK BIT(24) +#define SUPPORT_64B_SHIFT 24 +#define SUPPORT_HS400_MASK BIT(2) +#define SUPPORT_HS400_SHIFT 2 +#define SUPPORT_DDR50_MASK BIT(1) +#define SUPPORT_DDR50_SHIFT 1 +#define SUPPORT_SDR104_MASK BIT(0) +#define SUPPORT_SDR104_SHIFT 0 + +/* PHY_CFG_0 bit fields */ +#define SEL_DLY_TXCLK_MASK BIT(29) +#define SEL_DLY_TXCLK_SHIFT 29 +#define SEL_DLY_RXCLK_MASK BIT(28) +#define SEL_DLY_RXCLK_SHIFT 28 + +#define OTAP_DLY_ENA_MASK BIT(27) +#define OTAP_DLY_ENA_SHIFT 27 +#define OTAP_DLY_SEL_MASK GENMASK(26, 23) +#define OTAP_DLY_SEL_SHIFT 23 +#define ITAP_CHG_WIN_MASK BIT(22) +#define ITAP_CHG_WIN_SHIFT 22 +#define ITAP_DLY_ENA_MASK BIT(21) +#define ITAP_DLY_ENA_SHIFT 21 +#define ITAP_DLY_SEL_MASK GENMASK(20, 16) +#define ITAP_DLY_SEL_SHIFT 16 +#define RET_ENB_MASK BIT(15) +#define RET_ENB_SHIFT 15 +#define RET_EN_MASK BIT(14) +#define RET_EN_SHIFT 14 +#define DLL_IFF_MASK GENMASK(13, 11) +#define DLL_IFF_SHIFT 11 +#define DLL_EN_MASK BIT(10) +#define DLL_EN_SHIFT 10 +#define DLL_TRIM_ICP_MASK GENMASK(9, 6) +#define DLL_TRIM_ICP_SHIFT 6 +#define RETRIM_EN_MASK BIT(5) +#define RETRIM_EN_SHIFT 5 +#define RETRIM_MASK BIT(4) +#define RETRIM_SHIFT 4 +#define DR_TY_MASK GENMASK(3, 1) +#define DR_TY_SHIFT 1 +#define PWR_DOWN_MASK BIT(0) +#define PWR_DOWN_SHIFT 0 + +/* PHY_CFG_1 bit fields */ +#define REN_DAT_MASK GENMASK(19, 12) +#define REN_DAT_SHIFT 12 +#define REN_CMD_MASK BIT(11) +#define REN_CMD_SHIFT 11 +#define REN_STRB_MASK BIT(10) +#define REN_STRB_SHIFT 10 +#define PU_STRB_MASK BIT(20) +#define PU_STRB_SHIFT 20 + +/* PHY_CFG_2 bit fields */ +#define CLKBUF_MASK GENMASK(24, 21) +#define CLKBUF_SHIFT 21 +#define SEL_STRB_MASK GENMASK(20, 13) +#define SEL_STRB_SHIFT 13 +#define SEL_FREQ_MASK GENMASK(12, 10) +#define SEL_FREQ_SHIFT 10 + +/* PHY_STAT bit fields */ +#define CAL_DONE BIT(6) +#define DLL_RDY BIT(5) + +#define OTAP_DLY 0x0 +#define ITAP_DLY 0x0 +#define STRB 0x33 + +/* From ACS_eMMC51_16nFFC_RO1100_Userguide_v1p0.pdf p17 */ +#define FREQSEL_200M_170M 0x0 +#define FREQSEL_170M_140M 0x1 +#define FREQSEL_140M_110M 0x2 +#define FREQSEL_110M_80M 0x3 +#define FREQSEL_80M_50M 0x4 +#define FREQSEL_275M_250M 0x5 +#define FREQSEL_250M_225M 0x6 +#define FREQSEL_225M_200M 0x7 + +/* Phy power status */ +#define PHY_UNINITIALIZED 0 +#define PHY_INITIALIZED 1 + +/* + * During init(400KHz) phy_settings will be called with 200MHZ clock + * To avoid incorrectly setting the phy for init(400KHZ) "phy_power_sts" is used. + * When actual clock is set always phy is powered off once and then powered on. + * (sdhci_arasan_set_clock). That feature will be used to identify whether the + * settings are for init phy_power_on or actual clock phy_power_on + * 0 --> init settings + * 1 --> actual settings + */ + +struct thunderbay_emmc_phy { + void __iomem *reg_base; + struct clk *emmcclk; + int phy_power_sts; +}; + +static inline void update_reg(struct thunderbay_emmc_phy *tbh_phy, u32 offset, + u32 mask, u32 shift, u32 val) +{ + u32 tmp; + + tmp = readl(tbh_phy->reg_base + offset); + tmp &= ~mask; + tmp |= val << shift; + writel(tmp, tbh_phy->reg_base + offset); +} + +static int thunderbay_emmc_phy_power(struct phy *phy, bool power_on) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + unsigned int freqsel = FREQSEL_200M_170M; + unsigned long rate; + static int lock; + u32 val; + int ret; + + /* Disable DLL */ + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 200000000: + /* lock dll only when it is used, i.e only if SEL_DLY_TXCLK/RXCLK are 0 */ + update_reg(tbh_phy, PHY_CFG_0, DLL_EN_MASK, DLL_EN_SHIFT, 0x0); + break; + + /* dll lock not required for other frequencies */ + case 50000000 ... 52000000: + case 400000: + default: + break; + } + + if (!power_on) + return 0; + + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 170000001 ... 200000000: + freqsel = FREQSEL_200M_170M; + break; + + case 140000001 ... 170000000: + freqsel = FREQSEL_170M_140M; + break; + + case 110000001 ... 140000000: + freqsel = FREQSEL_140M_110M; + break; + + case 80000001 ... 110000000: + freqsel = FREQSEL_110M_80M; + break; + + case 50000000 ... 80000000: + freqsel = FREQSEL_80M_50M; + break; + + case 250000001 ... 275000000: + freqsel = FREQSEL_275M_250M; + break; + + case 225000001 ... 250000000: + freqsel = FREQSEL_250M_225M; + break; + + case 200000001 ... 225000000: + freqsel = FREQSEL_225M_200M; + break; + default: + break; + } + /* Clock rate is checked against upper limit. It may fall low during init */ + if (rate > 200000000) + dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate); + + udelay(5); + + if (lock == 0) { + /* PDB will be done only once per boot */ + update_reg(tbh_phy, PHY_CFG_0, PWR_DOWN_MASK, + PWR_DOWN_SHIFT, 0x1); + lock = 1; + /* + * According to the user manual, it asks driver to wait 5us for + * calpad busy trimming. However it is documented that this value is + * PVT(A.K.A. process, voltage and temperature) relevant, so some + * failure cases are found which indicates we should be more tolerant + * to calpad busy trimming. + */ + ret = readl_poll_timeout(tbh_phy->reg_base + PHY_STAT, + val, (val & CAL_DONE), 10, 50); + if (ret) { + dev_err(&phy->dev, "caldone failed, ret=%d\n", ret); + return ret; + } + } + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 200000000: + /* Set frequency of the DLL operation */ + update_reg(tbh_phy, PHY_CFG_2, SEL_FREQ_MASK, SEL_FREQ_SHIFT, freqsel); + + /* Enable DLL */ + update_reg(tbh_phy, PHY_CFG_0, DLL_EN_MASK, DLL_EN_SHIFT, 0x1); + + /* + * After enabling analog DLL circuits docs say that we need 10.2 us if + * our source clock is at 50 MHz and that lock time scales linearly + * with clock speed. If we are powering on the PHY and the card clock + * is super slow (like 100kHz) this could take as long as 5.1 ms as + * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms + * hopefully we won't be running at 100 kHz, but we should still make + * sure we wait long enough. + * + * NOTE: There appear to be corner cases where the DLL seems to take + * extra long to lock for reasons that aren't understood. In some + * extreme cases we've seen it take up to over 10ms (!). We'll be + * generous and give it 50ms. + */ + ret = readl_poll_timeout(tbh_phy->reg_base + PHY_STAT, + val, (val & DLL_RDY), 10, 50 * USEC_PER_MSEC); + if (ret) { + dev_err(&phy->dev, "dllrdy failed, ret=%d\n", ret); + return ret; + } + break; + + default: + break; + } + return 0; +} + +static int thunderbay_emmc_phy_init(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + + tbh_phy->emmcclk = clk_get(&phy->dev, "emmcclk"); + + return PTR_ERR_OR_ZERO(tbh_phy->emmcclk); +} + +static int thunderbay_emmc_phy_exit(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + + clk_put(tbh_phy->emmcclk); + + return 0; +} + +static int thunderbay_emmc_phy_power_on(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + unsigned long rate; + + /* Overwrite capability bits configurable in bootloader */ + update_reg(tbh_phy, CTRL_CFG_0, + SUPPORT_HS_MASK, SUPPORT_HS_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_0, + SUPPORT_8B_MASK, SUPPORT_8B_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_SDR50_MASK, SUPPORT_SDR50_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_DDR50_MASK, SUPPORT_DDR50_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_SDR104_MASK, SUPPORT_SDR104_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_HS400_MASK, SUPPORT_HS400_SHIFT, 0x1); + update_reg(tbh_phy, CTRL_CFG_1, + SUPPORT_64B_MASK, SUPPORT_64B_SHIFT, 0x1); + + if (tbh_phy->phy_power_sts == PHY_UNINITIALIZED) { + /* Indicates initialization, settings for init, same as 400KHZ setting */ + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, OTAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, OTAP_DLY_SEL_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, DLL_TRIM_ICP_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, DR_TY_SHIFT, 0x1); + + } else if (tbh_phy->phy_power_sts == PHY_INITIALIZED) { + /* Indicates actual clock setting */ + rate = clk_get_rate(tbh_phy->emmcclk); + switch (rate) { + case 200000000: + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 2); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0x8); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, + DR_TY_SHIFT, 0x1); + /* For HS400 only */ + update_reg(tbh_phy, PHY_CFG_2, SEL_STRB_MASK, + SEL_STRB_SHIFT, STRB); + break; + + case 50000000 ... 52000000: + /* For both HS and DDR52 this setting works */ + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 4); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0x8); + update_reg(tbh_phy, PHY_CFG_0, + DR_TY_MASK, DR_TY_SHIFT, 0x1); + break; + + case 400000: + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, DR_TY_SHIFT, 0x1); + break; + + default: + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_TXCLK_MASK, + SEL_DLY_TXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, SEL_DLY_RXCLK_MASK, + SEL_DLY_RXCLK_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_ENA_MASK, + ITAP_DLY_ENA_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, ITAP_DLY_SEL_MASK, + ITAP_DLY_SEL_SHIFT, 0x0); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_ENA_MASK, + OTAP_DLY_ENA_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, OTAP_DLY_SEL_MASK, + OTAP_DLY_SEL_SHIFT, 2); + update_reg(tbh_phy, PHY_CFG_0, DLL_TRIM_ICP_MASK, + DLL_TRIM_ICP_SHIFT, 0x8); + update_reg(tbh_phy, PHY_CFG_0, DR_TY_MASK, + DR_TY_SHIFT, 0x1); + break; + } + /* Reset, init seq called without phy_power_off, this indicates init seq */ + tbh_phy->phy_power_sts = PHY_UNINITIALIZED; + } + + update_reg(tbh_phy, PHY_CFG_0, RETRIM_EN_MASK, RETRIM_EN_SHIFT, 0x1); + update_reg(tbh_phy, PHY_CFG_0, RETRIM_MASK, RETRIM_SHIFT, 0x0); + + return thunderbay_emmc_phy_power(phy, 1); +} + +static int thunderbay_emmc_phy_power_off(struct phy *phy) +{ + struct thunderbay_emmc_phy *tbh_phy = phy_get_drvdata(phy); + + tbh_phy->phy_power_sts = PHY_INITIALIZED; + + return thunderbay_emmc_phy_power(phy, 0); +} + +static const struct phy_ops thunderbay_emmc_phy_ops = { + .init = thunderbay_emmc_phy_init, + .exit = thunderbay_emmc_phy_exit, + .power_on = thunderbay_emmc_phy_power_on, + .power_off = thunderbay_emmc_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct of_device_id thunderbay_emmc_phy_of_match[] = { + { .compatible = "intel,thunderbay-emmc-phy", + (void *)&thunderbay_emmc_phy_ops }, + {} +}; +MODULE_DEVICE_TABLE(of, thunderbay_emmc_phy_of_match); + +static int thunderbay_emmc_phy_probe(struct platform_device *pdev) +{ + struct thunderbay_emmc_phy *tbh_phy; + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + const struct of_device_id *id; + struct phy *generic_phy; + struct resource *res; + + if (!dev->of_node) + return -ENODEV; + + tbh_phy = devm_kzalloc(dev, sizeof(*tbh_phy), GFP_KERNEL); + if (!tbh_phy) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tbh_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(tbh_phy->reg_base)) { + dev_err(&pdev->dev, "region map failed\n"); + return PTR_ERR(tbh_phy->reg_base); + } + + tbh_phy->phy_power_sts = PHY_UNINITIALIZED; + id = of_match_node(thunderbay_emmc_phy_of_match, pdev->dev.of_node); + if (!id) { + dev_err(dev, "failed to get match_node\n"); + return -EINVAL; + } + + generic_phy = devm_phy_create(dev, dev->of_node, id->data); + if (IS_ERR(generic_phy)) { + dev_err(dev, "failed to create PHY\n"); + return PTR_ERR(generic_phy); + } + + phy_set_drvdata(generic_phy, tbh_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static struct platform_driver thunderbay_emmc_phy_driver = { + .probe = thunderbay_emmc_phy_probe, + .driver = { + .name = "thunderbay-emmc-phy", + .of_match_table = thunderbay_emmc_phy_of_match, + }, +}; +module_platform_driver(thunderbay_emmc_phy_driver); + +MODULE_AUTHOR("Nandhini S "); +MODULE_AUTHOR("Rashmi A "); +MODULE_DESCRIPTION("Intel Thunder Bay eMMC PHY driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From bb349fd2d58062c69508414a9080d822b8d096b5 Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 16 Nov 2021 10:50:17 +0000 Subject: soundwire: qcom: remove redundant version number read Controller version is already available in struct qcom_swrm_ctrl, Just make use of it instead of reading this again. Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211116105017.12010-1-srinivas.kandagatla@linaro.org Signed-off-by: Vinod Koul --- drivers/soundwire/qcom.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index a317bea2d42d..46995bb382eb 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -1156,11 +1156,7 @@ static int qcom_swrm_get_port_config(struct qcom_swrm_ctrl *ctrl) ret = of_property_read_u8_array(np, "qcom,ports-block-pack-mode", bp_mode, nports); if (ret) { - u32 version; - - ctrl->reg_read(ctrl, SWRM_COMP_HW_VERSION, &version); - - if (version <= 0x01030000) + if (ctrl->version <= 0x01030000) memset(bp_mode, SWR_INVALID_PARAM, QCOM_SDW_MAX_PORTS); else return ret; -- cgit v1.2.3 From 97ba12d3feca68dd240ba49c9559d9a3e13cf0cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 23 Nov 2021 23:15:21 +0100 Subject: phy: bcm-ns-usb2: improve printing ref clk errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve message & use dev_err_probe() helper which prints actual error (helpful for debugging) and deals with -EPROBE_DEFER. Signed-off-by: Rafał Miłecki Reviewed-by: Florian Fainelli Link: https://lore.kernel.org/r/20211123221521.25323-1-zajec5@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/broadcom/phy-bcm-ns-usb2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/broadcom/phy-bcm-ns-usb2.c b/drivers/phy/broadcom/phy-bcm-ns-usb2.c index 98d32729a45d..6a36e187d100 100644 --- a/drivers/phy/broadcom/phy-bcm-ns-usb2.c +++ b/drivers/phy/broadcom/phy-bcm-ns-usb2.c @@ -132,7 +132,7 @@ static int bcm_ns_usb2_probe(struct platform_device *pdev) usb2->ref_clk = devm_clk_get(dev, "phy-ref-clk"); if (IS_ERR(usb2->ref_clk)) { - dev_err(dev, "Clock not defined\n"); + dev_err_probe(dev, PTR_ERR(usb2->ref_clk), "failed to get ref clk\n"); return PTR_ERR(usb2->ref_clk); } -- cgit v1.2.3 From 77ba6e7ffbd8b0afe3e475629d5fcb52e7447405 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Mon, 25 Oct 2021 16:31:05 +0200 Subject: phy: stm32: adopt dev_err_probe for regulators Change stm32-usbphyc driver to use dev_err_probe(), to benefit of devices_deferred debugfs in case of probe deferral. Signed-off-by: Fabrice Gasnier Reviewed-by: Amelie Delaunay Link: https://lore.kernel.org/r/1635172265-26219-1-git-send-email-fabrice.gasnier@foss.st.com Signed-off-by: Vinod Koul --- drivers/phy/st/phy-stm32-usbphyc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/phy/st/phy-stm32-usbphyc.c b/drivers/phy/st/phy-stm32-usbphyc.c index 7df6a63ad37b..115be0eea0d6 100644 --- a/drivers/phy/st/phy-stm32-usbphyc.c +++ b/drivers/phy/st/phy-stm32-usbphyc.c @@ -672,17 +672,15 @@ static int stm32_usbphyc_probe(struct platform_device *pdev) usbphyc->vdda1v1 = devm_regulator_get(dev, "vdda1v1"); if (IS_ERR(usbphyc->vdda1v1)) { - ret = PTR_ERR(usbphyc->vdda1v1); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get vdda1v1 supply: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v1), + "failed to get vdda1v1 supply\n"); goto clk_disable; } usbphyc->vdda1v8 = devm_regulator_get(dev, "vdda1v8"); if (IS_ERR(usbphyc->vdda1v8)) { - ret = PTR_ERR(usbphyc->vdda1v8); - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to get vdda1v8 supply: %d\n", ret); + ret = dev_err_probe(dev, PTR_ERR(usbphyc->vdda1v8), + "failed to get vdda1v8 supply\n"); goto clk_disable; } -- cgit v1.2.3 From b4c80629c5c9d48880c5ad99943374f9ab72432e Mon Sep 17 00:00:00 2001 From: Heinrich Schuchardt Date: Sun, 23 May 2021 22:49:57 +0200 Subject: include/linux/byteorder/generic.h: fix index variables In cpu_to_be32_array() and be32_to_cpu_array() the length of the array is given by variable len of type size_t. An index variable of type int is used to iterate over the array. This is bound to fail for len > INT_MAX and lets GCC add instructions for sign extension. Correct the type of the index variable. Signed-off-by: Heinrich Schuchardt Link: https://lore.kernel.org/r/20210523204958.64575-1-xypron.glpk@gmx.de Signed-off-by: Greg Kroah-Hartman --- include/linux/byteorder/generic.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/byteorder/generic.h b/include/linux/byteorder/generic.h index 4b13e0a3e15b..c9a4c96c9943 100644 --- a/include/linux/byteorder/generic.h +++ b/include/linux/byteorder/generic.h @@ -190,7 +190,7 @@ static inline void be64_add_cpu(__be64 *var, u64 val) static inline void cpu_to_be32_array(__be32 *dst, const u32 *src, size_t len) { - int i; + size_t i; for (i = 0; i < len; i++) dst[i] = cpu_to_be32(src[i]); @@ -198,7 +198,7 @@ static inline void cpu_to_be32_array(__be32 *dst, const u32 *src, size_t len) static inline void be32_to_cpu_array(u32 *dst, const __be32 *src, size_t len) { - int i; + size_t i; for (i = 0; i < len; i++) dst[i] = be32_to_cpu(src[i]); -- cgit v1.2.3 From df0e68c1e9945e2ee86d266ce45597bbd8299b06 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 17 Nov 2021 12:05:59 +0000 Subject: comedi: Move the main COMEDI headers Move the main COMEDI driver headers out of "drivers/comedi/" into new directory "include/linux/comedi/". These are "comedidev.h", "comedilib.h", "comedi_pci.h", "comedi_pcmcia.h", and "comedi_usb.h". Additionally, move the user-space API header "comedi.h" into "include/uapi/linux/" and add "WITH Linux-syscall-note" to its SPDX-License-Identifier. Update the "COMEDI DRIVERS" section of the MAINTAINERS file to account for these changes. Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20211117120604.117740-2-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 2 + drivers/comedi/comedi.h | 1528 -------------------- drivers/comedi/comedi_buf.c | 3 +- drivers/comedi/comedi_fops.c | 2 +- drivers/comedi/comedi_pci.c | 3 +- drivers/comedi/comedi_pci.h | 57 - drivers/comedi/comedi_pcmcia.c | 3 +- drivers/comedi/comedi_pcmcia.h | 49 - drivers/comedi/comedi_usb.c | 3 +- drivers/comedi/comedi_usb.h | 42 - drivers/comedi/comedidev.h | 1054 -------------- drivers/comedi/comedilib.h | 26 - drivers/comedi/drivers.c | 3 +- drivers/comedi/drivers/8255.c | 2 +- drivers/comedi/drivers/8255_pci.c | 3 +- drivers/comedi/drivers/addi_apci_1032.c | 2 +- drivers/comedi/drivers/addi_apci_1500.c | 2 +- drivers/comedi/drivers/addi_apci_1516.c | 2 +- drivers/comedi/drivers/addi_apci_1564.c | 2 +- drivers/comedi/drivers/addi_apci_16xx.c | 3 +- drivers/comedi/drivers/addi_apci_2032.c | 2 +- drivers/comedi/drivers/addi_apci_2200.c | 2 +- drivers/comedi/drivers/addi_apci_3120.c | 2 +- drivers/comedi/drivers/addi_apci_3501.c | 2 +- drivers/comedi/drivers/addi_apci_3xxx.c | 3 +- drivers/comedi/drivers/addi_watchdog.c | 2 +- drivers/comedi/drivers/adl_pci6208.c | 3 +- drivers/comedi/drivers/adl_pci7x3x.c | 3 +- drivers/comedi/drivers/adl_pci8164.c | 3 +- drivers/comedi/drivers/adl_pci9111.c | 3 +- drivers/comedi/drivers/adl_pci9118.c | 3 +- drivers/comedi/drivers/adq12b.c | 3 +- drivers/comedi/drivers/adv_pci1710.c | 3 +- drivers/comedi/drivers/adv_pci1720.c | 3 +- drivers/comedi/drivers/adv_pci1723.c | 3 +- drivers/comedi/drivers/adv_pci1724.c | 3 +- drivers/comedi/drivers/adv_pci1760.c | 3 +- drivers/comedi/drivers/adv_pci_dio.c | 3 +- drivers/comedi/drivers/aio_aio12_8.c | 2 +- drivers/comedi/drivers/aio_iiro_16.c | 3 +- drivers/comedi/drivers/amplc_dio200.c | 2 +- drivers/comedi/drivers/amplc_dio200_common.c | 3 +- drivers/comedi/drivers/amplc_dio200_pci.c | 3 +- drivers/comedi/drivers/amplc_pc236.c | 3 +- drivers/comedi/drivers/amplc_pc236_common.c | 3 +- drivers/comedi/drivers/amplc_pc263.c | 2 +- drivers/comedi/drivers/amplc_pci224.c | 3 +- drivers/comedi/drivers/amplc_pci230.c | 3 +- drivers/comedi/drivers/amplc_pci236.c | 3 +- drivers/comedi/drivers/amplc_pci263.c | 3 +- drivers/comedi/drivers/c6xdigio.c | 3 +- drivers/comedi/drivers/cb_das16_cs.c | 3 +- drivers/comedi/drivers/cb_pcidas.c | 3 +- drivers/comedi/drivers/cb_pcidas64.c | 3 +- drivers/comedi/drivers/cb_pcidda.c | 3 +- drivers/comedi/drivers/cb_pcimdas.c | 3 +- drivers/comedi/drivers/cb_pcimdda.c | 3 +- drivers/comedi/drivers/comedi_8254.c | 3 +- drivers/comedi/drivers/comedi_8255.c | 2 +- drivers/comedi/drivers/comedi_bond.c | 6 +- drivers/comedi/drivers/comedi_isadma.c | 3 +- drivers/comedi/drivers/comedi_parport.c | 3 +- drivers/comedi/drivers/comedi_test.c | 4 +- drivers/comedi/drivers/contec_pci_dio.c | 3 +- drivers/comedi/drivers/dac02.c | 3 +- drivers/comedi/drivers/daqboard2000.c | 3 +- drivers/comedi/drivers/das08.c | 3 +- drivers/comedi/drivers/das08_cs.c | 3 +- drivers/comedi/drivers/das08_isa.c | 2 +- drivers/comedi/drivers/das08_pci.c | 3 +- drivers/comedi/drivers/das16.c | 3 +- drivers/comedi/drivers/das16m1.c | 2 +- drivers/comedi/drivers/das1800.c | 3 +- drivers/comedi/drivers/das6402.c | 3 +- drivers/comedi/drivers/das800.c | 3 +- drivers/comedi/drivers/dmm32at.c | 2 +- drivers/comedi/drivers/dt2801.c | 2 +- drivers/comedi/drivers/dt2811.c | 3 +- drivers/comedi/drivers/dt2814.c | 3 +- drivers/comedi/drivers/dt2815.c | 3 +- drivers/comedi/drivers/dt2817.c | 2 +- drivers/comedi/drivers/dt282x.c | 3 +- drivers/comedi/drivers/dt3000.c | 3 +- drivers/comedi/drivers/dt9812.c | 3 +- drivers/comedi/drivers/dyna_pci10xx.c | 3 +- drivers/comedi/drivers/fl512.c | 3 +- drivers/comedi/drivers/gsc_hpdi.c | 3 +- drivers/comedi/drivers/icp_multi.c | 3 +- drivers/comedi/drivers/ii_pci20kc.c | 2 +- drivers/comedi/drivers/jr3_pci.c | 3 +- drivers/comedi/drivers/ke_counter.c | 3 +- drivers/comedi/drivers/me4000.c | 3 +- drivers/comedi/drivers/me_daq.c | 3 +- drivers/comedi/drivers/mf6x4.c | 3 +- drivers/comedi/drivers/mite.c | 3 +- drivers/comedi/drivers/mpc624.c | 3 +- drivers/comedi/drivers/multiq3.c | 3 +- drivers/comedi/drivers/ni_6527.c | 3 +- drivers/comedi/drivers/ni_65xx.c | 3 +- drivers/comedi/drivers/ni_660x.c | 3 +- drivers/comedi/drivers/ni_670x.c | 3 +- drivers/comedi/drivers/ni_at_a2150.c | 3 +- drivers/comedi/drivers/ni_at_ao.c | 3 +- drivers/comedi/drivers/ni_atmio.c | 3 +- drivers/comedi/drivers/ni_atmio16d.c | 2 +- drivers/comedi/drivers/ni_daq_700.c | 3 +- drivers/comedi/drivers/ni_daq_dio24.c | 2 +- drivers/comedi/drivers/ni_labpc.c | 3 +- drivers/comedi/drivers/ni_labpc_common.c | 3 +- drivers/comedi/drivers/ni_labpc_cs.c | 3 +- drivers/comedi/drivers/ni_labpc_isadma.c | 3 +- drivers/comedi/drivers/ni_labpc_pci.c | 3 +- drivers/comedi/drivers/ni_mio_cs.c | 2 +- drivers/comedi/drivers/ni_pcidio.c | 3 +- drivers/comedi/drivers/ni_pcimio.c | 4 +- drivers/comedi/drivers/ni_routes.c | 3 +- drivers/comedi/drivers/ni_routes.h | 2 +- .../comedi/drivers/ni_routing/ni_route_values.h | 2 +- drivers/comedi/drivers/ni_tio.h | 2 +- drivers/comedi/drivers/ni_usb6501.c | 3 +- drivers/comedi/drivers/pcl711.c | 3 +- drivers/comedi/drivers/pcl724.c | 2 +- drivers/comedi/drivers/pcl726.c | 3 +- drivers/comedi/drivers/pcl730.c | 2 +- drivers/comedi/drivers/pcl812.c | 3 +- drivers/comedi/drivers/pcl816.c | 3 +- drivers/comedi/drivers/pcl818.c | 3 +- drivers/comedi/drivers/pcm3724.c | 2 +- drivers/comedi/drivers/pcmad.c | 2 +- drivers/comedi/drivers/pcmda12.c | 2 +- drivers/comedi/drivers/pcmmio.c | 3 +- drivers/comedi/drivers/pcmuio.c | 3 +- drivers/comedi/drivers/quatech_daqp_cs.c | 3 +- drivers/comedi/drivers/rtd520.c | 3 +- drivers/comedi/drivers/rti800.c | 2 +- drivers/comedi/drivers/rti802.c | 2 +- drivers/comedi/drivers/s526.c | 2 +- drivers/comedi/drivers/s626.c | 3 +- drivers/comedi/drivers/ssv_dnp.c | 2 +- drivers/comedi/drivers/usbdux.c | 3 +- drivers/comedi/drivers/usbduxfast.c | 2 +- drivers/comedi/drivers/usbduxsigma.c | 3 +- drivers/comedi/drivers/vmk80xx.c | 3 +- drivers/comedi/kcomedilib/kcomedilib_main.c | 6 +- drivers/comedi/proc.c | 2 +- drivers/comedi/range.c | 2 +- include/linux/comedi/comedi_pci.h | 56 + include/linux/comedi/comedi_pcmcia.h | 48 + include/linux/comedi/comedi_usb.h | 41 + include/linux/comedi/comedidev.h | 1053 ++++++++++++++ include/linux/comedi/comedilib.h | 26 + include/uapi/linux/comedi.h | 1528 ++++++++++++++++++++ 152 files changed, 2897 insertions(+), 2999 deletions(-) delete mode 100644 drivers/comedi/comedi.h delete mode 100644 drivers/comedi/comedi_pci.h delete mode 100644 drivers/comedi/comedi_pcmcia.h delete mode 100644 drivers/comedi/comedi_usb.h delete mode 100644 drivers/comedi/comedidev.h delete mode 100644 drivers/comedi/comedilib.h create mode 100644 include/linux/comedi/comedi_pci.h create mode 100644 include/linux/comedi/comedi_pcmcia.h create mode 100644 include/linux/comedi/comedi_usb.h create mode 100644 include/linux/comedi/comedidev.h create mode 100644 include/linux/comedi/comedilib.h create mode 100644 include/uapi/linux/comedi.h diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..f9b50d136c90 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4713,6 +4713,8 @@ M: Ian Abbott M: H Hartley Sweeten S: Odd Fixes F: drivers/comedi/ +F: include/linux/comedi/ +F: include/uapi/linux/comedi.h COMMON CLK FRAMEWORK M: Michael Turquette diff --git a/drivers/comedi/comedi.h b/drivers/comedi/comedi.h deleted file mode 100644 index b5d00a006dbb..000000000000 --- a/drivers/comedi/comedi.h +++ /dev/null @@ -1,1528 +0,0 @@ -/* SPDX-License-Identifier: LGPL-2.0+ */ -/* - * comedi.h - * header file for COMEDI user API - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1998-2001 David A. Schleef - */ - -#ifndef _COMEDI_H -#define _COMEDI_H - -#define COMEDI_MAJORVERSION 0 -#define COMEDI_MINORVERSION 7 -#define COMEDI_MICROVERSION 76 -#define VERSION "0.7.76" - -/* comedi's major device number */ -#define COMEDI_MAJOR 98 - -/* - * maximum number of minor devices. This can be increased, although - * kernel structures are currently statically allocated, thus you - * don't want this to be much more than you actually use. - */ -#define COMEDI_NDEVICES 16 - -/* number of config options in the config structure */ -#define COMEDI_NDEVCONFOPTS 32 - -/* - * NOTE: 'comedi_config --init-data' is deprecated - * - * The following indexes in the config options were used by - * comedi_config to pass firmware blobs from user space to the - * comedi drivers. The request_firmware() hotplug interface is - * now used by all comedi drivers instead. - */ - -/* length of nth chunk of firmware data -*/ -#define COMEDI_DEVCONF_AUX_DATA3_LENGTH 25 -#define COMEDI_DEVCONF_AUX_DATA2_LENGTH 26 -#define COMEDI_DEVCONF_AUX_DATA1_LENGTH 27 -#define COMEDI_DEVCONF_AUX_DATA0_LENGTH 28 -/* most significant 32 bits of pointer address (if needed) */ -#define COMEDI_DEVCONF_AUX_DATA_HI 29 -/* least significant 32 bits of pointer address */ -#define COMEDI_DEVCONF_AUX_DATA_LO 30 -#define COMEDI_DEVCONF_AUX_DATA_LENGTH 31 /* total data length */ - -/* max length of device and driver names */ -#define COMEDI_NAMELEN 20 - -/* packs and unpacks a channel/range number */ - -#define CR_PACK(chan, rng, aref) \ - ((((aref) & 0x3) << 24) | (((rng) & 0xff) << 16) | (chan)) -#define CR_PACK_FLAGS(chan, range, aref, flags) \ - (CR_PACK(chan, range, aref) | ((flags) & CR_FLAGS_MASK)) - -#define CR_CHAN(a) ((a) & 0xffff) -#define CR_RANGE(a) (((a) >> 16) & 0xff) -#define CR_AREF(a) (((a) >> 24) & 0x03) - -#define CR_FLAGS_MASK 0xfc000000 -#define CR_ALT_FILTER 0x04000000 -#define CR_DITHER CR_ALT_FILTER -#define CR_DEGLITCH CR_ALT_FILTER -#define CR_ALT_SOURCE 0x08000000 -#define CR_EDGE 0x40000000 -#define CR_INVERT 0x80000000 - -#define AREF_GROUND 0x00 /* analog ref = analog ground */ -#define AREF_COMMON 0x01 /* analog ref = analog common */ -#define AREF_DIFF 0x02 /* analog ref = differential */ -#define AREF_OTHER 0x03 /* analog ref = other (undefined) */ - -/* counters -- these are arbitrary values */ -#define GPCT_RESET 0x0001 -#define GPCT_SET_SOURCE 0x0002 -#define GPCT_SET_GATE 0x0004 -#define GPCT_SET_DIRECTION 0x0008 -#define GPCT_SET_OPERATION 0x0010 -#define GPCT_ARM 0x0020 -#define GPCT_DISARM 0x0040 -#define GPCT_GET_INT_CLK_FRQ 0x0080 - -#define GPCT_INT_CLOCK 0x0001 -#define GPCT_EXT_PIN 0x0002 -#define GPCT_NO_GATE 0x0004 -#define GPCT_UP 0x0008 -#define GPCT_DOWN 0x0010 -#define GPCT_HWUD 0x0020 -#define GPCT_SIMPLE_EVENT 0x0040 -#define GPCT_SINGLE_PERIOD 0x0080 -#define GPCT_SINGLE_PW 0x0100 -#define GPCT_CONT_PULSE_OUT 0x0200 -#define GPCT_SINGLE_PULSE_OUT 0x0400 - -/* instructions */ - -#define INSN_MASK_WRITE 0x8000000 -#define INSN_MASK_READ 0x4000000 -#define INSN_MASK_SPECIAL 0x2000000 - -#define INSN_READ (0 | INSN_MASK_READ) -#define INSN_WRITE (1 | INSN_MASK_WRITE) -#define INSN_BITS (2 | INSN_MASK_READ | INSN_MASK_WRITE) -#define INSN_CONFIG (3 | INSN_MASK_READ | INSN_MASK_WRITE) -#define INSN_DEVICE_CONFIG (INSN_CONFIG | INSN_MASK_SPECIAL) -#define INSN_GTOD (4 | INSN_MASK_READ | INSN_MASK_SPECIAL) -#define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) -#define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) - -/* command flags */ -/* These flags are used in comedi_cmd structures */ - -#define CMDF_BOGUS 0x00000001 /* do the motions */ - -/* try to use a real-time interrupt while performing command */ -#define CMDF_PRIORITY 0x00000008 - -/* wake up on end-of-scan events */ -#define CMDF_WAKE_EOS 0x00000020 - -#define CMDF_WRITE 0x00000040 - -#define CMDF_RAWDATA 0x00000080 - -/* timer rounding definitions */ -#define CMDF_ROUND_MASK 0x00030000 -#define CMDF_ROUND_NEAREST 0x00000000 -#define CMDF_ROUND_DOWN 0x00010000 -#define CMDF_ROUND_UP 0x00020000 -#define CMDF_ROUND_UP_NEXT 0x00030000 - -#define COMEDI_EV_START 0x00040000 -#define COMEDI_EV_SCAN_BEGIN 0x00080000 -#define COMEDI_EV_CONVERT 0x00100000 -#define COMEDI_EV_SCAN_END 0x00200000 -#define COMEDI_EV_STOP 0x00400000 - -/* compatibility definitions */ -#define TRIG_BOGUS CMDF_BOGUS -#define TRIG_RT CMDF_PRIORITY -#define TRIG_WAKE_EOS CMDF_WAKE_EOS -#define TRIG_WRITE CMDF_WRITE -#define TRIG_ROUND_MASK CMDF_ROUND_MASK -#define TRIG_ROUND_NEAREST CMDF_ROUND_NEAREST -#define TRIG_ROUND_DOWN CMDF_ROUND_DOWN -#define TRIG_ROUND_UP CMDF_ROUND_UP -#define TRIG_ROUND_UP_NEXT CMDF_ROUND_UP_NEXT - -/* trigger sources */ - -#define TRIG_ANY 0xffffffff -#define TRIG_INVALID 0x00000000 - -#define TRIG_NONE 0x00000001 /* never trigger */ -#define TRIG_NOW 0x00000002 /* trigger now + N ns */ -#define TRIG_FOLLOW 0x00000004 /* trigger on next lower level trig */ -#define TRIG_TIME 0x00000008 /* trigger at time N ns */ -#define TRIG_TIMER 0x00000010 /* trigger at rate N ns */ -#define TRIG_COUNT 0x00000020 /* trigger when count reaches N */ -#define TRIG_EXT 0x00000040 /* trigger on external signal N */ -#define TRIG_INT 0x00000080 /* trigger on comedi-internal signal N */ -#define TRIG_OTHER 0x00000100 /* driver defined */ - -/* subdevice flags */ - -#define SDF_BUSY 0x0001 /* device is busy */ -#define SDF_BUSY_OWNER 0x0002 /* device is busy with your job */ -#define SDF_LOCKED 0x0004 /* subdevice is locked */ -#define SDF_LOCK_OWNER 0x0008 /* you own lock */ -#define SDF_MAXDATA 0x0010 /* maxdata depends on channel */ -#define SDF_FLAGS 0x0020 /* flags depend on channel */ -#define SDF_RANGETYPE 0x0040 /* range type depends on channel */ -#define SDF_PWM_COUNTER 0x0080 /* PWM can automatically switch off */ -#define SDF_PWM_HBRIDGE 0x0100 /* PWM is signed (H-bridge) */ -#define SDF_CMD 0x1000 /* can do commands (deprecated) */ -#define SDF_SOFT_CALIBRATED 0x2000 /* subdevice uses software calibration */ -#define SDF_CMD_WRITE 0x4000 /* can do output commands */ -#define SDF_CMD_READ 0x8000 /* can do input commands */ - -/* subdevice can be read (e.g. analog input) */ -#define SDF_READABLE 0x00010000 -/* subdevice can be written (e.g. analog output) */ -#define SDF_WRITABLE 0x00020000 -#define SDF_WRITEABLE SDF_WRITABLE /* spelling error in API */ -/* subdevice does not have externally visible lines */ -#define SDF_INTERNAL 0x00040000 -#define SDF_GROUND 0x00100000 /* can do aref=ground */ -#define SDF_COMMON 0x00200000 /* can do aref=common */ -#define SDF_DIFF 0x00400000 /* can do aref=diff */ -#define SDF_OTHER 0x00800000 /* can do aref=other */ -#define SDF_DITHER 0x01000000 /* can do dithering */ -#define SDF_DEGLITCH 0x02000000 /* can do deglitching */ -#define SDF_MMAP 0x04000000 /* can do mmap() */ -#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */ -#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */ -#define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */ - -/* subdevice types */ - -/** - * enum comedi_subdevice_type - COMEDI subdevice types - * @COMEDI_SUBD_UNUSED: Unused subdevice. - * @COMEDI_SUBD_AI: Analog input. - * @COMEDI_SUBD_AO: Analog output. - * @COMEDI_SUBD_DI: Digital input. - * @COMEDI_SUBD_DO: Digital output. - * @COMEDI_SUBD_DIO: Digital input/output. - * @COMEDI_SUBD_COUNTER: Counter. - * @COMEDI_SUBD_TIMER: Timer. - * @COMEDI_SUBD_MEMORY: Memory, EEPROM, DPRAM. - * @COMEDI_SUBD_CALIB: Calibration DACs. - * @COMEDI_SUBD_PROC: Processor, DSP. - * @COMEDI_SUBD_SERIAL: Serial I/O. - * @COMEDI_SUBD_PWM: Pulse-Width Modulation output. - */ -enum comedi_subdevice_type { - COMEDI_SUBD_UNUSED, - COMEDI_SUBD_AI, - COMEDI_SUBD_AO, - COMEDI_SUBD_DI, - COMEDI_SUBD_DO, - COMEDI_SUBD_DIO, - COMEDI_SUBD_COUNTER, - COMEDI_SUBD_TIMER, - COMEDI_SUBD_MEMORY, - COMEDI_SUBD_CALIB, - COMEDI_SUBD_PROC, - COMEDI_SUBD_SERIAL, - COMEDI_SUBD_PWM -}; - -/* configuration instructions */ - -/** - * enum comedi_io_direction - COMEDI I/O directions - * @COMEDI_INPUT: Input. - * @COMEDI_OUTPUT: Output. - * @COMEDI_OPENDRAIN: Open-drain (or open-collector) output. - * - * These are used by the %INSN_CONFIG_DIO_QUERY configuration instruction to - * report a direction. They may also be used in other places where a direction - * needs to be specified. - */ -enum comedi_io_direction { - COMEDI_INPUT = 0, - COMEDI_OUTPUT = 1, - COMEDI_OPENDRAIN = 2 -}; - -/** - * enum configuration_ids - COMEDI configuration instruction codes - * @INSN_CONFIG_DIO_INPUT: Configure digital I/O as input. - * @INSN_CONFIG_DIO_OUTPUT: Configure digital I/O as output. - * @INSN_CONFIG_DIO_OPENDRAIN: Configure digital I/O as open-drain (or open - * collector) output. - * @INSN_CONFIG_ANALOG_TRIG: Configure analog trigger. - * @INSN_CONFIG_ALT_SOURCE: Configure alternate input source. - * @INSN_CONFIG_DIGITAL_TRIG: Configure digital trigger. - * @INSN_CONFIG_BLOCK_SIZE: Configure block size for DMA transfers. - * @INSN_CONFIG_TIMER_1: Configure divisor for external clock. - * @INSN_CONFIG_FILTER: Configure a filter. - * @INSN_CONFIG_CHANGE_NOTIFY: Configure change notification for digital - * inputs. (New drivers should use - * %INSN_CONFIG_DIGITAL_TRIG instead.) - * @INSN_CONFIG_SERIAL_CLOCK: Configure clock for serial I/O. - * @INSN_CONFIG_BIDIRECTIONAL_DATA: Send and receive byte over serial I/O. - * @INSN_CONFIG_DIO_QUERY: Query direction of digital I/O channel. - * @INSN_CONFIG_PWM_OUTPUT: Configure pulse-width modulator output. - * @INSN_CONFIG_GET_PWM_OUTPUT: Get pulse-width modulator output configuration. - * @INSN_CONFIG_ARM: Arm a subdevice or channel. - * @INSN_CONFIG_DISARM: Disarm a subdevice or channel. - * @INSN_CONFIG_GET_COUNTER_STATUS: Get counter status. - * @INSN_CONFIG_RESET: Reset a subdevice or channel. - * @INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: Configure counter/timer as - * single pulse generator. - * @INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: Configure counter/timer as - * pulse train generator. - * @INSN_CONFIG_GPCT_QUADRATURE_ENCODER: Configure counter as a quadrature - * encoder. - * @INSN_CONFIG_SET_GATE_SRC: Set counter/timer gate source. - * @INSN_CONFIG_GET_GATE_SRC: Get counter/timer gate source. - * @INSN_CONFIG_SET_CLOCK_SRC: Set counter/timer master clock source. - * @INSN_CONFIG_GET_CLOCK_SRC: Get counter/timer master clock source. - * @INSN_CONFIG_SET_OTHER_SRC: Set counter/timer "other" source. - * @INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: Get size (in bytes) of subdevice's - * on-board FIFOs used during streaming - * input/output. - * @INSN_CONFIG_SET_COUNTER_MODE: Set counter/timer mode. - * @INSN_CONFIG_8254_SET_MODE: (Deprecated) Same as - * %INSN_CONFIG_SET_COUNTER_MODE. - * @INSN_CONFIG_8254_READ_STATUS: Read status of 8254 counter channel. - * @INSN_CONFIG_SET_ROUTING: Set routing for a channel. - * @INSN_CONFIG_GET_ROUTING: Get routing for a channel. - * @INSN_CONFIG_PWM_SET_PERIOD: Set PWM period in nanoseconds. - * @INSN_CONFIG_PWM_GET_PERIOD: Get PWM period in nanoseconds. - * @INSN_CONFIG_GET_PWM_STATUS: Get PWM status. - * @INSN_CONFIG_PWM_SET_H_BRIDGE: Set PWM H bridge duty cycle and polarity for - * a relay simultaneously. - * @INSN_CONFIG_PWM_GET_H_BRIDGE: Get PWM H bridge duty cycle and polarity. - * @INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: Get the hardware timing restraints, - * regardless of trigger sources. - */ -enum configuration_ids { - INSN_CONFIG_DIO_INPUT = COMEDI_INPUT, - INSN_CONFIG_DIO_OUTPUT = COMEDI_OUTPUT, - INSN_CONFIG_DIO_OPENDRAIN = COMEDI_OPENDRAIN, - INSN_CONFIG_ANALOG_TRIG = 16, -/* INSN_CONFIG_WAVEFORM = 17, */ -/* INSN_CONFIG_TRIG = 18, */ -/* INSN_CONFIG_COUNTER = 19, */ - INSN_CONFIG_ALT_SOURCE = 20, - INSN_CONFIG_DIGITAL_TRIG = 21, - INSN_CONFIG_BLOCK_SIZE = 22, - INSN_CONFIG_TIMER_1 = 23, - INSN_CONFIG_FILTER = 24, - INSN_CONFIG_CHANGE_NOTIFY = 25, - - INSN_CONFIG_SERIAL_CLOCK = 26, /*ALPHA*/ - INSN_CONFIG_BIDIRECTIONAL_DATA = 27, - INSN_CONFIG_DIO_QUERY = 28, - INSN_CONFIG_PWM_OUTPUT = 29, - INSN_CONFIG_GET_PWM_OUTPUT = 30, - INSN_CONFIG_ARM = 31, - INSN_CONFIG_DISARM = 32, - INSN_CONFIG_GET_COUNTER_STATUS = 33, - INSN_CONFIG_RESET = 34, - INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, - INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, - INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, - INSN_CONFIG_SET_GATE_SRC = 2001, - INSN_CONFIG_GET_GATE_SRC = 2002, - INSN_CONFIG_SET_CLOCK_SRC = 2003, - INSN_CONFIG_GET_CLOCK_SRC = 2004, - INSN_CONFIG_SET_OTHER_SRC = 2005, - INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006, - INSN_CONFIG_SET_COUNTER_MODE = 4097, - INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE, - INSN_CONFIG_8254_READ_STATUS = 4098, - INSN_CONFIG_SET_ROUTING = 4099, - INSN_CONFIG_GET_ROUTING = 4109, - INSN_CONFIG_PWM_SET_PERIOD = 5000, - INSN_CONFIG_PWM_GET_PERIOD = 5001, - INSN_CONFIG_GET_PWM_STATUS = 5002, - INSN_CONFIG_PWM_SET_H_BRIDGE = 5003, - INSN_CONFIG_PWM_GET_H_BRIDGE = 5004, - INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS = 5005, -}; - -/** - * enum device_configuration_ids - COMEDI configuration instruction codes global - * to an entire device. - * @INSN_DEVICE_CONFIG_TEST_ROUTE: Validate the possibility of a - * globally-named route - * @INSN_DEVICE_CONFIG_CONNECT_ROUTE: Connect a globally-named route - * @INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:Disconnect a globally-named route - * @INSN_DEVICE_CONFIG_GET_ROUTES: Get a list of all globally-named routes - * that are valid for a particular device. - */ -enum device_config_route_ids { - INSN_DEVICE_CONFIG_TEST_ROUTE = 0, - INSN_DEVICE_CONFIG_CONNECT_ROUTE = 1, - INSN_DEVICE_CONFIG_DISCONNECT_ROUTE = 2, - INSN_DEVICE_CONFIG_GET_ROUTES = 3, -}; - -/** - * enum comedi_digital_trig_op - operations for configuring a digital trigger - * @COMEDI_DIGITAL_TRIG_DISABLE: Return digital trigger to its default, - * inactive, unconfigured state. - * @COMEDI_DIGITAL_TRIG_ENABLE_EDGES: Set rising and/or falling edge inputs - * that each can fire the trigger. - * @COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: Set a combination of high and/or low - * level inputs that can fire the trigger. - * - * These are used with the %INSN_CONFIG_DIGITAL_TRIG configuration instruction. - * The data for the configuration instruction is as follows... - * - * data[%0] = %INSN_CONFIG_DIGITAL_TRIG - * - * data[%1] = trigger ID - * - * data[%2] = configuration operation - * - * data[%3] = configuration parameter 1 - * - * data[%4] = configuration parameter 2 - * - * data[%5] = configuration parameter 3 - * - * The trigger ID (data[%1]) is used to differentiate multiple digital triggers - * belonging to the same subdevice. The configuration operation (data[%2]) is - * one of the enum comedi_digital_trig_op values. The configuration - * parameters (data[%3], data[%4], and data[%5]) depend on the operation; they - * are not used with %COMEDI_DIGITAL_TRIG_DISABLE. - * - * For %COMEDI_DIGITAL_TRIG_ENABLE_EDGES and %COMEDI_DIGITAL_TRIG_ENABLE_LEVELS, - * configuration parameter 1 (data[%3]) contains a "left-shift" value that - * specifies the input corresponding to bit 0 of configuration parameters 2 - * and 3. This is useful if the trigger has more than 32 inputs. - * - * For %COMEDI_DIGITAL_TRIG_ENABLE_EDGES, configuration parameter 2 (data[%4]) - * specifies which of up to 32 inputs have rising-edge sensitivity, and - * configuration parameter 3 (data[%5]) specifies which of up to 32 inputs - * have falling-edge sensitivity that can fire the trigger. - * - * For %COMEDI_DIGITAL_TRIG_ENABLE_LEVELS, configuration parameter 2 (data[%4]) - * specifies which of up to 32 inputs must be at a high level, and - * configuration parameter 3 (data[%5]) specifies which of up to 32 inputs - * must be at a low level for the trigger to fire. - * - * Some sequences of %INSN_CONFIG_DIGITAL_TRIG instructions may have a (partly) - * accumulative effect, depending on the low-level driver. This is useful - * when setting up a trigger that has more than 32 inputs, or has a combination - * of edge- and level-triggered inputs. - */ -enum comedi_digital_trig_op { - COMEDI_DIGITAL_TRIG_DISABLE = 0, - COMEDI_DIGITAL_TRIG_ENABLE_EDGES = 1, - COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = 2 -}; - -/** - * enum comedi_support_level - support level for a COMEDI feature - * @COMEDI_UNKNOWN_SUPPORT: Unspecified support for feature. - * @COMEDI_SUPPORTED: Feature is supported. - * @COMEDI_UNSUPPORTED: Feature is unsupported. - */ -enum comedi_support_level { - COMEDI_UNKNOWN_SUPPORT = 0, - COMEDI_SUPPORTED, - COMEDI_UNSUPPORTED -}; - -/** - * enum comedi_counter_status_flags - counter status bits - * @COMEDI_COUNTER_ARMED: Counter is armed. - * @COMEDI_COUNTER_COUNTING: Counter is counting. - * @COMEDI_COUNTER_TERMINAL_COUNT: Counter reached terminal count. - * - * These bitwise values are used by the %INSN_CONFIG_GET_COUNTER_STATUS - * configuration instruction to report the status of a counter. - */ -enum comedi_counter_status_flags { - COMEDI_COUNTER_ARMED = 0x1, - COMEDI_COUNTER_COUNTING = 0x2, - COMEDI_COUNTER_TERMINAL_COUNT = 0x4, -}; - -/* ioctls */ - -#define CIO 'd' -#define COMEDI_DEVCONFIG _IOW(CIO, 0, struct comedi_devconfig) -#define COMEDI_DEVINFO _IOR(CIO, 1, struct comedi_devinfo) -#define COMEDI_SUBDINFO _IOR(CIO, 2, struct comedi_subdinfo) -#define COMEDI_CHANINFO _IOR(CIO, 3, struct comedi_chaninfo) -/* _IOWR(CIO, 4, ...) is reserved */ -#define COMEDI_LOCK _IO(CIO, 5) -#define COMEDI_UNLOCK _IO(CIO, 6) -#define COMEDI_CANCEL _IO(CIO, 7) -#define COMEDI_RANGEINFO _IOR(CIO, 8, struct comedi_rangeinfo) -#define COMEDI_CMD _IOR(CIO, 9, struct comedi_cmd) -#define COMEDI_CMDTEST _IOR(CIO, 10, struct comedi_cmd) -#define COMEDI_INSNLIST _IOR(CIO, 11, struct comedi_insnlist) -#define COMEDI_INSN _IOR(CIO, 12, struct comedi_insn) -#define COMEDI_BUFCONFIG _IOR(CIO, 13, struct comedi_bufconfig) -#define COMEDI_BUFINFO _IOWR(CIO, 14, struct comedi_bufinfo) -#define COMEDI_POLL _IO(CIO, 15) -#define COMEDI_SETRSUBD _IO(CIO, 16) -#define COMEDI_SETWSUBD _IO(CIO, 17) - -/* structures */ - -/** - * struct comedi_insn - COMEDI instruction - * @insn: COMEDI instruction type (%INSN_xxx). - * @n: Length of @data[]. - * @data: Pointer to data array operated on by the instruction. - * @subdev: Subdevice index. - * @chanspec: A packed "chanspec" value consisting of channel number, - * analog range index, analog reference type, and flags. - * @unused: Reserved for future use. - * - * This is used with the %COMEDI_INSN ioctl, and indirectly with the - * %COMEDI_INSNLIST ioctl. - */ -struct comedi_insn { - unsigned int insn; - unsigned int n; - unsigned int __user *data; - unsigned int subdev; - unsigned int chanspec; - unsigned int unused[3]; -}; - -/** - * struct comedi_insnlist - list of COMEDI instructions - * @n_insns: Number of COMEDI instructions. - * @insns: Pointer to array COMEDI instructions. - * - * This is used with the %COMEDI_INSNLIST ioctl. - */ -struct comedi_insnlist { - unsigned int n_insns; - struct comedi_insn __user *insns; -}; - -/** - * struct comedi_cmd - COMEDI asynchronous acquisition command details - * @subdev: Subdevice index. - * @flags: Command flags (%CMDF_xxx). - * @start_src: "Start acquisition" trigger source (%TRIG_xxx). - * @start_arg: "Start acquisition" trigger argument. - * @scan_begin_src: "Scan begin" trigger source. - * @scan_begin_arg: "Scan begin" trigger argument. - * @convert_src: "Convert" trigger source. - * @convert_arg: "Convert" trigger argument. - * @scan_end_src: "Scan end" trigger source. - * @scan_end_arg: "Scan end" trigger argument. - * @stop_src: "Stop acquisition" trigger source. - * @stop_arg: "Stop acquisition" trigger argument. - * @chanlist: Pointer to array of "chanspec" values, containing a - * sequence of channel numbers packed with analog range - * index, etc. - * @chanlist_len: Number of channels in sequence. - * @data: Pointer to miscellaneous set-up data (not used). - * @data_len: Length of miscellaneous set-up data. - * - * This is used with the %COMEDI_CMD or %COMEDI_CMDTEST ioctl to set-up - * or validate an asynchronous acquisition command. The ioctl may modify - * the &struct comedi_cmd and copy it back to the caller. - * - * Optional command @flags values that can be ORed together... - * - * %CMDF_BOGUS - makes %COMEDI_CMD ioctl return error %EAGAIN instead of - * starting the command. - * - * %CMDF_PRIORITY - requests "hard real-time" processing (which is not - * supported in this version of COMEDI). - * - * %CMDF_WAKE_EOS - requests the command makes data available for reading - * after every "scan" period. - * - * %CMDF_WRITE - marks the command as being in the "write" (to device) - * direction. This does not need to be specified by the caller unless the - * subdevice supports commands in either direction. - * - * %CMDF_RAWDATA - prevents the command from "munging" the data between the - * COMEDI sample format and the raw hardware sample format. - * - * %CMDF_ROUND_NEAREST - requests timing periods to be rounded to nearest - * supported values. - * - * %CMDF_ROUND_DOWN - requests timing periods to be rounded down to supported - * values (frequencies rounded up). - * - * %CMDF_ROUND_UP - requests timing periods to be rounded up to supported - * values (frequencies rounded down). - * - * Trigger source values for @start_src, @scan_begin_src, @convert_src, - * @scan_end_src, and @stop_src... - * - * %TRIG_ANY - "all ones" value used to test which trigger sources are - * supported. - * - * %TRIG_INVALID - "all zeroes" value used to indicate that all requested - * trigger sources are invalid. - * - * %TRIG_NONE - never trigger (often used as a @stop_src value). - * - * %TRIG_NOW - trigger after '_arg' nanoseconds. - * - * %TRIG_FOLLOW - trigger follows another event. - * - * %TRIG_TIMER - trigger every '_arg' nanoseconds. - * - * %TRIG_COUNT - trigger when count '_arg' is reached. - * - * %TRIG_EXT - trigger on external signal specified by '_arg'. - * - * %TRIG_INT - trigger on internal, software trigger specified by '_arg'. - * - * %TRIG_OTHER - trigger on other, driver-defined signal specified by '_arg'. - */ -struct comedi_cmd { - unsigned int subdev; - unsigned int flags; - - unsigned int start_src; - unsigned int start_arg; - - unsigned int scan_begin_src; - unsigned int scan_begin_arg; - - unsigned int convert_src; - unsigned int convert_arg; - - unsigned int scan_end_src; - unsigned int scan_end_arg; - - unsigned int stop_src; - unsigned int stop_arg; - - unsigned int *chanlist; - unsigned int chanlist_len; - - short __user *data; - unsigned int data_len; -}; - -/** - * struct comedi_chaninfo - used to retrieve per-channel information - * @subdev: Subdevice index. - * @maxdata_list: Optional pointer to per-channel maximum data values. - * @flaglist: Optional pointer to per-channel flags. - * @rangelist: Optional pointer to per-channel range types. - * @unused: Reserved for future use. - * - * This is used with the %COMEDI_CHANINFO ioctl to get per-channel information - * for the subdevice. Use of this requires knowledge of the number of channels - * and subdevice flags obtained using the %COMEDI_SUBDINFO ioctl. - * - * The @maxdata_list member must be %NULL unless the %SDF_MAXDATA subdevice - * flag is set. The @flaglist member must be %NULL unless the %SDF_FLAGS - * subdevice flag is set. The @rangelist member must be %NULL unless the - * %SDF_RANGETYPE subdevice flag is set. Otherwise, the arrays they point to - * must be at least as long as the number of channels. - */ -struct comedi_chaninfo { - unsigned int subdev; - unsigned int __user *maxdata_list; - unsigned int __user *flaglist; - unsigned int __user *rangelist; - unsigned int unused[4]; -}; - -/** - * struct comedi_rangeinfo - used to retrieve the range table for a channel - * @range_type: Encodes subdevice index (bits 27:24), channel index - * (bits 23:16) and range table length (bits 15:0). - * @range_ptr: Pointer to array of @struct comedi_krange to be filled - * in with the range table for the channel or subdevice. - * - * This is used with the %COMEDI_RANGEINFO ioctl to retrieve the range table - * for a specific channel (if the subdevice has the %SDF_RANGETYPE flag set to - * indicate that the range table depends on the channel), or for the subdevice - * as a whole (if the %SDF_RANGETYPE flag is clear, indicating the range table - * is shared by all channels). - * - * The @range_type value is an input to the ioctl and comes from a previous - * use of the %COMEDI_SUBDINFO ioctl (if the %SDF_RANGETYPE flag is clear), - * or the %COMEDI_CHANINFO ioctl (if the %SDF_RANGETYPE flag is set). - */ -struct comedi_rangeinfo { - unsigned int range_type; - void __user *range_ptr; -}; - -/** - * struct comedi_krange - describes a range in a range table - * @min: Minimum value in millionths (1e-6) of a unit. - * @max: Maximum value in millionths (1e-6) of a unit. - * @flags: Indicates the units (in bits 7:0) OR'ed with optional flags. - * - * A range table is associated with a single channel, or with all channels in a - * subdevice, and a list of one or more ranges. A %struct comedi_krange - * describes the physical range of units for one of those ranges. Sample - * values in COMEDI are unsigned from %0 up to some 'maxdata' value. The - * mapping from sample values to physical units is assumed to be nomimally - * linear (for the purpose of describing the range), with sample value %0 - * mapping to @min, and the 'maxdata' sample value mapping to @max. - * - * The currently defined units are %UNIT_volt (%0), %UNIT_mA (%1), and - * %UNIT_none (%2). The @min and @max values are the physical range multiplied - * by 1e6, so a @max value of %1000000 (with %UNIT_volt) represents a maximal - * value of 1 volt. - * - * The only defined flag value is %RF_EXTERNAL (%0x100), indicating that the - * range needs to be multiplied by an external reference. - */ -struct comedi_krange { - int min; - int max; - unsigned int flags; -}; - -/** - * struct comedi_subdinfo - used to retrieve information about a subdevice - * @type: Type of subdevice from &enum comedi_subdevice_type. - * @n_chan: Number of channels the subdevice supports. - * @subd_flags: A mixture of static and dynamic flags describing - * aspects of the subdevice and its current state. - * @timer_type: Timer type. Always set to %5 ("nanosecond timer"). - * @len_chanlist: Maximum length of a channel list if the subdevice - * supports asynchronous acquisition commands. - * @maxdata: Maximum sample value for all channels if the - * %SDF_MAXDATA subdevice flag is clear. - * @flags: Channel flags for all channels if the %SDF_FLAGS - * subdevice flag is clear. - * @range_type: The range type for all channels if the %SDF_RANGETYPE - * subdevice flag is clear. Encodes the subdevice index - * (bits 27:24), a dummy channel index %0 (bits 23:16), - * and the range table length (bits 15:0). - * @settling_time_0: Not used. - * @insn_bits_support: Set to %COMEDI_SUPPORTED if the subdevice supports the - * %INSN_BITS instruction, or to %COMEDI_UNSUPPORTED if it - * does not. - * @unused: Reserved for future use. - * - * This is used with the %COMEDI_SUBDINFO ioctl which copies an array of - * &struct comedi_subdinfo back to user space, with one element per subdevice. - * Use of this requires knowledge of the number of subdevices obtained from - * the %COMEDI_DEVINFO ioctl. - * - * These are the @subd_flags values that may be ORed together... - * - * %SDF_BUSY - the subdevice is busy processing an asynchronous command or a - * synchronous instruction. - * - * %SDF_BUSY_OWNER - the subdevice is busy processing an asynchronous - * acquisition command started on the current file object (the file object - * issuing the %COMEDI_SUBDINFO ioctl). - * - * %SDF_LOCKED - the subdevice is locked by a %COMEDI_LOCK ioctl. - * - * %SDF_LOCK_OWNER - the subdevice is locked by a %COMEDI_LOCK ioctl from the - * current file object. - * - * %SDF_MAXDATA - maximum sample values are channel-specific. - * - * %SDF_FLAGS - channel flags are channel-specific. - * - * %SDF_RANGETYPE - range types are channel-specific. - * - * %SDF_PWM_COUNTER - PWM can switch off automatically. - * - * %SDF_PWM_HBRIDGE - or PWM is signed (H-bridge). - * - * %SDF_CMD - the subdevice supports asynchronous commands. - * - * %SDF_SOFT_CALIBRATED - the subdevice uses software calibration. - * - * %SDF_CMD_WRITE - the subdevice supports asynchronous commands in the output - * ("write") direction. - * - * %SDF_CMD_READ - the subdevice supports asynchronous commands in the input - * ("read") direction. - * - * %SDF_READABLE - the subdevice is readable (e.g. analog input). - * - * %SDF_WRITABLE (aliased as %SDF_WRITEABLE) - the subdevice is writable (e.g. - * analog output). - * - * %SDF_INTERNAL - the subdevice has no externally visible lines. - * - * %SDF_GROUND - the subdevice can use ground as an analog reference. - * - * %SDF_COMMON - the subdevice can use a common analog reference. - * - * %SDF_DIFF - the subdevice can use differential inputs (or outputs). - * - * %SDF_OTHER - the subdevice can use some other analog reference. - * - * %SDF_DITHER - the subdevice can do dithering. - * - * %SDF_DEGLITCH - the subdevice can do deglitching. - * - * %SDF_MMAP - this is never set. - * - * %SDF_RUNNING - an asynchronous command is still running. - * - * %SDF_LSAMPL - the subdevice uses "long" (32-bit) samples (for asynchronous - * command data). - * - * %SDF_PACKED - the subdevice packs several DIO samples into a single sample - * (for asynchronous command data). - * - * No "channel flags" (@flags) values are currently defined. - */ -struct comedi_subdinfo { - unsigned int type; - unsigned int n_chan; - unsigned int subd_flags; - unsigned int timer_type; - unsigned int len_chanlist; - unsigned int maxdata; - unsigned int flags; - unsigned int range_type; - unsigned int settling_time_0; - unsigned int insn_bits_support; - unsigned int unused[8]; -}; - -/** - * struct comedi_devinfo - used to retrieve information about a COMEDI device - * @version_code: COMEDI version code. - * @n_subdevs: Number of subdevices the device has. - * @driver_name: Null-terminated COMEDI driver name. - * @board_name: Null-terminated COMEDI board name. - * @read_subdevice: Index of the current "read" subdevice (%-1 if none). - * @write_subdevice: Index of the current "write" subdevice (%-1 if none). - * @unused: Reserved for future use. - * - * This is used with the %COMEDI_DEVINFO ioctl to get basic information about - * the device. - */ -struct comedi_devinfo { - unsigned int version_code; - unsigned int n_subdevs; - char driver_name[COMEDI_NAMELEN]; - char board_name[COMEDI_NAMELEN]; - int read_subdevice; - int write_subdevice; - int unused[30]; -}; - -/** - * struct comedi_devconfig - used to configure a legacy COMEDI device - * @board_name: Null-terminated string specifying the type of board - * to configure. - * @options: An array of integer configuration options. - * - * This is used with the %COMEDI_DEVCONFIG ioctl to configure a "legacy" COMEDI - * device, such as an ISA card. Not all COMEDI drivers support this. Those - * that do either expect the specified board name to match one of a list of - * names registered with the COMEDI core, or expect the specified board name - * to match the COMEDI driver name itself. The configuration options are - * handled in a driver-specific manner. - */ -struct comedi_devconfig { - char board_name[COMEDI_NAMELEN]; - int options[COMEDI_NDEVCONFOPTS]; -}; - -/** - * struct comedi_bufconfig - used to set or get buffer size for a subdevice - * @subdevice: Subdevice index. - * @flags: Not used. - * @maximum_size: Maximum allowed buffer size. - * @size: Buffer size. - * @unused: Reserved for future use. - * - * This is used with the %COMEDI_BUFCONFIG ioctl to get or configure the - * maximum buffer size and current buffer size for a COMEDI subdevice that - * supports asynchronous commands. If the subdevice does not support - * asynchronous commands, @maximum_size and @size are ignored and set to 0. - * - * On ioctl input, non-zero values of @maximum_size and @size specify a - * new maximum size and new current size (in bytes), respectively. These - * will by rounded up to a multiple of %PAGE_SIZE. Specifying a new maximum - * size requires admin capabilities. - * - * On ioctl output, @maximum_size and @size and set to the current maximum - * buffer size and current buffer size, respectively. - */ -struct comedi_bufconfig { - unsigned int subdevice; - unsigned int flags; - - unsigned int maximum_size; - unsigned int size; - - unsigned int unused[4]; -}; - -/** - * struct comedi_bufinfo - used to manipulate buffer position for a subdevice - * @subdevice: Subdevice index. - * @bytes_read: Specify amount to advance read position for an - * asynchronous command in the input ("read") direction. - * @buf_write_ptr: Current write position (index) within the buffer. - * @buf_read_ptr: Current read position (index) within the buffer. - * @buf_write_count: Total amount written, modulo 2^32. - * @buf_read_count: Total amount read, modulo 2^32. - * @bytes_written: Specify amount to advance write position for an - * asynchronous command in the output ("write") direction. - * @unused: Reserved for future use. - * - * This is used with the %COMEDI_BUFINFO ioctl to optionally advance the - * current read or write position in an asynchronous acquisition data buffer, - * and to get the current read and write positions in the buffer. - */ -struct comedi_bufinfo { - unsigned int subdevice; - unsigned int bytes_read; - - unsigned int buf_write_ptr; - unsigned int buf_read_ptr; - unsigned int buf_write_count; - unsigned int buf_read_count; - - unsigned int bytes_written; - - unsigned int unused[4]; -}; - -/* range stuff */ - -#define __RANGE(a, b) ((((a) & 0xffff) << 16) | ((b) & 0xffff)) - -#define RANGE_OFFSET(a) (((a) >> 16) & 0xffff) -#define RANGE_LENGTH(b) ((b) & 0xffff) - -#define RF_UNIT(flags) ((flags) & 0xff) -#define RF_EXTERNAL 0x100 - -#define UNIT_volt 0 -#define UNIT_mA 1 -#define UNIT_none 2 - -#define COMEDI_MIN_SPEED 0xffffffffu - -/**********************************************************/ -/* everything after this line is ALPHA */ -/**********************************************************/ - -/* - * 8254 specific configuration. - * - * It supports two config commands: - * - * 0 ID: INSN_CONFIG_SET_COUNTER_MODE - * 1 8254 Mode - * I8254_MODE0, I8254_MODE1, ..., I8254_MODE5 - * OR'ed with: - * I8254_BCD, I8254_BINARY - * - * 0 ID: INSN_CONFIG_8254_READ_STATUS - * 1 <-- Status byte returned here. - * B7 = Output - * B6 = NULL Count - * B5 - B0 Current mode. - */ - -enum i8254_mode { - I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */ - I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */ - I8254_MODE2 = (2 << 1), /* Rate generator */ - I8254_MODE3 = (3 << 1), /* Square wave mode */ - I8254_MODE4 = (4 << 1), /* Software triggered strobe */ - /* Hardware triggered strobe (retriggerable) */ - I8254_MODE5 = (5 << 1), - /* Use binary-coded decimal instead of binary (pretty useless) */ - I8254_BCD = 1, - I8254_BINARY = 0 -}; - -/* *** BEGIN GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */ - -/* - * Common National Instruments Terminal/Signal names. - * Some of these have no NI_ prefix as they are useful for non-NI hardware, such - * as those that utilize the PXI/RTSI trigger lines. - * - * NOTE ABOUT THE CHOICE OF NAMES HERE AND THE CAMELSCRIPT: - * The choice to use CamelScript and the exact names below is for - * maintainability, clarity, similarity to manufacturer's documentation, - * _and_ a mitigation for confusion that has plagued the use of these drivers - * for years! - * - * More detail: - * There have been significant confusions over the past many years for users - * when trying to understand how to connect to/from signals and terminals on - * NI hardware using comedi. The major reason for this is that the actual - * register values were exposed and required to be used by users. Several - * major reasons exist why this caused major confusion for users: - * 1) The register values are _NOT_ in user documentation, but rather in - * arcane locations, such as a few register programming manuals that are - * increasingly hard to find and the NI MHDDK (comments in example code). - * There is no one place to find the various valid values of the registers. - * 2) The register values are _NOT_ completely consistent. There is no way to - * gain any sense of intuition of which values, or even enums one should use - * for various registers. There was some attempt in prior use of comedi to - * name enums such that a user might know which enums should be used for - * varying purposes, but the end-user had to gain a knowledge of register - * values to correctly wield this approach. - * 3) The names for signals and registers found in the various register level - * programming manuals and vendor-provided documentation are _not_ even - * close to the same names that are in the end-user documentation. - * - * Similar, albeit less, confusion plagued NI's previous version of their own - * drivers. Earlier than 2003, NI greatly simplified the situation for users - * by releasing a new API that abstracted the names of signals/terminals to a - * common and intuitive set of names. - * - * The names below mirror the names chosen and well documented by NI. These - * names are exposed to the user via the comedilib user library. By keeping - * the names below, in spite of the use of CamelScript, maintenance will be - * greatly eased and confusion for users _and_ comedi developers will be - * greatly reduced. - */ - -/* - * Base of abstracted NI names. - * The first 16 bits of *_arg are reserved for channel selection. - * Since we only actually need the first 4 or 5 bits for all register values on - * NI select registers anyways, we'll identify all values >= (1<<15) as being an - * abstracted NI signal/terminal name. - * These values are also used/returned by INSN_DEVICE_CONFIG_TEST_ROUTE, - * INSN_DEVICE_CONFIG_CONNECT_ROUTE, INSN_DEVICE_CONFIG_DISCONNECT_ROUTE, - * and INSN_DEVICE_CONFIG_GET_ROUTES. - */ -#define NI_NAMES_BASE 0x8000u - -#define _TERM_N(base, n, x) ((base) + ((x) & ((n) - 1))) - -/* - * not necessarily all allowed 64 PFIs are valid--certainly not for all devices - */ -#define NI_PFI(x) _TERM_N(NI_NAMES_BASE, 64, x) -/* 8 trigger lines by standard, Some devices cannot talk to all eight. */ -#define TRIGGER_LINE(x) _TERM_N(NI_PFI(-1) + 1, 8, x) -/* 4 RTSI shared MUXes to route signals to/from TRIGGER_LINES on NI hardware */ -#define NI_RTSI_BRD(x) _TERM_N(TRIGGER_LINE(-1) + 1, 4, x) - -/* *** Counter/timer names : 8 counters max *** */ -#define NI_MAX_COUNTERS 8 -#define NI_COUNTER_NAMES_BASE (NI_RTSI_BRD(-1) + 1) -#define NI_CtrSource(x) _TERM_N(NI_COUNTER_NAMES_BASE, NI_MAX_COUNTERS, x) -/* Gate, Aux, A,B,Z are all treated, at times as gates */ -#define NI_GATES_NAMES_BASE (NI_CtrSource(-1) + 1) -#define NI_CtrGate(x) _TERM_N(NI_GATES_NAMES_BASE, NI_MAX_COUNTERS, x) -#define NI_CtrAux(x) _TERM_N(NI_CtrGate(-1) + 1, NI_MAX_COUNTERS, x) -#define NI_CtrA(x) _TERM_N(NI_CtrAux(-1) + 1, NI_MAX_COUNTERS, x) -#define NI_CtrB(x) _TERM_N(NI_CtrA(-1) + 1, NI_MAX_COUNTERS, x) -#define NI_CtrZ(x) _TERM_N(NI_CtrB(-1) + 1, NI_MAX_COUNTERS, x) -#define NI_GATES_NAMES_MAX NI_CtrZ(-1) -#define NI_CtrArmStartTrigger(x) _TERM_N(NI_CtrZ(-1) + 1, NI_MAX_COUNTERS, x) -#define NI_CtrInternalOutput(x) \ - _TERM_N(NI_CtrArmStartTrigger(-1) + 1, NI_MAX_COUNTERS, x) -/** external pin(s) labeled conveniently as CtrOut. */ -#define NI_CtrOut(x) _TERM_N(NI_CtrInternalOutput(-1) + 1, NI_MAX_COUNTERS, x) -/** For Buffered sampling of ctr -- x series capability. */ -#define NI_CtrSampleClock(x) _TERM_N(NI_CtrOut(-1) + 1, NI_MAX_COUNTERS, x) -#define NI_COUNTER_NAMES_MAX NI_CtrSampleClock(-1) - -enum ni_common_signal_names { - /* PXI_Star: this is a non-NI-specific signal */ - PXI_Star = NI_COUNTER_NAMES_MAX + 1, - PXI_Clk10, - PXIe_Clk100, - NI_AI_SampleClock, - NI_AI_SampleClockTimebase, - NI_AI_StartTrigger, - NI_AI_ReferenceTrigger, - NI_AI_ConvertClock, - NI_AI_ConvertClockTimebase, - NI_AI_PauseTrigger, - NI_AI_HoldCompleteEvent, - NI_AI_HoldComplete, - NI_AI_ExternalMUXClock, - NI_AI_STOP, /* pulse signal that occurs when a update is finished(?) */ - NI_AO_SampleClock, - NI_AO_SampleClockTimebase, - NI_AO_StartTrigger, - NI_AO_PauseTrigger, - NI_DI_SampleClock, - NI_DI_SampleClockTimebase, - NI_DI_StartTrigger, - NI_DI_ReferenceTrigger, - NI_DI_PauseTrigger, - NI_DI_InputBufferFull, - NI_DI_ReadyForStartEvent, - NI_DI_ReadyForTransferEventBurst, - NI_DI_ReadyForTransferEventPipelined, - NI_DO_SampleClock, - NI_DO_SampleClockTimebase, - NI_DO_StartTrigger, - NI_DO_PauseTrigger, - NI_DO_OutputBufferFull, - NI_DO_DataActiveEvent, - NI_DO_ReadyForStartEvent, - NI_DO_ReadyForTransferEvent, - NI_MasterTimebase, - NI_20MHzTimebase, - NI_80MHzTimebase, - NI_100MHzTimebase, - NI_200MHzTimebase, - NI_100kHzTimebase, - NI_10MHzRefClock, - NI_FrequencyOutput, - NI_ChangeDetectionEvent, - NI_AnalogComparisonEvent, - NI_WatchdogExpiredEvent, - NI_WatchdogExpirationTrigger, - NI_SCXI_Trig1, - NI_LogicLow, - NI_LogicHigh, - NI_ExternalStrobe, - NI_PFI_DO, - NI_CaseGround, - /* special internal signal used as variable source for RTSI bus: */ - NI_RGOUT0, - - /* just a name to make the next more convenient, regardless of above */ - _NI_NAMES_MAX_PLUS_1, - NI_NUM_NAMES = _NI_NAMES_MAX_PLUS_1 - NI_NAMES_BASE, -}; - -/* *** END GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */ - -#define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x))) -#define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b) - -/* - * mode bits for NI general-purpose counters, set with - * INSN_CONFIG_SET_COUNTER_MODE - */ -#define NI_GPCT_COUNTING_MODE_SHIFT 16 -#define NI_GPCT_INDEX_PHASE_BITSHIFT 20 -#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24 -enum ni_gpct_mode_bits { - NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4, - NI_GPCT_EDGE_GATE_MODE_MASK = 0x18, - NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0, - NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8, - NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10, - NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18, - NI_GPCT_STOP_MODE_MASK = 0x60, - NI_GPCT_STOP_ON_GATE_BITS = 0x00, - NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20, - NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40, - NI_GPCT_LOAD_B_SELECT_BIT = 0x80, - NI_GPCT_OUTPUT_MODE_MASK = 0x300, - NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100, - NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200, - NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300, - NI_GPCT_HARDWARE_DISARM_MASK = 0xc00, - NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000, - NI_GPCT_DISARM_AT_TC_BITS = 0x400, - NI_GPCT_DISARM_AT_GATE_BITS = 0x800, - NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00, - NI_GPCT_LOADING_ON_TC_BIT = 0x1000, - NI_GPCT_LOADING_ON_GATE_BIT = 0x4000, - NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_COUNTING_MODE_NORMAL_BITS = - 0x0 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS = - 0x1 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS = - 0x2 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS = - 0x3 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS = - 0x4 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS = - 0x6 << NI_GPCT_COUNTING_MODE_SHIFT, - NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT, - NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS = - 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT, - NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS = - 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT, - NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS = - 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT, - NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS = - 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT, - NI_GPCT_INDEX_ENABLE_BIT = 0x400000, - NI_GPCT_COUNTING_DIRECTION_MASK = - 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT, - NI_GPCT_COUNTING_DIRECTION_DOWN_BITS = - 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT, - NI_GPCT_COUNTING_DIRECTION_UP_BITS = - 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT, - NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS = - 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT, - NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS = - 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT, - NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000, - NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0, - NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000, - NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000, - NI_GPCT_OR_GATE_BIT = 0x10000000, - NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000 -}; - -/* - * Bits for setting a clock source with - * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. - */ -enum ni_gpct_clock_source_bits { - NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f, - NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0, - NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1, - NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2, - NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3, - NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4, - NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5, - /* NI 660x-specific */ - NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6, - NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7, - NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8, - NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9, - NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000, - NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0, - /* divide source by 2 */ - NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000, - /* divide source by 8 */ - NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000, - NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000 -}; - -/* NI 660x-specific */ -#define NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(x) (0x10 + (x)) - -#define NI_GPCT_RTSI_CLOCK_SRC_BITS(x) (0x18 + (x)) - -/* no pfi on NI 660x */ -#define NI_GPCT_PFI_CLOCK_SRC_BITS(x) (0x20 + (x)) - -/* - * Possibilities for setting a gate source with - * INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters. - * May be bitwise-or'd with CR_EDGE or CR_INVERT. - */ -enum ni_gpct_gate_select { - /* m-series gates */ - NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0, - NI_GPCT_AI_START2_GATE_SELECT = 0x12, - NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13, - NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14, - NI_GPCT_AI_START1_GATE_SELECT = 0x1c, - NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d, - NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e, - NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f, - /* more gates for 660x */ - NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100, - NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101, - /* more gates for 660x "second gate" */ - NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201, - NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e, - /* - * m-series "second gate" sources are unknown, - * we should add them here with an offset of 0x300 when - * known. - */ - NI_GPCT_DISABLED_GATE_SELECT = 0x8000, -}; - -#define NI_GPCT_GATE_PIN_GATE_SELECT(x) (0x102 + (x)) -#define NI_GPCT_RTSI_GATE_SELECT(x) NI_USUAL_RTSI_SELECT(x) -#define NI_GPCT_PFI_GATE_SELECT(x) NI_USUAL_PFI_SELECT(x) -#define NI_GPCT_UP_DOWN_PIN_GATE_SELECT(x) (0x202 + (x)) - -/* - * Possibilities for setting a source with - * INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. - */ -enum ni_gpct_other_index { - NI_GPCT_SOURCE_ENCODER_A, - NI_GPCT_SOURCE_ENCODER_B, - NI_GPCT_SOURCE_ENCODER_Z -}; - -enum ni_gpct_other_select { - /* m-series gates */ - /* Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT */ - NI_GPCT_DISABLED_OTHER_SELECT = 0x8000, -}; - -#define NI_GPCT_PFI_OTHER_SELECT(x) NI_USUAL_PFI_SELECT(x) - -/* - * start sources for ni general-purpose counters for use with - * INSN_CONFIG_ARM - */ -enum ni_gpct_arm_source { - NI_GPCT_ARM_IMMEDIATE = 0x0, - /* - * Start both the counter and the adjacent paired counter simultaneously - */ - NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, - /* - * If the NI_GPCT_HW_ARM bit is set, we will pass the least significant - * bits (3 bits for 660x or 5 bits for m-series) through to the - * hardware. To select a hardware trigger, pass the appropriate select - * bit, e.g., - * NI_GPCT_HW_ARM | NI_GPCT_AI_START1_GATE_SELECT or - * NI_GPCT_HW_ARM | NI_GPCT_PFI_GATE_SELECT(pfi_number) - */ - NI_GPCT_HW_ARM = 0x1000, - NI_GPCT_ARM_UNKNOWN = NI_GPCT_HW_ARM, /* for backward compatibility */ -}; - -/* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER. */ -enum ni_gpct_filter_select { - NI_GPCT_FILTER_OFF = 0x0, - NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1, - NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2, - NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3, - NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4, - NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5, - NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6 -}; - -/* - * PFI digital filtering options for ni m-series for use with - * INSN_CONFIG_FILTER. - */ -enum ni_pfi_filter_select { - NI_PFI_FILTER_OFF = 0x0, - NI_PFI_FILTER_125ns = 0x1, - NI_PFI_FILTER_6425ns = 0x2, - NI_PFI_FILTER_2550us = 0x3 -}; - -/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */ -enum ni_mio_clock_source { - NI_MIO_INTERNAL_CLOCK = 0, - /* - * Doesn't work for m-series, use NI_MIO_PLL_RTSI_CLOCK() - * the NI_MIO_PLL_* sources are m-series only - */ - NI_MIO_RTSI_CLOCK = 1, - NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2, - NI_MIO_PLL_PXI10_CLOCK = 3, - NI_MIO_PLL_RTSI0_CLOCK = 4 -}; - -#define NI_MIO_PLL_RTSI_CLOCK(x) (NI_MIO_PLL_RTSI0_CLOCK + (x)) - -/* - * Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING. - * The numbers assigned are not arbitrary, they correspond to the bits required - * to program the board. - */ -enum ni_rtsi_routing { - NI_RTSI_OUTPUT_ADR_START1 = 0, - NI_RTSI_OUTPUT_ADR_START2 = 1, - NI_RTSI_OUTPUT_SCLKG = 2, - NI_RTSI_OUTPUT_DACUPDN = 3, - NI_RTSI_OUTPUT_DA_START1 = 4, - NI_RTSI_OUTPUT_G_SRC0 = 5, - NI_RTSI_OUTPUT_G_GATE0 = 6, - NI_RTSI_OUTPUT_RGOUT0 = 7, - NI_RTSI_OUTPUT_RTSI_BRD_0 = 8, - /* Pre-m-series always have RTSI clock on line 7 */ - NI_RTSI_OUTPUT_RTSI_OSC = 12 -}; - -#define NI_RTSI_OUTPUT_RTSI_BRD(x) (NI_RTSI_OUTPUT_RTSI_BRD_0 + (x)) - -/* - * Signals which can be routed to an NI PFI pin on an m-series board with - * INSN_CONFIG_SET_ROUTING. These numbers are also returned by - * INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing - * cannot be changed. The numbers assigned are not arbitrary, they correspond - * to the bits required to program the board. - */ -enum ni_pfi_routing { - NI_PFI_OUTPUT_PFI_DEFAULT = 0, - NI_PFI_OUTPUT_AI_START1 = 1, - NI_PFI_OUTPUT_AI_START2 = 2, - NI_PFI_OUTPUT_AI_CONVERT = 3, - NI_PFI_OUTPUT_G_SRC1 = 4, - NI_PFI_OUTPUT_G_GATE1 = 5, - NI_PFI_OUTPUT_AO_UPDATE_N = 6, - NI_PFI_OUTPUT_AO_START1 = 7, - NI_PFI_OUTPUT_AI_START_PULSE = 8, - NI_PFI_OUTPUT_G_SRC0 = 9, - NI_PFI_OUTPUT_G_GATE0 = 10, - NI_PFI_OUTPUT_EXT_STROBE = 11, - NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12, - NI_PFI_OUTPUT_GOUT0 = 13, - NI_PFI_OUTPUT_GOUT1 = 14, - NI_PFI_OUTPUT_FREQ_OUT = 15, - NI_PFI_OUTPUT_PFI_DO = 16, - NI_PFI_OUTPUT_I_ATRIG = 17, - NI_PFI_OUTPUT_RTSI0 = 18, - NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26, - NI_PFI_OUTPUT_SCXI_TRIG1 = 27, - NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28, - NI_PFI_OUTPUT_CDI_SAMPLE = 29, - NI_PFI_OUTPUT_CDO_UPDATE = 30 -}; - -#define NI_PFI_OUTPUT_RTSI(x) (NI_PFI_OUTPUT_RTSI0 + (x)) - -/* - * Signals which can be routed to output on a NI PFI pin on a 660x board - * with INSN_CONFIG_SET_ROUTING. The numbers assigned are - * not arbitrary, they correspond to the bits required - * to program the board. Lines 0 to 7 can only be set to - * NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to - * NI_660X_PFI_OUTPUT_COUNTER. - */ -enum ni_660x_pfi_routing { - NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */ - NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */ -}; - -/* - * NI External Trigger lines. These values are not arbitrary, but are related - * to the bits required to program the board (offset by 1 for historical - * reasons). - */ -#define NI_EXT_PFI(x) (NI_USUAL_PFI_SELECT(x) - 1) -#define NI_EXT_RTSI(x) (NI_USUAL_RTSI_SELECT(x) - 1) - -/* - * Clock sources for CDIO subdevice on NI m-series boards. Used as the - * scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd - * with CR_INVERT to change polarity. - */ -enum ni_m_series_cdio_scan_begin_src { - NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0, - NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18, - NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19, - NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20, - NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28, - NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29, - NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30, - NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31, - NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32, - NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33 -}; - -#define NI_CDIO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x) -#define NI_CDIO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x) - -/* - * scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI - * boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to - * change polarity. - */ -#define NI_AO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x) -#define NI_AO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x) - -/* - * Bits for setting a clock source with - * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. - */ -enum ni_freq_out_clock_source_bits { - NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */ - NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */ -}; - -/* - * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for - * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). - */ -enum amplc_dio_clock_source { - /* - * Per channel external clock - * input/output pin (pin is only an - * input when clock source set to this value, - * otherwise it is an output) - */ - AMPLC_DIO_CLK_CLKN, - AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */ - AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */ - AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */ - AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */ - AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */ - /* - * Output of preceding counter channel - * (for channel 0, preceding counter - * channel is channel 2 on preceding - * counter subdevice, for first counter - * subdevice, preceding counter - * subdevice is the last counter - * subdevice) - */ - AMPLC_DIO_CLK_OUTNM1, - AMPLC_DIO_CLK_EXT, /* per chip external input pin */ - /* the following are "enhanced" clock sources for PCIe models */ - AMPLC_DIO_CLK_VCC, /* clock input HIGH */ - AMPLC_DIO_CLK_GND, /* clock input LOW */ - AMPLC_DIO_CLK_PAT_PRESENT, /* "pattern present" signal */ - AMPLC_DIO_CLK_20MHZ /* 20 MHz internal clock */ -}; - -/* - * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for - * timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver). - */ -enum amplc_dio_ts_clock_src { - AMPLC_DIO_TS_CLK_1GHZ, /* 1 ns period with 20 ns granularity */ - AMPLC_DIO_TS_CLK_1MHZ, /* 1 us period */ - AMPLC_DIO_TS_CLK_1KHZ /* 1 ms period */ -}; - -/* - * Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for - * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). - */ -enum amplc_dio_gate_source { - AMPLC_DIO_GAT_VCC, /* internal high logic level */ - AMPLC_DIO_GAT_GND, /* internal low logic level */ - AMPLC_DIO_GAT_GATN, /* per channel external gate input */ - /* - * negated output of counter channel minus 2 - * (for channels 0 or 1, channel minus 2 is channel 1 or 2 on - * the preceding counter subdevice, for the first counter subdevice - * the preceding counter subdevice is the last counter subdevice) - */ - AMPLC_DIO_GAT_NOUTNM2, - AMPLC_DIO_GAT_RESERVED4, - AMPLC_DIO_GAT_RESERVED5, - AMPLC_DIO_GAT_RESERVED6, - AMPLC_DIO_GAT_RESERVED7, - /* the following are "enhanced" gate sources for PCIe models */ - AMPLC_DIO_GAT_NGATN = 6, /* negated per channel gate input */ - /* non-negated output of counter channel minus 2 */ - AMPLC_DIO_GAT_OUTNM2, - AMPLC_DIO_GAT_PAT_PRESENT, /* "pattern present" signal */ - AMPLC_DIO_GAT_PAT_OCCURRED, /* "pattern occurred" latched */ - AMPLC_DIO_GAT_PAT_GONE, /* "pattern gone away" latched */ - AMPLC_DIO_GAT_NPAT_PRESENT, /* negated "pattern present" */ - AMPLC_DIO_GAT_NPAT_OCCURRED, /* negated "pattern occurred" */ - AMPLC_DIO_GAT_NPAT_GONE /* negated "pattern gone away" */ -}; - -/* - * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for - * the counter subdevice on the Kolter Electronic PCI-Counter board - * (ke_counter driver). - */ -enum ke_counter_clock_source { - KE_CLK_20MHZ, /* internal 20MHz (default) */ - KE_CLK_4MHZ, /* internal 4MHz (option) */ - KE_CLK_EXT /* external clock on pin 21 of D-Sub */ -}; - -#endif /* _COMEDI_H */ diff --git a/drivers/comedi/comedi_buf.c b/drivers/comedi/comedi_buf.c index 06bfc859ab31..393966c09740 100644 --- a/drivers/comedi/comedi_buf.c +++ b/drivers/comedi/comedi_buf.c @@ -9,8 +9,7 @@ #include #include - -#include "comedidev.h" +#include #include "comedi_internal.h" #ifdef PAGE_KERNEL_NOCACHE diff --git a/drivers/comedi/comedi_fops.c b/drivers/comedi/comedi_fops.c index 763cea8418f8..55a0cae04b8d 100644 --- a/drivers/comedi/comedi_fops.c +++ b/drivers/comedi/comedi_fops.c @@ -23,7 +23,7 @@ #include #include #include -#include "comedidev.h" +#include #include #include diff --git a/drivers/comedi/comedi_pci.c b/drivers/comedi/comedi_pci.c index 54739af7eb71..cc2581902195 100644 --- a/drivers/comedi/comedi_pci.c +++ b/drivers/comedi/comedi_pci.c @@ -9,8 +9,7 @@ #include #include - -#include "comedi_pci.h" +#include /** * comedi_to_pci_dev() - Return PCI device attached to COMEDI device diff --git a/drivers/comedi/comedi_pci.h b/drivers/comedi/comedi_pci.h deleted file mode 100644 index 4e069440cbdc..000000000000 --- a/drivers/comedi/comedi_pci.h +++ /dev/null @@ -1,57 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * comedi_pci.h - * header file for Comedi PCI drivers - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2000 David A. Schleef - */ - -#ifndef _COMEDI_PCI_H -#define _COMEDI_PCI_H - -#include - -#include "comedidev.h" - -/* - * PCI Vendor IDs not in - */ -#define PCI_VENDOR_ID_KOLTER 0x1001 -#define PCI_VENDOR_ID_ICP 0x104c -#define PCI_VENDOR_ID_DT 0x1116 -#define PCI_VENDOR_ID_IOTECH 0x1616 -#define PCI_VENDOR_ID_CONTEC 0x1221 -#define PCI_VENDOR_ID_RTD 0x1435 -#define PCI_VENDOR_ID_HUMUSOFT 0x186c - -struct pci_dev *comedi_to_pci_dev(struct comedi_device *dev); - -int comedi_pci_enable(struct comedi_device *dev); -void comedi_pci_disable(struct comedi_device *dev); -void comedi_pci_detach(struct comedi_device *dev); - -int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver, - unsigned long context); -void comedi_pci_auto_unconfig(struct pci_dev *pcidev); - -int comedi_pci_driver_register(struct comedi_driver *comedi_driver, - struct pci_driver *pci_driver); -void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver, - struct pci_driver *pci_driver); - -/** - * module_comedi_pci_driver() - Helper macro for registering a comedi PCI driver - * @__comedi_driver: comedi_driver struct - * @__pci_driver: pci_driver struct - * - * Helper macro for comedi PCI drivers which do not do anything special - * in module init/exit. This eliminates a lot of boilerplate. Each - * module may only use this macro once, and calling it replaces - * module_init() and module_exit() - */ -#define module_comedi_pci_driver(__comedi_driver, __pci_driver) \ - module_driver(__comedi_driver, comedi_pci_driver_register, \ - comedi_pci_driver_unregister, &(__pci_driver)) - -#endif /* _COMEDI_PCI_H */ diff --git a/drivers/comedi/comedi_pcmcia.c b/drivers/comedi/comedi_pcmcia.c index bb273bb202e6..c53aad0fc2ce 100644 --- a/drivers/comedi/comedi_pcmcia.c +++ b/drivers/comedi/comedi_pcmcia.c @@ -9,8 +9,7 @@ #include #include - -#include "comedi_pcmcia.h" +#include /** * comedi_to_pcmcia_dev() - Return PCMCIA device attached to COMEDI device diff --git a/drivers/comedi/comedi_pcmcia.h b/drivers/comedi/comedi_pcmcia.h deleted file mode 100644 index f2f6e779645b..000000000000 --- a/drivers/comedi/comedi_pcmcia.h +++ /dev/null @@ -1,49 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * comedi_pcmcia.h - * header file for Comedi PCMCIA drivers - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2000 David A. Schleef - */ - -#ifndef _COMEDI_PCMCIA_H -#define _COMEDI_PCMCIA_H - -#include -#include - -#include "comedidev.h" - -struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev); - -int comedi_pcmcia_enable(struct comedi_device *dev, - int (*conf_check)(struct pcmcia_device *p_dev, - void *priv_data)); -void comedi_pcmcia_disable(struct comedi_device *dev); - -int comedi_pcmcia_auto_config(struct pcmcia_device *link, - struct comedi_driver *driver); -void comedi_pcmcia_auto_unconfig(struct pcmcia_device *link); - -int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver, - struct pcmcia_driver *pcmcia_driver); -void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver, - struct pcmcia_driver *pcmcia_driver); - -/** - * module_comedi_pcmcia_driver() - Helper macro for registering a comedi - * PCMCIA driver - * @__comedi_driver: comedi_driver struct - * @__pcmcia_driver: pcmcia_driver struct - * - * Helper macro for comedi PCMCIA drivers which do not do anything special - * in module init/exit. This eliminates a lot of boilerplate. Each - * module may only use this macro once, and calling it replaces - * module_init() and module_exit() - */ -#define module_comedi_pcmcia_driver(__comedi_driver, __pcmcia_driver) \ - module_driver(__comedi_driver, comedi_pcmcia_driver_register, \ - comedi_pcmcia_driver_unregister, &(__pcmcia_driver)) - -#endif /* _COMEDI_PCMCIA_H */ diff --git a/drivers/comedi/comedi_usb.c b/drivers/comedi/comedi_usb.c index eea8ebf32ed0..d11ea148ebf8 100644 --- a/drivers/comedi/comedi_usb.c +++ b/drivers/comedi/comedi_usb.c @@ -8,8 +8,7 @@ */ #include - -#include "comedi_usb.h" +#include /** * comedi_to_usb_interface() - Return USB interface attached to COMEDI device diff --git a/drivers/comedi/comedi_usb.h b/drivers/comedi/comedi_usb.h deleted file mode 100644 index 601e29d3891c..000000000000 --- a/drivers/comedi/comedi_usb.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* comedi_usb.h - * header file for USB Comedi drivers - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2000 David A. Schleef - */ - -#ifndef _COMEDI_USB_H -#define _COMEDI_USB_H - -#include - -#include "comedidev.h" - -struct usb_interface *comedi_to_usb_interface(struct comedi_device *dev); -struct usb_device *comedi_to_usb_dev(struct comedi_device *dev); - -int comedi_usb_auto_config(struct usb_interface *intf, - struct comedi_driver *driver, unsigned long context); -void comedi_usb_auto_unconfig(struct usb_interface *intf); - -int comedi_usb_driver_register(struct comedi_driver *comedi_driver, - struct usb_driver *usb_driver); -void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver, - struct usb_driver *usb_driver); - -/** - * module_comedi_usb_driver() - Helper macro for registering a comedi USB driver - * @__comedi_driver: comedi_driver struct - * @__usb_driver: usb_driver struct - * - * Helper macro for comedi USB drivers which do not do anything special - * in module init/exit. This eliminates a lot of boilerplate. Each - * module may only use this macro once, and calling it replaces - * module_init() and module_exit() - */ -#define module_comedi_usb_driver(__comedi_driver, __usb_driver) \ - module_driver(__comedi_driver, comedi_usb_driver_register, \ - comedi_usb_driver_unregister, &(__usb_driver)) - -#endif /* _COMEDI_USB_H */ diff --git a/drivers/comedi/comedidev.h b/drivers/comedi/comedidev.h deleted file mode 100644 index 0e1b95ef9a4d..000000000000 --- a/drivers/comedi/comedidev.h +++ /dev/null @@ -1,1054 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * comedidev.h - * header file for kernel-only structures, variables, and constants - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1997-2000 David A. Schleef - */ - -#ifndef _COMEDIDEV_H -#define _COMEDIDEV_H - -#include -#include -#include -#include -#include - -#include "comedi.h" - -#define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) -#define COMEDI_VERSION_CODE COMEDI_VERSION(COMEDI_MAJORVERSION, \ - COMEDI_MINORVERSION, COMEDI_MICROVERSION) -#define COMEDI_RELEASE VERSION - -#define COMEDI_NUM_BOARD_MINORS 0x30 - -/** - * struct comedi_subdevice - Working data for a COMEDI subdevice - * @device: COMEDI device to which this subdevice belongs. (Initialized by - * comedi_alloc_subdevices().) - * @index: Index of this subdevice within device's array of subdevices. - * (Initialized by comedi_alloc_subdevices().) - * @type: Type of subdevice from &enum comedi_subdevice_type. (Initialized by - * the low-level driver.) - * @n_chan: Number of channels the subdevice supports. (Initialized by the - * low-level driver.) - * @subdev_flags: Various "SDF" flags indicating aspects of the subdevice to - * the COMEDI core and user application. (Initialized by the low-level - * driver.) - * @len_chanlist: Maximum length of a channel list if the subdevice supports - * asynchronous acquisition commands. (Optionally initialized by the - * low-level driver, or changed from 0 to 1 during post-configuration.) - * @private: Private data pointer which is either set by the low-level driver - * itself, or by a call to comedi_alloc_spriv() which allocates storage. - * In the latter case, the storage is automatically freed after the - * low-level driver's "detach" handler is called for the device. - * (Initialized by the low-level driver.) - * @async: Pointer to &struct comedi_async id the subdevice supports - * asynchronous acquisition commands. (Allocated and initialized during - * post-configuration if needed.) - * @lock: Pointer to a file object that performed a %COMEDI_LOCK ioctl on the - * subdevice. (Initially NULL.) - * @busy: Pointer to a file object that is performing an asynchronous - * acquisition command on the subdevice. (Initially NULL.) - * @runflags: Internal flags for use by COMEDI core, mostly indicating whether - * an asynchronous acquisition command is running. - * @spin_lock: Generic spin-lock for use by the COMEDI core and the low-level - * driver. (Initialized by comedi_alloc_subdevices().) - * @io_bits: Bit-mask indicating the channel directions for a DIO subdevice - * with no more than 32 channels. A '1' at a bit position indicates the - * corresponding channel is configured as an output. (Initialized by the - * low-level driver for a DIO subdevice. Forced to all-outputs during - * post-configuration for a digital output subdevice.) - * @maxdata: If non-zero, this is the maximum raw data value of each channel. - * If zero, the maximum data value is channel-specific. (Initialized by - * the low-level driver.) - * @maxdata_list: If the maximum data value is channel-specific, this points - * to an array of maximum data values indexed by channel index. - * (Initialized by the low-level driver.) - * @range_table: If non-NULL, this points to a COMEDI range table for the - * subdevice. If NULL, the range table is channel-specific. (Initialized - * by the low-level driver, will be set to an "invalid" range table during - * post-configuration if @range_table and @range_table_list are both - * NULL.) - * @range_table_list: If the COMEDI range table is channel-specific, this - * points to an array of pointers to COMEDI range tables indexed by - * channel number. (Initialized by the low-level driver.) - * @chanlist: Not used. - * @insn_read: Optional pointer to a handler for the %INSN_READ instruction. - * (Initialized by the low-level driver, or set to a default handler - * during post-configuration.) - * @insn_write: Optional pointer to a handler for the %INSN_WRITE instruction. - * (Initialized by the low-level driver, or set to a default handler - * during post-configuration.) - * @insn_bits: Optional pointer to a handler for the %INSN_BITS instruction - * for a digital input, digital output or digital input/output subdevice. - * (Initialized by the low-level driver, or set to a default handler - * during post-configuration.) - * @insn_config: Optional pointer to a handler for the %INSN_CONFIG - * instruction. (Initialized by the low-level driver, or set to a default - * handler during post-configuration.) - * @do_cmd: If the subdevice supports asynchronous acquisition commands, this - * points to a handler to set it up in hardware. (Initialized by the - * low-level driver.) - * @do_cmdtest: If the subdevice supports asynchronous acquisition commands, - * this points to a handler used to check and possibly tweak a prospective - * acquisition command without setting it up in hardware. (Initialized by - * the low-level driver.) - * @poll: If the subdevice supports asynchronous acquisition commands, this - * is an optional pointer to a handler for the %COMEDI_POLL ioctl which - * instructs the low-level driver to synchronize buffers. (Initialized by - * the low-level driver if needed.) - * @cancel: If the subdevice supports asynchronous acquisition commands, this - * points to a handler used to terminate a running command. (Initialized - * by the low-level driver.) - * @buf_change: If the subdevice supports asynchronous acquisition commands, - * this is an optional pointer to a handler that is called when the data - * buffer for handling asynchronous commands is allocated or reallocated. - * (Initialized by the low-level driver if needed.) - * @munge: If the subdevice supports asynchronous acquisition commands and - * uses DMA to transfer data from the hardware to the acquisition buffer, - * this points to a function used to "munge" the data values from the - * hardware into the format expected by COMEDI. (Initialized by the - * low-level driver if needed.) - * @async_dma_dir: If the subdevice supports asynchronous acquisition commands - * and uses DMA to transfer data from the hardware to the acquisition - * buffer, this sets the DMA direction for the buffer. (initialized to - * %DMA_NONE by comedi_alloc_subdevices() and changed by the low-level - * driver if necessary.) - * @state: Handy bit-mask indicating the output states for a DIO or digital - * output subdevice with no more than 32 channels. (Initialized by the - * low-level driver.) - * @class_dev: If the subdevice supports asynchronous acquisition commands, - * this points to a sysfs comediX_subdY device where X is the minor device - * number of the COMEDI device and Y is the subdevice number. The minor - * device number for the sysfs device is allocated dynamically in the - * range 48 to 255. This is used to allow the COMEDI device to be opened - * with a different default read or write subdevice. (Allocated during - * post-configuration if needed.) - * @minor: If @class_dev is set, this is its dynamically allocated minor - * device number. (Set during post-configuration if necessary.) - * @readback: Optional pointer to memory allocated by - * comedi_alloc_subdev_readback() used to hold the values written to - * analog output channels so they can be read back. The storage is - * automatically freed after the low-level driver's "detach" handler is - * called for the device. (Initialized by the low-level driver.) - * - * This is the main control structure for a COMEDI subdevice. If the subdevice - * supports asynchronous acquisition commands, additional information is stored - * in the &struct comedi_async pointed to by @async. - * - * Most of the subdevice is initialized by the low-level driver's "attach" or - * "auto_attach" handlers but parts of it are initialized by - * comedi_alloc_subdevices(), and other parts are initialized during - * post-configuration on return from that handler. - * - * A low-level driver that sets @insn_bits for a digital input, digital output, - * or DIO subdevice may leave @insn_read and @insn_write uninitialized, in - * which case they will be set to a default handler during post-configuration - * that uses @insn_bits to emulate the %INSN_READ and %INSN_WRITE instructions. - */ -struct comedi_subdevice { - struct comedi_device *device; - int index; - int type; - int n_chan; - int subdev_flags; - int len_chanlist; /* maximum length of channel/gain list */ - - void *private; - - struct comedi_async *async; - - void *lock; - void *busy; - unsigned int runflags; - spinlock_t spin_lock; /* generic spin-lock for COMEDI and drivers */ - - unsigned int io_bits; - - unsigned int maxdata; /* if maxdata==0, use list */ - const unsigned int *maxdata_list; /* list is channel specific */ - - const struct comedi_lrange *range_table; - const struct comedi_lrange *const *range_table_list; - - unsigned int *chanlist; /* driver-owned chanlist (not used) */ - - int (*insn_read)(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); - int (*insn_write)(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); - int (*insn_bits)(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); - int (*insn_config)(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, - unsigned int *data); - - int (*do_cmd)(struct comedi_device *dev, struct comedi_subdevice *s); - int (*do_cmdtest)(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_cmd *cmd); - int (*poll)(struct comedi_device *dev, struct comedi_subdevice *s); - int (*cancel)(struct comedi_device *dev, struct comedi_subdevice *s); - - /* called when the buffer changes */ - int (*buf_change)(struct comedi_device *dev, - struct comedi_subdevice *s); - - void (*munge)(struct comedi_device *dev, struct comedi_subdevice *s, - void *data, unsigned int num_bytes, - unsigned int start_chan_index); - enum dma_data_direction async_dma_dir; - - unsigned int state; - - struct device *class_dev; - int minor; - - unsigned int *readback; -}; - -/** - * struct comedi_buf_page - Describe a page of a COMEDI buffer - * @virt_addr: Kernel address of page. - * @dma_addr: DMA address of page if in DMA coherent memory. - */ -struct comedi_buf_page { - void *virt_addr; - dma_addr_t dma_addr; -}; - -/** - * struct comedi_buf_map - Describe pages in a COMEDI buffer - * @dma_hw_dev: Low-level hardware &struct device pointer copied from the - * COMEDI device's hw_dev member. - * @page_list: Pointer to array of &struct comedi_buf_page, one for each - * page in the buffer. - * @n_pages: Number of pages in the buffer. - * @dma_dir: DMA direction used to allocate pages of DMA coherent memory, - * or %DMA_NONE if pages allocated from regular memory. - * @refcount: &struct kref reference counter used to free the buffer. - * - * A COMEDI data buffer is allocated as individual pages, either in - * conventional memory or DMA coherent memory, depending on the attached, - * low-level hardware device. (The buffer pages also get mapped into the - * kernel's contiguous virtual address space pointed to by the 'prealloc_buf' - * member of &struct comedi_async.) - * - * The buffer is normally freed when the COMEDI device is detached from the - * low-level driver (which may happen due to device removal), but if it happens - * to be mmapped at the time, the pages cannot be freed until the buffer has - * been munmapped. That is what the reference counter is for. (The virtual - * address space pointed by 'prealloc_buf' is freed when the COMEDI device is - * detached.) - */ -struct comedi_buf_map { - struct device *dma_hw_dev; - struct comedi_buf_page *page_list; - unsigned int n_pages; - enum dma_data_direction dma_dir; - struct kref refcount; -}; - -/** - * struct comedi_async - Control data for asynchronous COMEDI commands - * @prealloc_buf: Kernel virtual address of allocated acquisition buffer. - * @prealloc_bufsz: Buffer size (in bytes). - * @buf_map: Map of buffer pages. - * @max_bufsize: Maximum allowed buffer size (in bytes). - * @buf_write_count: "Write completed" count (in bytes, modulo 2**32). - * @buf_write_alloc_count: "Allocated for writing" count (in bytes, - * modulo 2**32). - * @buf_read_count: "Read completed" count (in bytes, modulo 2**32). - * @buf_read_alloc_count: "Allocated for reading" count (in bytes, - * modulo 2**32). - * @buf_write_ptr: Buffer position for writer. - * @buf_read_ptr: Buffer position for reader. - * @cur_chan: Current position in chanlist for scan (for those drivers that - * use it). - * @scans_done: The number of scans completed. - * @scan_progress: Amount received or sent for current scan (in bytes). - * @munge_chan: Current position in chanlist for "munging". - * @munge_count: "Munge" count (in bytes, modulo 2**32). - * @munge_ptr: Buffer position for "munging". - * @events: Bit-vector of events that have occurred. - * @cmd: Details of comedi command in progress. - * @wait_head: Task wait queue for file reader or writer. - * @cb_mask: Bit-vector of events that should wake waiting tasks. - * @inttrig: Software trigger function for command, or NULL. - * - * Note about the ..._count and ..._ptr members: - * - * Think of the _Count values being integers of unlimited size, indexing - * into a buffer of infinite length (though only an advancing portion - * of the buffer of fixed length prealloc_bufsz is accessible at any - * time). Then: - * - * Buf_Read_Count <= Buf_Read_Alloc_Count <= Munge_Count <= - * Buf_Write_Count <= Buf_Write_Alloc_Count <= - * (Buf_Read_Count + prealloc_bufsz) - * - * (Those aren't the actual members, apart from prealloc_bufsz.) When the - * buffer is reset, those _Count values start at 0 and only increase in value, - * maintaining the above inequalities until the next time the buffer is - * reset. The buffer is divided into the following regions by the inequalities: - * - * [0, Buf_Read_Count): - * old region no longer accessible - * - * [Buf_Read_Count, Buf_Read_Alloc_Count): - * filled and munged region allocated for reading but not yet read - * - * [Buf_Read_Alloc_Count, Munge_Count): - * filled and munged region not yet allocated for reading - * - * [Munge_Count, Buf_Write_Count): - * filled region not yet munged - * - * [Buf_Write_Count, Buf_Write_Alloc_Count): - * unfilled region allocated for writing but not yet written - * - * [Buf_Write_Alloc_Count, Buf_Read_Count + prealloc_bufsz): - * unfilled region not yet allocated for writing - * - * [Buf_Read_Count + prealloc_bufsz, infinity): - * unfilled region not yet accessible - * - * Data needs to be written into the buffer before it can be read out, - * and may need to be converted (or "munged") between the two - * operations. Extra unfilled buffer space may need to allocated for - * writing (advancing Buf_Write_Alloc_Count) before new data is written. - * After writing new data, the newly filled space needs to be released - * (advancing Buf_Write_Count). This also results in the new data being - * "munged" (advancing Munge_Count). Before data is read out of the - * buffer, extra space may need to be allocated for reading (advancing - * Buf_Read_Alloc_Count). After the data has been read out, the space - * needs to be released (advancing Buf_Read_Count). - * - * The actual members, buf_read_count, buf_read_alloc_count, - * munge_count, buf_write_count, and buf_write_alloc_count take the - * value of the corresponding capitalized _Count values modulo 2^32 - * (UINT_MAX+1). Subtracting a "higher" _count value from a "lower" - * _count value gives the same answer as subtracting a "higher" _Count - * value from a lower _Count value because prealloc_bufsz < UINT_MAX+1. - * The modulo operation is done implicitly. - * - * The buf_read_ptr, munge_ptr, and buf_write_ptr members take the value - * of the corresponding capitalized _Count values modulo prealloc_bufsz. - * These correspond to byte indices in the physical buffer. The modulo - * operation is done by subtracting prealloc_bufsz when the value - * exceeds prealloc_bufsz (assuming prealloc_bufsz plus the increment is - * less than or equal to UINT_MAX). - */ -struct comedi_async { - void *prealloc_buf; - unsigned int prealloc_bufsz; - struct comedi_buf_map *buf_map; - unsigned int max_bufsize; - unsigned int buf_write_count; - unsigned int buf_write_alloc_count; - unsigned int buf_read_count; - unsigned int buf_read_alloc_count; - unsigned int buf_write_ptr; - unsigned int buf_read_ptr; - unsigned int cur_chan; - unsigned int scans_done; - unsigned int scan_progress; - unsigned int munge_chan; - unsigned int munge_count; - unsigned int munge_ptr; - unsigned int events; - struct comedi_cmd cmd; - wait_queue_head_t wait_head; - unsigned int cb_mask; - int (*inttrig)(struct comedi_device *dev, struct comedi_subdevice *s, - unsigned int x); -}; - -/** - * enum comedi_cb - &struct comedi_async callback "events" - * @COMEDI_CB_EOS: end-of-scan - * @COMEDI_CB_EOA: end-of-acquisition/output - * @COMEDI_CB_BLOCK: data has arrived, wakes up read() / write() - * @COMEDI_CB_EOBUF: DEPRECATED: end of buffer - * @COMEDI_CB_ERROR: card error during acquisition - * @COMEDI_CB_OVERFLOW: buffer overflow/underflow - * @COMEDI_CB_ERROR_MASK: events that indicate an error has occurred - * @COMEDI_CB_CANCEL_MASK: events that will cancel an async command - */ -enum comedi_cb { - COMEDI_CB_EOS = BIT(0), - COMEDI_CB_EOA = BIT(1), - COMEDI_CB_BLOCK = BIT(2), - COMEDI_CB_EOBUF = BIT(3), - COMEDI_CB_ERROR = BIT(4), - COMEDI_CB_OVERFLOW = BIT(5), - /* masks */ - COMEDI_CB_ERROR_MASK = (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW), - COMEDI_CB_CANCEL_MASK = (COMEDI_CB_EOA | COMEDI_CB_ERROR_MASK) -}; - -/** - * struct comedi_driver - COMEDI driver registration - * @driver_name: Name of driver. - * @module: Owning module. - * @attach: The optional "attach" handler for manually configured COMEDI - * devices. - * @detach: The "detach" handler for deconfiguring COMEDI devices. - * @auto_attach: The optional "auto_attach" handler for automatically - * configured COMEDI devices. - * @num_names: Optional number of "board names" supported. - * @board_name: Optional pointer to a pointer to a board name. The pointer - * to a board name is embedded in an element of a driver-defined array - * of static, read-only board type information. - * @offset: Optional size of each element of the driver-defined array of - * static, read-only board type information, i.e. the offset between each - * pointer to a board name. - * - * This is used with comedi_driver_register() and comedi_driver_unregister() to - * register and unregister a low-level COMEDI driver with the COMEDI core. - * - * If @num_names is non-zero, @board_name should be non-NULL, and @offset - * should be at least sizeof(*board_name). These are used by the handler for - * the %COMEDI_DEVCONFIG ioctl to match a hardware device and its driver by - * board name. If @num_names is zero, the %COMEDI_DEVCONFIG ioctl matches a - * hardware device and its driver by driver name. This is only useful if the - * @attach handler is set. If @num_names is non-zero, the driver's @attach - * handler will be called with the COMEDI device structure's board_ptr member - * pointing to the matched pointer to a board name within the driver's private - * array of static, read-only board type information. - * - * The @detach handler has two roles. If a COMEDI device was successfully - * configured by the @attach or @auto_attach handler, it is called when the - * device is being deconfigured (by the %COMEDI_DEVCONFIG ioctl, or due to - * unloading of the driver, or due to device removal). It is also called when - * the @attach or @auto_attach handler returns an error. Therefore, the - * @attach or @auto_attach handlers can defer clean-up on error until the - * @detach handler is called. If the @attach or @auto_attach handlers free - * any resources themselves, they must prevent the @detach handler from - * freeing the same resources. The @detach handler must not assume that all - * resources requested by the @attach or @auto_attach handler were - * successfully allocated. - */ -struct comedi_driver { - /* private: */ - struct comedi_driver *next; /* Next in list of COMEDI drivers. */ - /* public: */ - const char *driver_name; - struct module *module; - int (*attach)(struct comedi_device *dev, struct comedi_devconfig *it); - void (*detach)(struct comedi_device *dev); - int (*auto_attach)(struct comedi_device *dev, unsigned long context); - unsigned int num_names; - const char *const *board_name; - int offset; -}; - -/** - * struct comedi_device - Working data for a COMEDI device - * @use_count: Number of open file objects. - * @driver: Low-level COMEDI driver attached to this COMEDI device. - * @pacer: Optional pointer to a dynamically allocated acquisition pacer - * control. It is freed automatically after the COMEDI device is - * detached from the low-level driver. - * @private: Optional pointer to private data allocated by the low-level - * driver. It is freed automatically after the COMEDI device is - * detached from the low-level driver. - * @class_dev: Sysfs comediX device. - * @minor: Minor device number of COMEDI char device (0-47). - * @detach_count: Counter incremented every time the COMEDI device is detached. - * Used for checking a previous attachment is still valid. - * @hw_dev: Optional pointer to the low-level hardware &struct device. It is - * required for automatically configured COMEDI devices and optional for - * COMEDI devices configured by the %COMEDI_DEVCONFIG ioctl, although - * the bus-specific COMEDI functions only work if it is set correctly. - * It is also passed to dma_alloc_coherent() for COMEDI subdevices that - * have their 'async_dma_dir' member set to something other than - * %DMA_NONE. - * @board_name: Pointer to a COMEDI board name or a COMEDI driver name. When - * the low-level driver's "attach" handler is called by the handler for - * the %COMEDI_DEVCONFIG ioctl, it either points to a matched board name - * string if the 'num_names' member of the &struct comedi_driver is - * non-zero, otherwise it points to the low-level driver name string. - * When the low-lever driver's "auto_attach" handler is called for an - * automatically configured COMEDI device, it points to the low-level - * driver name string. The low-level driver is free to change it in its - * "attach" or "auto_attach" handler if it wishes. - * @board_ptr: Optional pointer to private, read-only board type information in - * the low-level driver. If the 'num_names' member of the &struct - * comedi_driver is non-zero, the handler for the %COMEDI_DEVCONFIG ioctl - * will point it to a pointer to a matched board name string within the - * driver's private array of static, read-only board type information when - * calling the driver's "attach" handler. The low-level driver is free to - * change it. - * @attached: Flag indicating that the COMEDI device is attached to a low-level - * driver. - * @ioenabled: Flag used to indicate that a PCI device has been enabled and - * its regions requested. - * @spinlock: Generic spin-lock for use by the low-level driver. - * @mutex: Generic mutex for use by the COMEDI core module. - * @attach_lock: &struct rw_semaphore used to guard against the COMEDI device - * being detached while an operation is in progress. The down_write() - * operation is only allowed while @mutex is held and is used when - * changing @attached and @detach_count and calling the low-level driver's - * "detach" handler. The down_read() operation is generally used without - * holding @mutex. - * @refcount: &struct kref reference counter for freeing COMEDI device. - * @n_subdevices: Number of COMEDI subdevices allocated by the low-level - * driver for this device. - * @subdevices: Dynamically allocated array of COMEDI subdevices. - * @mmio: Optional pointer to a remapped MMIO region set by the low-level - * driver. - * @iobase: Optional base of an I/O port region requested by the low-level - * driver. - * @iolen: Length of I/O port region requested at @iobase. - * @irq: Optional IRQ number requested by the low-level driver. - * @read_subdev: Optional pointer to a default COMEDI subdevice operated on by - * the read() file operation. Set by the low-level driver. - * @write_subdev: Optional pointer to a default COMEDI subdevice operated on by - * the write() file operation. Set by the low-level driver. - * @async_queue: Storage for fasync_helper(). - * @open: Optional pointer to a function set by the low-level driver to be - * called when @use_count changes from 0 to 1. - * @close: Optional pointer to a function set by the low-level driver to be - * called when @use_count changed from 1 to 0. - * @insn_device_config: Optional pointer to a handler for all sub-instructions - * except %INSN_DEVICE_CONFIG_GET_ROUTES of the %INSN_DEVICE_CONFIG - * instruction. If this is not initialized by the low-level driver, a - * default handler will be set during post-configuration. - * @get_valid_routes: Optional pointer to a handler for the - * %INSN_DEVICE_CONFIG_GET_ROUTES sub-instruction of the - * %INSN_DEVICE_CONFIG instruction set. If this is not initialized by the - * low-level driver, a default handler that copies zero routes back to the - * user will be used. - * - * This is the main control data structure for a COMEDI device (as far as the - * COMEDI core is concerned). There are two groups of COMEDI devices - - * "legacy" devices that are configured by the handler for the - * %COMEDI_DEVCONFIG ioctl, and automatically configured devices resulting - * from a call to comedi_auto_config() as a result of a bus driver probe in - * a low-level COMEDI driver. The "legacy" COMEDI devices are allocated - * during module initialization if the "comedi_num_legacy_minors" module - * parameter is non-zero and use minor device numbers from 0 to - * comedi_num_legacy_minors minus one. The automatically configured COMEDI - * devices are allocated on demand and use minor device numbers from - * comedi_num_legacy_minors to 47. - */ -struct comedi_device { - int use_count; - struct comedi_driver *driver; - struct comedi_8254 *pacer; - void *private; - - struct device *class_dev; - int minor; - unsigned int detach_count; - struct device *hw_dev; - - const char *board_name; - const void *board_ptr; - unsigned int attached:1; - unsigned int ioenabled:1; - spinlock_t spinlock; /* generic spin-lock for low-level driver */ - struct mutex mutex; /* generic mutex for COMEDI core */ - struct rw_semaphore attach_lock; - struct kref refcount; - - int n_subdevices; - struct comedi_subdevice *subdevices; - - /* dumb */ - void __iomem *mmio; - unsigned long iobase; - unsigned long iolen; - unsigned int irq; - - struct comedi_subdevice *read_subdev; - struct comedi_subdevice *write_subdev; - - struct fasync_struct *async_queue; - - int (*open)(struct comedi_device *dev); - void (*close)(struct comedi_device *dev); - int (*insn_device_config)(struct comedi_device *dev, - struct comedi_insn *insn, unsigned int *data); - unsigned int (*get_valid_routes)(struct comedi_device *dev, - unsigned int n_pairs, - unsigned int *pair_data); -}; - -/* - * function prototypes - */ - -void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s); - -struct comedi_device *comedi_dev_get_from_minor(unsigned int minor); -int comedi_dev_put(struct comedi_device *dev); - -bool comedi_is_subdevice_running(struct comedi_subdevice *s); - -void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size); -void comedi_set_spriv_auto_free(struct comedi_subdevice *s); - -int comedi_check_chanlist(struct comedi_subdevice *s, - int n, - unsigned int *chanlist); - -/* range stuff */ - -#define RANGE(a, b) {(a) * 1e6, (b) * 1e6, 0} -#define RANGE_ext(a, b) {(a) * 1e6, (b) * 1e6, RF_EXTERNAL} -#define RANGE_mA(a, b) {(a) * 1e6, (b) * 1e6, UNIT_mA} -#define RANGE_unitless(a, b) {(a) * 1e6, (b) * 1e6, 0} -#define BIP_RANGE(a) {-(a) * 1e6, (a) * 1e6, 0} -#define UNI_RANGE(a) {0, (a) * 1e6, 0} - -extern const struct comedi_lrange range_bipolar10; -extern const struct comedi_lrange range_bipolar5; -extern const struct comedi_lrange range_bipolar2_5; -extern const struct comedi_lrange range_unipolar10; -extern const struct comedi_lrange range_unipolar5; -extern const struct comedi_lrange range_unipolar2_5; -extern const struct comedi_lrange range_0_20mA; -extern const struct comedi_lrange range_4_20mA; -extern const struct comedi_lrange range_0_32mA; -extern const struct comedi_lrange range_unknown; - -#define range_digital range_unipolar5 - -/** - * struct comedi_lrange - Describes a COMEDI range table - * @length: Number of entries in the range table. - * @range: Array of &struct comedi_krange, one for each range. - * - * Each element of @range[] describes the minimum and maximum physical range - * and the type of units. Typically, the type of unit is %UNIT_volt - * (i.e. volts) and the minimum and maximum are in millionths of a volt. - * There may also be a flag that indicates the minimum and maximum are merely - * scale factors for an unknown, external reference. - */ -struct comedi_lrange { - int length; - struct comedi_krange range[]; -}; - -/** - * comedi_range_is_bipolar() - Test if subdevice range is bipolar - * @s: COMEDI subdevice. - * @range: Index of range within a range table. - * - * Tests whether a range is bipolar by checking whether its minimum value - * is negative. - * - * Assumes @range is valid. Does not work for subdevices using a - * channel-specific range table list. - * - * Return: - * %true if the range is bipolar. - * %false if the range is unipolar. - */ -static inline bool comedi_range_is_bipolar(struct comedi_subdevice *s, - unsigned int range) -{ - return s->range_table->range[range].min < 0; -} - -/** - * comedi_range_is_unipolar() - Test if subdevice range is unipolar - * @s: COMEDI subdevice. - * @range: Index of range within a range table. - * - * Tests whether a range is unipolar by checking whether its minimum value - * is at least 0. - * - * Assumes @range is valid. Does not work for subdevices using a - * channel-specific range table list. - * - * Return: - * %true if the range is unipolar. - * %false if the range is bipolar. - */ -static inline bool comedi_range_is_unipolar(struct comedi_subdevice *s, - unsigned int range) -{ - return s->range_table->range[range].min >= 0; -} - -/** - * comedi_range_is_external() - Test if subdevice range is external - * @s: COMEDI subdevice. - * @range: Index of range within a range table. - * - * Tests whether a range is externally reference by checking whether its - * %RF_EXTERNAL flag is set. - * - * Assumes @range is valid. Does not work for subdevices using a - * channel-specific range table list. - * - * Return: - * %true if the range is external. - * %false if the range is internal. - */ -static inline bool comedi_range_is_external(struct comedi_subdevice *s, - unsigned int range) -{ - return !!(s->range_table->range[range].flags & RF_EXTERNAL); -} - -/** - * comedi_chan_range_is_bipolar() - Test if channel-specific range is bipolar - * @s: COMEDI subdevice. - * @chan: The channel number. - * @range: Index of range within a range table. - * - * Tests whether a range is bipolar by checking whether its minimum value - * is negative. - * - * Assumes @chan and @range are valid. Only works for subdevices with a - * channel-specific range table list. - * - * Return: - * %true if the range is bipolar. - * %false if the range is unipolar. - */ -static inline bool comedi_chan_range_is_bipolar(struct comedi_subdevice *s, - unsigned int chan, - unsigned int range) -{ - return s->range_table_list[chan]->range[range].min < 0; -} - -/** - * comedi_chan_range_is_unipolar() - Test if channel-specific range is unipolar - * @s: COMEDI subdevice. - * @chan: The channel number. - * @range: Index of range within a range table. - * - * Tests whether a range is unipolar by checking whether its minimum value - * is at least 0. - * - * Assumes @chan and @range are valid. Only works for subdevices with a - * channel-specific range table list. - * - * Return: - * %true if the range is unipolar. - * %false if the range is bipolar. - */ -static inline bool comedi_chan_range_is_unipolar(struct comedi_subdevice *s, - unsigned int chan, - unsigned int range) -{ - return s->range_table_list[chan]->range[range].min >= 0; -} - -/** - * comedi_chan_range_is_external() - Test if channel-specific range is external - * @s: COMEDI subdevice. - * @chan: The channel number. - * @range: Index of range within a range table. - * - * Tests whether a range is externally reference by checking whether its - * %RF_EXTERNAL flag is set. - * - * Assumes @chan and @range are valid. Only works for subdevices with a - * channel-specific range table list. - * - * Return: - * %true if the range is bipolar. - * %false if the range is unipolar. - */ -static inline bool comedi_chan_range_is_external(struct comedi_subdevice *s, - unsigned int chan, - unsigned int range) -{ - return !!(s->range_table_list[chan]->range[range].flags & RF_EXTERNAL); -} - -/** - * comedi_offset_munge() - Convert between offset binary and 2's complement - * @s: COMEDI subdevice. - * @val: Value to be converted. - * - * Toggles the highest bit of a sample value to toggle between offset binary - * and 2's complement. Assumes that @s->maxdata is a power of 2 minus 1. - * - * Return: The converted value. - */ -static inline unsigned int comedi_offset_munge(struct comedi_subdevice *s, - unsigned int val) -{ - return val ^ s->maxdata ^ (s->maxdata >> 1); -} - -/** - * comedi_bytes_per_sample() - Determine subdevice sample size - * @s: COMEDI subdevice. - * - * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on - * whether the %SDF_LSAMPL subdevice flag is set or not. - * - * Return: The subdevice sample size. - */ -static inline unsigned int comedi_bytes_per_sample(struct comedi_subdevice *s) -{ - return s->subdev_flags & SDF_LSAMPL ? sizeof(int) : sizeof(short); -} - -/** - * comedi_sample_shift() - Determine log2 of subdevice sample size - * @s: COMEDI subdevice. - * - * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on - * whether the %SDF_LSAMPL subdevice flag is set or not. The log2 of the - * sample size will be 2 or 1 and can be used as the right operand of a - * bit-shift operator to multiply or divide something by the sample size. - * - * Return: log2 of the subdevice sample size. - */ -static inline unsigned int comedi_sample_shift(struct comedi_subdevice *s) -{ - return s->subdev_flags & SDF_LSAMPL ? 2 : 1; -} - -/** - * comedi_bytes_to_samples() - Convert a number of bytes to a number of samples - * @s: COMEDI subdevice. - * @nbytes: Number of bytes - * - * Return: The number of bytes divided by the subdevice sample size. - */ -static inline unsigned int comedi_bytes_to_samples(struct comedi_subdevice *s, - unsigned int nbytes) -{ - return nbytes >> comedi_sample_shift(s); -} - -/** - * comedi_samples_to_bytes() - Convert a number of samples to a number of bytes - * @s: COMEDI subdevice. - * @nsamples: Number of samples. - * - * Return: The number of samples multiplied by the subdevice sample size. - * (Does not check for arithmetic overflow.) - */ -static inline unsigned int comedi_samples_to_bytes(struct comedi_subdevice *s, - unsigned int nsamples) -{ - return nsamples << comedi_sample_shift(s); -} - -/** - * comedi_check_trigger_src() - Trivially validate a comedi_cmd trigger source - * @src: Pointer to the trigger source to validate. - * @flags: Bitmask of valid %TRIG_* for the trigger. - * - * This is used in "step 1" of the do_cmdtest functions of comedi drivers - * to validate the comedi_cmd triggers. The mask of the @src against the - * @flags allows the userspace comedilib to pass all the comedi_cmd - * triggers as %TRIG_ANY and get back a bitmask of the valid trigger sources. - * - * Return: - * 0 if trigger sources in *@src are all supported. - * -EINVAL if any trigger source in *@src is unsupported. - */ -static inline int comedi_check_trigger_src(unsigned int *src, - unsigned int flags) -{ - unsigned int orig_src = *src; - - *src = orig_src & flags; - if (*src == TRIG_INVALID || *src != orig_src) - return -EINVAL; - return 0; -} - -/** - * comedi_check_trigger_is_unique() - Make sure a trigger source is unique - * @src: The trigger source to check. - * - * Return: - * 0 if no more than one trigger source is set. - * -EINVAL if more than one trigger source is set. - */ -static inline int comedi_check_trigger_is_unique(unsigned int src) -{ - /* this test is true if more than one _src bit is set */ - if ((src & (src - 1)) != 0) - return -EINVAL; - return 0; -} - -/** - * comedi_check_trigger_arg_is() - Trivially validate a trigger argument - * @arg: Pointer to the trigger arg to validate. - * @val: The value the argument should be. - * - * Forces *@arg to be @val. - * - * Return: - * 0 if *@arg was already @val. - * -EINVAL if *@arg differed from @val. - */ -static inline int comedi_check_trigger_arg_is(unsigned int *arg, - unsigned int val) -{ - if (*arg != val) { - *arg = val; - return -EINVAL; - } - return 0; -} - -/** - * comedi_check_trigger_arg_min() - Trivially validate a trigger argument min - * @arg: Pointer to the trigger arg to validate. - * @val: The minimum value the argument should be. - * - * Forces *@arg to be at least @val, setting it to @val if necessary. - * - * Return: - * 0 if *@arg was already at least @val. - * -EINVAL if *@arg was less than @val. - */ -static inline int comedi_check_trigger_arg_min(unsigned int *arg, - unsigned int val) -{ - if (*arg < val) { - *arg = val; - return -EINVAL; - } - return 0; -} - -/** - * comedi_check_trigger_arg_max() - Trivially validate a trigger argument max - * @arg: Pointer to the trigger arg to validate. - * @val: The maximum value the argument should be. - * - * Forces *@arg to be no more than @val, setting it to @val if necessary. - * - * Return: - * 0 if*@arg was already no more than @val. - * -EINVAL if *@arg was greater than @val. - */ -static inline int comedi_check_trigger_arg_max(unsigned int *arg, - unsigned int val) -{ - if (*arg > val) { - *arg = val; - return -EINVAL; - } - return 0; -} - -/* - * Must set dev->hw_dev if you wish to dma directly into comedi's buffer. - * Also useful for retrieving a previously configured hardware device of - * known bus type. Set automatically for auto-configured devices. - * Automatically set to NULL when detaching hardware device. - */ -int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev); - -/** - * comedi_buf_n_bytes_ready - Determine amount of unread data in buffer - * @s: COMEDI subdevice. - * - * Determines the number of bytes of unread data in the asynchronous - * acquisition data buffer for a subdevice. The data in question might not - * have been fully "munged" yet. - * - * Returns: The amount of unread data in bytes. - */ -static inline unsigned int comedi_buf_n_bytes_ready(struct comedi_subdevice *s) -{ - return s->async->buf_write_count - s->async->buf_read_count; -} - -unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s, unsigned int n); -unsigned int comedi_buf_write_free(struct comedi_subdevice *s, unsigned int n); - -unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s); -unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s, unsigned int n); -unsigned int comedi_buf_read_free(struct comedi_subdevice *s, unsigned int n); - -unsigned int comedi_buf_write_samples(struct comedi_subdevice *s, - const void *data, unsigned int nsamples); -unsigned int comedi_buf_read_samples(struct comedi_subdevice *s, - void *data, unsigned int nsamples); - -/* drivers.c - general comedi driver functions */ - -#define COMEDI_TIMEOUT_MS 1000 - -int comedi_timeout(struct comedi_device *dev, struct comedi_subdevice *s, - struct comedi_insn *insn, - int (*cb)(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned long context), - unsigned long context); - -unsigned int comedi_handle_events(struct comedi_device *dev, - struct comedi_subdevice *s); - -int comedi_dio_insn_config(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data, - unsigned int mask); -unsigned int comedi_dio_update_state(struct comedi_subdevice *s, - unsigned int *data); -unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s, - struct comedi_cmd *cmd); -unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s); -unsigned int comedi_nscans_left(struct comedi_subdevice *s, - unsigned int nscans); -unsigned int comedi_nsamples_left(struct comedi_subdevice *s, - unsigned int nsamples); -void comedi_inc_scan_progress(struct comedi_subdevice *s, - unsigned int num_bytes); - -void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size); -int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices); -int comedi_alloc_subdev_readback(struct comedi_subdevice *s); - -int comedi_readback_insn_read(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); - -int comedi_load_firmware(struct comedi_device *dev, struct device *hw_dev, - const char *name, - int (*cb)(struct comedi_device *dev, - const u8 *data, size_t size, - unsigned long context), - unsigned long context); - -int __comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len); -int comedi_request_region(struct comedi_device *dev, - unsigned long start, unsigned long len); -void comedi_legacy_detach(struct comedi_device *dev); - -int comedi_auto_config(struct device *hardware_device, - struct comedi_driver *driver, unsigned long context); -void comedi_auto_unconfig(struct device *hardware_device); - -int comedi_driver_register(struct comedi_driver *driver); -void comedi_driver_unregister(struct comedi_driver *driver); - -/** - * module_comedi_driver() - Helper macro for registering a comedi driver - * @__comedi_driver: comedi_driver struct - * - * Helper macro for comedi drivers which do not do anything special in module - * init/exit. This eliminates a lot of boilerplate. Each module may only use - * this macro once, and calling it replaces module_init() and module_exit(). - */ -#define module_comedi_driver(__comedi_driver) \ - module_driver(__comedi_driver, comedi_driver_register, \ - comedi_driver_unregister) - -#endif /* _COMEDIDEV_H */ diff --git a/drivers/comedi/comedilib.h b/drivers/comedi/comedilib.h deleted file mode 100644 index 0223c9cd9215..000000000000 --- a/drivers/comedi/comedilib.h +++ /dev/null @@ -1,26 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * comedilib.h - * Header file for kcomedilib - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1998-2001 David A. Schleef - */ - -#ifndef _LINUX_COMEDILIB_H -#define _LINUX_COMEDILIB_H - -struct comedi_device *comedi_open(const char *path); -int comedi_close(struct comedi_device *dev); -int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev, - unsigned int chan, unsigned int *io); -int comedi_dio_config(struct comedi_device *dev, unsigned int subdev, - unsigned int chan, unsigned int io); -int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev, - unsigned int mask, unsigned int *bits, - unsigned int base_channel); -int comedi_find_subdevice_by_type(struct comedi_device *dev, int type, - unsigned int subd); -int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice); - -#endif diff --git a/drivers/comedi/drivers.c b/drivers/comedi/drivers.c index 750a6ff3c03c..8eb1f699a857 100644 --- a/drivers/comedi/drivers.c +++ b/drivers/comedi/drivers.c @@ -17,8 +17,7 @@ #include #include #include - -#include "comedidev.h" +#include #include "comedi_internal.h" struct comedi_driver *comedi_drivers; diff --git a/drivers/comedi/drivers/8255.c b/drivers/comedi/drivers/8255.c index e23335c75867..f23a52b7c919 100644 --- a/drivers/comedi/drivers/8255.c +++ b/drivers/comedi/drivers/8255.c @@ -40,7 +40,7 @@ */ #include -#include "../comedidev.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/8255_pci.c b/drivers/comedi/drivers/8255_pci.c index 5a810f0e532a..76b8b4762bae 100644 --- a/drivers/comedi/drivers/8255_pci.c +++ b/drivers/comedi/drivers/8255_pci.c @@ -53,8 +53,7 @@ */ #include - -#include "../comedi_pci.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/addi_apci_1032.c b/drivers/comedi/drivers/addi_apci_1032.c index 81a246fbcc01..8eec6d9402de 100644 --- a/drivers/comedi/drivers/addi_apci_1032.c +++ b/drivers/comedi/drivers/addi_apci_1032.c @@ -63,8 +63,8 @@ #include #include +#include -#include "../comedi_pci.h" #include "amcc_s5933.h" /* diff --git a/drivers/comedi/drivers/addi_apci_1500.c b/drivers/comedi/drivers/addi_apci_1500.c index b04c15dcfb57..c94c78588889 100644 --- a/drivers/comedi/drivers/addi_apci_1500.c +++ b/drivers/comedi/drivers/addi_apci_1500.c @@ -14,8 +14,8 @@ #include #include +#include -#include "../comedi_pci.h" #include "amcc_s5933.h" #include "z8536.h" diff --git a/drivers/comedi/drivers/addi_apci_1516.c b/drivers/comedi/drivers/addi_apci_1516.c index 274ec9fb030c..3c48b72dad9d 100644 --- a/drivers/comedi/drivers/addi_apci_1516.c +++ b/drivers/comedi/drivers/addi_apci_1516.c @@ -14,8 +14,8 @@ */ #include +#include -#include "../comedi_pci.h" #include "addi_watchdog.h" /* diff --git a/drivers/comedi/drivers/addi_apci_1564.c b/drivers/comedi/drivers/addi_apci_1564.c index 06fc7ed96200..0cd40948bee7 100644 --- a/drivers/comedi/drivers/addi_apci_1564.c +++ b/drivers/comedi/drivers/addi_apci_1564.c @@ -68,8 +68,8 @@ #include #include +#include -#include "../comedi_pci.h" #include "addi_tcw.h" #include "addi_watchdog.h" diff --git a/drivers/comedi/drivers/addi_apci_16xx.c b/drivers/comedi/drivers/addi_apci_16xx.c index c306aa41df97..ec2c321d2431 100644 --- a/drivers/comedi/drivers/addi_apci_16xx.c +++ b/drivers/comedi/drivers/addi_apci_16xx.c @@ -14,8 +14,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/addi_apci_2032.c b/drivers/comedi/drivers/addi_apci_2032.c index e9a2b37a4ae0..e048dfc3ec77 100644 --- a/drivers/comedi/drivers/addi_apci_2032.c +++ b/drivers/comedi/drivers/addi_apci_2032.c @@ -16,8 +16,8 @@ #include #include #include +#include -#include "../comedi_pci.h" #include "addi_watchdog.h" /* diff --git a/drivers/comedi/drivers/addi_apci_2200.c b/drivers/comedi/drivers/addi_apci_2200.c index 4c5aee784bd9..00378c9dddc8 100644 --- a/drivers/comedi/drivers/addi_apci_2200.c +++ b/drivers/comedi/drivers/addi_apci_2200.c @@ -14,8 +14,8 @@ */ #include +#include -#include "../comedi_pci.h" #include "addi_watchdog.h" /* diff --git a/drivers/comedi/drivers/addi_apci_3120.c b/drivers/comedi/drivers/addi_apci_3120.c index 1ed3b33d1a30..28a242e69721 100644 --- a/drivers/comedi/drivers/addi_apci_3120.c +++ b/drivers/comedi/drivers/addi_apci_3120.c @@ -14,8 +14,8 @@ #include #include +#include -#include "../comedi_pci.h" #include "amcc_s5933.h" /* diff --git a/drivers/comedi/drivers/addi_apci_3501.c b/drivers/comedi/drivers/addi_apci_3501.c index f0c9642f3f1a..ecb5552f1785 100644 --- a/drivers/comedi/drivers/addi_apci_3501.c +++ b/drivers/comedi/drivers/addi_apci_3501.c @@ -41,8 +41,8 @@ */ #include +#include -#include "../comedi_pci.h" #include "amcc_s5933.h" /* diff --git a/drivers/comedi/drivers/addi_apci_3xxx.c b/drivers/comedi/drivers/addi_apci_3xxx.c index a90d59377e18..bc72273e6a29 100644 --- a/drivers/comedi/drivers/addi_apci_3xxx.c +++ b/drivers/comedi/drivers/addi_apci_3xxx.c @@ -15,8 +15,7 @@ #include #include - -#include "../comedi_pci.h" +#include #define CONV_UNIT_NS BIT(0) #define CONV_UNIT_US BIT(1) diff --git a/drivers/comedi/drivers/addi_watchdog.c b/drivers/comedi/drivers/addi_watchdog.c index 69b323fb869f..ed87ab432020 100644 --- a/drivers/comedi/drivers/addi_watchdog.c +++ b/drivers/comedi/drivers/addi_watchdog.c @@ -10,7 +10,7 @@ */ #include -#include "../comedidev.h" +#include #include "addi_tcw.h" #include "addi_watchdog.h" diff --git a/drivers/comedi/drivers/adl_pci6208.c b/drivers/comedi/drivers/adl_pci6208.c index 9ae4cc523dd4..b27354a51f5c 100644 --- a/drivers/comedi/drivers/adl_pci6208.c +++ b/drivers/comedi/drivers/adl_pci6208.c @@ -24,8 +24,7 @@ #include #include - -#include "../comedi_pci.h" +#include /* * PCI-6208/6216-GL register map diff --git a/drivers/comedi/drivers/adl_pci7x3x.c b/drivers/comedi/drivers/adl_pci7x3x.c index 8fc45638ff59..e9f22de9b6f1 100644 --- a/drivers/comedi/drivers/adl_pci7x3x.c +++ b/drivers/comedi/drivers/adl_pci7x3x.c @@ -46,8 +46,7 @@ */ #include - -#include "../comedi_pci.h" +#include #include "plx9052.h" diff --git a/drivers/comedi/drivers/adl_pci8164.c b/drivers/comedi/drivers/adl_pci8164.c index d5e1bda81557..0c513a67a264 100644 --- a/drivers/comedi/drivers/adl_pci8164.c +++ b/drivers/comedi/drivers/adl_pci8164.c @@ -19,8 +19,7 @@ #include #include - -#include "../comedi_pci.h" +#include #define PCI8164_AXIS(x) ((x) * 0x08) #define PCI8164_CMD_MSTS_REG 0x00 diff --git a/drivers/comedi/drivers/adl_pci9111.c b/drivers/comedi/drivers/adl_pci9111.c index a062c5ab20e9..65454f3ecc91 100644 --- a/drivers/comedi/drivers/adl_pci9111.c +++ b/drivers/comedi/drivers/adl_pci9111.c @@ -42,8 +42,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "plx9052.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/adl_pci9118.c b/drivers/comedi/drivers/adl_pci9118.c index cda3a4267dca..248cec3d894f 100644 --- a/drivers/comedi/drivers/adl_pci9118.c +++ b/drivers/comedi/drivers/adl_pci9118.c @@ -78,8 +78,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "amcc_s5933.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/adq12b.c b/drivers/comedi/drivers/adq12b.c index d719f76709ef..19d765182006 100644 --- a/drivers/comedi/drivers/adq12b.c +++ b/drivers/comedi/drivers/adq12b.c @@ -48,8 +48,7 @@ #include #include - -#include "../comedidev.h" +#include /* address scheme (page 2.17 of the manual) */ #define ADQ12B_CTREG 0x00 diff --git a/drivers/comedi/drivers/adv_pci1710.c b/drivers/comedi/drivers/adv_pci1710.c index 090607760be6..47a800d72e58 100644 --- a/drivers/comedi/drivers/adv_pci1710.c +++ b/drivers/comedi/drivers/adv_pci1710.c @@ -30,8 +30,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" #include "amcc_s5933.h" diff --git a/drivers/comedi/drivers/adv_pci1720.c b/drivers/comedi/drivers/adv_pci1720.c index 2fcd7e8e7d85..2619591ba301 100644 --- a/drivers/comedi/drivers/adv_pci1720.c +++ b/drivers/comedi/drivers/adv_pci1720.c @@ -42,8 +42,7 @@ #include #include - -#include "../comedi_pci.h" +#include /* * PCI BAR2 Register map (dev->iobase) diff --git a/drivers/comedi/drivers/adv_pci1723.c b/drivers/comedi/drivers/adv_pci1723.c index 23660a9fdb9c..e2aedb152068 100644 --- a/drivers/comedi/drivers/adv_pci1723.c +++ b/drivers/comedi/drivers/adv_pci1723.c @@ -32,8 +32,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* * PCI Bar 2 I/O Register map (dev->iobase) diff --git a/drivers/comedi/drivers/adv_pci1724.c b/drivers/comedi/drivers/adv_pci1724.c index e8ab573c839f..bb43b7deeb56 100644 --- a/drivers/comedi/drivers/adv_pci1724.c +++ b/drivers/comedi/drivers/adv_pci1724.c @@ -38,8 +38,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* * PCI bar 2 Register I/O map (dev->iobase) diff --git a/drivers/comedi/drivers/adv_pci1760.c b/drivers/comedi/drivers/adv_pci1760.c index 6de8ab97d346..fcfc2e299110 100644 --- a/drivers/comedi/drivers/adv_pci1760.c +++ b/drivers/comedi/drivers/adv_pci1760.c @@ -22,8 +22,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* * PCI-1760 Register Map diff --git a/drivers/comedi/drivers/adv_pci_dio.c b/drivers/comedi/drivers/adv_pci_dio.c index 54c7419c8ca6..5947f08b9a1e 100644 --- a/drivers/comedi/drivers/adv_pci_dio.c +++ b/drivers/comedi/drivers/adv_pci_dio.c @@ -23,8 +23,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "8255.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/aio_aio12_8.c b/drivers/comedi/drivers/aio_aio12_8.c index 4829115921a3..36c3a2d8a352 100644 --- a/drivers/comedi/drivers/aio_aio12_8.c +++ b/drivers/comedi/drivers/aio_aio12_8.c @@ -22,7 +22,7 @@ */ #include -#include "../comedidev.h" +#include #include "comedi_8254.h" #include "8255.h" diff --git a/drivers/comedi/drivers/aio_iiro_16.c b/drivers/comedi/drivers/aio_iiro_16.c index fe3876235075..b00fab0b89d4 100644 --- a/drivers/comedi/drivers/aio_iiro_16.c +++ b/drivers/comedi/drivers/aio_iiro_16.c @@ -30,8 +30,7 @@ #include #include - -#include "../comedidev.h" +#include #define AIO_IIRO_16_RELAY_0_7 0x00 #define AIO_IIRO_16_INPUT_0_7 0x01 diff --git a/drivers/comedi/drivers/amplc_dio200.c b/drivers/comedi/drivers/amplc_dio200.c index fa19c9e7c56b..4544bcdd8a70 100644 --- a/drivers/comedi/drivers/amplc_dio200.c +++ b/drivers/comedi/drivers/amplc_dio200.c @@ -185,7 +185,7 @@ */ #include -#include "../comedidev.h" +#include #include "amplc_dio200.h" diff --git a/drivers/comedi/drivers/amplc_dio200_common.c b/drivers/comedi/drivers/amplc_dio200_common.c index a3454130d5f8..950c50be4ff3 100644 --- a/drivers/comedi/drivers/amplc_dio200_common.c +++ b/drivers/comedi/drivers/amplc_dio200_common.c @@ -12,8 +12,7 @@ #include #include - -#include "../comedidev.h" +#include #include "amplc_dio200.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/amplc_dio200_pci.c b/drivers/comedi/drivers/amplc_dio200_pci.c index 1bd7a42c8464..527994d82a1f 100644 --- a/drivers/comedi/drivers/amplc_dio200_pci.c +++ b/drivers/comedi/drivers/amplc_dio200_pci.c @@ -214,8 +214,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "amplc_dio200.h" diff --git a/drivers/comedi/drivers/amplc_pc236.c b/drivers/comedi/drivers/amplc_pc236.c index c377af1d5246..b21e0c906aab 100644 --- a/drivers/comedi/drivers/amplc_pc236.c +++ b/drivers/comedi/drivers/amplc_pc236.c @@ -32,8 +32,7 @@ */ #include - -#include "../comedidev.h" +#include #include "amplc_pc236.h" diff --git a/drivers/comedi/drivers/amplc_pc236_common.c b/drivers/comedi/drivers/amplc_pc236_common.c index 981d281e87a1..b8b0a624f72b 100644 --- a/drivers/comedi/drivers/amplc_pc236_common.c +++ b/drivers/comedi/drivers/amplc_pc236_common.c @@ -11,8 +11,7 @@ #include #include - -#include "../comedidev.h" +#include #include "amplc_pc236.h" #include "8255.h" diff --git a/drivers/comedi/drivers/amplc_pc263.c b/drivers/comedi/drivers/amplc_pc263.c index 68da6098ee84..d7f088a8a5e3 100644 --- a/drivers/comedi/drivers/amplc_pc263.c +++ b/drivers/comedi/drivers/amplc_pc263.c @@ -25,7 +25,7 @@ */ #include -#include "../comedidev.h" +#include /* PC263 registers */ #define PC263_DO_0_7_REG 0x00 diff --git a/drivers/comedi/drivers/amplc_pci224.c b/drivers/comedi/drivers/amplc_pci224.c index bcf6d61af863..3cf1b7fa565d 100644 --- a/drivers/comedi/drivers/amplc_pci224.c +++ b/drivers/comedi/drivers/amplc_pci224.c @@ -96,8 +96,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/amplc_pci230.c b/drivers/comedi/drivers/amplc_pci230.c index 8911dc2bd2c6..554ee40e321f 100644 --- a/drivers/comedi/drivers/amplc_pci230.c +++ b/drivers/comedi/drivers/amplc_pci230.c @@ -174,8 +174,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" #include "8255.h" diff --git a/drivers/comedi/drivers/amplc_pci236.c b/drivers/comedi/drivers/amplc_pci236.c index e7f6fa4d101a..482eb261c333 100644 --- a/drivers/comedi/drivers/amplc_pci236.c +++ b/drivers/comedi/drivers/amplc_pci236.c @@ -34,8 +34,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "amplc_pc236.h" #include "plx9052.h" diff --git a/drivers/comedi/drivers/amplc_pci263.c b/drivers/comedi/drivers/amplc_pci263.c index 9217973f1141..1609665c4b18 100644 --- a/drivers/comedi/drivers/amplc_pci263.c +++ b/drivers/comedi/drivers/amplc_pci263.c @@ -24,8 +24,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* PCI263 registers */ #define PCI263_DO_0_7_REG 0x00 diff --git a/drivers/comedi/drivers/c6xdigio.c b/drivers/comedi/drivers/c6xdigio.c index 786fd15698df..14b90d1c64dc 100644 --- a/drivers/comedi/drivers/c6xdigio.c +++ b/drivers/comedi/drivers/c6xdigio.c @@ -30,8 +30,7 @@ #include #include #include - -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/cb_das16_cs.c b/drivers/comedi/drivers/cb_das16_cs.c index a5d171e71c33..190d73a7d12c 100644 --- a/drivers/comedi/drivers/cb_das16_cs.c +++ b/drivers/comedi/drivers/cb_das16_cs.c @@ -27,8 +27,7 @@ #include #include #include - -#include "../comedi_pcmcia.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/cb_pcidas.c b/drivers/comedi/drivers/cb_pcidas.c index 2f20bd56ec6c..9b603532a4e7 100644 --- a/drivers/comedi/drivers/cb_pcidas.c +++ b/drivers/comedi/drivers/cb_pcidas.c @@ -54,8 +54,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" #include "8255.h" diff --git a/drivers/comedi/drivers/cb_pcidas64.c b/drivers/comedi/drivers/cb_pcidas64.c index 41a8fea7f48a..7d4808faa1fb 100644 --- a/drivers/comedi/drivers/cb_pcidas64.c +++ b/drivers/comedi/drivers/cb_pcidas64.c @@ -73,8 +73,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "8255.h" #include "plx9080.h" diff --git a/drivers/comedi/drivers/cb_pcidda.c b/drivers/comedi/drivers/cb_pcidda.c index 78cf1603638c..4ed3bcf47973 100644 --- a/drivers/comedi/drivers/cb_pcidda.c +++ b/drivers/comedi/drivers/cb_pcidda.c @@ -27,8 +27,7 @@ */ #include - -#include "../comedi_pci.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/cb_pcimdas.c b/drivers/comedi/drivers/cb_pcimdas.c index 2292f69da4f4..64c7d72c7956 100644 --- a/drivers/comedi/drivers/cb_pcimdas.c +++ b/drivers/comedi/drivers/cb_pcimdas.c @@ -34,8 +34,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" #include "plx9052.h" diff --git a/drivers/comedi/drivers/cb_pcimdda.c b/drivers/comedi/drivers/cb_pcimdda.c index 21fc7b3c5f60..69d7803b0e58 100644 --- a/drivers/comedi/drivers/cb_pcimdda.c +++ b/drivers/comedi/drivers/cb_pcimdda.c @@ -67,8 +67,7 @@ */ #include - -#include "../comedi_pci.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/comedi_8254.c b/drivers/comedi/drivers/comedi_8254.c index 4bf5daa9e885..fac81567133d 100644 --- a/drivers/comedi/drivers/comedi_8254.c +++ b/drivers/comedi/drivers/comedi_8254.c @@ -116,8 +116,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/comedi_8255.c b/drivers/comedi/drivers/comedi_8255.c index b7ca465933ee..10614603d677 100644 --- a/drivers/comedi/drivers/comedi_8255.c +++ b/drivers/comedi/drivers/comedi_8255.c @@ -29,7 +29,7 @@ */ #include -#include "../comedidev.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/comedi_bond.c b/drivers/comedi/drivers/comedi_bond.c index 4392b5927a99..78c39fa84177 100644 --- a/drivers/comedi/drivers/comedi_bond.c +++ b/drivers/comedi/drivers/comedi_bond.c @@ -40,9 +40,9 @@ #include #include #include -#include "../comedi.h" -#include "../comedilib.h" -#include "../comedidev.h" +#include +#include +#include struct bonded_device { struct comedi_device *dev; diff --git a/drivers/comedi/drivers/comedi_isadma.c b/drivers/comedi/drivers/comedi_isadma.c index 479b58e209ba..63457bd4ff78 100644 --- a/drivers/comedi/drivers/comedi_isadma.c +++ b/drivers/comedi/drivers/comedi_isadma.c @@ -9,8 +9,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" diff --git a/drivers/comedi/drivers/comedi_parport.c b/drivers/comedi/drivers/comedi_parport.c index 5338b5eea440..098738a688fe 100644 --- a/drivers/comedi/drivers/comedi_parport.c +++ b/drivers/comedi/drivers/comedi_parport.c @@ -57,8 +57,7 @@ #include #include - -#include "../comedidev.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/comedi_test.c b/drivers/comedi/drivers/comedi_test.c index cbc225eb1991..0b5c0af1cebf 100644 --- a/drivers/comedi/drivers/comedi_test.c +++ b/drivers/comedi/drivers/comedi_test.c @@ -45,10 +45,8 @@ */ #include -#include "../comedidev.h" - +#include #include - #include #include #include diff --git a/drivers/comedi/drivers/contec_pci_dio.c b/drivers/comedi/drivers/contec_pci_dio.c index b8fdd9c1f166..41d42ff14144 100644 --- a/drivers/comedi/drivers/contec_pci_dio.c +++ b/drivers/comedi/drivers/contec_pci_dio.c @@ -18,8 +18,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/dac02.c b/drivers/comedi/drivers/dac02.c index 5ef8114c2c85..4b011d66d7b0 100644 --- a/drivers/comedi/drivers/dac02.c +++ b/drivers/comedi/drivers/dac02.c @@ -25,8 +25,7 @@ */ #include - -#include "../comedidev.h" +#include /* * The output range is selected by jumpering pins on the I/O connector. diff --git a/drivers/comedi/drivers/daqboard2000.c b/drivers/comedi/drivers/daqboard2000.c index f64e747078bd..52e4bf16cbda 100644 --- a/drivers/comedi/drivers/daqboard2000.c +++ b/drivers/comedi/drivers/daqboard2000.c @@ -96,8 +96,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "8255.h" #include "plx9080.h" diff --git a/drivers/comedi/drivers/das08.c b/drivers/comedi/drivers/das08.c index b50743c5b822..c146a168f43b 100644 --- a/drivers/comedi/drivers/das08.c +++ b/drivers/comedi/drivers/das08.c @@ -10,8 +10,7 @@ */ #include - -#include "../comedidev.h" +#include #include "8255.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/das08_cs.c b/drivers/comedi/drivers/das08_cs.c index 223479f9ea3c..6075efcf10d6 100644 --- a/drivers/comedi/drivers/das08_cs.c +++ b/drivers/comedi/drivers/das08_cs.c @@ -30,8 +30,7 @@ */ #include - -#include "../comedi_pcmcia.h" +#include #include "das08.h" diff --git a/drivers/comedi/drivers/das08_isa.c b/drivers/comedi/drivers/das08_isa.c index 8c4cfa821423..3d43b77cc9f4 100644 --- a/drivers/comedi/drivers/das08_isa.c +++ b/drivers/comedi/drivers/das08_isa.c @@ -29,7 +29,7 @@ */ #include -#include "../comedidev.h" +#include #include "das08.h" diff --git a/drivers/comedi/drivers/das08_pci.c b/drivers/comedi/drivers/das08_pci.c index 1cd903336a4c..982f3ab0ccbd 100644 --- a/drivers/comedi/drivers/das08_pci.c +++ b/drivers/comedi/drivers/das08_pci.c @@ -23,8 +23,7 @@ */ #include - -#include "../comedi_pci.h" +#include #include "das08.h" diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index 4ac2622b0fac..362232ad4409 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -63,8 +63,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/das16m1.c b/drivers/comedi/drivers/das16m1.c index 75f3dbbe97ac..cc79e318cb2d 100644 --- a/drivers/comedi/drivers/das16m1.c +++ b/drivers/comedi/drivers/das16m1.c @@ -42,7 +42,7 @@ #include #include #include -#include "../comedidev.h" +#include #include "8255.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/das1800.c b/drivers/comedi/drivers/das1800.c index f50891a6ee7d..768803742350 100644 --- a/drivers/comedi/drivers/das1800.c +++ b/drivers/comedi/drivers/das1800.c @@ -73,8 +73,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index 96f4107b8054..d411ab7cf37c 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -24,8 +24,7 @@ #include #include - -#include "../comedidev.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/das800.c b/drivers/comedi/drivers/das800.c index bc08324f422f..c95e0fcb94a4 100644 --- a/drivers/comedi/drivers/das800.c +++ b/drivers/comedi/drivers/das800.c @@ -46,8 +46,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/dmm32at.c b/drivers/comedi/drivers/dmm32at.c index 56682f01242f..0f2bea88b8a7 100644 --- a/drivers/comedi/drivers/dmm32at.c +++ b/drivers/comedi/drivers/dmm32at.c @@ -29,7 +29,7 @@ #include #include #include -#include "../comedidev.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/dt2801.c b/drivers/comedi/drivers/dt2801.c index 0d571d817b4e..230d25010f58 100644 --- a/drivers/comedi/drivers/dt2801.c +++ b/drivers/comedi/drivers/dt2801.c @@ -31,7 +31,7 @@ */ #include -#include "../comedidev.h" +#include #include #define DT2801_TIMEOUT 1000 diff --git a/drivers/comedi/drivers/dt2811.c b/drivers/comedi/drivers/dt2811.c index 0eb5e6ba6916..dbb9f38da289 100644 --- a/drivers/comedi/drivers/dt2811.c +++ b/drivers/comedi/drivers/dt2811.c @@ -40,8 +40,7 @@ #include #include #include - -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/dt2814.c b/drivers/comedi/drivers/dt2814.c index ed44ce0d151b..c98a5a4a7aec 100644 --- a/drivers/comedi/drivers/dt2814.c +++ b/drivers/comedi/drivers/dt2814.c @@ -27,8 +27,7 @@ #include #include -#include "../comedidev.h" - +#include #include #define DT2814_CSR 0 diff --git a/drivers/comedi/drivers/dt2815.c b/drivers/comedi/drivers/dt2815.c index 5906f32aa01f..03ba2fd18a21 100644 --- a/drivers/comedi/drivers/dt2815.c +++ b/drivers/comedi/drivers/dt2815.c @@ -43,8 +43,7 @@ */ #include -#include "../comedidev.h" - +#include #include #define DT2815_DATA 0 diff --git a/drivers/comedi/drivers/dt2817.c b/drivers/comedi/drivers/dt2817.c index 7c1463e835d3..6738045c7531 100644 --- a/drivers/comedi/drivers/dt2817.c +++ b/drivers/comedi/drivers/dt2817.c @@ -25,7 +25,7 @@ */ #include -#include "../comedidev.h" +#include #define DT2817_CR 0 #define DT2817_DATA 1 diff --git a/drivers/comedi/drivers/dt282x.c b/drivers/comedi/drivers/dt282x.c index 2656b4b0e3d0..078f8fba7183 100644 --- a/drivers/comedi/drivers/dt282x.c +++ b/drivers/comedi/drivers/dt282x.c @@ -51,8 +51,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" diff --git a/drivers/comedi/drivers/dt3000.c b/drivers/comedi/drivers/dt3000.c index ec27aa4730d4..fc6e9c30e522 100644 --- a/drivers/comedi/drivers/dt3000.c +++ b/drivers/comedi/drivers/dt3000.c @@ -43,8 +43,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include /* * PCI BAR0 - dual-ported RAM location definitions (dev->mmio) diff --git a/drivers/comedi/drivers/dt9812.c b/drivers/comedi/drivers/dt9812.c index 704b04d2980d..b37b9d8eca0d 100644 --- a/drivers/comedi/drivers/dt9812.c +++ b/drivers/comedi/drivers/dt9812.c @@ -34,8 +34,7 @@ #include #include #include - -#include "../comedi_usb.h" +#include #define DT9812_DIAGS_BOARD_INFO_ADDR 0xFBFF #define DT9812_MAX_WRITE_CMD_PIPE_SIZE 32 diff --git a/drivers/comedi/drivers/dyna_pci10xx.c b/drivers/comedi/drivers/dyna_pci10xx.c index c224422bb126..407a038fb3e0 100644 --- a/drivers/comedi/drivers/dyna_pci10xx.c +++ b/drivers/comedi/drivers/dyna_pci10xx.c @@ -26,8 +26,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #define READ_TIMEOUT 50 diff --git a/drivers/comedi/drivers/fl512.c b/drivers/comedi/drivers/fl512.c index b715f30659fa..139e801fc358 100644 --- a/drivers/comedi/drivers/fl512.c +++ b/drivers/comedi/drivers/fl512.c @@ -21,8 +21,7 @@ */ #include -#include "../comedidev.h" - +#include #include /* diff --git a/drivers/comedi/drivers/gsc_hpdi.c b/drivers/comedi/drivers/gsc_hpdi.c index e35e4a743714..c09d135df38d 100644 --- a/drivers/comedi/drivers/gsc_hpdi.c +++ b/drivers/comedi/drivers/gsc_hpdi.c @@ -34,8 +34,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "plx9080.h" diff --git a/drivers/comedi/drivers/icp_multi.c b/drivers/comedi/drivers/icp_multi.c index 16d2b78de83c..ac4b11dbd741 100644 --- a/drivers/comedi/drivers/icp_multi.c +++ b/drivers/comedi/drivers/icp_multi.c @@ -36,8 +36,7 @@ #include #include - -#include "../comedi_pci.h" +#include #define ICP_MULTI_ADC_CSR 0x00 /* R/W: ADC command/status register */ #define ICP_MULTI_ADC_CSR_ST BIT(0) /* Start ADC */ diff --git a/drivers/comedi/drivers/ii_pci20kc.c b/drivers/comedi/drivers/ii_pci20kc.c index 399255dbe388..4a19bf8462be 100644 --- a/drivers/comedi/drivers/ii_pci20kc.c +++ b/drivers/comedi/drivers/ii_pci20kc.c @@ -30,7 +30,7 @@ #include #include -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/jr3_pci.c b/drivers/comedi/drivers/jr3_pci.c index f963080dd61f..951c23fa0369 100644 --- a/drivers/comedi/drivers/jr3_pci.c +++ b/drivers/comedi/drivers/jr3_pci.c @@ -35,8 +35,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "jr3_pci.h" diff --git a/drivers/comedi/drivers/ke_counter.c b/drivers/comedi/drivers/ke_counter.c index bef1b20c1c8d..b825cf60e1e0 100644 --- a/drivers/comedi/drivers/ke_counter.c +++ b/drivers/comedi/drivers/ke_counter.c @@ -19,8 +19,7 @@ */ #include - -#include "../comedi_pci.h" +#include /* * PCI BAR 0 Register I/O map diff --git a/drivers/comedi/drivers/me4000.c b/drivers/comedi/drivers/me4000.c index 0d3d4cafce2e..c5dc8199771f 100644 --- a/drivers/comedi/drivers/me4000.c +++ b/drivers/comedi/drivers/me4000.c @@ -32,8 +32,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" #include "plx9052.h" diff --git a/drivers/comedi/drivers/me_daq.c b/drivers/comedi/drivers/me_daq.c index ef18e387471b..076b15097afd 100644 --- a/drivers/comedi/drivers/me_daq.c +++ b/drivers/comedi/drivers/me_daq.c @@ -23,8 +23,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "plx9052.h" diff --git a/drivers/comedi/drivers/mf6x4.c b/drivers/comedi/drivers/mf6x4.c index 9da8dd748078..14f1d5e9cd59 100644 --- a/drivers/comedi/drivers/mf6x4.c +++ b/drivers/comedi/drivers/mf6x4.c @@ -18,8 +18,7 @@ #include #include - -#include "../comedi_pci.h" +#include /* Registers present in BAR0 memory region */ #define MF624_GPIOC_REG 0x54 diff --git a/drivers/comedi/drivers/mite.c b/drivers/comedi/drivers/mite.c index 70960e3ba878..88f3cd6f54f1 100644 --- a/drivers/comedi/drivers/mite.c +++ b/drivers/comedi/drivers/mite.c @@ -38,8 +38,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "mite.h" diff --git a/drivers/comedi/drivers/mpc624.c b/drivers/comedi/drivers/mpc624.c index 646f4c086204..9e51ff528ed1 100644 --- a/drivers/comedi/drivers/mpc624.c +++ b/drivers/comedi/drivers/mpc624.c @@ -44,8 +44,7 @@ */ #include -#include "../comedidev.h" - +#include #include /* Offsets of different ports */ diff --git a/drivers/comedi/drivers/multiq3.c b/drivers/comedi/drivers/multiq3.c index c1897aee9a9a..07ff5383da99 100644 --- a/drivers/comedi/drivers/multiq3.c +++ b/drivers/comedi/drivers/multiq3.c @@ -26,8 +26,7 @@ */ #include - -#include "../comedidev.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/ni_6527.c b/drivers/comedi/drivers/ni_6527.c index f1a45cf7342a..ac5820085231 100644 --- a/drivers/comedi/drivers/ni_6527.c +++ b/drivers/comedi/drivers/ni_6527.c @@ -20,8 +20,7 @@ #include #include - -#include "../comedi_pci.h" +#include /* * PCI BAR1 - Register memory map diff --git a/drivers/comedi/drivers/ni_65xx.c b/drivers/comedi/drivers/ni_65xx.c index 7cd8497420f2..58334de3b253 100644 --- a/drivers/comedi/drivers/ni_65xx.c +++ b/drivers/comedi/drivers/ni_65xx.c @@ -49,8 +49,7 @@ #include #include - -#include "../comedi_pci.h" +#include /* * PCI BAR1 Register Map diff --git a/drivers/comedi/drivers/ni_660x.c b/drivers/comedi/drivers/ni_660x.c index e60d0125bcb2..0679bc39e0bc 100644 --- a/drivers/comedi/drivers/ni_660x.c +++ b/drivers/comedi/drivers/ni_660x.c @@ -26,8 +26,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "mite.h" #include "ni_tio.h" diff --git a/drivers/comedi/drivers/ni_670x.c b/drivers/comedi/drivers/ni_670x.c index c197e47486be..c875d251c230 100644 --- a/drivers/comedi/drivers/ni_670x.c +++ b/drivers/comedi/drivers/ni_670x.c @@ -24,8 +24,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #define AO_VALUE_OFFSET 0x00 #define AO_CHAN_OFFSET 0x0c diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index 10ad7b88713e..ce5de58c499f 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -39,8 +39,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/ni_at_ao.c b/drivers/comedi/drivers/ni_at_ao.c index 2a0fb4d460db..a06dfb9da329 100644 --- a/drivers/comedi/drivers/ni_at_ao.c +++ b/drivers/comedi/drivers/ni_at_ao.c @@ -25,8 +25,7 @@ */ #include - -#include "../comedidev.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index 56c78da475e7..f60a4e459a98 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -73,8 +73,7 @@ #include #include -#include "../comedidev.h" - +#include #include #include "ni_stc.h" diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index dffce1aa3e69..0bd4f88a2ac8 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -39,7 +39,7 @@ #include #include -#include "../comedidev.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/ni_daq_700.c b/drivers/comedi/drivers/ni_daq_700.c index d40fc89f9cef..0ef20e9a8bc4 100644 --- a/drivers/comedi/drivers/ni_daq_700.c +++ b/drivers/comedi/drivers/ni_daq_700.c @@ -41,8 +41,7 @@ #include #include #include - -#include "../comedi_pcmcia.h" +#include /* daqcard700 registers */ #define DIO_W 0x04 /* WO 8bit */ diff --git a/drivers/comedi/drivers/ni_daq_dio24.c b/drivers/comedi/drivers/ni_daq_dio24.c index 44fb65afc218..84d78f2ee5ac 100644 --- a/drivers/comedi/drivers/ni_daq_dio24.c +++ b/drivers/comedi/drivers/ni_daq_dio24.c @@ -23,7 +23,7 @@ */ #include -#include "../comedi_pcmcia.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/ni_labpc.c b/drivers/comedi/drivers/ni_labpc.c index 1f4a07bd1d26..b25a8e117072 100644 --- a/drivers/comedi/drivers/ni_labpc.c +++ b/drivers/comedi/drivers/ni_labpc.c @@ -48,8 +48,7 @@ */ #include - -#include "../comedidev.h" +#include #include "ni_labpc.h" #include "ni_labpc_isadma.h" diff --git a/drivers/comedi/drivers/ni_labpc_common.c b/drivers/comedi/drivers/ni_labpc_common.c index dd97946eacaf..7c4687226450 100644 --- a/drivers/comedi/drivers/ni_labpc_common.c +++ b/drivers/comedi/drivers/ni_labpc_common.c @@ -12,8 +12,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_8254.h" #include "8255.h" diff --git a/drivers/comedi/drivers/ni_labpc_cs.c b/drivers/comedi/drivers/ni_labpc_cs.c index 4f7e2fe21254..62fecb50ec6e 100644 --- a/drivers/comedi/drivers/ni_labpc_cs.c +++ b/drivers/comedi/drivers/ni_labpc_cs.c @@ -38,8 +38,7 @@ */ #include - -#include "../comedi_pcmcia.h" +#include #include "ni_labpc.h" diff --git a/drivers/comedi/drivers/ni_labpc_isadma.c b/drivers/comedi/drivers/ni_labpc_isadma.c index a551aca6e615..dd37ec0d9b15 100644 --- a/drivers/comedi/drivers/ni_labpc_isadma.c +++ b/drivers/comedi/drivers/ni_labpc_isadma.c @@ -10,8 +10,7 @@ #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "ni_labpc.h" diff --git a/drivers/comedi/drivers/ni_labpc_pci.c b/drivers/comedi/drivers/ni_labpc_pci.c index ec180b0fedf7..e2a44bbd9fa6 100644 --- a/drivers/comedi/drivers/ni_labpc_pci.c +++ b/drivers/comedi/drivers/ni_labpc_pci.c @@ -22,8 +22,7 @@ #include #include - -#include "../comedi_pci.h" +#include #include "ni_labpc.h" diff --git a/drivers/comedi/drivers/ni_mio_cs.c b/drivers/comedi/drivers/ni_mio_cs.c index 4f37b4e58f09..bd967cdb2036 100644 --- a/drivers/comedi/drivers/ni_mio_cs.c +++ b/drivers/comedi/drivers/ni_mio_cs.c @@ -28,8 +28,8 @@ #include #include +#include -#include "../comedi_pcmcia.h" #include "ni_stc.h" #include "8255.h" diff --git a/drivers/comedi/drivers/ni_pcidio.c b/drivers/comedi/drivers/ni_pcidio.c index 623f8d08d13a..2d58e83420e8 100644 --- a/drivers/comedi/drivers/ni_pcidio.c +++ b/drivers/comedi/drivers/ni_pcidio.c @@ -42,8 +42,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "mite.h" diff --git a/drivers/comedi/drivers/ni_pcimio.c b/drivers/comedi/drivers/ni_pcimio.c index 6c813a490ba5..0b055321023d 100644 --- a/drivers/comedi/drivers/ni_pcimio.c +++ b/drivers/comedi/drivers/ni_pcimio.c @@ -94,9 +94,7 @@ #include #include - -#include "../comedi_pci.h" - +#include #include #include "ni_stc.h" diff --git a/drivers/comedi/drivers/ni_routes.c b/drivers/comedi/drivers/ni_routes.c index f0f8cd424b30..f24eeb464eba 100644 --- a/drivers/comedi/drivers/ni_routes.c +++ b/drivers/comedi/drivers/ni_routes.c @@ -21,8 +21,7 @@ #include #include #include - -#include "../comedi.h" +#include #include "ni_routes.h" #include "ni_routing/ni_route_values.h" diff --git a/drivers/comedi/drivers/ni_routes.h b/drivers/comedi/drivers/ni_routes.h index 036982315584..cff8a463a03f 100644 --- a/drivers/comedi/drivers/ni_routes.h +++ b/drivers/comedi/drivers/ni_routes.h @@ -27,7 +27,7 @@ #include #endif -#include "../comedi.h" +#include /** * struct ni_route_set - Set of destinations with a common source. diff --git a/drivers/comedi/drivers/ni_routing/ni_route_values.h b/drivers/comedi/drivers/ni_routing/ni_route_values.h index 6e358efa6f7f..80880083ea41 100644 --- a/drivers/comedi/drivers/ni_routing/ni_route_values.h +++ b/drivers/comedi/drivers/ni_routing/ni_route_values.h @@ -20,7 +20,7 @@ #ifndef _COMEDI_DRIVERS_NI_ROUTINT_NI_ROUTE_VALUES_H #define _COMEDI_DRIVERS_NI_ROUTINT_NI_ROUTE_VALUES_H -#include "../../comedi.h" +#include #include /* diff --git a/drivers/comedi/drivers/ni_tio.h b/drivers/comedi/drivers/ni_tio.h index e7b05718df9b..9ae2221c3c18 100644 --- a/drivers/comedi/drivers/ni_tio.h +++ b/drivers/comedi/drivers/ni_tio.h @@ -8,7 +8,7 @@ #ifndef _COMEDI_NI_TIO_H #define _COMEDI_NI_TIO_H -#include "../comedidev.h" +#include enum ni_gpct_register { NITIO_G0_AUTO_INC, diff --git a/drivers/comedi/drivers/ni_usb6501.c b/drivers/comedi/drivers/ni_usb6501.c index c42987b74b1d..0dd9edf7bced 100644 --- a/drivers/comedi/drivers/ni_usb6501.c +++ b/drivers/comedi/drivers/ni_usb6501.c @@ -87,8 +87,7 @@ #include #include #include - -#include "../comedi_usb.h" +#include #define NI6501_TIMEOUT 1000 diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index bd6f42fe9e3c..f1c383bd9d87 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -29,8 +29,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/pcl724.c b/drivers/comedi/drivers/pcl724.c index 1a5799278a7a..b3f472c93e80 100644 --- a/drivers/comedi/drivers/pcl724.c +++ b/drivers/comedi/drivers/pcl724.c @@ -25,7 +25,7 @@ */ #include -#include "../comedidev.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/pcl726.c b/drivers/comedi/drivers/pcl726.c index 88f25d7e76f7..0430630e6ebb 100644 --- a/drivers/comedi/drivers/pcl726.c +++ b/drivers/comedi/drivers/pcl726.c @@ -50,8 +50,7 @@ #include #include - -#include "../comedidev.h" +#include #define PCL726_AO_MSB_REG(x) (0x00 + ((x) * 2)) #define PCL726_AO_LSB_REG(x) (0x01 + ((x) * 2)) diff --git a/drivers/comedi/drivers/pcl730.c b/drivers/comedi/drivers/pcl730.c index 32a29129e6e8..d2733cd5383d 100644 --- a/drivers/comedi/drivers/pcl730.c +++ b/drivers/comedi/drivers/pcl730.c @@ -25,7 +25,7 @@ */ #include -#include "../comedidev.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/pcl812.c b/drivers/comedi/drivers/pcl812.c index b87ab3840eee..f00976ddfc2a 100644 --- a/drivers/comedi/drivers/pcl812.c +++ b/drivers/comedi/drivers/pcl812.c @@ -114,8 +114,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index c368a337a0ae..c5acdc8913f8 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -35,8 +35,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index f4b4a686c710..20fcd6d588f8 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -97,8 +97,7 @@ #include #include #include - -#include "../comedidev.h" +#include #include "comedi_isadma.h" #include "comedi_8254.h" diff --git a/drivers/comedi/drivers/pcm3724.c b/drivers/comedi/drivers/pcm3724.c index 0cb1ad060402..93ae6cffed44 100644 --- a/drivers/comedi/drivers/pcm3724.c +++ b/drivers/comedi/drivers/pcm3724.c @@ -24,7 +24,7 @@ */ #include -#include "../comedidev.h" +#include #include "8255.h" diff --git a/drivers/comedi/drivers/pcmad.c b/drivers/comedi/drivers/pcmad.c index eec89a0afb2f..976eda43881b 100644 --- a/drivers/comedi/drivers/pcmad.c +++ b/drivers/comedi/drivers/pcmad.c @@ -29,7 +29,7 @@ */ #include -#include "../comedidev.h" +#include #define PCMAD_STATUS 0 #define PCMAD_LSB 1 diff --git a/drivers/comedi/drivers/pcmda12.c b/drivers/comedi/drivers/pcmda12.c index 14ab1f0d1e9f..611f13bedca0 100644 --- a/drivers/comedi/drivers/pcmda12.c +++ b/drivers/comedi/drivers/pcmda12.c @@ -40,7 +40,7 @@ */ #include -#include "../comedidev.h" +#include /* AI range is not configurable, it's set by jumpers on the board */ static const struct comedi_lrange pcmda12_ranges = { diff --git a/drivers/comedi/drivers/pcmmio.c b/drivers/comedi/drivers/pcmmio.c index 24a9568d3378..c2402239d551 100644 --- a/drivers/comedi/drivers/pcmmio.c +++ b/drivers/comedi/drivers/pcmmio.c @@ -66,8 +66,7 @@ #include #include #include - -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/pcmuio.c b/drivers/comedi/drivers/pcmuio.c index b299d648a0eb..33b24dbbb919 100644 --- a/drivers/comedi/drivers/pcmuio.c +++ b/drivers/comedi/drivers/pcmuio.c @@ -65,8 +65,7 @@ #include #include - -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/quatech_daqp_cs.c b/drivers/comedi/drivers/quatech_daqp_cs.c index fe4408ebf6b3..2a76c75c513b 100644 --- a/drivers/comedi/drivers/quatech_daqp_cs.c +++ b/drivers/comedi/drivers/quatech_daqp_cs.c @@ -41,8 +41,7 @@ */ #include - -#include "../comedi_pcmcia.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/rtd520.c b/drivers/comedi/drivers/rtd520.c index 2d99a648b054..ee5bca2b1c09 100644 --- a/drivers/comedi/drivers/rtd520.c +++ b/drivers/comedi/drivers/rtd520.c @@ -85,8 +85,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "comedi_8254.h" #include "plx9080.h" diff --git a/drivers/comedi/drivers/rti800.c b/drivers/comedi/drivers/rti800.c index 327fd93b8b12..1b02e47bdb4c 100644 --- a/drivers/comedi/drivers/rti800.c +++ b/drivers/comedi/drivers/rti800.c @@ -42,7 +42,7 @@ #include #include #include -#include "../comedidev.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/rti802.c b/drivers/comedi/drivers/rti802.c index 195e2b1ac4c1..d66762a22258 100644 --- a/drivers/comedi/drivers/rti802.c +++ b/drivers/comedi/drivers/rti802.c @@ -22,7 +22,7 @@ */ #include -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/s526.c b/drivers/comedi/drivers/s526.c index 085cf5b449e5..9245c679a3c4 100644 --- a/drivers/comedi/drivers/s526.c +++ b/drivers/comedi/drivers/s526.c @@ -27,7 +27,7 @@ */ #include -#include "../comedidev.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/s626.c b/drivers/comedi/drivers/s626.c index e7aba937d896..0e5f9a9a7fd3 100644 --- a/drivers/comedi/drivers/s626.c +++ b/drivers/comedi/drivers/s626.c @@ -55,8 +55,7 @@ #include #include #include - -#include "../comedi_pci.h" +#include #include "s626.h" diff --git a/drivers/comedi/drivers/ssv_dnp.c b/drivers/comedi/drivers/ssv_dnp.c index 016d315aa584..813bd0853b0b 100644 --- a/drivers/comedi/drivers/ssv_dnp.c +++ b/drivers/comedi/drivers/ssv_dnp.c @@ -19,7 +19,7 @@ /* include files ----------------------------------------------------------- */ #include -#include "../comedidev.h" +#include /* Some global definitions: the registers of the DNP ----------------------- */ /* */ diff --git a/drivers/comedi/drivers/usbdux.c b/drivers/comedi/drivers/usbdux.c index 0350f303d557..92d514b3c1c3 100644 --- a/drivers/comedi/drivers/usbdux.c +++ b/drivers/comedi/drivers/usbdux.c @@ -73,8 +73,7 @@ #include #include #include - -#include "../comedi_usb.h" +#include /* constants for firmware upload and download */ #define USBDUX_FIRMWARE "usbdux_firmware.bin" diff --git a/drivers/comedi/drivers/usbduxfast.c b/drivers/comedi/drivers/usbduxfast.c index 4af012968cb6..39faae0ecb19 100644 --- a/drivers/comedi/drivers/usbduxfast.c +++ b/drivers/comedi/drivers/usbduxfast.c @@ -40,7 +40,7 @@ #include #include #include -#include "../comedi_usb.h" +#include /* * timeout for the USB-transfer diff --git a/drivers/comedi/drivers/usbduxsigma.c b/drivers/comedi/drivers/usbduxsigma.c index 54d7605e909f..2aaeaf44fbe5 100644 --- a/drivers/comedi/drivers/usbduxsigma.c +++ b/drivers/comedi/drivers/usbduxsigma.c @@ -40,8 +40,7 @@ #include #include #include - -#include "../comedi_usb.h" +#include /* timeout for the USB-transfer in ms*/ #define BULK_TIMEOUT 1000 diff --git a/drivers/comedi/drivers/vmk80xx.c b/drivers/comedi/drivers/vmk80xx.c index 4b00a9ea611a..46023adc5395 100644 --- a/drivers/comedi/drivers/vmk80xx.c +++ b/drivers/comedi/drivers/vmk80xx.c @@ -35,8 +35,7 @@ #include #include #include - -#include "../comedi_usb.h" +#include enum { DEVICE_VMK8055, diff --git a/drivers/comedi/kcomedilib/kcomedilib_main.c b/drivers/comedi/kcomedilib/kcomedilib_main.c index df9bba1b69ed..43fbe1a63b14 100644 --- a/drivers/comedi/kcomedilib/kcomedilib_main.c +++ b/drivers/comedi/kcomedilib/kcomedilib_main.c @@ -16,9 +16,9 @@ #include #include -#include "../comedi.h" -#include "../comedilib.h" -#include "../comedidev.h" +#include +#include +#include MODULE_AUTHOR("David Schleef "); MODULE_DESCRIPTION("Comedi kernel library"); diff --git a/drivers/comedi/proc.c b/drivers/comedi/proc.c index 8bc8e42beb90..2e4496633d3d 100644 --- a/drivers/comedi/proc.c +++ b/drivers/comedi/proc.c @@ -13,7 +13,7 @@ * was cool. */ -#include "comedidev.h" +#include #include "comedi_internal.h" #include #include diff --git a/drivers/comedi/range.c b/drivers/comedi/range.c index a4e6fe0fb729..8f43cf88d784 100644 --- a/drivers/comedi/range.c +++ b/drivers/comedi/range.c @@ -8,7 +8,7 @@ */ #include -#include "comedidev.h" +#include #include "comedi_internal.h" const struct comedi_lrange range_bipolar10 = { 1, {BIP_RANGE(10)} }; diff --git a/include/linux/comedi/comedi_pci.h b/include/linux/comedi/comedi_pci.h new file mode 100644 index 000000000000..2fb50663e3ed --- /dev/null +++ b/include/linux/comedi/comedi_pci.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * comedi_pci.h + * header file for Comedi PCI drivers + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1997-2000 David A. Schleef + */ + +#ifndef _COMEDI_PCI_H +#define _COMEDI_PCI_H + +#include +#include + +/* + * PCI Vendor IDs not in + */ +#define PCI_VENDOR_ID_KOLTER 0x1001 +#define PCI_VENDOR_ID_ICP 0x104c +#define PCI_VENDOR_ID_DT 0x1116 +#define PCI_VENDOR_ID_IOTECH 0x1616 +#define PCI_VENDOR_ID_CONTEC 0x1221 +#define PCI_VENDOR_ID_RTD 0x1435 +#define PCI_VENDOR_ID_HUMUSOFT 0x186c + +struct pci_dev *comedi_to_pci_dev(struct comedi_device *dev); + +int comedi_pci_enable(struct comedi_device *dev); +void comedi_pci_disable(struct comedi_device *dev); +void comedi_pci_detach(struct comedi_device *dev); + +int comedi_pci_auto_config(struct pci_dev *pcidev, struct comedi_driver *driver, + unsigned long context); +void comedi_pci_auto_unconfig(struct pci_dev *pcidev); + +int comedi_pci_driver_register(struct comedi_driver *comedi_driver, + struct pci_driver *pci_driver); +void comedi_pci_driver_unregister(struct comedi_driver *comedi_driver, + struct pci_driver *pci_driver); + +/** + * module_comedi_pci_driver() - Helper macro for registering a comedi PCI driver + * @__comedi_driver: comedi_driver struct + * @__pci_driver: pci_driver struct + * + * Helper macro for comedi PCI drivers which do not do anything special + * in module init/exit. This eliminates a lot of boilerplate. Each + * module may only use this macro once, and calling it replaces + * module_init() and module_exit() + */ +#define module_comedi_pci_driver(__comedi_driver, __pci_driver) \ + module_driver(__comedi_driver, comedi_pci_driver_register, \ + comedi_pci_driver_unregister, &(__pci_driver)) + +#endif /* _COMEDI_PCI_H */ diff --git a/include/linux/comedi/comedi_pcmcia.h b/include/linux/comedi/comedi_pcmcia.h new file mode 100644 index 000000000000..a33dfb65b869 --- /dev/null +++ b/include/linux/comedi/comedi_pcmcia.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * comedi_pcmcia.h + * header file for Comedi PCMCIA drivers + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1997-2000 David A. Schleef + */ + +#ifndef _COMEDI_PCMCIA_H +#define _COMEDI_PCMCIA_H + +#include +#include +#include + +struct pcmcia_device *comedi_to_pcmcia_dev(struct comedi_device *dev); + +int comedi_pcmcia_enable(struct comedi_device *dev, + int (*conf_check)(struct pcmcia_device *p_dev, + void *priv_data)); +void comedi_pcmcia_disable(struct comedi_device *dev); + +int comedi_pcmcia_auto_config(struct pcmcia_device *link, + struct comedi_driver *driver); +void comedi_pcmcia_auto_unconfig(struct pcmcia_device *link); + +int comedi_pcmcia_driver_register(struct comedi_driver *comedi_driver, + struct pcmcia_driver *pcmcia_driver); +void comedi_pcmcia_driver_unregister(struct comedi_driver *comedi_driver, + struct pcmcia_driver *pcmcia_driver); + +/** + * module_comedi_pcmcia_driver() - Helper macro for registering a comedi + * PCMCIA driver + * @__comedi_driver: comedi_driver struct + * @__pcmcia_driver: pcmcia_driver struct + * + * Helper macro for comedi PCMCIA drivers which do not do anything special + * in module init/exit. This eliminates a lot of boilerplate. Each + * module may only use this macro once, and calling it replaces + * module_init() and module_exit() + */ +#define module_comedi_pcmcia_driver(__comedi_driver, __pcmcia_driver) \ + module_driver(__comedi_driver, comedi_pcmcia_driver_register, \ + comedi_pcmcia_driver_unregister, &(__pcmcia_driver)) + +#endif /* _COMEDI_PCMCIA_H */ diff --git a/include/linux/comedi/comedi_usb.h b/include/linux/comedi/comedi_usb.h new file mode 100644 index 000000000000..5d17dd425bd2 --- /dev/null +++ b/include/linux/comedi/comedi_usb.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* comedi_usb.h + * header file for USB Comedi drivers + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1997-2000 David A. Schleef + */ + +#ifndef _COMEDI_USB_H +#define _COMEDI_USB_H + +#include +#include + +struct usb_interface *comedi_to_usb_interface(struct comedi_device *dev); +struct usb_device *comedi_to_usb_dev(struct comedi_device *dev); + +int comedi_usb_auto_config(struct usb_interface *intf, + struct comedi_driver *driver, unsigned long context); +void comedi_usb_auto_unconfig(struct usb_interface *intf); + +int comedi_usb_driver_register(struct comedi_driver *comedi_driver, + struct usb_driver *usb_driver); +void comedi_usb_driver_unregister(struct comedi_driver *comedi_driver, + struct usb_driver *usb_driver); + +/** + * module_comedi_usb_driver() - Helper macro for registering a comedi USB driver + * @__comedi_driver: comedi_driver struct + * @__usb_driver: usb_driver struct + * + * Helper macro for comedi USB drivers which do not do anything special + * in module init/exit. This eliminates a lot of boilerplate. Each + * module may only use this macro once, and calling it replaces + * module_init() and module_exit() + */ +#define module_comedi_usb_driver(__comedi_driver, __usb_driver) \ + module_driver(__comedi_driver, comedi_usb_driver_register, \ + comedi_usb_driver_unregister, &(__usb_driver)) + +#endif /* _COMEDI_USB_H */ diff --git a/include/linux/comedi/comedidev.h b/include/linux/comedi/comedidev.h new file mode 100644 index 000000000000..0a1150900ef3 --- /dev/null +++ b/include/linux/comedi/comedidev.h @@ -0,0 +1,1053 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * comedidev.h + * header file for kernel-only structures, variables, and constants + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1997-2000 David A. Schleef + */ + +#ifndef _COMEDIDEV_H +#define _COMEDIDEV_H + +#include +#include +#include +#include +#include +#include + +#define COMEDI_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#define COMEDI_VERSION_CODE COMEDI_VERSION(COMEDI_MAJORVERSION, \ + COMEDI_MINORVERSION, COMEDI_MICROVERSION) +#define COMEDI_RELEASE VERSION + +#define COMEDI_NUM_BOARD_MINORS 0x30 + +/** + * struct comedi_subdevice - Working data for a COMEDI subdevice + * @device: COMEDI device to which this subdevice belongs. (Initialized by + * comedi_alloc_subdevices().) + * @index: Index of this subdevice within device's array of subdevices. + * (Initialized by comedi_alloc_subdevices().) + * @type: Type of subdevice from &enum comedi_subdevice_type. (Initialized by + * the low-level driver.) + * @n_chan: Number of channels the subdevice supports. (Initialized by the + * low-level driver.) + * @subdev_flags: Various "SDF" flags indicating aspects of the subdevice to + * the COMEDI core and user application. (Initialized by the low-level + * driver.) + * @len_chanlist: Maximum length of a channel list if the subdevice supports + * asynchronous acquisition commands. (Optionally initialized by the + * low-level driver, or changed from 0 to 1 during post-configuration.) + * @private: Private data pointer which is either set by the low-level driver + * itself, or by a call to comedi_alloc_spriv() which allocates storage. + * In the latter case, the storage is automatically freed after the + * low-level driver's "detach" handler is called for the device. + * (Initialized by the low-level driver.) + * @async: Pointer to &struct comedi_async id the subdevice supports + * asynchronous acquisition commands. (Allocated and initialized during + * post-configuration if needed.) + * @lock: Pointer to a file object that performed a %COMEDI_LOCK ioctl on the + * subdevice. (Initially NULL.) + * @busy: Pointer to a file object that is performing an asynchronous + * acquisition command on the subdevice. (Initially NULL.) + * @runflags: Internal flags for use by COMEDI core, mostly indicating whether + * an asynchronous acquisition command is running. + * @spin_lock: Generic spin-lock for use by the COMEDI core and the low-level + * driver. (Initialized by comedi_alloc_subdevices().) + * @io_bits: Bit-mask indicating the channel directions for a DIO subdevice + * with no more than 32 channels. A '1' at a bit position indicates the + * corresponding channel is configured as an output. (Initialized by the + * low-level driver for a DIO subdevice. Forced to all-outputs during + * post-configuration for a digital output subdevice.) + * @maxdata: If non-zero, this is the maximum raw data value of each channel. + * If zero, the maximum data value is channel-specific. (Initialized by + * the low-level driver.) + * @maxdata_list: If the maximum data value is channel-specific, this points + * to an array of maximum data values indexed by channel index. + * (Initialized by the low-level driver.) + * @range_table: If non-NULL, this points to a COMEDI range table for the + * subdevice. If NULL, the range table is channel-specific. (Initialized + * by the low-level driver, will be set to an "invalid" range table during + * post-configuration if @range_table and @range_table_list are both + * NULL.) + * @range_table_list: If the COMEDI range table is channel-specific, this + * points to an array of pointers to COMEDI range tables indexed by + * channel number. (Initialized by the low-level driver.) + * @chanlist: Not used. + * @insn_read: Optional pointer to a handler for the %INSN_READ instruction. + * (Initialized by the low-level driver, or set to a default handler + * during post-configuration.) + * @insn_write: Optional pointer to a handler for the %INSN_WRITE instruction. + * (Initialized by the low-level driver, or set to a default handler + * during post-configuration.) + * @insn_bits: Optional pointer to a handler for the %INSN_BITS instruction + * for a digital input, digital output or digital input/output subdevice. + * (Initialized by the low-level driver, or set to a default handler + * during post-configuration.) + * @insn_config: Optional pointer to a handler for the %INSN_CONFIG + * instruction. (Initialized by the low-level driver, or set to a default + * handler during post-configuration.) + * @do_cmd: If the subdevice supports asynchronous acquisition commands, this + * points to a handler to set it up in hardware. (Initialized by the + * low-level driver.) + * @do_cmdtest: If the subdevice supports asynchronous acquisition commands, + * this points to a handler used to check and possibly tweak a prospective + * acquisition command without setting it up in hardware. (Initialized by + * the low-level driver.) + * @poll: If the subdevice supports asynchronous acquisition commands, this + * is an optional pointer to a handler for the %COMEDI_POLL ioctl which + * instructs the low-level driver to synchronize buffers. (Initialized by + * the low-level driver if needed.) + * @cancel: If the subdevice supports asynchronous acquisition commands, this + * points to a handler used to terminate a running command. (Initialized + * by the low-level driver.) + * @buf_change: If the subdevice supports asynchronous acquisition commands, + * this is an optional pointer to a handler that is called when the data + * buffer for handling asynchronous commands is allocated or reallocated. + * (Initialized by the low-level driver if needed.) + * @munge: If the subdevice supports asynchronous acquisition commands and + * uses DMA to transfer data from the hardware to the acquisition buffer, + * this points to a function used to "munge" the data values from the + * hardware into the format expected by COMEDI. (Initialized by the + * low-level driver if needed.) + * @async_dma_dir: If the subdevice supports asynchronous acquisition commands + * and uses DMA to transfer data from the hardware to the acquisition + * buffer, this sets the DMA direction for the buffer. (initialized to + * %DMA_NONE by comedi_alloc_subdevices() and changed by the low-level + * driver if necessary.) + * @state: Handy bit-mask indicating the output states for a DIO or digital + * output subdevice with no more than 32 channels. (Initialized by the + * low-level driver.) + * @class_dev: If the subdevice supports asynchronous acquisition commands, + * this points to a sysfs comediX_subdY device where X is the minor device + * number of the COMEDI device and Y is the subdevice number. The minor + * device number for the sysfs device is allocated dynamically in the + * range 48 to 255. This is used to allow the COMEDI device to be opened + * with a different default read or write subdevice. (Allocated during + * post-configuration if needed.) + * @minor: If @class_dev is set, this is its dynamically allocated minor + * device number. (Set during post-configuration if necessary.) + * @readback: Optional pointer to memory allocated by + * comedi_alloc_subdev_readback() used to hold the values written to + * analog output channels so they can be read back. The storage is + * automatically freed after the low-level driver's "detach" handler is + * called for the device. (Initialized by the low-level driver.) + * + * This is the main control structure for a COMEDI subdevice. If the subdevice + * supports asynchronous acquisition commands, additional information is stored + * in the &struct comedi_async pointed to by @async. + * + * Most of the subdevice is initialized by the low-level driver's "attach" or + * "auto_attach" handlers but parts of it are initialized by + * comedi_alloc_subdevices(), and other parts are initialized during + * post-configuration on return from that handler. + * + * A low-level driver that sets @insn_bits for a digital input, digital output, + * or DIO subdevice may leave @insn_read and @insn_write uninitialized, in + * which case they will be set to a default handler during post-configuration + * that uses @insn_bits to emulate the %INSN_READ and %INSN_WRITE instructions. + */ +struct comedi_subdevice { + struct comedi_device *device; + int index; + int type; + int n_chan; + int subdev_flags; + int len_chanlist; /* maximum length of channel/gain list */ + + void *private; + + struct comedi_async *async; + + void *lock; + void *busy; + unsigned int runflags; + spinlock_t spin_lock; /* generic spin-lock for COMEDI and drivers */ + + unsigned int io_bits; + + unsigned int maxdata; /* if maxdata==0, use list */ + const unsigned int *maxdata_list; /* list is channel specific */ + + const struct comedi_lrange *range_table; + const struct comedi_lrange *const *range_table_list; + + unsigned int *chanlist; /* driver-owned chanlist (not used) */ + + int (*insn_read)(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); + int (*insn_write)(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); + int (*insn_bits)(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); + int (*insn_config)(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, + unsigned int *data); + + int (*do_cmd)(struct comedi_device *dev, struct comedi_subdevice *s); + int (*do_cmdtest)(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_cmd *cmd); + int (*poll)(struct comedi_device *dev, struct comedi_subdevice *s); + int (*cancel)(struct comedi_device *dev, struct comedi_subdevice *s); + + /* called when the buffer changes */ + int (*buf_change)(struct comedi_device *dev, + struct comedi_subdevice *s); + + void (*munge)(struct comedi_device *dev, struct comedi_subdevice *s, + void *data, unsigned int num_bytes, + unsigned int start_chan_index); + enum dma_data_direction async_dma_dir; + + unsigned int state; + + struct device *class_dev; + int minor; + + unsigned int *readback; +}; + +/** + * struct comedi_buf_page - Describe a page of a COMEDI buffer + * @virt_addr: Kernel address of page. + * @dma_addr: DMA address of page if in DMA coherent memory. + */ +struct comedi_buf_page { + void *virt_addr; + dma_addr_t dma_addr; +}; + +/** + * struct comedi_buf_map - Describe pages in a COMEDI buffer + * @dma_hw_dev: Low-level hardware &struct device pointer copied from the + * COMEDI device's hw_dev member. + * @page_list: Pointer to array of &struct comedi_buf_page, one for each + * page in the buffer. + * @n_pages: Number of pages in the buffer. + * @dma_dir: DMA direction used to allocate pages of DMA coherent memory, + * or %DMA_NONE if pages allocated from regular memory. + * @refcount: &struct kref reference counter used to free the buffer. + * + * A COMEDI data buffer is allocated as individual pages, either in + * conventional memory or DMA coherent memory, depending on the attached, + * low-level hardware device. (The buffer pages also get mapped into the + * kernel's contiguous virtual address space pointed to by the 'prealloc_buf' + * member of &struct comedi_async.) + * + * The buffer is normally freed when the COMEDI device is detached from the + * low-level driver (which may happen due to device removal), but if it happens + * to be mmapped at the time, the pages cannot be freed until the buffer has + * been munmapped. That is what the reference counter is for. (The virtual + * address space pointed by 'prealloc_buf' is freed when the COMEDI device is + * detached.) + */ +struct comedi_buf_map { + struct device *dma_hw_dev; + struct comedi_buf_page *page_list; + unsigned int n_pages; + enum dma_data_direction dma_dir; + struct kref refcount; +}; + +/** + * struct comedi_async - Control data for asynchronous COMEDI commands + * @prealloc_buf: Kernel virtual address of allocated acquisition buffer. + * @prealloc_bufsz: Buffer size (in bytes). + * @buf_map: Map of buffer pages. + * @max_bufsize: Maximum allowed buffer size (in bytes). + * @buf_write_count: "Write completed" count (in bytes, modulo 2**32). + * @buf_write_alloc_count: "Allocated for writing" count (in bytes, + * modulo 2**32). + * @buf_read_count: "Read completed" count (in bytes, modulo 2**32). + * @buf_read_alloc_count: "Allocated for reading" count (in bytes, + * modulo 2**32). + * @buf_write_ptr: Buffer position for writer. + * @buf_read_ptr: Buffer position for reader. + * @cur_chan: Current position in chanlist for scan (for those drivers that + * use it). + * @scans_done: The number of scans completed. + * @scan_progress: Amount received or sent for current scan (in bytes). + * @munge_chan: Current position in chanlist for "munging". + * @munge_count: "Munge" count (in bytes, modulo 2**32). + * @munge_ptr: Buffer position for "munging". + * @events: Bit-vector of events that have occurred. + * @cmd: Details of comedi command in progress. + * @wait_head: Task wait queue for file reader or writer. + * @cb_mask: Bit-vector of events that should wake waiting tasks. + * @inttrig: Software trigger function for command, or NULL. + * + * Note about the ..._count and ..._ptr members: + * + * Think of the _Count values being integers of unlimited size, indexing + * into a buffer of infinite length (though only an advancing portion + * of the buffer of fixed length prealloc_bufsz is accessible at any + * time). Then: + * + * Buf_Read_Count <= Buf_Read_Alloc_Count <= Munge_Count <= + * Buf_Write_Count <= Buf_Write_Alloc_Count <= + * (Buf_Read_Count + prealloc_bufsz) + * + * (Those aren't the actual members, apart from prealloc_bufsz.) When the + * buffer is reset, those _Count values start at 0 and only increase in value, + * maintaining the above inequalities until the next time the buffer is + * reset. The buffer is divided into the following regions by the inequalities: + * + * [0, Buf_Read_Count): + * old region no longer accessible + * + * [Buf_Read_Count, Buf_Read_Alloc_Count): + * filled and munged region allocated for reading but not yet read + * + * [Buf_Read_Alloc_Count, Munge_Count): + * filled and munged region not yet allocated for reading + * + * [Munge_Count, Buf_Write_Count): + * filled region not yet munged + * + * [Buf_Write_Count, Buf_Write_Alloc_Count): + * unfilled region allocated for writing but not yet written + * + * [Buf_Write_Alloc_Count, Buf_Read_Count + prealloc_bufsz): + * unfilled region not yet allocated for writing + * + * [Buf_Read_Count + prealloc_bufsz, infinity): + * unfilled region not yet accessible + * + * Data needs to be written into the buffer before it can be read out, + * and may need to be converted (or "munged") between the two + * operations. Extra unfilled buffer space may need to allocated for + * writing (advancing Buf_Write_Alloc_Count) before new data is written. + * After writing new data, the newly filled space needs to be released + * (advancing Buf_Write_Count). This also results in the new data being + * "munged" (advancing Munge_Count). Before data is read out of the + * buffer, extra space may need to be allocated for reading (advancing + * Buf_Read_Alloc_Count). After the data has been read out, the space + * needs to be released (advancing Buf_Read_Count). + * + * The actual members, buf_read_count, buf_read_alloc_count, + * munge_count, buf_write_count, and buf_write_alloc_count take the + * value of the corresponding capitalized _Count values modulo 2^32 + * (UINT_MAX+1). Subtracting a "higher" _count value from a "lower" + * _count value gives the same answer as subtracting a "higher" _Count + * value from a lower _Count value because prealloc_bufsz < UINT_MAX+1. + * The modulo operation is done implicitly. + * + * The buf_read_ptr, munge_ptr, and buf_write_ptr members take the value + * of the corresponding capitalized _Count values modulo prealloc_bufsz. + * These correspond to byte indices in the physical buffer. The modulo + * operation is done by subtracting prealloc_bufsz when the value + * exceeds prealloc_bufsz (assuming prealloc_bufsz plus the increment is + * less than or equal to UINT_MAX). + */ +struct comedi_async { + void *prealloc_buf; + unsigned int prealloc_bufsz; + struct comedi_buf_map *buf_map; + unsigned int max_bufsize; + unsigned int buf_write_count; + unsigned int buf_write_alloc_count; + unsigned int buf_read_count; + unsigned int buf_read_alloc_count; + unsigned int buf_write_ptr; + unsigned int buf_read_ptr; + unsigned int cur_chan; + unsigned int scans_done; + unsigned int scan_progress; + unsigned int munge_chan; + unsigned int munge_count; + unsigned int munge_ptr; + unsigned int events; + struct comedi_cmd cmd; + wait_queue_head_t wait_head; + unsigned int cb_mask; + int (*inttrig)(struct comedi_device *dev, struct comedi_subdevice *s, + unsigned int x); +}; + +/** + * enum comedi_cb - &struct comedi_async callback "events" + * @COMEDI_CB_EOS: end-of-scan + * @COMEDI_CB_EOA: end-of-acquisition/output + * @COMEDI_CB_BLOCK: data has arrived, wakes up read() / write() + * @COMEDI_CB_EOBUF: DEPRECATED: end of buffer + * @COMEDI_CB_ERROR: card error during acquisition + * @COMEDI_CB_OVERFLOW: buffer overflow/underflow + * @COMEDI_CB_ERROR_MASK: events that indicate an error has occurred + * @COMEDI_CB_CANCEL_MASK: events that will cancel an async command + */ +enum comedi_cb { + COMEDI_CB_EOS = BIT(0), + COMEDI_CB_EOA = BIT(1), + COMEDI_CB_BLOCK = BIT(2), + COMEDI_CB_EOBUF = BIT(3), + COMEDI_CB_ERROR = BIT(4), + COMEDI_CB_OVERFLOW = BIT(5), + /* masks */ + COMEDI_CB_ERROR_MASK = (COMEDI_CB_ERROR | COMEDI_CB_OVERFLOW), + COMEDI_CB_CANCEL_MASK = (COMEDI_CB_EOA | COMEDI_CB_ERROR_MASK) +}; + +/** + * struct comedi_driver - COMEDI driver registration + * @driver_name: Name of driver. + * @module: Owning module. + * @attach: The optional "attach" handler for manually configured COMEDI + * devices. + * @detach: The "detach" handler for deconfiguring COMEDI devices. + * @auto_attach: The optional "auto_attach" handler for automatically + * configured COMEDI devices. + * @num_names: Optional number of "board names" supported. + * @board_name: Optional pointer to a pointer to a board name. The pointer + * to a board name is embedded in an element of a driver-defined array + * of static, read-only board type information. + * @offset: Optional size of each element of the driver-defined array of + * static, read-only board type information, i.e. the offset between each + * pointer to a board name. + * + * This is used with comedi_driver_register() and comedi_driver_unregister() to + * register and unregister a low-level COMEDI driver with the COMEDI core. + * + * If @num_names is non-zero, @board_name should be non-NULL, and @offset + * should be at least sizeof(*board_name). These are used by the handler for + * the %COMEDI_DEVCONFIG ioctl to match a hardware device and its driver by + * board name. If @num_names is zero, the %COMEDI_DEVCONFIG ioctl matches a + * hardware device and its driver by driver name. This is only useful if the + * @attach handler is set. If @num_names is non-zero, the driver's @attach + * handler will be called with the COMEDI device structure's board_ptr member + * pointing to the matched pointer to a board name within the driver's private + * array of static, read-only board type information. + * + * The @detach handler has two roles. If a COMEDI device was successfully + * configured by the @attach or @auto_attach handler, it is called when the + * device is being deconfigured (by the %COMEDI_DEVCONFIG ioctl, or due to + * unloading of the driver, or due to device removal). It is also called when + * the @attach or @auto_attach handler returns an error. Therefore, the + * @attach or @auto_attach handlers can defer clean-up on error until the + * @detach handler is called. If the @attach or @auto_attach handlers free + * any resources themselves, they must prevent the @detach handler from + * freeing the same resources. The @detach handler must not assume that all + * resources requested by the @attach or @auto_attach handler were + * successfully allocated. + */ +struct comedi_driver { + /* private: */ + struct comedi_driver *next; /* Next in list of COMEDI drivers. */ + /* public: */ + const char *driver_name; + struct module *module; + int (*attach)(struct comedi_device *dev, struct comedi_devconfig *it); + void (*detach)(struct comedi_device *dev); + int (*auto_attach)(struct comedi_device *dev, unsigned long context); + unsigned int num_names; + const char *const *board_name; + int offset; +}; + +/** + * struct comedi_device - Working data for a COMEDI device + * @use_count: Number of open file objects. + * @driver: Low-level COMEDI driver attached to this COMEDI device. + * @pacer: Optional pointer to a dynamically allocated acquisition pacer + * control. It is freed automatically after the COMEDI device is + * detached from the low-level driver. + * @private: Optional pointer to private data allocated by the low-level + * driver. It is freed automatically after the COMEDI device is + * detached from the low-level driver. + * @class_dev: Sysfs comediX device. + * @minor: Minor device number of COMEDI char device (0-47). + * @detach_count: Counter incremented every time the COMEDI device is detached. + * Used for checking a previous attachment is still valid. + * @hw_dev: Optional pointer to the low-level hardware &struct device. It is + * required for automatically configured COMEDI devices and optional for + * COMEDI devices configured by the %COMEDI_DEVCONFIG ioctl, although + * the bus-specific COMEDI functions only work if it is set correctly. + * It is also passed to dma_alloc_coherent() for COMEDI subdevices that + * have their 'async_dma_dir' member set to something other than + * %DMA_NONE. + * @board_name: Pointer to a COMEDI board name or a COMEDI driver name. When + * the low-level driver's "attach" handler is called by the handler for + * the %COMEDI_DEVCONFIG ioctl, it either points to a matched board name + * string if the 'num_names' member of the &struct comedi_driver is + * non-zero, otherwise it points to the low-level driver name string. + * When the low-lever driver's "auto_attach" handler is called for an + * automatically configured COMEDI device, it points to the low-level + * driver name string. The low-level driver is free to change it in its + * "attach" or "auto_attach" handler if it wishes. + * @board_ptr: Optional pointer to private, read-only board type information in + * the low-level driver. If the 'num_names' member of the &struct + * comedi_driver is non-zero, the handler for the %COMEDI_DEVCONFIG ioctl + * will point it to a pointer to a matched board name string within the + * driver's private array of static, read-only board type information when + * calling the driver's "attach" handler. The low-level driver is free to + * change it. + * @attached: Flag indicating that the COMEDI device is attached to a low-level + * driver. + * @ioenabled: Flag used to indicate that a PCI device has been enabled and + * its regions requested. + * @spinlock: Generic spin-lock for use by the low-level driver. + * @mutex: Generic mutex for use by the COMEDI core module. + * @attach_lock: &struct rw_semaphore used to guard against the COMEDI device + * being detached while an operation is in progress. The down_write() + * operation is only allowed while @mutex is held and is used when + * changing @attached and @detach_count and calling the low-level driver's + * "detach" handler. The down_read() operation is generally used without + * holding @mutex. + * @refcount: &struct kref reference counter for freeing COMEDI device. + * @n_subdevices: Number of COMEDI subdevices allocated by the low-level + * driver for this device. + * @subdevices: Dynamically allocated array of COMEDI subdevices. + * @mmio: Optional pointer to a remapped MMIO region set by the low-level + * driver. + * @iobase: Optional base of an I/O port region requested by the low-level + * driver. + * @iolen: Length of I/O port region requested at @iobase. + * @irq: Optional IRQ number requested by the low-level driver. + * @read_subdev: Optional pointer to a default COMEDI subdevice operated on by + * the read() file operation. Set by the low-level driver. + * @write_subdev: Optional pointer to a default COMEDI subdevice operated on by + * the write() file operation. Set by the low-level driver. + * @async_queue: Storage for fasync_helper(). + * @open: Optional pointer to a function set by the low-level driver to be + * called when @use_count changes from 0 to 1. + * @close: Optional pointer to a function set by the low-level driver to be + * called when @use_count changed from 1 to 0. + * @insn_device_config: Optional pointer to a handler for all sub-instructions + * except %INSN_DEVICE_CONFIG_GET_ROUTES of the %INSN_DEVICE_CONFIG + * instruction. If this is not initialized by the low-level driver, a + * default handler will be set during post-configuration. + * @get_valid_routes: Optional pointer to a handler for the + * %INSN_DEVICE_CONFIG_GET_ROUTES sub-instruction of the + * %INSN_DEVICE_CONFIG instruction set. If this is not initialized by the + * low-level driver, a default handler that copies zero routes back to the + * user will be used. + * + * This is the main control data structure for a COMEDI device (as far as the + * COMEDI core is concerned). There are two groups of COMEDI devices - + * "legacy" devices that are configured by the handler for the + * %COMEDI_DEVCONFIG ioctl, and automatically configured devices resulting + * from a call to comedi_auto_config() as a result of a bus driver probe in + * a low-level COMEDI driver. The "legacy" COMEDI devices are allocated + * during module initialization if the "comedi_num_legacy_minors" module + * parameter is non-zero and use minor device numbers from 0 to + * comedi_num_legacy_minors minus one. The automatically configured COMEDI + * devices are allocated on demand and use minor device numbers from + * comedi_num_legacy_minors to 47. + */ +struct comedi_device { + int use_count; + struct comedi_driver *driver; + struct comedi_8254 *pacer; + void *private; + + struct device *class_dev; + int minor; + unsigned int detach_count; + struct device *hw_dev; + + const char *board_name; + const void *board_ptr; + unsigned int attached:1; + unsigned int ioenabled:1; + spinlock_t spinlock; /* generic spin-lock for low-level driver */ + struct mutex mutex; /* generic mutex for COMEDI core */ + struct rw_semaphore attach_lock; + struct kref refcount; + + int n_subdevices; + struct comedi_subdevice *subdevices; + + /* dumb */ + void __iomem *mmio; + unsigned long iobase; + unsigned long iolen; + unsigned int irq; + + struct comedi_subdevice *read_subdev; + struct comedi_subdevice *write_subdev; + + struct fasync_struct *async_queue; + + int (*open)(struct comedi_device *dev); + void (*close)(struct comedi_device *dev); + int (*insn_device_config)(struct comedi_device *dev, + struct comedi_insn *insn, unsigned int *data); + unsigned int (*get_valid_routes)(struct comedi_device *dev, + unsigned int n_pairs, + unsigned int *pair_data); +}; + +/* + * function prototypes + */ + +void comedi_event(struct comedi_device *dev, struct comedi_subdevice *s); + +struct comedi_device *comedi_dev_get_from_minor(unsigned int minor); +int comedi_dev_put(struct comedi_device *dev); + +bool comedi_is_subdevice_running(struct comedi_subdevice *s); + +void *comedi_alloc_spriv(struct comedi_subdevice *s, size_t size); +void comedi_set_spriv_auto_free(struct comedi_subdevice *s); + +int comedi_check_chanlist(struct comedi_subdevice *s, + int n, + unsigned int *chanlist); + +/* range stuff */ + +#define RANGE(a, b) {(a) * 1e6, (b) * 1e6, 0} +#define RANGE_ext(a, b) {(a) * 1e6, (b) * 1e6, RF_EXTERNAL} +#define RANGE_mA(a, b) {(a) * 1e6, (b) * 1e6, UNIT_mA} +#define RANGE_unitless(a, b) {(a) * 1e6, (b) * 1e6, 0} +#define BIP_RANGE(a) {-(a) * 1e6, (a) * 1e6, 0} +#define UNI_RANGE(a) {0, (a) * 1e6, 0} + +extern const struct comedi_lrange range_bipolar10; +extern const struct comedi_lrange range_bipolar5; +extern const struct comedi_lrange range_bipolar2_5; +extern const struct comedi_lrange range_unipolar10; +extern const struct comedi_lrange range_unipolar5; +extern const struct comedi_lrange range_unipolar2_5; +extern const struct comedi_lrange range_0_20mA; +extern const struct comedi_lrange range_4_20mA; +extern const struct comedi_lrange range_0_32mA; +extern const struct comedi_lrange range_unknown; + +#define range_digital range_unipolar5 + +/** + * struct comedi_lrange - Describes a COMEDI range table + * @length: Number of entries in the range table. + * @range: Array of &struct comedi_krange, one for each range. + * + * Each element of @range[] describes the minimum and maximum physical range + * and the type of units. Typically, the type of unit is %UNIT_volt + * (i.e. volts) and the minimum and maximum are in millionths of a volt. + * There may also be a flag that indicates the minimum and maximum are merely + * scale factors for an unknown, external reference. + */ +struct comedi_lrange { + int length; + struct comedi_krange range[]; +}; + +/** + * comedi_range_is_bipolar() - Test if subdevice range is bipolar + * @s: COMEDI subdevice. + * @range: Index of range within a range table. + * + * Tests whether a range is bipolar by checking whether its minimum value + * is negative. + * + * Assumes @range is valid. Does not work for subdevices using a + * channel-specific range table list. + * + * Return: + * %true if the range is bipolar. + * %false if the range is unipolar. + */ +static inline bool comedi_range_is_bipolar(struct comedi_subdevice *s, + unsigned int range) +{ + return s->range_table->range[range].min < 0; +} + +/** + * comedi_range_is_unipolar() - Test if subdevice range is unipolar + * @s: COMEDI subdevice. + * @range: Index of range within a range table. + * + * Tests whether a range is unipolar by checking whether its minimum value + * is at least 0. + * + * Assumes @range is valid. Does not work for subdevices using a + * channel-specific range table list. + * + * Return: + * %true if the range is unipolar. + * %false if the range is bipolar. + */ +static inline bool comedi_range_is_unipolar(struct comedi_subdevice *s, + unsigned int range) +{ + return s->range_table->range[range].min >= 0; +} + +/** + * comedi_range_is_external() - Test if subdevice range is external + * @s: COMEDI subdevice. + * @range: Index of range within a range table. + * + * Tests whether a range is externally reference by checking whether its + * %RF_EXTERNAL flag is set. + * + * Assumes @range is valid. Does not work for subdevices using a + * channel-specific range table list. + * + * Return: + * %true if the range is external. + * %false if the range is internal. + */ +static inline bool comedi_range_is_external(struct comedi_subdevice *s, + unsigned int range) +{ + return !!(s->range_table->range[range].flags & RF_EXTERNAL); +} + +/** + * comedi_chan_range_is_bipolar() - Test if channel-specific range is bipolar + * @s: COMEDI subdevice. + * @chan: The channel number. + * @range: Index of range within a range table. + * + * Tests whether a range is bipolar by checking whether its minimum value + * is negative. + * + * Assumes @chan and @range are valid. Only works for subdevices with a + * channel-specific range table list. + * + * Return: + * %true if the range is bipolar. + * %false if the range is unipolar. + */ +static inline bool comedi_chan_range_is_bipolar(struct comedi_subdevice *s, + unsigned int chan, + unsigned int range) +{ + return s->range_table_list[chan]->range[range].min < 0; +} + +/** + * comedi_chan_range_is_unipolar() - Test if channel-specific range is unipolar + * @s: COMEDI subdevice. + * @chan: The channel number. + * @range: Index of range within a range table. + * + * Tests whether a range is unipolar by checking whether its minimum value + * is at least 0. + * + * Assumes @chan and @range are valid. Only works for subdevices with a + * channel-specific range table list. + * + * Return: + * %true if the range is unipolar. + * %false if the range is bipolar. + */ +static inline bool comedi_chan_range_is_unipolar(struct comedi_subdevice *s, + unsigned int chan, + unsigned int range) +{ + return s->range_table_list[chan]->range[range].min >= 0; +} + +/** + * comedi_chan_range_is_external() - Test if channel-specific range is external + * @s: COMEDI subdevice. + * @chan: The channel number. + * @range: Index of range within a range table. + * + * Tests whether a range is externally reference by checking whether its + * %RF_EXTERNAL flag is set. + * + * Assumes @chan and @range are valid. Only works for subdevices with a + * channel-specific range table list. + * + * Return: + * %true if the range is bipolar. + * %false if the range is unipolar. + */ +static inline bool comedi_chan_range_is_external(struct comedi_subdevice *s, + unsigned int chan, + unsigned int range) +{ + return !!(s->range_table_list[chan]->range[range].flags & RF_EXTERNAL); +} + +/** + * comedi_offset_munge() - Convert between offset binary and 2's complement + * @s: COMEDI subdevice. + * @val: Value to be converted. + * + * Toggles the highest bit of a sample value to toggle between offset binary + * and 2's complement. Assumes that @s->maxdata is a power of 2 minus 1. + * + * Return: The converted value. + */ +static inline unsigned int comedi_offset_munge(struct comedi_subdevice *s, + unsigned int val) +{ + return val ^ s->maxdata ^ (s->maxdata >> 1); +} + +/** + * comedi_bytes_per_sample() - Determine subdevice sample size + * @s: COMEDI subdevice. + * + * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on + * whether the %SDF_LSAMPL subdevice flag is set or not. + * + * Return: The subdevice sample size. + */ +static inline unsigned int comedi_bytes_per_sample(struct comedi_subdevice *s) +{ + return s->subdev_flags & SDF_LSAMPL ? sizeof(int) : sizeof(short); +} + +/** + * comedi_sample_shift() - Determine log2 of subdevice sample size + * @s: COMEDI subdevice. + * + * The sample size will be 4 (sizeof int) or 2 (sizeof short) depending on + * whether the %SDF_LSAMPL subdevice flag is set or not. The log2 of the + * sample size will be 2 or 1 and can be used as the right operand of a + * bit-shift operator to multiply or divide something by the sample size. + * + * Return: log2 of the subdevice sample size. + */ +static inline unsigned int comedi_sample_shift(struct comedi_subdevice *s) +{ + return s->subdev_flags & SDF_LSAMPL ? 2 : 1; +} + +/** + * comedi_bytes_to_samples() - Convert a number of bytes to a number of samples + * @s: COMEDI subdevice. + * @nbytes: Number of bytes + * + * Return: The number of bytes divided by the subdevice sample size. + */ +static inline unsigned int comedi_bytes_to_samples(struct comedi_subdevice *s, + unsigned int nbytes) +{ + return nbytes >> comedi_sample_shift(s); +} + +/** + * comedi_samples_to_bytes() - Convert a number of samples to a number of bytes + * @s: COMEDI subdevice. + * @nsamples: Number of samples. + * + * Return: The number of samples multiplied by the subdevice sample size. + * (Does not check for arithmetic overflow.) + */ +static inline unsigned int comedi_samples_to_bytes(struct comedi_subdevice *s, + unsigned int nsamples) +{ + return nsamples << comedi_sample_shift(s); +} + +/** + * comedi_check_trigger_src() - Trivially validate a comedi_cmd trigger source + * @src: Pointer to the trigger source to validate. + * @flags: Bitmask of valid %TRIG_* for the trigger. + * + * This is used in "step 1" of the do_cmdtest functions of comedi drivers + * to validate the comedi_cmd triggers. The mask of the @src against the + * @flags allows the userspace comedilib to pass all the comedi_cmd + * triggers as %TRIG_ANY and get back a bitmask of the valid trigger sources. + * + * Return: + * 0 if trigger sources in *@src are all supported. + * -EINVAL if any trigger source in *@src is unsupported. + */ +static inline int comedi_check_trigger_src(unsigned int *src, + unsigned int flags) +{ + unsigned int orig_src = *src; + + *src = orig_src & flags; + if (*src == TRIG_INVALID || *src != orig_src) + return -EINVAL; + return 0; +} + +/** + * comedi_check_trigger_is_unique() - Make sure a trigger source is unique + * @src: The trigger source to check. + * + * Return: + * 0 if no more than one trigger source is set. + * -EINVAL if more than one trigger source is set. + */ +static inline int comedi_check_trigger_is_unique(unsigned int src) +{ + /* this test is true if more than one _src bit is set */ + if ((src & (src - 1)) != 0) + return -EINVAL; + return 0; +} + +/** + * comedi_check_trigger_arg_is() - Trivially validate a trigger argument + * @arg: Pointer to the trigger arg to validate. + * @val: The value the argument should be. + * + * Forces *@arg to be @val. + * + * Return: + * 0 if *@arg was already @val. + * -EINVAL if *@arg differed from @val. + */ +static inline int comedi_check_trigger_arg_is(unsigned int *arg, + unsigned int val) +{ + if (*arg != val) { + *arg = val; + return -EINVAL; + } + return 0; +} + +/** + * comedi_check_trigger_arg_min() - Trivially validate a trigger argument min + * @arg: Pointer to the trigger arg to validate. + * @val: The minimum value the argument should be. + * + * Forces *@arg to be at least @val, setting it to @val if necessary. + * + * Return: + * 0 if *@arg was already at least @val. + * -EINVAL if *@arg was less than @val. + */ +static inline int comedi_check_trigger_arg_min(unsigned int *arg, + unsigned int val) +{ + if (*arg < val) { + *arg = val; + return -EINVAL; + } + return 0; +} + +/** + * comedi_check_trigger_arg_max() - Trivially validate a trigger argument max + * @arg: Pointer to the trigger arg to validate. + * @val: The maximum value the argument should be. + * + * Forces *@arg to be no more than @val, setting it to @val if necessary. + * + * Return: + * 0 if*@arg was already no more than @val. + * -EINVAL if *@arg was greater than @val. + */ +static inline int comedi_check_trigger_arg_max(unsigned int *arg, + unsigned int val) +{ + if (*arg > val) { + *arg = val; + return -EINVAL; + } + return 0; +} + +/* + * Must set dev->hw_dev if you wish to dma directly into comedi's buffer. + * Also useful for retrieving a previously configured hardware device of + * known bus type. Set automatically for auto-configured devices. + * Automatically set to NULL when detaching hardware device. + */ +int comedi_set_hw_dev(struct comedi_device *dev, struct device *hw_dev); + +/** + * comedi_buf_n_bytes_ready - Determine amount of unread data in buffer + * @s: COMEDI subdevice. + * + * Determines the number of bytes of unread data in the asynchronous + * acquisition data buffer for a subdevice. The data in question might not + * have been fully "munged" yet. + * + * Returns: The amount of unread data in bytes. + */ +static inline unsigned int comedi_buf_n_bytes_ready(struct comedi_subdevice *s) +{ + return s->async->buf_write_count - s->async->buf_read_count; +} + +unsigned int comedi_buf_write_alloc(struct comedi_subdevice *s, unsigned int n); +unsigned int comedi_buf_write_free(struct comedi_subdevice *s, unsigned int n); + +unsigned int comedi_buf_read_n_available(struct comedi_subdevice *s); +unsigned int comedi_buf_read_alloc(struct comedi_subdevice *s, unsigned int n); +unsigned int comedi_buf_read_free(struct comedi_subdevice *s, unsigned int n); + +unsigned int comedi_buf_write_samples(struct comedi_subdevice *s, + const void *data, unsigned int nsamples); +unsigned int comedi_buf_read_samples(struct comedi_subdevice *s, + void *data, unsigned int nsamples); + +/* drivers.c - general comedi driver functions */ + +#define COMEDI_TIMEOUT_MS 1000 + +int comedi_timeout(struct comedi_device *dev, struct comedi_subdevice *s, + struct comedi_insn *insn, + int (*cb)(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned long context), + unsigned long context); + +unsigned int comedi_handle_events(struct comedi_device *dev, + struct comedi_subdevice *s); + +int comedi_dio_insn_config(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data, + unsigned int mask); +unsigned int comedi_dio_update_state(struct comedi_subdevice *s, + unsigned int *data); +unsigned int comedi_bytes_per_scan_cmd(struct comedi_subdevice *s, + struct comedi_cmd *cmd); +unsigned int comedi_bytes_per_scan(struct comedi_subdevice *s); +unsigned int comedi_nscans_left(struct comedi_subdevice *s, + unsigned int nscans); +unsigned int comedi_nsamples_left(struct comedi_subdevice *s, + unsigned int nsamples); +void comedi_inc_scan_progress(struct comedi_subdevice *s, + unsigned int num_bytes); + +void *comedi_alloc_devpriv(struct comedi_device *dev, size_t size); +int comedi_alloc_subdevices(struct comedi_device *dev, int num_subdevices); +int comedi_alloc_subdev_readback(struct comedi_subdevice *s); + +int comedi_readback_insn_read(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); + +int comedi_load_firmware(struct comedi_device *dev, struct device *hw_dev, + const char *name, + int (*cb)(struct comedi_device *dev, + const u8 *data, size_t size, + unsigned long context), + unsigned long context); + +int __comedi_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len); +int comedi_request_region(struct comedi_device *dev, + unsigned long start, unsigned long len); +void comedi_legacy_detach(struct comedi_device *dev); + +int comedi_auto_config(struct device *hardware_device, + struct comedi_driver *driver, unsigned long context); +void comedi_auto_unconfig(struct device *hardware_device); + +int comedi_driver_register(struct comedi_driver *driver); +void comedi_driver_unregister(struct comedi_driver *driver); + +/** + * module_comedi_driver() - Helper macro for registering a comedi driver + * @__comedi_driver: comedi_driver struct + * + * Helper macro for comedi drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only use + * this macro once, and calling it replaces module_init() and module_exit(). + */ +#define module_comedi_driver(__comedi_driver) \ + module_driver(__comedi_driver, comedi_driver_register, \ + comedi_driver_unregister) + +#endif /* _COMEDIDEV_H */ diff --git a/include/linux/comedi/comedilib.h b/include/linux/comedi/comedilib.h new file mode 100644 index 000000000000..0223c9cd9215 --- /dev/null +++ b/include/linux/comedi/comedilib.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * comedilib.h + * Header file for kcomedilib + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1998-2001 David A. Schleef + */ + +#ifndef _LINUX_COMEDILIB_H +#define _LINUX_COMEDILIB_H + +struct comedi_device *comedi_open(const char *path); +int comedi_close(struct comedi_device *dev); +int comedi_dio_get_config(struct comedi_device *dev, unsigned int subdev, + unsigned int chan, unsigned int *io); +int comedi_dio_config(struct comedi_device *dev, unsigned int subdev, + unsigned int chan, unsigned int io); +int comedi_dio_bitfield2(struct comedi_device *dev, unsigned int subdev, + unsigned int mask, unsigned int *bits, + unsigned int base_channel); +int comedi_find_subdevice_by_type(struct comedi_device *dev, int type, + unsigned int subd); +int comedi_get_n_channels(struct comedi_device *dev, unsigned int subdevice); + +#endif diff --git a/include/uapi/linux/comedi.h b/include/uapi/linux/comedi.h new file mode 100644 index 000000000000..7314e5ee0a1e --- /dev/null +++ b/include/uapi/linux/comedi.h @@ -0,0 +1,1528 @@ +/* SPDX-License-Identifier: LGPL-2.0+ WITH Linux-syscall-note */ +/* + * comedi.h + * header file for COMEDI user API + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1998-2001 David A. Schleef + */ + +#ifndef _COMEDI_H +#define _COMEDI_H + +#define COMEDI_MAJORVERSION 0 +#define COMEDI_MINORVERSION 7 +#define COMEDI_MICROVERSION 76 +#define VERSION "0.7.76" + +/* comedi's major device number */ +#define COMEDI_MAJOR 98 + +/* + * maximum number of minor devices. This can be increased, although + * kernel structures are currently statically allocated, thus you + * don't want this to be much more than you actually use. + */ +#define COMEDI_NDEVICES 16 + +/* number of config options in the config structure */ +#define COMEDI_NDEVCONFOPTS 32 + +/* + * NOTE: 'comedi_config --init-data' is deprecated + * + * The following indexes in the config options were used by + * comedi_config to pass firmware blobs from user space to the + * comedi drivers. The request_firmware() hotplug interface is + * now used by all comedi drivers instead. + */ + +/* length of nth chunk of firmware data -*/ +#define COMEDI_DEVCONF_AUX_DATA3_LENGTH 25 +#define COMEDI_DEVCONF_AUX_DATA2_LENGTH 26 +#define COMEDI_DEVCONF_AUX_DATA1_LENGTH 27 +#define COMEDI_DEVCONF_AUX_DATA0_LENGTH 28 +/* most significant 32 bits of pointer address (if needed) */ +#define COMEDI_DEVCONF_AUX_DATA_HI 29 +/* least significant 32 bits of pointer address */ +#define COMEDI_DEVCONF_AUX_DATA_LO 30 +#define COMEDI_DEVCONF_AUX_DATA_LENGTH 31 /* total data length */ + +/* max length of device and driver names */ +#define COMEDI_NAMELEN 20 + +/* packs and unpacks a channel/range number */ + +#define CR_PACK(chan, rng, aref) \ + ((((aref) & 0x3) << 24) | (((rng) & 0xff) << 16) | (chan)) +#define CR_PACK_FLAGS(chan, range, aref, flags) \ + (CR_PACK(chan, range, aref) | ((flags) & CR_FLAGS_MASK)) + +#define CR_CHAN(a) ((a) & 0xffff) +#define CR_RANGE(a) (((a) >> 16) & 0xff) +#define CR_AREF(a) (((a) >> 24) & 0x03) + +#define CR_FLAGS_MASK 0xfc000000 +#define CR_ALT_FILTER 0x04000000 +#define CR_DITHER CR_ALT_FILTER +#define CR_DEGLITCH CR_ALT_FILTER +#define CR_ALT_SOURCE 0x08000000 +#define CR_EDGE 0x40000000 +#define CR_INVERT 0x80000000 + +#define AREF_GROUND 0x00 /* analog ref = analog ground */ +#define AREF_COMMON 0x01 /* analog ref = analog common */ +#define AREF_DIFF 0x02 /* analog ref = differential */ +#define AREF_OTHER 0x03 /* analog ref = other (undefined) */ + +/* counters -- these are arbitrary values */ +#define GPCT_RESET 0x0001 +#define GPCT_SET_SOURCE 0x0002 +#define GPCT_SET_GATE 0x0004 +#define GPCT_SET_DIRECTION 0x0008 +#define GPCT_SET_OPERATION 0x0010 +#define GPCT_ARM 0x0020 +#define GPCT_DISARM 0x0040 +#define GPCT_GET_INT_CLK_FRQ 0x0080 + +#define GPCT_INT_CLOCK 0x0001 +#define GPCT_EXT_PIN 0x0002 +#define GPCT_NO_GATE 0x0004 +#define GPCT_UP 0x0008 +#define GPCT_DOWN 0x0010 +#define GPCT_HWUD 0x0020 +#define GPCT_SIMPLE_EVENT 0x0040 +#define GPCT_SINGLE_PERIOD 0x0080 +#define GPCT_SINGLE_PW 0x0100 +#define GPCT_CONT_PULSE_OUT 0x0200 +#define GPCT_SINGLE_PULSE_OUT 0x0400 + +/* instructions */ + +#define INSN_MASK_WRITE 0x8000000 +#define INSN_MASK_READ 0x4000000 +#define INSN_MASK_SPECIAL 0x2000000 + +#define INSN_READ (0 | INSN_MASK_READ) +#define INSN_WRITE (1 | INSN_MASK_WRITE) +#define INSN_BITS (2 | INSN_MASK_READ | INSN_MASK_WRITE) +#define INSN_CONFIG (3 | INSN_MASK_READ | INSN_MASK_WRITE) +#define INSN_DEVICE_CONFIG (INSN_CONFIG | INSN_MASK_SPECIAL) +#define INSN_GTOD (4 | INSN_MASK_READ | INSN_MASK_SPECIAL) +#define INSN_WAIT (5 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) +#define INSN_INTTRIG (6 | INSN_MASK_WRITE | INSN_MASK_SPECIAL) + +/* command flags */ +/* These flags are used in comedi_cmd structures */ + +#define CMDF_BOGUS 0x00000001 /* do the motions */ + +/* try to use a real-time interrupt while performing command */ +#define CMDF_PRIORITY 0x00000008 + +/* wake up on end-of-scan events */ +#define CMDF_WAKE_EOS 0x00000020 + +#define CMDF_WRITE 0x00000040 + +#define CMDF_RAWDATA 0x00000080 + +/* timer rounding definitions */ +#define CMDF_ROUND_MASK 0x00030000 +#define CMDF_ROUND_NEAREST 0x00000000 +#define CMDF_ROUND_DOWN 0x00010000 +#define CMDF_ROUND_UP 0x00020000 +#define CMDF_ROUND_UP_NEXT 0x00030000 + +#define COMEDI_EV_START 0x00040000 +#define COMEDI_EV_SCAN_BEGIN 0x00080000 +#define COMEDI_EV_CONVERT 0x00100000 +#define COMEDI_EV_SCAN_END 0x00200000 +#define COMEDI_EV_STOP 0x00400000 + +/* compatibility definitions */ +#define TRIG_BOGUS CMDF_BOGUS +#define TRIG_RT CMDF_PRIORITY +#define TRIG_WAKE_EOS CMDF_WAKE_EOS +#define TRIG_WRITE CMDF_WRITE +#define TRIG_ROUND_MASK CMDF_ROUND_MASK +#define TRIG_ROUND_NEAREST CMDF_ROUND_NEAREST +#define TRIG_ROUND_DOWN CMDF_ROUND_DOWN +#define TRIG_ROUND_UP CMDF_ROUND_UP +#define TRIG_ROUND_UP_NEXT CMDF_ROUND_UP_NEXT + +/* trigger sources */ + +#define TRIG_ANY 0xffffffff +#define TRIG_INVALID 0x00000000 + +#define TRIG_NONE 0x00000001 /* never trigger */ +#define TRIG_NOW 0x00000002 /* trigger now + N ns */ +#define TRIG_FOLLOW 0x00000004 /* trigger on next lower level trig */ +#define TRIG_TIME 0x00000008 /* trigger at time N ns */ +#define TRIG_TIMER 0x00000010 /* trigger at rate N ns */ +#define TRIG_COUNT 0x00000020 /* trigger when count reaches N */ +#define TRIG_EXT 0x00000040 /* trigger on external signal N */ +#define TRIG_INT 0x00000080 /* trigger on comedi-internal signal N */ +#define TRIG_OTHER 0x00000100 /* driver defined */ + +/* subdevice flags */ + +#define SDF_BUSY 0x0001 /* device is busy */ +#define SDF_BUSY_OWNER 0x0002 /* device is busy with your job */ +#define SDF_LOCKED 0x0004 /* subdevice is locked */ +#define SDF_LOCK_OWNER 0x0008 /* you own lock */ +#define SDF_MAXDATA 0x0010 /* maxdata depends on channel */ +#define SDF_FLAGS 0x0020 /* flags depend on channel */ +#define SDF_RANGETYPE 0x0040 /* range type depends on channel */ +#define SDF_PWM_COUNTER 0x0080 /* PWM can automatically switch off */ +#define SDF_PWM_HBRIDGE 0x0100 /* PWM is signed (H-bridge) */ +#define SDF_CMD 0x1000 /* can do commands (deprecated) */ +#define SDF_SOFT_CALIBRATED 0x2000 /* subdevice uses software calibration */ +#define SDF_CMD_WRITE 0x4000 /* can do output commands */ +#define SDF_CMD_READ 0x8000 /* can do input commands */ + +/* subdevice can be read (e.g. analog input) */ +#define SDF_READABLE 0x00010000 +/* subdevice can be written (e.g. analog output) */ +#define SDF_WRITABLE 0x00020000 +#define SDF_WRITEABLE SDF_WRITABLE /* spelling error in API */ +/* subdevice does not have externally visible lines */ +#define SDF_INTERNAL 0x00040000 +#define SDF_GROUND 0x00100000 /* can do aref=ground */ +#define SDF_COMMON 0x00200000 /* can do aref=common */ +#define SDF_DIFF 0x00400000 /* can do aref=diff */ +#define SDF_OTHER 0x00800000 /* can do aref=other */ +#define SDF_DITHER 0x01000000 /* can do dithering */ +#define SDF_DEGLITCH 0x02000000 /* can do deglitching */ +#define SDF_MMAP 0x04000000 /* can do mmap() */ +#define SDF_RUNNING 0x08000000 /* subdevice is acquiring data */ +#define SDF_LSAMPL 0x10000000 /* subdevice uses 32-bit samples */ +#define SDF_PACKED 0x20000000 /* subdevice can do packed DIO */ + +/* subdevice types */ + +/** + * enum comedi_subdevice_type - COMEDI subdevice types + * @COMEDI_SUBD_UNUSED: Unused subdevice. + * @COMEDI_SUBD_AI: Analog input. + * @COMEDI_SUBD_AO: Analog output. + * @COMEDI_SUBD_DI: Digital input. + * @COMEDI_SUBD_DO: Digital output. + * @COMEDI_SUBD_DIO: Digital input/output. + * @COMEDI_SUBD_COUNTER: Counter. + * @COMEDI_SUBD_TIMER: Timer. + * @COMEDI_SUBD_MEMORY: Memory, EEPROM, DPRAM. + * @COMEDI_SUBD_CALIB: Calibration DACs. + * @COMEDI_SUBD_PROC: Processor, DSP. + * @COMEDI_SUBD_SERIAL: Serial I/O. + * @COMEDI_SUBD_PWM: Pulse-Width Modulation output. + */ +enum comedi_subdevice_type { + COMEDI_SUBD_UNUSED, + COMEDI_SUBD_AI, + COMEDI_SUBD_AO, + COMEDI_SUBD_DI, + COMEDI_SUBD_DO, + COMEDI_SUBD_DIO, + COMEDI_SUBD_COUNTER, + COMEDI_SUBD_TIMER, + COMEDI_SUBD_MEMORY, + COMEDI_SUBD_CALIB, + COMEDI_SUBD_PROC, + COMEDI_SUBD_SERIAL, + COMEDI_SUBD_PWM +}; + +/* configuration instructions */ + +/** + * enum comedi_io_direction - COMEDI I/O directions + * @COMEDI_INPUT: Input. + * @COMEDI_OUTPUT: Output. + * @COMEDI_OPENDRAIN: Open-drain (or open-collector) output. + * + * These are used by the %INSN_CONFIG_DIO_QUERY configuration instruction to + * report a direction. They may also be used in other places where a direction + * needs to be specified. + */ +enum comedi_io_direction { + COMEDI_INPUT = 0, + COMEDI_OUTPUT = 1, + COMEDI_OPENDRAIN = 2 +}; + +/** + * enum configuration_ids - COMEDI configuration instruction codes + * @INSN_CONFIG_DIO_INPUT: Configure digital I/O as input. + * @INSN_CONFIG_DIO_OUTPUT: Configure digital I/O as output. + * @INSN_CONFIG_DIO_OPENDRAIN: Configure digital I/O as open-drain (or open + * collector) output. + * @INSN_CONFIG_ANALOG_TRIG: Configure analog trigger. + * @INSN_CONFIG_ALT_SOURCE: Configure alternate input source. + * @INSN_CONFIG_DIGITAL_TRIG: Configure digital trigger. + * @INSN_CONFIG_BLOCK_SIZE: Configure block size for DMA transfers. + * @INSN_CONFIG_TIMER_1: Configure divisor for external clock. + * @INSN_CONFIG_FILTER: Configure a filter. + * @INSN_CONFIG_CHANGE_NOTIFY: Configure change notification for digital + * inputs. (New drivers should use + * %INSN_CONFIG_DIGITAL_TRIG instead.) + * @INSN_CONFIG_SERIAL_CLOCK: Configure clock for serial I/O. + * @INSN_CONFIG_BIDIRECTIONAL_DATA: Send and receive byte over serial I/O. + * @INSN_CONFIG_DIO_QUERY: Query direction of digital I/O channel. + * @INSN_CONFIG_PWM_OUTPUT: Configure pulse-width modulator output. + * @INSN_CONFIG_GET_PWM_OUTPUT: Get pulse-width modulator output configuration. + * @INSN_CONFIG_ARM: Arm a subdevice or channel. + * @INSN_CONFIG_DISARM: Disarm a subdevice or channel. + * @INSN_CONFIG_GET_COUNTER_STATUS: Get counter status. + * @INSN_CONFIG_RESET: Reset a subdevice or channel. + * @INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR: Configure counter/timer as + * single pulse generator. + * @INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR: Configure counter/timer as + * pulse train generator. + * @INSN_CONFIG_GPCT_QUADRATURE_ENCODER: Configure counter as a quadrature + * encoder. + * @INSN_CONFIG_SET_GATE_SRC: Set counter/timer gate source. + * @INSN_CONFIG_GET_GATE_SRC: Get counter/timer gate source. + * @INSN_CONFIG_SET_CLOCK_SRC: Set counter/timer master clock source. + * @INSN_CONFIG_GET_CLOCK_SRC: Get counter/timer master clock source. + * @INSN_CONFIG_SET_OTHER_SRC: Set counter/timer "other" source. + * @INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE: Get size (in bytes) of subdevice's + * on-board FIFOs used during streaming + * input/output. + * @INSN_CONFIG_SET_COUNTER_MODE: Set counter/timer mode. + * @INSN_CONFIG_8254_SET_MODE: (Deprecated) Same as + * %INSN_CONFIG_SET_COUNTER_MODE. + * @INSN_CONFIG_8254_READ_STATUS: Read status of 8254 counter channel. + * @INSN_CONFIG_SET_ROUTING: Set routing for a channel. + * @INSN_CONFIG_GET_ROUTING: Get routing for a channel. + * @INSN_CONFIG_PWM_SET_PERIOD: Set PWM period in nanoseconds. + * @INSN_CONFIG_PWM_GET_PERIOD: Get PWM period in nanoseconds. + * @INSN_CONFIG_GET_PWM_STATUS: Get PWM status. + * @INSN_CONFIG_PWM_SET_H_BRIDGE: Set PWM H bridge duty cycle and polarity for + * a relay simultaneously. + * @INSN_CONFIG_PWM_GET_H_BRIDGE: Get PWM H bridge duty cycle and polarity. + * @INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS: Get the hardware timing restraints, + * regardless of trigger sources. + */ +enum configuration_ids { + INSN_CONFIG_DIO_INPUT = COMEDI_INPUT, + INSN_CONFIG_DIO_OUTPUT = COMEDI_OUTPUT, + INSN_CONFIG_DIO_OPENDRAIN = COMEDI_OPENDRAIN, + INSN_CONFIG_ANALOG_TRIG = 16, +/* INSN_CONFIG_WAVEFORM = 17, */ +/* INSN_CONFIG_TRIG = 18, */ +/* INSN_CONFIG_COUNTER = 19, */ + INSN_CONFIG_ALT_SOURCE = 20, + INSN_CONFIG_DIGITAL_TRIG = 21, + INSN_CONFIG_BLOCK_SIZE = 22, + INSN_CONFIG_TIMER_1 = 23, + INSN_CONFIG_FILTER = 24, + INSN_CONFIG_CHANGE_NOTIFY = 25, + + INSN_CONFIG_SERIAL_CLOCK = 26, /*ALPHA*/ + INSN_CONFIG_BIDIRECTIONAL_DATA = 27, + INSN_CONFIG_DIO_QUERY = 28, + INSN_CONFIG_PWM_OUTPUT = 29, + INSN_CONFIG_GET_PWM_OUTPUT = 30, + INSN_CONFIG_ARM = 31, + INSN_CONFIG_DISARM = 32, + INSN_CONFIG_GET_COUNTER_STATUS = 33, + INSN_CONFIG_RESET = 34, + INSN_CONFIG_GPCT_SINGLE_PULSE_GENERATOR = 1001, + INSN_CONFIG_GPCT_PULSE_TRAIN_GENERATOR = 1002, + INSN_CONFIG_GPCT_QUADRATURE_ENCODER = 1003, + INSN_CONFIG_SET_GATE_SRC = 2001, + INSN_CONFIG_GET_GATE_SRC = 2002, + INSN_CONFIG_SET_CLOCK_SRC = 2003, + INSN_CONFIG_GET_CLOCK_SRC = 2004, + INSN_CONFIG_SET_OTHER_SRC = 2005, + INSN_CONFIG_GET_HARDWARE_BUFFER_SIZE = 2006, + INSN_CONFIG_SET_COUNTER_MODE = 4097, + INSN_CONFIG_8254_SET_MODE = INSN_CONFIG_SET_COUNTER_MODE, + INSN_CONFIG_8254_READ_STATUS = 4098, + INSN_CONFIG_SET_ROUTING = 4099, + INSN_CONFIG_GET_ROUTING = 4109, + INSN_CONFIG_PWM_SET_PERIOD = 5000, + INSN_CONFIG_PWM_GET_PERIOD = 5001, + INSN_CONFIG_GET_PWM_STATUS = 5002, + INSN_CONFIG_PWM_SET_H_BRIDGE = 5003, + INSN_CONFIG_PWM_GET_H_BRIDGE = 5004, + INSN_CONFIG_GET_CMD_TIMING_CONSTRAINTS = 5005, +}; + +/** + * enum device_configuration_ids - COMEDI configuration instruction codes global + * to an entire device. + * @INSN_DEVICE_CONFIG_TEST_ROUTE: Validate the possibility of a + * globally-named route + * @INSN_DEVICE_CONFIG_CONNECT_ROUTE: Connect a globally-named route + * @INSN_DEVICE_CONFIG_DISCONNECT_ROUTE:Disconnect a globally-named route + * @INSN_DEVICE_CONFIG_GET_ROUTES: Get a list of all globally-named routes + * that are valid for a particular device. + */ +enum device_config_route_ids { + INSN_DEVICE_CONFIG_TEST_ROUTE = 0, + INSN_DEVICE_CONFIG_CONNECT_ROUTE = 1, + INSN_DEVICE_CONFIG_DISCONNECT_ROUTE = 2, + INSN_DEVICE_CONFIG_GET_ROUTES = 3, +}; + +/** + * enum comedi_digital_trig_op - operations for configuring a digital trigger + * @COMEDI_DIGITAL_TRIG_DISABLE: Return digital trigger to its default, + * inactive, unconfigured state. + * @COMEDI_DIGITAL_TRIG_ENABLE_EDGES: Set rising and/or falling edge inputs + * that each can fire the trigger. + * @COMEDI_DIGITAL_TRIG_ENABLE_LEVELS: Set a combination of high and/or low + * level inputs that can fire the trigger. + * + * These are used with the %INSN_CONFIG_DIGITAL_TRIG configuration instruction. + * The data for the configuration instruction is as follows... + * + * data[%0] = %INSN_CONFIG_DIGITAL_TRIG + * + * data[%1] = trigger ID + * + * data[%2] = configuration operation + * + * data[%3] = configuration parameter 1 + * + * data[%4] = configuration parameter 2 + * + * data[%5] = configuration parameter 3 + * + * The trigger ID (data[%1]) is used to differentiate multiple digital triggers + * belonging to the same subdevice. The configuration operation (data[%2]) is + * one of the enum comedi_digital_trig_op values. The configuration + * parameters (data[%3], data[%4], and data[%5]) depend on the operation; they + * are not used with %COMEDI_DIGITAL_TRIG_DISABLE. + * + * For %COMEDI_DIGITAL_TRIG_ENABLE_EDGES and %COMEDI_DIGITAL_TRIG_ENABLE_LEVELS, + * configuration parameter 1 (data[%3]) contains a "left-shift" value that + * specifies the input corresponding to bit 0 of configuration parameters 2 + * and 3. This is useful if the trigger has more than 32 inputs. + * + * For %COMEDI_DIGITAL_TRIG_ENABLE_EDGES, configuration parameter 2 (data[%4]) + * specifies which of up to 32 inputs have rising-edge sensitivity, and + * configuration parameter 3 (data[%5]) specifies which of up to 32 inputs + * have falling-edge sensitivity that can fire the trigger. + * + * For %COMEDI_DIGITAL_TRIG_ENABLE_LEVELS, configuration parameter 2 (data[%4]) + * specifies which of up to 32 inputs must be at a high level, and + * configuration parameter 3 (data[%5]) specifies which of up to 32 inputs + * must be at a low level for the trigger to fire. + * + * Some sequences of %INSN_CONFIG_DIGITAL_TRIG instructions may have a (partly) + * accumulative effect, depending on the low-level driver. This is useful + * when setting up a trigger that has more than 32 inputs, or has a combination + * of edge- and level-triggered inputs. + */ +enum comedi_digital_trig_op { + COMEDI_DIGITAL_TRIG_DISABLE = 0, + COMEDI_DIGITAL_TRIG_ENABLE_EDGES = 1, + COMEDI_DIGITAL_TRIG_ENABLE_LEVELS = 2 +}; + +/** + * enum comedi_support_level - support level for a COMEDI feature + * @COMEDI_UNKNOWN_SUPPORT: Unspecified support for feature. + * @COMEDI_SUPPORTED: Feature is supported. + * @COMEDI_UNSUPPORTED: Feature is unsupported. + */ +enum comedi_support_level { + COMEDI_UNKNOWN_SUPPORT = 0, + COMEDI_SUPPORTED, + COMEDI_UNSUPPORTED +}; + +/** + * enum comedi_counter_status_flags - counter status bits + * @COMEDI_COUNTER_ARMED: Counter is armed. + * @COMEDI_COUNTER_COUNTING: Counter is counting. + * @COMEDI_COUNTER_TERMINAL_COUNT: Counter reached terminal count. + * + * These bitwise values are used by the %INSN_CONFIG_GET_COUNTER_STATUS + * configuration instruction to report the status of a counter. + */ +enum comedi_counter_status_flags { + COMEDI_COUNTER_ARMED = 0x1, + COMEDI_COUNTER_COUNTING = 0x2, + COMEDI_COUNTER_TERMINAL_COUNT = 0x4, +}; + +/* ioctls */ + +#define CIO 'd' +#define COMEDI_DEVCONFIG _IOW(CIO, 0, struct comedi_devconfig) +#define COMEDI_DEVINFO _IOR(CIO, 1, struct comedi_devinfo) +#define COMEDI_SUBDINFO _IOR(CIO, 2, struct comedi_subdinfo) +#define COMEDI_CHANINFO _IOR(CIO, 3, struct comedi_chaninfo) +/* _IOWR(CIO, 4, ...) is reserved */ +#define COMEDI_LOCK _IO(CIO, 5) +#define COMEDI_UNLOCK _IO(CIO, 6) +#define COMEDI_CANCEL _IO(CIO, 7) +#define COMEDI_RANGEINFO _IOR(CIO, 8, struct comedi_rangeinfo) +#define COMEDI_CMD _IOR(CIO, 9, struct comedi_cmd) +#define COMEDI_CMDTEST _IOR(CIO, 10, struct comedi_cmd) +#define COMEDI_INSNLIST _IOR(CIO, 11, struct comedi_insnlist) +#define COMEDI_INSN _IOR(CIO, 12, struct comedi_insn) +#define COMEDI_BUFCONFIG _IOR(CIO, 13, struct comedi_bufconfig) +#define COMEDI_BUFINFO _IOWR(CIO, 14, struct comedi_bufinfo) +#define COMEDI_POLL _IO(CIO, 15) +#define COMEDI_SETRSUBD _IO(CIO, 16) +#define COMEDI_SETWSUBD _IO(CIO, 17) + +/* structures */ + +/** + * struct comedi_insn - COMEDI instruction + * @insn: COMEDI instruction type (%INSN_xxx). + * @n: Length of @data[]. + * @data: Pointer to data array operated on by the instruction. + * @subdev: Subdevice index. + * @chanspec: A packed "chanspec" value consisting of channel number, + * analog range index, analog reference type, and flags. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_INSN ioctl, and indirectly with the + * %COMEDI_INSNLIST ioctl. + */ +struct comedi_insn { + unsigned int insn; + unsigned int n; + unsigned int __user *data; + unsigned int subdev; + unsigned int chanspec; + unsigned int unused[3]; +}; + +/** + * struct comedi_insnlist - list of COMEDI instructions + * @n_insns: Number of COMEDI instructions. + * @insns: Pointer to array COMEDI instructions. + * + * This is used with the %COMEDI_INSNLIST ioctl. + */ +struct comedi_insnlist { + unsigned int n_insns; + struct comedi_insn __user *insns; +}; + +/** + * struct comedi_cmd - COMEDI asynchronous acquisition command details + * @subdev: Subdevice index. + * @flags: Command flags (%CMDF_xxx). + * @start_src: "Start acquisition" trigger source (%TRIG_xxx). + * @start_arg: "Start acquisition" trigger argument. + * @scan_begin_src: "Scan begin" trigger source. + * @scan_begin_arg: "Scan begin" trigger argument. + * @convert_src: "Convert" trigger source. + * @convert_arg: "Convert" trigger argument. + * @scan_end_src: "Scan end" trigger source. + * @scan_end_arg: "Scan end" trigger argument. + * @stop_src: "Stop acquisition" trigger source. + * @stop_arg: "Stop acquisition" trigger argument. + * @chanlist: Pointer to array of "chanspec" values, containing a + * sequence of channel numbers packed with analog range + * index, etc. + * @chanlist_len: Number of channels in sequence. + * @data: Pointer to miscellaneous set-up data (not used). + * @data_len: Length of miscellaneous set-up data. + * + * This is used with the %COMEDI_CMD or %COMEDI_CMDTEST ioctl to set-up + * or validate an asynchronous acquisition command. The ioctl may modify + * the &struct comedi_cmd and copy it back to the caller. + * + * Optional command @flags values that can be ORed together... + * + * %CMDF_BOGUS - makes %COMEDI_CMD ioctl return error %EAGAIN instead of + * starting the command. + * + * %CMDF_PRIORITY - requests "hard real-time" processing (which is not + * supported in this version of COMEDI). + * + * %CMDF_WAKE_EOS - requests the command makes data available for reading + * after every "scan" period. + * + * %CMDF_WRITE - marks the command as being in the "write" (to device) + * direction. This does not need to be specified by the caller unless the + * subdevice supports commands in either direction. + * + * %CMDF_RAWDATA - prevents the command from "munging" the data between the + * COMEDI sample format and the raw hardware sample format. + * + * %CMDF_ROUND_NEAREST - requests timing periods to be rounded to nearest + * supported values. + * + * %CMDF_ROUND_DOWN - requests timing periods to be rounded down to supported + * values (frequencies rounded up). + * + * %CMDF_ROUND_UP - requests timing periods to be rounded up to supported + * values (frequencies rounded down). + * + * Trigger source values for @start_src, @scan_begin_src, @convert_src, + * @scan_end_src, and @stop_src... + * + * %TRIG_ANY - "all ones" value used to test which trigger sources are + * supported. + * + * %TRIG_INVALID - "all zeroes" value used to indicate that all requested + * trigger sources are invalid. + * + * %TRIG_NONE - never trigger (often used as a @stop_src value). + * + * %TRIG_NOW - trigger after '_arg' nanoseconds. + * + * %TRIG_FOLLOW - trigger follows another event. + * + * %TRIG_TIMER - trigger every '_arg' nanoseconds. + * + * %TRIG_COUNT - trigger when count '_arg' is reached. + * + * %TRIG_EXT - trigger on external signal specified by '_arg'. + * + * %TRIG_INT - trigger on internal, software trigger specified by '_arg'. + * + * %TRIG_OTHER - trigger on other, driver-defined signal specified by '_arg'. + */ +struct comedi_cmd { + unsigned int subdev; + unsigned int flags; + + unsigned int start_src; + unsigned int start_arg; + + unsigned int scan_begin_src; + unsigned int scan_begin_arg; + + unsigned int convert_src; + unsigned int convert_arg; + + unsigned int scan_end_src; + unsigned int scan_end_arg; + + unsigned int stop_src; + unsigned int stop_arg; + + unsigned int *chanlist; + unsigned int chanlist_len; + + short __user *data; + unsigned int data_len; +}; + +/** + * struct comedi_chaninfo - used to retrieve per-channel information + * @subdev: Subdevice index. + * @maxdata_list: Optional pointer to per-channel maximum data values. + * @flaglist: Optional pointer to per-channel flags. + * @rangelist: Optional pointer to per-channel range types. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_CHANINFO ioctl to get per-channel information + * for the subdevice. Use of this requires knowledge of the number of channels + * and subdevice flags obtained using the %COMEDI_SUBDINFO ioctl. + * + * The @maxdata_list member must be %NULL unless the %SDF_MAXDATA subdevice + * flag is set. The @flaglist member must be %NULL unless the %SDF_FLAGS + * subdevice flag is set. The @rangelist member must be %NULL unless the + * %SDF_RANGETYPE subdevice flag is set. Otherwise, the arrays they point to + * must be at least as long as the number of channels. + */ +struct comedi_chaninfo { + unsigned int subdev; + unsigned int __user *maxdata_list; + unsigned int __user *flaglist; + unsigned int __user *rangelist; + unsigned int unused[4]; +}; + +/** + * struct comedi_rangeinfo - used to retrieve the range table for a channel + * @range_type: Encodes subdevice index (bits 27:24), channel index + * (bits 23:16) and range table length (bits 15:0). + * @range_ptr: Pointer to array of @struct comedi_krange to be filled + * in with the range table for the channel or subdevice. + * + * This is used with the %COMEDI_RANGEINFO ioctl to retrieve the range table + * for a specific channel (if the subdevice has the %SDF_RANGETYPE flag set to + * indicate that the range table depends on the channel), or for the subdevice + * as a whole (if the %SDF_RANGETYPE flag is clear, indicating the range table + * is shared by all channels). + * + * The @range_type value is an input to the ioctl and comes from a previous + * use of the %COMEDI_SUBDINFO ioctl (if the %SDF_RANGETYPE flag is clear), + * or the %COMEDI_CHANINFO ioctl (if the %SDF_RANGETYPE flag is set). + */ +struct comedi_rangeinfo { + unsigned int range_type; + void __user *range_ptr; +}; + +/** + * struct comedi_krange - describes a range in a range table + * @min: Minimum value in millionths (1e-6) of a unit. + * @max: Maximum value in millionths (1e-6) of a unit. + * @flags: Indicates the units (in bits 7:0) OR'ed with optional flags. + * + * A range table is associated with a single channel, or with all channels in a + * subdevice, and a list of one or more ranges. A %struct comedi_krange + * describes the physical range of units for one of those ranges. Sample + * values in COMEDI are unsigned from %0 up to some 'maxdata' value. The + * mapping from sample values to physical units is assumed to be nomimally + * linear (for the purpose of describing the range), with sample value %0 + * mapping to @min, and the 'maxdata' sample value mapping to @max. + * + * The currently defined units are %UNIT_volt (%0), %UNIT_mA (%1), and + * %UNIT_none (%2). The @min and @max values are the physical range multiplied + * by 1e6, so a @max value of %1000000 (with %UNIT_volt) represents a maximal + * value of 1 volt. + * + * The only defined flag value is %RF_EXTERNAL (%0x100), indicating that the + * range needs to be multiplied by an external reference. + */ +struct comedi_krange { + int min; + int max; + unsigned int flags; +}; + +/** + * struct comedi_subdinfo - used to retrieve information about a subdevice + * @type: Type of subdevice from &enum comedi_subdevice_type. + * @n_chan: Number of channels the subdevice supports. + * @subd_flags: A mixture of static and dynamic flags describing + * aspects of the subdevice and its current state. + * @timer_type: Timer type. Always set to %5 ("nanosecond timer"). + * @len_chanlist: Maximum length of a channel list if the subdevice + * supports asynchronous acquisition commands. + * @maxdata: Maximum sample value for all channels if the + * %SDF_MAXDATA subdevice flag is clear. + * @flags: Channel flags for all channels if the %SDF_FLAGS + * subdevice flag is clear. + * @range_type: The range type for all channels if the %SDF_RANGETYPE + * subdevice flag is clear. Encodes the subdevice index + * (bits 27:24), a dummy channel index %0 (bits 23:16), + * and the range table length (bits 15:0). + * @settling_time_0: Not used. + * @insn_bits_support: Set to %COMEDI_SUPPORTED if the subdevice supports the + * %INSN_BITS instruction, or to %COMEDI_UNSUPPORTED if it + * does not. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_SUBDINFO ioctl which copies an array of + * &struct comedi_subdinfo back to user space, with one element per subdevice. + * Use of this requires knowledge of the number of subdevices obtained from + * the %COMEDI_DEVINFO ioctl. + * + * These are the @subd_flags values that may be ORed together... + * + * %SDF_BUSY - the subdevice is busy processing an asynchronous command or a + * synchronous instruction. + * + * %SDF_BUSY_OWNER - the subdevice is busy processing an asynchronous + * acquisition command started on the current file object (the file object + * issuing the %COMEDI_SUBDINFO ioctl). + * + * %SDF_LOCKED - the subdevice is locked by a %COMEDI_LOCK ioctl. + * + * %SDF_LOCK_OWNER - the subdevice is locked by a %COMEDI_LOCK ioctl from the + * current file object. + * + * %SDF_MAXDATA - maximum sample values are channel-specific. + * + * %SDF_FLAGS - channel flags are channel-specific. + * + * %SDF_RANGETYPE - range types are channel-specific. + * + * %SDF_PWM_COUNTER - PWM can switch off automatically. + * + * %SDF_PWM_HBRIDGE - or PWM is signed (H-bridge). + * + * %SDF_CMD - the subdevice supports asynchronous commands. + * + * %SDF_SOFT_CALIBRATED - the subdevice uses software calibration. + * + * %SDF_CMD_WRITE - the subdevice supports asynchronous commands in the output + * ("write") direction. + * + * %SDF_CMD_READ - the subdevice supports asynchronous commands in the input + * ("read") direction. + * + * %SDF_READABLE - the subdevice is readable (e.g. analog input). + * + * %SDF_WRITABLE (aliased as %SDF_WRITEABLE) - the subdevice is writable (e.g. + * analog output). + * + * %SDF_INTERNAL - the subdevice has no externally visible lines. + * + * %SDF_GROUND - the subdevice can use ground as an analog reference. + * + * %SDF_COMMON - the subdevice can use a common analog reference. + * + * %SDF_DIFF - the subdevice can use differential inputs (or outputs). + * + * %SDF_OTHER - the subdevice can use some other analog reference. + * + * %SDF_DITHER - the subdevice can do dithering. + * + * %SDF_DEGLITCH - the subdevice can do deglitching. + * + * %SDF_MMAP - this is never set. + * + * %SDF_RUNNING - an asynchronous command is still running. + * + * %SDF_LSAMPL - the subdevice uses "long" (32-bit) samples (for asynchronous + * command data). + * + * %SDF_PACKED - the subdevice packs several DIO samples into a single sample + * (for asynchronous command data). + * + * No "channel flags" (@flags) values are currently defined. + */ +struct comedi_subdinfo { + unsigned int type; + unsigned int n_chan; + unsigned int subd_flags; + unsigned int timer_type; + unsigned int len_chanlist; + unsigned int maxdata; + unsigned int flags; + unsigned int range_type; + unsigned int settling_time_0; + unsigned int insn_bits_support; + unsigned int unused[8]; +}; + +/** + * struct comedi_devinfo - used to retrieve information about a COMEDI device + * @version_code: COMEDI version code. + * @n_subdevs: Number of subdevices the device has. + * @driver_name: Null-terminated COMEDI driver name. + * @board_name: Null-terminated COMEDI board name. + * @read_subdevice: Index of the current "read" subdevice (%-1 if none). + * @write_subdevice: Index of the current "write" subdevice (%-1 if none). + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_DEVINFO ioctl to get basic information about + * the device. + */ +struct comedi_devinfo { + unsigned int version_code; + unsigned int n_subdevs; + char driver_name[COMEDI_NAMELEN]; + char board_name[COMEDI_NAMELEN]; + int read_subdevice; + int write_subdevice; + int unused[30]; +}; + +/** + * struct comedi_devconfig - used to configure a legacy COMEDI device + * @board_name: Null-terminated string specifying the type of board + * to configure. + * @options: An array of integer configuration options. + * + * This is used with the %COMEDI_DEVCONFIG ioctl to configure a "legacy" COMEDI + * device, such as an ISA card. Not all COMEDI drivers support this. Those + * that do either expect the specified board name to match one of a list of + * names registered with the COMEDI core, or expect the specified board name + * to match the COMEDI driver name itself. The configuration options are + * handled in a driver-specific manner. + */ +struct comedi_devconfig { + char board_name[COMEDI_NAMELEN]; + int options[COMEDI_NDEVCONFOPTS]; +}; + +/** + * struct comedi_bufconfig - used to set or get buffer size for a subdevice + * @subdevice: Subdevice index. + * @flags: Not used. + * @maximum_size: Maximum allowed buffer size. + * @size: Buffer size. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_BUFCONFIG ioctl to get or configure the + * maximum buffer size and current buffer size for a COMEDI subdevice that + * supports asynchronous commands. If the subdevice does not support + * asynchronous commands, @maximum_size and @size are ignored and set to 0. + * + * On ioctl input, non-zero values of @maximum_size and @size specify a + * new maximum size and new current size (in bytes), respectively. These + * will by rounded up to a multiple of %PAGE_SIZE. Specifying a new maximum + * size requires admin capabilities. + * + * On ioctl output, @maximum_size and @size and set to the current maximum + * buffer size and current buffer size, respectively. + */ +struct comedi_bufconfig { + unsigned int subdevice; + unsigned int flags; + + unsigned int maximum_size; + unsigned int size; + + unsigned int unused[4]; +}; + +/** + * struct comedi_bufinfo - used to manipulate buffer position for a subdevice + * @subdevice: Subdevice index. + * @bytes_read: Specify amount to advance read position for an + * asynchronous command in the input ("read") direction. + * @buf_write_ptr: Current write position (index) within the buffer. + * @buf_read_ptr: Current read position (index) within the buffer. + * @buf_write_count: Total amount written, modulo 2^32. + * @buf_read_count: Total amount read, modulo 2^32. + * @bytes_written: Specify amount to advance write position for an + * asynchronous command in the output ("write") direction. + * @unused: Reserved for future use. + * + * This is used with the %COMEDI_BUFINFO ioctl to optionally advance the + * current read or write position in an asynchronous acquisition data buffer, + * and to get the current read and write positions in the buffer. + */ +struct comedi_bufinfo { + unsigned int subdevice; + unsigned int bytes_read; + + unsigned int buf_write_ptr; + unsigned int buf_read_ptr; + unsigned int buf_write_count; + unsigned int buf_read_count; + + unsigned int bytes_written; + + unsigned int unused[4]; +}; + +/* range stuff */ + +#define __RANGE(a, b) ((((a) & 0xffff) << 16) | ((b) & 0xffff)) + +#define RANGE_OFFSET(a) (((a) >> 16) & 0xffff) +#define RANGE_LENGTH(b) ((b) & 0xffff) + +#define RF_UNIT(flags) ((flags) & 0xff) +#define RF_EXTERNAL 0x100 + +#define UNIT_volt 0 +#define UNIT_mA 1 +#define UNIT_none 2 + +#define COMEDI_MIN_SPEED 0xffffffffu + +/**********************************************************/ +/* everything after this line is ALPHA */ +/**********************************************************/ + +/* + * 8254 specific configuration. + * + * It supports two config commands: + * + * 0 ID: INSN_CONFIG_SET_COUNTER_MODE + * 1 8254 Mode + * I8254_MODE0, I8254_MODE1, ..., I8254_MODE5 + * OR'ed with: + * I8254_BCD, I8254_BINARY + * + * 0 ID: INSN_CONFIG_8254_READ_STATUS + * 1 <-- Status byte returned here. + * B7 = Output + * B6 = NULL Count + * B5 - B0 Current mode. + */ + +enum i8254_mode { + I8254_MODE0 = (0 << 1), /* Interrupt on terminal count */ + I8254_MODE1 = (1 << 1), /* Hardware retriggerable one-shot */ + I8254_MODE2 = (2 << 1), /* Rate generator */ + I8254_MODE3 = (3 << 1), /* Square wave mode */ + I8254_MODE4 = (4 << 1), /* Software triggered strobe */ + /* Hardware triggered strobe (retriggerable) */ + I8254_MODE5 = (5 << 1), + /* Use binary-coded decimal instead of binary (pretty useless) */ + I8254_BCD = 1, + I8254_BINARY = 0 +}; + +/* *** BEGIN GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */ + +/* + * Common National Instruments Terminal/Signal names. + * Some of these have no NI_ prefix as they are useful for non-NI hardware, such + * as those that utilize the PXI/RTSI trigger lines. + * + * NOTE ABOUT THE CHOICE OF NAMES HERE AND THE CAMELSCRIPT: + * The choice to use CamelScript and the exact names below is for + * maintainability, clarity, similarity to manufacturer's documentation, + * _and_ a mitigation for confusion that has plagued the use of these drivers + * for years! + * + * More detail: + * There have been significant confusions over the past many years for users + * when trying to understand how to connect to/from signals and terminals on + * NI hardware using comedi. The major reason for this is that the actual + * register values were exposed and required to be used by users. Several + * major reasons exist why this caused major confusion for users: + * 1) The register values are _NOT_ in user documentation, but rather in + * arcane locations, such as a few register programming manuals that are + * increasingly hard to find and the NI MHDDK (comments in example code). + * There is no one place to find the various valid values of the registers. + * 2) The register values are _NOT_ completely consistent. There is no way to + * gain any sense of intuition of which values, or even enums one should use + * for various registers. There was some attempt in prior use of comedi to + * name enums such that a user might know which enums should be used for + * varying purposes, but the end-user had to gain a knowledge of register + * values to correctly wield this approach. + * 3) The names for signals and registers found in the various register level + * programming manuals and vendor-provided documentation are _not_ even + * close to the same names that are in the end-user documentation. + * + * Similar, albeit less, confusion plagued NI's previous version of their own + * drivers. Earlier than 2003, NI greatly simplified the situation for users + * by releasing a new API that abstracted the names of signals/terminals to a + * common and intuitive set of names. + * + * The names below mirror the names chosen and well documented by NI. These + * names are exposed to the user via the comedilib user library. By keeping + * the names below, in spite of the use of CamelScript, maintenance will be + * greatly eased and confusion for users _and_ comedi developers will be + * greatly reduced. + */ + +/* + * Base of abstracted NI names. + * The first 16 bits of *_arg are reserved for channel selection. + * Since we only actually need the first 4 or 5 bits for all register values on + * NI select registers anyways, we'll identify all values >= (1<<15) as being an + * abstracted NI signal/terminal name. + * These values are also used/returned by INSN_DEVICE_CONFIG_TEST_ROUTE, + * INSN_DEVICE_CONFIG_CONNECT_ROUTE, INSN_DEVICE_CONFIG_DISCONNECT_ROUTE, + * and INSN_DEVICE_CONFIG_GET_ROUTES. + */ +#define NI_NAMES_BASE 0x8000u + +#define _TERM_N(base, n, x) ((base) + ((x) & ((n) - 1))) + +/* + * not necessarily all allowed 64 PFIs are valid--certainly not for all devices + */ +#define NI_PFI(x) _TERM_N(NI_NAMES_BASE, 64, x) +/* 8 trigger lines by standard, Some devices cannot talk to all eight. */ +#define TRIGGER_LINE(x) _TERM_N(NI_PFI(-1) + 1, 8, x) +/* 4 RTSI shared MUXes to route signals to/from TRIGGER_LINES on NI hardware */ +#define NI_RTSI_BRD(x) _TERM_N(TRIGGER_LINE(-1) + 1, 4, x) + +/* *** Counter/timer names : 8 counters max *** */ +#define NI_MAX_COUNTERS 8 +#define NI_COUNTER_NAMES_BASE (NI_RTSI_BRD(-1) + 1) +#define NI_CtrSource(x) _TERM_N(NI_COUNTER_NAMES_BASE, NI_MAX_COUNTERS, x) +/* Gate, Aux, A,B,Z are all treated, at times as gates */ +#define NI_GATES_NAMES_BASE (NI_CtrSource(-1) + 1) +#define NI_CtrGate(x) _TERM_N(NI_GATES_NAMES_BASE, NI_MAX_COUNTERS, x) +#define NI_CtrAux(x) _TERM_N(NI_CtrGate(-1) + 1, NI_MAX_COUNTERS, x) +#define NI_CtrA(x) _TERM_N(NI_CtrAux(-1) + 1, NI_MAX_COUNTERS, x) +#define NI_CtrB(x) _TERM_N(NI_CtrA(-1) + 1, NI_MAX_COUNTERS, x) +#define NI_CtrZ(x) _TERM_N(NI_CtrB(-1) + 1, NI_MAX_COUNTERS, x) +#define NI_GATES_NAMES_MAX NI_CtrZ(-1) +#define NI_CtrArmStartTrigger(x) _TERM_N(NI_CtrZ(-1) + 1, NI_MAX_COUNTERS, x) +#define NI_CtrInternalOutput(x) \ + _TERM_N(NI_CtrArmStartTrigger(-1) + 1, NI_MAX_COUNTERS, x) +/** external pin(s) labeled conveniently as CtrOut. */ +#define NI_CtrOut(x) _TERM_N(NI_CtrInternalOutput(-1) + 1, NI_MAX_COUNTERS, x) +/** For Buffered sampling of ctr -- x series capability. */ +#define NI_CtrSampleClock(x) _TERM_N(NI_CtrOut(-1) + 1, NI_MAX_COUNTERS, x) +#define NI_COUNTER_NAMES_MAX NI_CtrSampleClock(-1) + +enum ni_common_signal_names { + /* PXI_Star: this is a non-NI-specific signal */ + PXI_Star = NI_COUNTER_NAMES_MAX + 1, + PXI_Clk10, + PXIe_Clk100, + NI_AI_SampleClock, + NI_AI_SampleClockTimebase, + NI_AI_StartTrigger, + NI_AI_ReferenceTrigger, + NI_AI_ConvertClock, + NI_AI_ConvertClockTimebase, + NI_AI_PauseTrigger, + NI_AI_HoldCompleteEvent, + NI_AI_HoldComplete, + NI_AI_ExternalMUXClock, + NI_AI_STOP, /* pulse signal that occurs when a update is finished(?) */ + NI_AO_SampleClock, + NI_AO_SampleClockTimebase, + NI_AO_StartTrigger, + NI_AO_PauseTrigger, + NI_DI_SampleClock, + NI_DI_SampleClockTimebase, + NI_DI_StartTrigger, + NI_DI_ReferenceTrigger, + NI_DI_PauseTrigger, + NI_DI_InputBufferFull, + NI_DI_ReadyForStartEvent, + NI_DI_ReadyForTransferEventBurst, + NI_DI_ReadyForTransferEventPipelined, + NI_DO_SampleClock, + NI_DO_SampleClockTimebase, + NI_DO_StartTrigger, + NI_DO_PauseTrigger, + NI_DO_OutputBufferFull, + NI_DO_DataActiveEvent, + NI_DO_ReadyForStartEvent, + NI_DO_ReadyForTransferEvent, + NI_MasterTimebase, + NI_20MHzTimebase, + NI_80MHzTimebase, + NI_100MHzTimebase, + NI_200MHzTimebase, + NI_100kHzTimebase, + NI_10MHzRefClock, + NI_FrequencyOutput, + NI_ChangeDetectionEvent, + NI_AnalogComparisonEvent, + NI_WatchdogExpiredEvent, + NI_WatchdogExpirationTrigger, + NI_SCXI_Trig1, + NI_LogicLow, + NI_LogicHigh, + NI_ExternalStrobe, + NI_PFI_DO, + NI_CaseGround, + /* special internal signal used as variable source for RTSI bus: */ + NI_RGOUT0, + + /* just a name to make the next more convenient, regardless of above */ + _NI_NAMES_MAX_PLUS_1, + NI_NUM_NAMES = _NI_NAMES_MAX_PLUS_1 - NI_NAMES_BASE, +}; + +/* *** END GLOBALLY-NAMED NI TERMINALS/SIGNALS *** */ + +#define NI_USUAL_PFI_SELECT(x) (((x) < 10) ? (0x1 + (x)) : (0xb + (x))) +#define NI_USUAL_RTSI_SELECT(x) (((x) < 7) ? (0xb + (x)) : 0x1b) + +/* + * mode bits for NI general-purpose counters, set with + * INSN_CONFIG_SET_COUNTER_MODE + */ +#define NI_GPCT_COUNTING_MODE_SHIFT 16 +#define NI_GPCT_INDEX_PHASE_BITSHIFT 20 +#define NI_GPCT_COUNTING_DIRECTION_SHIFT 24 +enum ni_gpct_mode_bits { + NI_GPCT_GATE_ON_BOTH_EDGES_BIT = 0x4, + NI_GPCT_EDGE_GATE_MODE_MASK = 0x18, + NI_GPCT_EDGE_GATE_STARTS_STOPS_BITS = 0x0, + NI_GPCT_EDGE_GATE_STOPS_STARTS_BITS = 0x8, + NI_GPCT_EDGE_GATE_STARTS_BITS = 0x10, + NI_GPCT_EDGE_GATE_NO_STARTS_NO_STOPS_BITS = 0x18, + NI_GPCT_STOP_MODE_MASK = 0x60, + NI_GPCT_STOP_ON_GATE_BITS = 0x00, + NI_GPCT_STOP_ON_GATE_OR_TC_BITS = 0x20, + NI_GPCT_STOP_ON_GATE_OR_SECOND_TC_BITS = 0x40, + NI_GPCT_LOAD_B_SELECT_BIT = 0x80, + NI_GPCT_OUTPUT_MODE_MASK = 0x300, + NI_GPCT_OUTPUT_TC_PULSE_BITS = 0x100, + NI_GPCT_OUTPUT_TC_TOGGLE_BITS = 0x200, + NI_GPCT_OUTPUT_TC_OR_GATE_TOGGLE_BITS = 0x300, + NI_GPCT_HARDWARE_DISARM_MASK = 0xc00, + NI_GPCT_NO_HARDWARE_DISARM_BITS = 0x000, + NI_GPCT_DISARM_AT_TC_BITS = 0x400, + NI_GPCT_DISARM_AT_GATE_BITS = 0x800, + NI_GPCT_DISARM_AT_TC_OR_GATE_BITS = 0xc00, + NI_GPCT_LOADING_ON_TC_BIT = 0x1000, + NI_GPCT_LOADING_ON_GATE_BIT = 0x4000, + NI_GPCT_COUNTING_MODE_MASK = 0x7 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_COUNTING_MODE_NORMAL_BITS = + 0x0 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_COUNTING_MODE_QUADRATURE_X1_BITS = + 0x1 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_COUNTING_MODE_QUADRATURE_X2_BITS = + 0x2 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_COUNTING_MODE_QUADRATURE_X4_BITS = + 0x3 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_COUNTING_MODE_TWO_PULSE_BITS = + 0x4 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_COUNTING_MODE_SYNC_SOURCE_BITS = + 0x6 << NI_GPCT_COUNTING_MODE_SHIFT, + NI_GPCT_INDEX_PHASE_MASK = 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT, + NI_GPCT_INDEX_PHASE_LOW_A_LOW_B_BITS = + 0x0 << NI_GPCT_INDEX_PHASE_BITSHIFT, + NI_GPCT_INDEX_PHASE_LOW_A_HIGH_B_BITS = + 0x1 << NI_GPCT_INDEX_PHASE_BITSHIFT, + NI_GPCT_INDEX_PHASE_HIGH_A_LOW_B_BITS = + 0x2 << NI_GPCT_INDEX_PHASE_BITSHIFT, + NI_GPCT_INDEX_PHASE_HIGH_A_HIGH_B_BITS = + 0x3 << NI_GPCT_INDEX_PHASE_BITSHIFT, + NI_GPCT_INDEX_ENABLE_BIT = 0x400000, + NI_GPCT_COUNTING_DIRECTION_MASK = + 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT, + NI_GPCT_COUNTING_DIRECTION_DOWN_BITS = + 0x00 << NI_GPCT_COUNTING_DIRECTION_SHIFT, + NI_GPCT_COUNTING_DIRECTION_UP_BITS = + 0x1 << NI_GPCT_COUNTING_DIRECTION_SHIFT, + NI_GPCT_COUNTING_DIRECTION_HW_UP_DOWN_BITS = + 0x2 << NI_GPCT_COUNTING_DIRECTION_SHIFT, + NI_GPCT_COUNTING_DIRECTION_HW_GATE_BITS = + 0x3 << NI_GPCT_COUNTING_DIRECTION_SHIFT, + NI_GPCT_RELOAD_SOURCE_MASK = 0xc000000, + NI_GPCT_RELOAD_SOURCE_FIXED_BITS = 0x0, + NI_GPCT_RELOAD_SOURCE_SWITCHING_BITS = 0x4000000, + NI_GPCT_RELOAD_SOURCE_GATE_SELECT_BITS = 0x8000000, + NI_GPCT_OR_GATE_BIT = 0x10000000, + NI_GPCT_INVERT_OUTPUT_BIT = 0x20000000 +}; + +/* + * Bits for setting a clock source with + * INSN_CONFIG_SET_CLOCK_SRC when using NI general-purpose counters. + */ +enum ni_gpct_clock_source_bits { + NI_GPCT_CLOCK_SRC_SELECT_MASK = 0x3f, + NI_GPCT_TIMEBASE_1_CLOCK_SRC_BITS = 0x0, + NI_GPCT_TIMEBASE_2_CLOCK_SRC_BITS = 0x1, + NI_GPCT_TIMEBASE_3_CLOCK_SRC_BITS = 0x2, + NI_GPCT_LOGIC_LOW_CLOCK_SRC_BITS = 0x3, + NI_GPCT_NEXT_GATE_CLOCK_SRC_BITS = 0x4, + NI_GPCT_NEXT_TC_CLOCK_SRC_BITS = 0x5, + /* NI 660x-specific */ + NI_GPCT_SOURCE_PIN_i_CLOCK_SRC_BITS = 0x6, + NI_GPCT_PXI10_CLOCK_SRC_BITS = 0x7, + NI_GPCT_PXI_STAR_TRIGGER_CLOCK_SRC_BITS = 0x8, + NI_GPCT_ANALOG_TRIGGER_OUT_CLOCK_SRC_BITS = 0x9, + NI_GPCT_PRESCALE_MODE_CLOCK_SRC_MASK = 0x30000000, + NI_GPCT_NO_PRESCALE_CLOCK_SRC_BITS = 0x0, + /* divide source by 2 */ + NI_GPCT_PRESCALE_X2_CLOCK_SRC_BITS = 0x10000000, + /* divide source by 8 */ + NI_GPCT_PRESCALE_X8_CLOCK_SRC_BITS = 0x20000000, + NI_GPCT_INVERT_CLOCK_SRC_BIT = 0x80000000 +}; + +/* NI 660x-specific */ +#define NI_GPCT_SOURCE_PIN_CLOCK_SRC_BITS(x) (0x10 + (x)) + +#define NI_GPCT_RTSI_CLOCK_SRC_BITS(x) (0x18 + (x)) + +/* no pfi on NI 660x */ +#define NI_GPCT_PFI_CLOCK_SRC_BITS(x) (0x20 + (x)) + +/* + * Possibilities for setting a gate source with + * INSN_CONFIG_SET_GATE_SRC when using NI general-purpose counters. + * May be bitwise-or'd with CR_EDGE or CR_INVERT. + */ +enum ni_gpct_gate_select { + /* m-series gates */ + NI_GPCT_TIMESTAMP_MUX_GATE_SELECT = 0x0, + NI_GPCT_AI_START2_GATE_SELECT = 0x12, + NI_GPCT_PXI_STAR_TRIGGER_GATE_SELECT = 0x13, + NI_GPCT_NEXT_OUT_GATE_SELECT = 0x14, + NI_GPCT_AI_START1_GATE_SELECT = 0x1c, + NI_GPCT_NEXT_SOURCE_GATE_SELECT = 0x1d, + NI_GPCT_ANALOG_TRIGGER_OUT_GATE_SELECT = 0x1e, + NI_GPCT_LOGIC_LOW_GATE_SELECT = 0x1f, + /* more gates for 660x */ + NI_GPCT_SOURCE_PIN_i_GATE_SELECT = 0x100, + NI_GPCT_GATE_PIN_i_GATE_SELECT = 0x101, + /* more gates for 660x "second gate" */ + NI_GPCT_UP_DOWN_PIN_i_GATE_SELECT = 0x201, + NI_GPCT_SELECTED_GATE_GATE_SELECT = 0x21e, + /* + * m-series "second gate" sources are unknown, + * we should add them here with an offset of 0x300 when + * known. + */ + NI_GPCT_DISABLED_GATE_SELECT = 0x8000, +}; + +#define NI_GPCT_GATE_PIN_GATE_SELECT(x) (0x102 + (x)) +#define NI_GPCT_RTSI_GATE_SELECT(x) NI_USUAL_RTSI_SELECT(x) +#define NI_GPCT_PFI_GATE_SELECT(x) NI_USUAL_PFI_SELECT(x) +#define NI_GPCT_UP_DOWN_PIN_GATE_SELECT(x) (0x202 + (x)) + +/* + * Possibilities for setting a source with + * INSN_CONFIG_SET_OTHER_SRC when using NI general-purpose counters. + */ +enum ni_gpct_other_index { + NI_GPCT_SOURCE_ENCODER_A, + NI_GPCT_SOURCE_ENCODER_B, + NI_GPCT_SOURCE_ENCODER_Z +}; + +enum ni_gpct_other_select { + /* m-series gates */ + /* Still unknown, probably only need NI_GPCT_PFI_OTHER_SELECT */ + NI_GPCT_DISABLED_OTHER_SELECT = 0x8000, +}; + +#define NI_GPCT_PFI_OTHER_SELECT(x) NI_USUAL_PFI_SELECT(x) + +/* + * start sources for ni general-purpose counters for use with + * INSN_CONFIG_ARM + */ +enum ni_gpct_arm_source { + NI_GPCT_ARM_IMMEDIATE = 0x0, + /* + * Start both the counter and the adjacent paired counter simultaneously + */ + NI_GPCT_ARM_PAIRED_IMMEDIATE = 0x1, + /* + * If the NI_GPCT_HW_ARM bit is set, we will pass the least significant + * bits (3 bits for 660x or 5 bits for m-series) through to the + * hardware. To select a hardware trigger, pass the appropriate select + * bit, e.g., + * NI_GPCT_HW_ARM | NI_GPCT_AI_START1_GATE_SELECT or + * NI_GPCT_HW_ARM | NI_GPCT_PFI_GATE_SELECT(pfi_number) + */ + NI_GPCT_HW_ARM = 0x1000, + NI_GPCT_ARM_UNKNOWN = NI_GPCT_HW_ARM, /* for backward compatibility */ +}; + +/* digital filtering options for ni 660x for use with INSN_CONFIG_FILTER. */ +enum ni_gpct_filter_select { + NI_GPCT_FILTER_OFF = 0x0, + NI_GPCT_FILTER_TIMEBASE_3_SYNC = 0x1, + NI_GPCT_FILTER_100x_TIMEBASE_1 = 0x2, + NI_GPCT_FILTER_20x_TIMEBASE_1 = 0x3, + NI_GPCT_FILTER_10x_TIMEBASE_1 = 0x4, + NI_GPCT_FILTER_2x_TIMEBASE_1 = 0x5, + NI_GPCT_FILTER_2x_TIMEBASE_3 = 0x6 +}; + +/* + * PFI digital filtering options for ni m-series for use with + * INSN_CONFIG_FILTER. + */ +enum ni_pfi_filter_select { + NI_PFI_FILTER_OFF = 0x0, + NI_PFI_FILTER_125ns = 0x1, + NI_PFI_FILTER_6425ns = 0x2, + NI_PFI_FILTER_2550us = 0x3 +}; + +/* master clock sources for ni mio boards and INSN_CONFIG_SET_CLOCK_SRC */ +enum ni_mio_clock_source { + NI_MIO_INTERNAL_CLOCK = 0, + /* + * Doesn't work for m-series, use NI_MIO_PLL_RTSI_CLOCK() + * the NI_MIO_PLL_* sources are m-series only + */ + NI_MIO_RTSI_CLOCK = 1, + NI_MIO_PLL_PXI_STAR_TRIGGER_CLOCK = 2, + NI_MIO_PLL_PXI10_CLOCK = 3, + NI_MIO_PLL_RTSI0_CLOCK = 4 +}; + +#define NI_MIO_PLL_RTSI_CLOCK(x) (NI_MIO_PLL_RTSI0_CLOCK + (x)) + +/* + * Signals which can be routed to an NI RTSI pin with INSN_CONFIG_SET_ROUTING. + * The numbers assigned are not arbitrary, they correspond to the bits required + * to program the board. + */ +enum ni_rtsi_routing { + NI_RTSI_OUTPUT_ADR_START1 = 0, + NI_RTSI_OUTPUT_ADR_START2 = 1, + NI_RTSI_OUTPUT_SCLKG = 2, + NI_RTSI_OUTPUT_DACUPDN = 3, + NI_RTSI_OUTPUT_DA_START1 = 4, + NI_RTSI_OUTPUT_G_SRC0 = 5, + NI_RTSI_OUTPUT_G_GATE0 = 6, + NI_RTSI_OUTPUT_RGOUT0 = 7, + NI_RTSI_OUTPUT_RTSI_BRD_0 = 8, + /* Pre-m-series always have RTSI clock on line 7 */ + NI_RTSI_OUTPUT_RTSI_OSC = 12 +}; + +#define NI_RTSI_OUTPUT_RTSI_BRD(x) (NI_RTSI_OUTPUT_RTSI_BRD_0 + (x)) + +/* + * Signals which can be routed to an NI PFI pin on an m-series board with + * INSN_CONFIG_SET_ROUTING. These numbers are also returned by + * INSN_CONFIG_GET_ROUTING on pre-m-series boards, even though their routing + * cannot be changed. The numbers assigned are not arbitrary, they correspond + * to the bits required to program the board. + */ +enum ni_pfi_routing { + NI_PFI_OUTPUT_PFI_DEFAULT = 0, + NI_PFI_OUTPUT_AI_START1 = 1, + NI_PFI_OUTPUT_AI_START2 = 2, + NI_PFI_OUTPUT_AI_CONVERT = 3, + NI_PFI_OUTPUT_G_SRC1 = 4, + NI_PFI_OUTPUT_G_GATE1 = 5, + NI_PFI_OUTPUT_AO_UPDATE_N = 6, + NI_PFI_OUTPUT_AO_START1 = 7, + NI_PFI_OUTPUT_AI_START_PULSE = 8, + NI_PFI_OUTPUT_G_SRC0 = 9, + NI_PFI_OUTPUT_G_GATE0 = 10, + NI_PFI_OUTPUT_EXT_STROBE = 11, + NI_PFI_OUTPUT_AI_EXT_MUX_CLK = 12, + NI_PFI_OUTPUT_GOUT0 = 13, + NI_PFI_OUTPUT_GOUT1 = 14, + NI_PFI_OUTPUT_FREQ_OUT = 15, + NI_PFI_OUTPUT_PFI_DO = 16, + NI_PFI_OUTPUT_I_ATRIG = 17, + NI_PFI_OUTPUT_RTSI0 = 18, + NI_PFI_OUTPUT_PXI_STAR_TRIGGER_IN = 26, + NI_PFI_OUTPUT_SCXI_TRIG1 = 27, + NI_PFI_OUTPUT_DIO_CHANGE_DETECT_RTSI = 28, + NI_PFI_OUTPUT_CDI_SAMPLE = 29, + NI_PFI_OUTPUT_CDO_UPDATE = 30 +}; + +#define NI_PFI_OUTPUT_RTSI(x) (NI_PFI_OUTPUT_RTSI0 + (x)) + +/* + * Signals which can be routed to output on a NI PFI pin on a 660x board + * with INSN_CONFIG_SET_ROUTING. The numbers assigned are + * not arbitrary, they correspond to the bits required + * to program the board. Lines 0 to 7 can only be set to + * NI_660X_PFI_OUTPUT_DIO. Lines 32 to 39 can only be set to + * NI_660X_PFI_OUTPUT_COUNTER. + */ +enum ni_660x_pfi_routing { + NI_660X_PFI_OUTPUT_COUNTER = 1, /* counter */ + NI_660X_PFI_OUTPUT_DIO = 2, /* static digital output */ +}; + +/* + * NI External Trigger lines. These values are not arbitrary, but are related + * to the bits required to program the board (offset by 1 for historical + * reasons). + */ +#define NI_EXT_PFI(x) (NI_USUAL_PFI_SELECT(x) - 1) +#define NI_EXT_RTSI(x) (NI_USUAL_RTSI_SELECT(x) - 1) + +/* + * Clock sources for CDIO subdevice on NI m-series boards. Used as the + * scan_begin_arg for a comedi_command. These sources may also be bitwise-or'd + * with CR_INVERT to change polarity. + */ +enum ni_m_series_cdio_scan_begin_src { + NI_CDIO_SCAN_BEGIN_SRC_GROUND = 0, + NI_CDIO_SCAN_BEGIN_SRC_AI_START = 18, + NI_CDIO_SCAN_BEGIN_SRC_AI_CONVERT = 19, + NI_CDIO_SCAN_BEGIN_SRC_PXI_STAR_TRIGGER = 20, + NI_CDIO_SCAN_BEGIN_SRC_G0_OUT = 28, + NI_CDIO_SCAN_BEGIN_SRC_G1_OUT = 29, + NI_CDIO_SCAN_BEGIN_SRC_ANALOG_TRIGGER = 30, + NI_CDIO_SCAN_BEGIN_SRC_AO_UPDATE = 31, + NI_CDIO_SCAN_BEGIN_SRC_FREQ_OUT = 32, + NI_CDIO_SCAN_BEGIN_SRC_DIO_CHANGE_DETECT_IRQ = 33 +}; + +#define NI_CDIO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x) +#define NI_CDIO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x) + +/* + * scan_begin_src for scan_begin_arg==TRIG_EXT with analog output command on NI + * boards. These scan begin sources can also be bitwise-or'd with CR_INVERT to + * change polarity. + */ +#define NI_AO_SCAN_BEGIN_SRC_PFI(x) NI_USUAL_PFI_SELECT(x) +#define NI_AO_SCAN_BEGIN_SRC_RTSI(x) NI_USUAL_RTSI_SELECT(x) + +/* + * Bits for setting a clock source with + * INSN_CONFIG_SET_CLOCK_SRC when using NI frequency output subdevice. + */ +enum ni_freq_out_clock_source_bits { + NI_FREQ_OUT_TIMEBASE_1_DIV_2_CLOCK_SRC, /* 10 MHz */ + NI_FREQ_OUT_TIMEBASE_2_CLOCK_SRC /* 100 KHz */ +}; + +/* + * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for + * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). + */ +enum amplc_dio_clock_source { + /* + * Per channel external clock + * input/output pin (pin is only an + * input when clock source set to this value, + * otherwise it is an output) + */ + AMPLC_DIO_CLK_CLKN, + AMPLC_DIO_CLK_10MHZ, /* 10 MHz internal clock */ + AMPLC_DIO_CLK_1MHZ, /* 1 MHz internal clock */ + AMPLC_DIO_CLK_100KHZ, /* 100 kHz internal clock */ + AMPLC_DIO_CLK_10KHZ, /* 10 kHz internal clock */ + AMPLC_DIO_CLK_1KHZ, /* 1 kHz internal clock */ + /* + * Output of preceding counter channel + * (for channel 0, preceding counter + * channel is channel 2 on preceding + * counter subdevice, for first counter + * subdevice, preceding counter + * subdevice is the last counter + * subdevice) + */ + AMPLC_DIO_CLK_OUTNM1, + AMPLC_DIO_CLK_EXT, /* per chip external input pin */ + /* the following are "enhanced" clock sources for PCIe models */ + AMPLC_DIO_CLK_VCC, /* clock input HIGH */ + AMPLC_DIO_CLK_GND, /* clock input LOW */ + AMPLC_DIO_CLK_PAT_PRESENT, /* "pattern present" signal */ + AMPLC_DIO_CLK_20MHZ /* 20 MHz internal clock */ +}; + +/* + * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for + * timer subdevice on some Amplicon DIO PCIe boards (amplc_dio200 driver). + */ +enum amplc_dio_ts_clock_src { + AMPLC_DIO_TS_CLK_1GHZ, /* 1 ns period with 20 ns granularity */ + AMPLC_DIO_TS_CLK_1MHZ, /* 1 us period */ + AMPLC_DIO_TS_CLK_1KHZ /* 1 ms period */ +}; + +/* + * Values for setting a gate source with INSN_CONFIG_SET_GATE_SRC for + * 8254 counter subdevices on Amplicon DIO boards (amplc_dio200 driver). + */ +enum amplc_dio_gate_source { + AMPLC_DIO_GAT_VCC, /* internal high logic level */ + AMPLC_DIO_GAT_GND, /* internal low logic level */ + AMPLC_DIO_GAT_GATN, /* per channel external gate input */ + /* + * negated output of counter channel minus 2 + * (for channels 0 or 1, channel minus 2 is channel 1 or 2 on + * the preceding counter subdevice, for the first counter subdevice + * the preceding counter subdevice is the last counter subdevice) + */ + AMPLC_DIO_GAT_NOUTNM2, + AMPLC_DIO_GAT_RESERVED4, + AMPLC_DIO_GAT_RESERVED5, + AMPLC_DIO_GAT_RESERVED6, + AMPLC_DIO_GAT_RESERVED7, + /* the following are "enhanced" gate sources for PCIe models */ + AMPLC_DIO_GAT_NGATN = 6, /* negated per channel gate input */ + /* non-negated output of counter channel minus 2 */ + AMPLC_DIO_GAT_OUTNM2, + AMPLC_DIO_GAT_PAT_PRESENT, /* "pattern present" signal */ + AMPLC_DIO_GAT_PAT_OCCURRED, /* "pattern occurred" latched */ + AMPLC_DIO_GAT_PAT_GONE, /* "pattern gone away" latched */ + AMPLC_DIO_GAT_NPAT_PRESENT, /* negated "pattern present" */ + AMPLC_DIO_GAT_NPAT_OCCURRED, /* negated "pattern occurred" */ + AMPLC_DIO_GAT_NPAT_GONE /* negated "pattern gone away" */ +}; + +/* + * Values for setting a clock source with INSN_CONFIG_SET_CLOCK_SRC for + * the counter subdevice on the Kolter Electronic PCI-Counter board + * (ke_counter driver). + */ +enum ke_counter_clock_source { + KE_CLK_20MHZ, /* internal 20MHz (default) */ + KE_CLK_4MHZ, /* internal 4MHz (option) */ + KE_CLK_EXT /* external clock on pin 21 of D-Sub */ +}; + +#endif /* _COMEDI_H */ -- cgit v1.2.3 From 55d0f80ecf0be13c2fdfa0c0917436f88f6502ff Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 17 Nov 2021 12:06:00 +0000 Subject: comedi: ni_routing: tools: Update due to moved COMEDI headers Building of the tools for converting the NI routing information between CSV files (for maintenance) and C files (for building) was broken by the move of the main COMEDI header files to "include/uapi/linux/" and "include/linux/". (These tools are not built as part of the normal kernel build process.) Fix it in the Makefile. A slight niggle is that `#include ` needs to work when compiling the `convert_c_to_py` program, but it cannot use a `-I` option referring to the "uapi" include directory because that interferes with inclusion of other system headers. So it uses `-I.` and makes a local copy (actually a symbolic link) as "./linux/comedi.h". Also remove some unneeded cruft such as the `-D"BIT(x)=(1<<(x))"` preprocessor flag. Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20211117120604.117740-3-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/ni_routing/tools/.gitignore | 1 + drivers/comedi/drivers/ni_routing/tools/Makefile | 29 ++++++++++++++-------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/drivers/comedi/drivers/ni_routing/tools/.gitignore b/drivers/comedi/drivers/ni_routing/tools/.gitignore index e3ebffcd900e..c12f825db266 100644 --- a/drivers/comedi/drivers/ni_routing/tools/.gitignore +++ b/drivers/comedi/drivers/ni_routing/tools/.gitignore @@ -5,4 +5,5 @@ ni_values.py convert_c_to_py c/ csv/ +linux/ all_cfiles.c diff --git a/drivers/comedi/drivers/ni_routing/tools/Makefile b/drivers/comedi/drivers/ni_routing/tools/Makefile index 6e92a06a44cb..31212101b3bc 100644 --- a/drivers/comedi/drivers/ni_routing/tools/Makefile +++ b/drivers/comedi/drivers/ni_routing/tools/Makefile @@ -3,7 +3,7 @@ # ni_route_values.h # ni_device_routes.h # in order to do this, we are also generating a python representation (using -# ctypesgen) of ../../comedi.h. +# ctypesgen) of ../../../../../include/uapi/linux/comedi.h. # This allows us to sort NI signal/terminal names numerically to use a binary # search through the device_routes tables to find valid routes. @@ -30,13 +30,21 @@ ALL: everything : csv-files c-files csv-blank -CPPFLAGS=-D"BIT(x)=(1UL<<(x))" -D__user= +CPPFLAGS = -D__user= +INC_UAPI = ../../../../../include/uapi -comedi_h.py : ../../../comedi.h +comedi_h.py: $(INC_UAPI)/linux/comedi.h ctypesgen $< --include "sys/ioctl.h" --cpp 'gcc -E $(CPPFLAGS)' -o $@ -convert_c_to_py: all_cfiles.c - gcc -g convert_c_to_py.c -o convert_c_to_py -std=c99 +convert_c_to_py: all_cfiles.c linux/comedi.h + gcc -g -I. convert_c_to_py.c -o convert_c_to_py -std=c99 + +# Create a local 'linux/comedi.h' for use when compiling 'convert_c_to_py.c' +# with the '-I.' option. (Cannot specify '-I../../../../../include/uapi' +# because that interferes with inclusion of other system headers.) +linux/comedi.h: $(INC_UAPI)/linux/comedi.h + mkdir -p linux + ln -snf ../$< $@ ni_values.py: convert_c_to_py ./convert_c_to_py @@ -44,7 +52,7 @@ ni_values.py: convert_c_to_py csv-files : ni_values.py comedi_h.py ./convert_py_to_csv.py -csv-blank : +csv-blank : comedi_h.py ./make_blank_csv.py @echo New blank csv signal table in csv/blank_route_table.csv @@ -62,17 +70,16 @@ clean-partial : $(RM) -rf comedi_h.py ni_values.py convert_c_to_py all_cfiles.c *.pyc \ __pycache__/ -clean : partial_clean - $(RM) -rf c/ csv/ +clean : clean-partial + $(RM) -rf c/ csv/ linux/ # Note: One could also use ctypeslib in order to generate these files. The # caveat is that ctypeslib does not do a great job at handling macro functions. # The make rules are as follows: -# comedi.h.xml : ../../comedi.h +# comedi.h.xml : $(INC_UAPI)/linux/comedi.h # # note that we have to use PWD here to avoid h2xml finding a system # # installed version of the comedilib/comedi.h file -# h2xml ${PWD}/../../comedi.h -c -D__user="" -D"BIT(x)=(1<<(x))" \ -# -o comedi.h.xml +# h2xml ${PWD}/$(INC_UAPI)/linux/comedi.h -c D__user="" -o comedi.h.xml # # comedi_h.py : comedi.h.xml # xml2py ./comedi.h.xml -o comedi_h.py -- cgit v1.2.3 From 631e272b12075b60f7c7fc4f84f937d78a699844 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 17 Nov 2021 12:06:01 +0000 Subject: comedi: Move and rename "8255.h" to Some of the header files in "drivers/comedi/drivers/" are common enough to be useful to out-of-tree comedi driver modules. Using them for out-of-tree module builds is hampered by the headers being outside the "include/" directory so it is desirable to move them. There are about a couple of dozen Comedi device drivers that use the "comedi_8255" module to add digital I/O subdevices based on the venerable 8255 Programmable Peripheral Interface chip. The macros and declarations to use that module are in the "8255.h" header file in the comedi "drivers" directory. Move it into "include/linux/comedi/" and rename it to "comedi_8255.h" for naming consistency reasons. Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20211117120604.117740-4-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/8255.c | 3 +- drivers/comedi/drivers/8255.h | 42 ---------------------------- drivers/comedi/drivers/8255_pci.c | 3 +- drivers/comedi/drivers/adv_pci_dio.c | 2 +- drivers/comedi/drivers/aio_aio12_8.c | 2 +- drivers/comedi/drivers/amplc_dio200_common.c | 2 +- drivers/comedi/drivers/amplc_pc236_common.c | 2 +- drivers/comedi/drivers/amplc_pci230.c | 2 +- drivers/comedi/drivers/cb_pcidas.c | 2 +- drivers/comedi/drivers/cb_pcidas64.c | 2 +- drivers/comedi/drivers/cb_pcidda.c | 3 +- drivers/comedi/drivers/cb_pcimdas.c | 2 +- drivers/comedi/drivers/cb_pcimdda.c | 3 +- drivers/comedi/drivers/comedi_8255.c | 3 +- drivers/comedi/drivers/daqboard2000.c | 2 +- drivers/comedi/drivers/das08.c | 2 +- drivers/comedi/drivers/das16.c | 2 +- drivers/comedi/drivers/das16m1.c | 2 +- drivers/comedi/drivers/dmm32at.c | 3 +- drivers/comedi/drivers/ni_atmio.c | 2 +- drivers/comedi/drivers/ni_atmio16d.c | 3 +- drivers/comedi/drivers/ni_daq_dio24.c | 3 +- drivers/comedi/drivers/ni_labpc_common.c | 2 +- drivers/comedi/drivers/ni_mio_common.c | 2 +- drivers/comedi/drivers/ni_mio_cs.c | 2 +- drivers/comedi/drivers/pcl724.c | 3 +- drivers/comedi/drivers/pcm3724.c | 3 +- include/linux/comedi/comedi_8255.h | 42 ++++++++++++++++++++++++++++ 28 files changed, 68 insertions(+), 78 deletions(-) delete mode 100644 drivers/comedi/drivers/8255.h create mode 100644 include/linux/comedi/comedi_8255.h diff --git a/drivers/comedi/drivers/8255.c b/drivers/comedi/drivers/8255.c index f23a52b7c919..ced8ea09d4fa 100644 --- a/drivers/comedi/drivers/8255.c +++ b/drivers/comedi/drivers/8255.c @@ -41,8 +41,7 @@ #include #include - -#include "8255.h" +#include static int dev_8255_attach(struct comedi_device *dev, struct comedi_devconfig *it) diff --git a/drivers/comedi/drivers/8255.h b/drivers/comedi/drivers/8255.h deleted file mode 100644 index ceae3ca52e60..000000000000 --- a/drivers/comedi/drivers/8255.h +++ /dev/null @@ -1,42 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * module/8255.h - * Header file for 8255 - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 1998 David A. Schleef - */ - -#ifndef _8255_H -#define _8255_H - -#define I8255_SIZE 0x04 - -#define I8255_DATA_A_REG 0x00 -#define I8255_DATA_B_REG 0x01 -#define I8255_DATA_C_REG 0x02 -#define I8255_CTRL_REG 0x03 -#define I8255_CTRL_C_LO_IO BIT(0) -#define I8255_CTRL_B_IO BIT(1) -#define I8255_CTRL_B_MODE BIT(2) -#define I8255_CTRL_C_HI_IO BIT(3) -#define I8255_CTRL_A_IO BIT(4) -#define I8255_CTRL_A_MODE(x) ((x) << 5) -#define I8255_CTRL_CW BIT(7) - -struct comedi_device; -struct comedi_subdevice; - -int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, - int (*io)(struct comedi_device *dev, int dir, int port, - int data, unsigned long regbase), - unsigned long regbase); - -int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, - int (*io)(struct comedi_device *dev, int dir, int port, - int data, unsigned long regbase), - unsigned long regbase); - -unsigned long subdev_8255_regbase(struct comedi_subdevice *s); - -#endif diff --git a/drivers/comedi/drivers/8255_pci.c b/drivers/comedi/drivers/8255_pci.c index 76b8b4762bae..0fec048e3a53 100644 --- a/drivers/comedi/drivers/8255_pci.c +++ b/drivers/comedi/drivers/8255_pci.c @@ -54,8 +54,7 @@ #include #include - -#include "8255.h" +#include enum pci_8255_boardid { BOARD_ADLINK_PCI7224, diff --git a/drivers/comedi/drivers/adv_pci_dio.c b/drivers/comedi/drivers/adv_pci_dio.c index 5947f08b9a1e..1ec602f8c6e1 100644 --- a/drivers/comedi/drivers/adv_pci_dio.c +++ b/drivers/comedi/drivers/adv_pci_dio.c @@ -24,8 +24,8 @@ #include #include #include +#include -#include "8255.h" #include "comedi_8254.h" /* diff --git a/drivers/comedi/drivers/aio_aio12_8.c b/drivers/comedi/drivers/aio_aio12_8.c index 36c3a2d8a352..cd797dc0f828 100644 --- a/drivers/comedi/drivers/aio_aio12_8.c +++ b/drivers/comedi/drivers/aio_aio12_8.c @@ -23,9 +23,9 @@ #include #include +#include #include "comedi_8254.h" -#include "8255.h" /* * Register map diff --git a/drivers/comedi/drivers/amplc_dio200_common.c b/drivers/comedi/drivers/amplc_dio200_common.c index 950c50be4ff3..26b4049b366c 100644 --- a/drivers/comedi/drivers/amplc_dio200_common.c +++ b/drivers/comedi/drivers/amplc_dio200_common.c @@ -13,10 +13,10 @@ #include #include #include +#include /* only for register defines */ #include "amplc_dio200.h" #include "comedi_8254.h" -#include "8255.h" /* only for register defines */ /* 200 series registers */ #define DIO200_IO_SIZE 0x20 diff --git a/drivers/comedi/drivers/amplc_pc236_common.c b/drivers/comedi/drivers/amplc_pc236_common.c index b8b0a624f72b..9f4f89b1ef23 100644 --- a/drivers/comedi/drivers/amplc_pc236_common.c +++ b/drivers/comedi/drivers/amplc_pc236_common.c @@ -12,9 +12,9 @@ #include #include #include +#include #include "amplc_pc236.h" -#include "8255.h" static void pc236_intr_update(struct comedi_device *dev, bool enable) { diff --git a/drivers/comedi/drivers/amplc_pci230.c b/drivers/comedi/drivers/amplc_pci230.c index 554ee40e321f..93f7057d5b3f 100644 --- a/drivers/comedi/drivers/amplc_pci230.c +++ b/drivers/comedi/drivers/amplc_pci230.c @@ -175,9 +175,9 @@ #include #include #include +#include #include "comedi_8254.h" -#include "8255.h" /* * PCI230 PCI configuration register information diff --git a/drivers/comedi/drivers/cb_pcidas.c b/drivers/comedi/drivers/cb_pcidas.c index 9b603532a4e7..75ff02b47959 100644 --- a/drivers/comedi/drivers/cb_pcidas.c +++ b/drivers/comedi/drivers/cb_pcidas.c @@ -55,9 +55,9 @@ #include #include #include +#include #include "comedi_8254.h" -#include "8255.h" #include "amcc_s5933.h" #define AI_BUFFER_SIZE 1024 /* max ai fifo size */ diff --git a/drivers/comedi/drivers/cb_pcidas64.c b/drivers/comedi/drivers/cb_pcidas64.c index 7d4808faa1fb..ca6038a25f26 100644 --- a/drivers/comedi/drivers/cb_pcidas64.c +++ b/drivers/comedi/drivers/cb_pcidas64.c @@ -74,8 +74,8 @@ #include #include #include +#include -#include "8255.h" #include "plx9080.h" #define TIMER_BASE 25 /* 40MHz master clock */ diff --git a/drivers/comedi/drivers/cb_pcidda.c b/drivers/comedi/drivers/cb_pcidda.c index 4ed3bcf47973..c52204a6bda4 100644 --- a/drivers/comedi/drivers/cb_pcidda.c +++ b/drivers/comedi/drivers/cb_pcidda.c @@ -28,8 +28,7 @@ #include #include - -#include "8255.h" +#include #define EEPROM_SIZE 128 /* number of entries in eeprom */ /* maximum number of ao channels for supported boards */ diff --git a/drivers/comedi/drivers/cb_pcimdas.c b/drivers/comedi/drivers/cb_pcimdas.c index 64c7d72c7956..7bc0805c69e2 100644 --- a/drivers/comedi/drivers/cb_pcimdas.c +++ b/drivers/comedi/drivers/cb_pcimdas.c @@ -35,10 +35,10 @@ #include #include #include +#include #include "comedi_8254.h" #include "plx9052.h" -#include "8255.h" /* * PCI Bar 1 Register map diff --git a/drivers/comedi/drivers/cb_pcimdda.c b/drivers/comedi/drivers/cb_pcimdda.c index 69d7803b0e58..bf8093a10315 100644 --- a/drivers/comedi/drivers/cb_pcimdda.c +++ b/drivers/comedi/drivers/cb_pcimdda.c @@ -68,8 +68,7 @@ #include #include - -#include "8255.h" +#include /* device ids of the cards we support -- currently only 1 card supported */ #define PCI_ID_PCIM_DDA06_16 0x0053 diff --git a/drivers/comedi/drivers/comedi_8255.c b/drivers/comedi/drivers/comedi_8255.c index 10614603d677..5562b9cd0a17 100644 --- a/drivers/comedi/drivers/comedi_8255.c +++ b/drivers/comedi/drivers/comedi_8255.c @@ -30,8 +30,7 @@ #include #include - -#include "8255.h" +#include struct subdev_8255_private { unsigned long regbase; diff --git a/drivers/comedi/drivers/daqboard2000.c b/drivers/comedi/drivers/daqboard2000.c index 52e4bf16cbda..c0a4e1b06fb3 100644 --- a/drivers/comedi/drivers/daqboard2000.c +++ b/drivers/comedi/drivers/daqboard2000.c @@ -97,8 +97,8 @@ #include #include #include +#include -#include "8255.h" #include "plx9080.h" #define DB2K_FIRMWARE "daqboard2000_firmware.bin" diff --git a/drivers/comedi/drivers/das08.c b/drivers/comedi/drivers/das08.c index c146a168f43b..bab868de2967 100644 --- a/drivers/comedi/drivers/das08.c +++ b/drivers/comedi/drivers/das08.c @@ -11,8 +11,8 @@ #include #include +#include -#include "8255.h" #include "comedi_8254.h" #include "das08.h" diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index 362232ad4409..338396736936 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -64,10 +64,10 @@ #include #include #include +#include #include "comedi_isadma.h" #include "comedi_8254.h" -#include "8255.h" #define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */ diff --git a/drivers/comedi/drivers/das16m1.c b/drivers/comedi/drivers/das16m1.c index cc79e318cb2d..ea55024d8c5a 100644 --- a/drivers/comedi/drivers/das16m1.c +++ b/drivers/comedi/drivers/das16m1.c @@ -43,8 +43,8 @@ #include #include #include +#include -#include "8255.h" #include "comedi_8254.h" /* diff --git a/drivers/comedi/drivers/dmm32at.c b/drivers/comedi/drivers/dmm32at.c index 0f2bea88b8a7..fe023c722aa3 100644 --- a/drivers/comedi/drivers/dmm32at.c +++ b/drivers/comedi/drivers/dmm32at.c @@ -30,8 +30,7 @@ #include #include #include - -#include "8255.h" +#include /* Board register addresses */ #define DMM32AT_AI_START_CONV_REG 0x00 diff --git a/drivers/comedi/drivers/ni_atmio.c b/drivers/comedi/drivers/ni_atmio.c index f60a4e459a98..8876a1d24c56 100644 --- a/drivers/comedi/drivers/ni_atmio.c +++ b/drivers/comedi/drivers/ni_atmio.c @@ -75,9 +75,9 @@ #include #include #include +#include #include "ni_stc.h" -#include "8255.h" /* AT specific setup */ static const struct ni_board_struct ni_boards[] = { diff --git a/drivers/comedi/drivers/ni_atmio16d.c b/drivers/comedi/drivers/ni_atmio16d.c index 0bd4f88a2ac8..9fa902529a8e 100644 --- a/drivers/comedi/drivers/ni_atmio16d.c +++ b/drivers/comedi/drivers/ni_atmio16d.c @@ -40,8 +40,7 @@ #include #include #include - -#include "8255.h" +#include /* Configuration and Status Registers */ #define COM_REG_1 0x00 /* wo 16 */ diff --git a/drivers/comedi/drivers/ni_daq_dio24.c b/drivers/comedi/drivers/ni_daq_dio24.c index 84d78f2ee5ac..487733111023 100644 --- a/drivers/comedi/drivers/ni_daq_dio24.c +++ b/drivers/comedi/drivers/ni_daq_dio24.c @@ -24,8 +24,7 @@ #include #include - -#include "8255.h" +#include static int dio24_auto_attach(struct comedi_device *dev, unsigned long context) diff --git a/drivers/comedi/drivers/ni_labpc_common.c b/drivers/comedi/drivers/ni_labpc_common.c index 7c4687226450..4a1269aeb371 100644 --- a/drivers/comedi/drivers/ni_labpc_common.c +++ b/drivers/comedi/drivers/ni_labpc_common.c @@ -13,9 +13,9 @@ #include #include #include +#include #include "comedi_8254.h" -#include "8255.h" #include "ni_labpc.h" #include "ni_labpc_regs.h" #include "ni_labpc_isadma.h" diff --git a/drivers/comedi/drivers/ni_mio_common.c b/drivers/comedi/drivers/ni_mio_common.c index 4f80a4991f95..d39998565808 100644 --- a/drivers/comedi/drivers/ni_mio_common.c +++ b/drivers/comedi/drivers/ni_mio_common.c @@ -43,7 +43,7 @@ #include #include #include -#include "8255.h" +#include #include "mite.h" /* A timeout count */ diff --git a/drivers/comedi/drivers/ni_mio_cs.c b/drivers/comedi/drivers/ni_mio_cs.c index bd967cdb2036..796f0b743772 100644 --- a/drivers/comedi/drivers/ni_mio_cs.c +++ b/drivers/comedi/drivers/ni_mio_cs.c @@ -29,9 +29,9 @@ #include #include #include +#include #include "ni_stc.h" -#include "8255.h" /* * AT specific setup diff --git a/drivers/comedi/drivers/pcl724.c b/drivers/comedi/drivers/pcl724.c index b3f472c93e80..948a0576c9ef 100644 --- a/drivers/comedi/drivers/pcl724.c +++ b/drivers/comedi/drivers/pcl724.c @@ -26,8 +26,7 @@ #include #include - -#include "8255.h" +#include struct pcl724_board { const char *name; diff --git a/drivers/comedi/drivers/pcm3724.c b/drivers/comedi/drivers/pcm3724.c index 93ae6cffed44..e4103f9eeced 100644 --- a/drivers/comedi/drivers/pcm3724.c +++ b/drivers/comedi/drivers/pcm3724.c @@ -25,8 +25,7 @@ #include #include - -#include "8255.h" +#include /* * Register I/O Map diff --git a/include/linux/comedi/comedi_8255.h b/include/linux/comedi/comedi_8255.h new file mode 100644 index 000000000000..b2a5bc6b3a49 --- /dev/null +++ b/include/linux/comedi/comedi_8255.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * comedi_8255.h + * Generic 8255 digital I/O subdevice support + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 1998 David A. Schleef + */ + +#ifndef _COMEDI_8255_H +#define _COMEDI_8255_H + +#define I8255_SIZE 0x04 + +#define I8255_DATA_A_REG 0x00 +#define I8255_DATA_B_REG 0x01 +#define I8255_DATA_C_REG 0x02 +#define I8255_CTRL_REG 0x03 +#define I8255_CTRL_C_LO_IO BIT(0) +#define I8255_CTRL_B_IO BIT(1) +#define I8255_CTRL_B_MODE BIT(2) +#define I8255_CTRL_C_HI_IO BIT(3) +#define I8255_CTRL_A_IO BIT(4) +#define I8255_CTRL_A_MODE(x) ((x) << 5) +#define I8255_CTRL_CW BIT(7) + +struct comedi_device; +struct comedi_subdevice; + +int subdev_8255_init(struct comedi_device *dev, struct comedi_subdevice *s, + int (*io)(struct comedi_device *dev, int dir, int port, + int data, unsigned long regbase), + unsigned long regbase); + +int subdev_8255_mm_init(struct comedi_device *dev, struct comedi_subdevice *s, + int (*io)(struct comedi_device *dev, int dir, int port, + int data, unsigned long regbase), + unsigned long regbase); + +unsigned long subdev_8255_regbase(struct comedi_subdevice *s); + +#endif -- cgit v1.2.3 From 44fb7affcfa4e968e9c2ede023ef0e15f06d8209 Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 17 Nov 2021 12:06:02 +0000 Subject: comedi: Move "comedi_8254.h" to Some of the header files in "drivers/comedi/drivers/" are common enough to be useful to out-of-tree comedi driver modules. Using them for out-of-tree module builds is hampered by the headers being outside the "include/" directory so it is desirable to move them. There are about a couple of dozen or so Comedi device drivers that use the "comedi_8254" module to add timers based on the venerable 8254 Programmable Interval Timer chip. The macros and declarations to use that module are in the "comedi_8254.h" header file in the comedi "drivers" directory. Move it into "include/linux/comedi/". Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20211117120604.117740-5-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/adl_pci9111.c | 2 +- drivers/comedi/drivers/adl_pci9118.c | 2 +- drivers/comedi/drivers/adv_pci1710.c | 2 +- drivers/comedi/drivers/adv_pci_dio.c | 3 +- drivers/comedi/drivers/aio_aio12_8.c | 3 +- drivers/comedi/drivers/amplc_dio200_common.c | 2 +- drivers/comedi/drivers/amplc_pci224.c | 3 +- drivers/comedi/drivers/amplc_pci230.c | 3 +- drivers/comedi/drivers/cb_das16_cs.c | 3 +- drivers/comedi/drivers/cb_pcidas.c | 2 +- drivers/comedi/drivers/cb_pcimdas.c | 2 +- drivers/comedi/drivers/comedi_8254.c | 3 +- drivers/comedi/drivers/comedi_8254.h | 134 --------------------------- drivers/comedi/drivers/das08.c | 2 +- drivers/comedi/drivers/das16.c | 2 +- drivers/comedi/drivers/das16m1.c | 3 +- drivers/comedi/drivers/das1800.c | 2 +- drivers/comedi/drivers/das6402.c | 3 +- drivers/comedi/drivers/das800.c | 3 +- drivers/comedi/drivers/me4000.c | 2 +- drivers/comedi/drivers/ni_at_a2150.c | 2 +- drivers/comedi/drivers/ni_at_ao.c | 3 +- drivers/comedi/drivers/ni_labpc_common.c | 2 +- drivers/comedi/drivers/pcl711.c | 3 +- drivers/comedi/drivers/pcl812.c | 2 +- drivers/comedi/drivers/pcl816.c | 2 +- drivers/comedi/drivers/pcl818.c | 2 +- drivers/comedi/drivers/rtd520.c | 2 +- include/linux/comedi/comedi_8254.h | 134 +++++++++++++++++++++++++++ 29 files changed, 161 insertions(+), 172 deletions(-) delete mode 100644 drivers/comedi/drivers/comedi_8254.h create mode 100644 include/linux/comedi/comedi_8254.h diff --git a/drivers/comedi/drivers/adl_pci9111.c b/drivers/comedi/drivers/adl_pci9111.c index 65454f3ecc91..c50f94272a74 100644 --- a/drivers/comedi/drivers/adl_pci9111.c +++ b/drivers/comedi/drivers/adl_pci9111.c @@ -43,9 +43,9 @@ #include #include #include +#include #include "plx9052.h" -#include "comedi_8254.h" #define PCI9111_FIFO_HALF_SIZE 512 diff --git a/drivers/comedi/drivers/adl_pci9118.c b/drivers/comedi/drivers/adl_pci9118.c index 248cec3d894f..9a816c718303 100644 --- a/drivers/comedi/drivers/adl_pci9118.c +++ b/drivers/comedi/drivers/adl_pci9118.c @@ -79,9 +79,9 @@ #include #include #include +#include #include "amcc_s5933.h" -#include "comedi_8254.h" /* * PCI BAR2 Register map (dev->iobase) diff --git a/drivers/comedi/drivers/adv_pci1710.c b/drivers/comedi/drivers/adv_pci1710.c index 47a800d72e58..4f2639968260 100644 --- a/drivers/comedi/drivers/adv_pci1710.c +++ b/drivers/comedi/drivers/adv_pci1710.c @@ -31,8 +31,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "amcc_s5933.h" /* diff --git a/drivers/comedi/drivers/adv_pci_dio.c b/drivers/comedi/drivers/adv_pci_dio.c index 1ec602f8c6e1..efa3e46b554b 100644 --- a/drivers/comedi/drivers/adv_pci_dio.c +++ b/drivers/comedi/drivers/adv_pci_dio.c @@ -25,8 +25,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * Register offset definitions diff --git a/drivers/comedi/drivers/aio_aio12_8.c b/drivers/comedi/drivers/aio_aio12_8.c index cd797dc0f828..30b8a32204d8 100644 --- a/drivers/comedi/drivers/aio_aio12_8.c +++ b/drivers/comedi/drivers/aio_aio12_8.c @@ -24,8 +24,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/amplc_dio200_common.c b/drivers/comedi/drivers/amplc_dio200_common.c index 26b4049b366c..ff651f2eb86c 100644 --- a/drivers/comedi/drivers/amplc_dio200_common.c +++ b/drivers/comedi/drivers/amplc_dio200_common.c @@ -14,9 +14,9 @@ #include #include #include /* only for register defines */ +#include #include "amplc_dio200.h" -#include "comedi_8254.h" /* 200 series registers */ #define DIO200_IO_SIZE 0x20 diff --git a/drivers/comedi/drivers/amplc_pci224.c b/drivers/comedi/drivers/amplc_pci224.c index 3cf1b7fa565d..5a04e55daeea 100644 --- a/drivers/comedi/drivers/amplc_pci224.c +++ b/drivers/comedi/drivers/amplc_pci224.c @@ -97,8 +97,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * PCI224/234 i/o space 1 (PCIBAR2) registers. diff --git a/drivers/comedi/drivers/amplc_pci230.c b/drivers/comedi/drivers/amplc_pci230.c index 93f7057d5b3f..92ba8b8c0172 100644 --- a/drivers/comedi/drivers/amplc_pci230.c +++ b/drivers/comedi/drivers/amplc_pci230.c @@ -176,8 +176,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * PCI230 PCI configuration register information diff --git a/drivers/comedi/drivers/cb_das16_cs.c b/drivers/comedi/drivers/cb_das16_cs.c index 190d73a7d12c..8e0d2fa5f95d 100644 --- a/drivers/comedi/drivers/cb_das16_cs.c +++ b/drivers/comedi/drivers/cb_das16_cs.c @@ -28,8 +28,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/cb_pcidas.c b/drivers/comedi/drivers/cb_pcidas.c index 75ff02b47959..0c7576b967fc 100644 --- a/drivers/comedi/drivers/cb_pcidas.c +++ b/drivers/comedi/drivers/cb_pcidas.c @@ -56,8 +56,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "amcc_s5933.h" #define AI_BUFFER_SIZE 1024 /* max ai fifo size */ diff --git a/drivers/comedi/drivers/cb_pcimdas.c b/drivers/comedi/drivers/cb_pcimdas.c index 7bc0805c69e2..8bdb00774f11 100644 --- a/drivers/comedi/drivers/cb_pcimdas.c +++ b/drivers/comedi/drivers/cb_pcimdas.c @@ -36,8 +36,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "plx9052.h" /* diff --git a/drivers/comedi/drivers/comedi_8254.c b/drivers/comedi/drivers/comedi_8254.c index fac81567133d..b4185c1b2695 100644 --- a/drivers/comedi/drivers/comedi_8254.c +++ b/drivers/comedi/drivers/comedi_8254.c @@ -117,8 +117,7 @@ #include #include #include - -#include "comedi_8254.h" +#include static unsigned int __i8254_read(struct comedi_8254 *i8254, unsigned int reg) { diff --git a/drivers/comedi/drivers/comedi_8254.h b/drivers/comedi/drivers/comedi_8254.h deleted file mode 100644 index d8264417e53c..000000000000 --- a/drivers/comedi/drivers/comedi_8254.h +++ /dev/null @@ -1,134 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * comedi_8254.h - * Generic 8254 timer/counter support - * Copyright (C) 2014 H Hartley Sweeten - * - * COMEDI - Linux Control and Measurement Device Interface - * Copyright (C) 2000 David A. Schleef - */ - -#ifndef _COMEDI_8254_H -#define _COMEDI_8254_H - -#include - -struct comedi_device; -struct comedi_insn; -struct comedi_subdevice; - -/* - * Common oscillator base values in nanoseconds - */ -#define I8254_OSC_BASE_10MHZ 100 -#define I8254_OSC_BASE_5MHZ 200 -#define I8254_OSC_BASE_4MHZ 250 -#define I8254_OSC_BASE_2MHZ 500 -#define I8254_OSC_BASE_1MHZ 1000 -#define I8254_OSC_BASE_100KHZ 10000 -#define I8254_OSC_BASE_10KHZ 100000 -#define I8254_OSC_BASE_1KHZ 1000000 - -/* - * I/O access size used to read/write registers - */ -#define I8254_IO8 1 -#define I8254_IO16 2 -#define I8254_IO32 4 - -/* - * Register map for generic 8254 timer (I8254_IO8 with 0 regshift) - */ -#define I8254_COUNTER0_REG 0x00 -#define I8254_COUNTER1_REG 0x01 -#define I8254_COUNTER2_REG 0x02 -#define I8254_CTRL_REG 0x03 -#define I8254_CTRL_SEL_CTR(x) ((x) << 6) -#define I8254_CTRL_READBACK(x) (I8254_CTRL_SEL_CTR(3) | BIT(x)) -#define I8254_CTRL_READBACK_COUNT I8254_CTRL_READBACK(4) -#define I8254_CTRL_READBACK_STATUS I8254_CTRL_READBACK(5) -#define I8254_CTRL_READBACK_SEL_CTR(x) (2 << (x)) -#define I8254_CTRL_RW(x) (((x) & 0x3) << 4) -#define I8254_CTRL_LATCH I8254_CTRL_RW(0) -#define I8254_CTRL_LSB_ONLY I8254_CTRL_RW(1) -#define I8254_CTRL_MSB_ONLY I8254_CTRL_RW(2) -#define I8254_CTRL_LSB_MSB I8254_CTRL_RW(3) - -/* counter maps zero to 0x10000 */ -#define I8254_MAX_COUNT 0x10000 - -/** - * struct comedi_8254 - private data used by this module - * @iobase: PIO base address of the registers (in/out) - * @mmio: MMIO base address of the registers (read/write) - * @iosize: I/O size used to access the registers (b/w/l) - * @regshift: register gap shift - * @osc_base: cascaded oscillator speed in ns - * @divisor: divisor for single counter - * @divisor1: divisor loaded into first cascaded counter - * @divisor2: divisor loaded into second cascaded counter - * #next_div: next divisor for single counter - * @next_div1: next divisor to use for first cascaded counter - * @next_div2: next divisor to use for second cascaded counter - * @clock_src; current clock source for each counter (driver specific) - * @gate_src; current gate source for each counter (driver specific) - * @busy: flags used to indicate that a counter is "busy" - * @insn_config: driver specific (*insn_config) callback - */ -struct comedi_8254 { - unsigned long iobase; - void __iomem *mmio; - unsigned int iosize; - unsigned int regshift; - unsigned int osc_base; - unsigned int divisor; - unsigned int divisor1; - unsigned int divisor2; - unsigned int next_div; - unsigned int next_div1; - unsigned int next_div2; - unsigned int clock_src[3]; - unsigned int gate_src[3]; - bool busy[3]; - - int (*insn_config)(struct comedi_device *dev, - struct comedi_subdevice *s, - struct comedi_insn *insn, unsigned int *data); -}; - -unsigned int comedi_8254_status(struct comedi_8254 *i8254, - unsigned int counter); -unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter); -void comedi_8254_write(struct comedi_8254 *i8254, - unsigned int counter, unsigned int val); - -int comedi_8254_set_mode(struct comedi_8254 *i8254, - unsigned int counter, unsigned int mode); -int comedi_8254_load(struct comedi_8254 *i8254, - unsigned int counter, unsigned int val, unsigned int mode); - -void comedi_8254_pacer_enable(struct comedi_8254 *i8254, - unsigned int counter1, unsigned int counter2, - bool enable); -void comedi_8254_update_divisors(struct comedi_8254 *i8254); -void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254, - unsigned int *nanosec, unsigned int flags); -void comedi_8254_ns_to_timer(struct comedi_8254 *i8254, - unsigned int *nanosec, unsigned int flags); - -void comedi_8254_set_busy(struct comedi_8254 *i8254, - unsigned int counter, bool busy); - -void comedi_8254_subdevice_init(struct comedi_subdevice *s, - struct comedi_8254 *i8254); - -struct comedi_8254 *comedi_8254_init(unsigned long iobase, - unsigned int osc_base, - unsigned int iosize, - unsigned int regshift); -struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio, - unsigned int osc_base, - unsigned int iosize, - unsigned int regshift); - -#endif /* _COMEDI_8254_H */ diff --git a/drivers/comedi/drivers/das08.c b/drivers/comedi/drivers/das08.c index bab868de2967..f8ab3af2e391 100644 --- a/drivers/comedi/drivers/das08.c +++ b/drivers/comedi/drivers/das08.c @@ -12,8 +12,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "das08.h" /* diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index 338396736936..f6649ffa9670 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -65,9 +65,9 @@ #include #include #include +#include #include "comedi_isadma.h" -#include "comedi_8254.h" #define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */ diff --git a/drivers/comedi/drivers/das16m1.c b/drivers/comedi/drivers/das16m1.c index ea55024d8c5a..275effb77746 100644 --- a/drivers/comedi/drivers/das16m1.c +++ b/drivers/comedi/drivers/das16m1.c @@ -44,8 +44,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * Register map (dev->iobase) diff --git a/drivers/comedi/drivers/das1800.c b/drivers/comedi/drivers/das1800.c index 768803742350..a43d3414a122 100644 --- a/drivers/comedi/drivers/das1800.c +++ b/drivers/comedi/drivers/das1800.c @@ -74,9 +74,9 @@ #include #include #include +#include #include "comedi_isadma.h" -#include "comedi_8254.h" /* misc. defines */ #define DAS1800_SIZE 16 /* uses 16 io addresses */ diff --git a/drivers/comedi/drivers/das6402.c b/drivers/comedi/drivers/das6402.c index d411ab7cf37c..1af394591e74 100644 --- a/drivers/comedi/drivers/das6402.c +++ b/drivers/comedi/drivers/das6402.c @@ -25,8 +25,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/das800.c b/drivers/comedi/drivers/das800.c index c95e0fcb94a4..4ca33f46eaa7 100644 --- a/drivers/comedi/drivers/das800.c +++ b/drivers/comedi/drivers/das800.c @@ -47,8 +47,7 @@ #include #include #include - -#include "comedi_8254.h" +#include #define N_CHAN_AI 8 /* number of analog input channels */ diff --git a/drivers/comedi/drivers/me4000.c b/drivers/comedi/drivers/me4000.c index c5dc8199771f..9aea02b86ed9 100644 --- a/drivers/comedi/drivers/me4000.c +++ b/drivers/comedi/drivers/me4000.c @@ -33,8 +33,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "plx9052.h" #define ME4000_FIRMWARE "me4000_firmware.bin" diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index ce5de58c499f..9942d770add8 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -40,9 +40,9 @@ #include #include #include +#include #include "comedi_isadma.h" -#include "comedi_8254.h" #define A2150_DMA_BUFFER_SIZE 0xff00 /* size in bytes of dma buffer */ diff --git a/drivers/comedi/drivers/ni_at_ao.c b/drivers/comedi/drivers/ni_at_ao.c index a06dfb9da329..9f3147b72aa8 100644 --- a/drivers/comedi/drivers/ni_at_ao.c +++ b/drivers/comedi/drivers/ni_at_ao.c @@ -26,8 +26,7 @@ #include #include - -#include "comedi_8254.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/ni_labpc_common.c b/drivers/comedi/drivers/ni_labpc_common.c index 4a1269aeb371..763249653228 100644 --- a/drivers/comedi/drivers/ni_labpc_common.c +++ b/drivers/comedi/drivers/ni_labpc_common.c @@ -14,8 +14,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "ni_labpc.h" #include "ni_labpc_regs.h" #include "ni_labpc_isadma.h" diff --git a/drivers/comedi/drivers/pcl711.c b/drivers/comedi/drivers/pcl711.c index f1c383bd9d87..05172c553c8a 100644 --- a/drivers/comedi/drivers/pcl711.c +++ b/drivers/comedi/drivers/pcl711.c @@ -30,8 +30,7 @@ #include #include #include - -#include "comedi_8254.h" +#include /* * I/O port register map diff --git a/drivers/comedi/drivers/pcl812.c b/drivers/comedi/drivers/pcl812.c index f00976ddfc2a..790f54476a91 100644 --- a/drivers/comedi/drivers/pcl812.c +++ b/drivers/comedi/drivers/pcl812.c @@ -115,9 +115,9 @@ #include #include #include +#include #include "comedi_isadma.h" -#include "comedi_8254.h" /* * Register I/O map diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index c5acdc8913f8..77b30246d966 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -36,9 +36,9 @@ #include #include #include +#include #include "comedi_isadma.h" -#include "comedi_8254.h" /* * Register I/O map diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index 20fcd6d588f8..e5b7793cce05 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -98,9 +98,9 @@ #include #include #include +#include #include "comedi_isadma.h" -#include "comedi_8254.h" /* * Register I/O map diff --git a/drivers/comedi/drivers/rtd520.c b/drivers/comedi/drivers/rtd520.c index ee5bca2b1c09..7e0ec1a2a2ca 100644 --- a/drivers/comedi/drivers/rtd520.c +++ b/drivers/comedi/drivers/rtd520.c @@ -86,8 +86,8 @@ #include #include #include +#include -#include "comedi_8254.h" #include "plx9080.h" /* diff --git a/include/linux/comedi/comedi_8254.h b/include/linux/comedi/comedi_8254.h new file mode 100644 index 000000000000..d8264417e53c --- /dev/null +++ b/include/linux/comedi/comedi_8254.h @@ -0,0 +1,134 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * comedi_8254.h + * Generic 8254 timer/counter support + * Copyright (C) 2014 H Hartley Sweeten + * + * COMEDI - Linux Control and Measurement Device Interface + * Copyright (C) 2000 David A. Schleef + */ + +#ifndef _COMEDI_8254_H +#define _COMEDI_8254_H + +#include + +struct comedi_device; +struct comedi_insn; +struct comedi_subdevice; + +/* + * Common oscillator base values in nanoseconds + */ +#define I8254_OSC_BASE_10MHZ 100 +#define I8254_OSC_BASE_5MHZ 200 +#define I8254_OSC_BASE_4MHZ 250 +#define I8254_OSC_BASE_2MHZ 500 +#define I8254_OSC_BASE_1MHZ 1000 +#define I8254_OSC_BASE_100KHZ 10000 +#define I8254_OSC_BASE_10KHZ 100000 +#define I8254_OSC_BASE_1KHZ 1000000 + +/* + * I/O access size used to read/write registers + */ +#define I8254_IO8 1 +#define I8254_IO16 2 +#define I8254_IO32 4 + +/* + * Register map for generic 8254 timer (I8254_IO8 with 0 regshift) + */ +#define I8254_COUNTER0_REG 0x00 +#define I8254_COUNTER1_REG 0x01 +#define I8254_COUNTER2_REG 0x02 +#define I8254_CTRL_REG 0x03 +#define I8254_CTRL_SEL_CTR(x) ((x) << 6) +#define I8254_CTRL_READBACK(x) (I8254_CTRL_SEL_CTR(3) | BIT(x)) +#define I8254_CTRL_READBACK_COUNT I8254_CTRL_READBACK(4) +#define I8254_CTRL_READBACK_STATUS I8254_CTRL_READBACK(5) +#define I8254_CTRL_READBACK_SEL_CTR(x) (2 << (x)) +#define I8254_CTRL_RW(x) (((x) & 0x3) << 4) +#define I8254_CTRL_LATCH I8254_CTRL_RW(0) +#define I8254_CTRL_LSB_ONLY I8254_CTRL_RW(1) +#define I8254_CTRL_MSB_ONLY I8254_CTRL_RW(2) +#define I8254_CTRL_LSB_MSB I8254_CTRL_RW(3) + +/* counter maps zero to 0x10000 */ +#define I8254_MAX_COUNT 0x10000 + +/** + * struct comedi_8254 - private data used by this module + * @iobase: PIO base address of the registers (in/out) + * @mmio: MMIO base address of the registers (read/write) + * @iosize: I/O size used to access the registers (b/w/l) + * @regshift: register gap shift + * @osc_base: cascaded oscillator speed in ns + * @divisor: divisor for single counter + * @divisor1: divisor loaded into first cascaded counter + * @divisor2: divisor loaded into second cascaded counter + * #next_div: next divisor for single counter + * @next_div1: next divisor to use for first cascaded counter + * @next_div2: next divisor to use for second cascaded counter + * @clock_src; current clock source for each counter (driver specific) + * @gate_src; current gate source for each counter (driver specific) + * @busy: flags used to indicate that a counter is "busy" + * @insn_config: driver specific (*insn_config) callback + */ +struct comedi_8254 { + unsigned long iobase; + void __iomem *mmio; + unsigned int iosize; + unsigned int regshift; + unsigned int osc_base; + unsigned int divisor; + unsigned int divisor1; + unsigned int divisor2; + unsigned int next_div; + unsigned int next_div1; + unsigned int next_div2; + unsigned int clock_src[3]; + unsigned int gate_src[3]; + bool busy[3]; + + int (*insn_config)(struct comedi_device *dev, + struct comedi_subdevice *s, + struct comedi_insn *insn, unsigned int *data); +}; + +unsigned int comedi_8254_status(struct comedi_8254 *i8254, + unsigned int counter); +unsigned int comedi_8254_read(struct comedi_8254 *i8254, unsigned int counter); +void comedi_8254_write(struct comedi_8254 *i8254, + unsigned int counter, unsigned int val); + +int comedi_8254_set_mode(struct comedi_8254 *i8254, + unsigned int counter, unsigned int mode); +int comedi_8254_load(struct comedi_8254 *i8254, + unsigned int counter, unsigned int val, unsigned int mode); + +void comedi_8254_pacer_enable(struct comedi_8254 *i8254, + unsigned int counter1, unsigned int counter2, + bool enable); +void comedi_8254_update_divisors(struct comedi_8254 *i8254); +void comedi_8254_cascade_ns_to_timer(struct comedi_8254 *i8254, + unsigned int *nanosec, unsigned int flags); +void comedi_8254_ns_to_timer(struct comedi_8254 *i8254, + unsigned int *nanosec, unsigned int flags); + +void comedi_8254_set_busy(struct comedi_8254 *i8254, + unsigned int counter, bool busy); + +void comedi_8254_subdevice_init(struct comedi_subdevice *s, + struct comedi_8254 *i8254); + +struct comedi_8254 *comedi_8254_init(unsigned long iobase, + unsigned int osc_base, + unsigned int iosize, + unsigned int regshift); +struct comedi_8254 *comedi_8254_mm_init(void __iomem *mmio, + unsigned int osc_base, + unsigned int iosize, + unsigned int regshift); + +#endif /* _COMEDI_8254_H */ -- cgit v1.2.3 From fe7a4f5b9548456246ffda143bab59922acda9fd Mon Sep 17 00:00:00 2001 From: Ian Abbott Date: Wed, 17 Nov 2021 12:06:03 +0000 Subject: comedi: Move "comedi_isadma.h" to Some of the header files in "drivers/comedi/drivers/" are common enough to be useful to out-of-tree comedi driver modules. Using them for out-of-tree module builds is hampered by the headers being outside the "include/" directory so it is desirable to move them. There are about a half a dozen or so Comedi device drivers that use the "comedi_isadma" module to add ISA DMA support. The macros and declarations to use that module are in the "comedi_isadma.h" header file in the comedi "drivers" directory. Move it into "include/linux/comedi/". Signed-off-by: Ian Abbott Link: https://lore.kernel.org/r/20211117120604.117740-6-abbotti@mev.co.uk Signed-off-by: Greg Kroah-Hartman --- drivers/comedi/drivers/comedi_isadma.c | 3 +- drivers/comedi/drivers/comedi_isadma.h | 114 ------------------------------- drivers/comedi/drivers/das16.c | 3 +- drivers/comedi/drivers/das1800.c | 3 +- drivers/comedi/drivers/dt282x.c | 3 +- drivers/comedi/drivers/ni_at_a2150.c | 3 +- drivers/comedi/drivers/ni_labpc_isadma.c | 2 +- drivers/comedi/drivers/pcl812.c | 3 +- drivers/comedi/drivers/pcl816.c | 3 +- drivers/comedi/drivers/pcl818.c | 3 +- include/linux/comedi/comedi_isadma.h | 114 +++++++++++++++++++++++++++++++ 11 files changed, 123 insertions(+), 131 deletions(-) delete mode 100644 drivers/comedi/drivers/comedi_isadma.h create mode 100644 include/linux/comedi/comedi_isadma.h diff --git a/drivers/comedi/drivers/comedi_isadma.c b/drivers/comedi/drivers/comedi_isadma.c index 63457bd4ff78..700982464c53 100644 --- a/drivers/comedi/drivers/comedi_isadma.c +++ b/drivers/comedi/drivers/comedi_isadma.c @@ -10,8 +10,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include /** * comedi_isadma_program - program and enable an ISA DMA transfer diff --git a/drivers/comedi/drivers/comedi_isadma.h b/drivers/comedi/drivers/comedi_isadma.h deleted file mode 100644 index 9d2b12db7e6e..000000000000 --- a/drivers/comedi/drivers/comedi_isadma.h +++ /dev/null @@ -1,114 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * COMEDI ISA DMA support functions - * Copyright (c) 2014 H Hartley Sweeten - */ - -#ifndef _COMEDI_ISADMA_H -#define _COMEDI_ISADMA_H - -#include - -struct comedi_device; -struct device; - -/* - * These are used to avoid issues when and the DMA_MODE_ - * defines are not available. - */ -#define COMEDI_ISADMA_READ 0 -#define COMEDI_ISADMA_WRITE 1 - -/** - * struct comedi_isadma_desc - cookie for ISA DMA - * @virt_addr: virtual address of buffer - * @hw_addr: hardware (bus) address of buffer - * @chan: DMA channel - * @maxsize: allocated size of buffer (in bytes) - * @size: transfer size (in bytes) - * @mode: DMA_MODE_READ or DMA_MODE_WRITE - */ -struct comedi_isadma_desc { - void *virt_addr; - dma_addr_t hw_addr; - unsigned int chan; - unsigned int maxsize; - unsigned int size; - char mode; -}; - -/** - * struct comedi_isadma - ISA DMA data - * @dev: device to allocate non-coherent memory for - * @desc: cookie for each DMA buffer - * @n_desc: the number of cookies - * @cur_dma: the current cookie in use - * @chan: the first DMA channel requested - * @chan2: the second DMA channel requested - */ -struct comedi_isadma { - struct device *dev; - struct comedi_isadma_desc *desc; - int n_desc; - int cur_dma; - unsigned int chan; - unsigned int chan2; -}; - -#if IS_ENABLED(CONFIG_ISA_DMA_API) - -void comedi_isadma_program(struct comedi_isadma_desc *desc); -unsigned int comedi_isadma_disable(unsigned int dma_chan); -unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan, - unsigned int size); -unsigned int comedi_isadma_poll(struct comedi_isadma *dma); -void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, char dma_dir); - -struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev, - int n_desc, unsigned int dma_chan1, - unsigned int dma_chan2, - unsigned int maxsize, char dma_dir); -void comedi_isadma_free(struct comedi_isadma *dma); - -#else /* !IS_ENABLED(CONFIG_ISA_DMA_API) */ - -static inline void comedi_isadma_program(struct comedi_isadma_desc *desc) -{ -} - -static inline unsigned int comedi_isadma_disable(unsigned int dma_chan) -{ - return 0; -} - -static inline unsigned int -comedi_isadma_disable_on_sample(unsigned int dma_chan, unsigned int size) -{ - return 0; -} - -static inline unsigned int comedi_isadma_poll(struct comedi_isadma *dma) -{ - return 0; -} - -static inline void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, - char dma_dir) -{ -} - -static inline struct comedi_isadma * -comedi_isadma_alloc(struct comedi_device *dev, int n_desc, - unsigned int dma_chan1, unsigned int dma_chan2, - unsigned int maxsize, char dma_dir) -{ - return NULL; -} - -static inline void comedi_isadma_free(struct comedi_isadma *dma) -{ -} - -#endif /* !IS_ENABLED(CONFIG_ISA_DMA_API) */ - -#endif /* #ifndef _COMEDI_ISADMA_H */ diff --git a/drivers/comedi/drivers/das16.c b/drivers/comedi/drivers/das16.c index f6649ffa9670..937a69ce0977 100644 --- a/drivers/comedi/drivers/das16.c +++ b/drivers/comedi/drivers/das16.c @@ -66,8 +66,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include #define DAS16_DMA_SIZE 0xff00 /* size in bytes of allocated dma buffer */ diff --git a/drivers/comedi/drivers/das1800.c b/drivers/comedi/drivers/das1800.c index a43d3414a122..f09608c0f4ff 100644 --- a/drivers/comedi/drivers/das1800.c +++ b/drivers/comedi/drivers/das1800.c @@ -75,8 +75,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include /* misc. defines */ #define DAS1800_SIZE 16 /* uses 16 io addresses */ diff --git a/drivers/comedi/drivers/dt282x.c b/drivers/comedi/drivers/dt282x.c index 078f8fba7183..4ae80e6c7266 100644 --- a/drivers/comedi/drivers/dt282x.c +++ b/drivers/comedi/drivers/dt282x.c @@ -52,8 +52,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include /* * Register map diff --git a/drivers/comedi/drivers/ni_at_a2150.c b/drivers/comedi/drivers/ni_at_a2150.c index 9942d770add8..df8d219e6723 100644 --- a/drivers/comedi/drivers/ni_at_a2150.c +++ b/drivers/comedi/drivers/ni_at_a2150.c @@ -41,8 +41,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include #define A2150_DMA_BUFFER_SIZE 0xff00 /* size in bytes of dma buffer */ diff --git a/drivers/comedi/drivers/ni_labpc_isadma.c b/drivers/comedi/drivers/ni_labpc_isadma.c index dd37ec0d9b15..0652ca8345b6 100644 --- a/drivers/comedi/drivers/ni_labpc_isadma.c +++ b/drivers/comedi/drivers/ni_labpc_isadma.c @@ -11,8 +11,8 @@ #include #include #include +#include -#include "comedi_isadma.h" #include "ni_labpc.h" #include "ni_labpc_regs.h" #include "ni_labpc_isadma.h" diff --git a/drivers/comedi/drivers/pcl812.c b/drivers/comedi/drivers/pcl812.c index 790f54476a91..70dbc129fcf5 100644 --- a/drivers/comedi/drivers/pcl812.c +++ b/drivers/comedi/drivers/pcl812.c @@ -116,8 +116,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/pcl816.c b/drivers/comedi/drivers/pcl816.c index 77b30246d966..a5e5320be648 100644 --- a/drivers/comedi/drivers/pcl816.c +++ b/drivers/comedi/drivers/pcl816.c @@ -37,8 +37,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include /* * Register I/O map diff --git a/drivers/comedi/drivers/pcl818.c b/drivers/comedi/drivers/pcl818.c index e5b7793cce05..29e503de8267 100644 --- a/drivers/comedi/drivers/pcl818.c +++ b/drivers/comedi/drivers/pcl818.c @@ -99,8 +99,7 @@ #include #include #include - -#include "comedi_isadma.h" +#include /* * Register I/O map diff --git a/include/linux/comedi/comedi_isadma.h b/include/linux/comedi/comedi_isadma.h new file mode 100644 index 000000000000..9d2b12db7e6e --- /dev/null +++ b/include/linux/comedi/comedi_isadma.h @@ -0,0 +1,114 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * COMEDI ISA DMA support functions + * Copyright (c) 2014 H Hartley Sweeten + */ + +#ifndef _COMEDI_ISADMA_H +#define _COMEDI_ISADMA_H + +#include + +struct comedi_device; +struct device; + +/* + * These are used to avoid issues when and the DMA_MODE_ + * defines are not available. + */ +#define COMEDI_ISADMA_READ 0 +#define COMEDI_ISADMA_WRITE 1 + +/** + * struct comedi_isadma_desc - cookie for ISA DMA + * @virt_addr: virtual address of buffer + * @hw_addr: hardware (bus) address of buffer + * @chan: DMA channel + * @maxsize: allocated size of buffer (in bytes) + * @size: transfer size (in bytes) + * @mode: DMA_MODE_READ or DMA_MODE_WRITE + */ +struct comedi_isadma_desc { + void *virt_addr; + dma_addr_t hw_addr; + unsigned int chan; + unsigned int maxsize; + unsigned int size; + char mode; +}; + +/** + * struct comedi_isadma - ISA DMA data + * @dev: device to allocate non-coherent memory for + * @desc: cookie for each DMA buffer + * @n_desc: the number of cookies + * @cur_dma: the current cookie in use + * @chan: the first DMA channel requested + * @chan2: the second DMA channel requested + */ +struct comedi_isadma { + struct device *dev; + struct comedi_isadma_desc *desc; + int n_desc; + int cur_dma; + unsigned int chan; + unsigned int chan2; +}; + +#if IS_ENABLED(CONFIG_ISA_DMA_API) + +void comedi_isadma_program(struct comedi_isadma_desc *desc); +unsigned int comedi_isadma_disable(unsigned int dma_chan); +unsigned int comedi_isadma_disable_on_sample(unsigned int dma_chan, + unsigned int size); +unsigned int comedi_isadma_poll(struct comedi_isadma *dma); +void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, char dma_dir); + +struct comedi_isadma *comedi_isadma_alloc(struct comedi_device *dev, + int n_desc, unsigned int dma_chan1, + unsigned int dma_chan2, + unsigned int maxsize, char dma_dir); +void comedi_isadma_free(struct comedi_isadma *dma); + +#else /* !IS_ENABLED(CONFIG_ISA_DMA_API) */ + +static inline void comedi_isadma_program(struct comedi_isadma_desc *desc) +{ +} + +static inline unsigned int comedi_isadma_disable(unsigned int dma_chan) +{ + return 0; +} + +static inline unsigned int +comedi_isadma_disable_on_sample(unsigned int dma_chan, unsigned int size) +{ + return 0; +} + +static inline unsigned int comedi_isadma_poll(struct comedi_isadma *dma) +{ + return 0; +} + +static inline void comedi_isadma_set_mode(struct comedi_isadma_desc *desc, + char dma_dir) +{ +} + +static inline struct comedi_isadma * +comedi_isadma_alloc(struct comedi_device *dev, int n_desc, + unsigned int dma_chan1, unsigned int dma_chan2, + unsigned int maxsize, char dma_dir) +{ + return NULL; +} + +static inline void comedi_isadma_free(struct comedi_isadma *dma) +{ +} + +#endif /* !IS_ENABLED(CONFIG_ISA_DMA_API) */ + +#endif /* #ifndef _COMEDI_ISADMA_H */ -- cgit v1.2.3 From b6379e73add8dc56ff2b7e5d88a8dce89a8ace56 Mon Sep 17 00:00:00 2001 From: Zhaoyu Liu Date: Wed, 3 Nov 2021 23:22:42 +0800 Subject: scripts/tags: add space regexs to all regex_c When "make tags", it prompts a warning: ctags: Warning: drivers/pci/controller/pcie-apple.c:150: null expansion of name pattern "\1" The reason is that there is an indentation beside arguments of DECLARE_BITMAP, but it can parsed normally by gtags. It's also allowed in C. Regex [:space:] can match any white space character, so it's a better approach to add it to each item in regex_c. Suggested-by: Marc Zyngier Signed-off-by: Zhaoyu Liu Link: https://lore.kernel.org/r/20211103152234.GA23295@pc Signed-off-by: Greg Kroah-Hartman --- scripts/tags.sh | 126 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 63 insertions(+), 63 deletions(-) diff --git a/scripts/tags.sh b/scripts/tags.sh index b24bfaec6290..16d475b3e203 100755 --- a/scripts/tags.sh +++ b/scripts/tags.sh @@ -140,71 +140,71 @@ dogtags() # - etags regular expressions have to match at the start of a line; # a ^[^#] is prepended by setup_regex unless an anchor is already present regex_asm=( - '/^\(ENTRY\|_GLOBAL\)(\([[:alnum:]_\\]*\)).*/\2/' + '/^\(ENTRY\|_GLOBAL\)([[:space:]]*\([[:alnum:]_\\]*\)).*/\2/' ) regex_c=( - '/^SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/sys_\1/' - '/^BPF_CALL_[0-9](\([[:alnum:]_]*\).*/\1/' - '/^COMPAT_SYSCALL_DEFINE[0-9](\([[:alnum:]_]*\).*/compat_sys_\1/' - '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1/' - '/^TRACE_EVENT(\([[:alnum:]_]*\).*/trace_\1_rcuidle/' - '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1/' - '/^DEFINE_EVENT([^,)]*, *\([[:alnum:]_]*\).*/trace_\1_rcuidle/' - '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/get_\1_slot/' - '/^DEFINE_INSN_CACHE_OPS(\([[:alnum:]_]*\).*/free_\1_slot/' - '/^PAGEFLAG(\([[:alnum:]_]*\).*/Page\1/' - '/^PAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/' - '/^PAGEFLAG(\([[:alnum:]_]*\).*/ClearPage\1/' - '/^TESTSETFLAG(\([[:alnum:]_]*\).*/TestSetPage\1/' - '/^TESTPAGEFLAG(\([[:alnum:]_]*\).*/Page\1/' - '/^SETPAGEFLAG(\([[:alnum:]_]*\).*/SetPage\1/' - '/\<__SETPAGEFLAG(\([[:alnum:]_]*\).*/__SetPage\1/' - '/\ Date: Wed, 10 Nov 2021 23:33:42 +0000 Subject: speakup: remove redundant assignment of variable i The variable i is being initialized a value that is never read, it is re-assigned later on in a for-loop. The assignment is redundant and can be removed. Reviewed-by: Samuel Thibault Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20211110233342.1372516-1-colin.i.king@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/accessibility/speakup/speakup_acntpc.c | 2 +- drivers/accessibility/speakup/speakup_dtlk.c | 2 +- drivers/accessibility/speakup/speakup_keypc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/accessibility/speakup/speakup_acntpc.c b/drivers/accessibility/speakup/speakup_acntpc.c index c1ec087dca13..023172ca22ef 100644 --- a/drivers/accessibility/speakup/speakup_acntpc.c +++ b/drivers/accessibility/speakup/speakup_acntpc.c @@ -247,7 +247,7 @@ static void synth_flush(struct spk_synth *synth) static int synth_probe(struct spk_synth *synth) { unsigned int port_val = 0; - int i = 0; + int i; pr_info("Probing for %s.\n", synth->long_name); if (port_forced) { diff --git a/drivers/accessibility/speakup/speakup_dtlk.c b/drivers/accessibility/speakup/speakup_dtlk.c index 92838d3ae9eb..a9dd5c45d237 100644 --- a/drivers/accessibility/speakup/speakup_dtlk.c +++ b/drivers/accessibility/speakup/speakup_dtlk.c @@ -316,7 +316,7 @@ static struct synth_settings *synth_interrogate(struct spk_synth *synth) static int synth_probe(struct spk_synth *synth) { unsigned int port_val = 0; - int i = 0; + int i; struct synth_settings *sp; pr_info("Probing for DoubleTalk.\n"); diff --git a/drivers/accessibility/speakup/speakup_keypc.c b/drivers/accessibility/speakup/speakup_keypc.c index 311f4aa0be22..1618be87bff1 100644 --- a/drivers/accessibility/speakup/speakup_keypc.c +++ b/drivers/accessibility/speakup/speakup_keypc.c @@ -254,7 +254,7 @@ static void synth_flush(struct spk_synth *synth) static int synth_probe(struct spk_synth *synth) { unsigned int port_val = 0; - int i = 0; + int i; pr_info("Probing for %s.\n", synth->long_name); if (port_forced) { -- cgit v1.2.3 From cd455ebb748c4e198c8158e5d61b3034bf10f22b Mon Sep 17 00:00:00 2001 From: Jing Yao Date: Wed, 10 Nov 2021 02:53:41 +0000 Subject: most: usb: replace snprintf in show functions with sysfs_emit coccicheck complains about the use of snprintf() in sysfs show functions: WARNING use scnprintf or sprintf Use sysfs_emit instead of scnprintf, snprintf or sprintf makes more sense. Reported-by: Zeal Robot Signed-off-by: Jing Yao Link: https://lore.kernel.org/r/20211110025341.136194-1-yao.jing2@zte.com.cn Signed-off-by: Greg Kroah-Hartman --- drivers/most/most_usb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/most/most_usb.c b/drivers/most/most_usb.c index acabb7715b42..73258b24fea7 100644 --- a/drivers/most/most_usb.c +++ b/drivers/most/most_usb.c @@ -831,7 +831,7 @@ static ssize_t value_show(struct device *dev, struct device_attribute *attr, int err; if (sysfs_streq(name, "arb_address")) - return snprintf(buf, PAGE_SIZE, "%04x\n", dci_obj->reg_addr); + return sysfs_emit(buf, "%04x\n", dci_obj->reg_addr); if (sysfs_streq(name, "arb_value")) reg_addr = dci_obj->reg_addr; @@ -843,7 +843,7 @@ static ssize_t value_show(struct device *dev, struct device_attribute *attr, if (err < 0) return err; - return snprintf(buf, PAGE_SIZE, "%04x\n", val); + return sysfs_emit(buf, "%04x\n", val); } static ssize_t value_store(struct device *dev, struct device_attribute *attr, -- cgit v1.2.3 From da7000e8b83bb8dbdf8f01fd3fe4c4190974bfdc Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 24 Nov 2021 20:00:33 +0000 Subject: coresight: configuration: Update API to introduce load owner concept Update the existing load API to introduce a "load owner" concept. This allows the tracking of the loaded configurations and features against the loading owner type, to allow later unload according to owner. A list of loaded configurations by owner is created. The load owner infrastructure will be used in following patches to implement dynanic load and unload, alongside dependency tracking. Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211124200038.28662-2-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- .../hwtracing/coresight/coresight-cfg-preload.c | 9 +++++-- drivers/hwtracing/coresight/coresight-config.h | 5 +++- drivers/hwtracing/coresight/coresight-syscfg.c | 21 +++++++++++++--- drivers/hwtracing/coresight/coresight-syscfg.h | 29 ++++++++++++++++++++-- 4 files changed, 56 insertions(+), 8 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-cfg-preload.c b/drivers/hwtracing/coresight/coresight-cfg-preload.c index 751af3710d56..e237a4edfa09 100644 --- a/drivers/hwtracing/coresight/coresight-cfg-preload.c +++ b/drivers/hwtracing/coresight/coresight-cfg-preload.c @@ -24,8 +24,13 @@ static struct cscfg_config_desc *preload_cfgs[] = { NULL }; +static struct cscfg_load_owner_info preload_owner = { + .type = CSCFG_OWNER_PRELOAD, +}; + /* preload called on initialisation */ -int cscfg_preload(void) +int cscfg_preload(void *owner_handle) { - return cscfg_load_config_sets(preload_cfgs, preload_feats); + preload_owner.owner_handle = owner_handle; + return cscfg_load_config_sets(preload_cfgs, preload_feats, &preload_owner); } diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 25eb6c632692..6e0d43901d66 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -97,6 +97,7 @@ struct cscfg_regval_desc { * @params_desc: array of parameters used. * @nr_regs: number of registers used. * @regs_desc: array of registers used. + * @load_owner: handle to load owner for dynamic load and unload of features. */ struct cscfg_feature_desc { const char *name; @@ -107,6 +108,7 @@ struct cscfg_feature_desc { struct cscfg_parameter_desc *params_desc; int nr_regs; struct cscfg_regval_desc *regs_desc; + void *load_owner; }; /** @@ -128,7 +130,7 @@ struct cscfg_feature_desc { * @presets: Array of preset values. * @event_ea: Extended attribute for perf event value * @active_cnt: ref count for activate on this configuration. - * + * @load_owner: handle to load owner for dynamic load and unload of configs. */ struct cscfg_config_desc { const char *name; @@ -141,6 +143,7 @@ struct cscfg_config_desc { const u64 *presets; /* nr_presets * nr_total_params */ struct dev_ext_attribute *event_ea; atomic_t active_cnt; + void *load_owner; }; /** diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 43054568430f..021f50949d7b 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -361,13 +361,22 @@ unlock_exit: * descriptors and load into the system. * Features are loaded first to ensure configuration dependencies can be met. * + * To facilitate dynamic loading and unloading, features and configurations + * have a "load_owner", to allow later unload by the same owner. An owner may + * be a loadable module or configuration dynamically created via configfs. + * As later loaded configurations can use earlier loaded features, creating load + * dependencies, a load order list is maintained. Unload is strictly in the + * reverse order to load. + * * @config_descs: 0 terminated array of configuration descriptors. * @feat_descs: 0 terminated array of feature descriptors. + * @owner_info: Information on the owner of this set. */ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, - struct cscfg_feature_desc **feat_descs) + struct cscfg_feature_desc **feat_descs, + struct cscfg_load_owner_info *owner_info) { - int err, i = 0; + int err = 0, i = 0; mutex_lock(&cscfg_mutex); @@ -382,6 +391,7 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, feat_descs[i]->name); goto exit_unlock; } + feat_descs[i]->load_owner = owner_info; i++; } } @@ -398,10 +408,14 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, config_descs[i]->name); goto exit_unlock; } + config_descs[i]->load_owner = owner_info; i++; } } + /* add the load owner to the load order list */ + list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list); + exit_unlock: mutex_unlock(&cscfg_mutex); return err; @@ -827,10 +841,11 @@ int __init cscfg_init(void) INIT_LIST_HEAD(&cscfg_mgr->csdev_desc_list); INIT_LIST_HEAD(&cscfg_mgr->feat_desc_list); INIT_LIST_HEAD(&cscfg_mgr->config_desc_list); + INIT_LIST_HEAD(&cscfg_mgr->load_order_list); atomic_set(&cscfg_mgr->sys_active_cnt, 0); /* preload built-in configurations */ - err = cscfg_preload(); + err = cscfg_preload(THIS_MODULE); if (err) goto exit_err; diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 8d018efd6ead..08067e89edcf 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -25,6 +25,7 @@ * @csdev_desc_list: List of coresight devices registered with the configuration manager. * @feat_desc_list: List of feature descriptors to load into registered devices. * @config_desc_list: List of system configuration descriptors to load into registered devices. + * @load_order_list: Ordered list of owners for dynamically loaded configurations. * @sys_active_cnt: Total number of active config descriptor references. * @cfgfs_subsys: configfs subsystem used to manage configurations. */ @@ -33,6 +34,7 @@ struct cscfg_manager { struct list_head csdev_desc_list; struct list_head feat_desc_list; struct list_head config_desc_list; + struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys; }; @@ -56,10 +58,32 @@ struct cscfg_registered_csdev { struct list_head item; }; +/* owner types for loading and unloading of config and feature sets */ +enum cscfg_load_owner_type { + CSCFG_OWNER_PRELOAD, +}; + +/** + * Load item - item to add to the load order list allowing dynamic load and + * unload of configurations and features. Caller loading a config + * set provides a context handle for unload. API ensures that + * items unloaded strictly in reverse order from load to ensure + * dependencies are respected. + * + * @item: list entry for load order list. + * @type: type of owner - allows interpretation of owner_handle. + * @owner_handle: load context - handle for owner of loaded configs. + */ +struct cscfg_load_owner_info { + struct list_head item; + int type; + void *owner_handle; +}; + /* internal core operations for cscfg */ int __init cscfg_init(void); void cscfg_exit(void); -int cscfg_preload(void); +int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); @@ -67,7 +91,8 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, /* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs, - struct cscfg_feature_desc **feat_descs); + struct cscfg_feature_desc **feat_descs, + struct cscfg_load_owner_info *owner_info); int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags, struct cscfg_csdev_feat_ops *ops); void cscfg_unregister_csdev(struct coresight_device *csdev); -- cgit v1.2.3 From 02bd588e12df405bdf55244708151b7f238b79ba Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 24 Nov 2021 20:00:34 +0000 Subject: coresight: configuration: Update API to permit dynamic load/unload Expand the configuration API to allow dynamic runtime load and unload of configurations and features. On load, configurations and features are tagged with a "load owner" that is used to determine sets that were loaded together in a given API call. To unload the API uses the load owner to unload all elements previously loaded by that owner. The API also records the order in which different owners loaded their elements into the system. Later loading configurations can use previously loaded features, creating load dependencies. Therefore unload is enforced strictly in the reverse order to load. A load owner will be an additional loadable module, or a configuration loaded via configfs. Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211124200038.28662-3-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-config.h | 4 + .../coresight/coresight-syscfg-configfs.c | 20 ++++ .../coresight/coresight-syscfg-configfs.h | 2 + drivers/hwtracing/coresight/coresight-syscfg.c | 133 +++++++++++++++++++++ drivers/hwtracing/coresight/coresight-syscfg.h | 1 + 5 files changed, 160 insertions(+) diff --git a/drivers/hwtracing/coresight/coresight-config.h b/drivers/hwtracing/coresight/coresight-config.h index 6e0d43901d66..9bd44b940add 100644 --- a/drivers/hwtracing/coresight/coresight-config.h +++ b/drivers/hwtracing/coresight/coresight-config.h @@ -98,6 +98,7 @@ struct cscfg_regval_desc { * @nr_regs: number of registers used. * @regs_desc: array of registers used. * @load_owner: handle to load owner for dynamic load and unload of features. + * @fs_group: reference to configfs group for dynamic unload. */ struct cscfg_feature_desc { const char *name; @@ -109,6 +110,7 @@ struct cscfg_feature_desc { int nr_regs; struct cscfg_regval_desc *regs_desc; void *load_owner; + struct config_group *fs_group; }; /** @@ -131,6 +133,7 @@ struct cscfg_feature_desc { * @event_ea: Extended attribute for perf event value * @active_cnt: ref count for activate on this configuration. * @load_owner: handle to load owner for dynamic load and unload of configs. + * @fs_group: reference to configfs group for dynamic unload. */ struct cscfg_config_desc { const char *name; @@ -144,6 +147,7 @@ struct cscfg_config_desc { struct dev_ext_attribute *event_ea; atomic_t active_cnt; void *load_owner; + struct config_group *fs_group; }; /** diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index c547816b9000..345a62f1b728 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -334,9 +334,19 @@ int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_configs_grp, new_group); + if (!err) + config_desc->fs_group = new_group; return err; } +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc) +{ + if (config_desc->fs_group) { + configfs_unregister_group(config_desc->fs_group); + config_desc->fs_group = NULL; + } +} + static struct config_item_type cscfg_features_type = { .ct_owner = THIS_MODULE, }; @@ -358,9 +368,19 @@ int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc) if (IS_ERR(new_group)) return PTR_ERR(new_group); err = configfs_register_group(&cscfg_features_grp, new_group); + if (!err) + feat_desc->fs_group = new_group; return err; } +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc) +{ + if (feat_desc->fs_group) { + configfs_unregister_group(feat_desc->fs_group); + feat_desc->fs_group = NULL; + } +} + int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr) { struct configfs_subsystem *subsys; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index 7d6ffe35ca4c..ea1e54d29f7f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -41,5 +41,7 @@ int cscfg_configfs_init(struct cscfg_manager *cscfg_mgr); void cscfg_configfs_release(struct cscfg_manager *cscfg_mgr); int cscfg_configfs_add_config(struct cscfg_config_desc *config_desc); int cscfg_configfs_add_feature(struct cscfg_feature_desc *feat_desc); +void cscfg_configfs_del_config(struct cscfg_config_desc *config_desc); +void cscfg_configfs_del_feature(struct cscfg_feature_desc *feat_desc); #endif /* CORESIGHT_SYSCFG_CONFIGFS_H */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 021f50949d7b..8bff7da1aab2 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -250,6 +250,13 @@ static int cscfg_check_feat_for_cfg(struct cscfg_config_desc *config_desc) static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) { int err; + struct cscfg_feature_desc *feat_desc_exist; + + /* new feature must have unique name */ + list_for_each_entry(feat_desc_exist, &cscfg_mgr->feat_desc_list, item) { + if (!strcmp(feat_desc_exist->name, feat_desc->name)) + return -EEXIST; + } /* add feature to any matching registered devices */ err = cscfg_add_feat_to_csdevs(feat_desc); @@ -267,6 +274,13 @@ static int cscfg_load_feat(struct cscfg_feature_desc *feat_desc) static int cscfg_load_config(struct cscfg_config_desc *config_desc) { int err; + struct cscfg_config_desc *config_desc_exist; + + /* new configuration must have a unique name */ + list_for_each_entry(config_desc_exist, &cscfg_mgr->config_desc_list, item) { + if (!strcmp(config_desc_exist->name, config_desc->name)) + return -EEXIST; + } /* validate features are present */ err = cscfg_check_feat_for_cfg(config_desc); @@ -354,6 +368,72 @@ unlock_exit: return err; } +static void cscfg_remove_owned_csdev_configs(struct coresight_device *csdev, void *load_owner) +{ + struct cscfg_config_csdev *config_csdev, *tmp; + + if (list_empty(&csdev->config_csdev_list)) + return; + + list_for_each_entry_safe(config_csdev, tmp, &csdev->config_csdev_list, node) { + if (config_csdev->config_desc->load_owner == load_owner) + list_del(&config_csdev->node); + } +} + +static void cscfg_remove_owned_csdev_features(struct coresight_device *csdev, void *load_owner) +{ + struct cscfg_feature_csdev *feat_csdev, *tmp; + + if (list_empty(&csdev->feature_csdev_list)) + return; + + list_for_each_entry_safe(feat_csdev, tmp, &csdev->feature_csdev_list, node) { + if (feat_csdev->feat_desc->load_owner == load_owner) + list_del(&feat_csdev->node); + } +} + +/* + * removal is relatively easy - just remove from all lists, anything that + * matches the owner. Memory for the descriptors will be managed by the owner, + * memory for the csdev items is devm_ allocated with the individual csdev + * devices. + */ +static void cscfg_unload_owned_cfgs_feats(void *load_owner) +{ + struct cscfg_config_desc *config_desc, *cfg_tmp; + struct cscfg_feature_desc *feat_desc, *feat_tmp; + struct cscfg_registered_csdev *csdev_item; + + /* remove from each csdev instance feature and config lists */ + list_for_each_entry(csdev_item, &cscfg_mgr->csdev_desc_list, item) { + /* + * for each csdev, check the loaded lists and remove if + * referenced descriptor is owned + */ + cscfg_remove_owned_csdev_configs(csdev_item->csdev, load_owner); + cscfg_remove_owned_csdev_features(csdev_item->csdev, load_owner); + } + + /* remove from the config descriptor lists */ + list_for_each_entry_safe(config_desc, cfg_tmp, &cscfg_mgr->config_desc_list, item) { + if (config_desc->load_owner == load_owner) { + cscfg_configfs_del_config(config_desc); + etm_perf_del_symlink_cscfg(config_desc); + list_del(&config_desc->item); + } + } + + /* remove from the feature descriptor lists */ + list_for_each_entry_safe(feat_desc, feat_tmp, &cscfg_mgr->feat_desc_list, item) { + if (feat_desc->load_owner == load_owner) { + cscfg_configfs_del_feature(feat_desc); + list_del(&feat_desc->item); + } + } +} + /** * cscfg_load_config_sets - API function to load feature and config sets. * @@ -389,6 +469,7 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load feature %s\n", feat_descs[i]->name); + cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; } feat_descs[i]->load_owner = owner_info; @@ -406,6 +487,7 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, if (err) { pr_err("coresight-syscfg: Failed to load configuration %s\n", config_descs[i]->name); + cscfg_unload_owned_cfgs_feats(owner_info); goto exit_unlock; } config_descs[i]->load_owner = owner_info; @@ -422,6 +504,57 @@ exit_unlock: } EXPORT_SYMBOL_GPL(cscfg_load_config_sets); +/** + * cscfg_unload_config_sets - unload a set of configurations by owner. + * + * Dynamic unload of configuration and feature sets is done on the basis of + * the load owner of that set. Later loaded configurations can depend on + * features loaded earlier. + * + * Therefore, unload is only possible if:- + * 1) no configurations are active. + * 2) the set being unloaded was the last to be loaded to maintain dependencies. + * + * @owner_info: Information on owner for set being unloaded. + */ +int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info) +{ + int err = 0; + struct cscfg_load_owner_info *load_list_item = NULL; + + mutex_lock(&cscfg_mutex); + + /* cannot unload if anything is active */ + if (atomic_read(&cscfg_mgr->sys_active_cnt)) { + err = -EBUSY; + goto exit_unlock; + } + + /* cannot unload if not last loaded in load order */ + if (!list_empty(&cscfg_mgr->load_order_list)) { + load_list_item = list_last_entry(&cscfg_mgr->load_order_list, + struct cscfg_load_owner_info, item); + if (load_list_item != owner_info) + load_list_item = NULL; + } + + if (!load_list_item) { + err = -EINVAL; + goto exit_unlock; + } + + /* unload all belonging to load_owner */ + cscfg_unload_owned_cfgs_feats(owner_info); + + /* remove from load order list */ + list_del(&load_list_item->item); + +exit_unlock: + mutex_unlock(&cscfg_mutex); + return err; +} +EXPORT_SYMBOL_GPL(cscfg_unload_config_sets); + /* Handle coresight device registration and add configs and features to devices */ /* iterate through config lists and load matching configs to device */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 08067e89edcf..e2b2bdab31aa 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -93,6 +93,7 @@ int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs, struct cscfg_feature_desc **feat_descs, struct cscfg_load_owner_info *owner_info); +int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info); int cscfg_register_csdev(struct coresight_device *csdev, u32 match_flags, struct cscfg_csdev_feat_ops *ops); void cscfg_unregister_csdev(struct coresight_device *csdev); -- cgit v1.2.3 From eb2ec49606c2a02d6382d56a0e19f34e515fde3f Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 24 Nov 2021 20:00:35 +0000 Subject: coresight: syscfg: Update load API for config loadable modules CoreSight configurations and features can be added as kernel loadable modules. This patch updates the load owner API to ensure that the module cannot be unloaded either: 1) if the config it supplies is in use 2) if the module is not the last in the load order list. Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211124200038.28662-4-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-syscfg.c | 39 +++++++++++++++++++++++++- drivers/hwtracing/coresight/coresight-syscfg.h | 1 + 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 8bff7da1aab2..678ee8250d85 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -368,6 +368,26 @@ unlock_exit: return err; } +/* + * Conditionally up reference count on owner to prevent unload. + * + * module loaded configs need to be locked in to prevent premature unload. + */ +static int cscfg_owner_get(struct cscfg_load_owner_info *owner_info) +{ + if ((owner_info->type == CSCFG_OWNER_MODULE) && + (!try_module_get(owner_info->owner_handle))) + return -EINVAL; + return 0; +} + +/* conditionally lower ref count on an owner */ +static void cscfg_owner_put(struct cscfg_load_owner_info *owner_info) +{ + if (owner_info->type == CSCFG_OWNER_MODULE) + module_put(owner_info->owner_handle); +} + static void cscfg_remove_owned_csdev_configs(struct coresight_device *csdev, void *load_owner) { struct cscfg_config_csdev *config_csdev, *tmp; @@ -497,6 +517,14 @@ int cscfg_load_config_sets(struct cscfg_config_desc **config_descs, /* add the load owner to the load order list */ list_add_tail(&owner_info->item, &cscfg_mgr->load_order_list); + if (!list_is_singular(&cscfg_mgr->load_order_list)) { + /* lock previous item in load order list */ + err = cscfg_owner_get(list_prev_entry(owner_info, item)); + if (err) { + cscfg_unload_owned_cfgs_feats(owner_info); + list_del(&owner_info->item); + } + } exit_unlock: mutex_unlock(&cscfg_mutex); @@ -547,7 +575,11 @@ int cscfg_unload_config_sets(struct cscfg_load_owner_info *owner_info) cscfg_unload_owned_cfgs_feats(owner_info); /* remove from load order list */ - list_del(&load_list_item->item); + if (!list_is_singular(&cscfg_mgr->load_order_list)) { + /* unlock previous item in load order list */ + cscfg_owner_put(list_prev_entry(owner_info, item)); + } + list_del(&owner_info->item); exit_unlock: mutex_unlock(&cscfg_mutex); @@ -739,6 +771,10 @@ int cscfg_activate_config(unsigned long cfg_hash) list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) { + /* must ensure that config cannot be unloaded in use */ + err = cscfg_owner_get(config_desc->load_owner); + if (err) + break; /* * increment the global active count - control changes to * active configurations @@ -779,6 +815,7 @@ void cscfg_deactivate_config(unsigned long cfg_hash) if ((unsigned long)config_desc->event_ea->var == cfg_hash) { atomic_dec(&config_desc->active_cnt); atomic_dec(&cscfg_mgr->sys_active_cnt); + cscfg_owner_put(config_desc->load_owner); dev_dbg(cscfg_device(), "Deactivate config %s.\n", config_desc->name); break; } diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index e2b2bdab31aa..1da37874f70f 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -61,6 +61,7 @@ struct cscfg_registered_csdev { /* owner types for loading and unloading of config and feature sets */ enum cscfg_load_owner_type { CSCFG_OWNER_PRELOAD, + CSCFG_OWNER_MODULE, }; /** -- cgit v1.2.3 From ede5bab874f535355ccee5514383941b9525c03b Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 24 Nov 2021 20:00:36 +0000 Subject: coresight: syscfg: Example CoreSight configuration loadable module An example of creating a loadable module to add CoreSight configurations into a system. In the Kernel samples/coresight directory. Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211124200038.28662-5-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- MAINTAINERS | 1 + samples/Kconfig | 9 ++++ samples/Makefile | 1 + samples/coresight/Makefile | 4 ++ samples/coresight/coresight-cfg-sample.c | 73 ++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+) create mode 100644 samples/coresight/Makefile create mode 100644 samples/coresight/coresight-cfg-sample.c diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..85242fdd3b8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1890,6 +1890,7 @@ F: Documentation/trace/coresight/* F: drivers/hwtracing/coresight/* F: include/dt-bindings/arm/coresight-cti-dt.h F: include/linux/coresight* +F: samples/coresight/* F: tools/perf/arch/arm/util/auxtrace.c F: tools/perf/arch/arm/util/cs-etm.c F: tools/perf/arch/arm/util/cs-etm.h diff --git a/samples/Kconfig b/samples/Kconfig index bec3528aa2de..fe8b56d576ce 100644 --- a/samples/Kconfig +++ b/samples/Kconfig @@ -232,6 +232,15 @@ config SAMPLE_WATCH_QUEUE Build example userspace program to use the new mount_notify(), sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function. +config SAMPLE_CORESIGHT_SYSCFG + tristate "Build example loadable module for CoreSight config" + depends on CORESIGHT && m + help + Build an example loadable module that adds new CoreSight features + and configuration using the CoreSight system configuration API. + This demonstrates how a user may create their own CoreSight + configurations and easily load them into the system at runtime. + endif # SAMPLES config HAVE_SAMPLE_FTRACE_DIRECT diff --git a/samples/Makefile b/samples/Makefile index b7b98307c2b4..c148ada64beb 100644 --- a/samples/Makefile +++ b/samples/Makefile @@ -32,3 +32,4 @@ obj-$(CONFIG_SAMPLE_INTEL_MEI) += mei/ subdir-$(CONFIG_SAMPLE_WATCHDOG) += watchdog subdir-$(CONFIG_SAMPLE_WATCH_QUEUE) += watch_queue obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak/ +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight/ diff --git a/samples/coresight/Makefile b/samples/coresight/Makefile new file mode 100644 index 000000000000..b3fce4af2347 --- /dev/null +++ b/samples/coresight/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_SAMPLE_CORESIGHT_SYSCFG) += coresight-cfg-sample.o +ccflags-y += -I$(srctree)/drivers/hwtracing/coresight diff --git a/samples/coresight/coresight-cfg-sample.c b/samples/coresight/coresight-cfg-sample.c new file mode 100644 index 000000000000..25485c80b5e3 --- /dev/null +++ b/samples/coresight/coresight-cfg-sample.c @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright(C) 2020 Linaro Limited. All rights reserved. + * Author: Mike Leach + */ + +#include "coresight-config.h" +#include "coresight-syscfg.h" + +/* create an alternate autofdo configuration */ + +/* we will provide 4 sets of preset parameter values */ +#define AFDO2_NR_PRESETS 4 +/* the total number of parameters in used features - strobing has 2 */ +#define AFDO2_NR_PARAM_SUM 2 + +static const char *afdo2_ref_names[] = { + "strobing", +}; + +/* + * set of presets leaves strobing window constant while varying period to allow + * experimentation with mark / space ratios for various workloads + */ +static u64 afdo2_presets[AFDO2_NR_PRESETS][AFDO2_NR_PARAM_SUM] = { + { 1000, 100 }, + { 1000, 1000 }, + { 1000, 5000 }, + { 1000, 10000 }, +}; + +struct cscfg_config_desc afdo2 = { + .name = "autofdo2", + .description = "Setup ETMs with strobing for autofdo\n" + "Supplied presets allow experimentation with mark-space ratio for various loads\n", + .nr_feat_refs = ARRAY_SIZE(afdo2_ref_names), + .feat_ref_names = afdo2_ref_names, + .nr_presets = AFDO2_NR_PRESETS, + .nr_total_params = AFDO2_NR_PARAM_SUM, + .presets = &afdo2_presets[0][0], +}; + +static struct cscfg_feature_desc *sample_feats[] = { + NULL +}; + +static struct cscfg_config_desc *sample_cfgs[] = { + &afdo2, + NULL +}; + +static struct cscfg_load_owner_info mod_owner = { + .type = CSCFG_OWNER_MODULE, + .owner_handle = THIS_MODULE, +}; + +/* module init and exit - just load and unload configs */ +static int __init cscfg_sample_init(void) +{ + return cscfg_load_config_sets(sample_cfgs, sample_feats, &mod_owner); +} + +static void __exit cscfg_sample_exit(void) +{ + cscfg_unload_config_sets(&mod_owner); +} + +module_init(cscfg_sample_init); +module_exit(cscfg_sample_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Mike Leach "); +MODULE_DESCRIPTION("CoreSight Syscfg Example"); -- cgit v1.2.3 From 7ebd0ec6cf947c3292f21a5edf2d37c9e7317554 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 24 Nov 2021 20:00:37 +0000 Subject: coresight: configfs: Allow configfs to activate configuration Adds configfs attributes to allow a configuration to be enabled for use when sysfs is used to control CoreSight. perf retains independent enabling of configurations. Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211124200038.28662-6-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-etm4x-core.c | 11 +- .../coresight/coresight-syscfg-configfs.c | 67 +++++++++++ .../coresight/coresight-syscfg-configfs.h | 2 + drivers/hwtracing/coresight/coresight-syscfg.c | 126 ++++++++++++++++----- drivers/hwtracing/coresight/coresight-syscfg.h | 8 +- 5 files changed, 186 insertions(+), 28 deletions(-) diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c index 86a313857b58..bf18128cf5de 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x-core.c +++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c @@ -722,7 +722,16 @@ static int etm4_enable_sysfs(struct coresight_device *csdev) { struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct etm4_enable_arg arg = { }; - int ret; + unsigned long cfg_hash; + int ret, preset; + + /* enable any config activated by configfs */ + cscfg_config_sysfs_get_active_cfg(&cfg_hash, &preset); + if (cfg_hash) { + ret = cscfg_csdev_enable_active_config(csdev, cfg_hash, preset); + if (ret) + return ret; + } spin_lock(&drvdata->spinlock); diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c index 345a62f1b728..433ede94dd63 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.c +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.c @@ -6,6 +6,7 @@ #include +#include "coresight-config.h" #include "coresight-syscfg-configfs.h" /* create a default ci_type. */ @@ -87,9 +88,75 @@ static ssize_t cscfg_cfg_values_show(struct config_item *item, char *page) } CONFIGFS_ATTR_RO(cscfg_cfg_, values); +static ssize_t cscfg_cfg_enable_show(struct config_item *item, char *page) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + + return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->active); +} + +static ssize_t cscfg_cfg_enable_store(struct config_item *item, + const char *page, size_t count) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + int err; + bool val; + + err = kstrtobool(page, &val); + if (!err) + err = cscfg_config_sysfs_activate(fs_config->config_desc, val); + if (!err) { + fs_config->active = val; + if (val) + cscfg_config_sysfs_set_preset(fs_config->preset); + } + return err ? err : count; +} +CONFIGFS_ATTR(cscfg_cfg_, enable); + +static ssize_t cscfg_cfg_preset_show(struct config_item *item, char *page) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + + return scnprintf(page, PAGE_SIZE, "%d\n", fs_config->preset); +} + +static ssize_t cscfg_cfg_preset_store(struct config_item *item, + const char *page, size_t count) +{ + struct cscfg_fs_config *fs_config = container_of(to_config_group(item), + struct cscfg_fs_config, group); + int preset, err; + + err = kstrtoint(page, 0, &preset); + if (!err) { + /* + * presets start at 1, and go up to max (15), + * but the config may provide fewer. + */ + if ((preset < 1) || (preset > fs_config->config_desc->nr_presets)) + err = -EINVAL; + } + + if (!err) { + /* set new value */ + fs_config->preset = preset; + /* set on system if active */ + if (fs_config->active) + cscfg_config_sysfs_set_preset(fs_config->preset); + } + return err ? err : count; +} +CONFIGFS_ATTR(cscfg_cfg_, preset); + static struct configfs_attribute *cscfg_config_view_attrs[] = { &cscfg_cfg_attr_description, &cscfg_cfg_attr_feature_refs, + &cscfg_cfg_attr_enable, + &cscfg_cfg_attr_preset, NULL, }; diff --git a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h index ea1e54d29f7f..373d84d43268 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg-configfs.h +++ b/drivers/hwtracing/coresight/coresight-syscfg-configfs.h @@ -15,6 +15,8 @@ struct cscfg_fs_config { struct cscfg_config_desc *config_desc; struct config_group group; + bool active; + int preset; }; /* container for feature view */ diff --git a/drivers/hwtracing/coresight/coresight-syscfg.c b/drivers/hwtracing/coresight/coresight-syscfg.c index 678ee8250d85..098fc34c4829 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.c +++ b/drivers/hwtracing/coresight/coresight-syscfg.c @@ -745,30 +745,20 @@ unlock_exit: } EXPORT_SYMBOL_GPL(cscfg_csdev_reset_feats); -/** - * cscfg_activate_config - Mark a configuration descriptor as active. - * - * This will be seen when csdev devices are enabled in the system. - * Only activated configurations can be enabled on individual devices. - * Activation protects the configuration from alteration or removal while - * active. - * - * Selection by hash value - generated from the configuration name when it - * was loaded and added to the cs_etm/configurations file system for selection - * by perf. +/* + * This activate configuration for either perf or sysfs. Perf can have multiple + * active configs, selected per event, sysfs is limited to one. * * Increments the configuration descriptor active count and the global active * count. * * @cfg_hash: Hash value of the selected configuration name. */ -int cscfg_activate_config(unsigned long cfg_hash) +static int _cscfg_activate_config(unsigned long cfg_hash) { struct cscfg_config_desc *config_desc; int err = -EINVAL; - mutex_lock(&cscfg_mutex); - list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { if ((unsigned long)config_desc->event_ea->var == cfg_hash) { /* must ensure that config cannot be unloaded in use */ @@ -792,6 +782,101 @@ int cscfg_activate_config(unsigned long cfg_hash) break; } } + return err; +} + +static void _cscfg_deactivate_config(unsigned long cfg_hash) +{ + struct cscfg_config_desc *config_desc; + + list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { + if ((unsigned long)config_desc->event_ea->var == cfg_hash) { + atomic_dec(&config_desc->active_cnt); + atomic_dec(&cscfg_mgr->sys_active_cnt); + cscfg_owner_put(config_desc->load_owner); + dev_dbg(cscfg_device(), "Deactivate config %s.\n", config_desc->name); + break; + } + } +} + +/* + * called from configfs to set/clear the active configuration for use when + * using sysfs to control trace. + */ +int cscfg_config_sysfs_activate(struct cscfg_config_desc *config_desc, bool activate) +{ + unsigned long cfg_hash; + int err = 0; + + mutex_lock(&cscfg_mutex); + + cfg_hash = (unsigned long)config_desc->event_ea->var; + + if (activate) { + /* cannot be a current active value to activate this */ + if (cscfg_mgr->sysfs_active_config) { + err = -EBUSY; + goto exit_unlock; + } + err = _cscfg_activate_config(cfg_hash); + if (!err) + cscfg_mgr->sysfs_active_config = cfg_hash; + } else { + /* disable if matching current value */ + if (cscfg_mgr->sysfs_active_config == cfg_hash) { + _cscfg_deactivate_config(cfg_hash); + cscfg_mgr->sysfs_active_config = 0; + } else + err = -EINVAL; + } + +exit_unlock: + mutex_unlock(&cscfg_mutex); + return err; +} + +/* set the sysfs preset value */ +void cscfg_config_sysfs_set_preset(int preset) +{ + mutex_lock(&cscfg_mutex); + cscfg_mgr->sysfs_active_preset = preset; + mutex_unlock(&cscfg_mutex); +} + +/* + * Used by a device to get the config and preset selected as active in configfs, + * when using sysfs to control trace. + */ +void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset) +{ + mutex_lock(&cscfg_mutex); + *preset = cscfg_mgr->sysfs_active_preset; + *cfg_hash = cscfg_mgr->sysfs_active_config; + mutex_unlock(&cscfg_mutex); +} +EXPORT_SYMBOL_GPL(cscfg_config_sysfs_get_active_cfg); + +/** + * cscfg_activate_config - Mark a configuration descriptor as active. + * + * This will be seen when csdev devices are enabled in the system. + * Only activated configurations can be enabled on individual devices. + * Activation protects the configuration from alteration or removal while + * active. + * + * Selection by hash value - generated from the configuration name when it + * was loaded and added to the cs_etm/configurations file system for selection + * by perf. + * + * @cfg_hash: Hash value of the selected configuration name. + */ +int cscfg_activate_config(unsigned long cfg_hash) +{ + int err = 0; + + mutex_lock(&cscfg_mutex); + err = _cscfg_activate_config(cfg_hash); mutex_unlock(&cscfg_mutex); return err; @@ -807,19 +892,8 @@ EXPORT_SYMBOL_GPL(cscfg_activate_config); */ void cscfg_deactivate_config(unsigned long cfg_hash) { - struct cscfg_config_desc *config_desc; - mutex_lock(&cscfg_mutex); - - list_for_each_entry(config_desc, &cscfg_mgr->config_desc_list, item) { - if ((unsigned long)config_desc->event_ea->var == cfg_hash) { - atomic_dec(&config_desc->active_cnt); - atomic_dec(&cscfg_mgr->sys_active_cnt); - cscfg_owner_put(config_desc->load_owner); - dev_dbg(cscfg_device(), "Deactivate config %s.\n", config_desc->name); - break; - } - } + _cscfg_deactivate_config(cfg_hash); mutex_unlock(&cscfg_mutex); } EXPORT_SYMBOL_GPL(cscfg_deactivate_config); diff --git a/drivers/hwtracing/coresight/coresight-syscfg.h b/drivers/hwtracing/coresight/coresight-syscfg.h index 1da37874f70f..9106ffab4833 100644 --- a/drivers/hwtracing/coresight/coresight-syscfg.h +++ b/drivers/hwtracing/coresight/coresight-syscfg.h @@ -28,6 +28,8 @@ * @load_order_list: Ordered list of owners for dynamically loaded configurations. * @sys_active_cnt: Total number of active config descriptor references. * @cfgfs_subsys: configfs subsystem used to manage configurations. + * @sysfs_active_config:Active config hash used if CoreSight controlled from sysfs. + * @sysfs_active_preset:Active preset index used if CoreSight controlled from sysfs. */ struct cscfg_manager { struct device dev; @@ -37,6 +39,8 @@ struct cscfg_manager { struct list_head load_order_list; atomic_t sys_active_cnt; struct configfs_subsystem cfgfs_subsys; + u32 sysfs_active_config; + int sysfs_active_preset; }; /* get reference to dev in cscfg_manager */ @@ -88,7 +92,8 @@ int cscfg_preload(void *owner_handle); const struct cscfg_feature_desc *cscfg_get_named_feat_desc(const char *name); int cscfg_update_feat_param_val(struct cscfg_feature_desc *feat_desc, int param_idx, u64 value); - +int cscfg_config_sysfs_activate(struct cscfg_config_desc *cfg_desc, bool activate); +void cscfg_config_sysfs_set_preset(int preset); /* syscfg manager external API */ int cscfg_load_config_sets(struct cscfg_config_desc **cfg_descs, @@ -104,5 +109,6 @@ void cscfg_csdev_reset_feats(struct coresight_device *csdev); int cscfg_csdev_enable_active_config(struct coresight_device *csdev, unsigned long cfg_hash, int preset); void cscfg_csdev_disable_active_config(struct coresight_device *csdev); +void cscfg_config_sysfs_get_active_cfg(unsigned long *cfg_hash, int *preset); #endif /* CORESIGHT_SYSCFG_H */ -- cgit v1.2.3 From f9809d56513541ca10f67d9666de02384600f109 Mon Sep 17 00:00:00 2001 From: Mike Leach Date: Wed, 24 Nov 2021 20:00:38 +0000 Subject: Documentation: coresight: Update coresight configuration docs Update the CoreSight System Configuration document to cover the use of loadable modules to add configurations and features to the system. Signed-off-by: Mike Leach Link: https://lore.kernel.org/r/20211124200038.28662-7-mike.leach@linaro.org Signed-off-by: Mathieu Poirier --- Documentation/trace/coresight/coresight-config.rst | 62 ++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/Documentation/trace/coresight/coresight-config.rst b/Documentation/trace/coresight/coresight-config.rst index 6ed13398ca2c..6d5ffa6f7347 100644 --- a/Documentation/trace/coresight/coresight-config.rst +++ b/Documentation/trace/coresight/coresight-config.rst @@ -155,14 +155,14 @@ follows:: autofdo $ cd autofdo/ $ ls - description preset1 preset3 preset5 preset7 preset9 - feature_refs preset2 preset4 preset6 preset8 + description feature_refs preset1 preset3 preset5 preset7 preset9 + enable preset preset2 preset4 preset6 preset8 $ cat description Setup ETMs with strobing for autofdo $ cat feature_refs strobing -Each preset declared has a preset subdirectory declared. The values for +Each preset declared has a 'preset' subdirectory declared. The values for the preset can be examined:: $ cat preset1/values @@ -170,6 +170,9 @@ the preset can be examined:: $ cat preset2/values strobing.window = 0x1388 strobing.period = 0x4 +The 'enable' and 'preset' files allow the control of a configuration when +using CoreSight with sysfs. + The features referenced by the configuration can be examined in the features directory:: @@ -236,3 +239,56 @@ A preset to override the current parameter values can also be selected:: When configurations are selected in this way, then the trace sink used is automatically selected. + +Using Configurations in sysfs +============================= + +Coresight can be controlled using sysfs. When this is in use then a configuration +can be made active for the devices that are used in the sysfs session. + +In a configuration there are 'enable' and 'preset' files. + +To enable a configuration for use with sysfs:: + + $ cd configurations/autofdo + $ echo 1 > enable + +This will then use any default parameter values in the features - which can be +adjusted as described above. + +To use a preset set of parameter values:: + + $ echo 3 > preset + +This will select preset3 for the configuration. +The valid values for preset are 0 - to deselect presets, and any value of + where a preset sub-directory is present. + +Note that the active sysfs configuration is a global parameter, therefore +only a single configuration can be active for sysfs at any one time. +Attempting to enable a second configuration will result in an error. +Additionally, attempting to disable the configuration while in use will +also result in an error. + +The use of the active configuration by sysfs is independent of the configuration +used in perf. + + +Creating and Loading Custom Configurations +========================================== + +Custom configurations and / or features can be dynamically loaded into the +system by using a loadable module. + +An example of a custom configuration is found in ./samples/coresight. + +This creates a new configuration that uses the existing built in +strobing feature, but provides a different set of presets. + +When the module is loaded, then the configuration appears in the configfs +file system and is selectable in the same way as the built in configuration +described above. + +Configurations can use previously loaded features. The system will ensure +that it is not possible to unload a feature that is currently in use, by +enforcing the unload order as the strict reverse of the load order. -- cgit v1.2.3 From ed14e769f64311769dcf20dde544b82c158d01b1 Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 15 Nov 2021 14:19:12 +0000 Subject: iio: buffer-dma: Remove unused iio_buffer_block struct This structure was never used anywhere, so it can safely be dropped. It will later be re-introduced as a different structure in a different header. Signed-off-by: Paul Cercueil Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211115141925.60164-3-paul@crapouillou.net Signed-off-by: Jonathan Cameron --- include/linux/iio/buffer-dma.h | 5 ----- 1 file changed, 5 deletions(-) diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h index ff15c61bf319..6564bdcdac66 100644 --- a/include/linux/iio/buffer-dma.h +++ b/include/linux/iio/buffer-dma.h @@ -17,11 +17,6 @@ struct iio_dma_buffer_queue; struct iio_dma_buffer_ops; struct device; -struct iio_buffer_block { - u32 size; - u32 bytes_used; -}; - /** * enum iio_block_state - State of a struct iio_dma_buffer_block * @IIO_BLOCK_STATE_DEQUEUED: Block is not queued -- cgit v1.2.3 From ab1fb45579d876aee70eb736d3d9e6a9bacc798d Mon Sep 17 00:00:00 2001 From: Paul Cercueil Date: Mon, 15 Nov 2021 14:19:13 +0000 Subject: iio: buffer-dma: Use round_down() instead of rounddown() We know that the buffer's alignment will always be a power of two; therefore, we can use the faster round_down() macro. Signed-off-by: Paul Cercueil Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211115141925.60164-4-paul@crapouillou.net Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dmaengine.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index 1ac94c4e9792..f8ce26a24c57 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -67,7 +67,7 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, dma_cookie_t cookie; block->bytes_used = min(block->size, dmaengine_buffer->max_size); - block->bytes_used = rounddown(block->bytes_used, + block->bytes_used = round_down(block->bytes_used, dmaengine_buffer->align); desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, -- cgit v1.2.3 From ffc7c5172a6d1f7ec468066a7172ce65baf1e3e1 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Fri, 19 Nov 2021 10:56:27 +0200 Subject: iio: expose shared parameter in IIO_ENUM_AVAILABLE The shared parameter should be configurable based on its usage, and not constrained to IIO_SHARED_BY_TYPE. This patch aims to improve the flexibility in using the IIO_ENUM_AVAILABLE define and avoid redefining custom iio enums that expose the shared parameter. An example is the ad5766.c driver where IIO_ENUM_AVAILABLE_SHARED was defined in order to achieve `shared` parameter customization. The current state of the IIO_ENUM_AVAILABLE implementation will imply similar redefinitions each time a driver will require access to the `shared` parameter. An example would be admv1013 driver which will require custom device attribute for the frequency translation modes: Quadrature I/Q mode and Intermediate Frequency mode. Signed-off-by: Antoniu Miclaus Reviewed-by: Alexandru Ardelean Link: https://lore.kernel.org/r/20211119085627.6348-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/accel/bma180.c | 2 +- drivers/iio/accel/mma9553.c | 2 +- drivers/iio/adc/ad7192.c | 3 ++- drivers/iio/adc/hi8435.c | 2 +- drivers/iio/dac/ad5064.c | 4 ++-- drivers/iio/dac/ad5380.c | 2 +- drivers/iio/dac/ad5446.c | 2 +- drivers/iio/dac/ad5504.c | 2 +- drivers/iio/dac/ad5624r_spi.c | 2 +- drivers/iio/dac/ad5686.c | 2 +- drivers/iio/dac/ad5766.c | 13 ++----------- drivers/iio/dac/ad5791.c | 2 +- drivers/iio/dac/max5821.c | 2 +- drivers/iio/dac/mcp4725.c | 8 ++++---- drivers/iio/dac/stm32-dac.c | 2 +- drivers/iio/dac/ti-dac082s085.c | 2 +- drivers/iio/dac/ti-dac5571.c | 2 +- drivers/iio/dac/ti-dac7311.c | 2 +- drivers/iio/magnetometer/hmc5843_core.c | 4 ++-- drivers/iio/trigger/stm32-timer-trigger.c | 4 ++-- include/linux/iio/iio.h | 5 +++-- 21 files changed, 31 insertions(+), 38 deletions(-) diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 2edfcb4819b7..09496f358ad9 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -658,7 +658,7 @@ static const struct iio_chan_spec_ext_info bma023_ext_info[] = { static const struct iio_chan_spec_ext_info bma180_ext_info[] = { IIO_ENUM("power_mode", IIO_SHARED_BY_TYPE, &bma180_power_mode_enum), - IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum), + IIO_ENUM_AVAILABLE("power_mode", IIO_SHARED_BY_TYPE, &bma180_power_mode_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix), { } }; diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index ba3ecb3b57dc..0570ab1cc064 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -917,7 +917,7 @@ static const struct iio_enum mma9553_calibgender_enum = { static const struct iio_chan_spec_ext_info mma9553_ext_info[] = { IIO_ENUM("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), - IIO_ENUM_AVAILABLE("calibgender", &mma9553_calibgender_enum), + IIO_ENUM_AVAILABLE("calibgender", IIO_SHARED_BY_TYPE, &mma9553_calibgender_enum), {}, }; diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 2121a812b0c3..cc990205f306 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -257,7 +257,8 @@ static const struct iio_chan_spec_ext_info ad7192_calibsys_ext_info[] = { }, IIO_ENUM("sys_calibration_mode", IIO_SEPARATE, &ad7192_syscalib_mode_enum), - IIO_ENUM_AVAILABLE("sys_calibration_mode", &ad7192_syscalib_mode_enum), + IIO_ENUM_AVAILABLE("sys_calibration_mode", IIO_SHARED_BY_TYPE, + &ad7192_syscalib_mode_enum), {} }; diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index 8b353e26668e..e665e14c6e54 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -350,7 +350,7 @@ static const struct iio_enum hi8435_sensing_mode = { static const struct iio_chan_spec_ext_info hi8435_ext_info[] = { IIO_ENUM("sensing_mode", IIO_SEPARATE, &hi8435_sensing_mode), - IIO_ENUM_AVAILABLE("sensing_mode", &hi8435_sensing_mode), + IIO_ENUM_AVAILABLE("sensing_mode", IIO_SHARED_BY_TYPE, &hi8435_sensing_mode), {}, }; diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index fd9cac4f6321..27ee2c63c5d4 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -377,7 +377,7 @@ static const struct iio_chan_spec_ext_info ad5064_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5064_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5064_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5064_powerdown_mode_enum), { }, }; @@ -389,7 +389,7 @@ static const struct iio_chan_spec_ext_info ltc2617_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, <c2617_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", <c2617_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, <c2617_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 8ca26bb4b62f..e38860a6a9f3 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -249,7 +249,7 @@ static const struct iio_chan_spec_ext_info ad5380_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5380_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5380_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index 3cc5513a6cbf..1c9b54c012a7 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -142,7 +142,7 @@ static const struct iio_chan_spec_ext_info ad5446_ext_info_powerdown[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5446_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5446_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5446_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index 19cdf9890d02..b631261efa97 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -241,7 +241,7 @@ static const struct iio_chan_spec_ext_info ad5504_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5504_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5504_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5504_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 530529feebb5..3c98941b9f99 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -159,7 +159,7 @@ static const struct iio_chan_spec_ext_info ad5624r_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5624r_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5624r_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5624r_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 8f001db775f4..e592a995f404 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -184,7 +184,7 @@ static const struct iio_chan_spec_ext_info ad5686_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &ad5686_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5686_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5686_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index b0d220c3a126..43189af2fb1f 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -426,14 +426,6 @@ static ssize_t ad5766_write_ext(struct iio_dev *indio_dev, .shared = _shared, \ } -#define IIO_ENUM_AVAILABLE_SHARED(_name, _shared, _e) \ -{ \ - .name = (_name "_available"), \ - .shared = _shared, \ - .read = iio_enum_available_read, \ - .private = (uintptr_t)(_e), \ -} - static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { _AD5766_CHAN_EXT_INFO("dither_enable", AD5766_DITHER_ENABLE, @@ -443,9 +435,8 @@ static const struct iio_chan_spec_ext_info ad5766_ext_info[] = { _AD5766_CHAN_EXT_INFO("dither_source", AD5766_DITHER_SOURCE, IIO_SEPARATE), IIO_ENUM("dither_scale", IIO_SEPARATE, &ad5766_dither_scale_enum), - IIO_ENUM_AVAILABLE_SHARED("dither_scale", - IIO_SEPARATE, - &ad5766_dither_scale_enum), + IIO_ENUM_AVAILABLE("dither_scale", IIO_SEPARATE, + &ad5766_dither_scale_enum), {} }; diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index a0923b76e8b6..7b4579d73d18 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -285,7 +285,7 @@ static const struct iio_chan_spec_ext_info ad5791_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5791_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &ad5791_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ad5791_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index 7da4710a6408..fce640b7f1c8 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -137,7 +137,7 @@ static const struct iio_chan_spec_ext_info max5821_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &max5821_powerdown_mode_enum), - IIO_ENUM_AVAILABLE("powerdown_mode", &max5821_powerdown_mode_enum), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &max5821_powerdown_mode_enum), { }, }; diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 34b14aafb630..98b2c2f10bf3 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -221,8 +221,8 @@ static const struct iio_chan_spec_ext_info mcp4725_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp472x_powerdown_mode_enum[MCP4725]), - IIO_ENUM_AVAILABLE("powerdown_mode", - &mcp472x_powerdown_mode_enum[MCP4725]), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp472x_powerdown_mode_enum[MCP4725]), { }, }; @@ -235,8 +235,8 @@ static const struct iio_chan_spec_ext_info mcp4726_ext_info[] = { }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &mcp472x_powerdown_mode_enum[MCP4726]), - IIO_ENUM_AVAILABLE("powerdown_mode", - &mcp472x_powerdown_mode_enum[MCP4726]), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, + &mcp472x_powerdown_mode_enum[MCP4726]), { }, }; diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index dd2e306824e7..cd71cc4553a7 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -246,7 +246,7 @@ static const struct iio_chan_spec_ext_info stm32_dac_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &stm32_dac_powerdown_mode_en), - IIO_ENUM_AVAILABLE("powerdown_mode", &stm32_dac_powerdown_mode_en), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &stm32_dac_powerdown_mode_en), {}, }; diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index 5c14bfb16521..6beda2193683 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -160,7 +160,7 @@ static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { .shared = IIO_SHARED_BY_TYPE, }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), - IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), { }, }; diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index 546a4cf6c5ef..4a3b8d875518 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -212,7 +212,7 @@ static const struct iio_chan_spec_ext_info dac5571_ext_info[] = { .shared = IIO_SEPARATE, }, IIO_ENUM("powerdown_mode", IIO_SEPARATE, &dac5571_powerdown_mode), - IIO_ENUM_AVAILABLE("powerdown_mode", &dac5571_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &dac5571_powerdown_mode), {}, }; diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 09218c3029f0..99f275829ec2 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -146,7 +146,7 @@ static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = { .shared = IIO_SHARED_BY_TYPE, }, IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), - IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode), + IIO_ENUM_AVAILABLE("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode), { }, }; diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index f08726bf5ec3..5a730d9bdbb0 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -246,7 +246,7 @@ static const struct iio_enum hmc5843_meas_conf_enum = { static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = { IIO_ENUM("meas_conf", IIO_SHARED_BY_TYPE, &hmc5843_meas_conf_enum), - IIO_ENUM_AVAILABLE("meas_conf", &hmc5843_meas_conf_enum), + IIO_ENUM_AVAILABLE("meas_conf", IIO_SHARED_BY_TYPE, &hmc5843_meas_conf_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix), { } }; @@ -260,7 +260,7 @@ static const struct iio_enum hmc5983_meas_conf_enum = { static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = { IIO_ENUM("meas_conf", IIO_SHARED_BY_TYPE, &hmc5983_meas_conf_enum), - IIO_ENUM_AVAILABLE("meas_conf", &hmc5983_meas_conf_enum), + IIO_ENUM_AVAILABLE("meas_conf", IIO_SHARED_BY_TYPE, &hmc5983_meas_conf_enum), IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix), { } }; diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 33083877cd19..02b87b0f9d70 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -696,9 +696,9 @@ static const struct iio_chan_spec_ext_info stm32_trigger_count_info[] = { .write = stm32_count_set_preset }, IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum), - IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum), + IIO_ENUM_AVAILABLE("enable_mode", IIO_SHARED_BY_TYPE, &stm32_enable_mode_enum), IIO_ENUM("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum), - IIO_ENUM_AVAILABLE("trigger_mode", &stm32_trigger_mode_enum), + IIO_ENUM_AVAILABLE("trigger_mode", IIO_SHARED_BY_TYPE, &stm32_trigger_mode_enum), {} }; diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 324561b7a5e8..07025d6b3de1 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -103,15 +103,16 @@ ssize_t iio_enum_write(struct iio_dev *indio_dev, /** * IIO_ENUM_AVAILABLE() - Initialize enum available extended channel attribute * @_name: Attribute name ("_available" will be appended to the name) + * @_shared: Whether the attribute is shared between all channels * @_e: Pointer to an iio_enum struct * * Creates a read only attribute which lists all the available enum items in a * space separated list. This should usually be used together with IIO_ENUM() */ -#define IIO_ENUM_AVAILABLE(_name, _e) \ +#define IIO_ENUM_AVAILABLE(_name, _shared, _e) \ { \ .name = (_name "_available"), \ - .shared = IIO_SHARED_BY_TYPE, \ + .shared = _shared, \ .read = iio_enum_available_read, \ .private = (uintptr_t)(_e), \ } -- cgit v1.2.3 From ee8ec048e091bfe36cc463e7a30eefbe5fef3e75 Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Fri, 5 Nov 2021 12:05:00 +0200 Subject: iio: test: Add check against NULL for buffer in tests. Add KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf) for every test. Also use ARRAY_SIZE(values) where it is possible. Signed-off-by: Andriy Tryshnivskyy Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211105100501.1904-2-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-format.c | 69 +++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 27 deletions(-) diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index f1e951eddb43..b746d00bc0ea 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -14,10 +14,13 @@ static void iio_test_iio_format_value_integer(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + char *buf; int val; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + val = 42; ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "42\n"); @@ -41,142 +44,154 @@ static void iio_test_iio_format_value_integer(struct kunit *test) static void iio_test_iio_format_value_fixedpoint(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[2]; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + /* positive >= 1 */ values[0] = 1; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000010\n"); /* positive < 1 */ values[0] = 0; values[1] = 12; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000012\n"); /* negative <= -1 */ values[0] = -1; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000000010\n"); /* negative > -1 */ values[0] = 0; values[1] = -123; - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123 dB\n"); - ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000123\n"); } static void iio_test_iio_format_value_fractional(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[2]; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + /* positive < 1 */ values[0] = 1; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.100000000\n"); /* positive >= 1 */ values[0] = 100; values[1] = 3; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "33.333333333\n"); /* negative > -1 */ values[0] = -1; values[1] = 1000000000; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000001\n"); /* negative <= -1 */ values[0] = -200; values[1] = 3; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-66.666666666\n"); /* Zero */ values[0] = 0; values[1] = -10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); } static void iio_test_iio_format_value_fractional_log2(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[2]; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + /* positive < 1 */ values[0] = 123; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.120117187\n"); /* positive >= 1 */ values[0] = 1234567; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1205.631835937\n"); /* negative > -1 */ values[0] = -123; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.120117187\n"); /* negative <= -1 */ values[0] = -1234567; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1205.631835937\n"); /* Zero */ values[0] = 0; values[1] = 10; - ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); } static void iio_test_iio_format_value_multiple(struct kunit *test) { - char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); int values[] = {1, -2, 3, -4, 5}; + char *buf; int ret; + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + ret = iio_format_value(buf, IIO_VAL_INT_MULTIPLE, ARRAY_SIZE(values), values); IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1 -2 3 -4 5 \n"); -- cgit v1.2.3 From 1d9b750c92d738cb84eb1531dd85147466202b86 Mon Sep 17 00:00:00 2001 From: Andriy Tryshnivskyy Date: Fri, 5 Nov 2021 12:05:01 +0200 Subject: iio: test: Add test for IIO_VAL_INT_64. Add test for newly introduced type IIO_VAL_INT_64. Signed-off-by: Andriy Tryshnivskyy Reported-by: kernel test robot Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211105100501.1904-3-andriy.tryshnivskyy@opensynergy.com Signed-off-by: Jonathan Cameron --- drivers/iio/test/iio-test-format.c | 54 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c index b746d00bc0ea..237321436b83 100644 --- a/drivers/iio/test/iio-test-format.c +++ b/drivers/iio/test/iio-test-format.c @@ -197,12 +197,66 @@ static void iio_test_iio_format_value_multiple(struct kunit *test) IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1 -2 3 -4 5 \n"); } +static void iio_test_iio_format_value_integer_64(struct kunit *test) +{ + int values[2]; + s64 value; + char *buf; + int ret; + + buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, buf); + + value = 24; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "24\n"); + + value = -24; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-24\n"); + + value = 0; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); + + value = UINT_MAX; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "4294967295\n"); + + value = -((s64)UINT_MAX); + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-4294967295\n"); + + value = LLONG_MAX; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "9223372036854775807\n"); + + value = LLONG_MIN; + values[0] = lower_32_bits(value); + values[1] = upper_32_bits(value); + ret = iio_format_value(buf, IIO_VAL_INT_64, ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-9223372036854775808\n"); +} + static struct kunit_case iio_format_test_cases[] = { KUNIT_CASE(iio_test_iio_format_value_integer), KUNIT_CASE(iio_test_iio_format_value_fixedpoint), KUNIT_CASE(iio_test_iio_format_value_fractional), KUNIT_CASE(iio_test_iio_format_value_fractional_log2), KUNIT_CASE(iio_test_iio_format_value_multiple), + KUNIT_CASE(iio_test_iio_format_value_integer_64), {} }; -- cgit v1.2.3 From 4ba0b2c294fe691921271372f7b59e5cc2ce4b0f Mon Sep 17 00:00:00 2001 From: Russ Weight Date: Thu, 18 Nov 2021 17:55:51 -0800 Subject: fpga: mgr: Use standard dev_release for class driver The FPGA manager class driver data structure is being treated as a managed resource instead of using the standard dev_release call-back function to release the class data structure. This change removes the managed resource code for the freeing of the class data structure and combines the create() and register() functions into a single register() or register_full() function. The register_full() function accepts an info data structure to provide flexibility in passing optional parameters. The register() function supports the current parameter list for users that don't require the use of optional parameters. The devm_fpga_mgr_register() function is retained, and the devm_fpga_mgr_register_full() function is added. Signed-off-by: Russ Weight Reviewed-by: Xu Yilun Acked-by: Xu Yilun Signed-off-by: Moritz Fischer --- Documentation/driver-api/fpga/fpga-mgr.rst | 38 +++-- drivers/fpga/altera-cvp.c | 12 +- drivers/fpga/altera-pr-ip-core.c | 7 +- drivers/fpga/altera-ps-spi.c | 9 +- drivers/fpga/dfl-fme-mgr.c | 22 ++- drivers/fpga/fpga-mgr.c | 215 ++++++++++++----------------- drivers/fpga/ice40-spi.c | 9 +- drivers/fpga/machxo2-spi.c | 9 +- drivers/fpga/socfpga-a10.c | 16 +-- drivers/fpga/socfpga.c | 9 +- drivers/fpga/stratix10-soc.c | 16 +-- drivers/fpga/ts73xx-fpga.c | 9 +- drivers/fpga/versal-fpga.c | 9 +- drivers/fpga/xilinx-spi.c | 11 +- drivers/fpga/zynq-fpga.c | 16 +-- drivers/fpga/zynqmp-fpga.c | 9 +- include/linux/fpga/fpga-mgr.h | 62 ++++++--- 17 files changed, 217 insertions(+), 261 deletions(-) diff --git a/Documentation/driver-api/fpga/fpga-mgr.rst b/Documentation/driver-api/fpga/fpga-mgr.rst index 4d926b452cb3..42c01f396dce 100644 --- a/Documentation/driver-api/fpga/fpga-mgr.rst +++ b/Documentation/driver-api/fpga/fpga-mgr.rst @@ -24,7 +24,7 @@ How to support a new FPGA device -------------------------------- To add another FPGA manager, write a driver that implements a set of ops. The -probe function calls fpga_mgr_register(), such as:: +probe function calls fpga_mgr_register() or fpga_mgr_register_full(), such as:: static const struct fpga_manager_ops socfpga_fpga_ops = { .write_init = socfpga_fpga_ops_configure_init, @@ -49,14 +49,14 @@ probe function calls fpga_mgr_register(), such as:: * them in priv */ - mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", - &socfpga_fpga_ops, priv); - if (!mgr) - return -ENOMEM; + mgr = fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager", + &socfpga_fpga_ops, priv); + if (IS_ERR(mgr)) + return PTR_ERR(mgr); platform_set_drvdata(pdev, mgr); - return fpga_mgr_register(mgr); + return 0; } static int socfpga_fpga_remove(struct platform_device *pdev) @@ -68,6 +68,11 @@ probe function calls fpga_mgr_register(), such as:: return 0; } +Alternatively, the probe function could call one of the resource managed +register functions, devm_fpga_mgr_register() or devm_fpga_mgr_register_full(). +When these functions are used, the parameter syntax is the same, but the call +to fpga_mgr_unregister() should be removed. In the above example, the +socfpga_fpga_remove() function would not be required. The ops will implement whatever device specific register writes are needed to do the programming sequence for this particular FPGA. These ops return 0 for @@ -104,8 +109,14 @@ API for implementing a new FPGA Manager driver * ``fpga_mgr_states`` - Values for :c:expr:`fpga_manager->state`. * struct fpga_manager - the FPGA manager struct * struct fpga_manager_ops - Low level FPGA manager driver ops -* devm_fpga_mgr_create() - Allocate and init a manager struct -* fpga_mgr_register() - Register an FPGA manager +* struct fpga_manager_info - Parameter structure for fpga_mgr_register_full() +* fpga_mgr_register_full() - Create and register an FPGA manager using the + fpga_mgr_info structure to provide the full flexibility of options +* fpga_mgr_register() - Create and register an FPGA manager using standard + arguments +* devm_fpga_mgr_register_full() - Resource managed version of + fpga_mgr_register_full() +* devm_fpga_mgr_register() - Resource managed version of fpga_mgr_register() * fpga_mgr_unregister() - Unregister an FPGA manager .. kernel-doc:: include/linux/fpga/fpga-mgr.h @@ -117,11 +128,20 @@ API for implementing a new FPGA Manager driver .. kernel-doc:: include/linux/fpga/fpga-mgr.h :functions: fpga_manager_ops +.. kernel-doc:: include/linux/fpga/fpga-mgr.h + :functions: fpga_manager_info + .. kernel-doc:: drivers/fpga/fpga-mgr.c - :functions: devm_fpga_mgr_create + :functions: fpga_mgr_register_full .. kernel-doc:: drivers/fpga/fpga-mgr.c :functions: fpga_mgr_register +.. kernel-doc:: drivers/fpga/fpga-mgr.c + :functions: devm_fpga_mgr_register_full + +.. kernel-doc:: drivers/fpga/fpga-mgr.c + :functions: devm_fpga_mgr_register + .. kernel-doc:: drivers/fpga/fpga-mgr.c :functions: fpga_mgr_unregister diff --git a/drivers/fpga/altera-cvp.c b/drivers/fpga/altera-cvp.c index ccf4546eff29..4ffb9da537d8 100644 --- a/drivers/fpga/altera-cvp.c +++ b/drivers/fpga/altera-cvp.c @@ -652,19 +652,15 @@ static int altera_cvp_probe(struct pci_dev *pdev, snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s @%s", ALTERA_CVP_MGR_NAME, pci_name(pdev)); - mgr = devm_fpga_mgr_create(&pdev->dev, conf->mgr_name, - &altera_cvp_ops, conf); - if (!mgr) { - ret = -ENOMEM; + mgr = fpga_mgr_register(&pdev->dev, conf->mgr_name, + &altera_cvp_ops, conf); + if (IS_ERR(mgr)) { + ret = PTR_ERR(mgr); goto err_unmap; } pci_set_drvdata(pdev, mgr); - ret = fpga_mgr_register(mgr); - if (ret) - goto err_unmap; - return 0; err_unmap: diff --git a/drivers/fpga/altera-pr-ip-core.c b/drivers/fpga/altera-pr-ip-core.c index dfdf21ed34c4..be0667968d33 100644 --- a/drivers/fpga/altera-pr-ip-core.c +++ b/drivers/fpga/altera-pr-ip-core.c @@ -191,11 +191,8 @@ int alt_pr_register(struct device *dev, void __iomem *reg_base) (val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT, (int)(val & ALT_PR_CSR_PR_START)); - mgr = devm_fpga_mgr_create(dev, dev_name(dev), &alt_pr_ops, priv); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(dev, mgr); + mgr = devm_fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv); + return PTR_ERR_OR_ZERO(mgr); } EXPORT_SYMBOL_GPL(alt_pr_register); diff --git a/drivers/fpga/altera-ps-spi.c b/drivers/fpga/altera-ps-spi.c index 23bfd4d1ad0f..5e1e009dba89 100644 --- a/drivers/fpga/altera-ps-spi.c +++ b/drivers/fpga/altera-ps-spi.c @@ -302,12 +302,9 @@ static int altera_ps_probe(struct spi_device *spi) snprintf(conf->mgr_name, sizeof(conf->mgr_name), "%s %s", dev_driver_string(&spi->dev), dev_name(&spi->dev)); - mgr = devm_fpga_mgr_create(&spi->dev, conf->mgr_name, - &altera_ps_ops, conf); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(&spi->dev, mgr); + mgr = devm_fpga_mgr_register(&spi->dev, conf->mgr_name, + &altera_ps_ops, conf); + return PTR_ERR_OR_ZERO(mgr); } static const struct spi_device_id altera_ps_spi_ids[] = { diff --git a/drivers/fpga/dfl-fme-mgr.c b/drivers/fpga/dfl-fme-mgr.c index 313420405d5e..af0785783b52 100644 --- a/drivers/fpga/dfl-fme-mgr.c +++ b/drivers/fpga/dfl-fme-mgr.c @@ -276,7 +276,7 @@ static void fme_mgr_get_compat_id(void __iomem *fme_pr, static int fme_mgr_probe(struct platform_device *pdev) { struct dfl_fme_mgr_pdata *pdata = dev_get_platdata(&pdev->dev); - struct fpga_compat_id *compat_id; + struct fpga_manager_info info = { 0 }; struct device *dev = &pdev->dev; struct fme_mgr_priv *priv; struct fpga_manager *mgr; @@ -296,20 +296,16 @@ static int fme_mgr_probe(struct platform_device *pdev) return PTR_ERR(priv->ioaddr); } - compat_id = devm_kzalloc(dev, sizeof(*compat_id), GFP_KERNEL); - if (!compat_id) + info.name = "DFL FME FPGA Manager"; + info.mops = &fme_mgr_ops; + info.priv = priv; + info.compat_id = devm_kzalloc(dev, sizeof(*info.compat_id), GFP_KERNEL); + if (!info.compat_id) return -ENOMEM; - fme_mgr_get_compat_id(priv->ioaddr, compat_id); - - mgr = devm_fpga_mgr_create(dev, "DFL FME FPGA Manager", - &fme_mgr_ops, priv); - if (!mgr) - return -ENOMEM; - - mgr->compat_id = compat_id; - - return devm_fpga_mgr_register(dev, mgr); + fme_mgr_get_compat_id(priv->ioaddr, info.compat_id); + mgr = devm_fpga_mgr_register_full(dev, &info); + return PTR_ERR_OR_ZERO(mgr); } static struct platform_driver fme_mgr_driver = { diff --git a/drivers/fpga/fpga-mgr.c b/drivers/fpga/fpga-mgr.c index aa30889e2320..d49a9ce34568 100644 --- a/drivers/fpga/fpga-mgr.c +++ b/drivers/fpga/fpga-mgr.c @@ -592,49 +592,49 @@ void fpga_mgr_unlock(struct fpga_manager *mgr) EXPORT_SYMBOL_GPL(fpga_mgr_unlock); /** - * fpga_mgr_create - create and initialize an FPGA manager struct + * fpga_mgr_register_full - create and register an FPGA Manager device * @parent: fpga manager device from pdev - * @name: fpga manager name - * @mops: pointer to structure of fpga manager ops - * @priv: fpga manager private data + * @info: parameters for fpga manager * - * The caller of this function is responsible for freeing the struct with - * fpga_mgr_free(). Using devm_fpga_mgr_create() instead is recommended. + * The caller of this function is responsible for calling fpga_mgr_unregister(). + * Using devm_fpga_mgr_register_full() instead is recommended. * - * Return: pointer to struct fpga_manager or NULL + * Return: pointer to struct fpga_manager pointer or ERR_PTR() */ -struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name, - const struct fpga_manager_ops *mops, - void *priv) +struct fpga_manager * +fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info) { + const struct fpga_manager_ops *mops = info->mops; struct fpga_manager *mgr; int id, ret; if (!mops) { dev_err(parent, "Attempt to register without fpga_manager_ops\n"); - return NULL; + return ERR_PTR(-EINVAL); } - if (!name || !strlen(name)) { + if (!info->name || !strlen(info->name)) { dev_err(parent, "Attempt to register with no name!\n"); - return NULL; + return ERR_PTR(-EINVAL); } mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); if (!mgr) - return NULL; + return ERR_PTR(-ENOMEM); id = ida_simple_get(&fpga_mgr_ida, 0, 0, GFP_KERNEL); - if (id < 0) + if (id < 0) { + ret = id; goto error_kfree; + } mutex_init(&mgr->ref_mutex); - mgr->name = name; - mgr->mops = mops; - mgr->priv = priv; + mgr->name = info->name; + mgr->mops = info->mops; + mgr->priv = info->priv; + mgr->compat_id = info->compat_id; - device_initialize(&mgr->dev); mgr->dev.class = fpga_mgr_class; mgr->dev.groups = mops->groups; mgr->dev.parent = parent; @@ -645,6 +645,19 @@ struct fpga_manager *fpga_mgr_create(struct device *parent, const char *name, if (ret) goto error_device; + /* + * Initialize framework state by requesting low level driver read state + * from device. FPGA may be in reset mode or may have been programmed + * by bootloader or EEPROM. + */ + mgr->state = fpga_mgr_state(mgr); + + ret = device_register(&mgr->dev); + if (ret) { + put_device(&mgr->dev); + return ERR_PTR(ret); + } + return mgr; error_device: @@ -652,96 +665,36 @@ error_device: error_kfree: kfree(mgr); - return NULL; + return ERR_PTR(ret); } -EXPORT_SYMBOL_GPL(fpga_mgr_create); +EXPORT_SYMBOL_GPL(fpga_mgr_register_full); /** - * fpga_mgr_free - free an FPGA manager created with fpga_mgr_create() - * @mgr: fpga manager struct - */ -void fpga_mgr_free(struct fpga_manager *mgr) -{ - ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); - kfree(mgr); -} -EXPORT_SYMBOL_GPL(fpga_mgr_free); - -static void devm_fpga_mgr_release(struct device *dev, void *res) -{ - struct fpga_mgr_devres *dr = res; - - fpga_mgr_free(dr->mgr); -} - -/** - * devm_fpga_mgr_create - create and initialize a managed FPGA manager struct + * fpga_mgr_register - create and register an FPGA Manager device * @parent: fpga manager device from pdev * @name: fpga manager name * @mops: pointer to structure of fpga manager ops * @priv: fpga manager private data * - * This function is intended for use in an FPGA manager driver's probe function. - * After the manager driver creates the manager struct with - * devm_fpga_mgr_create(), it should register it with fpga_mgr_register(). The - * manager driver's remove function should call fpga_mgr_unregister(). The - * manager struct allocated with this function will be freed automatically on - * driver detach. This includes the case of a probe function returning error - * before calling fpga_mgr_register(), the struct will still get cleaned up. + * The caller of this function is responsible for calling fpga_mgr_unregister(). + * Using devm_fpga_mgr_register() instead is recommended. This simple + * version of the register function should be sufficient for most users. The + * fpga_mgr_register_full() function is available for users that need to pass + * additional, optional parameters. * - * Return: pointer to struct fpga_manager or NULL + * Return: pointer to struct fpga_manager pointer or ERR_PTR() */ -struct fpga_manager *devm_fpga_mgr_create(struct device *parent, const char *name, - const struct fpga_manager_ops *mops, - void *priv) +struct fpga_manager * +fpga_mgr_register(struct device *parent, const char *name, + const struct fpga_manager_ops *mops, void *priv) { - struct fpga_mgr_devres *dr; + struct fpga_manager_info info = { 0 }; - dr = devres_alloc(devm_fpga_mgr_release, sizeof(*dr), GFP_KERNEL); - if (!dr) - return NULL; + info.name = name; + info.mops = mops; + info.priv = priv; - dr->mgr = fpga_mgr_create(parent, name, mops, priv); - if (!dr->mgr) { - devres_free(dr); - return NULL; - } - - devres_add(parent, dr); - - return dr->mgr; -} -EXPORT_SYMBOL_GPL(devm_fpga_mgr_create); - -/** - * fpga_mgr_register - register an FPGA manager - * @mgr: fpga manager struct - * - * Return: 0 on success, negative error code otherwise. - */ -int fpga_mgr_register(struct fpga_manager *mgr) -{ - int ret; - - /* - * Initialize framework state by requesting low level driver read state - * from device. FPGA may be in reset mode or may have been programmed - * by bootloader or EEPROM. - */ - mgr->state = fpga_mgr_state(mgr); - - ret = device_add(&mgr->dev); - if (ret) - goto error_device; - - dev_info(&mgr->dev, "%s registered\n", mgr->name); - - return 0; - -error_device: - ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); - - return ret; + return fpga_mgr_register_full(parent, &info); } EXPORT_SYMBOL_GPL(fpga_mgr_register); @@ -765,14 +718,6 @@ void fpga_mgr_unregister(struct fpga_manager *mgr) } EXPORT_SYMBOL_GPL(fpga_mgr_unregister); -static int fpga_mgr_devres_match(struct device *dev, void *res, - void *match_data) -{ - struct fpga_mgr_devres *dr = res; - - return match_data == dr->mgr; -} - static void devm_fpga_mgr_unregister(struct device *dev, void *res) { struct fpga_mgr_devres *dr = res; @@ -781,45 +726,67 @@ static void devm_fpga_mgr_unregister(struct device *dev, void *res) } /** - * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register() - * @dev: managing device for this FPGA manager - * @mgr: fpga manager struct + * devm_fpga_mgr_register_full - resource managed variant of fpga_mgr_register() + * @parent: fpga manager device from pdev + * @info: parameters for fpga manager * - * This is the devres variant of fpga_mgr_register() for which the unregister + * This is the devres variant of fpga_mgr_register_full() for which the unregister * function will be called automatically when the managing device is detached. */ -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr) +struct fpga_manager * +devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info) { struct fpga_mgr_devres *dr; - int ret; - - /* - * Make sure that the struct fpga_manager * that is passed in is - * managed itself. - */ - if (WARN_ON(!devres_find(dev, devm_fpga_mgr_release, - fpga_mgr_devres_match, mgr))) - return -EINVAL; + struct fpga_manager *mgr; dr = devres_alloc(devm_fpga_mgr_unregister, sizeof(*dr), GFP_KERNEL); if (!dr) - return -ENOMEM; + return ERR_PTR(-ENOMEM); - ret = fpga_mgr_register(mgr); - if (ret) { + mgr = fpga_mgr_register_full(parent, info); + if (IS_ERR(mgr)) { devres_free(dr); - return ret; + return mgr; } dr->mgr = mgr; - devres_add(dev, dr); + devres_add(parent, dr); - return 0; + return mgr; +} +EXPORT_SYMBOL_GPL(devm_fpga_mgr_register_full); + +/** + * devm_fpga_mgr_register - resource managed variant of fpga_mgr_register() + * @parent: fpga manager device from pdev + * @name: fpga manager name + * @mops: pointer to structure of fpga manager ops + * @priv: fpga manager private data + * + * This is the devres variant of fpga_mgr_register() for which the + * unregister function will be called automatically when the managing + * device is detached. + */ +struct fpga_manager * +devm_fpga_mgr_register(struct device *parent, const char *name, + const struct fpga_manager_ops *mops, void *priv) +{ + struct fpga_manager_info info = { 0 }; + + info.name = name; + info.mops = mops; + info.priv = priv; + + return devm_fpga_mgr_register_full(parent, &info); } EXPORT_SYMBOL_GPL(devm_fpga_mgr_register); static void fpga_mgr_dev_release(struct device *dev) { + struct fpga_manager *mgr = to_fpga_manager(dev); + + ida_simple_remove(&fpga_mgr_ida, mgr->dev.id); + kfree(mgr); } static int __init fpga_mgr_class_init(void) diff --git a/drivers/fpga/ice40-spi.c b/drivers/fpga/ice40-spi.c index 029d3cdb918d..7cbb3558b844 100644 --- a/drivers/fpga/ice40-spi.c +++ b/drivers/fpga/ice40-spi.c @@ -178,12 +178,9 @@ static int ice40_fpga_probe(struct spi_device *spi) return ret; } - mgr = devm_fpga_mgr_create(dev, "Lattice iCE40 FPGA Manager", - &ice40_fpga_ops, priv); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(dev, mgr); + mgr = devm_fpga_mgr_register(dev, "Lattice iCE40 FPGA Manager", + &ice40_fpga_ops, priv); + return PTR_ERR_OR_ZERO(mgr); } static const struct of_device_id ice40_fpga_of_match[] = { diff --git a/drivers/fpga/machxo2-spi.c b/drivers/fpga/machxo2-spi.c index ea2ec3c6815c..905607992a12 100644 --- a/drivers/fpga/machxo2-spi.c +++ b/drivers/fpga/machxo2-spi.c @@ -370,12 +370,9 @@ static int machxo2_spi_probe(struct spi_device *spi) return -EINVAL; } - mgr = devm_fpga_mgr_create(dev, "Lattice MachXO2 SPI FPGA Manager", - &machxo2_ops, spi); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(dev, mgr); + mgr = devm_fpga_mgr_register(dev, "Lattice MachXO2 SPI FPGA Manager", + &machxo2_ops, spi); + return PTR_ERR_OR_ZERO(mgr); } #ifdef CONFIG_OF diff --git a/drivers/fpga/socfpga-a10.c b/drivers/fpga/socfpga-a10.c index 573d88bdf730..ac8e89b8a5cc 100644 --- a/drivers/fpga/socfpga-a10.c +++ b/drivers/fpga/socfpga-a10.c @@ -508,19 +508,15 @@ static int socfpga_a10_fpga_probe(struct platform_device *pdev) return -EBUSY; } - mgr = devm_fpga_mgr_create(dev, "SoCFPGA Arria10 FPGA Manager", - &socfpga_a10_fpga_mgr_ops, priv); - if (!mgr) - return -ENOMEM; - - platform_set_drvdata(pdev, mgr); - - ret = fpga_mgr_register(mgr); - if (ret) { + mgr = fpga_mgr_register(dev, "SoCFPGA Arria10 FPGA Manager", + &socfpga_a10_fpga_mgr_ops, priv); + if (IS_ERR(mgr)) { clk_disable_unprepare(priv->clk); - return ret; + return PTR_ERR(mgr); } + platform_set_drvdata(pdev, mgr); + return 0; } diff --git a/drivers/fpga/socfpga.c b/drivers/fpga/socfpga.c index 1f467173fc1f..7e0741f99696 100644 --- a/drivers/fpga/socfpga.c +++ b/drivers/fpga/socfpga.c @@ -571,12 +571,9 @@ static int socfpga_fpga_probe(struct platform_device *pdev) if (ret) return ret; - mgr = devm_fpga_mgr_create(dev, "Altera SOCFPGA FPGA Manager", - &socfpga_fpga_ops, priv); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(dev, mgr); + mgr = devm_fpga_mgr_register(dev, "Altera SOCFPGA FPGA Manager", + &socfpga_fpga_ops, priv); + return PTR_ERR_OR_ZERO(mgr); } #ifdef CONFIG_OF diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 047fd7f23706..737d14c6e0de 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -419,18 +419,11 @@ static int s10_probe(struct platform_device *pdev) init_completion(&priv->status_return_completion); - mgr = fpga_mgr_create(dev, "Stratix10 SOC FPGA Manager", - &s10_ops, priv); - if (!mgr) { - dev_err(dev, "unable to create FPGA manager\n"); - ret = -ENOMEM; - goto probe_err; - } - - ret = fpga_mgr_register(mgr); - if (ret) { + mgr = fpga_mgr_register(dev, "Stratix10 SOC FPGA Manager", + &s10_ops, priv); + if (IS_ERR(mgr)) { dev_err(dev, "unable to register FPGA manager\n"); - fpga_mgr_free(mgr); + ret = PTR_ERR(mgr); goto probe_err; } @@ -448,7 +441,6 @@ static int s10_remove(struct platform_device *pdev) struct s10_priv *priv = mgr->priv; fpga_mgr_unregister(mgr); - fpga_mgr_free(mgr); stratix10_svc_free_channel(priv->chan); return 0; diff --git a/drivers/fpga/ts73xx-fpga.c b/drivers/fpga/ts73xx-fpga.c index 167abb0b08d4..8e6e9c840d9d 100644 --- a/drivers/fpga/ts73xx-fpga.c +++ b/drivers/fpga/ts73xx-fpga.c @@ -116,12 +116,9 @@ static int ts73xx_fpga_probe(struct platform_device *pdev) if (IS_ERR(priv->io_base)) return PTR_ERR(priv->io_base); - mgr = devm_fpga_mgr_create(kdev, "TS-73xx FPGA Manager", - &ts73xx_fpga_ops, priv); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(kdev, mgr); + mgr = devm_fpga_mgr_register(kdev, "TS-73xx FPGA Manager", + &ts73xx_fpga_ops, priv); + return PTR_ERR_OR_ZERO(mgr); } static struct platform_driver ts73xx_fpga_driver = { diff --git a/drivers/fpga/versal-fpga.c b/drivers/fpga/versal-fpga.c index 5b0dda304bd2..e1601b3a345b 100644 --- a/drivers/fpga/versal-fpga.c +++ b/drivers/fpga/versal-fpga.c @@ -54,12 +54,9 @@ static int versal_fpga_probe(struct platform_device *pdev) return ret; } - mgr = devm_fpga_mgr_create(dev, "Xilinx Versal FPGA Manager", - &versal_fpga_ops, NULL); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(dev, mgr); + mgr = devm_fpga_mgr_register(dev, "Xilinx Versal FPGA Manager", + &versal_fpga_ops, NULL); + return PTR_ERR_OR_ZERO(mgr); } static const struct of_device_id versal_fpga_of_match[] = { diff --git a/drivers/fpga/xilinx-spi.c b/drivers/fpga/xilinx-spi.c index b6bcf1d9233d..e1a227e7ff2a 100644 --- a/drivers/fpga/xilinx-spi.c +++ b/drivers/fpga/xilinx-spi.c @@ -247,13 +247,10 @@ static int xilinx_spi_probe(struct spi_device *spi) return dev_err_probe(&spi->dev, PTR_ERR(conf->done), "Failed to get DONE gpio\n"); - mgr = devm_fpga_mgr_create(&spi->dev, - "Xilinx Slave Serial FPGA Manager", - &xilinx_spi_ops, conf); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(&spi->dev, mgr); + mgr = devm_fpga_mgr_register(&spi->dev, + "Xilinx Slave Serial FPGA Manager", + &xilinx_spi_ops, conf); + return PTR_ERR_OR_ZERO(mgr); } #ifdef CONFIG_OF diff --git a/drivers/fpga/zynq-fpga.c b/drivers/fpga/zynq-fpga.c index 9b75bd4f93d8..426aa34c6a0d 100644 --- a/drivers/fpga/zynq-fpga.c +++ b/drivers/fpga/zynq-fpga.c @@ -609,20 +609,16 @@ static int zynq_fpga_probe(struct platform_device *pdev) clk_disable(priv->clk); - mgr = devm_fpga_mgr_create(dev, "Xilinx Zynq FPGA Manager", - &zynq_fpga_ops, priv); - if (!mgr) - return -ENOMEM; - - platform_set_drvdata(pdev, mgr); - - err = fpga_mgr_register(mgr); - if (err) { + mgr = fpga_mgr_register(dev, "Xilinx Zynq FPGA Manager", + &zynq_fpga_ops, priv); + if (IS_ERR(mgr)) { dev_err(dev, "unable to register FPGA manager\n"); clk_unprepare(priv->clk); - return err; + return PTR_ERR(mgr); } + platform_set_drvdata(pdev, mgr); + return 0; } diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c index 7d3d5650c322..c60f20949c47 100644 --- a/drivers/fpga/zynqmp-fpga.c +++ b/drivers/fpga/zynqmp-fpga.c @@ -95,12 +95,9 @@ static int zynqmp_fpga_probe(struct platform_device *pdev) priv->dev = dev; - mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager", - &zynqmp_fpga_ops, priv); - if (!mgr) - return -ENOMEM; - - return devm_fpga_mgr_register(dev, mgr); + mgr = devm_fpga_mgr_register(dev, "Xilinx ZynqMP FPGA Manager", + &zynqmp_fpga_ops, priv); + return PTR_ERR_OR_ZERO(mgr); } #ifdef CONFIG_OF diff --git a/include/linux/fpga/fpga-mgr.h b/include/linux/fpga/fpga-mgr.h index 474c1f506307..0f9468771bb9 100644 --- a/include/linux/fpga/fpga-mgr.h +++ b/include/linux/fpga/fpga-mgr.h @@ -105,6 +105,36 @@ struct fpga_image_info { #endif }; +/** + * struct fpga_compat_id - id for compatibility check + * + * @id_h: high 64bit of the compat_id + * @id_l: low 64bit of the compat_id + */ +struct fpga_compat_id { + u64 id_h; + u64 id_l; +}; + +/** + * struct fpga_manager_info - collection of parameters for an FPGA Manager + * @name: fpga manager name + * @compat_id: FPGA manager id for compatibility check. + * @mops: pointer to structure of fpga manager ops + * @priv: fpga manager private data + * + * fpga_manager_info contains parameters for the register_full function. + * These are separated into an info structure because they some are optional + * others could be added to in the future. The info structure facilitates + * maintaining a stable API. + */ +struct fpga_manager_info { + const char *name; + struct fpga_compat_id *compat_id; + const struct fpga_manager_ops *mops; + void *priv; +}; + /** * struct fpga_manager_ops - ops for low level fpga manager drivers * @initial_header_size: Maximum number of bytes that should be passed into write_init @@ -143,17 +173,6 @@ struct fpga_manager_ops { #define FPGA_MGR_STATUS_IP_PROTOCOL_ERR BIT(3) #define FPGA_MGR_STATUS_FIFO_OVERFLOW_ERR BIT(4) -/** - * struct fpga_compat_id - id for compatibility check - * - * @id_h: high 64bit of the compat_id - * @id_l: low 64bit of the compat_id - */ -struct fpga_compat_id { - u64 id_h; - u64 id_l; -}; - /** * struct fpga_manager - fpga manager structure * @name: name of low level fpga manager @@ -191,17 +210,18 @@ struct fpga_manager *fpga_mgr_get(struct device *dev); void fpga_mgr_put(struct fpga_manager *mgr); -struct fpga_manager *fpga_mgr_create(struct device *dev, const char *name, - const struct fpga_manager_ops *mops, - void *priv); -void fpga_mgr_free(struct fpga_manager *mgr); -int fpga_mgr_register(struct fpga_manager *mgr); -void fpga_mgr_unregister(struct fpga_manager *mgr); +struct fpga_manager * +fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info); -int devm_fpga_mgr_register(struct device *dev, struct fpga_manager *mgr); +struct fpga_manager * +fpga_mgr_register(struct device *parent, const char *name, + const struct fpga_manager_ops *mops, void *priv); +void fpga_mgr_unregister(struct fpga_manager *mgr); -struct fpga_manager *devm_fpga_mgr_create(struct device *dev, const char *name, - const struct fpga_manager_ops *mops, - void *priv); +struct fpga_manager * +devm_fpga_mgr_register_full(struct device *parent, const struct fpga_manager_info *info); +struct fpga_manager * +devm_fpga_mgr_register(struct device *parent, const char *name, + const struct fpga_manager_ops *mops, void *priv); #endif /*_LINUX_FPGA_MGR_H */ -- cgit v1.2.3 From 0d70af3c2530a70f1b2c197feaa63fbd3548ce34 Mon Sep 17 00:00:00 2001 From: Russ Weight Date: Thu, 18 Nov 2021 17:55:52 -0800 Subject: fpga: bridge: Use standard dev_release for class driver The FPGA bridge class driver data structure is being treated as a managed resource instead of using the standard dev_release call-back function to release the class data structure. This change removes the managed resource code and combines the create() and register() functions into a single register() function. Signed-off-by: Russ Weight Reviewed-by: Xu Yilun Acked-by: Xu Yilun Signed-off-by: Moritz Fischer --- Documentation/driver-api/fpga/fpga-bridge.rst | 6 +- drivers/fpga/altera-fpga2sdram.c | 12 +-- drivers/fpga/altera-freeze-bridge.c | 10 +-- drivers/fpga/altera-hps2fpga.c | 12 +-- drivers/fpga/dfl-fme-br.c | 10 +-- drivers/fpga/fpga-bridge.c | 122 ++++++-------------------- drivers/fpga/xilinx-pr-decoupler.c | 17 ++-- include/linux/fpga/fpga-bridge.h | 30 +++++-- 8 files changed, 74 insertions(+), 145 deletions(-) diff --git a/Documentation/driver-api/fpga/fpga-bridge.rst b/Documentation/driver-api/fpga/fpga-bridge.rst index 8d650b4e2ce6..604208534095 100644 --- a/Documentation/driver-api/fpga/fpga-bridge.rst +++ b/Documentation/driver-api/fpga/fpga-bridge.rst @@ -6,8 +6,7 @@ API to implement a new FPGA bridge * struct fpga_bridge - The FPGA Bridge structure * struct fpga_bridge_ops - Low level Bridge driver ops -* devm_fpga_bridge_create() - Allocate and init a bridge struct -* fpga_bridge_register() - Register a bridge +* fpga_bridge_register() - Create and register a bridge * fpga_bridge_unregister() - Unregister a bridge .. kernel-doc:: include/linux/fpga/fpga-bridge.h @@ -16,9 +15,6 @@ API to implement a new FPGA bridge .. kernel-doc:: include/linux/fpga/fpga-bridge.h :functions: fpga_bridge_ops -.. kernel-doc:: drivers/fpga/fpga-bridge.c - :functions: devm_fpga_bridge_create - .. kernel-doc:: drivers/fpga/fpga-bridge.c :functions: fpga_bridge_register diff --git a/drivers/fpga/altera-fpga2sdram.c b/drivers/fpga/altera-fpga2sdram.c index a78e49c63c64..ff3a646fd9e3 100644 --- a/drivers/fpga/altera-fpga2sdram.c +++ b/drivers/fpga/altera-fpga2sdram.c @@ -121,17 +121,13 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) /* Get f2s bridge configuration saved in handoff register */ regmap_read(sysmgr, SYSMGR_ISWGRP_HANDOFF3, &priv->mask); - br = devm_fpga_bridge_create(dev, F2S_BRIDGE_NAME, - &altera_fpga2sdram_br_ops, priv); - if (!br) - return -ENOMEM; + br = fpga_bridge_register(dev, F2S_BRIDGE_NAME, + &altera_fpga2sdram_br_ops, priv); + if (IS_ERR(br)) + return PTR_ERR(br); platform_set_drvdata(pdev, br); - ret = fpga_bridge_register(br); - if (ret) - return ret; - dev_info(dev, "driver initialized with handoff %08x\n", priv->mask); if (!of_property_read_u32(dev->of_node, "bridge-enable", &enable)) { diff --git a/drivers/fpga/altera-freeze-bridge.c b/drivers/fpga/altera-freeze-bridge.c index 7d22a44d652e..445f4b011167 100644 --- a/drivers/fpga/altera-freeze-bridge.c +++ b/drivers/fpga/altera-freeze-bridge.c @@ -246,14 +246,14 @@ static int altera_freeze_br_probe(struct platform_device *pdev) priv->base_addr = base_addr; - br = devm_fpga_bridge_create(dev, FREEZE_BRIDGE_NAME, - &altera_freeze_br_br_ops, priv); - if (!br) - return -ENOMEM; + br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME, + &altera_freeze_br_br_ops, priv); + if (IS_ERR(br)) + return PTR_ERR(br); platform_set_drvdata(pdev, br); - return fpga_bridge_register(br); + return 0; } static int altera_freeze_br_remove(struct platform_device *pdev) diff --git a/drivers/fpga/altera-hps2fpga.c b/drivers/fpga/altera-hps2fpga.c index 77b95f251821..aa758426c22b 100644 --- a/drivers/fpga/altera-hps2fpga.c +++ b/drivers/fpga/altera-hps2fpga.c @@ -180,19 +180,15 @@ static int alt_fpga_bridge_probe(struct platform_device *pdev) } } - br = devm_fpga_bridge_create(dev, priv->name, - &altera_hps2fpga_br_ops, priv); - if (!br) { - ret = -ENOMEM; + br = fpga_bridge_register(dev, priv->name, + &altera_hps2fpga_br_ops, priv); + if (IS_ERR(br)) { + ret = PTR_ERR(br); goto err; } platform_set_drvdata(pdev, br); - ret = fpga_bridge_register(br); - if (ret) - goto err; - return 0; err: diff --git a/drivers/fpga/dfl-fme-br.c b/drivers/fpga/dfl-fme-br.c index 3ff9f3a687ce..808d1f4d76df 100644 --- a/drivers/fpga/dfl-fme-br.c +++ b/drivers/fpga/dfl-fme-br.c @@ -68,14 +68,14 @@ static int fme_br_probe(struct platform_device *pdev) priv->pdata = dev_get_platdata(dev); - br = devm_fpga_bridge_create(dev, "DFL FPGA FME Bridge", - &fme_bridge_ops, priv); - if (!br) - return -ENOMEM; + br = fpga_bridge_register(dev, "DFL FPGA FME Bridge", + &fme_bridge_ops, priv); + if (IS_ERR(br)) + return PTR_ERR(br); platform_set_drvdata(pdev, br); - return fpga_bridge_register(br); + return 0; } static int fme_br_remove(struct platform_device *pdev) diff --git a/drivers/fpga/fpga-bridge.c b/drivers/fpga/fpga-bridge.c index 798f55670646..16f2b164a178 100644 --- a/drivers/fpga/fpga-bridge.c +++ b/drivers/fpga/fpga-bridge.c @@ -312,36 +312,41 @@ static struct attribute *fpga_bridge_attrs[] = { ATTRIBUTE_GROUPS(fpga_bridge); /** - * fpga_bridge_create - create and initialize a struct fpga_bridge + * fpga_bridge_register - create and register an FPGA Bridge device * @parent: FPGA bridge device from pdev * @name: FPGA bridge name * @br_ops: pointer to structure of fpga bridge ops * @priv: FPGA bridge private data * - * The caller of this function is responsible for freeing the bridge with - * fpga_bridge_free(). Using devm_fpga_bridge_create() instead is recommended. - * - * Return: struct fpga_bridge or NULL + * Return: struct fpga_bridge pointer or ERR_PTR() */ -struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, - const struct fpga_bridge_ops *br_ops, - void *priv) +struct fpga_bridge * +fpga_bridge_register(struct device *parent, const char *name, + const struct fpga_bridge_ops *br_ops, + void *priv) { struct fpga_bridge *bridge; int id, ret; + if (!br_ops) { + dev_err(parent, "Attempt to register without fpga_bridge_ops\n"); + return ERR_PTR(-EINVAL); + } + if (!name || !strlen(name)) { dev_err(parent, "Attempt to register with no name!\n"); - return NULL; + return ERR_PTR(-EINVAL); } bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); if (!bridge) - return NULL; + return ERR_PTR(-ENOMEM); id = ida_simple_get(&fpga_bridge_ida, 0, 0, GFP_KERNEL); - if (id < 0) + if (id < 0) { + ret = id; goto error_kfree; + } mutex_init(&bridge->mutex); INIT_LIST_HEAD(&bridge->node); @@ -350,17 +355,23 @@ struct fpga_bridge *fpga_bridge_create(struct device *parent, const char *name, bridge->br_ops = br_ops; bridge->priv = priv; - device_initialize(&bridge->dev); bridge->dev.groups = br_ops->groups; bridge->dev.class = fpga_bridge_class; bridge->dev.parent = parent; bridge->dev.of_node = parent->of_node; bridge->dev.id = id; + of_platform_populate(bridge->dev.of_node, NULL, NULL, &bridge->dev); ret = dev_set_name(&bridge->dev, "br%d", id); if (ret) goto error_device; + ret = device_register(&bridge->dev); + if (ret) { + put_device(&bridge->dev); + return ERR_PTR(ret); + } + return bridge; error_device: @@ -368,88 +379,7 @@ error_device: error_kfree: kfree(bridge); - return NULL; -} -EXPORT_SYMBOL_GPL(fpga_bridge_create); - -/** - * fpga_bridge_free - free an fpga bridge created by fpga_bridge_create() - * @bridge: FPGA bridge struct - */ -void fpga_bridge_free(struct fpga_bridge *bridge) -{ - ida_simple_remove(&fpga_bridge_ida, bridge->dev.id); - kfree(bridge); -} -EXPORT_SYMBOL_GPL(fpga_bridge_free); - -static void devm_fpga_bridge_release(struct device *dev, void *res) -{ - struct fpga_bridge *bridge = *(struct fpga_bridge **)res; - - fpga_bridge_free(bridge); -} - -/** - * devm_fpga_bridge_create - create and init a managed struct fpga_bridge - * @parent: FPGA bridge device from pdev - * @name: FPGA bridge name - * @br_ops: pointer to structure of fpga bridge ops - * @priv: FPGA bridge private data - * - * This function is intended for use in an FPGA bridge driver's probe function. - * After the bridge driver creates the struct with devm_fpga_bridge_create(), it - * should register the bridge with fpga_bridge_register(). The bridge driver's - * remove function should call fpga_bridge_unregister(). The bridge struct - * allocated with this function will be freed automatically on driver detach. - * This includes the case of a probe function returning error before calling - * fpga_bridge_register(), the struct will still get cleaned up. - * - * Return: struct fpga_bridge or NULL - */ -struct fpga_bridge -*devm_fpga_bridge_create(struct device *parent, const char *name, - const struct fpga_bridge_ops *br_ops, void *priv) -{ - struct fpga_bridge **ptr, *bridge; - - ptr = devres_alloc(devm_fpga_bridge_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; - - bridge = fpga_bridge_create(parent, name, br_ops, priv); - if (!bridge) { - devres_free(ptr); - } else { - *ptr = bridge; - devres_add(parent, ptr); - } - - return bridge; -} -EXPORT_SYMBOL_GPL(devm_fpga_bridge_create); - -/** - * fpga_bridge_register - register an FPGA bridge - * - * @bridge: FPGA bridge struct - * - * Return: 0 for success, error code otherwise. - */ -int fpga_bridge_register(struct fpga_bridge *bridge) -{ - struct device *dev = &bridge->dev; - int ret; - - ret = device_add(dev); - if (ret) - return ret; - - of_platform_populate(dev->of_node, NULL, NULL, dev); - - dev_info(dev->parent, "fpga bridge [%s] registered\n", bridge->name); - - return 0; + return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(fpga_bridge_register); @@ -475,6 +405,10 @@ EXPORT_SYMBOL_GPL(fpga_bridge_unregister); static void fpga_bridge_dev_release(struct device *dev) { + struct fpga_bridge *bridge = to_fpga_bridge(dev); + + ida_simple_remove(&fpga_bridge_ida, bridge->dev.id); + kfree(bridge); } static int __init fpga_bridge_dev_init(void) diff --git a/drivers/fpga/xilinx-pr-decoupler.c b/drivers/fpga/xilinx-pr-decoupler.c index e986ed47c4ed..2d9c491f7be9 100644 --- a/drivers/fpga/xilinx-pr-decoupler.c +++ b/drivers/fpga/xilinx-pr-decoupler.c @@ -140,22 +140,17 @@ static int xlnx_pr_decoupler_probe(struct platform_device *pdev) clk_disable(priv->clk); - br = devm_fpga_bridge_create(&pdev->dev, priv->ipconfig->name, - &xlnx_pr_decoupler_br_ops, priv); - if (!br) { - err = -ENOMEM; - goto err_clk; - } - - platform_set_drvdata(pdev, br); - - err = fpga_bridge_register(br); - if (err) { + br = fpga_bridge_register(&pdev->dev, priv->ipconfig->name, + &xlnx_pr_decoupler_br_ops, priv); + if (IS_ERR(br)) { + err = PTR_ERR(br); dev_err(&pdev->dev, "unable to register %s", priv->ipconfig->name); goto err_clk; } + platform_set_drvdata(pdev, br); + return 0; err_clk: diff --git a/include/linux/fpga/fpga-bridge.h b/include/linux/fpga/fpga-bridge.h index 6c3c28806ff1..223da48a6d18 100644 --- a/include/linux/fpga/fpga-bridge.h +++ b/include/linux/fpga/fpga-bridge.h @@ -22,6 +22,23 @@ struct fpga_bridge_ops { const struct attribute_group **groups; }; +/** + * struct fpga_bridge_info - collection of parameters an FPGA Bridge + * @name: fpga bridge name + * @br_ops: pointer to structure of fpga bridge ops + * @priv: fpga bridge private data + * + * fpga_bridge_info contains parameters for the register function. These + * are separated into an info structure because they some are optional + * others could be added to in the future. The info structure facilitates + * maintaining a stable API. + */ +struct fpga_bridge_info { + const char *name; + const struct fpga_bridge_ops *br_ops; + void *priv; +}; + /** * struct fpga_bridge - FPGA bridge structure * @name: name of low level FPGA bridge @@ -62,15 +79,10 @@ int of_fpga_bridge_get_to_list(struct device_node *np, struct fpga_image_info *info, struct list_head *bridge_list); -struct fpga_bridge *fpga_bridge_create(struct device *dev, const char *name, - const struct fpga_bridge_ops *br_ops, - void *priv); -void fpga_bridge_free(struct fpga_bridge *br); -int fpga_bridge_register(struct fpga_bridge *br); +struct fpga_bridge * +fpga_bridge_register(struct device *parent, const char *name, + const struct fpga_bridge_ops *br_ops, + void *priv); void fpga_bridge_unregister(struct fpga_bridge *br); -struct fpga_bridge -*devm_fpga_bridge_create(struct device *dev, const char *name, - const struct fpga_bridge_ops *br_ops, void *priv); - #endif /* _LINUX_FPGA_BRIDGE_H */ -- cgit v1.2.3 From 8886a579744fbfa53e69aa453ed10ae3b1f9abac Mon Sep 17 00:00:00 2001 From: Russ Weight Date: Thu, 18 Nov 2021 17:55:53 -0800 Subject: fpga: region: Use standard dev_release for class driver The FPGA region class driver data structure is being treated as a managed resource instead of using the standard dev_release call-back function to release the class data structure. This change removes the managed resource code and combines the create() and register() functions into a single register() or register_full() function. The register_full() function accepts an info data structure to provide flexibility in passing optional parameters. The register() function supports the current parameter list for users that don't require the use of optional parameters. Signed-off-by: Russ Weight Reviewed-by: Xu Yilun Acked-by: Xu Yilun Signed-off-by: Moritz Fischer --- Documentation/driver-api/fpga/fpga-region.rst | 12 ++- drivers/fpga/dfl-fme-region.c | 17 ++-- drivers/fpga/dfl.c | 12 +-- drivers/fpga/fpga-region.c | 119 ++++++++++---------------- drivers/fpga/of-fpga-region.c | 10 +-- include/linux/fpga/fpga-region.h | 36 ++++++-- 6 files changed, 95 insertions(+), 111 deletions(-) diff --git a/Documentation/driver-api/fpga/fpga-region.rst b/Documentation/driver-api/fpga/fpga-region.rst index 2636a27c11b2..dc55d60a0b4a 100644 --- a/Documentation/driver-api/fpga/fpga-region.rst +++ b/Documentation/driver-api/fpga/fpga-region.rst @@ -46,8 +46,11 @@ API to add a new FPGA region ---------------------------- * struct fpga_region - The FPGA region struct -* devm_fpga_region_create() - Allocate and init a region struct -* fpga_region_register() - Register an FPGA region +* struct fpga_region_info - Parameter structure for fpga_region_register_full() +* fpga_region_register_full() - Create and register an FPGA region using the + fpga_region_info structure to provide the full flexibility of options +* fpga_region_register() - Create and register an FPGA region using standard + arguments * fpga_region_unregister() - Unregister an FPGA region The FPGA region's probe function will need to get a reference to the FPGA @@ -75,8 +78,11 @@ following APIs to handle building or tearing down that list. .. kernel-doc:: include/linux/fpga/fpga-region.h :functions: fpga_region +.. kernel-doc:: include/linux/fpga/fpga-region.h + :functions: fpga_region_info + .. kernel-doc:: drivers/fpga/fpga-region.c - :functions: devm_fpga_region_create + :functions: fpga_region_register_full .. kernel-doc:: drivers/fpga/fpga-region.c :functions: fpga_region_register diff --git a/drivers/fpga/dfl-fme-region.c b/drivers/fpga/dfl-fme-region.c index 1eeb42af1012..4aebde0a7f1c 100644 --- a/drivers/fpga/dfl-fme-region.c +++ b/drivers/fpga/dfl-fme-region.c @@ -30,6 +30,7 @@ static int fme_region_get_bridges(struct fpga_region *region) static int fme_region_probe(struct platform_device *pdev) { struct dfl_fme_region_pdata *pdata = dev_get_platdata(&pdev->dev); + struct fpga_region_info info = { 0 }; struct device *dev = &pdev->dev; struct fpga_region *region; struct fpga_manager *mgr; @@ -39,20 +40,18 @@ static int fme_region_probe(struct platform_device *pdev) if (IS_ERR(mgr)) return -EPROBE_DEFER; - region = devm_fpga_region_create(dev, mgr, fme_region_get_bridges); - if (!region) { - ret = -ENOMEM; + info.mgr = mgr; + info.compat_id = mgr->compat_id; + info.get_bridges = fme_region_get_bridges; + info.priv = pdata; + region = fpga_region_register_full(dev, &info); + if (IS_ERR(region)) { + ret = PTR_ERR(region); goto eprobe_mgr_put; } - region->priv = pdata; - region->compat_id = mgr->compat_id; platform_set_drvdata(pdev, region); - ret = fpga_region_register(region); - if (ret) - goto eprobe_mgr_put; - dev_dbg(dev, "DFL FME FPGA Region probed\n"); return 0; diff --git a/drivers/fpga/dfl.c b/drivers/fpga/dfl.c index f86666cf2c6a..599bb21d86af 100644 --- a/drivers/fpga/dfl.c +++ b/drivers/fpga/dfl.c @@ -1407,19 +1407,15 @@ dfl_fpga_feature_devs_enumerate(struct dfl_fpga_enum_info *info) if (!cdev) return ERR_PTR(-ENOMEM); - cdev->region = devm_fpga_region_create(info->dev, NULL, NULL); - if (!cdev->region) { - ret = -ENOMEM; - goto free_cdev_exit; - } - cdev->parent = info->dev; mutex_init(&cdev->lock); INIT_LIST_HEAD(&cdev->port_dev_list); - ret = fpga_region_register(cdev->region); - if (ret) + cdev->region = fpga_region_register(info->dev, NULL, NULL); + if (IS_ERR(cdev->region)) { + ret = PTR_ERR(cdev->region); goto free_cdev_exit; + } /* create and init build info for enumeration */ binfo = devm_kzalloc(info->dev, sizeof(*binfo), GFP_KERNEL); diff --git a/drivers/fpga/fpga-region.c b/drivers/fpga/fpga-region.c index a4838715221f..b0ac18de4885 100644 --- a/drivers/fpga/fpga-region.c +++ b/drivers/fpga/fpga-region.c @@ -180,39 +180,42 @@ static struct attribute *fpga_region_attrs[] = { ATTRIBUTE_GROUPS(fpga_region); /** - * fpga_region_create - alloc and init a struct fpga_region + * fpga_region_register_full - create and register an FPGA Region device * @parent: device parent - * @mgr: manager that programs this region - * @get_bridges: optional function to get bridges to a list - * - * The caller of this function is responsible for freeing the resulting region - * struct with fpga_region_free(). Using devm_fpga_region_create() instead is - * recommended. + * @info: parameters for FPGA Region * - * Return: struct fpga_region or NULL + * Return: struct fpga_region or ERR_PTR() */ -struct fpga_region -*fpga_region_create(struct device *parent, - struct fpga_manager *mgr, - int (*get_bridges)(struct fpga_region *)) +struct fpga_region * +fpga_region_register_full(struct device *parent, const struct fpga_region_info *info) { struct fpga_region *region; int id, ret = 0; + if (!info) { + dev_err(parent, + "Attempt to register without required info structure\n"); + return ERR_PTR(-EINVAL); + } + region = kzalloc(sizeof(*region), GFP_KERNEL); if (!region) - return NULL; + return ERR_PTR(-ENOMEM); id = ida_simple_get(&fpga_region_ida, 0, 0, GFP_KERNEL); - if (id < 0) + if (id < 0) { + ret = id; goto err_free; + } + + region->mgr = info->mgr; + region->compat_id = info->compat_id; + region->priv = info->priv; + region->get_bridges = info->get_bridges; - region->mgr = mgr; - region->get_bridges = get_bridges; mutex_init(®ion->mutex); INIT_LIST_HEAD(®ion->bridge_list); - device_initialize(®ion->dev); region->dev.class = fpga_region_class; region->dev.parent = parent; region->dev.of_node = parent->of_node; @@ -222,6 +225,12 @@ struct fpga_region if (ret) goto err_remove; + ret = device_register(®ion->dev); + if (ret) { + put_device(®ion->dev); + return ERR_PTR(ret); + } + return region; err_remove: @@ -229,76 +238,32 @@ err_remove: err_free: kfree(region); - return NULL; -} -EXPORT_SYMBOL_GPL(fpga_region_create); - -/** - * fpga_region_free - free an FPGA region created by fpga_region_create() - * @region: FPGA region - */ -void fpga_region_free(struct fpga_region *region) -{ - ida_simple_remove(&fpga_region_ida, region->dev.id); - kfree(region); -} -EXPORT_SYMBOL_GPL(fpga_region_free); - -static void devm_fpga_region_release(struct device *dev, void *res) -{ - struct fpga_region *region = *(struct fpga_region **)res; - - fpga_region_free(region); + return ERR_PTR(ret); } +EXPORT_SYMBOL_GPL(fpga_region_register_full); /** - * devm_fpga_region_create - create and initialize a managed FPGA region struct + * fpga_region_register - create and register an FPGA Region device * @parent: device parent * @mgr: manager that programs this region * @get_bridges: optional function to get bridges to a list * - * This function is intended for use in an FPGA region driver's probe function. - * After the region driver creates the region struct with - * devm_fpga_region_create(), it should register it with fpga_region_register(). - * The region driver's remove function should call fpga_region_unregister(). - * The region struct allocated with this function will be freed automatically on - * driver detach. This includes the case of a probe function returning error - * before calling fpga_region_register(), the struct will still get cleaned up. + * This simple version of the register function should be sufficient for most users. + * The fpga_region_register_full() function is available for users that need to + * pass additional, optional parameters. * - * Return: struct fpga_region or NULL + * Return: struct fpga_region or ERR_PTR() */ -struct fpga_region -*devm_fpga_region_create(struct device *parent, - struct fpga_manager *mgr, - int (*get_bridges)(struct fpga_region *)) +struct fpga_region * +fpga_region_register(struct device *parent, struct fpga_manager *mgr, + int (*get_bridges)(struct fpga_region *)) { - struct fpga_region **ptr, *region; - - ptr = devres_alloc(devm_fpga_region_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return NULL; + struct fpga_region_info info = { 0 }; - region = fpga_region_create(parent, mgr, get_bridges); - if (!region) { - devres_free(ptr); - } else { - *ptr = region; - devres_add(parent, ptr); - } + info.mgr = mgr; + info.get_bridges = get_bridges; - return region; -} -EXPORT_SYMBOL_GPL(devm_fpga_region_create); - -/** - * fpga_region_register - register an FPGA region - * @region: FPGA region - * - * Return: 0 or -errno - */ -int fpga_region_register(struct fpga_region *region) -{ - return device_add(®ion->dev); + return fpga_region_register_full(parent, &info); } EXPORT_SYMBOL_GPL(fpga_region_register); @@ -316,6 +281,10 @@ EXPORT_SYMBOL_GPL(fpga_region_unregister); static void fpga_region_dev_release(struct device *dev) { + struct fpga_region *region = to_fpga_region(dev); + + ida_simple_remove(&fpga_region_ida, region->dev.id); + kfree(region); } /** diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index e3c25576b6b9..9c662db1c508 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -405,16 +405,12 @@ static int of_fpga_region_probe(struct platform_device *pdev) if (IS_ERR(mgr)) return -EPROBE_DEFER; - region = devm_fpga_region_create(dev, mgr, of_fpga_region_get_bridges); - if (!region) { - ret = -ENOMEM; + region = fpga_region_register(dev, mgr, of_fpga_region_get_bridges); + if (IS_ERR(region)) { + ret = PTR_ERR(region); goto eprobe_mgr_put; } - ret = fpga_region_register(region); - if (ret) - goto eprobe_mgr_put; - of_platform_populate(np, fpga_region_of_match, NULL, ®ion->dev); platform_set_drvdata(pdev, region); diff --git a/include/linux/fpga/fpga-region.h b/include/linux/fpga/fpga-region.h index 27cb706275db..3b87f232425c 100644 --- a/include/linux/fpga/fpga-region.h +++ b/include/linux/fpga/fpga-region.h @@ -7,6 +7,27 @@ #include #include +struct fpga_region; + +/** + * struct fpga_region_info - collection of parameters an FPGA Region + * @mgr: fpga region manager + * @compat_id: FPGA region id for compatibility check. + * @priv: fpga region private data + * @get_bridges: optional function to get bridges to a list + * + * fpga_region_info contains parameters for the register_full function. + * These are separated into an info structure because they some are optional + * others could be added to in the future. The info structure facilitates + * maintaining a stable API. + */ +struct fpga_region_info { + struct fpga_manager *mgr; + struct fpga_compat_id *compat_id; + void *priv; + int (*get_bridges)(struct fpga_region *region); +}; + /** * struct fpga_region - FPGA Region structure * @dev: FPGA Region device @@ -37,15 +58,12 @@ struct fpga_region *fpga_region_class_find( int fpga_region_program_fpga(struct fpga_region *region); -struct fpga_region -*fpga_region_create(struct device *dev, struct fpga_manager *mgr, - int (*get_bridges)(struct fpga_region *)); -void fpga_region_free(struct fpga_region *region); -int fpga_region_register(struct fpga_region *region); -void fpga_region_unregister(struct fpga_region *region); +struct fpga_region * +fpga_region_register_full(struct device *parent, const struct fpga_region_info *info); -struct fpga_region -*devm_fpga_region_create(struct device *dev, struct fpga_manager *mgr, - int (*get_bridges)(struct fpga_region *)); +struct fpga_region * +fpga_region_register(struct device *parent, struct fpga_manager *mgr, + int (*get_bridges)(struct fpga_region *)); +void fpga_region_unregister(struct fpga_region *region); #endif /* _FPGA_REGION_H */ -- cgit v1.2.3 From b95b668eaaa2574e8ee72f143c52075e9955177e Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Thu, 25 Nov 2021 19:47:51 +0200 Subject: interconnect: qcom: icc-rpmh: Add BCMs to commit list in pre_aggregate We're only adding BCMs to the commit list in aggregate(), but there are cases where pre_aggregate() is called without subsequently calling aggregate(). In particular, in icc_sync_state() when a node with initial BW has zero requests. Since BCMs aren't added to the commit list in these cases, we don't actually send the zero BW request to HW. So the resources remain on unnecessarily. Add BCMs to the commit list in pre_aggregate() instead, which is always called even when there are no requests. Signed-off-by: Mike Tipton [georgi: remove icc_sync_state for platforms with incomplete support] Link: https://lore.kernel.org/r/20211125174751.25317-1-djakov@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpmh.c | 10 +++++----- drivers/interconnect/qcom/sm8150.c | 1 - drivers/interconnect/qcom/sm8250.c | 1 - drivers/interconnect/qcom/sm8350.c | 1 - 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 3eb7936d2cf6..2c8e12549804 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -21,13 +21,18 @@ void qcom_icc_pre_aggregate(struct icc_node *node) { size_t i; struct qcom_icc_node *qn; + struct qcom_icc_provider *qp; qn = node->data; + qp = to_qcom_provider(node->provider); for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { qn->sum_avg[i] = 0; qn->max_peak[i] = 0; } + + for (i = 0; i < qn->num_bcms; i++) + qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]); } EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate); @@ -45,10 +50,8 @@ int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, { size_t i; struct qcom_icc_node *qn; - struct qcom_icc_provider *qp; qn = node->data; - qp = to_qcom_provider(node->provider); if (!tag) tag = QCOM_ICC_TAG_ALWAYS; @@ -68,9 +71,6 @@ int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, *agg_avg += avg_bw; *agg_peak = max_t(u32, *agg_peak, peak_bw); - for (i = 0; i < qn->num_bcms; i++) - qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]); - return 0; } EXPORT_SYMBOL_GPL(qcom_icc_aggregate); diff --git a/drivers/interconnect/qcom/sm8150.c b/drivers/interconnect/qcom/sm8150.c index 2a85f53802b5..745e3c36a61a 100644 --- a/drivers/interconnect/qcom/sm8150.c +++ b/drivers/interconnect/qcom/sm8150.c @@ -535,7 +535,6 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8150", .of_match_table = qnoc_of_match, - .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c index 8dfb5dea562a..aa707582ea01 100644 --- a/drivers/interconnect/qcom/sm8250.c +++ b/drivers/interconnect/qcom/sm8250.c @@ -551,7 +551,6 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8250", .of_match_table = qnoc_of_match, - .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); diff --git a/drivers/interconnect/qcom/sm8350.c b/drivers/interconnect/qcom/sm8350.c index 3e26a2175b28..c79f93a1ac73 100644 --- a/drivers/interconnect/qcom/sm8350.c +++ b/drivers/interconnect/qcom/sm8350.c @@ -531,7 +531,6 @@ static struct platform_driver qnoc_driver = { .driver = { .name = "qnoc-sm8350", .of_match_table = qnoc_of_match, - .sync_state = icc_sync_state, }, }; module_platform_driver(qnoc_driver); -- cgit v1.2.3 From b2b56de9faaf19c829ede5cf56918b3793219971 Mon Sep 17 00:00:00 2001 From: Zou Wei Date: Thu, 25 Nov 2021 14:38:09 +0800 Subject: phy: intel: Remove redundant dev_err call in thunderbay_emmc_phy_probe() There is a error message within devm_ioremap_resource already, so remove the dev_err call to avoid redundant error message. Reported-by: Hulk Robot Signed-off-by: Zou Wei Link: https://lore.kernel.org/r/1637822289-24534-1-git-send-email-zou_wei@huawei.com Signed-off-by: Vinod Koul --- drivers/phy/intel/phy-intel-thunderbay-emmc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/phy/intel/phy-intel-thunderbay-emmc.c b/drivers/phy/intel/phy-intel-thunderbay-emmc.c index 2d6ea84492f2..593f6970b81e 100644 --- a/drivers/phy/intel/phy-intel-thunderbay-emmc.c +++ b/drivers/phy/intel/phy-intel-thunderbay-emmc.c @@ -472,10 +472,8 @@ static int thunderbay_emmc_phy_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); tbh_phy->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(tbh_phy->reg_base)) { - dev_err(&pdev->dev, "region map failed\n"); + if (IS_ERR(tbh_phy->reg_base)) return PTR_ERR(tbh_phy->reg_base); - } tbh_phy->phy_power_sts = PHY_UNINITIALIZED; id = of_match_node(thunderbay_emmc_phy_of_match, pdev->dev.of_node); -- cgit v1.2.3 From 17dcc120fb8d0da4e954ce4386f1376f9cef43d0 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Tue, 30 Nov 2021 11:10:15 +0100 Subject: phy: lan966x: Extend lan966x to support multiple phy interfaces. Currently the driver is supporting only the interfaces QSGMII, SGMII, RGMII and GMII. This patch extend the supported interfaces with 1000BASE-X and 2500BASE-X. Signed-off-by: Horatiu Vultur Acked-by: Russell King (Oracle) Link: https://lore.kernel.org/r/20211130101015.164916-1-horatiu.vultur@microchip.com Signed-off-by: Vinod Koul --- drivers/phy/microchip/lan966x_serdes.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/phy/microchip/lan966x_serdes.c b/drivers/phy/microchip/lan966x_serdes.c index 262bb616b4bb..c0b80a176387 100644 --- a/drivers/phy/microchip/lan966x_serdes.c +++ b/drivers/phy/microchip/lan966x_serdes.c @@ -392,6 +392,10 @@ static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; + if (submode == PHY_INTERFACE_MODE_1000BASEX || + submode == PHY_INTERFACE_MODE_2500BASEX) + submode = PHY_INTERFACE_MODE_SGMII; + for (i = 0; i < ARRAY_SIZE(lan966x_serdes_muxes); i++) { if (macro->idx != lan966x_serdes_muxes[i].idx || mode != lan966x_serdes_muxes[i].mode || -- cgit v1.2.3 From ea59fc1beff1358966b213b4df89aca3f7dec157 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Tue, 30 Nov 2021 15:11:24 -0700 Subject: fpga: stratix10-soc: Do not use ret uninitialized in s10_probe() Clang warns: drivers/fpga/stratix10-soc.c:431:9: warning: variable 'ret' is uninitialized when used here [-Wuninitialized] return ret; ^~~ ret is only assigned in an error path now so just return 0 directly. Fixes: 4ba0b2c294fe ("fpga: mgr: Use standard dev_release for class driver") Link: https://github.com/ClangBuiltLinux/linux/issues/1517 Reviewed-by: Russ Weight Reviewed-by: Tom Rix Acked-by: Xu Yilun Signed-off-by: Nathan Chancellor Signed-off-by: Moritz Fischer --- drivers/fpga/stratix10-soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c index 737d14c6e0de..357cea58ec98 100644 --- a/drivers/fpga/stratix10-soc.c +++ b/drivers/fpga/stratix10-soc.c @@ -428,7 +428,7 @@ static int s10_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, mgr); - return ret; + return 0; probe_err: stratix10_svc_free_channel(priv->chan); -- cgit v1.2.3 From 5b557298d7d09cce04e0565a535fbca63661724a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:27:27 +0200 Subject: misc: at25: Make driver OF independent again The commit f60e7074902a ("misc: at25: Make use of device property API") made a good job by enabling the driver for non-OF platforms, but the recent commit 604288bc6196 ("nvmem: eeprom: at25: fix type compiler warnings") brought that back. Restore greatness of the driver once again. Fixes: eab61fb1cc2e ("nvmem: eeprom: at25: fram discovery simplification") Fixes: fd307a4ad332 ("nvmem: prepare basics for FRAM support") Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125212729.86585-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 632325474233..57599eac2f71 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -17,8 +17,6 @@ #include #include #include -#include -#include #include /* @@ -381,13 +379,14 @@ static int at25_probe(struct spi_device *spi) int sr; u8 id[FM25_ID_LEN]; u8 sernum[FM25_SN_LEN]; + bool is_fram; int i; - const struct of_device_id *match; - bool is_fram = 0; - match = of_match_device(of_match_ptr(at25_of_match), &spi->dev); - if (match && !strcmp(match->compatible, "cypress,fm25")) - is_fram = 1; + err = device_property_match_string(&spi->dev, "compatible", "cypress,fm25"); + if (err >= 0) + is_fram = true; + else + is_fram = false; /* Chip description */ if (!spi->dev.platform_data) { -- cgit v1.2.3 From a692fc39bf90913f3cea57ee240ea5d6338da235 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:27:28 +0200 Subject: misc: at25: Don't copy garbage to the at25->chip in FRAM case Even if we know that we are going to fill everything later on it's bad style and fragile to copy garbage from the stack to the data structure that will be used in the driver. Fixes: fd307a4ad332 ("nvmem: prepare basics for FRAM support") Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125212729.86585-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 57599eac2f71..f0b0efc30ee6 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -390,7 +390,10 @@ static int at25_probe(struct spi_device *spi) /* Chip description */ if (!spi->dev.platform_data) { - if (!is_fram) { + if (is_fram) { + /* We file fields for FRAM case later on */ + memset(&chip, 0, sizeof(chip)); + } else { err = at25_fw_to_chip(&spi->dev, &chip); if (err) return err; -- cgit v1.2.3 From 58589a75bba96f43b62d8069b35be081bc00d7c3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:27:29 +0200 Subject: misc: at25: Check proper value of chip length in FRAM case Obviously the byte_len value should be checked from the chip and not from at25->chip. Fixes: fd307a4ad332 ("nvmem: prepare basics for FRAM support") Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125212729.86585-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index f0b0efc30ee6..e21216541b0f 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -433,9 +433,9 @@ static int at25_probe(struct spi_device *spi) dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]); return -ENODEV; } - chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; - if (at25->chip.byte_len > 64 * 1024) + chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; + if (chip.byte_len > 64 * 1024) at25->chip.flags |= EE_ADDR3; else at25->chip.flags |= EE_ADDR2; -- cgit v1.2.3 From 51902c1212feb9652826fd978e5c58b683f865db Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:31:54 +0200 Subject: misc: at25: Use at25->chip instead of local chip everywhere in ->probe() Currently some values are compared against the contents of the chip structure and most are from its updated copy in at25->chip. Use the latter one everywhere in ->probe(). Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-2-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index e21216541b0f..6bea9c7c64a0 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -434,8 +434,8 @@ static int at25_probe(struct spi_device *spi) return -ENODEV; } - chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; - if (chip.byte_len > 64 * 1024) + at25->chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; + if (at25->chip.byte_len > 64 * 1024) at25->chip.flags |= EE_ADDR3; else at25->chip.flags |= EE_ADDR2; @@ -466,7 +466,7 @@ static int at25_probe(struct spi_device *spi) at25->nvmem_config.type = is_fram ? NVMEM_TYPE_FRAM : NVMEM_TYPE_EEPROM; at25->nvmem_config.name = dev_name(&spi->dev); at25->nvmem_config.dev = &spi->dev; - at25->nvmem_config.read_only = chip.flags & EE_READONLY; + at25->nvmem_config.read_only = at25->chip.flags & EE_READONLY; at25->nvmem_config.root_only = true; at25->nvmem_config.owner = THIS_MODULE; at25->nvmem_config.compat = true; @@ -476,17 +476,17 @@ static int at25_probe(struct spi_device *spi) at25->nvmem_config.priv = at25; at25->nvmem_config.stride = 1; at25->nvmem_config.word_size = 1; - at25->nvmem_config.size = chip.byte_len; + at25->nvmem_config.size = at25->chip.byte_len; at25->nvmem = devm_nvmem_register(&spi->dev, &at25->nvmem_config); if (IS_ERR(at25->nvmem)) return PTR_ERR(at25->nvmem); dev_info(&spi->dev, "%d %s %s %s%s, pagesize %u\n", - (chip.byte_len < 1024) ? chip.byte_len : (chip.byte_len / 1024), - (chip.byte_len < 1024) ? "Byte" : "KByte", + (at25->chip.byte_len < 1024) ? at25->chip.byte_len : (at25->chip.byte_len / 1024), + (at25->chip.byte_len < 1024) ? "Byte" : "KByte", at25->chip.name, is_fram ? "fram" : "eeprom", - (chip.flags & EE_READONLY) ? " (readonly)" : "", + (at25->chip.flags & EE_READONLY) ? " (readonly)" : "", at25->chip.page_size); return 0; } -- cgit v1.2.3 From c329fe53474ac424cd5eb77c2b6b1fb3fc136d7b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:31:55 +0200 Subject: misc: at25: Unshadow error codes in at25_fw_to_chip() device_property_read_u32() may return different error codes. Unshadow them in the at25_fw_to_chip() to give better error report. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-3-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 6bea9c7c64a0..027840c73fc8 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -304,33 +304,35 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) { u32 val; + int err; memset(chip, 0, sizeof(*chip)); strncpy(chip->name, "at25", sizeof(chip->name)); - if (device_property_read_u32(dev, "size", &val) == 0 || - device_property_read_u32(dev, "at25,byte-len", &val) == 0) { - chip->byte_len = val; - } else { + err = device_property_read_u32(dev, "size", &val); + if (err) + err = device_property_read_u32(dev, "at25,byte-len", &val); + if (err) { dev_err(dev, "Error: missing \"size\" property\n"); - return -ENODEV; + return err; } + chip->byte_len = val; - if (device_property_read_u32(dev, "pagesize", &val) == 0 || - device_property_read_u32(dev, "at25,page-size", &val) == 0) { - chip->page_size = val; - } else { + err = device_property_read_u32(dev, "pagesize", &val); + if (err) + err = device_property_read_u32(dev, "at25,page-size", &val); + if (err) { dev_err(dev, "Error: missing \"pagesize\" property\n"); - return -ENODEV; + return err; } - - if (device_property_read_u32(dev, "at25,addr-mode", &val) == 0) { - chip->flags = (u16)val; - } else { - if (device_property_read_u32(dev, "address-width", &val)) { - dev_err(dev, - "Error: missing \"address-width\" property\n"); - return -ENODEV; + chip->page_size = val; + + err = device_property_read_u32(dev, "at25,addr-mode", &val); + if (err) { + err = device_property_read_u32(dev, "address-width", &val); + if (err) { + dev_err(dev, "Error: missing \"address-width\" property\n"); + return err; } switch (val) { case 9: @@ -353,6 +355,8 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) } if (device_property_present(dev, "read-only")) chip->flags |= EE_READONLY; + } else { + chip->flags = (u16)val; } return 0; } -- cgit v1.2.3 From fb422f44778df10d2f37c69fbfeeddd40aedae10 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:31:56 +0200 Subject: misc: at25: Check new property ("address-width") first As it's done elsewhere in at25_fw_to_chip() check new property ("address-width") first. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-4-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 027840c73fc8..86f5433d0278 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -327,13 +327,15 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) } chip->page_size = val; - err = device_property_read_u32(dev, "at25,addr-mode", &val); + err = device_property_read_u32(dev, "address-width", &val); if (err) { - err = device_property_read_u32(dev, "address-width", &val); + err = device_property_read_u32(dev, "at25,addr-mode", &val); if (err) { dev_err(dev, "Error: missing \"address-width\" property\n"); return err; } + chip->flags = (u16)val; + } else { switch (val) { case 9: chip->flags |= EE_INSTR_BIT3_IS_ADDR; @@ -355,8 +357,6 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) } if (device_property_present(dev, "read-only")) chip->flags |= EE_READONLY; - } else { - chip->flags = (u16)val; } return 0; } -- cgit v1.2.3 From 994233e195aaa53f30ca1722a280c5295f8782ce Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:31:57 +0200 Subject: misc: at25: Get platform data via dev_get_platdata() Access to platform data via dev_get_platdata() getter to make code cleaner. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-5-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 86f5433d0278..b235f20c56da 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -378,7 +378,7 @@ MODULE_DEVICE_TABLE(spi, at25_spi_ids); static int at25_probe(struct spi_device *spi) { struct at25_data *at25 = NULL; - struct spi_eeprom chip; + struct spi_eeprom chip, *pdata; int err; int sr; u8 id[FM25_ID_LEN]; @@ -393,7 +393,8 @@ static int at25_probe(struct spi_device *spi) is_fram = false; /* Chip description */ - if (!spi->dev.platform_data) { + pdata = dev_get_platdata(&spi->dev); + if (!pdata) { if (is_fram) { /* We file fields for FRAM case later on */ memset(&chip, 0, sizeof(chip)); @@ -403,7 +404,7 @@ static int at25_probe(struct spi_device *spi) return err; } } else - chip = *(struct spi_eeprom *)spi->dev.platform_data; + chip = *pdata; /* Ping the chip ... the status register is pretty portable, * unlike probing manufacturer IDs. We do expect that system -- cgit v1.2.3 From 01d3c42a08021617ad8ee79b0a9fed91d68e32b6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:31:58 +0200 Subject: misc: at25: Get rid of intermediate storage for AT25 chip data There is no need to copy twice the same data. Drop needless local variable. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-6-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b235f20c56da..70cab386040a 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -306,7 +306,6 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) u32 val; int err; - memset(chip, 0, sizeof(*chip)); strncpy(chip->name, "at25", sizeof(chip->name)); err = device_property_read_u32(dev, "size", &val); @@ -378,9 +377,9 @@ MODULE_DEVICE_TABLE(spi, at25_spi_ids); static int at25_probe(struct spi_device *spi) { struct at25_data *at25 = NULL; - struct spi_eeprom chip, *pdata; int err; int sr; + struct spi_eeprom *pdata; u8 id[FM25_ID_LEN]; u8 sernum[FM25_SN_LEN]; bool is_fram; @@ -392,20 +391,6 @@ static int at25_probe(struct spi_device *spi) else is_fram = false; - /* Chip description */ - pdata = dev_get_platdata(&spi->dev); - if (!pdata) { - if (is_fram) { - /* We file fields for FRAM case later on */ - memset(&chip, 0, sizeof(chip)); - } else { - err = at25_fw_to_chip(&spi->dev, &chip); - if (err) - return err; - } - } else - chip = *pdata; - /* Ping the chip ... the status register is pretty portable, * unlike probing manufacturer IDs. We do expect that system * firmware didn't write it in the past few milliseconds! @@ -421,10 +406,23 @@ static int at25_probe(struct spi_device *spi) return -ENOMEM; mutex_init(&at25->lock); - at25->chip = chip; at25->spi = spi; spi_set_drvdata(spi, at25); + /* Chip description */ + pdata = dev_get_platdata(&spi->dev); + if (pdata) { + at25->chip = *pdata; + } else { + if (is_fram) { + /* We file fields for FRAM case later on */ + } else { + err = at25_fw_to_chip(&spi->dev, &at25->chip); + if (err) + return err; + } + } + if (is_fram) { /* Get ID of chip */ fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); -- cgit v1.2.3 From d059ed1ba27bf0606471ac407008ddd1f65c4be4 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:31:59 +0200 Subject: misc: at25: Switch to use BIT() instead of custom approaches It's obvious that custom approach of getting power of 2 number with int_pow() kinda interesting. Replace it and some others approaches by using a simple BIT() operation. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-7-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 70cab386040a..c9660a4625ce 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -6,6 +6,7 @@ * Copyright (C) 2006 David Brownell */ +#include #include #include #include @@ -17,7 +18,6 @@ #include #include #include -#include /* * NOTE: this is an *EEPROM* driver. The vagaries of product naming @@ -94,7 +94,7 @@ static int at25_ee_read(void *priv, unsigned int offset, instr = AT25_READ; if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) - if (offset >= (1U << (at25->addrlen * 8))) + if (offset >= BIT(at25->addrlen * 8)) instr |= AT25_INSTR_BIT3; *cp++ = instr; @@ -227,7 +227,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) instr = AT25_WRITE; if (at25->chip.flags & EE_INSTR_BIT3_IS_ADDR) - if (offset >= (1U << (at25->addrlen * 8))) + if (offset >= BIT(at25->addrlen * 8)) instr |= AT25_INSTR_BIT3; *cp++ = instr; @@ -437,7 +437,7 @@ static int at25_probe(struct spi_device *spi) return -ENODEV; } - at25->chip.byte_len = int_pow(2, id[7] - 0x21 + 4) * 1024; + at25->chip.byte_len = BIT(id[7] - 0x21 + 4) * 1024; if (at25->chip.byte_len > 64 * 1024) at25->chip.flags |= EE_ADDR3; else -- cgit v1.2.3 From 31a45d27c9328b9c8193f01d7d534659a03cee2d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:32:00 +0200 Subject: misc: at25: Factor out at_fram_to_chip() In the similar way as it's done for EEPROM, factor out a new helper function for FRAM. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-8-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 85 ++++++++++++++++++++++++---------------------- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index c9660a4625ce..b9d26c9ee768 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -31,9 +31,9 @@ #define FM25_SN_LEN 8 /* serial number length */ struct at25_data { + struct spi_eeprom chip; struct spi_device *spi; struct mutex lock; - struct spi_eeprom chip; unsigned addrlen; struct nvmem_config nvmem_config; struct nvmem_device *nvmem; @@ -360,6 +360,44 @@ static int at25_fw_to_chip(struct device *dev, struct spi_eeprom *chip) return 0; } +static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) +{ + struct at25_data *at25 = container_of(chip, struct at25_data, chip); + u8 sernum[FM25_SN_LEN]; + u8 id[FM25_ID_LEN]; + int i; + + strncpy(chip->name, "fm25", sizeof(chip->name)); + + /* Get ID of chip */ + fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); + if (id[6] != 0xc2) { + dev_err(dev, "Error: no Cypress FRAM (id %02x)\n", id[6]); + return -ENODEV; + } + /* Set size found in ID */ + if (id[7] < 0x21 || id[7] > 0x26) { + dev_err(dev, "Error: unsupported size (id %02x)\n", id[7]); + return -ENODEV; + } + + chip->byte_len = BIT(id[7] - 0x21 + 4) * 1024; + if (chip->byte_len > 64 * 1024) + chip->flags |= EE_ADDR3; + else + chip->flags |= EE_ADDR2; + + if (id[8]) { + fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN); + /* Swap byte order */ + for (i = 0; i < FM25_SN_LEN; i++) + at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i]; + } + + chip->page_size = PAGE_SIZE; + return 0; +} + static const struct of_device_id at25_of_match[] = { { .compatible = "atmel,at25",}, { .compatible = "cypress,fm25",}, @@ -380,10 +418,7 @@ static int at25_probe(struct spi_device *spi) int err; int sr; struct spi_eeprom *pdata; - u8 id[FM25_ID_LEN]; - u8 sernum[FM25_SN_LEN]; bool is_fram; - int i; err = device_property_match_string(&spi->dev, "compatible", "cypress,fm25"); if (err >= 0) @@ -414,44 +449,12 @@ static int at25_probe(struct spi_device *spi) if (pdata) { at25->chip = *pdata; } else { - if (is_fram) { - /* We file fields for FRAM case later on */ - } else { - err = at25_fw_to_chip(&spi->dev, &at25->chip); - if (err) - return err; - } - } - - if (is_fram) { - /* Get ID of chip */ - fm25_aux_read(at25, id, FM25_RDID, FM25_ID_LEN); - if (id[6] != 0xc2) { - dev_err(&spi->dev, - "Error: no Cypress FRAM (id %02x)\n", id[6]); - return -ENODEV; - } - /* set size found in ID */ - if (id[7] < 0x21 || id[7] > 0x26) { - dev_err(&spi->dev, "Error: unsupported size (id %02x)\n", id[7]); - return -ENODEV; - } - - at25->chip.byte_len = BIT(id[7] - 0x21 + 4) * 1024; - if (at25->chip.byte_len > 64 * 1024) - at25->chip.flags |= EE_ADDR3; + if (is_fram) + err = at25_fram_to_chip(&spi->dev, &at25->chip); else - at25->chip.flags |= EE_ADDR2; - - if (id[8]) { - fm25_aux_read(at25, sernum, FM25_RDSN, FM25_SN_LEN); - /* swap byte order */ - for (i = 0; i < FM25_SN_LEN; i++) - at25->sernum[i] = sernum[FM25_SN_LEN - 1 - i]; - } - - at25->chip.page_size = PAGE_SIZE; - strncpy(at25->chip.name, "fm25", sizeof(at25->chip.name)); + err = at25_fw_to_chip(&spi->dev, &at25->chip); + if (err) + return err; } /* For now we only support 8/16/24 bit addressing */ -- cgit v1.2.3 From d5fb1304acfd9b8077485c9fb1bf94c8218fd899 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:32:01 +0200 Subject: misc: at25: Reorganize headers for better maintenance Split headers to three groups and sort alphabetically in each of them. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-9-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index b9d26c9ee768..3e60124d14a3 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -7,17 +7,18 @@ */ #include -#include -#include -#include #include #include +#include +#include +#include #include +#include -#include -#include #include -#include +#include + +#include /* * NOTE: this is an *EEPROM* driver. The vagaries of product naming -- cgit v1.2.3 From d6471ab9ab5814489ed2ebd8c554232b59ac571b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:32:02 +0200 Subject: misc: at25: Replace commas by spaces in the ID tables For better readability replace commas by spaces in the ID tables. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-10-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 3e60124d14a3..9264bb17963e 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -400,15 +400,15 @@ static int at25_fram_to_chip(struct device *dev, struct spi_eeprom *chip) } static const struct of_device_id at25_of_match[] = { - { .compatible = "atmel,at25",}, - { .compatible = "cypress,fm25",}, + { .compatible = "atmel,at25" }, + { .compatible = "cypress,fm25" }, { } }; MODULE_DEVICE_TABLE(of, at25_of_match); static const struct spi_device_id at25_spi_ids[] = { - { .name = "at25",}, - { .name = "fm25",}, + { .name = "at25" }, + { .name = "fm25" }, { } }; MODULE_DEVICE_TABLE(spi, at25_spi_ids); -- cgit v1.2.3 From 1ca54ce9a3ff157b93402a7fea52595d029daa8d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 25 Nov 2021 23:32:03 +0200 Subject: misc: at25: Align comment style Make multi-line comment style aligned. While at it, drop filename from the file. Acked-by: Arnd Bergmann Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20211125213203.86693-11-andriy.shevchenko@linux.intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/eeprom/at25.c | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/drivers/misc/eeprom/at25.c b/drivers/misc/eeprom/at25.c index 9264bb17963e..f16f67baf3d2 100644 --- a/drivers/misc/eeprom/at25.c +++ b/drivers/misc/eeprom/at25.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * at25.c -- support most SPI EEPROMs, such as Atmel AT25 models - * and Cypress FRAMs FM25 models + * Driver for most of the SPI EEPROMs, such as Atmel AT25 models + * and Cypress FRAMs FM25 models. * * Copyright (C) 2006 David Brownell */ @@ -21,7 +21,7 @@ #include /* - * NOTE: this is an *EEPROM* driver. The vagaries of product naming + * NOTE: this is an *EEPROM* driver. The vagaries of product naming * mean that some AT25 products are EEPROMs, and others are FLASH. * Handle FLASH chips with the drivers/mtd/devices/m25p80.c driver, * not this one! @@ -57,13 +57,14 @@ struct at25_data { #define AT25_SR_BP1 0x08 #define AT25_SR_WPEN 0x80 /* writeprotect enable */ -#define AT25_INSTR_BIT3 0x08 /* Additional address bit in instr */ +#define AT25_INSTR_BIT3 0x08 /* additional address bit in instr */ #define FM25_ID_LEN 9 /* ID length */ #define EE_MAXADDRLEN 3 /* 24 bit addresses, up to 2 MBytes */ -/* Specs often allow 5 msec for a page write, sometimes 20 msec; +/* + * Specs often allow 5ms for a page write, sometimes 20ms; * it's important to recover from write timeouts. */ #define EE_TIMEOUT 25 @@ -108,7 +109,7 @@ static int at25_ee_read(void *priv, unsigned int offset, *cp++ = offset >> 8; fallthrough; case 1: - case 0: /* can't happen: for better codegen */ + case 0: /* can't happen: for better code generation */ *cp++ = offset >> 0; } @@ -125,11 +126,12 @@ static int at25_ee_read(void *priv, unsigned int offset, mutex_lock(&at25->lock); - /* Read it all at once. + /* + * Read it all at once. * * REVISIT that's potentially a problem with large chips, if * other devices on the bus need to be accessed regularly or - * this chip is clocked very slowly + * this chip is clocked very slowly. */ status = spi_sync(at25->spi, &m); dev_dbg(&at25->spi->dev, "read %zu bytes at %d --> %zd\n", @@ -139,9 +141,7 @@ static int at25_ee_read(void *priv, unsigned int offset, return status; } -/* - * read extra registers as ID or serial number - */ +/* Read extra registers as ID or serial number */ static int fm25_aux_read(struct at25_data *at25, u8 *buf, uint8_t command, int len) { @@ -207,7 +207,8 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) if (!bounce) return -ENOMEM; - /* For write, rollover is within the page ... so we write at + /* + * For write, rollover is within the page ... so we write at * most one page, then manually roll over to the next page. */ mutex_lock(&at25->lock); @@ -241,7 +242,7 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) *cp++ = offset >> 8; fallthrough; case 1: - case 0: /* can't happen: for better codegen */ + case 0: /* can't happen: for better code generation */ *cp++ = offset >> 0; } @@ -257,8 +258,9 @@ static int at25_ee_write(void *priv, unsigned int off, void *val, size_t count) if (status < 0) break; - /* REVISIT this should detect (or prevent) failed writes - * to readonly sections of the EEPROM... + /* + * REVISIT this should detect (or prevent) failed writes + * to read-only sections of the EEPROM... */ /* Wait for non-busy status */ @@ -427,8 +429,9 @@ static int at25_probe(struct spi_device *spi) else is_fram = false; - /* Ping the chip ... the status register is pretty portable, - * unlike probing manufacturer IDs. We do expect that system + /* + * Ping the chip ... the status register is pretty portable, + * unlike probing manufacturer IDs. We do expect that system * firmware didn't write it in the past few milliseconds! */ sr = spi_w8r8(spi, AT25_RDSR); -- cgit v1.2.3 From d325537b88f504bcfdcc61055ad36ff0cb6d7d0b Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 12 Nov 2021 11:06:33 +0100 Subject: mei: Remove some dead code 'generated' is known to be true here, so "true || whatever" will still be true. So, remove some dead code. Acked-by: Tomas Winkler Signed-off-by: Christophe JAILLET Link: https://lore.kernel.org/r/3f904c291f3eed06223dd8d494028e0d49df6f10.1636711522.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hw-txe.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/drivers/misc/mei/hw-txe.c b/drivers/misc/mei/hw-txe.c index a4e854b9b9e6..00652c137cc7 100644 --- a/drivers/misc/mei/hw-txe.c +++ b/drivers/misc/mei/hw-txe.c @@ -994,11 +994,7 @@ static bool mei_txe_check_and_ack_intrs(struct mei_device *dev, bool do_ack) hhisr &= ~IPC_HHIER_SEC; } - generated = generated || - (hisr & HISR_INT_STS_MSK) || - (ipc_isr & SEC_IPC_HOST_INT_STATUS_PENDING); - - if (generated && do_ack) { + if (do_ack) { /* Save the interrupt causes */ hw->intr_cause |= hisr & HISR_INT_STS_MSK; if (ipc_isr & SEC_IPC_HOST_INT_STATUS_IN_RDY) -- cgit v1.2.3 From f5912cc19acd7c24b2dbf65a6340bf194244f085 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 3 Dec 2021 00:42:06 -0800 Subject: char/mwave: Adjust io port register size Using MKWORD() on a byte-sized variable results in OOB read. Expand the size of the reserved area so both MKWORD and MKBYTE continue to work without overflow. Silences this warning on a -Warray-bounds build: drivers/char/mwave/3780i.h:346:22: error: array subscript 'short unsigned int[0]' is partly outside array bounds of 'DSP_ISA_SLAVE_CONTROL[1]' [-Werror=array-bounds] 346 | #define MKWORD(var) (*((unsigned short *)(&var))) | ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~ drivers/char/mwave/3780i.h:356:40: note: in definition of macro 'OutWordDsp' 356 | #define OutWordDsp(index,value) outw(value,usDspBaseIO+index) | ^~~~~ drivers/char/mwave/3780i.c:373:41: note: in expansion of macro 'MKWORD' 373 | OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); | ^~~~~~ drivers/char/mwave/3780i.c:358:31: note: while referencing 'rSlaveControl' 358 | DSP_ISA_SLAVE_CONTROL rSlaveControl; | ^~~~~~~~~~~~~ Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2") Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20211203084206.3104326-1-keescook@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/char/mwave/3780i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h index 9ccb6b270b07..95164246afd1 100644 --- a/drivers/char/mwave/3780i.h +++ b/drivers/char/mwave/3780i.h @@ -68,7 +68,7 @@ typedef struct { unsigned char ClockControl:1; /* RW: Clock control: 0=normal, 1=stop 3780i clocks */ unsigned char SoftReset:1; /* RW: Soft reset 0=normal, 1=soft reset active */ unsigned char ConfigMode:1; /* RW: Configuration mode, 0=normal, 1=config mode */ - unsigned char Reserved:5; /* 0: Reserved */ + unsigned short Reserved:13; /* 0: Reserved */ } DSP_ISA_SLAVE_CONTROL; -- cgit v1.2.3 From 690cfa20d02da5aca6e4c141ff34ef9529843280 Mon Sep 17 00:00:00 2001 From: Ajith P V Date: Thu, 25 Nov 2021 17:52:18 +0530 Subject: binder: remove repeat word from comment binder.c file comment produce warning with checkpatch as below: WARNING: Possible repeated word: 'for' Remove the repeated word from the comment avoid this warning. Signed-off-by: Ajith P V Link: https://lore.kernel.org/r/20211125122218.6767-1-ajithpv.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index cffbe57a8e08..74ffb695a6c4 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1933,7 +1933,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, case BINDER_TYPE_FD: { /* * No need to close the file here since user-space - * closes it for for successfully delivered + * closes it for successfully delivered * transactions. For transactions that weren't * delivered, the new fd was never allocated so * there is no need to close and the fput on the -- cgit v1.2.3 From fe6b1869243f23a485a106c214bcfdc7aa0ed593 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Tue, 30 Nov 2021 10:51:49 -0800 Subject: binder: fix handling of error during copy If a memory copy function fails to copy the whole buffer, a positive integar with the remaining bytes is returned. In binder_translate_fd_array() this can result in an fd being skipped due to the failed copy, but the loop continues processing fds since the early return condition expects a negative integer on error. Fix by returning "ret > 0 ? -EINVAL : ret" to handle this case. Fixes: bb4a2e48d510 ("binder: return errors from buffer copy functions") Suggested-by: Dan Carpenter Acked-by: Christian Brauner Signed-off-by: Todd Kjos Link: https://lore.kernel.org/r/20211130185152.437403-2-tkjos@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 74ffb695a6c4..7cec5840cfcd 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2269,8 +2269,8 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, if (!ret) ret = binder_translate_fd(fd, offset, t, thread, in_reply_to); - if (ret < 0) - return ret; + if (ret) + return ret > 0 ? -EINVAL : ret; } return 0; } -- cgit v1.2.3 From 6d98eb95b450a75adb4516a1d33652dc78d2b20c Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Tue, 30 Nov 2021 10:51:50 -0800 Subject: binder: avoid potential data leakage when copying txn Transactions are copied from the sender to the target first and objects like BINDER_TYPE_PTR and BINDER_TYPE_FDA are then fixed up. This means there is a short period where the sender's version of these objects are visible to the target prior to the fixups. Instead of copying all of the data first, copy data only after any needed fixups have been applied. Fixes: 457b9a6f09f0 ("Staging: android: add binder driver") Reviewed-by: Martijn Coenen Acked-by: Christian Brauner Signed-off-by: Todd Kjos Link: https://lore.kernel.org/r/20211130185152.437403-3-tkjos@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 94 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 70 insertions(+), 24 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 7cec5840cfcd..73ae3ced72fb 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -1608,15 +1608,21 @@ static void binder_cleanup_transaction(struct binder_transaction *t, /** * binder_get_object() - gets object and checks for valid metadata * @proc: binder_proc owning the buffer + * @u: sender's user pointer to base of buffer * @buffer: binder_buffer that we're parsing. * @offset: offset in the @buffer at which to validate an object. * @object: struct binder_object to read into * - * Return: If there's a valid metadata object at @offset in @buffer, the + * Copy the binder object at the given offset into @object. If @u is + * provided then the copy is from the sender's buffer. If not, then + * it is copied from the target's @buffer. + * + * Return: If there's a valid metadata object at @offset, the * size of that object. Otherwise, it returns zero. The object * is read into the struct binder_object pointed to by @object. */ static size_t binder_get_object(struct binder_proc *proc, + const void __user *u, struct binder_buffer *buffer, unsigned long offset, struct binder_object *object) @@ -1626,10 +1632,16 @@ static size_t binder_get_object(struct binder_proc *proc, size_t object_size = 0; read_size = min_t(size_t, sizeof(*object), buffer->data_size - offset); - if (offset > buffer->data_size || read_size < sizeof(*hdr) || - binder_alloc_copy_from_buffer(&proc->alloc, object, buffer, - offset, read_size)) + if (offset > buffer->data_size || read_size < sizeof(*hdr)) return 0; + if (u) { + if (copy_from_user(object, u + offset, read_size)) + return 0; + } else { + if (binder_alloc_copy_from_buffer(&proc->alloc, object, buffer, + offset, read_size)) + return 0; + } /* Ok, now see if we read a complete object. */ hdr = &object->hdr; @@ -1702,7 +1714,7 @@ static struct binder_buffer_object *binder_validate_ptr( b, buffer_offset, sizeof(object_offset))) return NULL; - object_size = binder_get_object(proc, b, object_offset, object); + object_size = binder_get_object(proc, NULL, b, object_offset, object); if (!object_size || object->hdr.type != BINDER_TYPE_PTR) return NULL; if (object_offsetp) @@ -1767,7 +1779,8 @@ static bool binder_validate_fixup(struct binder_proc *proc, unsigned long buffer_offset; struct binder_object last_object; struct binder_buffer_object *last_bbo; - size_t object_size = binder_get_object(proc, b, last_obj_offset, + size_t object_size = binder_get_object(proc, NULL, b, + last_obj_offset, &last_object); if (object_size != sizeof(*last_bbo)) return false; @@ -1882,7 +1895,7 @@ static void binder_transaction_buffer_release(struct binder_proc *proc, if (!binder_alloc_copy_from_buffer(&proc->alloc, &object_offset, buffer, buffer_offset, sizeof(object_offset))) - object_size = binder_get_object(proc, buffer, + object_size = binder_get_object(proc, NULL, buffer, object_offset, &object); if (object_size == 0) { pr_err("transaction release %d bad object at offset %lld, size %zd\n", @@ -2455,6 +2468,7 @@ static void binder_transaction(struct binder_proc *proc, binder_size_t off_start_offset, off_end_offset; binder_size_t off_min; binder_size_t sg_buf_offset, sg_buf_end_offset; + binder_size_t user_offset = 0; struct binder_proc *target_proc = NULL; struct binder_thread *target_thread = NULL; struct binder_node *target_node = NULL; @@ -2469,6 +2483,8 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); char *secctx = NULL; u32 secctx_sz = 0; + const void __user *user_buffer = (const void __user *) + (uintptr_t)tr->data.ptr.buffer; e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; @@ -2780,19 +2796,6 @@ static void binder_transaction(struct binder_proc *proc, t->buffer->clear_on_free = !!(t->flags & TF_CLEAR_BUF); trace_binder_transaction_alloc_buf(t->buffer); - if (binder_alloc_copy_user_to_buffer( - &target_proc->alloc, - t->buffer, 0, - (const void __user *) - (uintptr_t)tr->data.ptr.buffer, - tr->data_size)) { - binder_user_error("%d:%d got transaction with invalid data ptr\n", - proc->pid, thread->pid); - return_error = BR_FAILED_REPLY; - return_error_param = -EFAULT; - return_error_line = __LINE__; - goto err_copy_data_failed; - } if (binder_alloc_copy_user_to_buffer( &target_proc->alloc, t->buffer, @@ -2837,6 +2840,7 @@ static void binder_transaction(struct binder_proc *proc, size_t object_size; struct binder_object object; binder_size_t object_offset; + binder_size_t copy_size; if (binder_alloc_copy_from_buffer(&target_proc->alloc, &object_offset, @@ -2848,8 +2852,27 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_bad_offset; } - object_size = binder_get_object(target_proc, t->buffer, - object_offset, &object); + + /* + * Copy the source user buffer up to the next object + * that will be processed. + */ + copy_size = object_offset - user_offset; + if (copy_size && (user_offset > object_offset || + binder_alloc_copy_user_to_buffer( + &target_proc->alloc, + t->buffer, user_offset, + user_buffer + user_offset, + copy_size))) { + binder_user_error("%d:%d got transaction with invalid data ptr\n", + proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + return_error_param = -EFAULT; + return_error_line = __LINE__; + goto err_copy_data_failed; + } + object_size = binder_get_object(target_proc, user_buffer, + t->buffer, object_offset, &object); if (object_size == 0 || object_offset < off_min) { binder_user_error("%d:%d got transaction with invalid offset (%lld, min %lld max %lld) or object.\n", proc->pid, thread->pid, @@ -2861,6 +2884,11 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_bad_offset; } + /* + * Set offset to the next buffer fragment to be + * copied + */ + user_offset = object_offset + object_size; hdr = &object.hdr; off_min = object_offset + object_size; @@ -2956,9 +2984,14 @@ static void binder_transaction(struct binder_proc *proc, } ret = binder_translate_fd_array(fda, parent, t, thread, in_reply_to); - if (ret < 0) { + if (!ret) + ret = binder_alloc_copy_to_buffer(&target_proc->alloc, + t->buffer, + object_offset, + fda, sizeof(*fda)); + if (ret) { return_error = BR_FAILED_REPLY; - return_error_param = ret; + return_error_param = ret > 0 ? -EINVAL : ret; return_error_line = __LINE__; goto err_translate_failed; } @@ -3028,6 +3061,19 @@ static void binder_transaction(struct binder_proc *proc, goto err_bad_object_type; } } + /* Done processing objects, copy the rest of the buffer */ + if (binder_alloc_copy_user_to_buffer( + &target_proc->alloc, + t->buffer, user_offset, + user_buffer + user_offset, + tr->data_size - user_offset)) { + binder_user_error("%d:%d got transaction with invalid data ptr\n", + proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + return_error_param = -EFAULT; + return_error_line = __LINE__; + goto err_copy_data_failed; + } if (t->buffer->oneway_spam_suspect) tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT; else -- cgit v1.2.3 From 656e01f3ab54afe71bed066996fc2640881e1220 Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Tue, 30 Nov 2021 10:51:51 -0800 Subject: binder: read pre-translated fds from sender buffer This patch is to prepare for an up coming patch where we read pre-translated fds from the sender buffer and translate them before copying them to the target. It does not change run time. The patch adds two new parameters to binder_translate_fd_array() to hold the sender buffer and sender buffer parent. These parameters let us call copy_from_user() directly from the sender instead of using binder_alloc_copy_from_buffer() to copy from the target. Also the patch adds some new alignment checks. Previously the alignment checks would have been done in a different place, but this lets us print more useful error messages. Reviewed-by: Martijn Coenen Acked-by: Christian Brauner Signed-off-by: Todd Kjos Link: https://lore.kernel.org/r/20211130185152.437403-4-tkjos@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 39 ++++++++++++++++++++++++++++++++------- 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 73ae3ced72fb..608ff978564b 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2234,15 +2234,17 @@ err_fd_not_accepted: } static int binder_translate_fd_array(struct binder_fd_array_object *fda, + const void __user *sender_ubuffer, struct binder_buffer_object *parent, + struct binder_buffer_object *sender_uparent, struct binder_transaction *t, struct binder_thread *thread, struct binder_transaction *in_reply_to) { binder_size_t fdi, fd_buf_size; binder_size_t fda_offset; + const void __user *sender_ufda_base; struct binder_proc *proc = thread->proc; - struct binder_proc *target_proc = t->to_proc; fd_buf_size = sizeof(u32) * fda->num_fds; if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { @@ -2266,7 +2268,10 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, */ fda_offset = (parent->buffer - (uintptr_t)t->buffer->user_data) + fda->parent_offset; - if (!IS_ALIGNED((unsigned long)fda_offset, sizeof(u32))) { + sender_ufda_base = (void __user *)sender_uparent->buffer + fda->parent_offset; + + if (!IS_ALIGNED((unsigned long)fda_offset, sizeof(u32)) || + !IS_ALIGNED((unsigned long)sender_ufda_base, sizeof(u32))) { binder_user_error("%d:%d parent offset not aligned correctly.\n", proc->pid, thread->pid); return -EINVAL; @@ -2275,10 +2280,9 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, u32 fd; int ret; binder_size_t offset = fda_offset + fdi * sizeof(fd); + binder_size_t sender_uoffset = fdi * sizeof(fd); - ret = binder_alloc_copy_from_buffer(&target_proc->alloc, - &fd, t->buffer, - offset, sizeof(fd)); + ret = copy_from_user(&fd, sender_ufda_base + sender_uoffset, sizeof(fd)); if (!ret) ret = binder_translate_fd(fd, offset, t, thread, in_reply_to); @@ -2951,6 +2955,8 @@ static void binder_transaction(struct binder_proc *proc, case BINDER_TYPE_FDA: { struct binder_object ptr_object; binder_size_t parent_offset; + struct binder_object user_object; + size_t user_parent_size; struct binder_fd_array_object *fda = to_binder_fd_array_object(hdr); size_t num_valid = (buffer_offset - off_start_offset) / @@ -2982,8 +2988,27 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_bad_parent; } - ret = binder_translate_fd_array(fda, parent, t, thread, - in_reply_to); + /* + * We need to read the user version of the parent + * object to get the original user offset + */ + user_parent_size = + binder_get_object(proc, user_buffer, t->buffer, + parent_offset, &user_object); + if (user_parent_size != sizeof(user_object.bbo)) { + binder_user_error("%d:%d invalid ptr object size: %zd vs %zd\n", + proc->pid, thread->pid, + user_parent_size, + sizeof(user_object.bbo)); + return_error = BR_FAILED_REPLY; + return_error_param = -EINVAL; + return_error_line = __LINE__; + goto err_bad_parent; + } + ret = binder_translate_fd_array(fda, user_buffer, + parent, + &user_object.bbo, t, + thread, in_reply_to); if (!ret) ret = binder_alloc_copy_to_buffer(&target_proc->alloc, t->buffer, -- cgit v1.2.3 From 09184ae9b5756cc469db6fd1d1cfdcffbf627c2d Mon Sep 17 00:00:00 2001 From: Todd Kjos Date: Tue, 30 Nov 2021 10:51:52 -0800 Subject: binder: defer copies of pre-patched txn data BINDER_TYPE_PTR objects point to memory areas in the source process to be copied into the target buffer as part of a transaction. This implements a scatter- gather model where non-contiguous memory in a source process is "gathered" into a contiguous region in the target buffer. The data can include pointers that must be fixed up to correctly point to the copied data. To avoid making source process pointers visible to the target process, this patch defers the copy until the fixups are known and then copies and fixeups are done together. There is a special case of BINDER_TYPE_FDA which applies the fixup later in the target process context. In this case the user data is skipped (so no untranslated fds become visible to the target). Reviewed-by: Martijn Coenen Signed-off-by: Todd Kjos Link: https://lore.kernel.org/r/20211130185152.437403-5-tkjos@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 299 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 274 insertions(+), 25 deletions(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 608ff978564b..5497797ab258 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2233,7 +2233,246 @@ err_fd_not_accepted: return ret; } -static int binder_translate_fd_array(struct binder_fd_array_object *fda, +/** + * struct binder_ptr_fixup - data to be fixed-up in target buffer + * @offset offset in target buffer to fixup + * @skip_size bytes to skip in copy (fixup will be written later) + * @fixup_data data to write at fixup offset + * @node list node + * + * This is used for the pointer fixup list (pf) which is created and consumed + * during binder_transaction() and is only accessed locally. No + * locking is necessary. + * + * The list is ordered by @offset. + */ +struct binder_ptr_fixup { + binder_size_t offset; + size_t skip_size; + binder_uintptr_t fixup_data; + struct list_head node; +}; + +/** + * struct binder_sg_copy - scatter-gather data to be copied + * @offset offset in target buffer + * @sender_uaddr user address in source buffer + * @length bytes to copy + * @node list node + * + * This is used for the sg copy list (sgc) which is created and consumed + * during binder_transaction() and is only accessed locally. No + * locking is necessary. + * + * The list is ordered by @offset. + */ +struct binder_sg_copy { + binder_size_t offset; + const void __user *sender_uaddr; + size_t length; + struct list_head node; +}; + +/** + * binder_do_deferred_txn_copies() - copy and fixup scatter-gather data + * @alloc: binder_alloc associated with @buffer + * @buffer: binder buffer in target process + * @sgc_head: list_head of scatter-gather copy list + * @pf_head: list_head of pointer fixup list + * + * Processes all elements of @sgc_head, applying fixups from @pf_head + * and copying the scatter-gather data from the source process' user + * buffer to the target's buffer. It is expected that the list creation + * and processing all occurs during binder_transaction() so these lists + * are only accessed in local context. + * + * Return: 0=success, else -errno + */ +static int binder_do_deferred_txn_copies(struct binder_alloc *alloc, + struct binder_buffer *buffer, + struct list_head *sgc_head, + struct list_head *pf_head) +{ + int ret = 0; + struct binder_sg_copy *sgc, *tmpsgc; + struct binder_ptr_fixup *pf = + list_first_entry_or_null(pf_head, struct binder_ptr_fixup, + node); + + list_for_each_entry_safe(sgc, tmpsgc, sgc_head, node) { + size_t bytes_copied = 0; + + while (bytes_copied < sgc->length) { + size_t copy_size; + size_t bytes_left = sgc->length - bytes_copied; + size_t offset = sgc->offset + bytes_copied; + + /* + * We copy up to the fixup (pointed to by pf) + */ + copy_size = pf ? min(bytes_left, (size_t)pf->offset - offset) + : bytes_left; + if (!ret && copy_size) + ret = binder_alloc_copy_user_to_buffer( + alloc, buffer, + offset, + sgc->sender_uaddr + bytes_copied, + copy_size); + bytes_copied += copy_size; + if (copy_size != bytes_left) { + BUG_ON(!pf); + /* we stopped at a fixup offset */ + if (pf->skip_size) { + /* + * we are just skipping. This is for + * BINDER_TYPE_FDA where the translated + * fds will be fixed up when we get + * to target context. + */ + bytes_copied += pf->skip_size; + } else { + /* apply the fixup indicated by pf */ + if (!ret) + ret = binder_alloc_copy_to_buffer( + alloc, buffer, + pf->offset, + &pf->fixup_data, + sizeof(pf->fixup_data)); + bytes_copied += sizeof(pf->fixup_data); + } + list_del(&pf->node); + kfree(pf); + pf = list_first_entry_or_null(pf_head, + struct binder_ptr_fixup, node); + } + } + list_del(&sgc->node); + kfree(sgc); + } + BUG_ON(!list_empty(pf_head)); + BUG_ON(!list_empty(sgc_head)); + + return ret > 0 ? -EINVAL : ret; +} + +/** + * binder_cleanup_deferred_txn_lists() - free specified lists + * @sgc_head: list_head of scatter-gather copy list + * @pf_head: list_head of pointer fixup list + * + * Called to clean up @sgc_head and @pf_head if there is an + * error. + */ +static void binder_cleanup_deferred_txn_lists(struct list_head *sgc_head, + struct list_head *pf_head) +{ + struct binder_sg_copy *sgc, *tmpsgc; + struct binder_ptr_fixup *pf, *tmppf; + + list_for_each_entry_safe(sgc, tmpsgc, sgc_head, node) { + list_del(&sgc->node); + kfree(sgc); + } + list_for_each_entry_safe(pf, tmppf, pf_head, node) { + list_del(&pf->node); + kfree(pf); + } +} + +/** + * binder_defer_copy() - queue a scatter-gather buffer for copy + * @sgc_head: list_head of scatter-gather copy list + * @offset: binder buffer offset in target process + * @sender_uaddr: user address in source process + * @length: bytes to copy + * + * Specify a scatter-gather block to be copied. The actual copy must + * be deferred until all the needed fixups are identified and queued. + * Then the copy and fixups are done together so un-translated values + * from the source are never visible in the target buffer. + * + * We are guaranteed that repeated calls to this function will have + * monotonically increasing @offset values so the list will naturally + * be ordered. + * + * Return: 0=success, else -errno + */ +static int binder_defer_copy(struct list_head *sgc_head, binder_size_t offset, + const void __user *sender_uaddr, size_t length) +{ + struct binder_sg_copy *bc = kzalloc(sizeof(*bc), GFP_KERNEL); + + if (!bc) + return -ENOMEM; + + bc->offset = offset; + bc->sender_uaddr = sender_uaddr; + bc->length = length; + INIT_LIST_HEAD(&bc->node); + + /* + * We are guaranteed that the deferred copies are in-order + * so just add to the tail. + */ + list_add_tail(&bc->node, sgc_head); + + return 0; +} + +/** + * binder_add_fixup() - queue a fixup to be applied to sg copy + * @pf_head: list_head of binder ptr fixup list + * @offset: binder buffer offset in target process + * @fixup: bytes to be copied for fixup + * @skip_size: bytes to skip when copying (fixup will be applied later) + * + * Add the specified fixup to a list ordered by @offset. When copying + * the scatter-gather buffers, the fixup will be copied instead of + * data from the source buffer. For BINDER_TYPE_FDA fixups, the fixup + * will be applied later (in target process context), so we just skip + * the bytes specified by @skip_size. If @skip_size is 0, we copy the + * value in @fixup. + * + * This function is called *mostly* in @offset order, but there are + * exceptions. Since out-of-order inserts are relatively uncommon, + * we insert the new element by searching backward from the tail of + * the list. + * + * Return: 0=success, else -errno + */ +static int binder_add_fixup(struct list_head *pf_head, binder_size_t offset, + binder_uintptr_t fixup, size_t skip_size) +{ + struct binder_ptr_fixup *pf = kzalloc(sizeof(*pf), GFP_KERNEL); + struct binder_ptr_fixup *tmppf; + + if (!pf) + return -ENOMEM; + + pf->offset = offset; + pf->fixup_data = fixup; + pf->skip_size = skip_size; + INIT_LIST_HEAD(&pf->node); + + /* Fixups are *mostly* added in-order, but there are some + * exceptions. Look backwards through list for insertion point. + */ + list_for_each_entry_reverse(tmppf, pf_head, node) { + if (tmppf->offset < pf->offset) { + list_add(&pf->node, &tmppf->node); + return 0; + } + } + /* + * if we get here, then the new offset is the lowest so + * insert at the head + */ + list_add(&pf->node, pf_head); + return 0; +} + +static int binder_translate_fd_array(struct list_head *pf_head, + struct binder_fd_array_object *fda, const void __user *sender_ubuffer, struct binder_buffer_object *parent, struct binder_buffer_object *sender_uparent, @@ -2245,6 +2484,7 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, binder_size_t fda_offset; const void __user *sender_ufda_base; struct binder_proc *proc = thread->proc; + int ret; fd_buf_size = sizeof(u32) * fda->num_fds; if (fda->num_fds >= SIZE_MAX / sizeof(u32)) { @@ -2276,9 +2516,12 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, proc->pid, thread->pid); return -EINVAL; } + ret = binder_add_fixup(pf_head, fda_offset, 0, fda->num_fds * sizeof(u32)); + if (ret) + return ret; + for (fdi = 0; fdi < fda->num_fds; fdi++) { u32 fd; - int ret; binder_size_t offset = fda_offset + fdi * sizeof(fd); binder_size_t sender_uoffset = fdi * sizeof(fd); @@ -2292,7 +2535,8 @@ static int binder_translate_fd_array(struct binder_fd_array_object *fda, return 0; } -static int binder_fixup_parent(struct binder_transaction *t, +static int binder_fixup_parent(struct list_head *pf_head, + struct binder_transaction *t, struct binder_thread *thread, struct binder_buffer_object *bp, binder_size_t off_start_offset, @@ -2338,14 +2582,7 @@ static int binder_fixup_parent(struct binder_transaction *t, } buffer_offset = bp->parent_offset + (uintptr_t)parent->buffer - (uintptr_t)b->user_data; - if (binder_alloc_copy_to_buffer(&target_proc->alloc, b, buffer_offset, - &bp->buffer, sizeof(bp->buffer))) { - binder_user_error("%d:%d got transaction with invalid parent offset\n", - proc->pid, thread->pid); - return -EINVAL; - } - - return 0; + return binder_add_fixup(pf_head, buffer_offset, bp->buffer, 0); } /** @@ -2487,8 +2724,12 @@ static void binder_transaction(struct binder_proc *proc, int t_debug_id = atomic_inc_return(&binder_last_id); char *secctx = NULL; u32 secctx_sz = 0; + struct list_head sgc_head; + struct list_head pf_head; const void __user *user_buffer = (const void __user *) (uintptr_t)tr->data.ptr.buffer; + INIT_LIST_HEAD(&sgc_head); + INIT_LIST_HEAD(&pf_head); e = binder_transaction_log_add(&binder_transaction_log); e->debug_id = t_debug_id; @@ -3005,8 +3246,8 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_bad_parent; } - ret = binder_translate_fd_array(fda, user_buffer, - parent, + ret = binder_translate_fd_array(&pf_head, fda, + user_buffer, parent, &user_object.bbo, t, thread, in_reply_to); if (!ret) @@ -3038,19 +3279,14 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_bad_offset; } - if (binder_alloc_copy_user_to_buffer( - &target_proc->alloc, - t->buffer, - sg_buf_offset, - (const void __user *) - (uintptr_t)bp->buffer, - bp->length)) { - binder_user_error("%d:%d got transaction with invalid offsets ptr\n", - proc->pid, thread->pid); - return_error_param = -EFAULT; + ret = binder_defer_copy(&sgc_head, sg_buf_offset, + (const void __user *)(uintptr_t)bp->buffer, + bp->length); + if (ret) { return_error = BR_FAILED_REPLY; + return_error_param = ret; return_error_line = __LINE__; - goto err_copy_data_failed; + goto err_translate_failed; } /* Fixup buffer pointer to target proc address space */ bp->buffer = (uintptr_t) @@ -3059,7 +3295,8 @@ static void binder_transaction(struct binder_proc *proc, num_valid = (buffer_offset - off_start_offset) / sizeof(binder_size_t); - ret = binder_fixup_parent(t, thread, bp, + ret = binder_fixup_parent(&pf_head, t, + thread, bp, off_start_offset, num_valid, last_fixup_obj_off, @@ -3099,6 +3336,17 @@ static void binder_transaction(struct binder_proc *proc, return_error_line = __LINE__; goto err_copy_data_failed; } + + ret = binder_do_deferred_txn_copies(&target_proc->alloc, t->buffer, + &sgc_head, &pf_head); + if (ret) { + binder_user_error("%d:%d got transaction with invalid offsets ptr\n", + proc->pid, thread->pid); + return_error = BR_FAILED_REPLY; + return_error_param = ret; + return_error_line = __LINE__; + goto err_copy_data_failed; + } if (t->buffer->oneway_spam_suspect) tcomplete->type = BINDER_WORK_TRANSACTION_ONEWAY_SPAM_SUSPECT; else @@ -3172,6 +3420,7 @@ err_bad_object_type: err_bad_offset: err_bad_parent: err_copy_data_failed: + binder_cleanup_deferred_txn_lists(&sgc_head, &pf_head); binder_free_txn_fixups(t); trace_binder_transaction_failed_buffer_release(t->buffer); binder_transaction_buffer_release(target_proc, NULL, t->buffer, -- cgit v1.2.3 From 33dc3e3e99e626ce51f462d883b05856c6c30b1d Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 26 Nov 2021 18:06:46 +0100 Subject: w1: Misuse of get_user()/put_user() reported by sparse sparse warnings: (new ones prefixed by >>) >> drivers/w1/slaves/w1_ds28e04.c:342:13: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected char [noderef] __user *_pu_addr @@ got char *buf @@ drivers/w1/slaves/w1_ds28e04.c:342:13: sparse: expected char [noderef] __user *_pu_addr drivers/w1/slaves/w1_ds28e04.c:342:13: sparse: got char *buf >> drivers/w1/slaves/w1_ds28e04.c:356:13: sparse: sparse: incorrect type in initializer (different address spaces) @@ expected char const [noderef] __user *_gu_addr @@ got char const *buf @@ drivers/w1/slaves/w1_ds28e04.c:356:13: sparse: expected char const [noderef] __user *_gu_addr drivers/w1/slaves/w1_ds28e04.c:356:13: sparse: got char const *buf The buffer buf is a failsafe buffer in kernel space, it's not user memory hence doesn't deserve the use of get_user() or put_user(). Access 'buf' content directly. Link: https://lore.kernel.org/lkml/202111190526.K5vb7NWC-lkp@intel.com/T/ Reported-by: kernel test robot Signed-off-by: Christophe Leroy Link: https://lore.kernel.org/r/d14ed8d71ad4372e6839ae427f91441d3ba0e94d.1637946316.git.christophe.leroy@csgroup.eu Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_ds28e04.c | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/drivers/w1/slaves/w1_ds28e04.c b/drivers/w1/slaves/w1_ds28e04.c index e4f336111edc..6cef6e2edb89 100644 --- a/drivers/w1/slaves/w1_ds28e04.c +++ b/drivers/w1/slaves/w1_ds28e04.c @@ -32,7 +32,7 @@ static int w1_strong_pullup = 1; module_param_named(strong_pullup, w1_strong_pullup, int, 0); /* enable/disable CRC checking on DS28E04-100 memory accesses */ -static char w1_enable_crccheck = 1; +static bool w1_enable_crccheck = true; #define W1_EEPROM_SIZE 512 #define W1_PAGE_COUNT 16 @@ -339,32 +339,18 @@ static BIN_ATTR_RW(pio, 1); static ssize_t crccheck_show(struct device *dev, struct device_attribute *attr, char *buf) { - if (put_user(w1_enable_crccheck + 0x30, buf)) - return -EFAULT; - - return sizeof(w1_enable_crccheck); + return sysfs_emit(buf, "%d\n", w1_enable_crccheck); } static ssize_t crccheck_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - char val; - - if (count != 1 || !buf) - return -EINVAL; + int err = kstrtobool(buf, &w1_enable_crccheck); - if (get_user(val, buf)) - return -EFAULT; + if (err) + return err; - /* convert to decimal */ - val = val - 0x30; - if (val != 0 && val != 1) - return -EINVAL; - - /* set the new value */ - w1_enable_crccheck = val; - - return sizeof(w1_enable_crccheck); + return count; } static DEVICE_ATTR_RW(crccheck); -- cgit v1.2.3 From 86192251033308bb42f1e9813c962989d8ed07ec Mon Sep 17 00:00:00 2001 From: Srinivas Kandagatla Date: Tue, 30 Nov 2021 13:39:09 +0000 Subject: nvmem: core: set size for sysfs bin file For some reason we never set the size for nvmem sysfs binary file. Set this. Reported-by: Gilles BULOZ Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211130133909.6154-1-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/core.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c index e765d3d0542e..23a38dcf0fc4 100644 --- a/drivers/nvmem/core.c +++ b/drivers/nvmem/core.c @@ -312,6 +312,8 @@ static umode_t nvmem_bin_attr_is_visible(struct kobject *kobj, struct device *dev = kobj_to_dev(kobj); struct nvmem_device *nvmem = to_nvmem_device(dev); + attr->size = nvmem->size; + return nvmem_bin_attr_get_umode(nvmem); } -- cgit v1.2.3 From c7fdb2404f66131bc9c22e06f712717288826487 Mon Sep 17 00:00:00 2001 From: Abhyuday Godhasara Date: Sun, 28 Nov 2021 23:02:14 -0800 Subject: drivers: soc: xilinx: add xilinx event management driver Xilinx event management driver provides an interface to subscribe or unsubscribe for the event/callback supported by firmware. An agent can use this driver to register for Error Event, Device Event and Suspend callback. This driver only allows one agent per event to do registration. Driver will return an error in case of multiple registration for the same event. This driver gets notification from firmware through TF-A as SGI. During initialization, event manager driver register handler for SGI used for notification. It also provides SGI number info to TF-A by using IOCTL_REGISTER_SGI call to TF-A. After receiving notification from firmware, the driver makes an SMC call to TF-A to get IPI data. From the IPI data provided by TF-A, event manager identified the cause of event and forward that event/callback notification to the respective subscribed driver. After this, in case of Error Event, driver performs unregistration as firmware expecting from agent to do re-registration if the agent wants to get notified on the second occurrence of an error event. Add new IOCTL id IOCTL_REGISTER_SGI = 25 which is used to register SGI on TF-A. Older firmware doesn't have all required support for event handling which is required by the event manager driver. So add check for the register notifier version in the event manager driver. Xilinx event management driver provides support to subscribe for multiple error events with the use of Event Mask in a single call of xlnx_register_event(). Agent driver can provide 'Event' parameter value as ORed of multiple event masks to register single callback for multiple events. For example, to register callback for event=0x1 and event=0x2 for the given node, agent can provide event=0x3 (0x1 | 0x2). It is not possible to register multiple events for different nodes in a single registration call. Also provide support to receive multiple error events as in single notification from firmware and then forward it to subscribed drivers via registered callback one by one. Acked-by: Michal Simek Signed-off-by: Tejas Patel Signed-off-by: Rajan Vaja Signed-off-by: Abhyuday Godhasara Link: https://lore.kernel.org/r/20211129070216.30253-2-abhyuday.godhasara@xilinx.com Signed-off-by: Greg Kroah-Hartman --- MAINTAINERS | 6 + drivers/soc/xilinx/Kconfig | 10 + drivers/soc/xilinx/Makefile | 1 + drivers/soc/xilinx/xlnx_event_manager.c | 600 ++++++++++++++++++++++++++++ include/linux/firmware/xlnx-event-manager.h | 36 ++ include/linux/firmware/xlnx-zynqmp.h | 2 + 6 files changed, 655 insertions(+) create mode 100644 drivers/soc/xilinx/xlnx_event_manager.c create mode 100644 include/linux/firmware/xlnx-event-manager.h diff --git a/MAINTAINERS b/MAINTAINERS index 0533c00325d6..3ef68211cc6f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20946,6 +20946,12 @@ T: git https://github.com/Xilinx/linux-xlnx.git F: Documentation/devicetree/bindings/phy/xlnx,zynqmp-psgtr.yaml F: drivers/phy/xilinx/phy-zynqmp.c +XILINX EVENT MANAGEMENT DRIVER +M: Abhyuday Godhasara +S: Maintained +F: drivers/soc/xilinx/xlnx_event_manager.c +F: include/linux/firmware/xlnx-event-manager.h + XILLYBUS DRIVER M: Eli Billauer L: linux-kernel@vger.kernel.org diff --git a/drivers/soc/xilinx/Kconfig b/drivers/soc/xilinx/Kconfig index 53af9115dc31..8a755a5c8836 100644 --- a/drivers/soc/xilinx/Kconfig +++ b/drivers/soc/xilinx/Kconfig @@ -25,4 +25,14 @@ config ZYNQMP_PM_DOMAINS Say yes to enable device power management through PM domains If in doubt, say N. +config XLNX_EVENT_MANAGER + bool "Enable Xilinx Event Management Driver" + depends on ZYNQMP_FIRMWARE + default ZYNQMP_FIRMWARE + help + Say yes to enable event management support for Xilinx. + This driver uses firmware driver as an interface for event/power + management request to firmware. + + If in doubt, say N. endmenu diff --git a/drivers/soc/xilinx/Makefile b/drivers/soc/xilinx/Makefile index 9854e6f6086b..41e585bc9c67 100644 --- a/drivers/soc/xilinx/Makefile +++ b/drivers/soc/xilinx/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_ZYNQMP_POWER) += zynqmp_power.o obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += zynqmp_pm_domains.o +obj-$(CONFIG_XLNX_EVENT_MANAGER) += xlnx_event_manager.o diff --git a/drivers/soc/xilinx/xlnx_event_manager.c b/drivers/soc/xilinx/xlnx_event_manager.c new file mode 100644 index 000000000000..b27f8853508e --- /dev/null +++ b/drivers/soc/xilinx/xlnx_event_manager.c @@ -0,0 +1,600 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx Event Management Driver + * + * Copyright (C) 2021 Xilinx, Inc. + * + * Abhyuday Godhasara + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static DEFINE_PER_CPU_READ_MOSTLY(int, cpu_number1); + +static int virq_sgi; +static int event_manager_availability = -EACCES; + +/* SGI number used for Event management driver */ +#define XLNX_EVENT_SGI_NUM (15) + +/* Max number of driver can register for same event */ +#define MAX_DRIVER_PER_EVENT (10U) + +/* Max HashMap Order for PM API feature check (1<<7 = 128) */ +#define REGISTERED_DRIVER_MAX_ORDER (7) + +#define MAX_BITS (32U) /* Number of bits available for error mask */ + +#define FIRMWARE_VERSION_MASK (0xFFFFU) +#define REGISTER_NOTIFIER_FIRMWARE_VERSION (2U) + +static DEFINE_HASHTABLE(reg_driver_map, REGISTERED_DRIVER_MAX_ORDER); +static int sgi_num = XLNX_EVENT_SGI_NUM; + +/** + * struct registered_event_data - Registered Event Data. + * @key: key is the combine id(Node-Id | Event-Id) of type u64 + * where upper u32 for Node-Id and lower u32 for Event-Id, + * And this used as key to index into hashmap. + * @agent_data: Data passed back to handler function. + * @cb_type: Type of Api callback, like PM_NOTIFY_CB, etc. + * @eve_cb: Function pointer to store the callback function. + * @wake: If this flag set, firmware will wakeup processor if is + * in sleep or power down state. + * @hentry: hlist_node that hooks this entry into hashtable. + */ +struct registered_event_data { + u64 key; + enum pm_api_cb_id cb_type; + void *agent_data; + + event_cb_func_t eve_cb; + bool wake; + struct hlist_node hentry; +}; + +static bool xlnx_is_error_event(const u32 node_id) +{ + if (node_id == EVENT_ERROR_PMC_ERR1 || + node_id == EVENT_ERROR_PMC_ERR2 || + node_id == EVENT_ERROR_PSM_ERR1 || + node_id == EVENT_ERROR_PSM_ERR2) + return true; + + return false; +} + +static int xlnx_add_cb_for_notify_event(const u32 node_id, const u32 event, const bool wake, + event_cb_func_t cb_fun, void *data) +{ + u64 key = 0; + struct registered_event_data *eve_data; + + key = ((u64)node_id << 32U) | (u64)event; + /* Check for existing entry in hash table for given key id */ + hash_for_each_possible(reg_driver_map, eve_data, hentry, key) { + if (eve_data->key == key) { + pr_err("Found as already registered\n"); + return -EINVAL; + } + } + + /* Add new entry if not present */ + eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL); + if (!eve_data) + return -ENOMEM; + + eve_data->key = key; + eve_data->cb_type = PM_NOTIFY_CB; + eve_data->eve_cb = cb_fun; + eve_data->wake = wake; + eve_data->agent_data = data; + + hash_add(reg_driver_map, &eve_data->hentry, key); + + return 0; +} + +static int xlnx_add_cb_for_suspend(event_cb_func_t cb_fun, void *data) +{ + struct registered_event_data *eve_data; + + /* Check for existing entry in hash table for given cb_type */ + hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) { + if (eve_data->cb_type == PM_INIT_SUSPEND_CB) { + pr_err("Found as already registered\n"); + return -EINVAL; + } + } + + /* Add new entry if not present */ + eve_data = kmalloc(sizeof(*eve_data), GFP_KERNEL); + if (!eve_data) + return -ENOMEM; + + eve_data->key = 0; + eve_data->cb_type = PM_INIT_SUSPEND_CB; + eve_data->eve_cb = cb_fun; + eve_data->agent_data = data; + + hash_add(reg_driver_map, &eve_data->hentry, PM_INIT_SUSPEND_CB); + + return 0; +} + +static int xlnx_remove_cb_for_suspend(event_cb_func_t cb_fun) +{ + bool is_callback_found = false; + struct registered_event_data *eve_data; + + /* Check for existing entry in hash table for given cb_type */ + hash_for_each_possible(reg_driver_map, eve_data, hentry, PM_INIT_SUSPEND_CB) { + if (eve_data->cb_type == PM_INIT_SUSPEND_CB && + eve_data->eve_cb == cb_fun) { + is_callback_found = true; + /* remove an object from a hashtable */ + hash_del(&eve_data->hentry); + kfree(eve_data); + } + } + if (!is_callback_found) { + pr_warn("Didn't find any registered callback for suspend event\n"); + return -EINVAL; + } + + return 0; +} + +static int xlnx_remove_cb_for_notify_event(const u32 node_id, const u32 event, + event_cb_func_t cb_fun) +{ + bool is_callback_found = false; + struct registered_event_data *eve_data; + u64 key = ((u64)node_id << 32U) | (u64)event; + + /* Check for existing entry in hash table for given key id */ + hash_for_each_possible(reg_driver_map, eve_data, hentry, key) { + if (eve_data->key == key && + eve_data->eve_cb == cb_fun) { + is_callback_found = true; + /* remove an object from a hashtable */ + hash_del(&eve_data->hentry); + kfree(eve_data); + } + } + if (!is_callback_found) { + pr_warn("Didn't find any registered callback for 0x%x 0x%x\n", + node_id, event); + return -EINVAL; + } + + return 0; +} + +/** + * xlnx_register_event() - Register for the event. + * @cb_type: Type of callback from pm_api_cb_id, + * PM_NOTIFY_CB - for Error Events, + * PM_INIT_SUSPEND_CB - for suspend callback. + * @node_id: Node-Id related to event. + * @event: Event Mask for the Error Event. + * @wake: Flag specifying whether the subsystem should be woken upon + * event notification. + * @cb_fun: Function pointer to store the callback function. + * @data: Pointer for the driver instance. + * + * Return: Returns 0 on successful registration else error code. + */ +int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event, + const bool wake, event_cb_func_t cb_fun, void *data) +{ + int ret = 0; + u32 eve; + int pos; + + if (event_manager_availability) + return event_manager_availability; + + if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) { + pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type); + return -EINVAL; + } + + if (!cb_fun) + return -EFAULT; + + if (cb_type == PM_INIT_SUSPEND_CB) { + ret = xlnx_add_cb_for_suspend(cb_fun, data); + } else { + if (!xlnx_is_error_event(node_id)) { + /* Add entry for Node-Id/Event in hash table */ + ret = xlnx_add_cb_for_notify_event(node_id, event, wake, cb_fun, data); + } else { + /* Add into Hash table */ + for (pos = 0; pos < MAX_BITS; pos++) { + eve = event & (1 << pos); + if (!eve) + continue; + + /* Add entry for Node-Id/Eve in hash table */ + ret = xlnx_add_cb_for_notify_event(node_id, eve, wake, cb_fun, + data); + /* Break the loop if got error */ + if (ret) + break; + } + if (ret) { + /* Skip the Event for which got the error */ + pos--; + /* Remove registered(during this call) event from hash table */ + for ( ; pos >= 0; pos--) { + eve = event & (1 << pos); + if (!eve) + continue; + xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun); + } + } + } + + if (ret) { + pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id, + event, ret); + return ret; + } + + /* Register for Node-Id/Event combination in firmware */ + ret = zynqmp_pm_register_notifier(node_id, event, wake, true); + if (ret) { + pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, node_id, + event, ret); + /* Remove already registered event from hash table */ + if (xlnx_is_error_event(node_id)) { + for (pos = 0; pos < MAX_BITS; pos++) { + eve = event & (1 << pos); + if (!eve) + continue; + xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun); + } + } else { + xlnx_remove_cb_for_notify_event(node_id, event, cb_fun); + } + return ret; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(xlnx_register_event); + +/** + * xlnx_unregister_event() - Unregister for the event. + * @cb_type: Type of callback from pm_api_cb_id, + * PM_NOTIFY_CB - for Error Events, + * PM_INIT_SUSPEND_CB - for suspend callback. + * @node_id: Node-Id related to event. + * @event: Event Mask for the Error Event. + * @cb_fun: Function pointer of callback function. + * + * Return: Returns 0 on successful unregistration else error code. + */ +int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, const u32 event, + event_cb_func_t cb_fun) +{ + int ret; + u32 eve, pos; + + if (event_manager_availability) + return event_manager_availability; + + if (cb_type != PM_NOTIFY_CB && cb_type != PM_INIT_SUSPEND_CB) { + pr_err("%s() Unsupported Callback 0x%x\n", __func__, cb_type); + return -EINVAL; + } + + if (!cb_fun) + return -EFAULT; + + if (cb_type == PM_INIT_SUSPEND_CB) { + ret = xlnx_remove_cb_for_suspend(cb_fun); + } else { + /* Remove Node-Id/Event from hash table */ + if (!xlnx_is_error_event(node_id)) { + xlnx_remove_cb_for_notify_event(node_id, event, cb_fun); + } else { + for (pos = 0; pos < MAX_BITS; pos++) { + eve = event & (1 << pos); + if (!eve) + continue; + + xlnx_remove_cb_for_notify_event(node_id, eve, cb_fun); + } + } + + /* Un-register for Node-Id/Event combination */ + ret = zynqmp_pm_register_notifier(node_id, event, false, false); + if (ret) { + pr_err("%s() failed for 0x%x and 0x%x: %d\n", + __func__, node_id, event, ret); + return ret; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(xlnx_unregister_event); + +static void xlnx_call_suspend_cb_handler(const u32 *payload) +{ + bool is_callback_found = false; + struct registered_event_data *eve_data; + u32 cb_type = payload[0]; + + /* Check for existing entry in hash table for given cb_type */ + hash_for_each_possible(reg_driver_map, eve_data, hentry, cb_type) { + if (eve_data->cb_type == cb_type) { + eve_data->eve_cb(&payload[0], eve_data->agent_data); + is_callback_found = true; + } + } + if (!is_callback_found) + pr_warn("Didn't find any registered callback for suspend event\n"); +} + +static void xlnx_call_notify_cb_handler(const u32 *payload) +{ + bool is_callback_found = false; + struct registered_event_data *eve_data; + u64 key = ((u64)payload[1] << 32U) | (u64)payload[2]; + int ret; + + /* Check for existing entry in hash table for given key id */ + hash_for_each_possible(reg_driver_map, eve_data, hentry, key) { + if (eve_data->key == key) { + eve_data->eve_cb(&payload[0], eve_data->agent_data); + is_callback_found = true; + + /* re register with firmware to get future events */ + ret = zynqmp_pm_register_notifier(payload[1], payload[2], + eve_data->wake, true); + if (ret) { + pr_err("%s() failed for 0x%x and 0x%x: %d\r\n", __func__, + payload[1], payload[2], ret); + /* Remove already registered event from hash table */ + xlnx_remove_cb_for_notify_event(payload[1], payload[2], + eve_data->eve_cb); + } + } + } + if (!is_callback_found) + pr_warn("Didn't find any registered callback for 0x%x 0x%x\n", + payload[1], payload[2]); +} + +static void xlnx_get_event_callback_data(u32 *buf) +{ + zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); +} + +static irqreturn_t xlnx_event_handler(int irq, void *dev_id) +{ + u32 cb_type, node_id, event, pos; + u32 payload[CB_MAX_PAYLOAD_SIZE] = {0}; + u32 event_data[CB_MAX_PAYLOAD_SIZE] = {0}; + + /* Get event data */ + xlnx_get_event_callback_data(payload); + + /* First element is callback type, others are callback arguments */ + cb_type = payload[0]; + + if (cb_type == PM_NOTIFY_CB) { + node_id = payload[1]; + event = payload[2]; + if (!xlnx_is_error_event(node_id)) { + xlnx_call_notify_cb_handler(payload); + } else { + /* + * Each call back function expecting payload as an input arguments. + * We can get multiple error events as in one call back through error + * mask. So payload[2] may can contain multiple error events. + * In reg_driver_map database we store data in the combination of single + * node_id-error combination. + * So coping the payload message into event_data and update the + * event_data[2] with Error Mask for single error event and use + * event_data as input argument for registered call back function. + * + */ + memcpy(event_data, payload, (4 * CB_MAX_PAYLOAD_SIZE)); + /* Support Multiple Error Event */ + for (pos = 0; pos < MAX_BITS; pos++) { + if ((0 == (event & (1 << pos)))) + continue; + event_data[2] = (event & (1 << pos)); + xlnx_call_notify_cb_handler(event_data); + } + } + } else if (cb_type == PM_INIT_SUSPEND_CB) { + xlnx_call_suspend_cb_handler(payload); + } else { + pr_err("%s() Unsupported Callback %d\n", __func__, cb_type); + } + + return IRQ_HANDLED; +} + +static int xlnx_event_cpuhp_start(unsigned int cpu) +{ + enable_percpu_irq(virq_sgi, IRQ_TYPE_NONE); + + return 0; +} + +static int xlnx_event_cpuhp_down(unsigned int cpu) +{ + disable_percpu_irq(virq_sgi); + + return 0; +} + +static void xlnx_disable_percpu_irq(void *data) +{ + disable_percpu_irq(virq_sgi); +} + +static int xlnx_event_init_sgi(struct platform_device *pdev) +{ + int ret = 0; + int cpu = smp_processor_id(); + /* + * IRQ related structures are used for the following: + * for each SGI interrupt ensure its mapped by GIC IRQ domain + * and that each corresponding linux IRQ for the HW IRQ has + * a handler for when receiving an interrupt from the remote + * processor. + */ + struct irq_domain *domain; + struct irq_fwspec sgi_fwspec; + struct device_node *interrupt_parent = NULL; + struct device *parent = pdev->dev.parent; + + /* Find GIC controller to map SGIs. */ + interrupt_parent = of_irq_find_parent(parent->of_node); + if (!interrupt_parent) { + dev_err(&pdev->dev, "Failed to find property for Interrupt parent\n"); + return -EINVAL; + } + + /* Each SGI needs to be associated with GIC's IRQ domain. */ + domain = irq_find_host(interrupt_parent); + of_node_put(interrupt_parent); + + /* Each mapping needs GIC domain when finding IRQ mapping. */ + sgi_fwspec.fwnode = domain->fwnode; + + /* + * When irq domain looks at mapping each arg is as follows: + * 3 args for: interrupt type (SGI), interrupt # (set later), type + */ + sgi_fwspec.param_count = 1; + + /* Set SGI's hwirq */ + sgi_fwspec.param[0] = sgi_num; + virq_sgi = irq_create_fwspec_mapping(&sgi_fwspec); + + per_cpu(cpu_number1, cpu) = cpu; + ret = request_percpu_irq(virq_sgi, xlnx_event_handler, "xlnx_event_mgmt", + &cpu_number1); + WARN_ON(ret); + if (ret) { + irq_dispose_mapping(virq_sgi); + return ret; + } + + irq_to_desc(virq_sgi); + irq_set_status_flags(virq_sgi, IRQ_PER_CPU); + + return ret; +} + +static void xlnx_event_cleanup_sgi(struct platform_device *pdev) +{ + int cpu = smp_processor_id(); + + per_cpu(cpu_number1, cpu) = cpu; + + cpuhp_remove_state(CPUHP_AP_ONLINE_DYN); + + on_each_cpu(xlnx_disable_percpu_irq, NULL, 1); + + irq_clear_status_flags(virq_sgi, IRQ_PER_CPU); + free_percpu_irq(virq_sgi, &cpu_number1); + irq_dispose_mapping(virq_sgi); +} + +static int xlnx_event_manager_probe(struct platform_device *pdev) +{ + int ret; + + ret = zynqmp_pm_feature(PM_REGISTER_NOTIFIER); + if (ret < 0) { + dev_err(&pdev->dev, "Feature check failed with %d\n", ret); + return ret; + } + + if ((ret & FIRMWARE_VERSION_MASK) < + REGISTER_NOTIFIER_FIRMWARE_VERSION) { + dev_err(&pdev->dev, "Register notifier version error. Expected Firmware: v%d - Found: v%d\n", + REGISTER_NOTIFIER_FIRMWARE_VERSION, + ret & FIRMWARE_VERSION_MASK); + return -EOPNOTSUPP; + } + + /* Initialize the SGI */ + ret = xlnx_event_init_sgi(pdev); + if (ret) { + dev_err(&pdev->dev, "SGI Init has been failed with %d\n", ret); + return ret; + } + + /* Setup function for the CPU hot-plug cases */ + cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "soc/event:starting", + xlnx_event_cpuhp_start, xlnx_event_cpuhp_down); + + ret = zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_REGISTER_SGI, sgi_num, + 0, NULL); + if (ret) { + dev_err(&pdev->dev, "SGI %d Registration over TF-A failed with %d\n", sgi_num, ret); + xlnx_event_cleanup_sgi(pdev); + return ret; + } + + event_manager_availability = 0; + + dev_info(&pdev->dev, "SGI %d Registered over TF-A\n", sgi_num); + dev_info(&pdev->dev, "Xilinx Event Management driver probed\n"); + + return ret; +} + +static int xlnx_event_manager_remove(struct platform_device *pdev) +{ + int i; + struct registered_event_data *eve_data; + struct hlist_node *tmp; + int ret; + + hash_for_each_safe(reg_driver_map, i, tmp, eve_data, hentry) { + hash_del(&eve_data->hentry); + kfree(eve_data); + } + + ret = zynqmp_pm_invoke_fn(PM_IOCTL, 0, IOCTL_REGISTER_SGI, 0, 1, NULL); + if (ret) + dev_err(&pdev->dev, "SGI unregistration over TF-A failed with %d\n", ret); + + xlnx_event_cleanup_sgi(pdev); + + event_manager_availability = -EACCES; + + return ret; +} + +static struct platform_driver xlnx_event_manager_driver = { + .probe = xlnx_event_manager_probe, + .remove = xlnx_event_manager_remove, + .driver = { + .name = "xlnx_event_manager", + }, +}; +module_param(sgi_num, uint, 0); +module_platform_driver(xlnx_event_manager_driver); diff --git a/include/linux/firmware/xlnx-event-manager.h b/include/linux/firmware/xlnx-event-manager.h new file mode 100644 index 000000000000..3f87c4929d21 --- /dev/null +++ b/include/linux/firmware/xlnx-event-manager.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _FIRMWARE_XLNX_EVENT_MANAGER_H_ +#define _FIRMWARE_XLNX_EVENT_MANAGER_H_ + +#include + +#define CB_MAX_PAYLOAD_SIZE (4U) /*In payload maximum 32bytes */ + +/************************** Exported Function *****************************/ + +typedef void (*event_cb_func_t)(const u32 *payload, void *data); + +#if IS_REACHABLE(CONFIG_XLNX_EVENT_MANAGER) +int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, + const u32 event, const bool wake, + event_cb_func_t cb_fun, void *data); + +int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, + const u32 event, event_cb_func_t cb_fun); +#else +static inline int xlnx_register_event(const enum pm_api_cb_id cb_type, const u32 node_id, + const u32 event, const bool wake, + event_cb_func_t cb_fun, void *data) +{ + return -ENODEV; +} + +static inline int xlnx_unregister_event(const enum pm_api_cb_id cb_type, const u32 node_id, + const u32 event, event_cb_func_t cb_fun) +{ + return -ENODEV; +} +#endif + +#endif /* _FIRMWARE_XLNX_EVENT_MANAGER_H_ */ diff --git a/include/linux/firmware/xlnx-zynqmp.h b/include/linux/firmware/xlnx-zynqmp.h index 077e894bb340..907cb01890cf 100644 --- a/include/linux/firmware/xlnx-zynqmp.h +++ b/include/linux/firmware/xlnx-zynqmp.h @@ -141,6 +141,8 @@ enum pm_ioctl_id { /* Set healthy bit value */ IOCTL_SET_BOOT_HEALTH_STATUS = 17, IOCTL_OSPI_MUX_SELECT = 21, + /* Register SGI to ATF */ + IOCTL_REGISTER_SGI = 25, }; enum pm_query_id { -- cgit v1.2.3 From a515814e742d8dbd04a0bc2d73b798d7855ec532 Mon Sep 17 00:00:00 2001 From: Abhyuday Godhasara Date: Sun, 28 Nov 2021 23:02:15 -0800 Subject: firmware: xilinx: instantiate xilinx event manager driver Register simple platform device to instantiate Xilinx event manager driver. Acked-by: Michal Simek Signed-off-by: Rajan Vaja Signed-off-by: Abhyuday Godhasara Link: https://lore.kernel.org/r/20211129070216.30253-3-abhyuday.godhasara@xilinx.com Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/xilinx/zynqmp.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index c2828ee6d4cf..bfa5ec7a808c 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -23,6 +23,7 @@ #include #include +#include #include "zynqmp-debug.h" /* Max HashMap Order for PM API feature check (1<<7 = 128) */ @@ -38,6 +39,8 @@ static bool feature_check_enabled; static DEFINE_HASHTABLE(pm_api_features_map, PM_API_FEATURE_CHECK_MAX_ORDER); +static struct platform_device *em_dev; + /** * struct pm_api_feature_data - PM API Feature data * @pm_api_id: PM API Id, used as key to index into hashmap @@ -1492,6 +1495,15 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) zynqmp_pm_api_debugfs_init(); + np = of_find_compatible_node(NULL, NULL, "xlnx,versal"); + if (np) { + em_dev = platform_device_register_data(&pdev->dev, "xlnx_event_manager", + -1, NULL, 0); + if (IS_ERR(em_dev)) + dev_err_probe(&pdev->dev, PTR_ERR(em_dev), "EM register fail with error\n"); + } + of_node_put(np); + return of_platform_populate(dev->of_node, NULL, NULL, dev); } @@ -1509,6 +1521,8 @@ static int zynqmp_firmware_remove(struct platform_device *pdev) kfree(feature_data); } + platform_device_unregister(em_dev); + return 0; } -- cgit v1.2.3 From 70602b37c4afd91c4dfc237121b31310b6c02a7a Mon Sep 17 00:00:00 2001 From: Abhyuday Godhasara Date: Sun, 28 Nov 2021 23:02:16 -0800 Subject: driver: soc: xilinx: register for power events in zynqmp power driver With Xilinx Event Management driver, all types of events like power and error gets handled from single place as part of event management driver. So power events(SUSPEND_POWER_REQUEST and SUSPEND_SYSTEM_SHUTDOWN) also gets handled by event management driver instead of zynqmp_power driver. zynqmp-power driver use event management driver and provide callback function for Suspend and shutdown handler, which will be called by event management driver when respective event is arrived. If event management driver is not available than use ipi-mailbox rx channel or IPI interrupt IRQ handler for power events (suspend/shutdown) same as current zynqmp-power driver. Acked-by: Michal Simek Signed-off-by: Rajan Vaja Signed-off-by: Abhyuday Godhasara Link: https://lore.kernel.org/r/20211129070216.30253-4-abhyuday.godhasara@xilinx.com Signed-off-by: Greg Kroah-Hartman --- drivers/soc/xilinx/zynqmp_power.c | 48 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 76478fe2301f..fe7be176b226 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -16,6 +16,7 @@ #include #include +#include #include /** @@ -30,6 +31,7 @@ struct zynqmp_pm_work_struct { static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work; static struct mbox_chan *rx_chan; +static bool event_registered; enum pm_suspend_mode { PM_SUSPEND_MODE_FIRST = 0, @@ -51,6 +53,19 @@ static void zynqmp_pm_get_callback_data(u32 *buf) zynqmp_pm_invoke_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf); } +static void suspend_event_callback(const u32 *payload, void *data) +{ + /* First element is callback API ID, others are callback arguments */ + if (work_pending(&zynqmp_pm_init_suspend_work->callback_work)) + return; + + /* Copy callback arguments into work's structure */ + memcpy(zynqmp_pm_init_suspend_work->args, &payload[1], + sizeof(zynqmp_pm_init_suspend_work->args)); + + queue_work(system_unbound_wq, &zynqmp_pm_init_suspend_work->callback_work); +} + static irqreturn_t zynqmp_pm_isr(int irq, void *data) { u32 payload[CB_PAYLOAD_SIZE]; @@ -179,7 +194,32 @@ static int zynqmp_pm_probe(struct platform_device *pdev) if (pm_api_version < ZYNQMP_PM_VERSION) return -ENODEV; - if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) { + /* + * First try to use Xilinx Event Manager by registering suspend_event_callback + * for suspend/shutdown event. + * If xlnx_register_event() returns -EACCES (Xilinx Event Manager + * is not available to use) or -ENODEV(Xilinx Event Manager not compiled), + * then use ipi-mailbox or interrupt method. + */ + ret = xlnx_register_event(PM_INIT_SUSPEND_CB, 0, 0, false, + suspend_event_callback, NULL); + if (!ret) { + zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, + sizeof(struct zynqmp_pm_work_struct), + GFP_KERNEL); + if (!zynqmp_pm_init_suspend_work) { + xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, + suspend_event_callback); + return -ENOMEM; + } + event_registered = true; + + INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work, + zynqmp_pm_init_suspend_work_fn); + } else if (ret != -EACCES && ret != -ENODEV) { + dev_err(&pdev->dev, "Failed to Register with Xilinx Event manager %d\n", ret); + return ret; + } else if (of_find_property(pdev->dev.of_node, "mboxes", NULL)) { zynqmp_pm_init_suspend_work = devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_pm_work_struct), @@ -223,6 +263,10 @@ static int zynqmp_pm_probe(struct platform_device *pdev) ret = sysfs_create_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); if (ret) { + if (event_registered) { + xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback); + event_registered = false; + } dev_err(&pdev->dev, "unable to create sysfs interface\n"); return ret; } @@ -233,6 +277,8 @@ static int zynqmp_pm_probe(struct platform_device *pdev) static int zynqmp_pm_remove(struct platform_device *pdev) { sysfs_remove_file(&pdev->dev.kobj, &dev_attr_suspend_mode.attr); + if (event_registered) + xlnx_unregister_event(PM_INIT_SUSPEND_CB, 0, 0, suspend_event_callback); if (!rx_chan) mbox_free_channel(rx_chan); -- cgit v1.2.3 From 35619155d044830357f06f1d2c8188c4530b4d7a Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 13 Nov 2021 16:23:14 +0100 Subject: iio: imu: st_lsm6dsx: add dts property to disable sensor-hub Introduce the capability to disable sensorhub through a device-tree property since there are some configurations where users want to explicitly disable sensor-hub auto-probing at bootstrap. A typical configuration is when the sensorhub clock/data lines are connected to a pull-up resistor since no slave sensors are connected to the i2c master. If SDO/SA0 line is connected to the same pull-up resistor, when the driver tries to probe slave devices connected on sensor-hub, it will force SDO/SA0 line to low, modifying the device i2c address. Tested-by: Mario Tesi Signed-off-by: Lorenzo Bianconi Link: https://lore.kernel.org/r/ad7894e7b1c6fb3427fab3f623bb942860ad45cf.1636816719.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index f2cbbc756459..727b4b6ac696 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2244,7 +2244,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; hub_settings = &hw->settings->shub_settings; - if (hub_settings->master_en.addr) { + if (hub_settings->master_en.addr && + (!dev_fwnode(dev) || + !device_property_read_bool(dev, "st,disable-sensor-hub"))) { err = st_lsm6dsx_shub_probe(hw, name); if (err < 0) return err; -- cgit v1.2.3 From a91f82d944e369c13e62f94f19994e8c915cb028 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 13 Nov 2021 16:23:15 +0100 Subject: Documentation: dt: iio: st_lsm6dsx: add disable-sensor-hub property Enable/disable internal i2c controller slave autoprobing at bootstrap. Disable sensor-hub is useful if i2c controller clock/data lines are connected through a pull-up with other chip lines (e.g. SDO/SA0). Signed-off-by: Lorenzo Bianconi Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/54287a93922ac839501b776d288cc368aa81f0ab.1636816719.git.lorenzo@kernel.org Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml index d9b3213318fb..0750f700a143 100644 --- a/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml +++ b/Documentation/devicetree/bindings/iio/imu/st,lsm6dsx.yaml @@ -61,6 +61,13 @@ properties: type: boolean description: enable/disable internal i2c controller pullup resistors. + st,disable-sensor-hub: + type: boolean + description: + Enable/disable internal i2c controller slave autoprobing at bootstrap. + Disable sensor-hub is useful if i2c controller clock/data lines are + connected through a pull-up with other chip lines (e.g. SDO/SA0). + drive-open-drain: type: boolean description: -- cgit v1.2.3 From 4da5f2d6f2e3286262d32db901ec735a6a5a51b9 Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Thu, 18 Nov 2021 17:12:32 +0300 Subject: iio:adc:axp20x: add support for NTC thermistor Most AXPxxx-based reference designs place a 10k NTC thermistor on a TS pin. When appropriately configured, AXP PMICs will inject fixed current (80uA by default) into TS pin and measure the voltage across a thermistor. The PMIC itself will by default compare this voltage with predefined thresholds and disable battery charging whenever the battery is too hot or too cold. Alternatively, the TS pin can be configured as general-purpose ADC input. This mode is not supported by the driver. This patch allows reading the voltage on the TS pin. It can be then either processed by userspace or used by kernel consumer like hwmon ntc thermistor driver. Signed-off-by: Evgeny Boger Acked-by: Maxime Ripard Reviewed-by: Quentin Schulz Link: https://lore.kernel.org/r/20211118141233.247907-2-boger@wirenboard.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 45 ++++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 3e0c0233b431..12d469a52cea 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -186,6 +186,8 @@ static const struct iio_chan_spec axp20x_adc_channels[] = { AXP20X_BATT_CHRG_I_H), AXP20X_ADC_CHANNEL(AXP20X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP20X_TS_IN, "ts_v", IIO_VOLTAGE, + AXP20X_TS_IN_H), }; static const struct iio_chan_spec axp22x_adc_channels[] = { @@ -203,6 +205,8 @@ static const struct iio_chan_spec axp22x_adc_channels[] = { AXP20X_BATT_CHRG_I_H), AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_TS_IN, "ts_v", IIO_VOLTAGE, + AXP22X_TS_ADC_H), }; static const struct iio_chan_spec axp813_adc_channels[] = { @@ -222,6 +226,8 @@ static const struct iio_chan_spec axp813_adc_channels[] = { AXP20X_BATT_CHRG_I_H), AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, AXP20X_BATT_DISCHRG_I_H), + AXP20X_ADC_CHANNEL(AXP813_TS_IN, "ts_v", IIO_VOLTAGE, + AXP288_TS_ADC_H), }; static int axp20x_adc_raw(struct iio_dev *indio_dev, @@ -307,11 +313,36 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) *val2 = 400000; return IIO_VAL_INT_PLUS_MICRO; + case AXP20X_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + default: return -EINVAL; } } +static int axp22x_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP22X_BATT_V: + /* 1.1 mV per LSB */ + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP22X_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} static int axp813_adc_scale_voltage(int channel, int *val, int *val2) { switch (channel) { @@ -325,6 +356,12 @@ static int axp813_adc_scale_voltage(int channel, int *val, int *val2) *val2 = 100000; return IIO_VAL_INT_PLUS_MICRO; + case AXP813_TS_IN: + /* 0.8 mV per LSB */ + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + default: return -EINVAL; } @@ -378,12 +415,7 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, { switch (chan->type) { case IIO_VOLTAGE: - if (chan->channel != AXP22X_BATT_V) - return -EINVAL; - - *val = 1; - *val2 = 100000; - return IIO_VAL_INT_PLUS_MICRO; + return axp22x_adc_scale_voltage(chan->channel, val, val2); case IIO_CURRENT: *val = 0; @@ -488,6 +520,7 @@ static int axp22x_read_raw(struct iio_dev *indio_dev, { switch (mask) { case IIO_CHAN_INFO_OFFSET: + /* For PMIC temp only */ *val = -2677; return IIO_VAL_INT; -- cgit v1.2.3 From fc27e69f4df643e7985752ebee72299d085b0efc Mon Sep 17 00:00:00 2001 From: Evgeny Boger Date: Thu, 18 Nov 2021 17:12:33 +0300 Subject: dt-bindings: iio: adc: document TS voltage in AXP PMICs Most AXPxxx-based reference designs place a 10k NTC thermistor on a TS pin. axp20x IIO driver now report the voltage of this pin via additional IIO channel. Add new "ts_v" channel to the channel description. Signed-off-by: Evgeny Boger Acked-by: Rob Herring Reviewed-by: Quentin Schulz Link: https://lore.kernel.org/r/20211118141233.247907-3-boger@wirenboard.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml index e759a5da708d..d6d3d8590171 100644 --- a/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/x-powers,axp209-adc.yaml @@ -27,6 +27,7 @@ description: | 8 | batt_v 9 | batt_chrg_i 10 | batt_dischrg_i + 11 | ts_v AXP22x ------ @@ -34,6 +35,7 @@ description: | 1 | batt_v 2 | batt_chrg_i 3 | batt_dischrg_i + 4 | ts_v AXP813 ------ @@ -42,6 +44,7 @@ description: | 2 | batt_v 3 | batt_chrg_i 4 | batt_dischrg_i + 5 | ts_v properties: -- cgit v1.2.3 From 2cc131ace0d2f47e5d8fcc8a2be12f5e3a20b1f0 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Thu, 25 Nov 2021 17:56:45 +0500 Subject: dt-bindings: iio: light: ltr501: Add proximity-near-level This value inidcates the proximity level that should be considered "close". Signed-off-by: Nikita Travkin Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211125125646.54831-1-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml index db0407bc9209..c8074f180a79 100644 --- a/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml +++ b/Documentation/devicetree/bindings/iio/light/liteon,ltr501.yaml @@ -9,6 +9,9 @@ title: LiteON LTR501 I2C Proximity and Light sensor maintainers: - Nikita Travkin +allOf: + - $ref: ../common.yaml# + properties: compatible: enum: @@ -25,6 +28,8 @@ properties: interrupts: maxItems: 1 + proximity-near-level: true + additionalProperties: false required: @@ -42,6 +47,8 @@ examples: light-sensor@23 { compatible = "liteon,ltr559"; reg = <0x23>; + proximity-near-level = <75>; + vdd-supply = <&pm8916_l17>; vddio-supply = <&pm8916_l6>; -- cgit v1.2.3 From 4114835810aecec94b4163b8b1086dd953476391 Mon Sep 17 00:00:00 2001 From: Nikita Travkin Date: Thu, 25 Nov 2021 17:56:46 +0500 Subject: iio: ltr501: Export near level property for proximity sensor Userspace tools like iio-sensor-proxy need to know the proximity level that should be considered "near". This value is hardware-specific and can be defined via the devicetree. Allow the driver to export the near level. Signed-off-by: Nikita Travkin Link: https://lore.kernel.org/r/20211125125646.54831-2-nikita@trvn.ru Signed-off-by: Jonathan Cameron --- drivers/iio/light/ltr501.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index bab5b78f2e30..902b9c7a96a7 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -166,6 +166,7 @@ struct ltr501_data { struct regmap_field *reg_ps_rate; struct regmap_field *reg_als_prst; struct regmap_field *reg_ps_prst; + uint32_t near_level; }; static const struct ltr501_samp_table ltr501_als_samp_table[] = { @@ -525,6 +526,25 @@ static int ltr501_write_intr_prst(struct ltr501_data *data, return -EINVAL; } +static ssize_t ltr501_read_near_level(struct iio_dev *indio_dev, + uintptr_t priv, + const struct iio_chan_spec *chan, + char *buf) +{ + struct ltr501_data *data = iio_priv(indio_dev); + + return sprintf(buf, "%u\n", data->near_level); +} + +static const struct iio_chan_spec_ext_info ltr501_ext_info[] = { + { + .name = "nearlevel", + .shared = IIO_SEPARATE, + .read = ltr501_read_near_level, + }, + { /* sentinel */ } +}; + static const struct iio_event_spec ltr501_als_event_spec[] = { { .type = IIO_EV_TYPE_THRESH, @@ -609,6 +629,7 @@ static const struct iio_chan_spec ltr501_channels[] = { }, .event_spec = ltr501_pxs_event_spec, .num_event_specs = ARRAY_SIZE(ltr501_pxs_event_spec), + .ext_info = ltr501_ext_info, }, IIO_CHAN_SOFT_TIMESTAMP(3), }; @@ -1531,6 +1552,10 @@ static int ltr501_probe(struct i2c_client *client, if ((partid >> 4) != data->chip_info->partid) return -ENODEV; + if (device_property_read_u32(&client->dev, "proximity-near-level", + &data->near_level)) + data->near_level = 0; + indio_dev->info = data->chip_info->info; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->no_channels; -- cgit v1.2.3 From 0bb12606c05fe9737e3056fe76d6e4b9c2a87b57 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 2 Dec 2021 17:08:18 +0200 Subject: iio:dac:ad7293: add support for AD7293 The AD7293 is a Power Amplifier drain current controller containing functionality for general-purpose monitoring and control of current, voltage, and temperature, integrated into a single chip solution with an SPI-compatible interface. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/AD7293.pdf Signed-off-by: Antoniu Miclaus Reviewed-by: Cai Huoqing Link: https://lore.kernel.org/r/20211202150819.24832-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 11 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad7293.c | 934 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 946 insertions(+) create mode 100644 drivers/iio/dac/ad7293.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 75e1f2b48638..6206b90fc08f 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -221,6 +221,17 @@ config AD5791 To compile this driver as a module, choose M here: the module will be called ad5791. +config AD7293 + tristate "Analog Devices AD7293 Power Amplifier Current Controller" + depends on SPI + help + Say yes here to build support for Analog Devices AD7293 + Power Amplifier Current Controller with + ADC, DACs, and Temperature and Current Sensors + + To compile this driver as a module, choose M here: the + module will be called ad7293. + config AD7303 tristate "Analog Devices AD7303 DAC driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 33e16f14902a..3c17246ee89b 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_AD5791) += ad5791.o obj-$(CONFIG_AD5686) += ad5686.o obj-$(CONFIG_AD5686_SPI) += ad5686-spi.o obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o +obj-$(CONFIG_AD7293) += ad7293.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o obj-$(CONFIG_CIO_DAC) += cio-dac.o diff --git a/drivers/iio/dac/ad7293.c b/drivers/iio/dac/ad7293.c new file mode 100644 index 000000000000..59a38ca4c3c7 --- /dev/null +++ b/drivers/iio/dac/ad7293.c @@ -0,0 +1,934 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * AD7293 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD7293_R1B BIT(16) +#define AD7293_R2B BIT(17) +#define AD7293_PAGE_ADDR_MSK GENMASK(15, 8) +#define AD7293_PAGE(x) FIELD_PREP(AD7293_PAGE_ADDR_MSK, x) + +/* AD7293 Register Map Common */ +#define AD7293_REG_NO_OP (AD7293_R1B | AD7293_PAGE(0x0) | 0x0) +#define AD7293_REG_PAGE_SELECT (AD7293_R1B | AD7293_PAGE(0x0) | 0x1) +#define AD7293_REG_CONV_CMD (AD7293_R2B | AD7293_PAGE(0x0) | 0x2) +#define AD7293_REG_RESULT (AD7293_R1B | AD7293_PAGE(0x0) | 0x3) +#define AD7293_REG_DAC_EN (AD7293_R1B | AD7293_PAGE(0x0) | 0x4) +#define AD7293_REG_DEVICE_ID (AD7293_R2B | AD7293_PAGE(0x0) | 0xC) +#define AD7293_REG_SOFT_RESET (AD7293_R2B | AD7293_PAGE(0x0) | 0xF) + +/* AD7293 Register Map Page 0x0 */ +#define AD7293_REG_VIN0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x10) +#define AD7293_REG_VIN1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x11) +#define AD7293_REG_VIN2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x12) +#define AD7293_REG_VIN3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x13) +#define AD7293_REG_TSENSE_INT (AD7293_R2B | AD7293_PAGE(0x0) | 0x20) +#define AD7293_REG_TSENSE_D0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x21) +#define AD7293_REG_TSENSE_D1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x22) +#define AD7293_REG_ISENSE_0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x28) +#define AD7293_REG_ISENSE_1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x29) +#define AD7293_REG_ISENSE_2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x2A) +#define AD7293_REG_ISENSE_3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x2B) +#define AD7293_REG_UNI_VOUT0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x30) +#define AD7293_REG_UNI_VOUT1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x31) +#define AD7293_REG_UNI_VOUT2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x32) +#define AD7293_REG_UNI_VOUT3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x33) +#define AD7293_REG_BI_VOUT0 (AD7293_R2B | AD7293_PAGE(0x0) | 0x34) +#define AD7293_REG_BI_VOUT1 (AD7293_R2B | AD7293_PAGE(0x0) | 0x35) +#define AD7293_REG_BI_VOUT2 (AD7293_R2B | AD7293_PAGE(0x0) | 0x36) +#define AD7293_REG_BI_VOUT3 (AD7293_R2B | AD7293_PAGE(0x0) | 0x37) + +/* AD7293 Register Map Page 0x2 */ +#define AD7293_REG_DIGITAL_OUT_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x11) +#define AD7293_REG_DIGITAL_INOUT_FUNC (AD7293_R2B | AD7293_PAGE(0x2) | 0x12) +#define AD7293_REG_DIGITAL_FUNC_POL (AD7293_R2B | AD7293_PAGE(0x2) | 0x13) +#define AD7293_REG_GENERAL (AD7293_R2B | AD7293_PAGE(0x2) | 0x14) +#define AD7293_REG_VINX_RANGE0 (AD7293_R2B | AD7293_PAGE(0x2) | 0x15) +#define AD7293_REG_VINX_RANGE1 (AD7293_R2B | AD7293_PAGE(0x2) | 0x16) +#define AD7293_REG_VINX_DIFF_SE (AD7293_R2B | AD7293_PAGE(0x2) | 0x17) +#define AD7293_REG_VINX_FILTER (AD7293_R2B | AD7293_PAGE(0x2) | 0x18) +#define AD7293_REG_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x19) +#define AD7293_REG_CONV_DELAY (AD7293_R2B | AD7293_PAGE(0x2) | 0x1A) +#define AD7293_REG_TSENSE_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x1B) +#define AD7293_REG_ISENSE_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x1C) +#define AD7293_REG_ISENSE_GAIN (AD7293_R2B | AD7293_PAGE(0x2) | 0x1D) +#define AD7293_REG_DAC_SNOOZE_O (AD7293_R2B | AD7293_PAGE(0x2) | 0x1F) +#define AD7293_REG_DAC_SNOOZE_1 (AD7293_R2B | AD7293_PAGE(0x2) | 0x20) +#define AD7293_REG_RSX_MON_BG_EN (AD7293_R2B | AD7293_PAGE(0x2) | 0x23) +#define AD7293_REG_INTEGR_CL (AD7293_R2B | AD7293_PAGE(0x2) | 0x28) +#define AD7293_REG_PA_ON_CTRL (AD7293_R2B | AD7293_PAGE(0x2) | 0x29) +#define AD7293_REG_RAMP_TIME_0 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2A) +#define AD7293_REG_RAMP_TIME_1 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2B) +#define AD7293_REG_RAMP_TIME_2 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2C) +#define AD7293_REG_RAMP_TIME_3 (AD7293_R2B | AD7293_PAGE(0x2) | 0x2D) +#define AD7293_REG_CL_FR_IT (AD7293_R2B | AD7293_PAGE(0x2) | 0x2E) +#define AD7293_REG_INTX_AVSS_AVDD (AD7293_R2B | AD7293_PAGE(0x2) | 0x2F) + +/* AD7293 Register Map Page 0x3 */ +#define AD7293_REG_VINX_SEQ (AD7293_R2B | AD7293_PAGE(0x3) | 0x10) +#define AD7293_REG_ISENSEX_TSENSEX_SEQ (AD7293_R2B | AD7293_PAGE(0x3) | 0x11) +#define AD7293_REG_RSX_MON_BI_VOUTX_SEQ (AD7293_R2B | AD7293_PAGE(0x3) | 0x12) + +/* AD7293 Register Map Page 0xE */ +#define AD7293_REG_VIN0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x10) +#define AD7293_REG_VIN1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x11) +#define AD7293_REG_VIN2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x12) +#define AD7293_REG_VIN3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x13) +#define AD7293_REG_TSENSE_INT_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x20) +#define AD7293_REG_TSENSE_D0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x21) +#define AD7293_REG_TSENSE_D1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x22) +#define AD7293_REG_ISENSE0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x28) +#define AD7293_REG_ISENSE1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x29) +#define AD7293_REG_ISENSE2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x2A) +#define AD7293_REG_ISENSE3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x2B) +#define AD7293_REG_UNI_VOUT0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x30) +#define AD7293_REG_UNI_VOUT1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x31) +#define AD7293_REG_UNI_VOUT2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x32) +#define AD7293_REG_UNI_VOUT3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x33) +#define AD7293_REG_BI_VOUT0_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x34) +#define AD7293_REG_BI_VOUT1_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x35) +#define AD7293_REG_BI_VOUT2_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x36) +#define AD7293_REG_BI_VOUT3_OFFSET (AD7293_R1B | AD7293_PAGE(0xE) | 0x37) + +/* AD7293 Miscellaneous Definitions */ +#define AD7293_READ BIT(7) +#define AD7293_TRANSF_LEN_MSK GENMASK(17, 16) + +#define AD7293_REG_ADDR_MSK GENMASK(7, 0) +#define AD7293_REG_VOUT_OFFSET_MSK GENMASK(5, 4) +#define AD7293_REG_DATA_RAW_MSK GENMASK(15, 4) +#define AD7293_REG_VINX_RANGE_GET_CH_MSK(x, ch) (((x) >> (ch)) & 0x1) +#define AD7293_REG_VINX_RANGE_SET_CH_MSK(x, ch) (((x) & 0x1) << (ch)) +#define AD7293_CHIP_ID 0x18 + +enum ad7293_ch_type { + AD7293_ADC_VINX, + AD7293_ADC_TSENSE, + AD7293_ADC_ISENSE, + AD7293_DAC, +}; + +enum ad7293_max_offset { + AD7293_TSENSE_MIN_OFFSET_CH = 4, + AD7293_ISENSE_MIN_OFFSET_CH = 7, + AD7293_VOUT_MIN_OFFSET_CH = 11, + AD7293_VOUT_MAX_OFFSET_CH = 18, +}; + +static const int dac_offset_table[] = {0, 1, 2}; + +static const int isense_gain_table[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + +static const int adc_range_table[] = {0, 1, 2, 3}; + +struct ad7293_state { + struct spi_device *spi; + /* Protect against concurrent accesses to the device, page selection and data content */ + struct mutex lock; + struct gpio_desc *gpio_reset; + struct regulator *reg_avdd; + struct regulator *reg_vdrive; + u8 page_select; + u8 data[3] ____cacheline_aligned; +}; + +static int ad7293_page_select(struct ad7293_state *st, unsigned int reg) +{ + int ret; + + if (st->page_select != FIELD_GET(AD7293_PAGE_ADDR_MSK, reg)) { + st->data[0] = FIELD_GET(AD7293_REG_ADDR_MSK, AD7293_REG_PAGE_SELECT); + st->data[1] = FIELD_GET(AD7293_PAGE_ADDR_MSK, reg); + + ret = spi_write(st->spi, &st->data[0], 2); + if (ret) + return ret; + + st->page_select = FIELD_GET(AD7293_PAGE_ADDR_MSK, reg); + } + + return 0; +} + +static int __ad7293_spi_read(struct ad7293_state *st, unsigned int reg, + u16 *val) +{ + int ret; + unsigned int length; + struct spi_transfer t = {0}; + + length = FIELD_GET(AD7293_TRANSF_LEN_MSK, reg); + + ret = ad7293_page_select(st, reg); + if (ret) + return ret; + + st->data[0] = AD7293_READ | FIELD_GET(AD7293_REG_ADDR_MSK, reg); + st->data[1] = 0x0; + st->data[2] = 0x0; + + t.tx_buf = &st->data[0]; + t.rx_buf = &st->data[0]; + t.len = length + 1; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + if (length == 1) + *val = st->data[1]; + else + *val = get_unaligned_be16(&st->data[1]); + + return 0; +} + +static int ad7293_spi_read(struct ad7293_state *st, unsigned int reg, + u16 *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __ad7293_spi_read(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __ad7293_spi_write(struct ad7293_state *st, unsigned int reg, + u16 val) +{ + int ret; + unsigned int length; + + length = FIELD_GET(AD7293_TRANSF_LEN_MSK, reg); + + ret = ad7293_page_select(st, reg); + if (ret) + return ret; + + st->data[0] = FIELD_GET(AD7293_REG_ADDR_MSK, reg); + + if (length == 1) + st->data[1] = val; + else + put_unaligned_be16(val, &st->data[1]); + + return spi_write(st->spi, &st->data[0], length + 1); +} + +static int ad7293_spi_write(struct ad7293_state *st, unsigned int reg, + u16 val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __ad7293_spi_write(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __ad7293_spi_update_bits(struct ad7293_state *st, unsigned int reg, + u16 mask, u16 val) +{ + int ret; + u16 data, temp; + + ret = __ad7293_spi_read(st, reg, &data); + if (ret) + return ret; + + temp = (data & ~mask) | (val & mask); + + return __ad7293_spi_write(st, reg, temp); +} + +static int ad7293_spi_update_bits(struct ad7293_state *st, unsigned int reg, + u16 mask, u16 val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __ad7293_spi_update_bits(st, reg, mask, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_adc_get_scale(struct ad7293_state *st, unsigned int ch, + u16 *range) +{ + int ret; + u16 data; + + mutex_lock(&st->lock); + + ret = __ad7293_spi_read(st, AD7293_REG_VINX_RANGE1, &data); + if (ret) + goto exit; + + *range = AD7293_REG_VINX_RANGE_GET_CH_MSK(data, ch); + + ret = __ad7293_spi_read(st, AD7293_REG_VINX_RANGE0, &data); + if (ret) + goto exit; + + *range |= AD7293_REG_VINX_RANGE_GET_CH_MSK(data, ch) << 1; + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_adc_set_scale(struct ad7293_state *st, unsigned int ch, + u16 range) +{ + int ret; + unsigned int ch_msk = BIT(ch); + + mutex_lock(&st->lock); + ret = __ad7293_spi_update_bits(st, AD7293_REG_VINX_RANGE1, ch_msk, + AD7293_REG_VINX_RANGE_SET_CH_MSK(range, ch)); + if (ret) + goto exit; + + ret = __ad7293_spi_update_bits(st, AD7293_REG_VINX_RANGE0, ch_msk, + AD7293_REG_VINX_RANGE_SET_CH_MSK((range >> 1), ch)); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_get_offset(struct ad7293_state *st, unsigned int ch, + u16 *offset) +{ + if (ch < AD7293_TSENSE_MIN_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_VIN0_OFFSET + ch, offset); + else if (ch < AD7293_ISENSE_MIN_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_TSENSE_INT_OFFSET + (ch - 4), offset); + else if (ch < AD7293_VOUT_MIN_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_ISENSE0_OFFSET + (ch - 7), offset); + else if (ch <= AD7293_VOUT_MAX_OFFSET_CH) + return ad7293_spi_read(st, AD7293_REG_UNI_VOUT0_OFFSET + (ch - 11), offset); + + return -EINVAL; +} + +static int ad7293_set_offset(struct ad7293_state *st, unsigned int ch, + u16 offset) +{ + if (ch < AD7293_TSENSE_MIN_OFFSET_CH) + return ad7293_spi_write(st, AD7293_REG_VIN0_OFFSET + ch, + offset); + else if (ch < AD7293_ISENSE_MIN_OFFSET_CH) + return ad7293_spi_write(st, + AD7293_REG_TSENSE_INT_OFFSET + + (ch - AD7293_TSENSE_MIN_OFFSET_CH), + offset); + else if (ch < AD7293_VOUT_MIN_OFFSET_CH) + return ad7293_spi_write(st, + AD7293_REG_ISENSE0_OFFSET + + (ch - AD7293_ISENSE_MIN_OFFSET_CH), + offset); + else if (ch <= AD7293_VOUT_MAX_OFFSET_CH) + return ad7293_spi_update_bits(st, + AD7293_REG_UNI_VOUT0_OFFSET + + (ch - AD7293_VOUT_MIN_OFFSET_CH), + AD7293_REG_VOUT_OFFSET_MSK, + FIELD_PREP(AD7293_REG_VOUT_OFFSET_MSK, offset)); + + return -EINVAL; +} + +static int ad7293_isense_set_scale(struct ad7293_state *st, unsigned int ch, + u16 gain) +{ + unsigned int ch_msk = (0xf << (4 * ch)); + + return ad7293_spi_update_bits(st, AD7293_REG_ISENSE_GAIN, ch_msk, + gain << (4 * ch)); +} + +static int ad7293_isense_get_scale(struct ad7293_state *st, unsigned int ch, + u16 *gain) +{ + int ret; + + ret = ad7293_spi_read(st, AD7293_REG_ISENSE_GAIN, gain); + if (ret) + return ret; + + *gain = (*gain >> (4 * ch)) & 0xf; + + return ret; +} + +static int ad7293_dac_write_raw(struct ad7293_state *st, unsigned int ch, + u16 raw) +{ + int ret; + + mutex_lock(&st->lock); + + ret = __ad7293_spi_update_bits(st, AD7293_REG_DAC_EN, BIT(ch), BIT(ch)); + if (ret) + goto exit; + + ret = __ad7293_spi_write(st, AD7293_REG_UNI_VOUT0 + ch, + FIELD_PREP(AD7293_REG_DATA_RAW_MSK, raw)); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_ch_read_raw(struct ad7293_state *st, enum ad7293_ch_type type, + unsigned int ch, u16 *raw) +{ + int ret; + unsigned int reg_wr, reg_rd, data_wr; + + switch (type) { + case AD7293_ADC_VINX: + reg_wr = AD7293_REG_VINX_SEQ; + reg_rd = AD7293_REG_VIN0 + ch; + data_wr = BIT(ch); + + break; + case AD7293_ADC_TSENSE: + reg_wr = AD7293_REG_ISENSEX_TSENSEX_SEQ; + reg_rd = AD7293_REG_TSENSE_INT + ch; + data_wr = BIT(ch); + + break; + case AD7293_ADC_ISENSE: + reg_wr = AD7293_REG_ISENSEX_TSENSEX_SEQ; + reg_rd = AD7293_REG_ISENSE_0 + ch; + data_wr = BIT(ch) << 8; + + break; + case AD7293_DAC: + reg_rd = AD7293_REG_UNI_VOUT0 + ch; + + break; + default: + return -EINVAL; + } + + mutex_lock(&st->lock); + + if (type != AD7293_DAC) { + if (type == AD7293_ADC_TSENSE) { + ret = __ad7293_spi_write(st, AD7293_REG_TSENSE_BG_EN, + BIT(ch)); + if (ret) + goto exit; + + usleep_range(9000, 9900); + } else if (type == AD7293_ADC_ISENSE) { + ret = __ad7293_spi_write(st, AD7293_REG_ISENSE_BG_EN, + BIT(ch)); + if (ret) + goto exit; + + usleep_range(2000, 7000); + } + + ret = __ad7293_spi_write(st, reg_wr, data_wr); + if (ret) + goto exit; + + ret = __ad7293_spi_write(st, AD7293_REG_CONV_CMD, 0x82); + if (ret) + goto exit; + } + + ret = __ad7293_spi_read(st, reg_rd, raw); + + *raw = FIELD_GET(AD7293_REG_DATA_RAW_MSK, *raw); + +exit: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad7293_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7293_state *st = iio_priv(indio_dev); + int ret; + u16 data; + + switch (info) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + ret = ad7293_ch_read_raw(st, AD7293_DAC, + chan->channel, &data); + else + ret = ad7293_ch_read_raw(st, AD7293_ADC_VINX, + chan->channel, &data); + + break; + case IIO_CURRENT: + ret = ad7293_ch_read_raw(st, AD7293_ADC_ISENSE, + chan->channel, &data); + + break; + case IIO_TEMP: + ret = ad7293_ch_read_raw(st, AD7293_ADC_TSENSE, + chan->channel, &data); + + break; + default: + return -EINVAL; + } + + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) { + ret = ad7293_get_offset(st, + chan->channel + AD7293_VOUT_MIN_OFFSET_CH, + &data); + + data = FIELD_GET(AD7293_REG_VOUT_OFFSET_MSK, data); + } else { + ret = ad7293_get_offset(st, chan->channel, &data); + } + + break; + case IIO_CURRENT: + ret = ad7293_get_offset(st, + chan->channel + AD7293_ISENSE_MIN_OFFSET_CH, + &data); + + break; + case IIO_TEMP: + ret = ad7293_get_offset(st, + chan->channel + AD7293_TSENSE_MIN_OFFSET_CH, + &data); + + break; + default: + return -EINVAL; + } + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + ret = ad7293_adc_get_scale(st, chan->channel, &data); + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_CURRENT: + ret = ad7293_isense_get_scale(st, chan->channel, &data); + if (ret) + return ret; + + *val = data; + + return IIO_VAL_INT; + case IIO_TEMP: + *val = 1; + *val2 = 8; + + return IIO_VAL_FRACTIONAL; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad7293_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7293_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_VOLTAGE: + if (!chan->output) + return -EINVAL; + + return ad7293_dac_write_raw(st, chan->channel, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + return ad7293_set_offset(st, + chan->channel + + AD7293_VOUT_MIN_OFFSET_CH, + val); + else + return ad7293_set_offset(st, chan->channel, val); + case IIO_CURRENT: + return ad7293_set_offset(st, + chan->channel + + AD7293_ISENSE_MIN_OFFSET_CH, + val); + case IIO_TEMP: + return ad7293_set_offset(st, + chan->channel + + AD7293_TSENSE_MIN_OFFSET_CH, + val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + return ad7293_adc_set_scale(st, chan->channel, val); + case IIO_CURRENT: + return ad7293_isense_set_scale(st, chan->channel, val); + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +static int ad7293_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct ad7293_state *st = iio_priv(indio_dev); + int ret; + + if (read_val) { + u16 temp; + ret = ad7293_spi_read(st, reg, &temp); + *read_val = temp; + } else { + ret = ad7293_spi_write(st, reg, (u16)write_val); + } + + return ret; +} + +static int ad7293_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + switch (info) { + case IIO_CHAN_INFO_OFFSET: + *vals = dac_offset_table; + *type = IIO_VAL_INT; + *length = ARRAY_SIZE(dac_offset_table); + + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT; + + switch (chan->type) { + case IIO_VOLTAGE: + *vals = adc_range_table; + *length = ARRAY_SIZE(adc_range_table); + return IIO_AVAIL_LIST; + case IIO_CURRENT: + *vals = isense_gain_table; + *length = ARRAY_SIZE(isense_gain_table); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } + default: + return -EINVAL; + } +} + +#define AD7293_CHAN_ADC(_channel) { \ + .type = IIO_VOLTAGE, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) \ +} + +#define AD7293_CHAN_DAC(_channel) { \ + .type = IIO_VOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_OFFSET) \ +} + +#define AD7293_CHAN_ISENSE(_channel) { \ + .type = IIO_CURRENT, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) \ +} + +#define AD7293_CHAN_TEMP(_channel) { \ + .type = IIO_TEMP, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \ +} + +static const struct iio_chan_spec ad7293_channels[] = { + AD7293_CHAN_ADC(0), + AD7293_CHAN_ADC(1), + AD7293_CHAN_ADC(2), + AD7293_CHAN_ADC(3), + AD7293_CHAN_ISENSE(0), + AD7293_CHAN_ISENSE(1), + AD7293_CHAN_ISENSE(2), + AD7293_CHAN_ISENSE(3), + AD7293_CHAN_TEMP(0), + AD7293_CHAN_TEMP(1), + AD7293_CHAN_TEMP(2), + AD7293_CHAN_DAC(0), + AD7293_CHAN_DAC(1), + AD7293_CHAN_DAC(2), + AD7293_CHAN_DAC(3), + AD7293_CHAN_DAC(4), + AD7293_CHAN_DAC(5), + AD7293_CHAN_DAC(6), + AD7293_CHAN_DAC(7) +}; + +static int ad7293_soft_reset(struct ad7293_state *st) +{ + int ret; + + ret = __ad7293_spi_write(st, AD7293_REG_SOFT_RESET, 0x7293); + if (ret) + return ret; + + return __ad7293_spi_write(st, AD7293_REG_SOFT_RESET, 0x0000); +} + +static int ad7293_reset(struct ad7293_state *st) +{ + if (st->gpio_reset) { + gpiod_set_value(st->gpio_reset, 0); + usleep_range(100, 1000); + gpiod_set_value(st->gpio_reset, 1); + usleep_range(100, 1000); + + return 0; + } + + /* Perform a software reset */ + return ad7293_soft_reset(st); +} + +static int ad7293_properties_parse(struct ad7293_state *st) +{ + struct spi_device *spi = st->spi; + + st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(st->gpio_reset)) + return dev_err_probe(&spi->dev, PTR_ERR(st->gpio_reset), + "failed to get the reset GPIO\n"); + + st->reg_avdd = devm_regulator_get(&spi->dev, "avdd"); + if (IS_ERR(st->reg_avdd)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg_avdd), + "failed to get the AVDD voltage\n"); + + st->reg_vdrive = devm_regulator_get(&spi->dev, "vdrive"); + if (IS_ERR(st->reg_vdrive)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg_vdrive), + "failed to get the VDRIVE voltage\n"); + + return 0; +} + +static void ad7293_reg_disable(void *data) +{ + regulator_disable(data); +} + +static int ad7293_init(struct ad7293_state *st) +{ + int ret; + u16 chip_id; + struct spi_device *spi = st->spi; + + ret = ad7293_properties_parse(st); + if (ret) + return ret; + + ret = ad7293_reset(st); + if (ret) + return ret; + + ret = regulator_enable(st->reg_avdd); + if (ret) { + dev_err(&spi->dev, + "Failed to enable specified AVDD Voltage!\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, + st->reg_avdd); + if (ret) + return ret; + + ret = regulator_enable(st->reg_vdrive); + if (ret) { + dev_err(&spi->dev, + "Failed to enable specified VDRIVE Voltage!\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ad7293_reg_disable, + st->reg_vdrive); + if (ret) + return ret; + + ret = regulator_get_voltage(st->reg_avdd); + if (ret < 0) { + dev_err(&spi->dev, "Failed to read avdd regulator: %d\n", ret); + return ret; + } + + if (ret > 5500000 || ret < 4500000) + return -EINVAL; + + ret = regulator_get_voltage(st->reg_vdrive); + if (ret < 0) { + dev_err(&spi->dev, + "Failed to read vdrive regulator: %d\n", ret); + return ret; + } + if (ret > 5500000 || ret < 1700000) + return -EINVAL; + + /* Check Chip ID */ + ret = __ad7293_spi_read(st, AD7293_REG_DEVICE_ID, &chip_id); + if (ret) + return ret; + + if (chip_id != AD7293_CHIP_ID) { + dev_err(&spi->dev, "Invalid Chip ID.\n"); + return -EINVAL; + } + + return 0; +} + +static const struct iio_info ad7293_info = { + .read_raw = ad7293_read_raw, + .write_raw = ad7293_write_raw, + .read_avail = &ad7293_read_avail, + .debugfs_reg_access = &ad7293_reg_access, +}; + +static int ad7293_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct ad7293_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + indio_dev->info = &ad7293_info; + indio_dev->name = "ad7293"; + indio_dev->channels = ad7293_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7293_channels); + + st->spi = spi; + st->page_select = 0; + + mutex_init(&st->lock); + + ret = ad7293_init(st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad7293_id[] = { + { "ad7293", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, ad7293_id); + +static const struct of_device_id ad7293_of_match[] = { + { .compatible = "adi,ad7293" }, + {} +}; +MODULE_DEVICE_TABLE(of, ad7293_of_match); + +static struct spi_driver ad7293_driver = { + .driver = { + .name = "ad7293", + .of_match_table = ad7293_of_match, + }, + .probe = ad7293_probe, + .id_table = ad7293_id, +}; +module_spi_driver(ad7293_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Sat, 4 Dec 2021 08:03:26 +0800 Subject: uio: uio_dmem_genirq: Catch the Exception The return value of dma_set_coherent_mask() is not always 0. To catch the exception in case that dma is not support the mask. Fixes: 0a0c3b5a24bd ("Add new uio device for dynamic memory allocation") Signed-off-by: Jiasheng Jiang Link: https://lore.kernel.org/r/20211204000326.1592687-1-jiasheng@iscas.ac.cn Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio_dmem_genirq.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/uio/uio_dmem_genirq.c b/drivers/uio/uio_dmem_genirq.c index 6b5cfa5b0673..1106f3376404 100644 --- a/drivers/uio/uio_dmem_genirq.c +++ b/drivers/uio/uio_dmem_genirq.c @@ -188,7 +188,11 @@ static int uio_dmem_genirq_probe(struct platform_device *pdev) return -ENOMEM; } - dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "DMA enable failed\n"); + return ret; + } priv->uioinfo = uioinfo; spin_lock_init(&priv->lock); -- cgit v1.2.3 From 42cdeb69d95e8b320adcb0ceff57d1dd9b0ba19f Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Mon, 6 Dec 2021 15:58:03 +0800 Subject: interconnect: icc-rpm: Use NOC_QOS_MODE_INVALID for qos_mode check Use NOC_QOS_MODE_INVALID for invalid qos_mode check to improve the readability. Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20211206075808.18124-2-shawn.guo@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index ef7999a08c8b..35888721a690 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -76,7 +76,7 @@ static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw) provider = src->provider; qp = to_qcom_provider(provider); - if (qn->qos.qos_mode != -1) + if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) mode = qn->qos.qos_mode; /* QoS Priority: The QoS Health parameters are getting considered @@ -137,7 +137,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) return 0; } - if (qn->qos.qos_mode != -1) + if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) mode = qn->qos.qos_mode; if (mode == NOC_QOS_MODE_FIXED) { -- cgit v1.2.3 From a7d9436a6c85fcb8843c910fd323dcd7f839bf63 Mon Sep 17 00:00:00 2001 From: Stephan Gerhold Date: Mon, 6 Dec 2021 12:45:42 +0100 Subject: interconnect: qcom: rpm: Prevent integer overflow in rate Using icc-rpm on ARM32 currently results in clk_set_rate() errors during boot, e.g. "bus clk_set_rate error: -22". This is very similar to commit 7381e27b1e56 ("interconnect: qcom: msm8974: Prevent integer overflow in rate") where the u64 is converted to a signed long during clock rate rounding, resulting in an overflow on 32-bit platforms. Let's fix it similarly by making sure that the rate does not exceed LONG_MAX. Such high clock rates will surely result in the maximum frequency of the bus anyway. Signed-off-by: Stephan Gerhold Link: https://lore.kernel.org/r/20211206114542.45325-1-stephan@gerhold.net Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 35888721a690..6fa56c930bd1 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -239,6 +239,7 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) rate = max(sum_bw, max_peak_bw); do_div(rate, qn->buswidth); + rate = min_t(u64, rate, LONG_MAX); if (qn->rate == rate) return 0; -- cgit v1.2.3 From 07cc0fa49bdbe051c69c1497b334b75a18d44a73 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 1 Dec 2021 13:14:54 +0530 Subject: scsi: ufs: dt-bindings: Add SM8450 compatible strings Document "qcom,sm8450-ufshc" compatible string. "qcom,sm8450-ufshc" is for UFS HC found in SM8450 SoC. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Acked-by: Martin K. Petersen Link: https://lore.kernel.org/r/20211201074456.3969849-2-vkoul@kernel.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt index d8fd4df81743..d0fee78e6203 100644 --- a/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt +++ b/Documentation/devicetree/bindings/ufs/ufshcd-pltfrm.txt @@ -16,6 +16,7 @@ Required properties: "qcom,sm8150-ufshc", "qcom,ufshc", "jedec,ufs-2.0" "qcom,sm8250-ufshc", "qcom,ufshc", "jedec,ufs-2.0" "qcom,sm8350-ufshc", "qcom,ufshc", "jedec,ufs-2.0" + "qcom,sm8450-ufshc", "qcom,ufshc", "jedec,ufs-2.0" - interrupts : - reg : -- cgit v1.2.3 From e04121ba1b085af40b3c9ca2516b3577245d078d Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 1 Dec 2021 13:14:55 +0530 Subject: dt-bindings: phy: qcom,qmp: Add SM8450 UFS phy compatible Document the UFS phy compatible for QMP UFS phy found in SM8450 SoC. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Acked-by: Martin K. Petersen Link: https://lore.kernel.org/r/20211201074456.3969849-3-vkoul@kernel.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 630ceaf915e2..c59bbca9a900 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -50,6 +50,7 @@ properties: - qcom,sm8350-qmp-ufs-phy - qcom,sm8350-qmp-usb3-phy - qcom,sm8350-qmp-usb3-uni-phy + - qcom,sm8450-qmp-ufs-phy - qcom,sdx55-qmp-pcie-phy - qcom,sdx55-qmp-usb3-uni-phy -- cgit v1.2.3 From 15aa1f668c5464fe201cf15d0d76f9429fdf163f Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 1 Dec 2021 13:14:56 +0530 Subject: phy: qcom-qmp: Add SM8450 UFS QMP Phy SM8450 UFS seems to use same sequence as SM8350, so reuse the sequence from SM8450. Add the new clock list for this phy and the new compatible Signed-off-by: Vinod Koul Co-developed-by: Dmitry Baryshkov Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Acked-by: Martin K. Petersen Link: https://lore.kernel.org/r/20211201074456.3969849-4-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 456a59d8c7d0..a959c97a699f 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -3091,6 +3091,10 @@ static const char * const qmp_v4_sm8250_usbphy_clk_l[] = { "aux", "ref_clk_src", "com_aux" }; +static const char * const sm8450_ufs_phy_clk_l[] = { + "qref", "ref", "ref_aux", +}; + static const char * const sdm845_ufs_phy_clk_l[] = { "ref", "ref_aux", }; @@ -4087,6 +4091,31 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { .pwrdn_delay_max = POWER_DOWN_DELAY_US_MAX, }; +static const struct qmp_phy_cfg sm8450_ufsphy_cfg = { + .type = PHY_TYPE_UFS, + .nlanes = 2, + + .serdes_tbl = sm8350_ufsphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8350_ufsphy_serdes_tbl), + .tx_tbl = sm8350_ufsphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_tx_tbl), + .rx_tbl = sm8350_ufsphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8350_ufsphy_rx_tbl), + .pcs_tbl = sm8350_ufsphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8350_ufsphy_pcs_tbl), + .clk_list = sm8450_ufs_phy_clk_l, + .num_clks = ARRAY_SIZE(sm8450_ufs_phy_clk_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8150_ufsphy_regs_layout, + + .start_ctrl = SERDES_START, + .pwrdn_ctrl = SW_PWRDN, + .phy_status = PHYSTATUS, + + .is_dual_lane_phy = true, +}; + static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -5745,6 +5774,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8350-qmp-usb3-uni-phy", .data = &sm8350_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-ufs-phy", + .data = &sm8450_ufsphy_cfg, }, { .compatible = "qcom,qcm2290-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, -- cgit v1.2.3 From 2925fc1c102943a2496e13ef78d68acd5fd0dc99 Mon Sep 17 00:00:00 2001 From: Mikko Perttunen Date: Wed, 8 Dec 2021 15:05:41 +0100 Subject: misc: sram: Add compatible string for Tegra234 SYSRAM We want to use the same behavior as on Tegra186 and Tegra194, so add this the compatible string for Tegra234 SYSRAM to the list. Signed-off-by: Mikko Perttunen Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20211208140541.520238-1-thierry.reding@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/sram.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/sram.c b/drivers/misc/sram.c index 4c26b19f5154..f0e7f02605eb 100644 --- a/drivers/misc/sram.c +++ b/drivers/misc/sram.c @@ -371,6 +371,7 @@ static const struct of_device_id sram_dt_ids[] = { { .compatible = "atmel,sama5d2-securam", .data = &atmel_securam_config }, { .compatible = "nvidia,tegra186-sysram", .data = &tegra_sysram_config }, { .compatible = "nvidia,tegra194-sysram", .data = &tegra_sysram_config }, + { .compatible = "nvidia,tegra234-sysram", .data = &tegra_sysram_config }, {} }; -- cgit v1.2.3 From 9a0a930fe2535a76ad70d3f43caeccf0d86a3009 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 7 Dec 2021 13:24:42 +0100 Subject: binder: fix pointer cast warning binder_uintptr_t is not the same as uintptr_t, so converting it into a pointer requires a second cast: drivers/android/binder.c: In function 'binder_translate_fd_array': drivers/android/binder.c:2511:28: error: cast to pointer from integer of different size [-Werror=int-to-pointer-cast] 2511 | sender_ufda_base = (void __user *)sender_uparent->buffer + fda->parent_offset; | ^ Fixes: 656e01f3ab54 ("binder: read pre-translated fds from sender buffer") Acked-by: Todd Kjos Acked-by: Randy Dunlap # build-tested Acked-by: Christian Brauner Signed-off-by: Arnd Bergmann Link: https://lore.kernel.org/r/20211207122448.1185769-1-arnd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 5497797ab258..182bb4221b06 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -2508,7 +2508,8 @@ static int binder_translate_fd_array(struct list_head *pf_head, */ fda_offset = (parent->buffer - (uintptr_t)t->buffer->user_data) + fda->parent_offset; - sender_ufda_base = (void __user *)sender_uparent->buffer + fda->parent_offset; + sender_ufda_base = (void __user *)(uintptr_t)sender_uparent->buffer + + fda->parent_offset; if (!IS_ALIGNED((unsigned long)fda_offset, sizeof(u32)) || !IS_ALIGNED((unsigned long)sender_ufda_base, sizeof(u32))) { -- cgit v1.2.3 From e87f13c33e126ab2c72f9acb5ae98fbb93ddfd32 Mon Sep 17 00:00:00 2001 From: Guo Zhengkui Date: Thu, 9 Dec 2021 11:21:14 +0800 Subject: phy: qcom: use struct_size instead of sizeof Use struct_size() to get the accurate size of `clk_hw_onecell_data` with a variable size array, instead of sizeof(data) to get the size of a pointer. Suggested-by: Bjorn Andersson Signed-off-by: Guo Zhengkui Fixes: f199223cb490 ("phy: qcom: Introduce new eDP PHY driver") Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211209032114.9416-1-guozhengkui@vivo.com Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-edp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/qualcomm/phy-qcom-edp.c b/drivers/phy/qualcomm/phy-qcom-edp.c index 17d5653b661d..a8ecd2e8442d 100644 --- a/drivers/phy/qualcomm/phy-qcom-edp.c +++ b/drivers/phy/qualcomm/phy-qcom-edp.c @@ -571,7 +571,7 @@ static int qcom_edp_clks_register(struct qcom_edp *edp, struct device_node *np) struct clk_init_data init = { }; int ret; - data = devm_kzalloc(edp->dev, sizeof(data), GFP_KERNEL); + data = devm_kzalloc(edp->dev, struct_size(data, hws, 2), GFP_KERNEL); if (!data) return -ENOMEM; -- cgit v1.2.3 From 98ceca2f29325d6114ea77be719a68c467c103d6 Mon Sep 17 00:00:00 2001 From: Yang Li Date: Fri, 3 Dec 2021 14:19:13 +0800 Subject: fpga: region: fix kernel-doc Fix function name in of-fpga-region.c kernel-doc comment to remove a warning found by running scripts/kernel-doc, which is caused by using 'make W=1'. drivers/fpga/of-fpga-region.c:451: warning: expecting prototype for fpga_region_init(). Prototype was for of_fpga_region_init() instead. Reported-by: Abaci Robot Signed-off-by: Yang Li Acked-by: Randy Dunlap Signed-off-by: Moritz Fischer --- drivers/fpga/of-fpga-region.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/fpga/of-fpga-region.c b/drivers/fpga/of-fpga-region.c index 9c662db1c508..50b83057c048 100644 --- a/drivers/fpga/of-fpga-region.c +++ b/drivers/fpga/of-fpga-region.c @@ -444,7 +444,7 @@ static struct platform_driver of_fpga_region_driver = { }; /** - * fpga_region_init - init function for fpga_region class + * of_fpga_region_init - init function for fpga_region class * Creates the fpga_region class and registers a reconfig notifier. */ static int __init of_fpga_region_init(void) -- cgit v1.2.3 From 2ff1f4d8df665316921bb752d28a5ea68c1a9811 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Thu, 2 Dec 2021 17:08:19 +0200 Subject: dt-bindings:iio:dac: add ad7293 doc Add device tree bindings for the AD7293 Power Amplifier. Signed-off-by: Antoniu Miclaus Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211202150819.24832-2-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/adi,ad7293.yaml | 61 ++++++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/adi,ad7293.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad7293.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad7293.yaml new file mode 100644 index 000000000000..5ee80bf6aa11 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad7293.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad7293.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: AD7293 12-Bit Power Amplifier Current Controller with ADC, + DACs, Temperature and Current Sensors + +maintainers: + - Antoniu Miclaus + +description: | + Power Amplifier drain current controller containing functionality + for general-purpose monitoring and control of current, voltage, + and temperature, integrated into a single chip solution with an + SPI-compatible interface. + + https://www.analog.com/en/products/ad7293.html + +properties: + compatible: + enum: + - adi,ad7293 + + avdd-supply: true + + vdrive-supply: true + + reset-gpios: + maxItems: 1 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 1000000 + +required: + - compatible + - reg + - avdd-supply + - vdrive-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + ad7293@0 { + compatible = "adi,ad7293"; + reg = <0>; + spi-max-frequency = <1000000>; + avdd-supply = <&avdd>; + vdrive-supply = <&vdrive>; + reset-gpios = <&gpio 10 0>; + }; + }; +... -- cgit v1.2.3 From d4b572f835a58bb394024fe3250441fabab9eee6 Mon Sep 17 00:00:00 2001 From: Cai Huoqing Date: Wed, 1 Dec 2021 16:31:00 +0800 Subject: MAINTAINERS: Update i.MX 8QXP ADC info Update my email address to use developer mail address, because the old address will be dropped soon. And change the status from 'Supported' to 'Maintained' for me to look after this code without any payment now. Signed-off-by: Cai Huoqing Link: https://lore.kernel.org/r/20211201083100.1587-1-caihuoqing@baidu.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7a2345ce8521..46c915468801 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13696,9 +13696,9 @@ F: Documentation/devicetree/bindings/display/imx/nxp,imx8mq-dcss.yaml F: drivers/gpu/drm/imx/dcss/ NXP i.MX 8QXP ADC DRIVER -M: Cai Huoqing +M: Cai Huoqing L: linux-iio@vger.kernel.org -S: Supported +S: Maintained F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml F: drivers/iio/adc/imx8qxp-adc.c -- cgit v1.2.3 From b62e2e1763cda3a6c494ed754317f19be1249297 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 5 Dec 2021 13:40:43 +0200 Subject: iio: add addac subdirectory For IIO devices that expose both ADC and DAC functionality. Signed-off-by: Cosmin Tanislav Link: https://lore.kernel.org/r/20211205114045.173612-2-cosmin.tanislav@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/addac/Kconfig | 8 ++++++++ drivers/iio/addac/Makefile | 6 ++++++ 4 files changed, 16 insertions(+) create mode 100644 drivers/iio/addac/Kconfig create mode 100644 drivers/iio/addac/Makefile diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 2334ad249b46..4fb4321a72cb 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -70,6 +70,7 @@ config IIO_TRIGGERED_EVENT source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" +source "drivers/iio/addac/Kconfig" source "drivers/iio/afe/Kconfig" source "drivers/iio/amplifiers/Kconfig" source "drivers/iio/cdc/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 65e39bd4f934..8d48c70fee4d 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -15,6 +15,7 @@ obj-$(CONFIG_IIO_TRIGGERED_EVENT) += industrialio-triggered-event.o obj-y += accel/ obj-y += adc/ +obj-y += addac/ obj-y += afe/ obj-y += amplifiers/ obj-y += buffer/ diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig new file mode 100644 index 000000000000..2e64d7755d5e --- /dev/null +++ b/drivers/iio/addac/Kconfig @@ -0,0 +1,8 @@ +# +# ADC DAC drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Analog to digital and digital to analog converters" + +endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile new file mode 100644 index 000000000000..b888b9ee12da --- /dev/null +++ b/drivers/iio/addac/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O ADDAC drivers +# + +# When adding new entries keep the list in alphabetical order -- cgit v1.2.3 From 3cf3cdea6fe3fdb7a1e4ac1372b80408e4f56b73 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 5 Dec 2021 13:40:44 +0200 Subject: dt-bindings: iio: add AD74413R The AD74412R and AD74413R are quad-channel, software configurable, input/output solutions for building and process control applications. They contain functionality for analog output, analog input, digital input, resistance temperature detector, and thermocouple measurements integrated into a single chip solution with an SPI interface. The devices feature a 16-bit ADC and four configurable 13-bit DACs to provide four configurable input/output channels and a suite of diagnostic functions. The AD74413R differentiates itself from the AD74412R by being HART-compatible. Signed-off-by: Cosmin Tanislav Reviewed-by: Rob Herring Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211205114045.173612-3-cosmin.tanislav@analog.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/addac/adi,ad74413r.yaml | 158 +++++++++++++++++++++ include/dt-bindings/iio/addac/adi,ad74413r.h | 21 +++ 2 files changed, 179 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml create mode 100644 include/dt-bindings/iio/addac/adi,ad74413r.h diff --git a/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml new file mode 100644 index 000000000000..baa65a521bad --- /dev/null +++ b/Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/addac/adi,ad74413r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD74412R/AD74413R device + +maintainers: + - Cosmin Tanislav + +description: | + The AD74412R and AD74413R are quad-channel software configurable input/output + solutions for building and process control applications. They contain + functionality for analog output, analog input, digital input, resistance + temperature detector, and thermocouple measurements integrated + into a single chip solution with an SPI interface. + The devices feature a 16-bit ADC and four configurable 13-bit DACs to provide + four configurable input/output channels and a suite of diagnostic functions. + The AD74413R differentiates itself from the AD74412R by being HART-compatible. + https://www.analog.com/en/products/ad74412r.html + https://www.analog.com/en/products/ad74413r.html + +properties: + compatible: + enum: + - adi,ad74412r + - adi,ad74413r + + reg: + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + spi-max-frequency: + maximum: 1000000 + + spi-cpol: true + + interrupts: + maxItems: 1 + + refin-supply: true + + shunt-resistor-micro-ohms: + description: + Shunt (sense) resistor value in micro-Ohms. + default: 100000000 + +required: + - compatible + - reg + - spi-max-frequency + - spi-cpol + - refin-supply + +additionalProperties: false + +patternProperties: + "^channel@[0-3]$": + type: object + description: Represents the external channels which are connected to the device. + + properties: + reg: + description: | + The channel number. It can have up to 4 channels numbered from 0 to 3. + minimum: 0 + maximum: 3 + + adi,ch-func: + $ref: /schemas/types.yaml#/definitions/uint32 + description: | + Channel function. + HART functions are not supported on AD74412R. + 0 - CH_FUNC_HIGH_IMPEDANCE + 1 - CH_FUNC_VOLTAGE_OUTPUT + 2 - CH_FUNC_CURRENT_OUTPUT + 3 - CH_FUNC_VOLTAGE_INPUT + 4 - CH_FUNC_CURRENT_INPUT_EXT_POWER + 5 - CH_FUNC_CURRENT_INPUT_LOOP_POWER + 6 - CH_FUNC_RESISTANCE_INPUT + 7 - CH_FUNC_DIGITAL_INPUT_LOGIC + 8 - CH_FUNC_DIGITAL_INPUT_LOOP_POWER + 9 - CH_FUNC_CURRENT_INPUT_EXT_POWER_HART + 10 - CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART + minimum: 0 + maximum: 10 + default: 0 + + adi,gpo-comparator: + type: boolean + description: | + Whether to configure GPO as a comparator or not. + When not configured as a comparator, the GPO will be treated as an + output-only GPIO. + + required: + - reg + +examples: + - | + #include + #include + #include + + spi { + #address-cells = <1>; + #size-cells = <0>; + + cs-gpios = <&gpio 17 GPIO_ACTIVE_LOW>; + status = "okay"; + + ad74413r@0 { + compatible = "adi,ad74413r"; + reg = <0>; + spi-max-frequency = <1000000>; + spi-cpol; + + #address-cells = <1>; + #size-cells = <0>; + + interrupt-parent = <&gpio>; + interrupts = <26 IRQ_TYPE_EDGE_FALLING>; + + refin-supply = <&ad74413r_refin>; + + channel@0 { + reg = <0>; + + adi,ch-func = ; + }; + + channel@1 { + reg = <1>; + + adi,ch-func = ; + }; + + channel@2 { + reg = <2>; + + adi,ch-func = ; + adi,gpo-comparator; + }; + + channel@3 { + reg = <3>; + + adi,ch-func = ; + }; + }; + }; +... diff --git a/include/dt-bindings/iio/addac/adi,ad74413r.h b/include/dt-bindings/iio/addac/adi,ad74413r.h new file mode 100644 index 000000000000..204f92bbd79f --- /dev/null +++ b/include/dt-bindings/iio/addac/adi,ad74413r.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _DT_BINDINGS_ADI_AD74413R_H +#define _DT_BINDINGS_ADI_AD74413R_H + +#define CH_FUNC_HIGH_IMPEDANCE 0x0 +#define CH_FUNC_VOLTAGE_OUTPUT 0x1 +#define CH_FUNC_CURRENT_OUTPUT 0x2 +#define CH_FUNC_VOLTAGE_INPUT 0x3 +#define CH_FUNC_CURRENT_INPUT_EXT_POWER 0x4 +#define CH_FUNC_CURRENT_INPUT_LOOP_POWER 0x5 +#define CH_FUNC_RESISTANCE_INPUT 0x6 +#define CH_FUNC_DIGITAL_INPUT_LOGIC 0x7 +#define CH_FUNC_DIGITAL_INPUT_LOOP_POWER 0x8 +#define CH_FUNC_CURRENT_INPUT_EXT_POWER_HART 0x9 +#define CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART 0xA + +#define CH_FUNC_MIN CH_FUNC_HIGH_IMPEDANCE +#define CH_FUNC_MAX CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART + +#endif /* _DT_BINDINGS_ADI_AD74413R_H */ -- cgit v1.2.3 From fea251b6a5dbdf8ba8af64abcd013d66ab6b05ee Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 5 Dec 2021 13:40:45 +0200 Subject: iio: addac: add AD74413R driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AD74412R and AD74413R are quad-channel, software configurable, input/output solutions for building and process control applications. They contain functionality for analog output, analog input, digital input, resistance temperature detector, and thermocouple measurements integrated into a single chip solution with an SPI interface. The devices feature a 16-bit ADC and four configurable 13-bit DACs to provide four configurable input/output channels and a suite of diagnostic functions. The AD74413R differentiates itself from the AD74412R by being HART-compatible. When configured with channel 0 as voltage output, channel 1 as current output, channel 2 as voltage input and channel 3 as current input, the following structure is created under the corresponding IIO device. . ├── in_current0_offset ├── in_current0_raw ├── in_current0_sampling_frequency ├── in_current0_sampling_frequency_available ├── in_current0_scale ├── in_voltage1_offset ├── in_voltage1_raw ├── in_voltage1_sampling_frequency ├── in_voltage1_sampling_frequency_available ├── in_voltage1_scale ├── in_voltage2_offset ├── in_voltage2_raw ├── in_voltage2_sampling_frequency ├── in_voltage2_sampling_frequency_available ├── in_voltage2_scale ├── in_current3_offset ├── in_current3_raw ├── in_current3_sampling_frequency ├── in_current3_sampling_frequency_available ├── in_current3_scale ├── out_voltage0_raw ├── out_voltage0_scale ├── out_current1_raw ├── out_current1_scale ├── name ├── buffer │   ├── data_available │   ├── enable │   ├── length │   └── watermark └── scan_elements    ├── in_current0_en    ├── in_current0_index    ├── in_current0_type    ├── in_voltage1_en    ├── in_voltage1_index    ├── in_voltage1_type    ├── in_voltage2_en    ├── in_voltage2_index    ├── in_voltage2_type    ├── in_current3_en    ├── in_current3_index    └── in_current3_type Signed-off-by: Cosmin Tanislav Reviewed-by: Linus Walleij Link: https://lore.kernel.org/r/20211205114045.173612-4-cosmin.tanislav@analog.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 9 + drivers/iio/addac/Kconfig | 12 + drivers/iio/addac/Makefile | 1 + drivers/iio/addac/ad74413r.c | 1475 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1497 insertions(+) create mode 100644 drivers/iio/addac/ad74413r.c diff --git a/MAINTAINERS b/MAINTAINERS index 46c915468801..57fb0f19ee08 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1069,6 +1069,15 @@ W: http://ez.analog.com/community/linux-device-drivers F: Documentation/devicetree/bindings/iio/adc/adi,ad7780.yaml F: drivers/iio/adc/ad7780.c +ANALOG DEVICES INC AD74413R DRIVER +M: Cosmin Tanislav +L: linux-iio@vger.kernel.org +S: Supported +W: http://ez.analog.com/community/linux-device-drivers +F: Documentation/devicetree/bindings/iio/addac/adi,ad74413r.yaml +F: drivers/iio/addac/ad74413r.c +F: include/dt-bindings/iio/addac/adi,ad74413r.h + ANALOG DEVICES INC AD9389B DRIVER M: Hans Verkuil L: linux-media@vger.kernel.org diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig index 2e64d7755d5e..138492362f20 100644 --- a/drivers/iio/addac/Kconfig +++ b/drivers/iio/addac/Kconfig @@ -5,4 +5,16 @@ menu "Analog to digital and digital to analog converters" +config AD74413R + tristate "Analog Devices AD74412R/AD74413R driver" + depends on GPIOLIB && SPI + select REGMAP_SPI + select CRC8 + help + Say yes here to build support for Analog Devices AD74412R/AD74413R + quad-channel software configurable input/output solution. + + To compile this driver as a module, choose M here: the + module will be called ad74413r. + endmenu diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile index b888b9ee12da..cfd4bbe64ad3 100644 --- a/drivers/iio/addac/Makefile +++ b/drivers/iio/addac/Makefile @@ -4,3 +4,4 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD74413R) += ad74413r.o diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c new file mode 100644 index 000000000000..cbd9aa9b399a --- /dev/null +++ b/drivers/iio/addac/ad74413r.c @@ -0,0 +1,1475 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2021 Analog Devices, Inc. + * Author: Cosmin Tanislav + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define AD74413R_CRC_POLYNOMIAL 0x7 +DECLARE_CRC8_TABLE(ad74413r_crc8_table); + +#define AD74413R_CHANNEL_MAX 4 + +#define AD74413R_FRAME_SIZE 4 + +struct ad74413r_chip_info { + const char *name; + bool hart_support; +}; + +struct ad74413r_channel_config { + u32 func; + bool gpo_comparator; + bool initialized; +}; + +struct ad74413r_channels { + struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct ad74413r_state { + struct ad74413r_channel_config channel_configs[AD74413R_CHANNEL_MAX]; + unsigned int gpo_gpio_offsets[AD74413R_CHANNEL_MAX]; + unsigned int comp_gpio_offsets[AD74413R_CHANNEL_MAX]; + struct gpio_chip gpo_gpiochip; + struct gpio_chip comp_gpiochip; + struct completion adc_data_completion; + unsigned int num_gpo_gpios; + unsigned int num_comparator_gpios; + u32 sense_resistor_ohms; + + /* + * Synchronize consecutive operations when doing a one-shot + * conversion and when updating the ADC samples SPI message. + */ + struct mutex lock; + + const struct ad74413r_chip_info *chip_info; + struct spi_device *spi; + struct regulator *refin_reg; + struct regmap *regmap; + struct device *dev; + struct iio_trigger *trig; + + size_t adc_active_channels; + struct spi_message adc_samples_msg; + struct spi_transfer adc_samples_xfer[AD74413R_CHANNEL_MAX + 1]; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + u8 rx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; + s64 timestamp; + } adc_samples_buf ____cacheline_aligned; + + u8 adc_samples_tx_buf[AD74413R_FRAME_SIZE * AD74413R_CHANNEL_MAX]; + u8 reg_tx_buf[AD74413R_FRAME_SIZE]; + u8 reg_rx_buf[AD74413R_FRAME_SIZE]; +}; + +#define AD74413R_REG_NOP 0x00 + +#define AD74413R_REG_CH_FUNC_SETUP_X(x) (0x01 + (x)) +#define AD74413R_CH_FUNC_SETUP_MASK GENMASK(3, 0) + +#define AD74413R_REG_ADC_CONFIG_X(x) (0x05 + (x)) +#define AD74413R_ADC_CONFIG_RANGE_MASK GENMASK(7, 5) +#define AD74413R_ADC_CONFIG_REJECTION_MASK GENMASK(4, 3) +#define AD74413R_ADC_RANGE_10V 0b000 +#define AD74413R_ADC_RANGE_2P5V_EXT_POW 0b001 +#define AD74413R_ADC_RANGE_2P5V_INT_POW 0b010 +#define AD74413R_ADC_RANGE_5V_BI_DIR 0b011 +#define AD74413R_ADC_REJECTION_50_60 0b00 +#define AD74413R_ADC_REJECTION_NONE 0b01 +#define AD74413R_ADC_REJECTION_50_60_HART 0b10 +#define AD74413R_ADC_REJECTION_HART 0b11 + +#define AD74413R_REG_DIN_CONFIG_X(x) (0x09 + (x)) +#define AD74413R_DIN_DEBOUNCE_MASK GENMASK(4, 0) +#define AD74413R_DIN_DEBOUNCE_LEN BIT(5) + +#define AD74413R_REG_DAC_CODE_X(x) (0x16 + (x)) +#define AD74413R_DAC_CODE_MAX GENMASK(12, 0) +#define AD74413R_DAC_VOLTAGE_MAX 11000 + +#define AD74413R_REG_GPO_PAR_DATA 0x0d +#define AD74413R_REG_GPO_CONFIG_X(x) (0x0e + (x)) +#define AD74413R_GPO_CONFIG_DATA_MASK BIT(3) +#define AD74413R_GPO_CONFIG_SELECT_MASK GENMASK(2, 0) +#define AD74413R_GPO_CONFIG_100K_PULL_DOWN 0b000 +#define AD74413R_GPO_CONFIG_LOGIC 0b001 +#define AD74413R_GPO_CONFIG_LOGIC_PARALLEL 0b010 +#define AD74413R_GPO_CONFIG_COMPARATOR 0b011 +#define AD74413R_GPO_CONFIG_HIGH_IMPEDANCE 0b100 + +#define AD74413R_REG_ADC_CONV_CTRL 0x23 +#define AD74413R_CONV_SEQ_MASK GENMASK(9, 8) +#define AD74413R_CONV_SEQ_ON 0b00 +#define AD74413R_CONV_SEQ_SINGLE 0b01 +#define AD74413R_CONV_SEQ_CONTINUOUS 0b10 +#define AD74413R_CONV_SEQ_OFF 0b11 +#define AD74413R_CH_EN_MASK(x) BIT(x) + +#define AD74413R_REG_DIN_COMP_OUT 0x25 +#define AD74413R_DIN_COMP_OUT_SHIFT_X(x) x + +#define AD74413R_REG_ADC_RESULT_X(x) (0x26 + (x)) +#define AD74413R_ADC_RESULT_MAX GENMASK(15, 0) + +#define AD74413R_REG_READ_SELECT 0x41 + +#define AD74413R_REG_CMD_KEY 0x44 +#define AD74413R_CMD_KEY_LDAC 0x953a +#define AD74413R_CMD_KEY_RESET1 0x15fa +#define AD74413R_CMD_KEY_RESET2 0xaf51 + +static const int ad74413r_adc_sampling_rates[] = { + 20, 4800, +}; + +static const int ad74413r_adc_sampling_rates_hart[] = { + 10, 20, 1200, 4800, +}; + +static int ad74413r_crc(u8 *buf) +{ + return crc8(ad74413r_crc8_table, buf, 3, 0); +} + +static void ad74413r_format_reg_write(u8 reg, u16 val, u8 *buf) +{ + buf[0] = reg; + put_unaligned_be16(val, &buf[1]); + buf[3] = ad74413r_crc(buf); +} + +static int ad74413r_reg_write(void *context, unsigned int reg, unsigned int val) +{ + struct ad74413r_state *st = context; + + ad74413r_format_reg_write(reg, val, st->reg_tx_buf); + + return spi_write(st->spi, st->reg_tx_buf, AD74413R_FRAME_SIZE); +} + +static int ad74413r_crc_check(struct ad74413r_state *st, u8 *buf) +{ + u8 expected_crc = ad74413r_crc(buf); + + if (buf[3] != expected_crc) { + dev_err(st->dev, "Bad CRC %02x for %02x%02x%02x\n", + buf[3], buf[0], buf[1], buf[2]); + return -EINVAL; + } + + return 0; +} + +static int ad74413r_reg_read(void *context, unsigned int reg, unsigned int *val) +{ + struct ad74413r_state *st = context; + struct spi_transfer reg_read_xfer[] = { + { + .tx_buf = st->reg_tx_buf, + .len = AD74413R_FRAME_SIZE, + .cs_change = 1, + }, + { + .rx_buf = st->reg_rx_buf, + .len = AD74413R_FRAME_SIZE, + }, + }; + int ret; + + ad74413r_format_reg_write(AD74413R_REG_READ_SELECT, reg, + st->reg_tx_buf); + + ret = spi_sync_transfer(st->spi, reg_read_xfer, + ARRAY_SIZE(reg_read_xfer)); + if (ret) + return ret; + + ret = ad74413r_crc_check(st, st->reg_rx_buf); + if (ret) + return ret; + + *val = get_unaligned_be16(&st->reg_rx_buf[1]); + + return 0; +} + +static const struct regmap_config ad74413r_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .reg_read = ad74413r_reg_read, + .reg_write = ad74413r_reg_write, +}; + +static int ad74413r_set_gpo_config(struct ad74413r_state *st, + unsigned int offset, u8 mode) +{ + return regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(offset), + AD74413R_GPO_CONFIG_SELECT_MASK, mode); +} + +static const unsigned int ad74413r_debounce_map[AD74413R_DIN_DEBOUNCE_LEN] = { + 0, 13, 18, 24, 32, 42, 56, 75, + 100, 130, 180, 240, 320, 420, 560, 750, + 1000, 1300, 1800, 2400, 3200, 4200, 5600, 7500, + 10000, 13000, 18000, 24000, 32000, 42000, 56000, 75000, +}; + +static int ad74413r_set_comp_debounce(struct ad74413r_state *st, + unsigned int offset, + unsigned int debounce) +{ + unsigned int val = AD74413R_DIN_DEBOUNCE_LEN - 1; + unsigned int i; + + for (i = 0; i < AD74413R_DIN_DEBOUNCE_LEN; i++) + if (debounce <= ad74413r_debounce_map[i]) { + val = i; + break; + } + + return regmap_update_bits(st->regmap, + AD74413R_REG_DIN_CONFIG_X(offset), + AD74413R_DIN_DEBOUNCE_MASK, + val); +} + +static void ad74413r_gpio_set(struct gpio_chip *chip, + unsigned int offset, int val) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->gpo_gpio_offsets[offset]; + int ret; + + ret = ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_LOGIC); + if (ret) + return; + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_CONFIG_X(real_offset), + AD74413R_GPO_CONFIG_DATA_MASK, + val ? AD74413R_GPO_CONFIG_DATA_MASK : 0); +} + +static void ad74413r_gpio_set_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned long real_mask = 0; + unsigned long real_bits = 0; + unsigned int offset = 0; + int ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + unsigned int real_offset = st->gpo_gpio_offsets[offset]; + + ret = ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_LOGIC_PARALLEL); + if (ret) + return; + + real_mask |= BIT(real_offset); + if (*bits & offset) + real_bits |= BIT(real_offset); + } + + regmap_update_bits(st->regmap, AD74413R_REG_GPO_PAR_DATA, + real_mask, real_bits); +} + +static int ad74413r_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->comp_gpio_offsets[offset]; + unsigned int status; + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &status); + if (ret) + return ret; + + status &= AD74413R_DIN_COMP_OUT_SHIFT_X(real_offset); + + return status ? 1 : 0; +} + +static int ad74413r_gpio_get_multiple(struct gpio_chip *chip, + unsigned long *mask, + unsigned long *bits) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int offset = 0; + unsigned int val; + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_DIN_COMP_OUT, &val); + if (ret) + return ret; + + for_each_set_bit_from(offset, mask, AD74413R_CHANNEL_MAX) { + unsigned int real_offset = st->comp_gpio_offsets[offset]; + + if (val & BIT(real_offset)) + *bits |= offset; + } + + return ret; +} + +static int ad74413r_gpio_get_gpo_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_OUT; +} + +static int ad74413r_gpio_get_comp_direction(struct gpio_chip *chip, + unsigned int offset) +{ + return GPIO_LINE_DIRECTION_IN; +} + +static int ad74413r_gpio_set_gpo_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->gpo_gpio_offsets[offset]; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_BIAS_PULL_DOWN: + return ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_100K_PULL_DOWN); + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + return ad74413r_set_gpo_config(st, real_offset, + AD74413R_GPO_CONFIG_HIGH_IMPEDANCE); + default: + return -ENOTSUPP; + } +} + +static int ad74413r_gpio_set_comp_config(struct gpio_chip *chip, + unsigned int offset, + unsigned long config) +{ + struct ad74413r_state *st = gpiochip_get_data(chip); + unsigned int real_offset = st->comp_gpio_offsets[offset]; + + switch (pinconf_to_config_param(config)) { + case PIN_CONFIG_INPUT_DEBOUNCE: + return ad74413r_set_comp_debounce(st, real_offset, + pinconf_to_config_argument(config)); + default: + return -ENOTSUPP; + } +} + +static int ad74413r_reset(struct ad74413r_state *st) +{ + int ret; + + ret = regmap_write(st->regmap, AD74413R_REG_CMD_KEY, + AD74413R_CMD_KEY_RESET1); + if (ret) + return ret; + + return regmap_write(st->regmap, AD74413R_REG_CMD_KEY, + AD74413R_CMD_KEY_RESET2); +} + +static int ad74413r_set_channel_dac_code(struct ad74413r_state *st, + unsigned int channel, int dac_code) +{ + struct reg_sequence reg_seq[2] = { + { AD74413R_REG_DAC_CODE_X(channel), dac_code }, + { AD74413R_REG_CMD_KEY, AD74413R_CMD_KEY_LDAC }, + }; + + return regmap_multi_reg_write(st->regmap, reg_seq, 2); +} + +static int ad74413r_set_channel_function(struct ad74413r_state *st, + unsigned int channel, u8 func) +{ + return regmap_update_bits(st->regmap, + AD74413R_REG_CH_FUNC_SETUP_X(channel), + AD74413R_CH_FUNC_SETUP_MASK, func); +} + +static int ad74413r_set_adc_conv_seq(struct ad74413r_state *st, + unsigned int status) +{ + int ret; + + /* + * These bits do not clear when a conversion completes. + * To enable a subsequent conversion, repeat the write. + */ + ret = regmap_write_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CONV_SEQ_MASK, + FIELD_PREP(AD74413R_CONV_SEQ_MASK, status)); + if (ret) + return ret; + + /* + * Wait 100us before starting conversions. + */ + usleep_range(100, 120); + + return 0; +} + +static int ad74413r_set_adc_channel_enable(struct ad74413r_state *st, + unsigned int channel, + bool status) +{ + return regmap_update_bits(st->regmap, AD74413R_REG_ADC_CONV_CTRL, + AD74413R_CH_EN_MASK(channel), + status ? AD74413R_CH_EN_MASK(channel) : 0); +} + +static int ad74413r_get_adc_range(struct ad74413r_state *st, + unsigned int channel, + unsigned int *val) +{ + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val = FIELD_GET(AD74413R_ADC_CONFIG_RANGE_MASK, *val); + + return 0; +} + +static int ad74413r_get_adc_rejection(struct ad74413r_state *st, + unsigned int channel, + unsigned int *val) +{ + int ret; + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_CONFIG_X(channel), val); + if (ret) + return ret; + + *val = FIELD_GET(AD74413R_ADC_CONFIG_REJECTION_MASK, *val); + + return 0; +} + +static int ad74413r_set_adc_rejection(struct ad74413r_state *st, + unsigned int channel, + unsigned int val) +{ + return regmap_update_bits(st->regmap, + AD74413R_REG_ADC_CONFIG_X(channel), + AD74413R_ADC_CONFIG_REJECTION_MASK, + FIELD_PREP(AD74413R_ADC_CONFIG_REJECTION_MASK, + val)); +} + +static int ad74413r_rejection_to_rate(struct ad74413r_state *st, + unsigned int rej, int *val) +{ + switch (rej) { + case AD74413R_ADC_REJECTION_50_60: + *val = 20; + return 0; + case AD74413R_ADC_REJECTION_NONE: + *val = 4800; + return 0; + case AD74413R_ADC_REJECTION_50_60_HART: + *val = 10; + return 0; + case AD74413R_ADC_REJECTION_HART: + *val = 1200; + return 0; + default: + dev_err(st->dev, "ADC rejection invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_rate_to_rejection(struct ad74413r_state *st, + int rate, unsigned int *val) +{ + switch (rate) { + case 20: + *val = AD74413R_ADC_REJECTION_50_60; + return 0; + case 4800: + *val = AD74413R_ADC_REJECTION_NONE; + return 0; + case 10: + *val = AD74413R_ADC_REJECTION_50_60_HART; + return 0; + case 1200: + *val = AD74413R_ADC_REJECTION_HART; + return 0; + default: + dev_err(st->dev, "ADC rate invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_range(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + *val = 10000; + return 0; + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val = 2500; + return 0; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = 5000; + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_offset(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val = 0; + return 0; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = -2500; + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_range_to_voltage_offset_raw(struct ad74413r_state *st, + unsigned int range, int *val) +{ + switch (range) { + case AD74413R_ADC_RANGE_10V: + case AD74413R_ADC_RANGE_2P5V_EXT_POW: + *val = 0; + return 0; + case AD74413R_ADC_RANGE_2P5V_INT_POW: + *val = -((int)AD74413R_ADC_RESULT_MAX); + return 0; + case AD74413R_ADC_RANGE_5V_BI_DIR: + *val = -((int)AD74413R_ADC_RESULT_MAX / 2); + return 0; + default: + dev_err(st->dev, "ADC range invalid\n"); + return -EINVAL; + } +} + +static int ad74413r_get_output_voltage_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val = AD74413R_DAC_VOLTAGE_MAX; + *val2 = AD74413R_DAC_CODE_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_output_current_scale(struct ad74413r_state *st, + int *val, int *val2) +{ + *val = regulator_get_voltage(st->refin_reg); + *val2 = st->sense_resistor_ohms * AD74413R_DAC_CODE_MAX * 1000; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_scale(struct ad74413r_state *st, + unsigned int channel, + int *val, int *val2) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_range(st, range, val); + if (ret) + return ret; + + *val2 = AD74413R_ADC_RESULT_MAX; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413r_get_input_voltage_offset(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_offset_raw(st, range, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad74413r_get_input_current_scale(struct ad74413r_state *st, + unsigned int channel, int *val, + int *val2) +{ + unsigned int range; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_range(st, range, val); + if (ret) + return ret; + + *val2 = AD74413R_ADC_RESULT_MAX * st->sense_resistor_ohms; + + return IIO_VAL_FRACTIONAL; +} + +static int ad74413_get_input_current_offset(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int range; + int voltage_range; + int voltage_offset; + int ret; + + ret = ad74413r_get_adc_range(st, channel, &range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_range(st, range, &voltage_range); + if (ret) + return ret; + + ret = ad74413r_range_to_voltage_offset(st, range, &voltage_offset); + if (ret) + return ret; + + *val = voltage_offset * AD74413R_ADC_RESULT_MAX / voltage_range; + + return IIO_VAL_INT; +} + +static int ad74413r_get_adc_rate(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int rejection; + int ret; + + ret = ad74413r_get_adc_rejection(st, channel, &rejection); + if (ret) + return ret; + + ret = ad74413r_rejection_to_rate(st, rejection, val); + if (ret) + return ret; + + return IIO_VAL_INT; +} + +static int ad74413r_set_adc_rate(struct ad74413r_state *st, + unsigned int channel, int val) +{ + unsigned int rejection; + int ret; + + ret = ad74413r_rate_to_rejection(st, val, &rejection); + if (ret) + return ret; + + return ad74413r_set_adc_rejection(st, channel, rejection); +} + +static irqreturn_t ad74413r_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad74413r_state *st = iio_priv(indio_dev); + u8 *rx_buf = st->adc_samples_buf.rx_buf; + unsigned int i; + int ret; + + ret = spi_sync(st->spi, &st->adc_samples_msg); + if (ret) + goto out; + + for (i = 0; i < st->adc_active_channels; i++) + ad74413r_crc_check(st, &rx_buf[i * AD74413R_FRAME_SIZE]); + + iio_push_to_buffers_with_timestamp(indio_dev, &st->adc_samples_buf, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ad74413r_adc_data_interrupt(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ad74413r_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->adc_data_completion); + + return IRQ_HANDLED; +} + +static int _ad74413r_get_single_adc_result(struct ad74413r_state *st, + unsigned int channel, int *val) +{ + unsigned int uval; + int ret; + + reinit_completion(&st->adc_data_completion); + + ret = ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + return ret; + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_SINGLE); + if (ret) + return ret; + + ret = wait_for_completion_timeout(&st->adc_data_completion, + msecs_to_jiffies(1000)); + if (!ret) { + ret = -ETIMEDOUT; + return ret; + } + + ret = regmap_read(st->regmap, AD74413R_REG_ADC_RESULT_X(channel), + &uval); + if (ret) + return ret; + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret = ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + return ret; + + *val = uval; + + return IIO_VAL_INT; +} + +static int ad74413r_get_single_adc_result(struct iio_dev *indio_dev, + unsigned int channel, int *val) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + mutex_lock(&st->lock); + ret = _ad74413r_get_single_adc_result(st, channel, val); + mutex_unlock(&st->lock); + + iio_device_release_direct_mode(indio_dev); + + return ret; +} + +static void ad74413r_adc_to_resistance_result(int adc_result, int *val) +{ + if (adc_result == AD74413R_ADC_RESULT_MAX) + adc_result = AD74413R_ADC_RESULT_MAX - 1; + + *val = DIV_ROUND_CLOSEST(adc_result * 2100, + AD74413R_ADC_RESULT_MAX - adc_result); +} + +static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct spi_transfer *xfer = st->adc_samples_xfer; + u8 *rx_buf = &st->adc_samples_buf.rx_buf[-1 * AD74413R_FRAME_SIZE]; + u8 *tx_buf = st->adc_samples_tx_buf; + unsigned int channel; + int ret; + + mutex_lock(&st->lock); + + spi_message_init(&st->adc_samples_msg); + st->adc_active_channels = 0; + + for_each_clear_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { + ret = ad74413r_set_adc_channel_enable(st, channel, false); + if (ret) + goto out; + } + + if (*active_scan_mask == 0) + goto out; + + /* + * The read select register is used to select which register's value + * will be sent by the slave on the next SPI frame. + * + * Create an SPI message that, on each step, writes to the read select + * register to select the ADC result of the next enabled channel, and + * reads the ADC result of the previous enabled channel. + * + * Example: + * W: [WCH1] [WCH2] [WCH2] [WCH3] [ ] + * R: [ ] [RCH1] [RCH2] [RCH3] [RCH4] + */ + + for_each_set_bit(channel, active_scan_mask, AD74413R_CHANNEL_MAX) { + ret = ad74413r_set_adc_channel_enable(st, channel, true); + if (ret) + goto out; + + st->adc_active_channels++; + + if (xfer == st->adc_samples_xfer) + xfer->rx_buf = NULL; + else + xfer->rx_buf = rx_buf; + + xfer->tx_buf = tx_buf; + xfer->len = AD74413R_FRAME_SIZE; + xfer->cs_change = 1; + + ad74413r_format_reg_write(AD74413R_REG_READ_SELECT, + AD74413R_REG_ADC_RESULT_X(channel), + tx_buf); + + spi_message_add_tail(xfer, &st->adc_samples_msg); + + xfer++; + tx_buf += AD74413R_FRAME_SIZE; + rx_buf += AD74413R_FRAME_SIZE; + } + + xfer->rx_buf = rx_buf; + xfer->tx_buf = NULL; + xfer->len = AD74413R_FRAME_SIZE; + xfer->cs_change = 0; + + spi_message_add_tail(xfer, &st->adc_samples_msg); + +out: + mutex_unlock(&st->lock); + + return ret; +} + +static int ad74413r_buffer_postenable(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + return ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_CONTINUOUS); +} + +static int ad74413r_buffer_predisable(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + return ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); +} + +static int ad74413r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->output) + return ad74413r_get_output_voltage_scale(st, + val, val2); + else + return ad74413r_get_input_voltage_scale(st, + chan->channel, val, val2); + case IIO_CURRENT: + if (chan->output) + return ad74413r_get_output_current_scale(st, + val, val2); + else + return ad74413r_get_input_current_scale(st, + chan->channel, val, val2); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_VOLTAGE: + return ad74413r_get_input_voltage_offset(st, + chan->channel, val); + case IIO_CURRENT: + return ad74413_get_input_current_offset(st, + chan->channel, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + if (chan->output) + return -EINVAL; + + return ad74413r_get_single_adc_result(indio_dev, chan->channel, + val); + case IIO_CHAN_INFO_PROCESSED: { + int ret; + + ret = ad74413r_get_single_adc_result(indio_dev, chan->channel, + val); + if (ret) + return ret; + + ad74413r_adc_to_resistance_result(*val, val); + + return ret; + } + case IIO_CHAN_INFO_SAMP_FREQ: + return ad74413r_get_adc_rate(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad74413r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + if (!chan->output) + return -EINVAL; + + if (val < 0 || val > AD74413R_DAC_CODE_MAX) { + dev_err(st->dev, "Invalid DAC code\n"); + return -EINVAL; + } + + return ad74413r_set_channel_dac_code(st, chan->channel, val); + case IIO_CHAN_INFO_SAMP_FREQ: + return ad74413r_set_adc_rate(st, chan->channel, val); + default: + return -EINVAL; + } +} + +static int ad74413r_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long info) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (st->chip_info->hart_support) { + *vals = ad74413r_adc_sampling_rates_hart; + *length = ARRAY_SIZE(ad74413r_adc_sampling_rates_hart); + } else { + *vals = ad74413r_adc_sampling_rates; + *length = ARRAY_SIZE(ad74413r_adc_sampling_rates); + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_buffer_setup_ops ad74413r_buffer_ops = { + .postenable = &ad74413r_buffer_postenable, + .predisable = &ad74413r_buffer_predisable, +}; + +static const struct iio_trigger_ops ad74413r_trigger_ops = { + .validate_device = iio_trigger_validate_own_device, +}; + +static const struct iio_info ad74413r_info = { + .read_raw = &ad74413r_read_raw, + .write_raw = &ad74413r_write_raw, + .read_avail = &ad74413r_read_avail, + .update_scan_mode = &ad74413r_update_scan_mode, +}; + +#define AD74413R_DAC_CHANNEL(_type, extra_mask_separate) \ + { \ + .type = (_type), \ + .indexed = 1, \ + .output = 1, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | (extra_mask_separate), \ + } + +#define AD74413R_ADC_CHANNEL(_type, extra_mask_separate) \ + { \ + .type = (_type), \ + .indexed = 1, \ + .output = 0, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SAMP_FREQ) \ + | (extra_mask_separate), \ + .info_mask_separate_available = \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 32, \ + .shift = 8, \ + .endianness = IIO_BE, \ + }, \ + } + +#define AD74413R_ADC_VOLTAGE_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET)) + +#define AD74413R_ADC_CURRENT_CHANNEL \ + AD74413R_ADC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE) \ + | BIT(IIO_CHAN_INFO_OFFSET)) + +static struct iio_chan_spec ad74413r_voltage_output_channels[] = { + AD74413R_DAC_CHANNEL(IIO_VOLTAGE, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_output_channels[] = { + AD74413R_DAC_CHANNEL(IIO_CURRENT, BIT(IIO_CHAN_INFO_SCALE)), + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_voltage_input_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_current_input_channels[] = { + AD74413R_ADC_CURRENT_CHANNEL, +}; + +static struct iio_chan_spec ad74413r_resistance_input_channels[] = { + AD74413R_ADC_CHANNEL(IIO_RESISTANCE, BIT(IIO_CHAN_INFO_PROCESSED)), +}; + +static struct iio_chan_spec ad74413r_digital_input_channels[] = { + AD74413R_ADC_VOLTAGE_CHANNEL, +}; + +#define _AD74413R_CHANNELS(_channels) \ + { \ + .channels = _channels, \ + .num_channels = ARRAY_SIZE(_channels), \ + } + +#define AD74413R_CHANNELS(name) \ + _AD74413R_CHANNELS(ad74413r_ ## name ## _channels) + +static const struct ad74413r_channels ad74413r_channels_map[] = { + [CH_FUNC_HIGH_IMPEDANCE] = AD74413R_CHANNELS(voltage_input), + [CH_FUNC_VOLTAGE_OUTPUT] = AD74413R_CHANNELS(voltage_output), + [CH_FUNC_CURRENT_OUTPUT] = AD74413R_CHANNELS(current_output), + [CH_FUNC_VOLTAGE_INPUT] = AD74413R_CHANNELS(voltage_input), + [CH_FUNC_CURRENT_INPUT_EXT_POWER] = AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER] = AD74413R_CHANNELS(current_input), + [CH_FUNC_RESISTANCE_INPUT] = AD74413R_CHANNELS(resistance_input), + [CH_FUNC_DIGITAL_INPUT_LOGIC] = AD74413R_CHANNELS(digital_input), + [CH_FUNC_DIGITAL_INPUT_LOOP_POWER] = AD74413R_CHANNELS(digital_input), + [CH_FUNC_CURRENT_INPUT_EXT_POWER_HART] = AD74413R_CHANNELS(current_input), + [CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART] = AD74413R_CHANNELS(current_input), +}; + +static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, + struct fwnode_handle *channel_node) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct ad74413r_channel_config *config; + u32 index; + int ret; + + ret = fwnode_property_read_u32(channel_node, "reg", &index); + if (ret) { + dev_err(st->dev, "Failed to read channel reg: %d\n", ret); + return ret; + } + + if (index > AD74413R_CHANNEL_MAX) { + dev_err(st->dev, "Channel index %u is too large\n", index); + return -EINVAL; + } + + config = &st->channel_configs[index]; + if (config->initialized) { + dev_err(st->dev, "Channel %u already initialized\n", index); + return -EINVAL; + } + + config->func = CH_FUNC_HIGH_IMPEDANCE; + fwnode_property_read_u32(channel_node, "adi,ch-func", &config->func); + + if (config->func < CH_FUNC_MIN || config->func > CH_FUNC_MAX) { + dev_err(st->dev, "Invalid channel function %u\n", config->func); + return -EINVAL; + } + + if (!st->chip_info->hart_support && + (config->func == CH_FUNC_CURRENT_INPUT_EXT_POWER_HART || + config->func == CH_FUNC_CURRENT_INPUT_LOOP_POWER_HART)) { + dev_err(st->dev, "Unsupported HART function %u\n", config->func); + return -EINVAL; + } + + if (config->func == CH_FUNC_DIGITAL_INPUT_LOGIC || + config->func == CH_FUNC_DIGITAL_INPUT_LOOP_POWER) + st->num_comparator_gpios++; + + config->gpo_comparator = fwnode_property_read_bool(channel_node, + "adi,gpo-comparator"); + + if (!config->gpo_comparator) + st->num_gpo_gpios++; + + indio_dev->num_channels += ad74413r_channels_map[config->func].num_channels; + + config->initialized = true; + + return 0; +} + +static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct fwnode_handle *channel_node = NULL; + int ret; + + fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) { + ret = ad74413r_parse_channel_config(indio_dev, channel_node); + if (ret) + goto put_channel_node; + } + + return 0; + +put_channel_node: + fwnode_handle_put(channel_node); + + return ret; +} + +static int ad74413r_setup_channels(struct iio_dev *indio_dev) +{ + struct ad74413r_state *st = iio_priv(indio_dev); + struct ad74413r_channel_config *config; + struct iio_chan_spec *channels, *chans; + unsigned int i, num_chans, chan_i; + int ret; + + channels = devm_kcalloc(st->dev, sizeof(*channels), + indio_dev->num_channels, GFP_KERNEL); + if (!channels) + return -ENOMEM; + + indio_dev->channels = channels; + + for (i = 0; i < AD74413R_CHANNEL_MAX; i++) { + config = &st->channel_configs[i]; + chans = ad74413r_channels_map[config->func].channels; + num_chans = ad74413r_channels_map[config->func].num_channels; + + memcpy(channels, chans, num_chans * sizeof(*chans)); + + for (chan_i = 0; chan_i < num_chans; chan_i++) { + struct iio_chan_spec *chan = &channels[chan_i]; + + chan->channel = i; + if (chan->output) + chan->scan_index = -1; + else + chan->scan_index = i; + } + + ret = ad74413r_set_channel_function(st, i, config->func); + if (ret) + return ret; + + channels += num_chans; + } + + return 0; +} + +static int ad74413r_setup_gpios(struct ad74413r_state *st) +{ + struct ad74413r_channel_config *config; + unsigned int comp_gpio_i = 0; + unsigned int gpo_gpio_i = 0; + unsigned int i; + u8 gpo_config; + int ret; + + for (i = 0; i < AD74413R_CHANNEL_MAX; i++) { + config = &st->channel_configs[i]; + + if (config->gpo_comparator) { + gpo_config = AD74413R_GPO_CONFIG_COMPARATOR; + } else { + gpo_config = AD74413R_GPO_CONFIG_LOGIC; + st->gpo_gpio_offsets[gpo_gpio_i++] = i; + } + + if (config->func == CH_FUNC_DIGITAL_INPUT_LOGIC || + config->func == CH_FUNC_DIGITAL_INPUT_LOOP_POWER) + st->comp_gpio_offsets[comp_gpio_i++] = i; + + ret = ad74413r_set_gpo_config(st, i, gpo_config); + if (ret) + return ret; + } + + return 0; +} + +static void ad74413r_regulator_disable(void *regulator) +{ + regulator_disable(regulator); +} + +static int ad74413r_probe(struct spi_device *spi) +{ + struct ad74413r_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + st->spi = spi; + st->dev = &spi->dev; + st->chip_info = device_get_match_data(&spi->dev); + mutex_init(&st->lock); + init_completion(&st->adc_data_completion); + + st->regmap = devm_regmap_init(st->dev, NULL, st, + &ad74413r_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + st->refin_reg = devm_regulator_get(st->dev, "refin"); + if (IS_ERR(st->refin_reg)) + return dev_err_probe(st->dev, PTR_ERR(st->refin_reg), + "Failed to get refin regulator\n"); + + ret = regulator_enable(st->refin_reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(st->dev, ad74413r_regulator_disable, + st->refin_reg); + if (ret) + return ret; + + st->sense_resistor_ohms = 100000000; + device_property_read_u32(st->dev, "shunt-resistor-micro-ohms", + &st->sense_resistor_ohms); + st->sense_resistor_ohms /= 1000000; + + st->trig = devm_iio_trigger_alloc(st->dev, "%s-dev%d", + st->chip_info->name, iio_device_id(indio_dev)); + if (!st->trig) + return -ENOMEM; + + st->trig->ops = &ad74413r_trigger_ops; + iio_trigger_set_drvdata(st->trig, st); + + ret = devm_iio_trigger_register(st->dev, st->trig); + if (ret) + return ret; + + indio_dev->name = st->chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad74413r_info; + indio_dev->trig = iio_trigger_get(st->trig); + + ret = ad74413r_reset(st); + if (ret) + return ret; + + ret = ad74413r_parse_channel_configs(indio_dev); + if (ret) + return ret; + + ret = ad74413r_setup_channels(indio_dev); + if (ret) + return ret; + + ret = ad74413r_setup_gpios(st); + if (ret) + return ret; + + if (st->num_gpo_gpios) { + st->gpo_gpiochip.owner = THIS_MODULE; + st->gpo_gpiochip.label = st->chip_info->name; + st->gpo_gpiochip.base = -1; + st->gpo_gpiochip.ngpio = st->num_gpo_gpios; + st->gpo_gpiochip.parent = st->dev; + st->gpo_gpiochip.can_sleep = true; + st->gpo_gpiochip.set = ad74413r_gpio_set; + st->gpo_gpiochip.set_multiple = ad74413r_gpio_set_multiple; + st->gpo_gpiochip.set_config = ad74413r_gpio_set_gpo_config; + st->gpo_gpiochip.get_direction = + ad74413r_gpio_get_gpo_direction; + + ret = devm_gpiochip_add_data(st->dev, &st->gpo_gpiochip, st); + if (ret) + return ret; + } + + if (st->num_comparator_gpios) { + st->comp_gpiochip.owner = THIS_MODULE; + st->comp_gpiochip.label = st->chip_info->name; + st->comp_gpiochip.base = -1; + st->comp_gpiochip.ngpio = st->num_comparator_gpios; + st->comp_gpiochip.parent = st->dev; + st->comp_gpiochip.can_sleep = true; + st->comp_gpiochip.get = ad74413r_gpio_get; + st->comp_gpiochip.get_multiple = ad74413r_gpio_get_multiple; + st->comp_gpiochip.set_config = ad74413r_gpio_set_comp_config; + st->comp_gpiochip.get_direction = + ad74413r_gpio_get_comp_direction; + + ret = devm_gpiochip_add_data(st->dev, &st->comp_gpiochip, st); + if (ret) + return ret; + } + + ret = ad74413r_set_adc_conv_seq(st, AD74413R_CONV_SEQ_OFF); + if (ret) + return ret; + + ret = devm_request_irq(st->dev, spi->irq, ad74413r_adc_data_interrupt, + 0, st->chip_info->name, indio_dev); + if (ret) + return dev_err_probe(st->dev, ret, "Failed to request irq\n"); + + ret = devm_iio_triggered_buffer_setup(st->dev, indio_dev, + &iio_pollfunc_store_time, + &ad74413r_trigger_handler, + &ad74413r_buffer_ops); + if (ret) + return ret; + + return devm_iio_device_register(st->dev, indio_dev); +} + +static int ad74413r_unregister_driver(struct spi_driver *spi) +{ + spi_unregister_driver(spi); + + return 0; +} + +static int __init ad74413r_register_driver(struct spi_driver *spi) +{ + crc8_populate_msb(ad74413r_crc8_table, AD74413R_CRC_POLYNOMIAL); + + return spi_register_driver(spi); +} + +static const struct ad74413r_chip_info ad74412r_chip_info_data = { + .hart_support = false, + .name = "ad74412r", +}; + +static const struct ad74413r_chip_info ad74413r_chip_info_data = { + .hart_support = true, + .name = "ad74413r", +}; + +static const struct of_device_id ad74413r_dt_id[] = { + { + .compatible = "adi,ad74412r", + .data = &ad74412r_chip_info_data, + }, + { + .compatible = "adi,ad74413r", + .data = &ad74413r_chip_info_data, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, ad74413r_dt_id); + +static struct spi_driver ad74413r_driver = { + .driver = { + .name = "ad74413r", + .of_match_table = ad74413r_dt_id, + }, + .probe = ad74413r_probe, +}; + +module_driver(ad74413r_driver, + ad74413r_register_driver, + ad74413r_unregister_driver); + +MODULE_AUTHOR("Cosmin Tanislav "); +MODULE_DESCRIPTION("Analog Devices AD74413R ADDAC"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 9020ef659885f2622cfb386cc229b6d618362895 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 17 Oct 2021 18:22:09 +0100 Subject: iio: trigger: Fix a scheduling whilst atomic issue seen on tsc2046 IIO triggers are software IRQ chips that split an incoming IRQ into separate IRQs routed to all devices using the trigger. When all consumers are done then a trigger callback reenable() is called. There are a few circumstances under which this can happen in atomic context. 1) A single user of the trigger that calls the iio_trigger_done() function from interrupt context. 2) A race between disconnecting the last device from a trigger and the trigger itself sucessfully being disabled. To avoid a resulting scheduling whilst atomic, close this second corner by using schedule_work() to ensure the reenable is not done in atomic context. Note that drivers must be careful to manage the interaction of set_state() and reenable() callbacks to ensure appropriate reference counting if they are relying on the same hardware controls. Deliberately taking this the slow path rather than via a fixes tree because the error has hard to hit and I would like it to soak for a while before hitting a release kernel. Signed-off-by: Jonathan Cameron Cc: Pengutronix Kernel Team Cc: Dmitry Torokhov Tested-by: Oleksij Rempel Cc: Link: https://lore.kernel.org/r/20211017172209.112387-1-jic23@kernel.org --- drivers/iio/industrialio-trigger.c | 36 +++++++++++++++++++++++++++++++++++- include/linux/iio/trigger.h | 2 ++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index b23caa2f2aa1..d3bdc9800b4a 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -162,6 +162,39 @@ static struct iio_trigger *iio_trigger_acquire_by_name(const char *name) return trig; } +static void iio_reenable_work_fn(struct work_struct *work) +{ + struct iio_trigger *trig = container_of(work, struct iio_trigger, + reenable_work); + + /* + * This 'might' occur after the trigger state is set to disabled - + * in that case the driver should skip reenabling. + */ + trig->ops->reenable(trig); +} + +/* + * In general, reenable callbacks may need to sleep and this path is + * not performance sensitive, so just queue up a work item + * to reneable the trigger for us. + * + * Races that can cause this. + * 1) A handler occurs entirely in interrupt context so the counter + * the final decrement is still in this interrupt. + * 2) The trigger has been removed, but one last interrupt gets through. + * + * For (1) we must call reenable, but not in atomic context. + * For (2) it should be safe to call reenanble, if drivers never blindly + * reenable after state is off. + */ +static void iio_trigger_notify_done_atomic(struct iio_trigger *trig) +{ + if (atomic_dec_and_test(&trig->use_count) && trig->ops && + trig->ops->reenable) + schedule_work(&trig->reenable_work); +} + void iio_trigger_poll(struct iio_trigger *trig) { int i; @@ -173,7 +206,7 @@ void iio_trigger_poll(struct iio_trigger *trig) if (trig->subirqs[i].enabled) generic_handle_irq(trig->subirq_base + i); else - iio_trigger_notify_done(trig); + iio_trigger_notify_done_atomic(trig); } } } @@ -535,6 +568,7 @@ struct iio_trigger *viio_trigger_alloc(struct device *parent, trig->dev.type = &iio_trig_type; trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); + INIT_WORK(&trig->reenable_work, iio_reenable_work_fn); mutex_init(&trig->pool_lock); trig->subirq_base = irq_alloc_descs(-1, 0, diff --git a/include/linux/iio/trigger.h b/include/linux/iio/trigger.h index 096f68dd2e0c..4c69b144677b 100644 --- a/include/linux/iio/trigger.h +++ b/include/linux/iio/trigger.h @@ -55,6 +55,7 @@ struct iio_trigger_ops { * @attached_own_device:[INTERN] if we are using our own device as trigger, * i.e. if we registered a poll function to the same * device as the one providing the trigger. + * @reenable_work: [INTERN] work item used to ensure reenable can sleep. **/ struct iio_trigger { const struct iio_trigger_ops *ops; @@ -74,6 +75,7 @@ struct iio_trigger { unsigned long pool[BITS_TO_LONGS(CONFIG_IIO_CONSUMERS_PER_TRIGGER)]; struct mutex pool_lock; bool attached_own_device; + struct work_struct reenable_work; }; -- cgit v1.2.3 From 3ac27afefd5dd6a53e830542b899f092a58b6b51 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:29 +0000 Subject: iio:dac:ad5755: Switch to generic firmware properties and drop pdata Lars pointed out that platform data can also be supported via the generic properties interface, so there is no point in continuing to support it separately. Hence squish the linux/platform_data/ad5755.h header into the c file and drop accessing the platform data directly. Done by inspection only. Mostly completely mechanical with the exception of a few places where default value handling is cleaner done by first setting the value, then calling the firmware reading function but and not checking the return value, as opposed to reading firmware then setting the default if an error occurs. Part of general attempt to move all of IIO over to generic device properties, both to enable other firmware types and to remove drivers that can be the source of of_ specific behaviour in new drivers. Suggested-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/dac/ad5755.c | 152 ++++++++++++++++++++++++++--------- include/linux/platform_data/ad5755.h | 102 ----------------------- 2 files changed, 116 insertions(+), 138 deletions(-) delete mode 100644 include/linux/platform_data/ad5755.h diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index cabc38d54085..7a62e6e1d5f1 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -13,10 +13,10 @@ #include #include #include -#include +#include + #include #include -#include #define AD5755_NUM_CHANNELS 4 @@ -63,6 +63,101 @@ #define AD5755_SLEW_RATE_SHIFT 3 #define AD5755_SLEW_ENABLE BIT(12) +enum ad5755_mode { + AD5755_MODE_VOLTAGE_0V_5V = 0, + AD5755_MODE_VOLTAGE_0V_10V = 1, + AD5755_MODE_VOLTAGE_PLUSMINUS_5V = 2, + AD5755_MODE_VOLTAGE_PLUSMINUS_10V = 3, + AD5755_MODE_CURRENT_4mA_20mA = 4, + AD5755_MODE_CURRENT_0mA_20mA = 5, + AD5755_MODE_CURRENT_0mA_24mA = 6, +}; + +enum ad5755_dc_dc_phase { + AD5755_DC_DC_PHASE_ALL_SAME_EDGE = 0, + AD5755_DC_DC_PHASE_A_B_SAME_EDGE_C_D_OPP_EDGE = 1, + AD5755_DC_DC_PHASE_A_C_SAME_EDGE_B_D_OPP_EDGE = 2, + AD5755_DC_DC_PHASE_90_DEGREE = 3, +}; + +enum ad5755_dc_dc_freq { + AD5755_DC_DC_FREQ_250kHZ = 0, + AD5755_DC_DC_FREQ_410kHZ = 1, + AD5755_DC_DC_FREQ_650kHZ = 2, +}; + +enum ad5755_dc_dc_maxv { + AD5755_DC_DC_MAXV_23V = 0, + AD5755_DC_DC_MAXV_24V5 = 1, + AD5755_DC_DC_MAXV_27V = 2, + AD5755_DC_DC_MAXV_29V5 = 3, +}; + +enum ad5755_slew_rate { + AD5755_SLEW_RATE_64k = 0, + AD5755_SLEW_RATE_32k = 1, + AD5755_SLEW_RATE_16k = 2, + AD5755_SLEW_RATE_8k = 3, + AD5755_SLEW_RATE_4k = 4, + AD5755_SLEW_RATE_2k = 5, + AD5755_SLEW_RATE_1k = 6, + AD5755_SLEW_RATE_500 = 7, + AD5755_SLEW_RATE_250 = 8, + AD5755_SLEW_RATE_125 = 9, + AD5755_SLEW_RATE_64 = 10, + AD5755_SLEW_RATE_32 = 11, + AD5755_SLEW_RATE_16 = 12, + AD5755_SLEW_RATE_8 = 13, + AD5755_SLEW_RATE_4 = 14, + AD5755_SLEW_RATE_0_5 = 15, +}; + +enum ad5755_slew_step_size { + AD5755_SLEW_STEP_SIZE_1 = 0, + AD5755_SLEW_STEP_SIZE_2 = 1, + AD5755_SLEW_STEP_SIZE_4 = 2, + AD5755_SLEW_STEP_SIZE_8 = 3, + AD5755_SLEW_STEP_SIZE_16 = 4, + AD5755_SLEW_STEP_SIZE_32 = 5, + AD5755_SLEW_STEP_SIZE_64 = 6, + AD5755_SLEW_STEP_SIZE_128 = 7, + AD5755_SLEW_STEP_SIZE_256 = 8, +}; + +/** + * struct ad5755_platform_data - AD5755 DAC driver platform data + * @ext_dc_dc_compenstation_resistor: Whether an external DC-DC converter + * compensation register is used. + * @dc_dc_phase: DC-DC converter phase. + * @dc_dc_freq: DC-DC converter frequency. + * @dc_dc_maxv: DC-DC maximum allowed boost voltage. + * @dac: Per DAC instance parameters. + * @dac.mode: The mode to be used for the DAC output. + * @dac.ext_current_sense_resistor: Whether an external current sense resistor + * is used. + * @dac.enable_voltage_overrange: Whether to enable 20% voltage output overrange. + * @dac.slew.enable: Whether to enable digital slew. + * @dac.slew.rate: Slew rate of the digital slew. + * @dac.slew.step_size: Slew step size of the digital slew. + **/ +struct ad5755_platform_data { + bool ext_dc_dc_compenstation_resistor; + enum ad5755_dc_dc_phase dc_dc_phase; + enum ad5755_dc_dc_freq dc_dc_freq; + enum ad5755_dc_dc_maxv dc_dc_maxv; + + struct { + enum ad5755_mode mode; + bool ext_current_sense_resistor; + bool enable_voltage_overrange; + struct { + bool enable; + enum ad5755_slew_rate rate; + enum ad5755_slew_step_size step_size; + } slew; + } dac[4]; +}; + /** * struct ad5755_chip_info - chip specific information * @channel_template: channel specification @@ -111,7 +206,6 @@ enum ad5755_type { ID_AD5737, }; -#ifdef CONFIG_OF static const int ad5755_dcdc_freq_table[][2] = { { 250000, AD5755_DC_DC_FREQ_250kHZ }, { 410000, AD5755_DC_DC_FREQ_410kHZ }, @@ -154,7 +248,6 @@ static const int ad5755_slew_step_table[][2] = { { 2, AD5755_SLEW_STEP_SIZE_2 }, { 1, AD5755_SLEW_STEP_SIZE_1 }, }; -#endif static int ad5755_write_unlocked(struct iio_dev *indio_dev, unsigned int reg, unsigned int val) @@ -604,30 +697,29 @@ static const struct ad5755_platform_data ad5755_default_pdata = { }, }; -#ifdef CONFIG_OF -static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) +static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) { - struct device_node *np = dev->of_node; - struct device_node *pp; + struct fwnode_handle *pp; struct ad5755_platform_data *pdata; unsigned int tmp; unsigned int tmparray[3]; int devnr, i; + if (!dev_fwnode(dev)) + return NULL; + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return NULL; pdata->ext_dc_dc_compenstation_resistor = - of_property_read_bool(np, "adi,ext-dc-dc-compenstation-resistor"); + device_property_read_bool(dev, "adi,ext-dc-dc-compenstation-resistor"); - if (!of_property_read_u32(np, "adi,dc-dc-phase", &tmp)) - pdata->dc_dc_phase = tmp; - else - pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE; + pdata->dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE; + device_property_read_u32(dev, "adi,dc-dc-phase", &pdata->dc_dc_phase); pdata->dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ; - if (!of_property_read_u32(np, "adi,dc-dc-freq-hz", &tmp)) { + if (!device_property_read_u32(dev, "adi,dc-dc-freq-hz", &tmp)) { for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_freq_table); i++) { if (tmp == ad5755_dcdc_freq_table[i][0]) { pdata->dc_dc_freq = ad5755_dcdc_freq_table[i][1]; @@ -641,7 +733,7 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) } pdata->dc_dc_maxv = AD5755_DC_DC_MAXV_23V; - if (!of_property_read_u32(np, "adi,dc-dc-max-microvolt", &tmp)) { + if (!device_property_read_u32(dev, "adi,dc-dc-max-microvolt", &tmp)) { for (i = 0; i < ARRAY_SIZE(ad5755_dcdc_maxv_table); i++) { if (tmp == ad5755_dcdc_maxv_table[i][0]) { pdata->dc_dc_maxv = ad5755_dcdc_maxv_table[i][1]; @@ -654,25 +746,23 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) } devnr = 0; - for_each_child_of_node(np, pp) { + device_for_each_child_node(dev, pp) { if (devnr >= AD5755_NUM_CHANNELS) { dev_err(dev, "There are too many channels defined in DT\n"); goto error_out; } - if (!of_property_read_u32(pp, "adi,mode", &tmp)) - pdata->dac[devnr].mode = tmp; - else - pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; + pdata->dac[devnr].mode = AD5755_MODE_CURRENT_4mA_20mA; + fwnode_property_read_u32(pp, "adi,mode", &pdata->dac[devnr].mode); pdata->dac[devnr].ext_current_sense_resistor = - of_property_read_bool(pp, "adi,ext-current-sense-resistor"); + fwnode_property_read_bool(pp, "adi,ext-current-sense-resistor"); pdata->dac[devnr].enable_voltage_overrange = - of_property_read_bool(pp, "adi,enable-voltage-overrange"); + fwnode_property_read_bool(pp, "adi,enable-voltage-overrange"); - if (!of_property_read_u32_array(pp, "adi,slew", tmparray, 3)) { + if (!fwnode_property_read_u32_array(pp, "adi,slew", tmparray, 3)) { pdata->dac[devnr].slew.enable = tmparray[0]; pdata->dac[devnr].slew.rate = AD5755_SLEW_RATE_64k; @@ -715,18 +805,11 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) devm_kfree(dev, pdata); return NULL; } -#else -static -struct ad5755_platform_data *ad5755_parse_dt(struct device *dev) -{ - return NULL; -} -#endif static int ad5755_probe(struct spi_device *spi) { enum ad5755_type type = spi_get_device_id(spi)->driver_data; - const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev); + const struct ad5755_platform_data *pdata; struct iio_dev *indio_dev; struct ad5755_state *st; int ret; @@ -751,13 +834,10 @@ static int ad5755_probe(struct spi_device *spi) mutex_init(&st->lock); - if (spi->dev.of_node) - pdata = ad5755_parse_dt(&spi->dev); - else - pdata = spi->dev.platform_data; + pdata = ad5755_parse_fw(&spi->dev); if (!pdata) { - dev_warn(&spi->dev, "no platform data? using default\n"); + dev_warn(&spi->dev, "no firmware provided parameters? using default\n"); pdata = &ad5755_default_pdata; } diff --git a/include/linux/platform_data/ad5755.h b/include/linux/platform_data/ad5755.h deleted file mode 100644 index e371e08f04bc..000000000000 --- a/include/linux/platform_data/ad5755.h +++ /dev/null @@ -1,102 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Copyright 2012 Analog Devices Inc. - */ -#ifndef __LINUX_PLATFORM_DATA_AD5755_H__ -#define __LINUX_PLATFORM_DATA_AD5755_H__ - -enum ad5755_mode { - AD5755_MODE_VOLTAGE_0V_5V = 0, - AD5755_MODE_VOLTAGE_0V_10V = 1, - AD5755_MODE_VOLTAGE_PLUSMINUS_5V = 2, - AD5755_MODE_VOLTAGE_PLUSMINUS_10V = 3, - AD5755_MODE_CURRENT_4mA_20mA = 4, - AD5755_MODE_CURRENT_0mA_20mA = 5, - AD5755_MODE_CURRENT_0mA_24mA = 6, -}; - -enum ad5755_dc_dc_phase { - AD5755_DC_DC_PHASE_ALL_SAME_EDGE = 0, - AD5755_DC_DC_PHASE_A_B_SAME_EDGE_C_D_OPP_EDGE = 1, - AD5755_DC_DC_PHASE_A_C_SAME_EDGE_B_D_OPP_EDGE = 2, - AD5755_DC_DC_PHASE_90_DEGREE = 3, -}; - -enum ad5755_dc_dc_freq { - AD5755_DC_DC_FREQ_250kHZ = 0, - AD5755_DC_DC_FREQ_410kHZ = 1, - AD5755_DC_DC_FREQ_650kHZ = 2, -}; - -enum ad5755_dc_dc_maxv { - AD5755_DC_DC_MAXV_23V = 0, - AD5755_DC_DC_MAXV_24V5 = 1, - AD5755_DC_DC_MAXV_27V = 2, - AD5755_DC_DC_MAXV_29V5 = 3, -}; - -enum ad5755_slew_rate { - AD5755_SLEW_RATE_64k = 0, - AD5755_SLEW_RATE_32k = 1, - AD5755_SLEW_RATE_16k = 2, - AD5755_SLEW_RATE_8k = 3, - AD5755_SLEW_RATE_4k = 4, - AD5755_SLEW_RATE_2k = 5, - AD5755_SLEW_RATE_1k = 6, - AD5755_SLEW_RATE_500 = 7, - AD5755_SLEW_RATE_250 = 8, - AD5755_SLEW_RATE_125 = 9, - AD5755_SLEW_RATE_64 = 10, - AD5755_SLEW_RATE_32 = 11, - AD5755_SLEW_RATE_16 = 12, - AD5755_SLEW_RATE_8 = 13, - AD5755_SLEW_RATE_4 = 14, - AD5755_SLEW_RATE_0_5 = 15, -}; - -enum ad5755_slew_step_size { - AD5755_SLEW_STEP_SIZE_1 = 0, - AD5755_SLEW_STEP_SIZE_2 = 1, - AD5755_SLEW_STEP_SIZE_4 = 2, - AD5755_SLEW_STEP_SIZE_8 = 3, - AD5755_SLEW_STEP_SIZE_16 = 4, - AD5755_SLEW_STEP_SIZE_32 = 5, - AD5755_SLEW_STEP_SIZE_64 = 6, - AD5755_SLEW_STEP_SIZE_128 = 7, - AD5755_SLEW_STEP_SIZE_256 = 8, -}; - -/** - * struct ad5755_platform_data - AD5755 DAC driver platform data - * @ext_dc_dc_compenstation_resistor: Whether an external DC-DC converter - * compensation register is used. - * @dc_dc_phase: DC-DC converter phase. - * @dc_dc_freq: DC-DC converter frequency. - * @dc_dc_maxv: DC-DC maximum allowed boost voltage. - * @dac.mode: The mode to be used for the DAC output. - * @dac.ext_current_sense_resistor: Whether an external current sense resistor - * is used. - * @dac.enable_voltage_overrange: Whether to enable 20% voltage output overrange. - * @dac.slew.enable: Whether to enable digital slew. - * @dac.slew.rate: Slew rate of the digital slew. - * @dac.slew.step_size: Slew step size of the digital slew. - **/ -struct ad5755_platform_data { - bool ext_dc_dc_compenstation_resistor; - enum ad5755_dc_dc_phase dc_dc_phase; - enum ad5755_dc_dc_freq dc_dc_freq; - enum ad5755_dc_dc_maxv dc_dc_maxv; - - struct { - enum ad5755_mode mode; - bool ext_current_sense_resistor; - bool enable_voltage_overrange; - struct { - bool enable; - enum ad5755_slew_rate rate; - enum ad5755_slew_step_size step_size; - } slew; - } dac[4]; -}; - -#endif -- cgit v1.2.3 From f191fe4f0d3e8ed033d888b4da9039f8ffe4039f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:30 +0000 Subject: iio:dac:ad5758: Drop unused of specific headers. These have never been used in this driver. What is used is in mod_devicetable.h so add that include (struct of_device_id) Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Daniel Gomez --- drivers/iio/dac/ad5758.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index 0572ef518101..98771e37a7b5 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -10,9 +10,8 @@ #include #include #include +#include #include -#include -#include #include #include -- cgit v1.2.3 From 5669c086e699ff269a977b225a8c9643cf39e53f Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:31 +0000 Subject: iio:dac:dpot-dac: Swap of.h for mod_devicetable.h This driver never used anything in the of specific header. It just wants the struct of_device_id from mod_devicetable.h. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Acked-by: Peter Rosin --- drivers/iio/dac/dpot-dac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c index 5d1819448102..83ce9489259c 100644 --- a/drivers/iio/dac/dpot-dac.c +++ b/drivers/iio/dac/dpot-dac.c @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include -- cgit v1.2.3 From 09a74ea737352a80ed6e3fd427350d7d9c5a5502 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:32 +0000 Subject: iio:dac:lpc18xx_dac: Swap from of* to mod_devicetable.h This driver never used anything from the of specific headers. mod_devicetable.h provides the struct of_device_id definition. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/dac/lpc18xx_dac.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c index 5502e4f62f0d..60467c6f2c6e 100644 --- a/drivers/iio/dac/lpc18xx_dac.c +++ b/drivers/iio/dac/lpc18xx_dac.c @@ -16,9 +16,8 @@ #include #include #include +#include #include -#include -#include #include #include -- cgit v1.2.3 From 92311717b3a37f2c888b30b940510ed6c058bfb0 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:33 +0000 Subject: iio:pot:mcp41010: Switch to generic firmware properties. In this case it was only of_device_get_match_data() + header update. This enables use of other firmware types with no other changes, such as ACPI via the PRP0001 route. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Chris Coffey --- drivers/iio/potentiometer/mcp41010.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c index 79ccac6d4be0..30a4594d4e11 100644 --- a/drivers/iio/potentiometer/mcp41010.c +++ b/drivers/iio/potentiometer/mcp41010.c @@ -21,9 +21,9 @@ #include #include #include +#include #include -#include -#include +#include #include #define MCP41010_MAX_WIPERS 2 @@ -146,7 +146,7 @@ static int mcp41010_probe(struct spi_device *spi) data = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); data->spi = spi; - data->cfg = of_device_get_match_data(&spi->dev); + data->cfg = device_get_match_data(&spi->dev); if (!data->cfg) data->cfg = &mcp41010_cfg[spi_get_device_id(spi)->driver_data]; -- cgit v1.2.3 From fdb726c4f9ef8a9301137530344aa4303fa0e571 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:34 +0000 Subject: iio:light:cm3605: Switch to generic firmware properties. This enables use of other firmware types with minimal driver changes. Part of an ongoing effort to move all IIO drivers over to generic accessors in order to reduce the chance of of_* versions being copied into new drivers. Also updated the headers to reflect this change including using mod_devicetable.h for struct of_device_id definition rather than going via of.h Signed-off-by: Jonathan Cameron Reviewed-by: Linus Walleij Reviewed-by: Andy Shevchenko --- drivers/iio/light/cm3605.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 3e7fb16ab1f6..50d34a98839c 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -18,7 +19,7 @@ #include #include #include -#include +#include #include #include #include @@ -156,7 +157,6 @@ static int cm3605_probe(struct platform_device *pdev) struct cm3605 *cm3605; struct iio_dev *indio_dev; struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; enum iio_chan_type ch_type; u32 rset; int irq; @@ -171,7 +171,7 @@ static int cm3605_probe(struct platform_device *pdev) cm3605->dev = dev; cm3605->dir = IIO_EV_DIR_FALLING; - ret = of_property_read_u32(np, "capella,aset-resistance-ohms", &rset); + ret = device_property_read_u32(dev, "capella,aset-resistance-ohms", &rset); if (ret) { dev_info(dev, "no RSET specified, assuming 100K\n"); rset = 100000; -- cgit v1.2.3 From c88eba5a186e6de87d7dcdf160d7d9fb989a191c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:35 +0000 Subject: iio:adc:max9611: Switch to generic firmware properties. Note the handling of the device tree node in this driver was somewhat unusual. I have cleaned that up whilst also moving over to generic properties. Part of a general attempt to move all IIO drivers over to generic firmware properties both as a general improvement and to avoid sources of cut and paste into future drivers. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/max9611.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c index 052ab23f10b2..01a4275e9c46 100644 --- a/drivers/iio/adc/max9611.c +++ b/drivers/iio/adc/max9611.c @@ -22,7 +22,8 @@ #include #include #include -#include +#include +#include #define DRIVER_NAME "max9611" @@ -513,11 +514,9 @@ static int max9611_probe(struct i2c_client *client, const struct i2c_device_id *id) { const char * const shunt_res_prop = "shunt-resistor-micro-ohms"; - const struct device_node *of_node = client->dev.of_node; - const struct of_device_id *of_id = - of_match_device(max9611_of_table, &client->dev); struct max9611_dev *max9611; struct iio_dev *indio_dev; + struct device *dev = &client->dev; unsigned int of_shunt; int ret; @@ -528,15 +527,14 @@ static int max9611_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); max9611 = iio_priv(indio_dev); - max9611->dev = &client->dev; + max9611->dev = dev; max9611->i2c_client = client; mutex_init(&max9611->lock); - ret = of_property_read_u32(of_node, shunt_res_prop, &of_shunt); + ret = device_property_read_u32(dev, shunt_res_prop, &of_shunt); if (ret) { - dev_err(&client->dev, - "Missing %s property for %pOF node\n", - shunt_res_prop, of_node); + dev_err(dev, "Missing %s property for %pfw node\n", + shunt_res_prop, dev_fwnode(dev)); return ret; } max9611->shunt_resistor_uohm = of_shunt; @@ -545,13 +543,13 @@ static int max9611_probe(struct i2c_client *client, if (ret) return ret; - indio_dev->name = of_id->data; + indio_dev->name = device_get_match_data(dev); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &indio_info; indio_dev->channels = max9611_channels; indio_dev->num_channels = ARRAY_SIZE(max9611_channels); - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static struct i2c_driver max9611_driver = { -- cgit v1.2.3 From 4efc1c614d334883cce09c38aa3fe74d3fb0bbf0 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:36 +0000 Subject: iio:adc:mcp3911: Switch to generic firmware properties. This allows use of the driver with other types of firmware such as ACPI PRP0001 based probing. Also part of a general attempt to remove direct use of of_ specific accessors from IIO. Added an include for mod_devicetable.h whilst here to cover the struct of_device_id definition. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko Cc: Kent Gustavsson Reviewed-by: Marcus Folkesson --- drivers/iio/adc/mcp3911.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c index e573da5397bb..13535f148c4c 100644 --- a/drivers/iio/adc/mcp3911.c +++ b/drivers/iio/adc/mcp3911.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include #include @@ -200,12 +202,13 @@ static const struct iio_info mcp3911_info = { .write_raw = mcp3911_write_raw, }; -static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node) +static int mcp3911_config(struct mcp3911 *adc) { + struct device *dev = &adc->spi->dev; u32 configreg; int ret; - of_property_read_u32(of_node, "device-addr", &adc->dev_addr); + device_property_read_u32(dev, "device-addr", &adc->dev_addr); if (adc->dev_addr > 3) { dev_err(&adc->spi->dev, "invalid device address (%i). Must be in range 0-3.\n", @@ -289,7 +292,7 @@ static int mcp3911_probe(struct spi_device *spi) } } - ret = mcp3911_config(adc, spi->dev.of_node); + ret = mcp3911_config(adc); if (ret) goto clk_disable; -- cgit v1.2.3 From 3c3969a0c99b0da5223d869b3b21ce3d38931810 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:37 +0000 Subject: iio:adc:ti-adc12138: Switch to generic firmware properties and drop of_match_ptr This enables using the driver with other firmware types such as ACPI via PRP0001. Also part of a general attempt to move IIO drivers over to generic properties to avoid opportunities for cut and paste. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/ti-adc12138.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c index 5b5d45210539..6eb62b564dae 100644 --- a/drivers/iio/adc/ti-adc12138.c +++ b/drivers/iio/adc/ti-adc12138.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -430,8 +431,8 @@ static int adc12138_probe(struct spi_device *spi) return -EINVAL; } - ret = of_property_read_u32(spi->dev.of_node, "ti,acquisition-time", - &adc->acquisition_time); + ret = device_property_read_u32(&spi->dev, "ti,acquisition-time", + &adc->acquisition_time); if (ret) adc->acquisition_time = 10; @@ -517,8 +518,6 @@ static int adc12138_remove(struct spi_device *spi) return 0; } -#ifdef CONFIG_OF - static const struct of_device_id adc12138_dt_ids[] = { { .compatible = "ti,adc12130", }, { .compatible = "ti,adc12132", }, @@ -527,8 +526,6 @@ static const struct of_device_id adc12138_dt_ids[] = { }; MODULE_DEVICE_TABLE(of, adc12138_dt_ids); -#endif - static const struct spi_device_id adc12138_id[] = { { "adc12130", adc12130 }, { "adc12132", adc12132 }, @@ -540,7 +537,7 @@ MODULE_DEVICE_TABLE(spi, adc12138_id); static struct spi_driver adc12138_driver = { .driver = { .name = "adc12138", - .of_match_table = of_match_ptr(adc12138_dt_ids), + .of_match_table = adc12138_dt_ids, }, .probe = adc12138_probe, .remove = adc12138_remove, -- cgit v1.2.3 From f346c96505412d83f7c8aa09629e48c2d3315fd4 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:38 +0000 Subject: iio:adc:envelope-detector: Switch from of headers to mod_devicetable.h There is nothing directly using of specific interfaces in this driver, so lets not include the headers. Signed-off-by: Jonathan Cameron Acked-by: Peter Rosin Reviewed-by: Andy Shevchenko --- drivers/iio/adc/envelope-detector.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c index d73eac36153f..e911c25d106d 100644 --- a/drivers/iio/adc/envelope-detector.c +++ b/drivers/iio/adc/envelope-detector.c @@ -31,14 +31,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include -#include #include #include #include -- cgit v1.2.3 From ade2be6d9b07529f35cb98c49b00a1b3bf26973c Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:39 +0000 Subject: iio:adc:ti-ads124s08: Drop dependency on OF. Nothing in this driver depends on OF firmware so drop the dependency and update the headers to remove the false impression such a dependency exists. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/Kconfig | 2 +- drivers/iio/adc/ti-ads124s08.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 8bf5b62a73f4..9b0b99bc826c 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1166,7 +1166,7 @@ config TI_ADS8688 config TI_ADS124S08 tristate "Texas Instruments ADS124S08" - depends on SPI && OF + depends on SPI help If you say yes here you get support for Texas Instruments ADS124S08 and ADS124S06 ADC chips diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index 17d0da5877a9..767b3b634809 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -8,8 +8,7 @@ #include #include #include -#include -#include +#include #include #include -- cgit v1.2.3 From a81c33f56abea1a63495e9a72073dda8c28083b8 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:01:40 +0000 Subject: iio:adc/dac:Kconfig: Update to drop OF dependencies. We could probably drop a lot more of these, but for now this removes unnecessary restrictions on stand alone ADC devices. For these 3 drivers the false dependency seems to date all the way back to their initial introduction. Signed-off-by: Jonathan Cameron Reviewed-by: Andy Shevchenko --- drivers/iio/adc/Kconfig | 4 ++-- drivers/iio/dac/Kconfig | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 9b0b99bc826c..c7de4632f24a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1146,7 +1146,7 @@ config TI_ADS7950 config TI_ADS8344 tristate "Texas Instruments ADS8344" - depends on SPI && OF + depends on SPI help If you say yes here you get support for Texas Instruments ADS8344 ADC chips @@ -1156,7 +1156,7 @@ config TI_ADS8344 config TI_ADS8688 tristate "Texas Instruments ADS8688" - depends on SPI && OF + depends on SPI help If you say yes here you get support for Texas Instruments ADS8684 and and ADS8688 ADC chips diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 6206b90fc08f..b95619f18fa5 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -340,7 +340,6 @@ config MAX517 config MAX5821 tristate "Maxim MAX5821 DAC driver" depends on I2C - depends on OF help Say yes here to build support for Maxim MAX5821 10 bits DAC. -- cgit v1.2.3 From 1665a92f780ac47c56c47916a9f8c4efbdd794b9 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sat, 4 Dec 2021 16:58:17 +0000 Subject: dt-bindings: iio: dac: adi,ad5755: drop unrelated included. Probably a cut and paste error, but the binding header used in the example is for the wrong device and nothing from it is used. Signed-off-by: Jonathan Cameron Cc: Sean Nyekjaer Reviewed-by: Rob Herring --- Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml index be419ac46caa..f866b88e1440 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5755.yaml @@ -125,7 +125,6 @@ oneOf: examples: - | - #include spi { #address-cells = <1>; #size-cells = <0>; -- cgit v1.2.3 From efa56eddf5d5c03a90abe708431f16c12c291837 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sat, 11 Dec 2021 17:02:21 +0800 Subject: coresight: core: Fix typo in a comment The double `the' in the comment in line 732 is repeated. Remove one of them from the comment. Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20211211090221.241529-1-wangborong@cdjrlc.com [Fixed capital letter in title] Signed-off-by: Mathieu Poirier --- drivers/hwtracing/coresight/coresight-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 8a18c71df37a..88653d1c06a4 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -729,7 +729,7 @@ static inline void coresight_put_ref(struct coresight_device *csdev) * coresight_grab_device - Power up this device and any of the helper * devices connected to it for trace operation. Since the helper devices * don't appear on the trace path, they should be handled along with the - * the master device. + * master device. */ static int coresight_grab_device(struct coresight_device *csdev) { -- cgit v1.2.3 From 045a31b95509c8f25f5f04ec5e0dec5cd09f2c5f Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 13 Dec 2021 02:05:07 +0000 Subject: phy: tegra: xusb: Fix return value of tegra_xusb_find_port_node function callers of tegra_xusb_find_port_node() function only do NULL checking for the return value. return NULL instead of ERR_PTR(-ENOMEM) to keep consistent. Signed-off-by: Miaoqian Lin Acked-by: Thierry Reding Link: https://lore.kernel.org/r/20211213020507.1458-1-linmq006@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/tegra/xusb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index 963de5913e50..aa5237eacd29 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -455,7 +455,7 @@ tegra_xusb_find_port_node(struct tegra_xusb_padctl *padctl, const char *type, name = kasprintf(GFP_KERNEL, "%s-%u", type, index); if (!name) { of_node_put(ports); - return ERR_PTR(-ENOMEM); + return NULL; } np = of_get_child_by_name(ports, name); kfree(name); -- cgit v1.2.3 From 918aaae300a634c4e0e98ad6820c9dc31eba09af Mon Sep 17 00:00:00 2001 From: Luca Weiss Date: Mon, 13 Dec 2021 09:26:05 +0100 Subject: dt-bindings: phy: qcom,qusb2: Add SM6350 compatible Add devicetree compatible for the usb phy on SM6350 SoC. Signed-off-by: Luca Weiss Acked-by: Konrad Dybcio Link: https://lore.kernel.org/r/20211213082614.22651-5-luca.weiss@fairphone.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml index aa2e409a1a09..e651a63a4be3 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml @@ -30,6 +30,7 @@ properties: - enum: - qcom,sc7180-qusb2-phy - qcom,sdm845-qusb2-phy + - qcom,sm6350-qusb2-phy - const: qcom,qusb2-v2-phy reg: maxItems: 1 -- cgit v1.2.3 From 16c57fff8390bf494b10c8321293295493e91a0b Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Sun, 12 Dec 2021 14:22:21 +0000 Subject: phy: ti: Use IS_ERR_OR_NULL() to clean code Use IS_ERR_OR_NULL() to make the code cleaner. Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20211212142226.23674-1-linmq006@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/ti/phy-omap-control.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/phy/ti/phy-omap-control.c b/drivers/phy/ti/phy-omap-control.c index 47482f106fab..76c5595f0859 100644 --- a/drivers/phy/ti/phy-omap-control.c +++ b/drivers/phy/ti/phy-omap-control.c @@ -26,7 +26,7 @@ void omap_control_pcie_pcs(struct device *dev, u8 delay) u32 val; struct omap_control_phy *control_phy; - if (IS_ERR(dev) || !dev) { + if (IS_ERR_OR_NULL(dev)) { pr_err("%s: invalid device\n", __func__); return; } @@ -61,7 +61,7 @@ void omap_control_phy_power(struct device *dev, int on) unsigned long rate; struct omap_control_phy *control_phy; - if (IS_ERR(dev) || !dev) { + if (IS_ERR_OR_NULL(dev)) { pr_err("%s: invalid device\n", __func__); return; } @@ -202,7 +202,7 @@ void omap_control_usb_set_mode(struct device *dev, { struct omap_control_phy *ctrl_phy; - if (IS_ERR(dev) || !dev) + if (IS_ERR_OR_NULL(dev)) return; ctrl_phy = dev_get_drvdata(dev); -- cgit v1.2.3 From 9d031a51b399b368b180886632402273bf53d6a2 Mon Sep 17 00:00:00 2001 From: Horatiu Vultur Date: Sat, 11 Dec 2021 22:47:17 +0100 Subject: phy: lan966x: Remove set_speed function Remove the set_speed function and allow the driver to figure out the speed at which needs to configure the serdes based on the interface type. Fixes: 305524902a0045 ("phy: Add lan966x ethernet serdes PHY driver") Signed-off-by: Horatiu Vultur Link: https://lore.kernel.org/r/20211211214717.1284306-1-horatiu.vultur@microchip.com Signed-off-by: Vinod Koul --- drivers/phy/microchip/lan966x_serdes.c | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/drivers/phy/microchip/lan966x_serdes.c b/drivers/phy/microchip/lan966x_serdes.c index c0b80a176387..e86a879b92b5 100644 --- a/drivers/phy/microchip/lan966x_serdes.c +++ b/drivers/phy/microchip/lan966x_serdes.c @@ -392,6 +392,11 @@ static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; + if (submode == PHY_INTERFACE_MODE_2500BASEX) + macro->speed = SPEED_2500; + else + macro->speed = SPEED_1000; + if (submode == PHY_INTERFACE_MODE_1000BASEX || submode == PHY_INTERFACE_MODE_2500BASEX) submode = PHY_INTERFACE_MODE_SGMII; @@ -427,19 +432,8 @@ static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) return -EINVAL; } -static int serdes_set_speed(struct phy *phy, int speed) -{ - struct serdes_macro *macro = phy_get_drvdata(phy); - - macro->speed = speed; - - return lan966x_sd6g40_setup(macro, macro->idx - (CU_MAX + 1), - macro->mode); -} - static const struct phy_ops serdes_ops = { .set_mode = serdes_set_mode, - .set_speed = serdes_set_speed, .owner = THIS_MODULE, }; @@ -482,7 +476,6 @@ static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy) macro->idx = idx; macro->ctrl = ctrl; - macro->speed = SPEED_1000; macro->port = -1; phy_set_drvdata(*phy, macro); -- cgit v1.2.3 From b156117aed1b9d192efcb27f5b37f78cd21fa545 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 11 Dec 2021 18:00:54 +0000 Subject: phy: rockchip-inno-usb2: remove redundant assignment to variable delay Variable delay is being assigned to zero and the code falls through to the next case in a switch statement that returns out of the function. The variable is never read in this scenario and so the assignment is redundant and can be removed. Cleans up scan-build static analysis warning: drivers/phy/rockchip/phy-rockchip-inno-usb2.c:753:3: warning: Value stored to 'delay' is never read [deadcode.DeadStores] Signed-off-by: Colin Ian King Link: https://lore.kernel.org/r/20211211180054.525368-1-colin.i.king@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 1938365abbb3..9f95b587e2c0 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -750,7 +750,6 @@ static void rockchip_chg_detect_work(struct work_struct *work) fallthrough; case USB_CHG_STATE_SECONDARY_DONE: rphy->chg_state = USB_CHG_STATE_DETECTED; - delay = 0; fallthrough; case USB_CHG_STATE_DETECTED: /* put the controller in normal mode */ -- cgit v1.2.3 From f6f787874aa52bbfbfd0210f519439d38fd5377f Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 2 Dec 2021 16:02:31 +0800 Subject: dt-bindings: phy: phy-imx8-pcie: Add binding for the pad modes of imx8 pcie phy Add binding for reference clock PAD modes of the i.MX8 PCIe PHY. Signed-off-by: Richard Zhu Tested-by: Marcel Ziswiler Reviewed-by: Tim Harvey Tested-by: Tim Harvey Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1638432158-4119-2-git-send-email-hongxing.zhu@nxp.com Signed-off-by: Vinod Koul --- include/dt-bindings/phy/phy-imx8-pcie.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 include/dt-bindings/phy/phy-imx8-pcie.h diff --git a/include/dt-bindings/phy/phy-imx8-pcie.h b/include/dt-bindings/phy/phy-imx8-pcie.h new file mode 100644 index 000000000000..8bbe2d6538d8 --- /dev/null +++ b/include/dt-bindings/phy/phy-imx8-pcie.h @@ -0,0 +1,14 @@ +/* SPDX-License-Identifier: (GPL-2.0+ OR MIT) */ +/* + * This header provides constants for i.MX8 PCIe. + */ + +#ifndef _DT_BINDINGS_IMX8_PCIE_H +#define _DT_BINDINGS_IMX8_PCIE_H + +/* Reference clock PAD mode */ +#define IMX8_PCIE_REFCLK_PAD_UNUSED 0 +#define IMX8_PCIE_REFCLK_PAD_INPUT 1 +#define IMX8_PCIE_REFCLK_PAD_OUTPUT 2 + +#endif /* _DT_BINDINGS_IMX8_PCIE_H */ -- cgit v1.2.3 From b3b5516a6fee5a4d39cd04fd84ed0a64b74e7238 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 2 Dec 2021 16:02:32 +0800 Subject: dt-bindings: phy: Add imx8 pcie phy driver support Add dt-binding for the standalone i.MX8 PCIe PHY driver. Signed-off-by: Richard Zhu Tested-by: Marcel Ziswiler Reviewed-by: Tim Harvey Tested-by: Tim Harvey Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/1638432158-4119-3-git-send-email-hongxing.zhu@nxp.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/fsl,imx8-pcie-phy.yaml | 92 ++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 Documentation/devicetree/bindings/phy/fsl,imx8-pcie-phy.yaml diff --git a/Documentation/devicetree/bindings/phy/fsl,imx8-pcie-phy.yaml b/Documentation/devicetree/bindings/phy/fsl,imx8-pcie-phy.yaml new file mode 100644 index 000000000000..b6421eedece3 --- /dev/null +++ b/Documentation/devicetree/bindings/phy/fsl,imx8-pcie-phy.yaml @@ -0,0 +1,92 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/phy/fsl,imx8-pcie-phy.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale i.MX8 SoC series PCIe PHY Device Tree Bindings + +maintainers: + - Richard Zhu + +properties: + "#phy-cells": + const: 0 + + compatible: + enum: + - fsl,imx8mm-pcie-phy + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: ref + + resets: + maxItems: 1 + + reset-names: + items: + - const: pciephy + + fsl,refclk-pad-mode: + description: | + Specifies the mode of the refclk pad used. It can be UNUSED(PHY + refclock is derived from SoC internal source), INPUT(PHY refclock + is provided externally via the refclk pad) or OUTPUT(PHY refclock + is derived from SoC internal source and provided on the refclk pad). + Refer include/dt-bindings/phy/phy-imx8-pcie.h for the constants + to be used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2 ] + + fsl,tx-deemph-gen1: + description: Gen1 De-emphasis value (optional). + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + + fsl,tx-deemph-gen2: + description: Gen2 De-emphasis value (optional). + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + + fsl,clkreq-unsupported: + type: boolean + description: A boolean property indicating the CLKREQ# signal is + not supported in the board design (optional) + +required: + - "#phy-cells" + - compatible + - reg + - clocks + - clock-names + - fsl,refclk-pad-mode + +additionalProperties: false + +examples: + - | + #include + #include + #include + + pcie_phy: pcie-phy@32f00000 { + compatible = "fsl,imx8mm-pcie-phy"; + reg = <0x32f00000 0x10000>; + clocks = <&clk IMX8MM_CLK_PCIE1_PHY>; + clock-names = "ref"; + assigned-clocks = <&clk IMX8MM_CLK_PCIE1_PHY>; + assigned-clock-rates = <100000000>; + assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_100M>; + resets = <&src IMX8MQ_RESET_PCIEPHY>; + reset-names = "pciephy"; + fsl,refclk-pad-mode = ; + #phy-cells = <0>; + }; +... -- cgit v1.2.3 From 1aa97b002258a190d7790a1a5c0c27829f82e569 Mon Sep 17 00:00:00 2001 From: Richard Zhu Date: Thu, 2 Dec 2021 16:02:35 +0800 Subject: phy: freescale: pcie: Initialize the imx8 pcie standalone phy driver Add the standalone i.MX8 PCIe PHY driver. Signed-off-by: Richard Zhu Tested-by: Marcel Ziswiler Reviewed-by: Tim Harvey Tested-by: Tim Harvey Link: https://lore.kernel.org/r/1638432158-4119-6-git-send-email-hongxing.zhu@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/Kconfig | 8 + drivers/phy/freescale/Makefile | 1 + drivers/phy/freescale/phy-fsl-imx8m-pcie.c | 236 +++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) create mode 100644 drivers/phy/freescale/phy-fsl-imx8m-pcie.c diff --git a/drivers/phy/freescale/Kconfig b/drivers/phy/freescale/Kconfig index 320630ffe3cd..c3669c28ea9f 100644 --- a/drivers/phy/freescale/Kconfig +++ b/drivers/phy/freescale/Kconfig @@ -14,3 +14,11 @@ config PHY_MIXEL_MIPI_DPHY help Enable this to add support for the Mixel DSI PHY as found on NXP's i.MX8 family of SOCs. + +config PHY_FSL_IMX8M_PCIE + tristate "Freescale i.MX8M PCIE PHY" + depends on OF && HAS_IOMEM + select GENERIC_PHY + help + Enable this to add support for the PCIE PHY as found on + i.MX8M family of SOCs. diff --git a/drivers/phy/freescale/Makefile b/drivers/phy/freescale/Makefile index 1d02e3869b45..55d07c742ab0 100644 --- a/drivers/phy/freescale/Makefile +++ b/drivers/phy/freescale/Makefile @@ -1,3 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_PHY_FSL_IMX8MQ_USB) += phy-fsl-imx8mq-usb.o obj-$(CONFIG_PHY_MIXEL_MIPI_DPHY) += phy-fsl-imx8-mipi-dphy.o +obj-$(CONFIG_PHY_FSL_IMX8M_PCIE) += phy-fsl-imx8m-pcie.o diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c new file mode 100644 index 000000000000..f6502463d49a --- /dev/null +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 NXP + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define IMX8MM_PCIE_PHY_CMN_REG061 0x184 +#define ANA_PLL_CLK_OUT_TO_EXT_IO_EN BIT(0) +#define IMX8MM_PCIE_PHY_CMN_REG062 0x188 +#define ANA_PLL_CLK_OUT_TO_EXT_IO_SEL BIT(3) +#define IMX8MM_PCIE_PHY_CMN_REG063 0x18C +#define AUX_PLL_REFCLK_SEL_SYS_PLL GENMASK(7, 6) +#define IMX8MM_PCIE_PHY_CMN_REG064 0x190 +#define ANA_AUX_RX_TX_SEL_TX BIT(7) +#define ANA_AUX_RX_TERM_GND_EN BIT(3) +#define ANA_AUX_TX_TERM BIT(2) +#define IMX8MM_PCIE_PHY_CMN_REG065 0x194 +#define ANA_AUX_RX_TERM (BIT(7) | BIT(4)) +#define ANA_AUX_TX_LVL GENMASK(3, 0) +#define IMX8MM_PCIE_PHY_CMN_REG75 0x1D4 +#define PCIE_PHY_CMN_REG75_PLL_DONE 0x3 +#define PCIE_PHY_TRSV_REG5 0x414 +#define PCIE_PHY_TRSV_REG5_GEN1_DEEMP 0x2D +#define PCIE_PHY_TRSV_REG6 0x418 +#define PCIE_PHY_TRSV_REG6_GEN2_DEEMP 0xF + +#define IMX8MM_GPR_PCIE_REF_CLK_SEL GENMASK(25, 24) +#define IMX8MM_GPR_PCIE_REF_CLK_PLL FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x3) +#define IMX8MM_GPR_PCIE_REF_CLK_EXT FIELD_PREP(IMX8MM_GPR_PCIE_REF_CLK_SEL, 0x2) +#define IMX8MM_GPR_PCIE_AUX_EN BIT(19) +#define IMX8MM_GPR_PCIE_CMN_RST BIT(18) +#define IMX8MM_GPR_PCIE_POWER_OFF BIT(17) +#define IMX8MM_GPR_PCIE_SSC_EN BIT(16) +#define IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE BIT(9) + +struct imx8_pcie_phy { + void __iomem *base; + struct clk *clk; + struct phy *phy; + struct regmap *iomuxc_gpr; + struct reset_control *reset; + u32 refclk_pad_mode; + u32 tx_deemph_gen1; + u32 tx_deemph_gen2; + bool clkreq_unused; +}; + +static int imx8_pcie_phy_init(struct phy *phy) +{ + int ret; + u32 val, pad_mode; + struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); + + reset_control_assert(imx8_phy->reset); + + pad_mode = imx8_phy->refclk_pad_mode; + /* Set AUX_EN_OVERRIDE 1'b0, when the CLKREQ# isn't hooked */ + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE, + imx8_phy->clkreq_unused ? + 0 : IMX8MM_GPR_PCIE_AUX_EN_OVERRIDE); + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_AUX_EN, + IMX8MM_GPR_PCIE_AUX_EN); + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_POWER_OFF, 0); + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_SSC_EN, 0); + + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_REF_CLK_SEL, + pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT ? + IMX8MM_GPR_PCIE_REF_CLK_EXT : + IMX8MM_GPR_PCIE_REF_CLK_PLL); + usleep_range(100, 200); + + /* Do the PHY common block reset */ + regmap_update_bits(imx8_phy->iomuxc_gpr, IOMUXC_GPR14, + IMX8MM_GPR_PCIE_CMN_RST, + IMX8MM_GPR_PCIE_CMN_RST); + usleep_range(200, 500); + + if (pad_mode == IMX8_PCIE_REFCLK_PAD_INPUT) { + /* Configure the pad as input */ + val = readl(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); + writel(val & ~ANA_PLL_CLK_OUT_TO_EXT_IO_EN, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); + } else if (pad_mode == IMX8_PCIE_REFCLK_PAD_OUTPUT) { + /* Configure the PHY to output the refclock via pad */ + writel(ANA_PLL_CLK_OUT_TO_EXT_IO_EN, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG061); + writel(ANA_PLL_CLK_OUT_TO_EXT_IO_SEL, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG062); + writel(AUX_PLL_REFCLK_SEL_SYS_PLL, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG063); + val = ANA_AUX_RX_TX_SEL_TX | ANA_AUX_TX_TERM; + writel(val | ANA_AUX_RX_TERM_GND_EN, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG064); + writel(ANA_AUX_RX_TERM | ANA_AUX_TX_LVL, + imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG065); + } + + /* Tune PHY de-emphasis setting to pass PCIe compliance. */ + if (imx8_phy->tx_deemph_gen1) + writel(imx8_phy->tx_deemph_gen1, + imx8_phy->base + PCIE_PHY_TRSV_REG5); + if (imx8_phy->tx_deemph_gen2) + writel(imx8_phy->tx_deemph_gen2, + imx8_phy->base + PCIE_PHY_TRSV_REG6); + + reset_control_deassert(imx8_phy->reset); + + /* Polling to check the phy is ready or not. */ + ret = readl_poll_timeout(imx8_phy->base + IMX8MM_PCIE_PHY_CMN_REG75, + val, val == PCIE_PHY_CMN_REG75_PLL_DONE, + 10, 20000); + return ret; +} + +static int imx8_pcie_phy_power_on(struct phy *phy) +{ + struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); + + return clk_prepare_enable(imx8_phy->clk); +} + +static int imx8_pcie_phy_power_off(struct phy *phy) +{ + struct imx8_pcie_phy *imx8_phy = phy_get_drvdata(phy); + + clk_disable_unprepare(imx8_phy->clk); + + return 0; +} + +static const struct phy_ops imx8_pcie_phy_ops = { + .init = imx8_pcie_phy_init, + .power_on = imx8_pcie_phy_power_on, + .power_off = imx8_pcie_phy_power_off, + .owner = THIS_MODULE, +}; + +static int imx8_pcie_phy_probe(struct platform_device *pdev) +{ + struct phy_provider *phy_provider; + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct imx8_pcie_phy *imx8_phy; + struct resource *res; + + imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL); + if (!imx8_phy) + return -ENOMEM; + + /* get PHY refclk pad mode */ + of_property_read_u32(np, "fsl,refclk-pad-mode", + &imx8_phy->refclk_pad_mode); + + if (of_property_read_u32(np, "fsl,tx-deemph-gen1", + &imx8_phy->tx_deemph_gen1)) + imx8_phy->tx_deemph_gen1 = 0; + + if (of_property_read_u32(np, "fsl,tx-deemph-gen2", + &imx8_phy->tx_deemph_gen2)) + imx8_phy->tx_deemph_gen2 = 0; + + if (of_property_read_bool(np, "fsl,clkreq-unsupported")) + imx8_phy->clkreq_unused = true; + else + imx8_phy->clkreq_unused = false; + + imx8_phy->clk = devm_clk_get(dev, "ref"); + if (IS_ERR(imx8_phy->clk)) { + dev_err(dev, "failed to get imx pcie phy clock\n"); + return PTR_ERR(imx8_phy->clk); + } + + /* Grab GPR config register range */ + imx8_phy->iomuxc_gpr = + syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr"); + if (IS_ERR(imx8_phy->iomuxc_gpr)) { + dev_err(dev, "unable to find iomuxc registers\n"); + return PTR_ERR(imx8_phy->iomuxc_gpr); + } + + imx8_phy->reset = devm_reset_control_get_exclusive(dev, "pciephy"); + if (IS_ERR(imx8_phy->reset)) { + dev_err(dev, "Failed to get PCIEPHY reset control\n"); + return PTR_ERR(imx8_phy->reset); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + imx8_phy->base = devm_ioremap_resource(dev, res); + if (IS_ERR(imx8_phy->base)) + return PTR_ERR(imx8_phy->base); + + imx8_phy->phy = devm_phy_create(dev, NULL, &imx8_pcie_phy_ops); + if (IS_ERR(imx8_phy->phy)) + return PTR_ERR(imx8_phy->phy); + + phy_set_drvdata(imx8_phy->phy, imx8_phy); + + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id imx8_pcie_phy_of_match[] = { + {.compatible = "fsl,imx8mm-pcie-phy",}, + { }, +}; +MODULE_DEVICE_TABLE(of, imx8_pcie_phy_of_match); + +static struct platform_driver imx8_pcie_phy_driver = { + .probe = imx8_pcie_phy_probe, + .driver = { + .name = "imx8-pcie-phy", + .of_match_table = imx8_pcie_phy_of_match, + } +}; +module_platform_driver(imx8_pcie_phy_driver); + +MODULE_DESCRIPTION("FSL IMX8 PCIE PHY driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 0ae8c6252888d487f69b406369c3176172bb2064 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 9 Dec 2021 14:18:41 +0530 Subject: dt-bindings: interconnect: Add Qualcomm SM8450 DT bindings The Qualcomm SM8450 SoC has several bus fabrics that could be controlled and tuned dynamically according to the bandwidth demand Signed-off-by: Vinod Koul Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211209084842.189627-2-vkoul@kernel.org Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,rpmh.yaml | 11 ++ include/dt-bindings/interconnect/qcom,sm8450.h | 171 +++++++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 include/dt-bindings/interconnect/qcom,sm8450.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml b/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml index 3fd1a134162d..cbb24f9bb609 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,rpmh.yaml @@ -104,6 +104,17 @@ properties: - qcom,sm8350-mmss-noc - qcom,sm8350-compute-noc - qcom,sm8350-system-noc + - qcom,sm8450-aggre1-noc + - qcom,sm8450-aggre2-noc + - qcom,sm8450-clk-virt + - qcom,sm8450-config-noc + - qcom,sm8450-gem-noc + - qcom,sm8450-lpass-ag-noc + - qcom,sm8450-mc-virt + - qcom,sm8450-mmss-noc + - qcom,sm8450-nsp-noc + - qcom,sm8450-pcie-anoc + - qcom,sm8450-system-noc '#interconnect-cells': enum: [ 1, 2 ] diff --git a/include/dt-bindings/interconnect/qcom,sm8450.h b/include/dt-bindings/interconnect/qcom,sm8450.h new file mode 100644 index 000000000000..8f3c5e1fb4c4 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,sm8450.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Limited + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_SM8450_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_SM8450_H + +#define MASTER_QSPI_0 0 +#define MASTER_QUP_1 1 +#define MASTER_A1NOC_CFG 2 +#define MASTER_SDCC_4 3 +#define MASTER_UFS_MEM 4 +#define MASTER_USB3_0 5 +#define SLAVE_A1NOC_SNOC 6 +#define SLAVE_SERVICE_A1NOC 7 + +#define MASTER_QDSS_BAM 0 +#define MASTER_QUP_0 1 +#define MASTER_QUP_2 2 +#define MASTER_A2NOC_CFG 3 +#define MASTER_CRYPTO 4 +#define MASTER_IPA 5 +#define MASTER_SENSORS_PROC 6 +#define MASTER_SP 7 +#define MASTER_QDSS_ETR 8 +#define MASTER_QDSS_ETR_1 9 +#define MASTER_SDCC_2 10 +#define SLAVE_A2NOC_SNOC 11 +#define SLAVE_SERVICE_A2NOC 12 + +#define MASTER_QUP_CORE_0 0 +#define MASTER_QUP_CORE_1 1 +#define MASTER_QUP_CORE_2 2 +#define SLAVE_QUP_CORE_0 3 +#define SLAVE_QUP_CORE_1 4 +#define SLAVE_QUP_CORE_2 5 + +#define MASTER_GEM_NOC_CNOC 0 +#define MASTER_GEM_NOC_PCIE_SNOC 1 +#define SLAVE_AHB2PHY_SOUTH 2 +#define SLAVE_AHB2PHY_NORTH 3 +#define SLAVE_AOSS 4 +#define SLAVE_CAMERA_CFG 5 +#define SLAVE_CLK_CTL 6 +#define SLAVE_CDSP_CFG 7 +#define SLAVE_RBCPR_CX_CFG 8 +#define SLAVE_RBCPR_MMCX_CFG 9 +#define SLAVE_RBCPR_MXA_CFG 10 +#define SLAVE_RBCPR_MXC_CFG 11 +#define SLAVE_CRYPTO_0_CFG 12 +#define SLAVE_CX_RDPM 13 +#define SLAVE_DISPLAY_CFG 14 +#define SLAVE_GFX3D_CFG 15 +#define SLAVE_IMEM_CFG 16 +#define SLAVE_IPA_CFG 17 +#define SLAVE_IPC_ROUTER_CFG 18 +#define SLAVE_LPASS 19 +#define SLAVE_CNOC_MSS 20 +#define SLAVE_MX_RDPM 21 +#define SLAVE_PCIE_0_CFG 22 +#define SLAVE_PCIE_1_CFG 23 +#define SLAVE_PDM 24 +#define SLAVE_PIMEM_CFG 25 +#define SLAVE_PRNG 26 +#define SLAVE_QDSS_CFG 27 +#define SLAVE_QSPI_0 28 +#define SLAVE_QUP_0 29 +#define SLAVE_QUP_1 30 +#define SLAVE_QUP_2 31 +#define SLAVE_SDCC_2 32 +#define SLAVE_SDCC_4 33 +#define SLAVE_SPSS_CFG 34 +#define SLAVE_TCSR 35 +#define SLAVE_TLMM 36 +#define SLAVE_TME_CFG 37 +#define SLAVE_UFS_MEM_CFG 38 +#define SLAVE_USB3_0 39 +#define SLAVE_VENUS_CFG 40 +#define SLAVE_VSENSE_CTRL_CFG 41 +#define SLAVE_A1NOC_CFG 42 +#define SLAVE_A2NOC_CFG 43 +#define SLAVE_DDRSS_CFG 44 +#define SLAVE_CNOC_MNOC_CFG 45 +#define SLAVE_PCIE_ANOC_CFG 46 +#define SLAVE_SNOC_CFG 47 +#define SLAVE_IMEM 48 +#define SLAVE_PIMEM 49 +#define SLAVE_SERVICE_CNOC 50 +#define SLAVE_PCIE_0 51 +#define SLAVE_PCIE_1 52 +#define SLAVE_QDSS_STM 53 +#define SLAVE_TCU 54 + +#define MASTER_GPU_TCU 0 +#define MASTER_SYS_TCU 1 +#define MASTER_APPSS_PROC 2 +#define MASTER_GFX3D 3 +#define MASTER_MSS_PROC 4 +#define MASTER_MNOC_HF_MEM_NOC 5 +#define MASTER_MNOC_SF_MEM_NOC 6 +#define MASTER_COMPUTE_NOC 7 +#define MASTER_ANOC_PCIE_GEM_NOC 8 +#define MASTER_SNOC_GC_MEM_NOC 9 +#define MASTER_SNOC_SF_MEM_NOC 10 +#define SLAVE_GEM_NOC_CNOC 11 +#define SLAVE_LLCC 12 +#define SLAVE_MEM_NOC_PCIE_SNOC 13 +#define MASTER_MNOC_HF_MEM_NOC_DISP 14 +#define MASTER_MNOC_SF_MEM_NOC_DISP 15 +#define MASTER_ANOC_PCIE_GEM_NOC_DISP 16 +#define SLAVE_LLCC_DISP 17 + +#define MASTER_CNOC_LPASS_AG_NOC 0 +#define MASTER_LPASS_PROC 1 +#define SLAVE_LPASS_CORE_CFG 2 +#define SLAVE_LPASS_LPI_CFG 3 +#define SLAVE_LPASS_MPU_CFG 4 +#define SLAVE_LPASS_TOP_CFG 5 +#define SLAVE_LPASS_SNOC 6 +#define SLAVE_SERVICES_LPASS_AML_NOC 7 +#define SLAVE_SERVICE_LPASS_AG_NOC 8 + +#define MASTER_LLCC 0 +#define SLAVE_EBI1 1 +#define MASTER_LLCC_DISP 2 +#define SLAVE_EBI1_DISP 3 + +#define MASTER_CAMNOC_HF 0 +#define MASTER_CAMNOC_ICP 1 +#define MASTER_CAMNOC_SF 2 +#define MASTER_MDP 3 +#define MASTER_CNOC_MNOC_CFG 4 +#define MASTER_ROTATOR 5 +#define MASTER_CDSP_HCP 6 +#define MASTER_VIDEO 7 +#define MASTER_VIDEO_CV_PROC 8 +#define MASTER_VIDEO_PROC 9 +#define MASTER_VIDEO_V_PROC 10 +#define SLAVE_MNOC_HF_MEM_NOC 11 +#define SLAVE_MNOC_SF_MEM_NOC 12 +#define SLAVE_SERVICE_MNOC 13 +#define MASTER_MDP_DISP 14 +#define MASTER_ROTATOR_DISP 15 +#define SLAVE_MNOC_HF_MEM_NOC_DISP 16 +#define SLAVE_MNOC_SF_MEM_NOC_DISP 17 + +#define MASTER_CDSP_NOC_CFG 0 +#define MASTER_CDSP_PROC 1 +#define SLAVE_CDSP_MEM_NOC 2 +#define SLAVE_SERVICE_NSP_NOC 3 + +#define MASTER_PCIE_ANOC_CFG 0 +#define MASTER_PCIE_0 1 +#define MASTER_PCIE_1 2 +#define SLAVE_ANOC_PCIE_GEM_NOC 3 +#define SLAVE_SERVICE_PCIE_ANOC 4 + +#define MASTER_GIC_AHB 0 +#define MASTER_A1NOC_SNOC 1 +#define MASTER_A2NOC_SNOC 2 +#define MASTER_LPASS_ANOC 3 +#define MASTER_SNOC_CFG 4 +#define MASTER_PIMEM 5 +#define MASTER_GIC 6 +#define SLAVE_SNOC_GEM_NOC_GC 7 +#define SLAVE_SNOC_GEM_NOC_SF 8 +#define SLAVE_SERVICE_SNOC 9 + +#endif -- cgit v1.2.3 From fafc114a468ee43b95388bec8a45d042bc3f9344 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Thu, 9 Dec 2021 14:18:42 +0530 Subject: interconnect: qcom: Add SM8450 interconnect provider driver Add driver for the Qualcomm interconnect buses found in SM8450 based platforms. The topology consists of several NoCs that are controlled by a remote processor that collects the aggregated bandwidth for each master-slave pairs. This is based on the downstream driver by Vivek Aknurwar Signed-off-by: Vinod Koul Link: https://lore.kernel.org/r/20211209084842.189627-3-vkoul@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/sm8450.c | 1987 ++++++++++++++++++++++++++++++++++++ drivers/interconnect/qcom/sm8450.h | 169 +++ 4 files changed, 2167 insertions(+) create mode 100644 drivers/interconnect/qcom/sm8450.c create mode 100644 drivers/interconnect/qcom/sm8450.h diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index daf1e25f6042..e2207f9a1a47 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -146,5 +146,14 @@ config INTERCONNECT_QCOM_SM8350 This is a driver for the Qualcomm Network-on-Chip on SM8350-based platforms. +config INTERCONNECT_QCOM_SM8450 + tristate "Qualcomm SM8450 interconnect driver" + depends on INTERCONNECT_QCOM_RPMH_POSSIBLE + select INTERCONNECT_QCOM_RPMH + select INTERCONNECT_QCOM_BCM_VOTER + help + This is a driver for the Qualcomm Network-on-Chip on SM8450-based + platforms. + config INTERCONNECT_QCOM_SMD_RPM tristate diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 69300b1d48ef..180046d532df 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -16,6 +16,7 @@ qnoc-sdx55-objs := sdx55.o qnoc-sm8150-objs := sm8150.o qnoc-sm8250-objs := sm8250.o qnoc-sm8350-objs := sm8350.o +qnoc-sm8450-objs := sm8450.o icc-smd-rpm-objs := smd-rpm.o icc-rpm.o obj-$(CONFIG_INTERCONNECT_QCOM_BCM_VOTER) += icc-bcm-voter.o @@ -34,4 +35,5 @@ obj-$(CONFIG_INTERCONNECT_QCOM_SDX55) += qnoc-sdx55.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8150) += qnoc-sm8150.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8250) += qnoc-sm8250.o obj-$(CONFIG_INTERCONNECT_QCOM_SM8350) += qnoc-sm8350.o +obj-$(CONFIG_INTERCONNECT_QCOM_SM8450) += qnoc-sm8450.o obj-$(CONFIG_INTERCONNECT_QCOM_SMD_RPM) += icc-smd-rpm.o diff --git a/drivers/interconnect/qcom/sm8450.c b/drivers/interconnect/qcom/sm8450.c new file mode 100644 index 000000000000..8d99ee6421df --- /dev/null +++ b/drivers/interconnect/qcom/sm8450.c @@ -0,0 +1,1987 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include + +#include "bcm-voter.h" +#include "icc-rpmh.h" +#include "sm8450.h" + +static struct qcom_icc_node qhm_qspi = { + .name = "qhm_qspi", + .id = SM8450_MASTER_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup1 = { + .name = "qhm_qup1", + .id = SM8450_MASTER_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_a1noc_cfg = { + .name = "qnm_a1noc_cfg", + .id = SM8450_MASTER_A1NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SERVICE_A1NOC }, +}; + +static struct qcom_icc_node xm_sdc4 = { + .name = "xm_sdc4", + .id = SM8450_MASTER_SDCC_4, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_ufs_mem = { + .name = "xm_ufs_mem", + .id = SM8450_MASTER_UFS_MEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node xm_usb3_0 = { + .name = "xm_usb3_0", + .id = SM8450_MASTER_USB3_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A1NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qdss_bam = { + .name = "qhm_qdss_bam", + .id = SM8450_MASTER_QDSS_BAM, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup0 = { + .name = "qhm_qup0", + .id = SM8450_MASTER_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qhm_qup2 = { + .name = "qhm_qup2", + .id = SM8450_MASTER_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qnm_a2noc_cfg = { + .name = "qnm_a2noc_cfg", + .id = SM8450_MASTER_A2NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SERVICE_A2NOC }, +}; + +static struct qcom_icc_node qxm_crypto = { + .name = "qxm_crypto", + .id = SM8450_MASTER_CRYPTO, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_ipa = { + .name = "qxm_ipa", + .id = SM8450_MASTER_IPA, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_sensorss_q6 = { + .name = "qxm_sensorss_q6", + .id = SM8450_MASTER_SENSORS_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qxm_sp = { + .name = "qxm_sp", + .id = SM8450_MASTER_SP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_0 = { + .name = "xm_qdss_etr_0", + .id = SM8450_MASTER_QDSS_ETR, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_qdss_etr_1 = { + .name = "xm_qdss_etr_1", + .id = SM8450_MASTER_QDSS_ETR_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node xm_sdc2 = { + .name = "xm_sdc2", + .id = SM8450_MASTER_SDCC_2, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_A2NOC_SNOC }, +}; + +static struct qcom_icc_node qup0_core_master = { + .name = "qup0_core_master", + .id = SM8450_MASTER_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_QUP_CORE_0 }, +}; + +static struct qcom_icc_node qup1_core_master = { + .name = "qup1_core_master", + .id = SM8450_MASTER_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_QUP_CORE_1 }, +}; + +static struct qcom_icc_node qup2_core_master = { + .name = "qup2_core_master", + .id = SM8450_MASTER_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_QUP_CORE_2 }, +}; + +static struct qcom_icc_node qnm_gemnoc_cnoc = { + .name = "qnm_gemnoc_cnoc", + .id = SM8450_MASTER_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 51, + .links = { SM8450_SLAVE_AHB2PHY_SOUTH, SM8450_SLAVE_AHB2PHY_NORTH, + SM8450_SLAVE_AOSS, SM8450_SLAVE_CAMERA_CFG, + SM8450_SLAVE_CLK_CTL, SM8450_SLAVE_CDSP_CFG, + SM8450_SLAVE_RBCPR_CX_CFG, SM8450_SLAVE_RBCPR_MMCX_CFG, + SM8450_SLAVE_RBCPR_MXA_CFG, SM8450_SLAVE_RBCPR_MXC_CFG, + SM8450_SLAVE_CRYPTO_0_CFG, SM8450_SLAVE_CX_RDPM, + SM8450_SLAVE_DISPLAY_CFG, SM8450_SLAVE_GFX3D_CFG, + SM8450_SLAVE_IMEM_CFG, SM8450_SLAVE_IPA_CFG, + SM8450_SLAVE_IPC_ROUTER_CFG, SM8450_SLAVE_LPASS, + SM8450_SLAVE_CNOC_MSS, SM8450_SLAVE_MX_RDPM, + SM8450_SLAVE_PCIE_0_CFG, SM8450_SLAVE_PCIE_1_CFG, + SM8450_SLAVE_PDM, SM8450_SLAVE_PIMEM_CFG, + SM8450_SLAVE_PRNG, SM8450_SLAVE_QDSS_CFG, + SM8450_SLAVE_QSPI_0, SM8450_SLAVE_QUP_0, + SM8450_SLAVE_QUP_1, SM8450_SLAVE_QUP_2, + SM8450_SLAVE_SDCC_2, SM8450_SLAVE_SDCC_4, + SM8450_SLAVE_SPSS_CFG, SM8450_SLAVE_TCSR, + SM8450_SLAVE_TLMM, SM8450_SLAVE_TME_CFG, + SM8450_SLAVE_UFS_MEM_CFG, SM8450_SLAVE_USB3_0, + SM8450_SLAVE_VENUS_CFG, SM8450_SLAVE_VSENSE_CTRL_CFG, + SM8450_SLAVE_A1NOC_CFG, SM8450_SLAVE_A2NOC_CFG, + SM8450_SLAVE_DDRSS_CFG, SM8450_SLAVE_CNOC_MNOC_CFG, + SM8450_SLAVE_PCIE_ANOC_CFG, SM8450_SLAVE_SNOC_CFG, + SM8450_SLAVE_IMEM, SM8450_SLAVE_PIMEM, + SM8450_SLAVE_SERVICE_CNOC, SM8450_SLAVE_QDSS_STM, + SM8450_SLAVE_TCU }, +}; + +static struct qcom_icc_node qnm_gemnoc_pcie = { + .name = "qnm_gemnoc_pcie", + .id = SM8450_MASTER_GEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8450_SLAVE_PCIE_0, SM8450_SLAVE_PCIE_1 }, +}; + +static struct qcom_icc_node alm_gpu_tcu = { + .name = "alm_gpu_tcu", + .id = SM8450_MASTER_GPU_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node alm_sys_tcu = { + .name = "alm_sys_tcu", + .id = SM8450_MASTER_SYS_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 2, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node chm_apps = { + .name = "chm_apps", + .id = SM8450_MASTER_APPSS_PROC, + .channels = 3, + .buswidth = 32, + .num_links = 3, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC, + SM8450_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_gpu = { + .name = "qnm_gpu", + .id = SM8450_MASTER_GFX3D, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mdsp = { + .name = "qnm_mdsp", + .id = SM8450_MASTER_MSS_PROC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC, + SM8450_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf = { + .name = "qnm_mnoc_hf", + .id = SM8450_MASTER_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_mnoc_sf = { + .name = "qnm_mnoc_sf", + .id = SM8450_MASTER_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_nsp_gemnoc = { + .name = "qnm_nsp_gemnoc", + .id = SM8450_MASTER_COMPUTE_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 2, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_pcie = { + .name = "qnm_pcie", + .id = SM8450_MASTER_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 2, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_gc = { + .name = "qnm_snoc_gc", + .id = SM8450_MASTER_SNOC_GC_MEM_NOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_LLCC }, +}; + +static struct qcom_icc_node qnm_snoc_sf = { + .name = "qnm_snoc_sf", + .id = SM8450_MASTER_SNOC_SF_MEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 3, + .links = { SM8450_SLAVE_GEM_NOC_CNOC, SM8450_SLAVE_LLCC, + SM8450_SLAVE_MEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qhm_config_noc = { + .name = "qhm_config_noc", + .id = SM8450_MASTER_CNOC_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 6, + .links = { SM8450_SLAVE_LPASS_CORE_CFG, SM8450_SLAVE_LPASS_LPI_CFG, + SM8450_SLAVE_LPASS_MPU_CFG, SM8450_SLAVE_LPASS_TOP_CFG, + SM8450_SLAVE_SERVICES_LPASS_AML_NOC, SM8450_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qxm_lpass_dsp = { + .name = "qxm_lpass_dsp", + .id = SM8450_MASTER_LPASS_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 4, + .links = { SM8450_SLAVE_LPASS_TOP_CFG, SM8450_SLAVE_LPASS_SNOC, + SM8450_SLAVE_SERVICES_LPASS_AML_NOC, SM8450_SLAVE_SERVICE_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node llcc_mc = { + .name = "llcc_mc", + .id = SM8450_MASTER_LLCC, + .channels = 4, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_EBI1 }, +}; + +static struct qcom_icc_node qnm_camnoc_hf = { + .name = "qnm_camnoc_hf", + .id = SM8450_MASTER_CAMNOC_HF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_icp = { + .name = "qnm_camnoc_icp", + .id = SM8450_MASTER_CAMNOC_ICP, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_camnoc_sf = { + .name = "qnm_camnoc_sf", + .id = SM8450_MASTER_CAMNOC_SF, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mdp = { + .name = "qnm_mdp", + .id = SM8450_MASTER_MDP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_mnoc_cfg = { + .name = "qnm_mnoc_cfg", + .id = SM8450_MASTER_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SERVICE_MNOC }, +}; + +static struct qcom_icc_node qnm_rot = { + .name = "qnm_rot", + .id = SM8450_MASTER_ROTATOR, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_vapss_hcp = { + .name = "qnm_vapss_hcp", + .id = SM8450_MASTER_CDSP_HCP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video = { + .name = "qnm_video", + .id = SM8450_MASTER_VIDEO, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cv_cpu = { + .name = "qnm_video_cv_cpu", + .id = SM8450_MASTER_VIDEO_CV_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_cvp = { + .name = "qnm_video_cvp", + .id = SM8450_MASTER_VIDEO_PROC, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_video_v_cpu = { + .name = "qnm_video_v_cpu", + .id = SM8450_MASTER_VIDEO_V_PROC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node qhm_nsp_noc_config = { + .name = "qhm_nsp_noc_config", + .id = SM8450_MASTER_CDSP_NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SERVICE_NSP_NOC }, +}; + +static struct qcom_icc_node qxm_nsp = { + .name = "qxm_nsp", + .id = SM8450_MASTER_CDSP_PROC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_CDSP_MEM_NOC }, +}; + +static struct qcom_icc_node qnm_pcie_anoc_cfg = { + .name = "qnm_pcie_anoc_cfg", + .id = SM8450_MASTER_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SERVICE_PCIE_ANOC }, +}; + +static struct qcom_icc_node xm_pcie3_0 = { + .name = "xm_pcie3_0", + .id = SM8450_MASTER_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node xm_pcie3_1 = { + .name = "xm_pcie3_1", + .id = SM8450_MASTER_PCIE_1, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node qhm_gic = { + .name = "qhm_gic", + .id = SM8450_MASTER_GIC_AHB, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre1_noc = { + .name = "qnm_aggre1_noc", + .id = SM8450_MASTER_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_aggre2_noc = { + .name = "qnm_aggre2_noc", + .id = SM8450_MASTER_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_lpass_noc = { + .name = "qnm_lpass_noc", + .id = SM8450_MASTER_LPASS_ANOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_SLAVE_SNOC_GEM_NOC_SF }, +}; + +static struct qcom_icc_node qnm_snoc_cfg = { + .name = "qnm_snoc_cfg", + .id = SM8450_MASTER_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_SERVICE_SNOC }, +}; + +static struct qcom_icc_node qxm_pimem = { + .name = "qxm_pimem", + .id = SM8450_MASTER_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static struct qcom_icc_node xm_gic = { + .name = "xm_gic", + .id = SM8450_MASTER_GIC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_SLAVE_SNOC_GEM_NOC_GC }, +}; + +static struct qcom_icc_node qnm_mnoc_hf_disp = { + .name = "qnm_mnoc_hf_disp", + .id = SM8450_MASTER_MNOC_HF_MEM_NOC_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_LLCC_DISP }, +}; + +static struct qcom_icc_node qnm_mnoc_sf_disp = { + .name = "qnm_mnoc_sf_disp", + .id = SM8450_MASTER_MNOC_SF_MEM_NOC_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_LLCC_DISP }, +}; + +static struct qcom_icc_node qnm_pcie_disp = { + .name = "qnm_pcie_disp", + .id = SM8450_MASTER_ANOC_PCIE_GEM_NOC_DISP, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_SLAVE_LLCC_DISP }, +}; + +static struct qcom_icc_node llcc_mc_disp = { + .name = "llcc_mc_disp", + .id = SM8450_MASTER_LLCC_DISP, + .channels = 4, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_SLAVE_EBI1_DISP }, +}; + +static struct qcom_icc_node qnm_mdp_disp = { + .name = "qnm_mdp_disp", + .id = SM8450_MASTER_MDP_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_HF_MEM_NOC_DISP }, +}; + +static struct qcom_icc_node qnm_rot_disp = { + .name = "qnm_rot_disp", + .id = SM8450_MASTER_ROTATOR_DISP, + .channels = 1, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_SLAVE_MNOC_SF_MEM_NOC_DISP }, +}; + +static struct qcom_icc_node qns_a1noc_snoc = { + .name = "qns_a1noc_snoc", + .id = SM8450_SLAVE_A1NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_A1NOC_SNOC }, +}; + +static struct qcom_icc_node srvc_aggre1_noc = { + .name = "srvc_aggre1_noc", + .id = SM8450_SLAVE_SERVICE_A1NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_a2noc_snoc = { + .name = "qns_a2noc_snoc", + .id = SM8450_SLAVE_A2NOC_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_A2NOC_SNOC }, +}; + +static struct qcom_icc_node srvc_aggre2_noc = { + .name = "srvc_aggre2_noc", + .id = SM8450_SLAVE_SERVICE_A2NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup0_core_slave = { + .name = "qup0_core_slave", + .id = SM8450_SLAVE_QUP_CORE_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup1_core_slave = { + .name = "qup1_core_slave", + .id = SM8450_SLAVE_QUP_CORE_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qup2_core_slave = { + .name = "qup2_core_slave", + .id = SM8450_SLAVE_QUP_CORE_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy0 = { + .name = "qhs_ahb2phy0", + .id = SM8450_SLAVE_AHB2PHY_SOUTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ahb2phy1 = { + .name = "qhs_ahb2phy1", + .id = SM8450_SLAVE_AHB2PHY_NORTH, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_aoss = { + .name = "qhs_aoss", + .id = SM8450_SLAVE_AOSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_camera_cfg = { + .name = "qhs_camera_cfg", + .id = SM8450_SLAVE_CAMERA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_clk_ctl = { + .name = "qhs_clk_ctl", + .id = SM8450_SLAVE_CLK_CTL, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_compute_cfg = { + .name = "qhs_compute_cfg", + .id = SM8450_SLAVE_CDSP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { MASTER_CDSP_NOC_CFG }, +}; + +static struct qcom_icc_node qhs_cpr_cx = { + .name = "qhs_cpr_cx", + .id = SM8450_SLAVE_RBCPR_CX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mmcx = { + .name = "qhs_cpr_mmcx", + .id = SM8450_SLAVE_RBCPR_MMCX_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxa = { + .name = "qhs_cpr_mxa", + .id = SM8450_SLAVE_RBCPR_MXA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cpr_mxc = { + .name = "qhs_cpr_mxc", + .id = SM8450_SLAVE_RBCPR_MXC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_crypto0_cfg = { + .name = "qhs_crypto0_cfg", + .id = SM8450_SLAVE_CRYPTO_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_cx_rdpm = { + .name = "qhs_cx_rdpm", + .id = SM8450_SLAVE_CX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_display_cfg = { + .name = "qhs_display_cfg", + .id = SM8450_SLAVE_DISPLAY_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_gpuss_cfg = { + .name = "qhs_gpuss_cfg", + .id = SM8450_SLAVE_GFX3D_CFG, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_imem_cfg = { + .name = "qhs_imem_cfg", + .id = SM8450_SLAVE_IMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipa = { + .name = "qhs_ipa", + .id = SM8450_SLAVE_IPA_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ipc_router = { + .name = "qhs_ipc_router", + .id = SM8450_SLAVE_IPC_ROUTER_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_cfg = { + .name = "qhs_lpass_cfg", + .id = SM8450_SLAVE_LPASS, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { MASTER_CNOC_LPASS_AG_NOC }, +}; + +static struct qcom_icc_node qhs_mss_cfg = { + .name = "qhs_mss_cfg", + .id = SM8450_SLAVE_CNOC_MSS, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_mx_rdpm = { + .name = "qhs_mx_rdpm", + .id = SM8450_SLAVE_MX_RDPM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie0_cfg = { + .name = "qhs_pcie0_cfg", + .id = SM8450_SLAVE_PCIE_0_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pcie1_cfg = { + .name = "qhs_pcie1_cfg", + .id = SM8450_SLAVE_PCIE_1_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pdm = { + .name = "qhs_pdm", + .id = SM8450_SLAVE_PDM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_pimem_cfg = { + .name = "qhs_pimem_cfg", + .id = SM8450_SLAVE_PIMEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_prng = { + .name = "qhs_prng", + .id = SM8450_SLAVE_PRNG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qdss_cfg = { + .name = "qhs_qdss_cfg", + .id = SM8450_SLAVE_QDSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qspi = { + .name = "qhs_qspi", + .id = SM8450_SLAVE_QSPI_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup0 = { + .name = "qhs_qup0", + .id = SM8450_SLAVE_QUP_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup1 = { + .name = "qhs_qup1", + .id = SM8450_SLAVE_QUP_1, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_qup2 = { + .name = "qhs_qup2", + .id = SM8450_SLAVE_QUP_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc2 = { + .name = "qhs_sdc2", + .id = SM8450_SLAVE_SDCC_2, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_sdc4 = { + .name = "qhs_sdc4", + .id = SM8450_SLAVE_SDCC_4, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_spss_cfg = { + .name = "qhs_spss_cfg", + .id = SM8450_SLAVE_SPSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tcsr = { + .name = "qhs_tcsr", + .id = SM8450_SLAVE_TCSR, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tlmm = { + .name = "qhs_tlmm", + .id = SM8450_SLAVE_TLMM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_tme_cfg = { + .name = "qhs_tme_cfg", + .id = SM8450_SLAVE_TME_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_ufs_mem_cfg = { + .name = "qhs_ufs_mem_cfg", + .id = SM8450_SLAVE_UFS_MEM_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_usb3_0 = { + .name = "qhs_usb3_0", + .id = SM8450_SLAVE_USB3_0, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_venus_cfg = { + .name = "qhs_venus_cfg", + .id = SM8450_SLAVE_VENUS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_vsense_ctrl_cfg = { + .name = "qhs_vsense_ctrl_cfg", + .id = SM8450_SLAVE_VSENSE_CTRL_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_a1_noc_cfg = { + .name = "qns_a1_noc_cfg", + .id = SM8450_SLAVE_A1NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_MASTER_A1NOC_CFG }, +}; + +static struct qcom_icc_node qns_a2_noc_cfg = { + .name = "qns_a2_noc_cfg", + .id = SM8450_SLAVE_A2NOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_MASTER_A2NOC_CFG }, +}; + +static struct qcom_icc_node qns_ddrss_cfg = { + .name = "qns_ddrss_cfg", + .id = SM8450_SLAVE_DDRSS_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + //FIXME where is link +}; + +static struct qcom_icc_node qns_mnoc_cfg = { + .name = "qns_mnoc_cfg", + .id = SM8450_SLAVE_CNOC_MNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_MASTER_CNOC_MNOC_CFG }, +}; + +static struct qcom_icc_node qns_pcie_anoc_cfg = { + .name = "qns_pcie_anoc_cfg", + .id = SM8450_SLAVE_PCIE_ANOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_MASTER_PCIE_ANOC_CFG }, +}; + +static struct qcom_icc_node qns_snoc_cfg = { + .name = "qns_snoc_cfg", + .id = SM8450_SLAVE_SNOC_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 1, + .links = { SM8450_MASTER_SNOC_CFG }, +}; + +static struct qcom_icc_node qxs_imem = { + .name = "qxs_imem", + .id = SM8450_SLAVE_IMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qxs_pimem = { + .name = "qxs_pimem", + .id = SM8450_SLAVE_PIMEM, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_cnoc = { + .name = "srvc_cnoc", + .id = SM8450_SLAVE_SERVICE_CNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_0 = { + .name = "xs_pcie_0", + .id = SM8450_SLAVE_PCIE_0, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_pcie_1 = { + .name = "xs_pcie_1", + .id = SM8450_SLAVE_PCIE_1, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node xs_qdss_stm = { + .name = "xs_qdss_stm", + .id = SM8450_SLAVE_QDSS_STM, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node xs_sys_tcu_cfg = { + .name = "xs_sys_tcu_cfg", + .id = SM8450_SLAVE_TCU, + .channels = 1, + .buswidth = 8, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gem_noc_cnoc = { + .name = "qns_gem_noc_cnoc", + .id = SM8450_SLAVE_GEM_NOC_CNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_GEM_NOC_CNOC }, +}; + +static struct qcom_icc_node qns_llcc = { + .name = "qns_llcc", + .id = SM8450_SLAVE_LLCC, + .channels = 4, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_LLCC }, +}; + +static struct qcom_icc_node qns_pcie = { + .name = "qns_pcie", + .id = SM8450_SLAVE_MEM_NOC_PCIE_SNOC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_MASTER_GEM_NOC_PCIE_SNOC }, +}; + +static struct qcom_icc_node qhs_lpass_core = { + .name = "qhs_lpass_core", + .id = SM8450_SLAVE_LPASS_CORE_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_lpi = { + .name = "qhs_lpass_lpi", + .id = SM8450_SLAVE_LPASS_LPI_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_mpu = { + .name = "qhs_lpass_mpu", + .id = SM8450_SLAVE_LPASS_MPU_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qhs_lpass_top = { + .name = "qhs_lpass_top", + .id = SM8450_SLAVE_LPASS_TOP_CFG, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_sysnoc = { + .name = "qns_sysnoc", + .id = SM8450_SLAVE_LPASS_SNOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_LPASS_ANOC }, +}; + +static struct qcom_icc_node srvc_niu_aml_noc = { + .name = "srvc_niu_aml_noc", + .id = SM8450_SLAVE_SERVICES_LPASS_AML_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node srvc_niu_lpass_agnoc = { + .name = "srvc_niu_lpass_agnoc", + .id = SM8450_SLAVE_SERVICE_LPASS_AG_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node ebi = { + .name = "ebi", + .id = SM8450_SLAVE_EBI1, + .channels = 4, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf = { + .name = "qns_mem_noc_hf", + .id = SM8450_SLAVE_MNOC_HF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_MASTER_MNOC_HF_MEM_NOC }, +}; + +static struct qcom_icc_node qns_mem_noc_sf = { + .name = "qns_mem_noc_sf", + .id = SM8450_SLAVE_MNOC_SF_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_MASTER_MNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_mnoc = { + .name = "srvc_mnoc", + .id = SM8450_SLAVE_SERVICE_MNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_nsp_gemnoc = { + .name = "qns_nsp_gemnoc", + .id = SM8450_SLAVE_CDSP_MEM_NOC, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_MASTER_COMPUTE_NOC }, +}; + +static struct qcom_icc_node service_nsp_noc = { + .name = "service_nsp_noc", + .id = SM8450_SLAVE_SERVICE_NSP_NOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_pcie_mem_noc = { + .name = "qns_pcie_mem_noc", + .id = SM8450_SLAVE_ANOC_PCIE_GEM_NOC, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_ANOC_PCIE_GEM_NOC }, +}; + +static struct qcom_icc_node srvc_pcie_aggre_noc = { + .name = "srvc_pcie_aggre_noc", + .id = SM8450_SLAVE_SERVICE_PCIE_ANOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_gemnoc_gc = { + .name = "qns_gemnoc_gc", + .id = SM8450_SLAVE_SNOC_GEM_NOC_GC, + .channels = 1, + .buswidth = 8, + .num_links = 1, + .links = { SM8450_MASTER_SNOC_GC_MEM_NOC }, +}; + +static struct qcom_icc_node qns_gemnoc_sf = { + .name = "qns_gemnoc_sf", + .id = SM8450_SLAVE_SNOC_GEM_NOC_SF, + .channels = 1, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_SNOC_SF_MEM_NOC }, +}; + +static struct qcom_icc_node srvc_snoc = { + .name = "srvc_snoc", + .id = SM8450_SLAVE_SERVICE_SNOC, + .channels = 1, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_llcc_disp = { + .name = "qns_llcc_disp", + .id = SM8450_SLAVE_LLCC_DISP, + .channels = 4, + .buswidth = 16, + .num_links = 1, + .links = { SM8450_MASTER_LLCC_DISP }, +}; + +static struct qcom_icc_node ebi_disp = { + .name = "ebi_disp", + .id = SM8450_SLAVE_EBI1_DISP, + .channels = 4, + .buswidth = 4, + .num_links = 0, +}; + +static struct qcom_icc_node qns_mem_noc_hf_disp = { + .name = "qns_mem_noc_hf_disp", + .id = SM8450_SLAVE_MNOC_HF_MEM_NOC_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_MASTER_MNOC_HF_MEM_NOC_DISP }, +}; + +static struct qcom_icc_node qns_mem_noc_sf_disp = { + .name = "qns_mem_noc_sf_disp", + .id = SM8450_SLAVE_MNOC_SF_MEM_NOC_DISP, + .channels = 2, + .buswidth = 32, + .num_links = 1, + .links = { SM8450_MASTER_MNOC_SF_MEM_NOC_DISP }, +}; + +static struct qcom_icc_bcm bcm_acv = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_ce0 = { + .name = "CE0", + .num_nodes = 1, + .nodes = { &qxm_crypto }, +}; + +static struct qcom_icc_bcm bcm_cn0 = { + .name = "CN0", + .keepalive = true, + .num_nodes = 55, + .nodes = { &qnm_gemnoc_cnoc, &qnm_gemnoc_pcie, + &qhs_ahb2phy0, &qhs_ahb2phy1, + &qhs_aoss, &qhs_camera_cfg, + &qhs_clk_ctl, &qhs_compute_cfg, + &qhs_cpr_cx, &qhs_cpr_mmcx, + &qhs_cpr_mxa, &qhs_cpr_mxc, + &qhs_crypto0_cfg, &qhs_cx_rdpm, + &qhs_display_cfg, &qhs_gpuss_cfg, + &qhs_imem_cfg, &qhs_ipa, + &qhs_ipc_router, &qhs_lpass_cfg, + &qhs_mss_cfg, &qhs_mx_rdpm, + &qhs_pcie0_cfg, &qhs_pcie1_cfg, + &qhs_pdm, &qhs_pimem_cfg, + &qhs_prng, &qhs_qdss_cfg, + &qhs_qspi, &qhs_qup0, + &qhs_qup1, &qhs_qup2, + &qhs_sdc2, &qhs_sdc4, + &qhs_spss_cfg, &qhs_tcsr, + &qhs_tlmm, &qhs_tme_cfg, + &qhs_ufs_mem_cfg, &qhs_usb3_0, + &qhs_venus_cfg, &qhs_vsense_ctrl_cfg, + &qns_a1_noc_cfg, &qns_a2_noc_cfg, + &qns_ddrss_cfg, &qns_mnoc_cfg, + &qns_pcie_anoc_cfg, &qns_snoc_cfg, + &qxs_imem, &qxs_pimem, + &srvc_cnoc, &xs_pcie_0, + &xs_pcie_1, &xs_qdss_stm, + &xs_sys_tcu_cfg }, +}; + +static struct qcom_icc_bcm bcm_co0 = { + .name = "CO0", + .num_nodes = 2, + .nodes = { &qxm_nsp, &qns_nsp_gemnoc }, +}; + +static struct qcom_icc_bcm bcm_mc0 = { + .name = "MC0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &ebi }, +}; + +static struct qcom_icc_bcm bcm_mm0 = { + .name = "MM0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf }, +}; + +static struct qcom_icc_bcm bcm_mm1 = { + .name = "MM1", + .num_nodes = 12, + .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp, + &qnm_camnoc_sf, &qnm_mdp, + &qnm_mnoc_cfg, &qnm_rot, + &qnm_vapss_hcp, &qnm_video, + &qnm_video_cv_cpu, &qnm_video_cvp, + &qnm_video_v_cpu, &qns_mem_noc_sf }, +}; + +static struct qcom_icc_bcm bcm_qup0 = { + .name = "QUP0", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup0_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup1 = { + .name = "QUP1", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup1_core_slave }, +}; + +static struct qcom_icc_bcm bcm_qup2 = { + .name = "QUP2", + .keepalive = true, + .vote_scale = 1, + .num_nodes = 1, + .nodes = { &qup2_core_slave }, +}; + +static struct qcom_icc_bcm bcm_sh0 = { + .name = "SH0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_llcc }, +}; + +static struct qcom_icc_bcm bcm_sh1 = { + .name = "SH1", + .num_nodes = 7, + .nodes = { &alm_gpu_tcu, &alm_sys_tcu, + &qnm_nsp_gemnoc, &qnm_pcie, + &qnm_snoc_gc, &qns_gem_noc_cnoc, + &qns_pcie }, +}; + +static struct qcom_icc_bcm bcm_sn0 = { + .name = "SN0", + .keepalive = true, + .num_nodes = 1, + .nodes = { &qns_gemnoc_sf }, +}; + +static struct qcom_icc_bcm bcm_sn1 = { + .name = "SN1", + .num_nodes = 4, + .nodes = { &qhm_gic, &qxm_pimem, + &xm_gic, &qns_gemnoc_gc }, +}; + +static struct qcom_icc_bcm bcm_sn2 = { + .name = "SN2", + .num_nodes = 1, + .nodes = { &qnm_aggre1_noc }, +}; + +static struct qcom_icc_bcm bcm_sn3 = { + .name = "SN3", + .num_nodes = 1, + .nodes = { &qnm_aggre2_noc }, +}; + +static struct qcom_icc_bcm bcm_sn4 = { + .name = "SN4", + .num_nodes = 1, + .nodes = { &qnm_lpass_noc }, +}; + +static struct qcom_icc_bcm bcm_sn7 = { + .name = "SN7", + .num_nodes = 1, + .nodes = { &qns_pcie_mem_noc }, +}; + +static struct qcom_icc_bcm bcm_acv_disp = { + .name = "ACV", + .num_nodes = 1, + .nodes = { &ebi_disp }, +}; + +static struct qcom_icc_bcm bcm_mc0_disp = { + .name = "MC0", + .num_nodes = 1, + .nodes = { &ebi_disp }, +}; + +static struct qcom_icc_bcm bcm_mm0_disp = { + .name = "MM0", + .num_nodes = 1, + .nodes = { &qns_mem_noc_hf_disp }, +}; + +static struct qcom_icc_bcm bcm_mm1_disp = { + .name = "MM1", + .num_nodes = 3, + .nodes = { &qnm_mdp_disp, &qnm_rot_disp, + &qns_mem_noc_sf_disp }, +}; + +static struct qcom_icc_bcm bcm_sh0_disp = { + .name = "SH0", + .num_nodes = 1, + .nodes = { &qns_llcc_disp }, +}; + +static struct qcom_icc_bcm bcm_sh1_disp = { + .name = "SH1", + .num_nodes = 1, + .nodes = { &qnm_pcie_disp }, +}; + +static struct qcom_icc_bcm *aggre1_noc_bcms[] = { +}; + +static struct qcom_icc_node *aggre1_noc_nodes[] = { + [MASTER_QSPI_0] = &qhm_qspi, + [MASTER_QUP_1] = &qhm_qup1, + [MASTER_A1NOC_CFG] = &qnm_a1noc_cfg, + [MASTER_SDCC_4] = &xm_sdc4, + [MASTER_UFS_MEM] = &xm_ufs_mem, + [MASTER_USB3_0] = &xm_usb3_0, + [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc, + [SLAVE_SERVICE_A1NOC] = &srvc_aggre1_noc, +}; + +static struct qcom_icc_desc sm8450_aggre1_noc = { + .nodes = aggre1_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre1_noc_nodes), + .bcms = aggre1_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre1_noc_bcms), +}; + +static struct qcom_icc_bcm *aggre2_noc_bcms[] = { + &bcm_ce0, +}; + +static struct qcom_icc_node *aggre2_noc_nodes[] = { + [MASTER_QDSS_BAM] = &qhm_qdss_bam, + [MASTER_QUP_0] = &qhm_qup0, + [MASTER_QUP_2] = &qhm_qup2, + [MASTER_A2NOC_CFG] = &qnm_a2noc_cfg, + [MASTER_CRYPTO] = &qxm_crypto, + [MASTER_IPA] = &qxm_ipa, + [MASTER_SENSORS_PROC] = &qxm_sensorss_q6, + [MASTER_SP] = &qxm_sp, + [MASTER_QDSS_ETR] = &xm_qdss_etr_0, + [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1, + [MASTER_SDCC_2] = &xm_sdc2, + [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc, + [SLAVE_SERVICE_A2NOC] = &srvc_aggre2_noc, +}; + +static struct qcom_icc_desc sm8450_aggre2_noc = { + .nodes = aggre2_noc_nodes, + .num_nodes = ARRAY_SIZE(aggre2_noc_nodes), + .bcms = aggre2_noc_bcms, + .num_bcms = ARRAY_SIZE(aggre2_noc_bcms), +}; + +static struct qcom_icc_bcm *clk_virt_bcms[] = { + &bcm_qup0, + &bcm_qup1, + &bcm_qup2, +}; + +static struct qcom_icc_node *clk_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &qup0_core_master, + [MASTER_QUP_CORE_1] = &qup1_core_master, + [MASTER_QUP_CORE_2] = &qup2_core_master, + [SLAVE_QUP_CORE_0] = &qup0_core_slave, + [SLAVE_QUP_CORE_1] = &qup1_core_slave, + [SLAVE_QUP_CORE_2] = &qup2_core_slave, +}; + +static struct qcom_icc_desc sm8450_clk_virt = { + .nodes = clk_virt_nodes, + .num_nodes = ARRAY_SIZE(clk_virt_nodes), + .bcms = clk_virt_bcms, + .num_bcms = ARRAY_SIZE(clk_virt_bcms), +}; + +static struct qcom_icc_bcm *config_noc_bcms[] = { + &bcm_cn0, +}; + +static struct qcom_icc_node *config_noc_nodes[] = { + [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc, + [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie, + [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0, + [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1, + [SLAVE_AOSS] = &qhs_aoss, + [SLAVE_CAMERA_CFG] = &qhs_camera_cfg, + [SLAVE_CLK_CTL] = &qhs_clk_ctl, + [SLAVE_CDSP_CFG] = &qhs_compute_cfg, + [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx, + [SLAVE_RBCPR_MMCX_CFG] = &qhs_cpr_mmcx, + [SLAVE_RBCPR_MXA_CFG] = &qhs_cpr_mxa, + [SLAVE_RBCPR_MXC_CFG] = &qhs_cpr_mxc, + [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg, + [SLAVE_CX_RDPM] = &qhs_cx_rdpm, + [SLAVE_DISPLAY_CFG] = &qhs_display_cfg, + [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg, + [SLAVE_IMEM_CFG] = &qhs_imem_cfg, + [SLAVE_IPA_CFG] = &qhs_ipa, + [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router, + [SLAVE_LPASS] = &qhs_lpass_cfg, + [SLAVE_CNOC_MSS] = &qhs_mss_cfg, + [SLAVE_MX_RDPM] = &qhs_mx_rdpm, + [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg, + [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg, + [SLAVE_PDM] = &qhs_pdm, + [SLAVE_PIMEM_CFG] = &qhs_pimem_cfg, + [SLAVE_PRNG] = &qhs_prng, + [SLAVE_QDSS_CFG] = &qhs_qdss_cfg, + [SLAVE_QSPI_0] = &qhs_qspi, + [SLAVE_QUP_0] = &qhs_qup0, + [SLAVE_QUP_1] = &qhs_qup1, + [SLAVE_QUP_2] = &qhs_qup2, + [SLAVE_SDCC_2] = &qhs_sdc2, + [SLAVE_SDCC_4] = &qhs_sdc4, + [SLAVE_SPSS_CFG] = &qhs_spss_cfg, + [SLAVE_TCSR] = &qhs_tcsr, + [SLAVE_TLMM] = &qhs_tlmm, + [SLAVE_TME_CFG] = &qhs_tme_cfg, + [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg, + [SLAVE_USB3_0] = &qhs_usb3_0, + [SLAVE_VENUS_CFG] = &qhs_venus_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg, + [SLAVE_A1NOC_CFG] = &qns_a1_noc_cfg, + [SLAVE_A2NOC_CFG] = &qns_a2_noc_cfg, + [SLAVE_DDRSS_CFG] = &qns_ddrss_cfg, + [SLAVE_CNOC_MNOC_CFG] = &qns_mnoc_cfg, + [SLAVE_PCIE_ANOC_CFG] = &qns_pcie_anoc_cfg, + [SLAVE_SNOC_CFG] = &qns_snoc_cfg, + [SLAVE_IMEM] = &qxs_imem, + [SLAVE_PIMEM] = &qxs_pimem, + [SLAVE_SERVICE_CNOC] = &srvc_cnoc, + [SLAVE_PCIE_0] = &xs_pcie_0, + [SLAVE_PCIE_1] = &xs_pcie_1, + [SLAVE_QDSS_STM] = &xs_qdss_stm, + [SLAVE_TCU] = &xs_sys_tcu_cfg, +}; + +static struct qcom_icc_desc sm8450_config_noc = { + .nodes = config_noc_nodes, + .num_nodes = ARRAY_SIZE(config_noc_nodes), + .bcms = config_noc_bcms, + .num_bcms = ARRAY_SIZE(config_noc_bcms), +}; + +static struct qcom_icc_bcm *gem_noc_bcms[] = { + &bcm_sh0, + &bcm_sh1, + &bcm_sh0_disp, + &bcm_sh1_disp, +}; + +static struct qcom_icc_node *gem_noc_nodes[] = { + [MASTER_GPU_TCU] = &alm_gpu_tcu, + [MASTER_SYS_TCU] = &alm_sys_tcu, + [MASTER_APPSS_PROC] = &chm_apps, + [MASTER_GFX3D] = &qnm_gpu, + [MASTER_MSS_PROC] = &qnm_mdsp, + [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf, + [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf, + [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc, + [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie, + [MASTER_SNOC_GC_MEM_NOC] = &qnm_snoc_gc, + [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf, + [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc, + [SLAVE_LLCC] = &qns_llcc, + [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie, + [MASTER_MNOC_HF_MEM_NOC_DISP] = &qnm_mnoc_hf_disp, + [MASTER_MNOC_SF_MEM_NOC_DISP] = &qnm_mnoc_sf_disp, + [MASTER_ANOC_PCIE_GEM_NOC_DISP] = &qnm_pcie_disp, + [SLAVE_LLCC_DISP] = &qns_llcc_disp, +}; + +static struct qcom_icc_desc sm8450_gem_noc = { + .nodes = gem_noc_nodes, + .num_nodes = ARRAY_SIZE(gem_noc_nodes), + .bcms = gem_noc_bcms, + .num_bcms = ARRAY_SIZE(gem_noc_bcms), +}; + +static struct qcom_icc_bcm *lpass_ag_noc_bcms[] = { +}; + +static struct qcom_icc_node *lpass_ag_noc_nodes[] = { + [MASTER_CNOC_LPASS_AG_NOC] = &qhm_config_noc, + [MASTER_LPASS_PROC] = &qxm_lpass_dsp, + [SLAVE_LPASS_CORE_CFG] = &qhs_lpass_core, + [SLAVE_LPASS_LPI_CFG] = &qhs_lpass_lpi, + [SLAVE_LPASS_MPU_CFG] = &qhs_lpass_mpu, + [SLAVE_LPASS_TOP_CFG] = &qhs_lpass_top, + [SLAVE_LPASS_SNOC] = &qns_sysnoc, + [SLAVE_SERVICES_LPASS_AML_NOC] = &srvc_niu_aml_noc, + [SLAVE_SERVICE_LPASS_AG_NOC] = &srvc_niu_lpass_agnoc, +}; + +static struct qcom_icc_desc sm8450_lpass_ag_noc = { + .nodes = lpass_ag_noc_nodes, + .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes), + .bcms = lpass_ag_noc_bcms, + .num_bcms = ARRAY_SIZE(lpass_ag_noc_bcms), +}; + +static struct qcom_icc_bcm *mc_virt_bcms[] = { + &bcm_acv, + &bcm_mc0, + &bcm_acv_disp, + &bcm_mc0_disp, +}; + +static struct qcom_icc_node *mc_virt_nodes[] = { + [MASTER_LLCC] = &llcc_mc, + [SLAVE_EBI1] = &ebi, + [MASTER_LLCC_DISP] = &llcc_mc_disp, + [SLAVE_EBI1_DISP] = &ebi_disp, +}; + +static struct qcom_icc_desc sm8450_mc_virt = { + .nodes = mc_virt_nodes, + .num_nodes = ARRAY_SIZE(mc_virt_nodes), + .bcms = mc_virt_bcms, + .num_bcms = ARRAY_SIZE(mc_virt_bcms), +}; + +static struct qcom_icc_bcm *mmss_noc_bcms[] = { + &bcm_mm0, + &bcm_mm1, + &bcm_mm0_disp, + &bcm_mm1_disp, +}; + +static struct qcom_icc_node *mmss_noc_nodes[] = { + [MASTER_CAMNOC_HF] = &qnm_camnoc_hf, + [MASTER_CAMNOC_ICP] = &qnm_camnoc_icp, + [MASTER_CAMNOC_SF] = &qnm_camnoc_sf, + [MASTER_MDP] = &qnm_mdp, + [MASTER_CNOC_MNOC_CFG] = &qnm_mnoc_cfg, + [MASTER_ROTATOR] = &qnm_rot, + [MASTER_CDSP_HCP] = &qnm_vapss_hcp, + [MASTER_VIDEO] = &qnm_video, + [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu, + [MASTER_VIDEO_PROC] = &qnm_video_cvp, + [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu, + [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf, + [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf, + [SLAVE_SERVICE_MNOC] = &srvc_mnoc, + [MASTER_MDP_DISP] = &qnm_mdp_disp, + [MASTER_ROTATOR_DISP] = &qnm_rot_disp, + [SLAVE_MNOC_HF_MEM_NOC_DISP] = &qns_mem_noc_hf_disp, + [SLAVE_MNOC_SF_MEM_NOC_DISP] = &qns_mem_noc_sf_disp, +}; + +static struct qcom_icc_desc sm8450_mmss_noc = { + .nodes = mmss_noc_nodes, + .num_nodes = ARRAY_SIZE(mmss_noc_nodes), + .bcms = mmss_noc_bcms, + .num_bcms = ARRAY_SIZE(mmss_noc_bcms), +}; + +static struct qcom_icc_bcm *nsp_noc_bcms[] = { + &bcm_co0, +}; + +static struct qcom_icc_node *nsp_noc_nodes[] = { + [MASTER_CDSP_NOC_CFG] = &qhm_nsp_noc_config, + [MASTER_CDSP_PROC] = &qxm_nsp, + [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc, + [SLAVE_SERVICE_NSP_NOC] = &service_nsp_noc, +}; + +static struct qcom_icc_desc sm8450_nsp_noc = { + .nodes = nsp_noc_nodes, + .num_nodes = ARRAY_SIZE(nsp_noc_nodes), + .bcms = nsp_noc_bcms, + .num_bcms = ARRAY_SIZE(nsp_noc_bcms), +}; + +static struct qcom_icc_bcm *pcie_anoc_bcms[] = { + &bcm_sn7, +}; + +static struct qcom_icc_node *pcie_anoc_nodes[] = { + [MASTER_PCIE_ANOC_CFG] = &qnm_pcie_anoc_cfg, + [MASTER_PCIE_0] = &xm_pcie3_0, + [MASTER_PCIE_1] = &xm_pcie3_1, + [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc, + [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc, +}; + +static struct qcom_icc_desc sm8450_pcie_anoc = { + .nodes = pcie_anoc_nodes, + .num_nodes = ARRAY_SIZE(pcie_anoc_nodes), + .bcms = pcie_anoc_bcms, + .num_bcms = ARRAY_SIZE(pcie_anoc_bcms), +}; + +static struct qcom_icc_bcm *system_noc_bcms[] = { + &bcm_sn0, + &bcm_sn1, + &bcm_sn2, + &bcm_sn3, + &bcm_sn4, +}; + +static struct qcom_icc_node *system_noc_nodes[] = { + [MASTER_GIC_AHB] = &qhm_gic, + [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc, + [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc, + [MASTER_LPASS_ANOC] = &qnm_lpass_noc, + [MASTER_SNOC_CFG] = &qnm_snoc_cfg, + [MASTER_PIMEM] = &qxm_pimem, + [MASTER_GIC] = &xm_gic, + [SLAVE_SNOC_GEM_NOC_GC] = &qns_gemnoc_gc, + [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf, + [SLAVE_SERVICE_SNOC] = &srvc_snoc, +}; + +static struct qcom_icc_desc sm8450_system_noc = { + .nodes = system_noc_nodes, + .num_nodes = ARRAY_SIZE(system_noc_nodes), + .bcms = system_noc_bcms, + .num_bcms = ARRAY_SIZE(system_noc_bcms), +}; + +static int qnoc_probe(struct platform_device *pdev) +{ + const struct qcom_icc_desc *desc; + struct icc_onecell_data *data; + struct icc_provider *provider; + struct qcom_icc_node **qnodes; + struct qcom_icc_provider *qp; + struct icc_node *node; + size_t num_nodes, i; + int ret; + + desc = device_get_match_data(&pdev->dev); + if (!desc) + return -EINVAL; + + qnodes = desc->nodes; + num_nodes = desc->num_nodes; + + qp = devm_kzalloc(&pdev->dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + data = devm_kcalloc(&pdev->dev, num_nodes, sizeof(*node), GFP_KERNEL); + if (!data) + return -ENOMEM; + + provider = &qp->provider; + provider->dev = &pdev->dev; + provider->set = qcom_icc_set; + provider->pre_aggregate = qcom_icc_pre_aggregate; + provider->aggregate = qcom_icc_aggregate; + provider->xlate_extended = qcom_icc_xlate_extended; + INIT_LIST_HEAD(&provider->nodes); + provider->data = data; + + qp->dev = &pdev->dev; + qp->bcms = desc->bcms; + qp->num_bcms = desc->num_bcms; + + qp->voter = of_bcm_voter_get(qp->dev, NULL); + if (IS_ERR(qp->voter)) + return PTR_ERR(qp->voter); + + ret = icc_provider_add(provider); + if (ret) { + dev_err(&pdev->dev, "error adding interconnect provider\n"); + return ret; + } + + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); + + for (i = 0; i < num_nodes; i++) { + size_t j; + + if (!qnodes[i]) + continue; + + node = icc_node_create(qnodes[i]->id); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = qnodes[i]->name; + node->data = qnodes[i]; + icc_node_add(node, provider); + + for (j = 0; j < qnodes[i]->num_links; j++) + icc_link_create(node, qnodes[i]->links[j]); + + data->nodes[i] = node; + } + data->num_nodes = num_nodes; + + platform_set_drvdata(pdev, qp); + + return 0; +err: + icc_nodes_remove(provider); + icc_provider_del(provider); + return ret; +} + +static int qnoc_remove(struct platform_device *pdev) +{ + struct qcom_icc_provider *qp = platform_get_drvdata(pdev); + + icc_nodes_remove(&qp->provider); + return icc_provider_del(&qp->provider); +} + +static const struct of_device_id qnoc_of_match[] = { + { .compatible = "qcom,sm8450-aggre1-noc", + .data = &sm8450_aggre1_noc}, + { .compatible = "qcom,sm8450-aggre2-noc", + .data = &sm8450_aggre2_noc}, + { .compatible = "qcom,sm8450-clk-virt", + .data = &sm8450_clk_virt}, + { .compatible = "qcom,sm8450-config-noc", + .data = &sm8450_config_noc}, + { .compatible = "qcom,sm8450-gem-noc", + .data = &sm8450_gem_noc}, + { .compatible = "qcom,sm8450-lpass-ag-noc", + .data = &sm8450_lpass_ag_noc}, + { .compatible = "qcom,sm8450-mc-virt", + .data = &sm8450_mc_virt}, + { .compatible = "qcom,sm8450-mmss-noc", + .data = &sm8450_mmss_noc}, + { .compatible = "qcom,sm8450-nsp-noc", + .data = &sm8450_nsp_noc}, + { .compatible = "qcom,sm8450-pcie-anoc", + .data = &sm8450_pcie_anoc}, + { .compatible = "qcom,sm8450-system-noc", + .data = &sm8450_system_noc}, + { } +}; +MODULE_DEVICE_TABLE(of, qnoc_of_match); + +static struct platform_driver qnoc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-sm8450", + .of_match_table = qnoc_of_match, + }, +}; + +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); + +MODULE_DESCRIPTION("sm8450 NoC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/interconnect/qcom/sm8450.h b/drivers/interconnect/qcom/sm8450.h new file mode 100644 index 000000000000..a5790ec6767b --- /dev/null +++ b/drivers/interconnect/qcom/sm8450.h @@ -0,0 +1,169 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * SM8450 interconnect IDs + * + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + * Copyright (c) 2021, Linaro Limited + */ + +#ifndef __DRIVERS_INTERCONNECT_QCOM_SM8450_H +#define __DRIVERS_INTERCONNECT_QCOM_SM8450_H + +#define SM8450_MASTER_GPU_TCU 0 +#define SM8450_MASTER_SYS_TCU 1 +#define SM8450_MASTER_APPSS_PROC 2 +#define SM8450_MASTER_LLCC 3 +#define SM8450_MASTER_CNOC_LPASS_AG_NOC 4 +#define SM8450_MASTER_GIC_AHB 5 +#define SM8450_MASTER_CDSP_NOC_CFG 6 +#define SM8450_MASTER_QDSS_BAM 7 +#define SM8450_MASTER_QSPI_0 8 +#define SM8450_MASTER_QUP_0 9 +#define SM8450_MASTER_QUP_1 10 +#define SM8450_MASTER_QUP_2 11 +#define SM8450_MASTER_A1NOC_CFG 12 +#define SM8450_MASTER_A2NOC_CFG 13 +#define SM8450_MASTER_A1NOC_SNOC 14 +#define SM8450_MASTER_A2NOC_SNOC 15 +#define SM8450_MASTER_CAMNOC_HF 16 +#define SM8450_MASTER_CAMNOC_ICP 17 +#define SM8450_MASTER_CAMNOC_SF 18 +#define SM8450_MASTER_GEM_NOC_CNOC 19 +#define SM8450_MASTER_GEM_NOC_PCIE_SNOC 20 +#define SM8450_MASTER_GFX3D 21 +#define SM8450_MASTER_LPASS_ANOC 22 +#define SM8450_MASTER_MDP 23 +#define SM8450_MASTER_MDP0 SM8450_MASTER_MDP +#define SM8450_MASTER_MDP1 SM8450_MASTER_MDP +#define SM8450_MASTER_MSS_PROC 24 +#define SM8450_MASTER_CNOC_MNOC_CFG 25 +#define SM8450_MASTER_MNOC_HF_MEM_NOC 26 +#define SM8450_MASTER_MNOC_SF_MEM_NOC 27 +#define SM8450_MASTER_COMPUTE_NOC 28 +#define SM8450_MASTER_ANOC_PCIE_GEM_NOC 29 +#define SM8450_MASTER_PCIE_ANOC_CFG 30 +#define SM8450_MASTER_ROTATOR 31 +#define SM8450_MASTER_SNOC_CFG 32 +#define SM8450_MASTER_SNOC_GC_MEM_NOC 33 +#define SM8450_MASTER_SNOC_SF_MEM_NOC 34 +#define SM8450_MASTER_CDSP_HCP 35 +#define SM8450_MASTER_VIDEO 36 +#define SM8450_MASTER_VIDEO_P0 SM8450_MASTER_VIDEO +#define SM8450_MASTER_VIDEO_P1 SM8450_MASTER_VIDEO +#define SM8450_MASTER_VIDEO_CV_PROC 37 +#define SM8450_MASTER_VIDEO_PROC 38 +#define SM8450_MASTER_VIDEO_V_PROC 39 +#define SM8450_MASTER_QUP_CORE_0 40 +#define SM8450_MASTER_QUP_CORE_1 41 +#define SM8450_MASTER_QUP_CORE_2 42 +#define SM8450_MASTER_CRYPTO 43 +#define SM8450_MASTER_IPA 44 +#define SM8450_MASTER_LPASS_PROC 45 +#define SM8450_MASTER_CDSP_PROC 46 +#define SM8450_MASTER_PIMEM 47 +#define SM8450_MASTER_SENSORS_PROC 48 +#define SM8450_MASTER_SP 49 +#define SM8450_MASTER_GIC 50 +#define SM8450_MASTER_PCIE_0 51 +#define SM8450_MASTER_PCIE_1 52 +#define SM8450_MASTER_QDSS_ETR 53 +#define SM8450_MASTER_QDSS_ETR_1 54 +#define SM8450_MASTER_SDCC_2 55 +#define SM8450_MASTER_SDCC_4 56 +#define SM8450_MASTER_UFS_MEM 57 +#define SM8450_MASTER_USB3_0 58 +#define SM8450_SLAVE_EBI1 512 +#define SM8450_SLAVE_AHB2PHY_SOUTH 513 +#define SM8450_SLAVE_AHB2PHY_NORTH 514 +#define SM8450_SLAVE_AOSS 515 +#define SM8450_SLAVE_CAMERA_CFG 516 +#define SM8450_SLAVE_CLK_CTL 517 +#define SM8450_SLAVE_CDSP_CFG 518 +#define SM8450_SLAVE_RBCPR_CX_CFG 519 +#define SM8450_SLAVE_RBCPR_MMCX_CFG 520 +#define SM8450_SLAVE_RBCPR_MXA_CFG 521 +#define SM8450_SLAVE_RBCPR_MXC_CFG 522 +#define SM8450_SLAVE_CRYPTO_0_CFG 523 +#define SM8450_SLAVE_CX_RDPM 524 +#define SM8450_SLAVE_DISPLAY_CFG 525 +#define SM8450_SLAVE_GFX3D_CFG 526 +#define SM8450_SLAVE_IMEM_CFG 527 +#define SM8450_SLAVE_IPA_CFG 528 +#define SM8450_SLAVE_IPC_ROUTER_CFG 529 +#define SM8450_SLAVE_LPASS 530 +#define SM8450_SLAVE_LPASS_CORE_CFG 531 +#define SM8450_SLAVE_LPASS_LPI_CFG 532 +#define SM8450_SLAVE_LPASS_MPU_CFG 533 +#define SM8450_SLAVE_LPASS_TOP_CFG 534 +#define SM8450_SLAVE_CNOC_MSS 535 +#define SM8450_SLAVE_MX_RDPM 536 +#define SM8450_SLAVE_PCIE_0_CFG 537 +#define SM8450_SLAVE_PCIE_1_CFG 538 +#define SM8450_SLAVE_PDM 539 +#define SM8450_SLAVE_PIMEM_CFG 540 +#define SM8450_SLAVE_PRNG 541 +#define SM8450_SLAVE_QDSS_CFG 542 +#define SM8450_SLAVE_QSPI_0 543 +#define SM8450_SLAVE_QUP_0 544 +#define SM8450_SLAVE_QUP_1 545 +#define SM8450_SLAVE_QUP_2 546 +#define SM8450_SLAVE_SDCC_2 547 +#define SM8450_SLAVE_SDCC_4 548 +#define SM8450_SLAVE_SPSS_CFG 549 +#define SM8450_SLAVE_TCSR 550 +#define SM8450_SLAVE_TLMM 551 +#define SM8450_SLAVE_TME_CFG 552 +#define SM8450_SLAVE_UFS_MEM_CFG 553 +#define SM8450_SLAVE_USB3_0 554 +#define SM8450_SLAVE_VENUS_CFG 555 +#define SM8450_SLAVE_VSENSE_CTRL_CFG 556 +#define SM8450_SLAVE_A1NOC_CFG 557 +#define SM8450_SLAVE_A1NOC_SNOC 558 +#define SM8450_SLAVE_A2NOC_CFG 559 +#define SM8450_SLAVE_A2NOC_SNOC 560 +#define SM8450_SLAVE_DDRSS_CFG 561 +#define SM8450_SLAVE_GEM_NOC_CNOC 562 +#define SM8450_SLAVE_SNOC_GEM_NOC_GC 563 +#define SM8450_SLAVE_SNOC_GEM_NOC_SF 564 +#define SM8450_SLAVE_LLCC 565 +#define SM8450_SLAVE_MNOC_HF_MEM_NOC 566 +#define SM8450_SLAVE_MNOC_SF_MEM_NOC 567 +#define SM8450_SLAVE_CNOC_MNOC_CFG 568 +#define SM8450_SLAVE_CDSP_MEM_NOC 569 +#define SM8450_SLAVE_MEM_NOC_PCIE_SNOC 570 +#define SM8450_SLAVE_PCIE_ANOC_CFG 571 +#define SM8450_SLAVE_ANOC_PCIE_GEM_NOC 572 +#define SM8450_SLAVE_SNOC_CFG 573 +#define SM8450_SLAVE_LPASS_SNOC 574 +#define SM8450_SLAVE_QUP_CORE_0 575 +#define SM8450_SLAVE_QUP_CORE_1 576 +#define SM8450_SLAVE_QUP_CORE_2 577 +#define SM8450_SLAVE_IMEM 578 +#define SM8450_SLAVE_PIMEM 579 +#define SM8450_SLAVE_SERVICE_NSP_NOC 580 +#define SM8450_SLAVE_SERVICE_A1NOC 581 +#define SM8450_SLAVE_SERVICE_A2NOC 582 +#define SM8450_SLAVE_SERVICE_CNOC 583 +#define SM8450_SLAVE_SERVICE_MNOC 584 +#define SM8450_SLAVE_SERVICES_LPASS_AML_NOC 585 +#define SM8450_SLAVE_SERVICE_LPASS_AG_NOC 586 +#define SM8450_SLAVE_SERVICE_PCIE_ANOC 587 +#define SM8450_SLAVE_SERVICE_SNOC 588 +#define SM8450_SLAVE_PCIE_0 589 +#define SM8450_SLAVE_PCIE_1 590 +#define SM8450_SLAVE_QDSS_STM 591 +#define SM8450_SLAVE_TCU 592 +#define SM8450_MASTER_LLCC_DISP 1000 +#define SM8450_MASTER_MDP_DISP 1001 +#define SM8450_MASTER_MDP0_DISP SM8450_MASTER_MDP_DISP +#define SM8450_MASTER_MDP1_DISP SM8450_MASTER_MDP_DISP +#define SM8450_MASTER_MNOC_HF_MEM_NOC_DISP 1002 +#define SM8450_MASTER_MNOC_SF_MEM_NOC_DISP 1003 +#define SM8450_MASTER_ANOC_PCIE_GEM_NOC_DISP 1004 +#define SM8450_MASTER_ROTATOR_DISP 1005 +#define SM8450_SLAVE_EBI1_DISP 1512 +#define SM8450_SLAVE_LLCC_DISP 1513 +#define SM8450_SLAVE_MNOC_HF_MEM_NOC_DISP 1514 +#define SM8450_SLAVE_MNOC_SF_MEM_NOC_DISP 1515 + +#endif -- cgit v1.2.3 From e9d54c26344f8e5390c643613ec192858104eca2 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 15 Dec 2021 08:23:20 +0800 Subject: interconnect: icc-rpm: Define ICC device type The driver currently uses .is_bimc_node to distinguish device type BIMC from NOC. Define type for bus/noc devices like what downstream[1] does to make support for more types easier. [1] https://source.codeaurora.org/quic/la/kernel/msm-4.19/tree/drivers/soc/qcom/msm_bus/msm_bus_core.h?h=kernel.lnx.4.19.r22-rel#n46 Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20211215002324.1727-2-shawn.guo@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 4 ++-- drivers/interconnect/qcom/icc-rpm.h | 11 ++++++++--- drivers/interconnect/qcom/msm8916.c | 4 +++- drivers/interconnect/qcom/msm8939.c | 5 ++++- drivers/interconnect/qcom/msm8996.c | 9 ++++++++- drivers/interconnect/qcom/sdm660.c | 7 ++++++- 6 files changed, 31 insertions(+), 9 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 939045f7c349..429c377231e6 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -164,7 +164,7 @@ static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); - if (qp->is_bimc_node) + if (qp->type == QCOM_ICC_BIMC) return qcom_icc_set_bimc_qos(node, sum_bw); return qcom_icc_set_noc_qos(node, sum_bw); @@ -309,7 +309,7 @@ int qnoc_probe(struct platform_device *pdev) qp->bus_clks[i].id = cds[i]; qp->num_clks = cd_num; - qp->is_bimc_node = desc->is_bimc_node; + qp->type = desc->type; qp->qos_offset = desc->qos_offset; if (desc->regmap_cfg) { diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index fd06a3b9e3f7..2268777348cb 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -12,19 +12,24 @@ #define to_qcom_provider(_provider) \ container_of(_provider, struct qcom_icc_provider, provider) +enum qcom_icc_type { + QCOM_ICC_NOC, + QCOM_ICC_BIMC, +}; + /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider * @bus_clks: the clk_bulk_data table of bus clocks * @num_clks: the total number of clk_bulk_data entries - * @is_bimc_node: indicates whether to use bimc specific setting + * @type: the ICC provider type * @qos_offset: offset to QoS registers * @regmap: regmap for QoS registers read/write access */ struct qcom_icc_provider { struct icc_provider provider; int num_clks; - bool is_bimc_node; + enum qcom_icc_type type; struct regmap *regmap; unsigned int qos_offset; struct clk_bulk_data bus_clks[]; @@ -78,7 +83,7 @@ struct qcom_icc_desc { const char * const *clocks; size_t num_clocks; bool has_bus_pd; - bool is_bimc_node; + enum qcom_icc_type type; const struct regmap_config *regmap_cfg; unsigned int qos_offset; }; diff --git a/drivers/interconnect/qcom/msm8916.c b/drivers/interconnect/qcom/msm8916.c index e3c995b11357..2f397a7c3322 100644 --- a/drivers/interconnect/qcom/msm8916.c +++ b/drivers/interconnect/qcom/msm8916.c @@ -1229,6 +1229,7 @@ static const struct regmap_config msm8916_snoc_regmap_config = { }; static struct qcom_icc_desc msm8916_snoc = { + .type = QCOM_ICC_NOC, .nodes = msm8916_snoc_nodes, .num_nodes = ARRAY_SIZE(msm8916_snoc_nodes), .regmap_cfg = &msm8916_snoc_regmap_config, @@ -1256,9 +1257,9 @@ static const struct regmap_config msm8916_bimc_regmap_config = { }; static struct qcom_icc_desc msm8916_bimc = { + .type = QCOM_ICC_BIMC, .nodes = msm8916_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8916_bimc_nodes), - .is_bimc_node = true, .regmap_cfg = &msm8916_bimc_regmap_config, .qos_offset = 0x8000, }; @@ -1325,6 +1326,7 @@ static const struct regmap_config msm8916_pcnoc_regmap_config = { }; static struct qcom_icc_desc msm8916_pcnoc = { + .type = QCOM_ICC_NOC, .nodes = msm8916_pcnoc_nodes, .num_nodes = ARRAY_SIZE(msm8916_pcnoc_nodes), .regmap_cfg = &msm8916_pcnoc_regmap_config, diff --git a/drivers/interconnect/qcom/msm8939.c b/drivers/interconnect/qcom/msm8939.c index 16272a477bd8..d188f3636e4c 100644 --- a/drivers/interconnect/qcom/msm8939.c +++ b/drivers/interconnect/qcom/msm8939.c @@ -1282,6 +1282,7 @@ static const struct regmap_config msm8939_snoc_regmap_config = { }; static struct qcom_icc_desc msm8939_snoc = { + .type = QCOM_ICC_NOC, .nodes = msm8939_snoc_nodes, .num_nodes = ARRAY_SIZE(msm8939_snoc_nodes), .regmap_cfg = &msm8939_snoc_regmap_config, @@ -1309,6 +1310,7 @@ static const struct regmap_config msm8939_snoc_mm_regmap_config = { }; static struct qcom_icc_desc msm8939_snoc_mm = { + .type = QCOM_ICC_NOC, .nodes = msm8939_snoc_mm_nodes, .num_nodes = ARRAY_SIZE(msm8939_snoc_mm_nodes), .regmap_cfg = &msm8939_snoc_mm_regmap_config, @@ -1336,9 +1338,9 @@ static const struct regmap_config msm8939_bimc_regmap_config = { }; static struct qcom_icc_desc msm8939_bimc = { + .type = QCOM_ICC_BIMC, .nodes = msm8939_bimc_nodes, .num_nodes = ARRAY_SIZE(msm8939_bimc_nodes), - .is_bimc_node = true, .regmap_cfg = &msm8939_bimc_regmap_config, .qos_offset = 0x8000, }; @@ -1407,6 +1409,7 @@ static const struct regmap_config msm8939_pcnoc_regmap_config = { }; static struct qcom_icc_desc msm8939_pcnoc = { + .type = QCOM_ICC_NOC, .nodes = msm8939_pcnoc_nodes, .num_nodes = ARRAY_SIZE(msm8939_pcnoc_nodes), .regmap_cfg = &msm8939_pcnoc_regmap_config, diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index d8248ebdf6b3..499e11fbbd2e 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -1811,6 +1811,7 @@ static const struct regmap_config msm8996_a0noc_regmap_config = { }; static const struct qcom_icc_desc msm8996_a0noc = { + .type = QCOM_ICC_NOC, .nodes = a0noc_nodes, .num_nodes = ARRAY_SIZE(a0noc_nodes), .clocks = bus_a0noc_clocks, @@ -1834,6 +1835,7 @@ static const struct regmap_config msm8996_a1noc_regmap_config = { }; static const struct qcom_icc_desc msm8996_a1noc = { + .type = QCOM_ICC_NOC, .nodes = a1noc_nodes, .num_nodes = ARRAY_SIZE(a1noc_nodes), .regmap_cfg = &msm8996_a1noc_regmap_config @@ -1854,6 +1856,7 @@ static const struct regmap_config msm8996_a2noc_regmap_config = { }; static const struct qcom_icc_desc msm8996_a2noc = { + .type = QCOM_ICC_NOC, .nodes = a2noc_nodes, .num_nodes = ARRAY_SIZE(a2noc_nodes), .regmap_cfg = &msm8996_a2noc_regmap_config @@ -1879,9 +1882,9 @@ static const struct regmap_config msm8996_bimc_regmap_config = { }; static const struct qcom_icc_desc msm8996_bimc = { + .type = QCOM_ICC_BIMC, .nodes = bimc_nodes, .num_nodes = ARRAY_SIZE(bimc_nodes), - .is_bimc_node = true, .regmap_cfg = &msm8996_bimc_regmap_config }; @@ -1937,6 +1940,7 @@ static const struct regmap_config msm8996_cnoc_regmap_config = { }; static const struct qcom_icc_desc msm8996_cnoc = { + .type = QCOM_ICC_NOC, .nodes = cnoc_nodes, .num_nodes = ARRAY_SIZE(cnoc_nodes), .regmap_cfg = &msm8996_cnoc_regmap_config @@ -1989,6 +1993,7 @@ static const struct regmap_config msm8996_mnoc_regmap_config = { }; static const struct qcom_icc_desc msm8996_mnoc = { + .type = QCOM_ICC_NOC, .nodes = mnoc_nodes, .num_nodes = ARRAY_SIZE(mnoc_nodes), .clocks = bus_mm_clocks, @@ -2026,6 +2031,7 @@ static const struct regmap_config msm8996_pnoc_regmap_config = { }; static const struct qcom_icc_desc msm8996_pnoc = { + .type = QCOM_ICC_NOC, .nodes = pnoc_nodes, .num_nodes = ARRAY_SIZE(pnoc_nodes), .regmap_cfg = &msm8996_pnoc_regmap_config @@ -2069,6 +2075,7 @@ static const struct regmap_config msm8996_snoc_regmap_config = { }; static const struct qcom_icc_desc msm8996_snoc = { + .type = QCOM_ICC_NOC, .nodes = snoc_nodes, .num_nodes = ARRAY_SIZE(snoc_nodes), .regmap_cfg = &msm8996_snoc_regmap_config diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 471bb88f8828..274a7139fe1a 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -1513,6 +1513,7 @@ static const struct regmap_config sdm660_a2noc_regmap_config = { }; static struct qcom_icc_desc sdm660_a2noc = { + .type = QCOM_ICC_NOC, .nodes = sdm660_a2noc_nodes, .num_nodes = ARRAY_SIZE(sdm660_a2noc_nodes), .clocks = bus_a2noc_clocks, @@ -1540,9 +1541,9 @@ static const struct regmap_config sdm660_bimc_regmap_config = { }; static struct qcom_icc_desc sdm660_bimc = { + .type = QCOM_ICC_BIMC, .nodes = sdm660_bimc_nodes, .num_nodes = ARRAY_SIZE(sdm660_bimc_nodes), - .is_bimc_node = true, .regmap_cfg = &sdm660_bimc_regmap_config, }; @@ -1594,6 +1595,7 @@ static const struct regmap_config sdm660_cnoc_regmap_config = { }; static struct qcom_icc_desc sdm660_cnoc = { + .type = QCOM_ICC_NOC, .nodes = sdm660_cnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_cnoc_nodes), .regmap_cfg = &sdm660_cnoc_regmap_config, @@ -1614,6 +1616,7 @@ static const struct regmap_config sdm660_gnoc_regmap_config = { }; static struct qcom_icc_desc sdm660_gnoc = { + .type = QCOM_ICC_NOC, .nodes = sdm660_gnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_gnoc_nodes), .regmap_cfg = &sdm660_gnoc_regmap_config, @@ -1653,6 +1656,7 @@ static const struct regmap_config sdm660_mnoc_regmap_config = { }; static struct qcom_icc_desc sdm660_mnoc = { + .type = QCOM_ICC_NOC, .nodes = sdm660_mnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_mnoc_nodes), .clocks = bus_mm_clocks, @@ -1689,6 +1693,7 @@ static const struct regmap_config sdm660_snoc_regmap_config = { }; static struct qcom_icc_desc sdm660_snoc = { + .type = QCOM_ICC_NOC, .nodes = sdm660_snoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_snoc_nodes), .regmap_cfg = &sdm660_snoc_regmap_config, -- cgit v1.2.3 From 08c590409f303d61461b8fcaa9083438e4300448 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 15 Dec 2021 08:23:21 +0800 Subject: interconnect: icc-rpm: Add QNOC type QoS support It adds QoS support for QNOC type device which can be found on QCM2290 platform. The downstream driver[1] includes support for priority, limiter, regulator and forwarding setup. As QCM2290 support only requires priority and forwarding configuration, limiter and regulator support are omitted for this initial submission. [1] https://source.codeaurora.org/quic/la/kernel/msm-4.19/tree/drivers/soc/qcom/msm_bus/msm_bus_qnoc_adhoc.c?h=kernel.lnx.4.19.r22-rel Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20211215002324.1727-3-shawn.guo@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 38 ++++++++++++++++++++++++++++++++++--- drivers/interconnect/qcom/icc-rpm.h | 3 +++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 429c377231e6..d8ea9bb479b1 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -18,6 +18,13 @@ #include "smd-rpm.h" #include "icc-rpm.h" +/* QNOC QoS */ +#define QNOC_QOS_MCTL_LOWn_ADDR(n) (0x8 + (n * 0x1000)) +#define QNOC_QOS_MCTL_DFLT_PRIO_MASK 0x70 +#define QNOC_QOS_MCTL_DFLT_PRIO_SHIFT 4 +#define QNOC_QOS_MCTL_URGFWD_EN_MASK 0x8 +#define QNOC_QOS_MCTL_URGFWD_EN_SHIFT 3 + /* BIMC QoS */ #define M_BKE_REG_BASE(n) (0x300 + (0x4000 * n)) #define M_BKE_EN_ADDR(n) (M_BKE_REG_BASE(n)) @@ -40,6 +47,27 @@ #define NOC_QOS_MODEn_ADDR(n) (0xc + (n * 0x1000)) #define NOC_QOS_MODEn_MASK 0x3 +static int qcom_icc_set_qnoc_qos(struct icc_node *src, u64 max_bw) +{ + struct icc_provider *provider = src->provider; + struct qcom_icc_provider *qp = to_qcom_provider(provider); + struct qcom_icc_node *qn = src->data; + struct qcom_icc_qos *qos = &qn->qos; + int rc; + + rc = regmap_update_bits(qp->regmap, + qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port), + QNOC_QOS_MCTL_DFLT_PRIO_MASK, + qos->areq_prio << QNOC_QOS_MCTL_DFLT_PRIO_SHIFT); + if (rc) + return rc; + + return regmap_update_bits(qp->regmap, + qp->qos_offset + QNOC_QOS_MCTL_LOWn_ADDR(qos->qos_port), + QNOC_QOS_MCTL_URGFWD_EN_MASK, + !!qos->urg_fwd_en << QNOC_QOS_MCTL_URGFWD_EN_SHIFT); +} + static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp, struct qcom_icc_qos *qos, int regnum) @@ -164,10 +192,14 @@ static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) dev_dbg(node->provider->dev, "Setting QoS for %s\n", qn->name); - if (qp->type == QCOM_ICC_BIMC) + switch (qp->type) { + case QCOM_ICC_BIMC: return qcom_icc_set_bimc_qos(node, sum_bw); - - return qcom_icc_set_noc_qos(node, sum_bw); + case QCOM_ICC_QNOC: + return qcom_icc_set_qnoc_qos(node, sum_bw); + default: + return qcom_icc_set_noc_qos(node, sum_bw); + } } static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 2268777348cb..26dad006034f 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -15,6 +15,7 @@ enum qcom_icc_type { QCOM_ICC_NOC, QCOM_ICC_BIMC, + QCOM_ICC_QNOC, }; /** @@ -43,6 +44,7 @@ struct qcom_icc_provider { * @ap_owned: indicates if the node is owned by the AP or by the RPM * @qos_mode: default qos mode for this node * @qos_port: qos port number for finding qos registers of this node + * @urg_fwd_en: enable urgent forwarding */ struct qcom_icc_qos { u32 areq_prio; @@ -51,6 +53,7 @@ struct qcom_icc_qos { bool ap_owned; int qos_mode; int qos_port; + bool urg_fwd_en; }; /** -- cgit v1.2.3 From e39bf2972c6e82eb7c51a78ca990d839aafeb124 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 15 Dec 2021 08:23:22 +0800 Subject: interconnect: icc-rpm: Support child NoC device probe As shown in downstream DT[1], the System NoC of QCM2290 is modelled using 4 fab/noc devices: sys_noc + qup_virt + mmnrt_virt + mmrt_virt. Among those 3 virtual devices, qup is owned by RPM and has no regmap resource, while mmnrt and mmrt are owned by AP and share the same regmap as sys_noc. So it's logical to represent these virtual devices as child nodes of sys_noc in DT, so that such configuration can be supported with a couple of changes on qnoc_probe(): - If there are child nodes, populate them. - If the device descriptor has .regmap_cfg but there is no IOMEM resource for the device, use parent's regmap. [1] https://android.googlesource.com/kernel/msm-extra/devicetree/+/refs/tags/android-11.0.0_r0.56/qcom/scuba-bus.dtsi Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20211215002324.1727-4-shawn.guo@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index d8ea9bb479b1..34125e8f8b60 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -349,8 +349,13 @@ int qnoc_probe(struct platform_device *pdev) void __iomem *mmio; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) + if (!res) { + /* Try parent's regmap */ + qp->regmap = dev_get_regmap(dev->parent, NULL); + if (qp->regmap) + goto regmap_done; return -ENODEV; + } mmio = devm_ioremap_resource(dev, res); @@ -366,6 +371,7 @@ int qnoc_probe(struct platform_device *pdev) } } +regmap_done: ret = devm_clk_bulk_get(dev, qp->num_clks, qp->bus_clks); if (ret) return ret; @@ -417,6 +423,10 @@ int qnoc_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qp); + /* Populate child NoC devices if any */ + if (of_get_child_count(dev->of_node) > 0) + return of_platform_populate(dev->of_node, NULL, NULL, dev); + return 0; err: icc_nodes_remove(provider); -- cgit v1.2.3 From 061dbde2bf3b12d80a4efd4b40db0b272e55b7f5 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 15 Dec 2021 08:23:23 +0800 Subject: dt-bindings: interconnect: Add Qualcomm QCM2290 NoC support Add bindings for Qualcomm QCM2290 Network-On-Chip interconnect devices. Signed-off-by: Shawn Guo Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211215002324.1727-5-shawn.guo@linaro.org Signed-off-by: Georgi Djakov --- .../bindings/interconnect/qcom,qcm2290.yaml | 137 +++++++++++++++++++++ include/dt-bindings/interconnect/qcom,qcm2290.h | 94 ++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 Documentation/devicetree/bindings/interconnect/qcom,qcm2290.yaml create mode 100644 include/dt-bindings/interconnect/qcom,qcm2290.h diff --git a/Documentation/devicetree/bindings/interconnect/qcom,qcm2290.yaml b/Documentation/devicetree/bindings/interconnect/qcom,qcm2290.yaml new file mode 100644 index 000000000000..f65a2fe846de --- /dev/null +++ b/Documentation/devicetree/bindings/interconnect/qcom,qcm2290.yaml @@ -0,0 +1,137 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interconnect/qcom,qcm2290.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm QCM2290 Network-On-Chip interconnect + +maintainers: + - Shawn Guo + +description: | + The Qualcomm QCM2290 interconnect providers support adjusting the + bandwidth requirements between the various NoC fabrics. + +properties: + reg: + maxItems: 1 + + compatible: + enum: + - qcom,qcm2290-bimc + - qcom,qcm2290-cnoc + - qcom,qcm2290-snoc + + '#interconnect-cells': + const: 1 + + clock-names: + items: + - const: bus + - const: bus_a + + clocks: + items: + - description: Bus Clock + - description: Bus A Clock + +# Child node's properties +patternProperties: + '^interconnect-[a-z0-9]+$': + type: object + description: + The interconnect providers do not have a separate QoS register space, + but share parent's space. + + properties: + compatible: + enum: + - qcom,qcm2290-qup-virt + - qcom,qcm2290-mmrt-virt + - qcom,qcm2290-mmnrt-virt + + '#interconnect-cells': + const: 1 + + clock-names: + items: + - const: bus + - const: bus_a + + clocks: + items: + - description: Bus Clock + - description: Bus A Clock + + required: + - compatible + - '#interconnect-cells' + - clock-names + - clocks + + additionalProperties: false + +required: + - compatible + - reg + - '#interconnect-cells' + - clock-names + - clocks + +additionalProperties: false + +examples: + - | + #include + + snoc: interconnect@1880000 { + compatible = "qcom,qcm2290-snoc"; + reg = <0x01880000 0x60200>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_SNOC_CLK>, + <&rpmcc RPM_SMD_SNOC_A_CLK>; + + qup_virt: interconnect-qup { + compatible = "qcom,qcm2290-qup-virt"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_QUP_CLK>, + <&rpmcc RPM_SMD_QUP_A_CLK>; + }; + + mmnrt_virt: interconnect-mmnrt { + compatible = "qcom,qcm2290-mmnrt-virt"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_MMNRT_CLK>, + <&rpmcc RPM_SMD_MMNRT_A_CLK>; + }; + + mmrt_virt: interconnect-mmrt { + compatible = "qcom,qcm2290-mmrt-virt"; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_MMRT_CLK>, + <&rpmcc RPM_SMD_MMRT_A_CLK>; + }; + }; + + cnoc: interconnect@1900000 { + compatible = "qcom,qcm2290-cnoc"; + reg = <0x01900000 0x8200>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_CNOC_CLK>, + <&rpmcc RPM_SMD_CNOC_A_CLK>; + }; + + bimc: interconnect@4480000 { + compatible = "qcom,qcm2290-bimc"; + reg = <0x04480000 0x80000>; + #interconnect-cells = <1>; + clock-names = "bus", "bus_a"; + clocks = <&rpmcc RPM_SMD_BIMC_CLK>, + <&rpmcc RPM_SMD_BIMC_A_CLK>; + }; diff --git a/include/dt-bindings/interconnect/qcom,qcm2290.h b/include/dt-bindings/interconnect/qcom,qcm2290.h new file mode 100644 index 000000000000..6cbbb7fe0bd3 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,qcm2290.h @@ -0,0 +1,94 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* QCM2290 interconnect IDs */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_QCM2290_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_QCM2290_H + +/* BIMC */ +#define MASTER_APPSS_PROC 0 +#define MASTER_SNOC_BIMC_RT 1 +#define MASTER_SNOC_BIMC_NRT 2 +#define MASTER_SNOC_BIMC 3 +#define MASTER_TCU_0 4 +#define MASTER_GFX3D 5 +#define SLAVE_EBI1 6 +#define SLAVE_BIMC_SNOC 7 + +/* CNOC */ +#define MASTER_SNOC_CNOC 0 +#define MASTER_QDSS_DAP 1 +#define SLAVE_BIMC_CFG 2 +#define SLAVE_CAMERA_NRT_THROTTLE_CFG 3 +#define SLAVE_CAMERA_RT_THROTTLE_CFG 4 +#define SLAVE_CAMERA_CFG 5 +#define SLAVE_CLK_CTL 6 +#define SLAVE_CRYPTO_0_CFG 7 +#define SLAVE_DISPLAY_CFG 8 +#define SLAVE_DISPLAY_THROTTLE_CFG 9 +#define SLAVE_GPU_CFG 10 +#define SLAVE_HWKM 11 +#define SLAVE_IMEM_CFG 12 +#define SLAVE_IPA_CFG 13 +#define SLAVE_LPASS 14 +#define SLAVE_MESSAGE_RAM 15 +#define SLAVE_PDM 16 +#define SLAVE_PIMEM_CFG 17 +#define SLAVE_PKA_WRAPPER 18 +#define SLAVE_PMIC_ARB 19 +#define SLAVE_PRNG 20 +#define SLAVE_QDSS_CFG 21 +#define SLAVE_QM_CFG 22 +#define SLAVE_QM_MPU_CFG 23 +#define SLAVE_QPIC 24 +#define SLAVE_QUP_0 25 +#define SLAVE_SDCC_1 26 +#define SLAVE_SDCC_2 27 +#define SLAVE_SNOC_CFG 28 +#define SLAVE_TCSR 29 +#define SLAVE_USB3 30 +#define SLAVE_VENUS_CFG 31 +#define SLAVE_VENUS_THROTTLE_CFG 32 +#define SLAVE_VSENSE_CTRL_CFG 33 +#define SLAVE_SERVICE_CNOC 34 + +/* SNOC */ +#define MASTER_CRYPTO_CORE0 0 +#define MASTER_SNOC_CFG 1 +#define MASTER_TIC 2 +#define MASTER_ANOC_SNOC 3 +#define MASTER_BIMC_SNOC 4 +#define MASTER_PIMEM 5 +#define MASTER_QDSS_BAM 6 +#define MASTER_QUP_0 7 +#define MASTER_IPA 8 +#define MASTER_QDSS_ETR 9 +#define MASTER_SDCC_1 10 +#define MASTER_SDCC_2 11 +#define MASTER_QPIC 12 +#define MASTER_USB3_0 13 +#define SLAVE_APPSS 14 +#define SLAVE_SNOC_CNOC 15 +#define SLAVE_IMEM 16 +#define SLAVE_PIMEM 17 +#define SLAVE_SNOC_BIMC 18 +#define SLAVE_SERVICE_SNOC 19 +#define SLAVE_QDSS_STM 20 +#define SLAVE_TCU 21 +#define SLAVE_ANOC_SNOC 22 + +/* QUP Virtual */ +#define MASTER_QUP_CORE_0 0 +#define SLAVE_QUP_CORE_0 1 + +/* MMNRT Virtual */ +#define MASTER_CAMNOC_SF 0 +#define MASTER_VIDEO_P0 1 +#define MASTER_VIDEO_PROC 2 +#define SLAVE_SNOC_BIMC_NRT 3 + +/* MMRT Virtual */ +#define MASTER_CAMNOC_HF 0 +#define MASTER_MDP0 1 +#define SLAVE_SNOC_BIMC_RT 2 + +#endif -- cgit v1.2.3 From 1a14b1ac3935788de75ea3b0ef68b46375070f77 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Wed, 15 Dec 2021 08:23:24 +0800 Subject: interconnect: qcom: Add QCM2290 driver support It adds interconnect driver support for QCM2290 platform. The topology consists of 3 NoCs: BIMC, Config NoC (CNOC) and System NoC (SNOC). SNOC is a QCOM_ICC_QNOC type device, as well as its 3 virtual child devices, QUP, MMNRT and MMRT. QUP is owned by RPM and thus has no .regmap_cfg, while the other 2 share the same .regmap_cfg with SNOC (parent). Signed-off-by: Shawn Guo Link: https://lore.kernel.org/r/20211215002324.1727-6-shawn.guo@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/Kconfig | 9 + drivers/interconnect/qcom/Makefile | 2 + drivers/interconnect/qcom/qcm2290.c | 1363 +++++++++++++++++++++++++++++++++++ 3 files changed, 1374 insertions(+) create mode 100644 drivers/interconnect/qcom/qcm2290.c diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig index d0ed6f570355..8876ffaf0b53 100644 --- a/drivers/interconnect/qcom/Kconfig +++ b/drivers/interconnect/qcom/Kconfig @@ -51,6 +51,15 @@ config INTERCONNECT_QCOM_OSM_L3 Say y here to support the Operating State Manager (OSM) interconnect driver which controls the scaling of L3 caches on Qualcomm SoCs. +config INTERCONNECT_QCOM_QCM2290 + tristate "Qualcomm QCM2290 interconnect driver" + depends on INTERCONNECT_QCOM + depends on QCOM_SMD_RPM + select INTERCONNECT_QCOM_SMD_RPM + help + This is a driver for the Qualcomm Network-on-Chip on qcm2290-based + platforms. + config INTERCONNECT_QCOM_QCS404 tristate "Qualcomm QCS404 interconnect driver" depends on INTERCONNECT_QCOM diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile index 750e42ab82ac..3b73a5908f1c 100644 --- a/drivers/interconnect/qcom/Makefile +++ b/drivers/interconnect/qcom/Makefile @@ -6,6 +6,7 @@ qnoc-msm8939-objs := msm8939.o qnoc-msm8974-objs := msm8974.o qnoc-msm8996-objs := msm8996.o icc-osm-l3-objs := osm-l3.o +qnoc-qcm2290-objs := qcm2290.o qnoc-qcs404-objs := qcs404.o icc-rpmh-obj := icc-rpmh.o qnoc-sc7180-objs := sc7180.o @@ -25,6 +26,7 @@ obj-$(CONFIG_INTERCONNECT_QCOM_MSM8939) += qnoc-msm8939.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8974) += qnoc-msm8974.o obj-$(CONFIG_INTERCONNECT_QCOM_MSM8996) += qnoc-msm8996.o obj-$(CONFIG_INTERCONNECT_QCOM_OSM_L3) += icc-osm-l3.o +obj-$(CONFIG_INTERCONNECT_QCOM_QCM2290) += qnoc-qcm2290.o obj-$(CONFIG_INTERCONNECT_QCOM_QCS404) += qnoc-qcs404.o obj-$(CONFIG_INTERCONNECT_QCOM_RPMH) += icc-rpmh.o obj-$(CONFIG_INTERCONNECT_QCOM_SC7180) += qnoc-sc7180.o diff --git a/drivers/interconnect/qcom/qcm2290.c b/drivers/interconnect/qcom/qcm2290.c new file mode 100644 index 000000000000..74404e0b2080 --- /dev/null +++ b/drivers/interconnect/qcom/qcm2290.c @@ -0,0 +1,1363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Qualcomm QCM2290 Network-on-Chip (NoC) QoS driver + * + * Copyright (c) 2021, Linaro Ltd. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "icc-rpm.h" +#include "smd-rpm.h" + +enum { + QCM2290_MASTER_APPSS_PROC = 1, + QCM2290_MASTER_SNOC_BIMC_RT, + QCM2290_MASTER_SNOC_BIMC_NRT, + QCM2290_MASTER_SNOC_BIMC, + QCM2290_MASTER_TCU_0, + QCM2290_MASTER_GFX3D, + QCM2290_MASTER_SNOC_CNOC, + QCM2290_MASTER_QDSS_DAP, + QCM2290_MASTER_CRYPTO_CORE0, + QCM2290_MASTER_SNOC_CFG, + QCM2290_MASTER_TIC, + QCM2290_MASTER_ANOC_SNOC, + QCM2290_MASTER_BIMC_SNOC, + QCM2290_MASTER_PIMEM, + QCM2290_MASTER_QDSS_BAM, + QCM2290_MASTER_QUP_0, + QCM2290_MASTER_IPA, + QCM2290_MASTER_QDSS_ETR, + QCM2290_MASTER_SDCC_1, + QCM2290_MASTER_SDCC_2, + QCM2290_MASTER_QPIC, + QCM2290_MASTER_USB3_0, + QCM2290_MASTER_QUP_CORE_0, + QCM2290_MASTER_CAMNOC_SF, + QCM2290_MASTER_VIDEO_P0, + QCM2290_MASTER_VIDEO_PROC, + QCM2290_MASTER_CAMNOC_HF, + QCM2290_MASTER_MDP0, + + QCM2290_SLAVE_EBI1, + QCM2290_SLAVE_BIMC_SNOC, + QCM2290_SLAVE_BIMC_CFG, + QCM2290_SLAVE_CAMERA_NRT_THROTTLE_CFG, + QCM2290_SLAVE_CAMERA_RT_THROTTLE_CFG, + QCM2290_SLAVE_CAMERA_CFG, + QCM2290_SLAVE_CLK_CTL, + QCM2290_SLAVE_CRYPTO_0_CFG, + QCM2290_SLAVE_DISPLAY_CFG, + QCM2290_SLAVE_DISPLAY_THROTTLE_CFG, + QCM2290_SLAVE_GPU_CFG, + QCM2290_SLAVE_HWKM, + QCM2290_SLAVE_IMEM_CFG, + QCM2290_SLAVE_IPA_CFG, + QCM2290_SLAVE_LPASS, + QCM2290_SLAVE_MESSAGE_RAM, + QCM2290_SLAVE_PDM, + QCM2290_SLAVE_PIMEM_CFG, + QCM2290_SLAVE_PKA_WRAPPER, + QCM2290_SLAVE_PMIC_ARB, + QCM2290_SLAVE_PRNG, + QCM2290_SLAVE_QDSS_CFG, + QCM2290_SLAVE_QM_CFG, + QCM2290_SLAVE_QM_MPU_CFG, + QCM2290_SLAVE_QPIC, + QCM2290_SLAVE_QUP_0, + QCM2290_SLAVE_SDCC_1, + QCM2290_SLAVE_SDCC_2, + QCM2290_SLAVE_SNOC_CFG, + QCM2290_SLAVE_TCSR, + QCM2290_SLAVE_USB3, + QCM2290_SLAVE_VENUS_CFG, + QCM2290_SLAVE_VENUS_THROTTLE_CFG, + QCM2290_SLAVE_VSENSE_CTRL_CFG, + QCM2290_SLAVE_SERVICE_CNOC, + QCM2290_SLAVE_APPSS, + QCM2290_SLAVE_SNOC_CNOC, + QCM2290_SLAVE_IMEM, + QCM2290_SLAVE_PIMEM, + QCM2290_SLAVE_SNOC_BIMC, + QCM2290_SLAVE_SERVICE_SNOC, + QCM2290_SLAVE_QDSS_STM, + QCM2290_SLAVE_TCU, + QCM2290_SLAVE_ANOC_SNOC, + QCM2290_SLAVE_QUP_CORE_0, + QCM2290_SLAVE_SNOC_BIMC_NRT, + QCM2290_SLAVE_SNOC_BIMC_RT, +}; + +/* Master nodes */ +static const u16 mas_appss_proc_links[] = { + QCM2290_SLAVE_EBI1, + QCM2290_SLAVE_BIMC_SNOC, +}; + +static struct qcom_icc_node mas_appss_proc = { + .id = QCM2290_MASTER_APPSS_PROC, + .name = "mas_apps_proc", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 0, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.prio_level = 0, + .qos.areq_prio = 0, + .mas_rpm_id = 0, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_appss_proc_links), + .links = mas_appss_proc_links, +}; + +static const u16 mas_snoc_bimc_rt_links[] = { + QCM2290_SLAVE_EBI1, +}; + +static struct qcom_icc_node mas_snoc_bimc_rt = { + .id = QCM2290_MASTER_SNOC_BIMC_RT, + .name = "mas_snoc_bimc_rt", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 2, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .mas_rpm_id = 163, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_bimc_rt_links), + .links = mas_snoc_bimc_rt_links, +}; + +static const u16 mas_snoc_bimc_nrt_links[] = { + QCM2290_SLAVE_EBI1, +}; + +static struct qcom_icc_node mas_snoc_bimc_nrt = { + .id = QCM2290_MASTER_SNOC_BIMC_NRT, + .name = "mas_snoc_bimc_nrt", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 2, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .mas_rpm_id = 163, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_bimc_nrt_links), + .links = mas_snoc_bimc_nrt_links, +}; + +static const u16 mas_snoc_bimc_links[] = { + QCM2290_SLAVE_EBI1, +}; + +static struct qcom_icc_node mas_snoc_bimc = { + .id = QCM2290_MASTER_SNOC_BIMC, + .name = "mas_snoc_bimc", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 2, + .qos.qos_mode = NOC_QOS_MODE_BYPASS, + .mas_rpm_id = 164, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_bimc_links), + .links = mas_snoc_bimc_links, +}; + +static const u16 mas_tcu_0_links[] = { + QCM2290_SLAVE_EBI1, + QCM2290_SLAVE_BIMC_SNOC, +}; + +static struct qcom_icc_node mas_tcu_0 = { + .id = QCM2290_MASTER_TCU_0, + .name = "mas_tcu_0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 4, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.prio_level = 6, + .qos.areq_prio = 6, + .mas_rpm_id = 102, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tcu_0_links), + .links = mas_tcu_0_links, +}; + +static const u16 mas_snoc_cnoc_links[] = { + QCM2290_SLAVE_CAMERA_RT_THROTTLE_CFG, + QCM2290_SLAVE_SDCC_2, + QCM2290_SLAVE_SDCC_1, + QCM2290_SLAVE_QM_CFG, + QCM2290_SLAVE_BIMC_CFG, + QCM2290_SLAVE_USB3, + QCM2290_SLAVE_QM_MPU_CFG, + QCM2290_SLAVE_CAMERA_NRT_THROTTLE_CFG, + QCM2290_SLAVE_QDSS_CFG, + QCM2290_SLAVE_PDM, + QCM2290_SLAVE_IPA_CFG, + QCM2290_SLAVE_DISPLAY_THROTTLE_CFG, + QCM2290_SLAVE_TCSR, + QCM2290_SLAVE_MESSAGE_RAM, + QCM2290_SLAVE_PMIC_ARB, + QCM2290_SLAVE_LPASS, + QCM2290_SLAVE_DISPLAY_CFG, + QCM2290_SLAVE_VENUS_CFG, + QCM2290_SLAVE_GPU_CFG, + QCM2290_SLAVE_IMEM_CFG, + QCM2290_SLAVE_SNOC_CFG, + QCM2290_SLAVE_SERVICE_CNOC, + QCM2290_SLAVE_VENUS_THROTTLE_CFG, + QCM2290_SLAVE_PKA_WRAPPER, + QCM2290_SLAVE_HWKM, + QCM2290_SLAVE_PRNG, + QCM2290_SLAVE_VSENSE_CTRL_CFG, + QCM2290_SLAVE_CRYPTO_0_CFG, + QCM2290_SLAVE_PIMEM_CFG, + QCM2290_SLAVE_QUP_0, + QCM2290_SLAVE_CAMERA_CFG, + QCM2290_SLAVE_CLK_CTL, + QCM2290_SLAVE_QPIC, +}; + +static struct qcom_icc_node mas_snoc_cnoc = { + .id = QCM2290_MASTER_SNOC_CNOC, + .name = "mas_snoc_cnoc", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = 52, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cnoc_links), + .links = mas_snoc_cnoc_links, +}; + +static const u16 mas_qdss_dap_links[] = { + QCM2290_SLAVE_CAMERA_RT_THROTTLE_CFG, + QCM2290_SLAVE_SDCC_2, + QCM2290_SLAVE_SDCC_1, + QCM2290_SLAVE_QM_CFG, + QCM2290_SLAVE_BIMC_CFG, + QCM2290_SLAVE_USB3, + QCM2290_SLAVE_QM_MPU_CFG, + QCM2290_SLAVE_CAMERA_NRT_THROTTLE_CFG, + QCM2290_SLAVE_QDSS_CFG, + QCM2290_SLAVE_PDM, + QCM2290_SLAVE_IPA_CFG, + QCM2290_SLAVE_DISPLAY_THROTTLE_CFG, + QCM2290_SLAVE_TCSR, + QCM2290_SLAVE_MESSAGE_RAM, + QCM2290_SLAVE_PMIC_ARB, + QCM2290_SLAVE_LPASS, + QCM2290_SLAVE_DISPLAY_CFG, + QCM2290_SLAVE_VENUS_CFG, + QCM2290_SLAVE_GPU_CFG, + QCM2290_SLAVE_IMEM_CFG, + QCM2290_SLAVE_SNOC_CFG, + QCM2290_SLAVE_SERVICE_CNOC, + QCM2290_SLAVE_VENUS_THROTTLE_CFG, + QCM2290_SLAVE_PKA_WRAPPER, + QCM2290_SLAVE_HWKM, + QCM2290_SLAVE_PRNG, + QCM2290_SLAVE_VSENSE_CTRL_CFG, + QCM2290_SLAVE_CRYPTO_0_CFG, + QCM2290_SLAVE_PIMEM_CFG, + QCM2290_SLAVE_QUP_0, + QCM2290_SLAVE_CAMERA_CFG, + QCM2290_SLAVE_CLK_CTL, + QCM2290_SLAVE_QPIC, +}; + +static struct qcom_icc_node mas_qdss_dap = { + .id = QCM2290_MASTER_QDSS_DAP, + .name = "mas_qdss_dap", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = 49, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_dap_links), + .links = mas_qdss_dap_links, +}; + +static const u16 mas_crypto_core0_links[] = { + QCM2290_SLAVE_ANOC_SNOC +}; + +static struct qcom_icc_node mas_crypto_core0 = { + .id = QCM2290_MASTER_CRYPTO_CORE0, + .name = "mas_crypto_core0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 22, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 23, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_crypto_core0_links), + .links = mas_crypto_core0_links, +}; + +static const u16 mas_qup_core_0_links[] = { + QCM2290_SLAVE_QUP_CORE_0, +}; + +static struct qcom_icc_node mas_qup_core_0 = { + .id = QCM2290_MASTER_QUP_CORE_0, + .name = "mas_qup_core_0", + .buswidth = 4, + .mas_rpm_id = 170, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qup_core_0_links), + .links = mas_qup_core_0_links, +}; + +static const u16 mas_camnoc_sf_links[] = { + QCM2290_SLAVE_SNOC_BIMC_NRT, +}; + +static struct qcom_icc_node mas_camnoc_sf = { + .id = QCM2290_MASTER_CAMNOC_SF, + .name = "mas_camnoc_sf", + .buswidth = 32, + .qos.ap_owned = true, + .qos.qos_port = 4, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .mas_rpm_id = 172, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_camnoc_sf_links), + .links = mas_camnoc_sf_links, +}; + +static const u16 mas_camnoc_hf_links[] = { + QCM2290_SLAVE_SNOC_BIMC_RT, +}; + +static struct qcom_icc_node mas_camnoc_hf = { + .id = QCM2290_MASTER_CAMNOC_HF, + .name = "mas_camnoc_hf", + .buswidth = 32, + .qos.ap_owned = true, + .qos.qos_port = 10, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .qos.urg_fwd_en = true, + .mas_rpm_id = 173, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_camnoc_hf_links), + .links = mas_camnoc_hf_links, +}; + +static const u16 mas_mdp0_links[] = { + QCM2290_SLAVE_SNOC_BIMC_RT, +}; + +static struct qcom_icc_node mas_mdp0 = { + .id = QCM2290_MASTER_MDP0, + .name = "mas_mdp0", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 5, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .qos.urg_fwd_en = true, + .mas_rpm_id = 8, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_mdp0_links), + .links = mas_mdp0_links, +}; + +static const u16 mas_video_p0_links[] = { + QCM2290_SLAVE_SNOC_BIMC_NRT, +}; + +static struct qcom_icc_node mas_video_p0 = { + .id = QCM2290_MASTER_VIDEO_P0, + .name = "mas_video_p0", + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_port = 9, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 3, + .qos.urg_fwd_en = true, + .mas_rpm_id = 9, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_video_p0_links), + .links = mas_video_p0_links, +}; + +static const u16 mas_video_proc_links[] = { + QCM2290_SLAVE_SNOC_BIMC_NRT, +}; + +static struct qcom_icc_node mas_video_proc = { + .id = QCM2290_MASTER_VIDEO_PROC, + .name = "mas_video_proc", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 13, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 4, + .mas_rpm_id = 168, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_video_proc_links), + .links = mas_video_proc_links, +}; + +static const u16 mas_snoc_cfg_links[] = { + QCM2290_SLAVE_SERVICE_SNOC, +}; + +static struct qcom_icc_node mas_snoc_cfg = { + .id = QCM2290_MASTER_SNOC_CFG, + .name = "mas_snoc_cfg", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = 20, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_snoc_cfg_links), + .links = mas_snoc_cfg_links, +}; + +static const u16 mas_tic_links[] = { + QCM2290_SLAVE_PIMEM, + QCM2290_SLAVE_IMEM, + QCM2290_SLAVE_APPSS, + QCM2290_SLAVE_SNOC_BIMC, + QCM2290_SLAVE_SNOC_CNOC, + QCM2290_SLAVE_TCU, + QCM2290_SLAVE_QDSS_STM, +}; + +static struct qcom_icc_node mas_tic = { + .id = QCM2290_MASTER_TIC, + .name = "mas_tic", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 8, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 51, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_tic_links), + .links = mas_tic_links, +}; + +static const u16 mas_anoc_snoc_links[] = { + QCM2290_SLAVE_PIMEM, + QCM2290_SLAVE_IMEM, + QCM2290_SLAVE_APPSS, + QCM2290_SLAVE_SNOC_BIMC, + QCM2290_SLAVE_SNOC_CNOC, + QCM2290_SLAVE_TCU, + QCM2290_SLAVE_QDSS_STM, +}; + +static struct qcom_icc_node mas_anoc_snoc = { + .id = QCM2290_MASTER_ANOC_SNOC, + .name = "mas_anoc_snoc", + .buswidth = 16, + .mas_rpm_id = 110, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_anoc_snoc_links), + .links = mas_anoc_snoc_links, +}; + +static const u16 mas_bimc_snoc_links[] = { + QCM2290_SLAVE_PIMEM, + QCM2290_SLAVE_IMEM, + QCM2290_SLAVE_APPSS, + QCM2290_SLAVE_SNOC_CNOC, + QCM2290_SLAVE_TCU, + QCM2290_SLAVE_QDSS_STM, +}; + +static struct qcom_icc_node mas_bimc_snoc = { + .id = QCM2290_MASTER_BIMC_SNOC, + .name = "mas_bimc_snoc", + .buswidth = 8, + .mas_rpm_id = 21, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_bimc_snoc_links), + .links = mas_bimc_snoc_links, +}; + +static const u16 mas_pimem_links[] = { + QCM2290_SLAVE_IMEM, + QCM2290_SLAVE_SNOC_BIMC, +}; + +static struct qcom_icc_node mas_pimem = { + .id = QCM2290_MASTER_PIMEM, + .name = "mas_pimem", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 20, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 113, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_pimem_links), + .links = mas_pimem_links, +}; + +static const u16 mas_qdss_bam_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_qdss_bam = { + .id = QCM2290_MASTER_QDSS_BAM, + .name = "mas_qdss_bam", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 2, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 19, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_bam_links), + .links = mas_qdss_bam_links, +}; + +static const u16 mas_qup_0_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_qup_0 = { + .id = QCM2290_MASTER_QUP_0, + .name = "mas_qup_0", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 0, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 166, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qup_0_links), + .links = mas_qup_0_links, +}; + +static const u16 mas_ipa_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_ipa = { + .id = QCM2290_MASTER_IPA, + .name = "mas_ipa", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 3, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 59, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_ipa_links), + .links = mas_ipa_links, +}; + +static const u16 mas_qdss_etr_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_qdss_etr = { + .id = QCM2290_MASTER_QDSS_ETR, + .name = "mas_qdss_etr", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 12, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 31, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qdss_etr_links), + .links = mas_qdss_etr_links, +}; + +static const u16 mas_sdcc_1_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_sdcc_1 = { + .id = QCM2290_MASTER_SDCC_1, + .name = "mas_sdcc_1", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 17, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 33, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_1_links), + .links = mas_sdcc_1_links, +}; + +static const u16 mas_sdcc_2_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_sdcc_2 = { + .id = QCM2290_MASTER_SDCC_2, + .name = "mas_sdcc_2", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 23, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 35, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_sdcc_2_links), + .links = mas_sdcc_2_links, +}; + +static const u16 mas_qpic_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_qpic = { + .id = QCM2290_MASTER_QPIC, + .name = "mas_qpic", + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_port = 1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 58, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_qpic_links), + .links = mas_qpic_links, +}; + +static const u16 mas_usb3_0_links[] = { + QCM2290_SLAVE_ANOC_SNOC, +}; + +static struct qcom_icc_node mas_usb3_0 = { + .id = QCM2290_MASTER_USB3_0, + .name = "mas_usb3_0", + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_port = 24, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.areq_prio = 2, + .mas_rpm_id = 32, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_usb3_0_links), + .links = mas_usb3_0_links, +}; + +static const u16 mas_gfx3d_links[] = { + QCM2290_SLAVE_EBI1, +}; + +static struct qcom_icc_node mas_gfx3d = { + .id = QCM2290_MASTER_GFX3D, + .name = "mas_gfx3d", + .buswidth = 32, + .qos.ap_owned = true, + .qos.qos_port = 1, + .qos.qos_mode = NOC_QOS_MODE_FIXED, + .qos.prio_level = 0, + .qos.areq_prio = 0, + .mas_rpm_id = 6, + .slv_rpm_id = -1, + .num_links = ARRAY_SIZE(mas_gfx3d_links), + .links = mas_gfx3d_links, +}; + +/* Slave nodes */ +static struct qcom_icc_node slv_ebi1 = { + .name = "slv_ebi1", + .id = QCM2290_SLAVE_EBI1, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 0, +}; + +static const u16 slv_bimc_snoc_links[] = { + QCM2290_MASTER_BIMC_SNOC, +}; + +static struct qcom_icc_node slv_bimc_snoc = { + .name = "slv_bimc_snoc", + .id = QCM2290_SLAVE_BIMC_SNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 2, + .num_links = ARRAY_SIZE(slv_bimc_snoc_links), + .links = slv_bimc_snoc_links, +}; + +static struct qcom_icc_node slv_bimc_cfg = { + .name = "slv_bimc_cfg", + .id = QCM2290_SLAVE_BIMC_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 56, +}; + +static struct qcom_icc_node slv_camera_nrt_throttle_cfg = { + .name = "slv_camera_nrt_throttle_cfg", + .id = QCM2290_SLAVE_CAMERA_NRT_THROTTLE_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 271, +}; + +static struct qcom_icc_node slv_camera_rt_throttle_cfg = { + .name = "slv_camera_rt_throttle_cfg", + .id = QCM2290_SLAVE_CAMERA_RT_THROTTLE_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 279, +}; + +static struct qcom_icc_node slv_camera_cfg = { + .name = "slv_camera_cfg", + .id = QCM2290_SLAVE_CAMERA_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 3, +}; + +static struct qcom_icc_node slv_clk_ctl = { + .name = "slv_clk_ctl", + .id = QCM2290_SLAVE_CLK_CTL, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 47, +}; + +static struct qcom_icc_node slv_crypto_0_cfg = { + .name = "slv_crypto_0_cfg", + .id = QCM2290_SLAVE_CRYPTO_0_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 52, +}; + +static struct qcom_icc_node slv_display_cfg = { + .name = "slv_display_cfg", + .id = QCM2290_SLAVE_DISPLAY_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 4, +}; + +static struct qcom_icc_node slv_display_throttle_cfg = { + .name = "slv_display_throttle_cfg", + .id = QCM2290_SLAVE_DISPLAY_THROTTLE_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 156, +}; + +static struct qcom_icc_node slv_gpu_cfg = { + .name = "slv_gpu_cfg", + .id = QCM2290_SLAVE_GPU_CFG, + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 275, +}; + +static struct qcom_icc_node slv_hwkm = { + .name = "slv_hwkm", + .id = QCM2290_SLAVE_HWKM, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 280, +}; + +static struct qcom_icc_node slv_imem_cfg = { + .name = "slv_imem_cfg", + .id = QCM2290_SLAVE_IMEM_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 54, +}; + +static struct qcom_icc_node slv_ipa_cfg = { + .name = "slv_ipa_cfg", + .id = QCM2290_SLAVE_IPA_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 183, +}; + +static struct qcom_icc_node slv_lpass = { + .name = "slv_lpass", + .id = QCM2290_SLAVE_LPASS, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 21, +}; + +static struct qcom_icc_node slv_message_ram = { + .name = "slv_message_ram", + .id = QCM2290_SLAVE_MESSAGE_RAM, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 55, +}; + +static struct qcom_icc_node slv_pdm = { + .name = "slv_pdm", + .id = QCM2290_SLAVE_PDM, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 41, +}; + +static struct qcom_icc_node slv_pimem_cfg = { + .name = "slv_pimem_cfg", + .id = QCM2290_SLAVE_PIMEM_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 167, +}; + +static struct qcom_icc_node slv_pka_wrapper = { + .name = "slv_pka_wrapper", + .id = QCM2290_SLAVE_PKA_WRAPPER, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 281, +}; + +static struct qcom_icc_node slv_pmic_arb = { + .name = "slv_pmic_arb", + .id = QCM2290_SLAVE_PMIC_ARB, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 59, +}; + +static struct qcom_icc_node slv_prng = { + .name = "slv_prng", + .id = QCM2290_SLAVE_PRNG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 44, +}; + +static struct qcom_icc_node slv_qdss_cfg = { + .name = "slv_qdss_cfg", + .id = QCM2290_SLAVE_QDSS_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 63, +}; + +static struct qcom_icc_node slv_qm_cfg = { + .name = "slv_qm_cfg", + .id = QCM2290_SLAVE_QM_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 212, +}; + +static struct qcom_icc_node slv_qm_mpu_cfg = { + .name = "slv_qm_mpu_cfg", + .id = QCM2290_SLAVE_QM_MPU_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 231, +}; + +static struct qcom_icc_node slv_qpic = { + .name = "slv_qpic", + .id = QCM2290_SLAVE_QPIC, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 80, +}; + +static struct qcom_icc_node slv_qup_0 = { + .name = "slv_qup_0", + .id = QCM2290_SLAVE_QUP_0, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 261, +}; + +static struct qcom_icc_node slv_sdcc_1 = { + .name = "slv_sdcc_1", + .id = QCM2290_SLAVE_SDCC_1, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 31, +}; + +static struct qcom_icc_node slv_sdcc_2 = { + .name = "slv_sdcc_2", + .id = QCM2290_SLAVE_SDCC_2, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 33, +}; + +static const u16 slv_snoc_cfg_links[] = { + QCM2290_MASTER_SNOC_CFG, +}; + +static struct qcom_icc_node slv_snoc_cfg = { + .name = "slv_snoc_cfg", + .id = QCM2290_SLAVE_SNOC_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 70, + .num_links = ARRAY_SIZE(slv_snoc_cfg_links), + .links = slv_snoc_cfg_links, +}; + +static struct qcom_icc_node slv_tcsr = { + .name = "slv_tcsr", + .id = QCM2290_SLAVE_TCSR, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 50, +}; + +static struct qcom_icc_node slv_usb3 = { + .name = "slv_usb3", + .id = QCM2290_SLAVE_USB3, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 22, +}; + +static struct qcom_icc_node slv_venus_cfg = { + .name = "slv_venus_cfg", + .id = QCM2290_SLAVE_VENUS_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 10, +}; + +static struct qcom_icc_node slv_venus_throttle_cfg = { + .name = "slv_venus_throttle_cfg", + .id = QCM2290_SLAVE_VENUS_THROTTLE_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 178, +}; + +static struct qcom_icc_node slv_vsense_ctrl_cfg = { + .name = "slv_vsense_ctrl_cfg", + .id = QCM2290_SLAVE_VSENSE_CTRL_CFG, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 263, +}; + +static struct qcom_icc_node slv_service_cnoc = { + .name = "slv_service_cnoc", + .id = QCM2290_SLAVE_SERVICE_CNOC, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 76, +}; + +static struct qcom_icc_node slv_qup_core_0 = { + .name = "slv_qup_core_0", + .id = QCM2290_SLAVE_QUP_CORE_0, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 264, +}; + +static const u16 slv_snoc_bimc_nrt_links[] = { + QCM2290_MASTER_SNOC_BIMC_NRT, +}; + +static struct qcom_icc_node slv_snoc_bimc_nrt = { + .name = "slv_snoc_bimc_nrt", + .id = QCM2290_SLAVE_SNOC_BIMC_NRT, + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 259, + .num_links = ARRAY_SIZE(slv_snoc_bimc_nrt_links), + .links = slv_snoc_bimc_nrt_links, +}; + +static const u16 slv_snoc_bimc_rt_links[] = { + QCM2290_MASTER_SNOC_BIMC_RT, +}; + +static struct qcom_icc_node slv_snoc_bimc_rt = { + .name = "slv_snoc_bimc_rt", + .id = QCM2290_SLAVE_SNOC_BIMC_RT, + .buswidth = 16, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 260, + .num_links = ARRAY_SIZE(slv_snoc_bimc_rt_links), + .links = slv_snoc_bimc_rt_links, +}; + +static struct qcom_icc_node slv_appss = { + .name = "slv_appss", + .id = QCM2290_SLAVE_APPSS, + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 20, +}; + +static const u16 slv_snoc_cnoc_links[] = { + QCM2290_MASTER_SNOC_CNOC, +}; + +static struct qcom_icc_node slv_snoc_cnoc = { + .name = "slv_snoc_cnoc", + .id = QCM2290_SLAVE_SNOC_CNOC, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 25, + .num_links = ARRAY_SIZE(slv_snoc_cnoc_links), + .links = slv_snoc_cnoc_links, +}; + +static struct qcom_icc_node slv_imem = { + .name = "slv_imem", + .id = QCM2290_SLAVE_IMEM, + .buswidth = 8, + .mas_rpm_id = -1, + .slv_rpm_id = 26, +}; + +static struct qcom_icc_node slv_pimem = { + .name = "slv_pimem", + .id = QCM2290_SLAVE_PIMEM, + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 166, +}; + +static const u16 slv_snoc_bimc_links[] = { + QCM2290_MASTER_SNOC_BIMC, +}; + +static struct qcom_icc_node slv_snoc_bimc = { + .name = "slv_snoc_bimc", + .id = QCM2290_SLAVE_SNOC_BIMC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 24, + .num_links = ARRAY_SIZE(slv_snoc_bimc_links), + .links = slv_snoc_bimc_links, +}; + +static struct qcom_icc_node slv_service_snoc = { + .name = "slv_service_snoc", + .id = QCM2290_SLAVE_SERVICE_SNOC, + .buswidth = 4, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 29, +}; + +static struct qcom_icc_node slv_qdss_stm = { + .name = "slv_qdss_stm", + .id = QCM2290_SLAVE_QDSS_STM, + .buswidth = 4, + .mas_rpm_id = -1, + .slv_rpm_id = 30, +}; + +static struct qcom_icc_node slv_tcu = { + .name = "slv_tcu", + .id = QCM2290_SLAVE_TCU, + .buswidth = 8, + .qos.ap_owned = true, + .qos.qos_mode = NOC_QOS_MODE_INVALID, + .mas_rpm_id = -1, + .slv_rpm_id = 133, +}; + +static const u16 slv_anoc_snoc_links[] = { + QCM2290_MASTER_ANOC_SNOC, +}; + +static struct qcom_icc_node slv_anoc_snoc = { + .name = "slv_anoc_snoc", + .id = QCM2290_SLAVE_ANOC_SNOC, + .buswidth = 16, + .mas_rpm_id = -1, + .slv_rpm_id = 141, + .num_links = ARRAY_SIZE(slv_anoc_snoc_links), + .links = slv_anoc_snoc_links, +}; + +/* NoC descriptors */ +static struct qcom_icc_node *qcm2290_bimc_nodes[] = { + [MASTER_APPSS_PROC] = &mas_appss_proc, + [MASTER_SNOC_BIMC_RT] = &mas_snoc_bimc_rt, + [MASTER_SNOC_BIMC_NRT] = &mas_snoc_bimc_nrt, + [MASTER_SNOC_BIMC] = &mas_snoc_bimc, + [MASTER_TCU_0] = &mas_tcu_0, + [MASTER_GFX3D] = &mas_gfx3d, + [SLAVE_EBI1] = &slv_ebi1, + [SLAVE_BIMC_SNOC] = &slv_bimc_snoc, +}; + +static const struct regmap_config qcm2290_bimc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x80000, + .fast_io = true, +}; + +static struct qcom_icc_desc qcm2290_bimc = { + .type = QCOM_ICC_BIMC, + .nodes = qcm2290_bimc_nodes, + .num_nodes = ARRAY_SIZE(qcm2290_bimc_nodes), + .regmap_cfg = &qcm2290_bimc_regmap_config, + /* M_REG_BASE() in vendor msm_bus_bimc_adhoc driver */ + .qos_offset = 0x8000, +}; + +static struct qcom_icc_node *qcm2290_cnoc_nodes[] = { + [MASTER_SNOC_CNOC] = &mas_snoc_cnoc, + [MASTER_QDSS_DAP] = &mas_qdss_dap, + [SLAVE_BIMC_CFG] = &slv_bimc_cfg, + [SLAVE_CAMERA_NRT_THROTTLE_CFG] = &slv_camera_nrt_throttle_cfg, + [SLAVE_CAMERA_RT_THROTTLE_CFG] = &slv_camera_rt_throttle_cfg, + [SLAVE_CAMERA_CFG] = &slv_camera_cfg, + [SLAVE_CLK_CTL] = &slv_clk_ctl, + [SLAVE_CRYPTO_0_CFG] = &slv_crypto_0_cfg, + [SLAVE_DISPLAY_CFG] = &slv_display_cfg, + [SLAVE_DISPLAY_THROTTLE_CFG] = &slv_display_throttle_cfg, + [SLAVE_GPU_CFG] = &slv_gpu_cfg, + [SLAVE_HWKM] = &slv_hwkm, + [SLAVE_IMEM_CFG] = &slv_imem_cfg, + [SLAVE_IPA_CFG] = &slv_ipa_cfg, + [SLAVE_LPASS] = &slv_lpass, + [SLAVE_MESSAGE_RAM] = &slv_message_ram, + [SLAVE_PDM] = &slv_pdm, + [SLAVE_PIMEM_CFG] = &slv_pimem_cfg, + [SLAVE_PKA_WRAPPER] = &slv_pka_wrapper, + [SLAVE_PMIC_ARB] = &slv_pmic_arb, + [SLAVE_PRNG] = &slv_prng, + [SLAVE_QDSS_CFG] = &slv_qdss_cfg, + [SLAVE_QM_CFG] = &slv_qm_cfg, + [SLAVE_QM_MPU_CFG] = &slv_qm_mpu_cfg, + [SLAVE_QPIC] = &slv_qpic, + [SLAVE_QUP_0] = &slv_qup_0, + [SLAVE_SDCC_1] = &slv_sdcc_1, + [SLAVE_SDCC_2] = &slv_sdcc_2, + [SLAVE_SNOC_CFG] = &slv_snoc_cfg, + [SLAVE_TCSR] = &slv_tcsr, + [SLAVE_USB3] = &slv_usb3, + [SLAVE_VENUS_CFG] = &slv_venus_cfg, + [SLAVE_VENUS_THROTTLE_CFG] = &slv_venus_throttle_cfg, + [SLAVE_VSENSE_CTRL_CFG] = &slv_vsense_ctrl_cfg, + [SLAVE_SERVICE_CNOC] = &slv_service_cnoc, +}; + +static const struct regmap_config qcm2290_cnoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8200, + .fast_io = true, +}; + +static struct qcom_icc_desc qcm2290_cnoc = { + .type = QCOM_ICC_NOC, + .nodes = qcm2290_cnoc_nodes, + .num_nodes = ARRAY_SIZE(qcm2290_cnoc_nodes), + .regmap_cfg = &qcm2290_cnoc_regmap_config, +}; + +static struct qcom_icc_node *qcm2290_snoc_nodes[] = { + [MASTER_CRYPTO_CORE0] = &mas_crypto_core0, + [MASTER_SNOC_CFG] = &mas_snoc_cfg, + [MASTER_TIC] = &mas_tic, + [MASTER_ANOC_SNOC] = &mas_anoc_snoc, + [MASTER_BIMC_SNOC] = &mas_bimc_snoc, + [MASTER_PIMEM] = &mas_pimem, + [MASTER_QDSS_BAM] = &mas_qdss_bam, + [MASTER_QUP_0] = &mas_qup_0, + [MASTER_IPA] = &mas_ipa, + [MASTER_QDSS_ETR] = &mas_qdss_etr, + [MASTER_SDCC_1] = &mas_sdcc_1, + [MASTER_SDCC_2] = &mas_sdcc_2, + [MASTER_QPIC] = &mas_qpic, + [MASTER_USB3_0] = &mas_usb3_0, + [SLAVE_APPSS] = &slv_appss, + [SLAVE_SNOC_CNOC] = &slv_snoc_cnoc, + [SLAVE_IMEM] = &slv_imem, + [SLAVE_PIMEM] = &slv_pimem, + [SLAVE_SNOC_BIMC] = &slv_snoc_bimc, + [SLAVE_SERVICE_SNOC] = &slv_service_snoc, + [SLAVE_QDSS_STM] = &slv_qdss_stm, + [SLAVE_TCU] = &slv_tcu, + [SLAVE_ANOC_SNOC] = &slv_anoc_snoc, +}; + +static const struct regmap_config qcm2290_snoc_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x60200, + .fast_io = true, +}; + +static struct qcom_icc_desc qcm2290_snoc = { + .type = QCOM_ICC_QNOC, + .nodes = qcm2290_snoc_nodes, + .num_nodes = ARRAY_SIZE(qcm2290_snoc_nodes), + .regmap_cfg = &qcm2290_snoc_regmap_config, + /* Vendor DT node fab-sys_noc property 'qcom,base-offset' */ + .qos_offset = 0x15000, +}; + +static struct qcom_icc_node *qcm2290_qup_virt_nodes[] = { + [MASTER_QUP_CORE_0] = &mas_qup_core_0, + [SLAVE_QUP_CORE_0] = &slv_qup_core_0 +}; + +static struct qcom_icc_desc qcm2290_qup_virt = { + .type = QCOM_ICC_QNOC, + .nodes = qcm2290_qup_virt_nodes, + .num_nodes = ARRAY_SIZE(qcm2290_qup_virt_nodes), +}; + +static struct qcom_icc_node *qcm2290_mmnrt_virt_nodes[] = { + [MASTER_CAMNOC_SF] = &mas_camnoc_sf, + [MASTER_VIDEO_P0] = &mas_video_p0, + [MASTER_VIDEO_PROC] = &mas_video_proc, + [SLAVE_SNOC_BIMC_NRT] = &slv_snoc_bimc_nrt, +}; + +static struct qcom_icc_desc qcm2290_mmnrt_virt = { + .type = QCOM_ICC_QNOC, + .nodes = qcm2290_mmnrt_virt_nodes, + .num_nodes = ARRAY_SIZE(qcm2290_mmnrt_virt_nodes), + .regmap_cfg = &qcm2290_snoc_regmap_config, + .qos_offset = 0x15000, +}; + +static struct qcom_icc_node *qcm2290_mmrt_virt_nodes[] = { + [MASTER_CAMNOC_HF] = &mas_camnoc_hf, + [MASTER_MDP0] = &mas_mdp0, + [SLAVE_SNOC_BIMC_RT] = &slv_snoc_bimc_rt, +}; + +static struct qcom_icc_desc qcm2290_mmrt_virt = { + .type = QCOM_ICC_QNOC, + .nodes = qcm2290_mmrt_virt_nodes, + .num_nodes = ARRAY_SIZE(qcm2290_mmrt_virt_nodes), + .regmap_cfg = &qcm2290_snoc_regmap_config, + .qos_offset = 0x15000, +}; + +static const struct of_device_id qcm2290_noc_of_match[] = { + { .compatible = "qcom,qcm2290-bimc", .data = &qcm2290_bimc }, + { .compatible = "qcom,qcm2290-cnoc", .data = &qcm2290_cnoc }, + { .compatible = "qcom,qcm2290-snoc", .data = &qcm2290_snoc }, + { .compatible = "qcom,qcm2290-qup-virt", .data = &qcm2290_qup_virt }, + { .compatible = "qcom,qcm2290-mmrt-virt", .data = &qcm2290_mmrt_virt }, + { .compatible = "qcom,qcm2290-mmnrt-virt", .data = &qcm2290_mmnrt_virt }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcm2290_noc_of_match); + +static struct platform_driver qcm2290_noc_driver = { + .probe = qnoc_probe, + .remove = qnoc_remove, + .driver = { + .name = "qnoc-qcm2290", + .of_match_table = qcm2290_noc_of_match, + }, +}; +module_platform_driver(qcm2290_noc_driver); + +MODULE_DESCRIPTION("Qualcomm QCM2290 NoC driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From c8d09c7ebcffcbc734eee45c92f11d6ec8884b92 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Wed, 15 Dec 2021 11:38:34 +0530 Subject: phy: freescale: pcie: explicitly add bitfield.h kernel test robot complains about missing FIELD_PREP, so include bitfield.h for that drivers/phy/freescale/phy-fsl-imx8m-pcie.c:41:37: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration] drivers/phy/freescale/phy-fsl-imx8m-pcie.c:41:41: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration] Reported-by: kernel test robot Fixes: 1aa97b002258 ("phy: freescale: pcie: Initialize the imx8 pcie standalone phy driver") Signed-off-by: Vinod Koul Reviewed-by: Richard Zhu Link: https://lore.kernel.org/r/20211215060834.921617-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-imx8m-pcie.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c index f6502463d49a..04b1aafb29f4 100644 --- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -3,6 +3,7 @@ * Copyright 2021 NXP */ +#include #include #include #include -- cgit v1.2.3 From d8f0136919128135b0a7a7e3a05dca5b569eef45 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 13 Dec 2021 18:44:48 +0530 Subject: dt-bindings: phy: qcom,usb-snps-femto-v2: Add bindings for SM8450 Document the compatible string for USB phy found in Qualcomm SM8450 SoC Signed-off-by: Vinod Koul Acked-by: Rob Herring Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213131450.535775-1-vkoul@kernel.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml index 20203a8a9e41..0dfe6914ec87 100644 --- a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml @@ -20,6 +20,7 @@ properties: - qcom,sm8150-usb-hs-phy - qcom,sm8250-usb-hs-phy - qcom,sm8350-usb-hs-phy + - qcom,sm8450-usb-hs-phy - qcom,usb-snps-femto-v2-phy reg: -- cgit v1.2.3 From 03eacc3c6523749294d2d0922591c0ad78a0b633 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 13 Dec 2021 18:44:49 +0530 Subject: dt-bindings: phy: qcom,qmp: Add SM8450 USB3 PHY Add compatible string for USB QMP phy in Qualcomm SM8450 SoC Signed-off-by: Vinod Koul Acked-by: Rob Herring Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213131450.535775-2-vkoul@kernel.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index c59bbca9a900..d625a6fe0205 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -51,6 +51,7 @@ properties: - qcom,sm8350-qmp-usb3-phy - qcom,sm8350-qmp-usb3-uni-phy - qcom,sm8450-qmp-ufs-phy + - qcom,sm8450-qmp-usb3-phy - qcom,sdx55-qmp-pcie-phy - qcom,sdx55-qmp-usb3-uni-phy -- cgit v1.2.3 From 6ad102e05d211aba0ee9c811936eda4341ee5a75 Mon Sep 17 00:00:00 2001 From: Vinod Koul Date: Mon, 13 Dec 2021 18:44:50 +0530 Subject: phy: qcom-qmp: Add SM8450 USB QMP PHYs Add support for the USB DP & UNI PHYs found on SM8450. This is same as the phy version used on SM8350 and sequences turned out to be same, so use the same table from SM8350 for this as well. Signed-off-by: Vinod Koul Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211213131450.535775-3-vkoul@kernel.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index a959c97a699f..13a249ec8ab6 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -5777,6 +5777,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8450-qmp-ufs-phy", .data = &sm8450_ufsphy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-usb3-phy", + .data = &sm8350_usb3phy_cfg, }, { .compatible = "qcom,qcm2290-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, -- cgit v1.2.3 From 8b7651f2596238ca54225ebbcfbd3f14a4c41887 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Sun, 5 Dec 2021 13:50:52 +0100 Subject: iio: iio_device_alloc(): Remove unnecessary self drvdata Drvdata is typically used by drivers to attach driver specific data to a device. It is used to retrieve driver specific information when only the device to which the data is attached is available. In the IIO core in the `iio_device_alloc()` function we call `iio_device_set_drvdata(indio_dev, indio_dev)`. This sets the drvdata of the IIO device to itself. This is rather unnecessary since if we have a pointer to the IIO device to call `iio_device_get_drvdata()` on it we don't need to call the function since we already have the pointer. If we only have a pointer to the `struct device` we can use `dev_to_iio_dev()` to get the IIO device from it. Furthermore the drvdata is supposed to be reserved for drivers, so it should not be used by the IIO core in the first place. The `set_drvdata()` has been around from the very beginning of the IIO framework and back then it was used in the IIO device sysfs attribute handling code. But that was subsequently replaced with a `dev_to_iio_dev()` in commit e53f5ac52ec1 ("iio: Use dev_to_iio_dev()") and other cleanups. The self `set_drvdata()` is now no longer needed and can be removed. Verified that there no longer any users by checking for potential users using the following two coccinelle scripts and reviewing that none of the matches are problematic code. @@ struct iio_dev *iio_dev; expression dev; identifier fn !~ "(remove|resume|suspend)"; @@ fn(...) { ... *iio_dev = dev_get_drvdata(dev) ... } @r1@ position p; struct iio_dev *indio_dev; identifier dev_fn =~ "^dev_"; identifier devm_fn =~ "^devm_"; @@ ( dev_fn | devm_fn ) (&indio_dev@p->dev, ...) @@ struct iio_dev *indio_dev; position p != r1.p; @@ *&indio_dev@p->dev Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 20d5178ca073..409c278a4c2c 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -1656,7 +1656,6 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) indio_dev->dev.type = &iio_device_type; indio_dev->dev.bus = &iio_bus_type; device_initialize(&indio_dev->dev); - iio_device_set_drvdata(indio_dev, (void *)indio_dev); mutex_init(&indio_dev->mlock); mutex_init(&iio_dev_opaque->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); -- cgit v1.2.3 From c054fe9936065759b21b493ac1e7d9a2be014083 Mon Sep 17 00:00:00 2001 From: Zach DeCook Date: Wed, 8 Dec 2021 16:48:08 -0500 Subject: iio: event_monitor: Flush output on event By flushing the output, iio_event_monitor can be more useful to programs chained along with it. iio_event_monitor stk3310 | awk '/rising/{system("my_unlockscreen.sh")} /falling/{system("my_lockscreen.sh")}' Without this flush, the above example would buffer a number of events, then after a while run the lock/unlock scripts several times. Signed-off-by: Zach DeCook Signed-off-by: Jonathan Cameron --- tools/iio/iio_event_monitor.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/iio/iio_event_monitor.c b/tools/iio/iio_event_monitor.c index 0076437f6e3f..b94a16ba5c6c 100644 --- a/tools/iio/iio_event_monitor.c +++ b/tools/iio/iio_event_monitor.c @@ -279,6 +279,7 @@ static void print_event(struct iio_event_data *event) printf(", direction: %s", iio_ev_dir_text[dir]); printf("\n"); + fflush(stdout); } /* Enable or disable events in sysfs if the knob is available */ -- cgit v1.2.3 From 0a52c3f347fd0173a6aa718bffedca90816ddac6 Mon Sep 17 00:00:00 2001 From: Xiang wangx Date: Sun, 12 Dec 2021 22:41:18 +0800 Subject: iio: adc: ad7606: Fix syntax errors in comments Delete the redundant word 'the'. Signed-off-by: Xiang wangx Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7606.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h index 9350ef1f63b5..4f82d7c9acfd 100644 --- a/drivers/iio/adc/ad7606.h +++ b/drivers/iio/adc/ad7606.h @@ -62,7 +62,7 @@ struct ad7606_chip_info { * struct ad7606_state - driver instance specific data * @dev pointer to kernel device * @chip_info entry in the table of chips that describes this device - * @reg regulator info for the the power supply of the device + * @reg regulator info for the power supply of the device * @bops bus operations (SPI or parallel) * @range voltage range selection, selects which scale to apply * @oversampling oversampling selection -- cgit v1.2.3 From 5d97d9e9a703be2a602ac24c1ba3dae22155a2c8 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Wed, 15 Dec 2021 14:50:53 +0300 Subject: iio: addac: ad74413r: fix off by one in ad74413r_parse_channel_config() The > needs to be >= to prevent accessing one element beyond the end of the st->channel_configs[] array. Fixes: fea251b6a5db ("iio: addac: add AD74413R driver") Signed-off-by: Dan Carpenter Reviewed-by: Cosmin Tanislav Signed-off-by: Jonathan Cameron --- drivers/iio/addac/ad74413r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index cbd9aa9b399a..289d254943e1 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -1150,7 +1150,7 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, return ret; } - if (index > AD74413R_CHANNEL_MAX) { + if (index >= AD74413R_CHANNEL_MAX) { dev_err(st->dev, "Channel index %u is too large\n", index); return -EINVAL; } -- cgit v1.2.3 From 8a457852bc12c16968c025cce6a7005b41fafa87 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Wed, 15 Dec 2021 06:07:10 +0000 Subject: iio:adc:ti-ads8688:: remove redundant ret variable Return value from ads8688_prog_write() directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Signed-off-by: Minghao Chi Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads8688.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index 79c803537dc4..2e24717d7f55 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -281,12 +281,10 @@ static int ads8688_write_reg_range(struct iio_dev *indio_dev, enum ads8688_range range) { unsigned int tmp; - int ret; tmp = ADS8688_PROG_REG_RANGE_CH(chan->channel); - ret = ads8688_prog_write(indio_dev, tmp, range); - return ret; + return ads8688_prog_write(indio_dev, tmp, range); } static int ads8688_write_raw(struct iio_dev *indio_dev, -- cgit v1.2.3 From 3511989cd22b06599b98e2566ecb571b846ffb86 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 15 Dec 2021 15:25:13 -0800 Subject: iio: stmpe-adc: Use correctly sized arguments for bit field The find.h APIs are designed to be used only on unsigned long arguments. This can technically result in a over-read, but it is harmless in this case. Regardless, fix it to avoid the warning seen under -Warray-bounds, which we'd like to enable globally: In file included from ./include/linux/bitmap.h:9, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/cpumask.h:5, from ./arch/x86/include/asm/msr.h:11, from ./arch/x86/include/asm/processor.h:22, from ./arch/x86/include/asm/cpufeature.h:5, from ./arch/x86/include/asm/thread_info.h:53, from ./include/linux/thread_info.h:60, from ./arch/x86/include/asm/preempt.h:7, from ./include/linux/preempt.h:78, from ./include/linux/spinlock.h:55, from ./include/linux/swait.h:7, from ./include/linux/completion.h:12, from drivers/iio/adc/stmpe-adc.c:10: drivers/iio/adc/stmpe-adc.c: In function 'stmpe_adc_probe': ./include/linux/find.h:98:23: warning: array subscript 'long unsigned int[0]' is partly outside array bounds of 'u32[1]' {aka 'unsigned int[1]'} [-Warray-bounds] 98 | val = *addr | ~GENMASK(size - 1, offset); | ^~~~~ drivers/iio/adc/stmpe-adc.c:258:13: note: while referencing 'norequest_mask' 258 | u32 norequest_mask = 0; | ^~~~~~~~~~~~~~ Signed-off-by: Kees Cook Signed-off-by: Jonathan Cameron --- drivers/iio/adc/stmpe-adc.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c index fba659bfdb40..d2d405388499 100644 --- a/drivers/iio/adc/stmpe-adc.c +++ b/drivers/iio/adc/stmpe-adc.c @@ -256,6 +256,7 @@ static int stmpe_adc_probe(struct platform_device *pdev) struct stmpe_adc *info; struct device_node *np; u32 norequest_mask = 0; + unsigned long bits; int irq_temp, irq_adc; int num_chan = 0; int i = 0; @@ -309,8 +310,8 @@ static int stmpe_adc_probe(struct platform_device *pdev) of_property_read_u32(np, "st,norequest-mask", &norequest_mask); - for_each_clear_bit(i, (unsigned long *) &norequest_mask, - (STMPE_ADC_LAST_NR + 1)) { + bits = norequest_mask; + for_each_clear_bit(i, &bits, (STMPE_ADC_LAST_NR + 1)) { stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i); num_chan++; } -- cgit v1.2.3 From 91b49aadbabf6860a8dae45df7aa982ca058b203 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Dec 2021 17:17:28 +0100 Subject: iio: as3935: Remove unnecessary cast `buf` is cast to a const char *, but `buf` is already a const char *, so the case is unnecessary. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/as3935.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index d62766b6b39e..51f4f92ae84a 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -133,7 +133,7 @@ static ssize_t as3935_sensor_sensitivity_store(struct device *dev, unsigned long val; int ret; - ret = kstrtoul((const char *) buf, 10, &val); + ret = kstrtoul(buf, 10, &val); if (ret) return -EINVAL; -- cgit v1.2.3 From 52c65f5b095782abb1accbacfe6f6962a583fe05 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Dec 2021 17:17:29 +0100 Subject: iio: in2xx-adc: Remove unnecessary cast `buf` is cast to a const char *, but `buf` is already a const char *, so the case is unnecessary. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ina2xx-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 352f27657238..08f243f5b92b 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -550,7 +550,7 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev, bool val; int ret; - ret = strtobool((const char *) buf, &val); + ret = strtobool(buf, &val); if (ret) return ret; -- cgit v1.2.3 From 79ca243d83415c8f43e1c071b744fd8db8c1ccc1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 9 Dec 2021 17:17:30 +0100 Subject: iio: vz89x: Remove unnecessary cast The case to u8 * is unnecessary here since the expression is already of type u8 *. Signed-off-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/vz89x.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 23b22a5f5c1c..e7e1c74a351e 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -242,7 +242,7 @@ static int vz89x_get_resistance_reading(struct vz89x_data *data, struct iio_chan_spec const *chan, int *val) { - u8 *tmp = (u8 *) &data->buffer[chan->address]; + u8 *tmp = &data->buffer[chan->address]; switch (chan->scan_type.endianness) { case IIO_LE: -- cgit v1.2.3 From 35c35b0c4161273e22d1bfb17e935d5dd7cefa8e Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 7 Dec 2021 17:54:42 +0200 Subject: iio: add filter subfolder Add filter subfolder for IIO devices that handle filter functionality. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/Kconfig | 1 + drivers/iio/Makefile | 1 + drivers/iio/filter/Kconfig | 8 ++++++++ drivers/iio/filter/Makefile | 6 ++++++ 4 files changed, 16 insertions(+) create mode 100644 drivers/iio/filter/Kconfig create mode 100644 drivers/iio/filter/Makefile diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 4fb4321a72cb..b190846c3dc2 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -78,6 +78,7 @@ source "drivers/iio/chemical/Kconfig" source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" source "drivers/iio/dummy/Kconfig" +source "drivers/iio/filter/Kconfig" source "drivers/iio/frequency/Kconfig" source "drivers/iio/gyro/Kconfig" source "drivers/iio/health/Kconfig" diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 8d48c70fee4d..3be08cdadd7e 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -25,6 +25,7 @@ obj-y += common/ obj-y += dac/ obj-y += dummy/ obj-y += gyro/ +obj-y += filter/ obj-y += frequency/ obj-y += health/ obj-y += humidity/ diff --git a/drivers/iio/filter/Kconfig b/drivers/iio/filter/Kconfig new file mode 100644 index 000000000000..e268bba43852 --- /dev/null +++ b/drivers/iio/filter/Kconfig @@ -0,0 +1,8 @@ +# +# Filter drivers +# +# When adding new entries keep the list in alphabetical order + +menu "Filters" + +endmenu diff --git a/drivers/iio/filter/Makefile b/drivers/iio/filter/Makefile new file mode 100644 index 000000000000..cc0892c01142 --- /dev/null +++ b/drivers/iio/filter/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O Filter drivers +# + +# When adding new entries keep the list in alphabetical order -- cgit v1.2.3 From f34fe888ad0546dacf678aa604435d442934984f Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 7 Dec 2021 17:54:43 +0200 Subject: iio:filter:admv8818: add support for ADMV8818 The ADMV8818-EP is a fully monolithic microwave integrated circuit (MMIC) that features a digitally selectable frequency of operation. The device features four independently controlled high- pass filters (HPFs) and four independently controlled low-pass filters (LPFs) that span the 2 GHz to 18 GHz frequency range. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/admv8818-ep.pdf Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- drivers/iio/filter/Kconfig | 10 + drivers/iio/filter/Makefile | 1 + drivers/iio/filter/admv8818.c | 665 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 676 insertions(+) create mode 100644 drivers/iio/filter/admv8818.c diff --git a/drivers/iio/filter/Kconfig b/drivers/iio/filter/Kconfig index e268bba43852..3ae35817ad82 100644 --- a/drivers/iio/filter/Kconfig +++ b/drivers/iio/filter/Kconfig @@ -5,4 +5,14 @@ menu "Filters" +config ADMV8818 + tristate "Analog Devices ADMV8818 High-Pass and Low-Pass Filter" + depends on SPI && COMMON_CLK && 64BIT + help + Say yes here to build support for Analog Devices ADMV8818 + 2 GHz to 18 GHz, Digitally Tunable, High-Pass and Low-Pass Filter. + + To compile this driver as a module, choose M here: the + modiule will be called admv8818. + endmenu diff --git a/drivers/iio/filter/Makefile b/drivers/iio/filter/Makefile index cc0892c01142..55e228c0dd20 100644 --- a/drivers/iio/filter/Makefile +++ b/drivers/iio/filter/Makefile @@ -4,3 +4,4 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_ADMV8818) += admv8818.o diff --git a/drivers/iio/filter/admv8818.c b/drivers/iio/filter/admv8818.c new file mode 100644 index 000000000000..68de45fe21b4 --- /dev/null +++ b/drivers/iio/filter/admv8818.c @@ -0,0 +1,665 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADMV8818 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* ADMV8818 Register Map */ +#define ADMV8818_REG_SPI_CONFIG_A 0x0 +#define ADMV8818_REG_SPI_CONFIG_B 0x1 +#define ADMV8818_REG_CHIPTYPE 0x3 +#define ADMV8818_REG_PRODUCT_ID_L 0x4 +#define ADMV8818_REG_PRODUCT_ID_H 0x5 +#define ADMV8818_REG_FAST_LATCH_POINTER 0x10 +#define ADMV8818_REG_FAST_LATCH_STOP 0x11 +#define ADMV8818_REG_FAST_LATCH_START 0x12 +#define ADMV8818_REG_FAST_LATCH_DIRECTION 0x13 +#define ADMV8818_REG_FAST_LATCH_STATE 0x14 +#define ADMV8818_REG_WR0_SW 0x20 +#define ADMV8818_REG_WR0_FILTER 0x21 +#define ADMV8818_REG_WR1_SW 0x22 +#define ADMV8818_REG_WR1_FILTER 0x23 +#define ADMV8818_REG_WR2_SW 0x24 +#define ADMV8818_REG_WR2_FILTER 0x25 +#define ADMV8818_REG_WR3_SW 0x26 +#define ADMV8818_REG_WR3_FILTER 0x27 +#define ADMV8818_REG_WR4_SW 0x28 +#define ADMV8818_REG_WR4_FILTER 0x29 +#define ADMV8818_REG_LUT0_SW 0x100 +#define ADMV8818_REG_LUT0_FILTER 0x101 +#define ADMV8818_REG_LUT127_SW 0x1FE +#define ADMV8818_REG_LUT127_FILTER 0x1FF + +/* ADMV8818_REG_SPI_CONFIG_A Map */ +#define ADMV8818_SOFTRESET_N_MSK BIT(7) +#define ADMV8818_LSB_FIRST_N_MSK BIT(6) +#define ADMV8818_ENDIAN_N_MSK BIT(5) +#define ADMV8818_SDOACTIVE_N_MSK BIT(4) +#define ADMV8818_SDOACTIVE_MSK BIT(3) +#define ADMV8818_ENDIAN_MSK BIT(2) +#define ADMV8818_LSBFIRST_MSK BIT(1) +#define ADMV8818_SOFTRESET_MSK BIT(0) + +/* ADMV8818_REG_SPI_CONFIG_B Map */ +#define ADMV8818_SINGLE_INSTRUCTION_MSK BIT(7) +#define ADMV8818_CSB_STALL_MSK BIT(6) +#define ADMV8818_MASTER_SLAVE_RB_MSK BIT(5) +#define ADMV8818_MASTER_SLAVE_TRANSFER_MSK BIT(0) + +/* ADMV8818_REG_WR0_SW Map */ +#define ADMV8818_SW_IN_SET_WR0_MSK BIT(7) +#define ADMV8818_SW_OUT_SET_WR0_MSK BIT(6) +#define ADMV8818_SW_IN_WR0_MSK GENMASK(5, 3) +#define ADMV8818_SW_OUT_WR0_MSK GENMASK(2, 0) + +/* ADMV8818_REG_WR0_FILTER Map */ +#define ADMV8818_HPF_WR0_MSK GENMASK(7, 4) +#define ADMV8818_LPF_WR0_MSK GENMASK(3, 0) + +enum { + ADMV8818_BW_FREQ, + ADMV8818_CENTER_FREQ +}; + +enum { + ADMV8818_AUTO_MODE, + ADMV8818_MANUAL_MODE, +}; + +struct admv8818_state { + struct spi_device *spi; + struct regmap *regmap; + struct clk *clkin; + struct notifier_block nb; + /* Protect against concurrent accesses to the device and data content*/ + struct mutex lock; + unsigned int filter_mode; + u64 cf_hz; +}; + +static const unsigned long long freq_range_hpf[4][2] = { + {1750000000ULL, 3550000000ULL}, + {3400000000ULL, 7250000000ULL}, + {6600000000, 12000000000}, + {12500000000, 19900000000} +}; + +static const unsigned long long freq_range_lpf[4][2] = { + {2050000000ULL, 3850000000ULL}, + {3350000000ULL, 7250000000ULL}, + {7000000000, 13000000000}, + {12550000000, 18500000000} +}; + +static const struct regmap_config admv8818_regmap_config = { + .reg_bits = 16, + .val_bits = 8, + .read_flag_mask = 0x80, + .max_register = 0x1FF, +}; + +static const char * const admv8818_modes[] = { + [0] = "auto", + [1] = "manual" +}; + +static int __admv8818_hpf_select(struct admv8818_state *st, u64 freq) +{ + unsigned int hpf_step = 0, hpf_band = 0, i, j; + u64 freq_step; + int ret; + + if (freq < freq_range_hpf[0][0]) + goto hpf_write; + + if (freq > freq_range_hpf[3][1]) { + hpf_step = 15; + hpf_band = 4; + + goto hpf_write; + } + + for (i = 0; i < 4; i++) { + freq_step = div_u64((freq_range_hpf[i][1] - + freq_range_hpf[i][0]), 15); + + if (freq > freq_range_hpf[i][0] && + (freq < freq_range_hpf[i][1] + freq_step)) { + hpf_band = i + 1; + + for (j = 1; j <= 16; j++) { + if (freq < (freq_range_hpf[i][0] + (freq_step * j))) { + hpf_step = j - 1; + break; + } + } + break; + } + } + + /* Close HPF frequency gap between 12 and 12.5 GHz */ + if (freq >= 12000 * HZ_PER_MHZ && freq <= 12500 * HZ_PER_MHZ) { + hpf_band = 3; + hpf_step = 15; + } + +hpf_write: + ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW, + ADMV8818_SW_IN_SET_WR0_MSK | + ADMV8818_SW_IN_WR0_MSK, + FIELD_PREP(ADMV8818_SW_IN_SET_WR0_MSK, 1) | + FIELD_PREP(ADMV8818_SW_IN_WR0_MSK, hpf_band)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, + ADMV8818_HPF_WR0_MSK, + FIELD_PREP(ADMV8818_HPF_WR0_MSK, hpf_step)); +} + +static int admv8818_hpf_select(struct admv8818_state *st, u64 freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_hpf_select(st, freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv8818_lpf_select(struct admv8818_state *st, u64 freq) +{ + unsigned int lpf_step = 0, lpf_band = 0, i, j; + u64 freq_step; + int ret; + + if (freq > freq_range_lpf[3][1]) + goto lpf_write; + + if (freq < freq_range_lpf[0][0]) { + lpf_band = 1; + + goto lpf_write; + } + + for (i = 0; i < 4; i++) { + if (freq > freq_range_lpf[i][0] && freq < freq_range_lpf[i][1]) { + lpf_band = i + 1; + freq_step = div_u64((freq_range_lpf[i][1] - freq_range_lpf[i][0]), 15); + + for (j = 0; j <= 15; j++) { + if (freq < (freq_range_lpf[i][0] + (freq_step * j))) { + lpf_step = j; + break; + } + } + break; + } + } + +lpf_write: + ret = regmap_update_bits(st->regmap, ADMV8818_REG_WR0_SW, + ADMV8818_SW_OUT_SET_WR0_MSK | + ADMV8818_SW_OUT_WR0_MSK, + FIELD_PREP(ADMV8818_SW_OUT_SET_WR0_MSK, 1) | + FIELD_PREP(ADMV8818_SW_OUT_WR0_MSK, lpf_band)); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, ADMV8818_REG_WR0_FILTER, + ADMV8818_LPF_WR0_MSK, + FIELD_PREP(ADMV8818_LPF_WR0_MSK, lpf_step)); +} + +static int admv8818_lpf_select(struct admv8818_state *st, u64 freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_lpf_select(st, freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int admv8818_rfin_band_select(struct admv8818_state *st) +{ + int ret; + + st->cf_hz = clk_get_rate(st->clkin); + + mutex_lock(&st->lock); + + ret = __admv8818_hpf_select(st, st->cf_hz); + if (ret) + goto exit; + + ret = __admv8818_lpf_select(st, st->cf_hz); +exit: + mutex_unlock(&st->lock); + return ret; +} + +static int __admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) +{ + unsigned int data, hpf_band, hpf_state; + int ret; + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_SW, &data); + if (ret) + return ret; + + hpf_band = FIELD_GET(ADMV8818_SW_IN_WR0_MSK, data); + if (!hpf_band) { + *hpf_freq = 0; + return ret; + } + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_FILTER, &data); + if (ret) + return ret; + + hpf_state = FIELD_GET(ADMV8818_HPF_WR0_MSK, data); + + *hpf_freq = div_u64(freq_range_hpf[hpf_band - 1][1] - freq_range_hpf[hpf_band - 1][0], 15); + *hpf_freq = freq_range_hpf[hpf_band - 1][0] + (*hpf_freq * hpf_state); + + return ret; +} + +static int admv8818_read_hpf_freq(struct admv8818_state *st, u64 *hpf_freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_read_hpf_freq(st, hpf_freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) +{ + unsigned int data, lpf_band, lpf_state; + int ret; + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_SW, &data); + if (ret) + return ret; + + lpf_band = FIELD_GET(ADMV8818_SW_OUT_WR0_MSK, data); + if (!lpf_band) { + *lpf_freq = 0; + return ret; + } + + ret = regmap_read(st->regmap, ADMV8818_REG_WR0_FILTER, &data); + if (ret) + return ret; + + lpf_state = FIELD_GET(ADMV8818_LPF_WR0_MSK, data); + + *lpf_freq = div_u64(freq_range_lpf[lpf_band - 1][1] - freq_range_lpf[lpf_band - 1][0], 15); + *lpf_freq = freq_range_lpf[lpf_band - 1][0] + (*lpf_freq * lpf_state); + + return ret; +} + +static int admv8818_read_lpf_freq(struct admv8818_state *st, u64 *lpf_freq) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv8818_read_lpf_freq(st, lpf_freq); + mutex_unlock(&st->lock); + + return ret; +} + +static int admv8818_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct admv8818_state *st = iio_priv(indio_dev); + + u64 freq = ((u64)val2 << 32 | (u32)val); + + switch (info) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + return admv8818_lpf_select(st, freq); + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + return admv8818_hpf_select(st, freq); + default: + return -EINVAL; + } +} + +static int admv8818_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct admv8818_state *st = iio_priv(indio_dev); + int ret; + u64 freq; + + switch (info) { + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = admv8818_read_lpf_freq(st, &freq); + if (ret) + return ret; + + *val = (u32)freq; + *val2 = (u32)(freq >> 32); + + return IIO_VAL_INT_64; + case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY: + ret = admv8818_read_hpf_freq(st, &freq); + if (ret) + return ret; + + *val = (u32)freq; + *val2 = (u32)(freq >> 32); + + return IIO_VAL_INT_64; + default: + return -EINVAL; + } +} + +static int admv8818_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct admv8818_state *st = iio_priv(indio_dev); + + if (read_val) + return regmap_read(st->regmap, reg, read_val); + else + return regmap_write(st->regmap, reg, write_val); +} + +static int admv8818_get_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct admv8818_state *st = iio_priv(indio_dev); + + return st->filter_mode; +} + +static int admv8818_set_mode(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + unsigned int mode) +{ + struct admv8818_state *st = iio_priv(indio_dev); + int ret = 0; + + if (!st->clkin) { + if (mode == ADMV8818_MANUAL_MODE) + return 0; + + return -EINVAL; + } + + switch (mode) { + case ADMV8818_AUTO_MODE: + if (!st->filter_mode) + return 0; + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = clk_notifier_register(st->clkin, &st->nb); + if (ret) { + clk_disable_unprepare(st->clkin); + + return ret; + } + + break; + case ADMV8818_MANUAL_MODE: + if (st->filter_mode) + return 0; + + clk_disable_unprepare(st->clkin); + + ret = clk_notifier_unregister(st->clkin, &st->nb); + if (ret) + return ret; + + break; + default: + return -EINVAL; + } + + st->filter_mode = mode; + + return ret; +} + +static const struct iio_info admv8818_info = { + .write_raw = admv8818_write_raw, + .read_raw = admv8818_read_raw, + .debugfs_reg_access = &admv8818_reg_access, +}; + +static const struct iio_enum admv8818_mode_enum = { + .items = admv8818_modes, + .num_items = ARRAY_SIZE(admv8818_modes), + .get = admv8818_get_mode, + .set = admv8818_set_mode, +}; + +static const struct iio_chan_spec_ext_info admv8818_ext_info[] = { + IIO_ENUM("filter_mode", IIO_SHARED_BY_ALL, &admv8818_mode_enum), + IIO_ENUM_AVAILABLE("filter_mode", IIO_SHARED_BY_ALL, &admv8818_mode_enum), + { }, +}; + +#define ADMV8818_CHAN(_channel) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .info_mask_separate = \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \ + BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) \ +} + +#define ADMV8818_CHAN_BW_CF(_channel, _admv8818_ext_info) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 1, \ + .indexed = 1, \ + .channel = _channel, \ + .ext_info = _admv8818_ext_info, \ +} + +static const struct iio_chan_spec admv8818_channels[] = { + ADMV8818_CHAN(0), + ADMV8818_CHAN_BW_CF(0, admv8818_ext_info), +}; + +static int admv8818_freq_change(struct notifier_block *nb, unsigned long action, void *data) +{ + struct admv8818_state *st = container_of(nb, struct admv8818_state, nb); + + if (action == POST_RATE_CHANGE) + return notifier_from_errno(admv8818_rfin_band_select(st)); + + return NOTIFY_OK; +} + +static void admv8818_clk_notifier_unreg(void *data) +{ + struct admv8818_state *st = data; + + if (st->filter_mode == 0) + clk_notifier_unregister(st->clkin, &st->nb); +} + +static void admv8818_clk_disable(void *data) +{ + struct admv8818_state *st = data; + + if (st->filter_mode == 0) + clk_disable_unprepare(st->clkin); +} + +static int admv8818_init(struct admv8818_state *st) +{ + int ret; + struct spi_device *spi = st->spi; + unsigned int chip_id; + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_A, + ADMV8818_SOFTRESET_N_MSK | + ADMV8818_SOFTRESET_MSK, + FIELD_PREP(ADMV8818_SOFTRESET_N_MSK, 1) | + FIELD_PREP(ADMV8818_SOFTRESET_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV8818 Soft Reset failed.\n"); + return ret; + } + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_A, + ADMV8818_SDOACTIVE_N_MSK | + ADMV8818_SDOACTIVE_MSK, + FIELD_PREP(ADMV8818_SDOACTIVE_N_MSK, 1) | + FIELD_PREP(ADMV8818_SDOACTIVE_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV8818 SDO Enable failed.\n"); + return ret; + } + + ret = regmap_read(st->regmap, ADMV8818_REG_CHIPTYPE, &chip_id); + if (ret) { + dev_err(&spi->dev, "ADMV8818 Chip ID read failed.\n"); + return ret; + } + + if (chip_id != 0x1) { + dev_err(&spi->dev, "ADMV8818 Invalid Chip ID.\n"); + return -EINVAL; + } + + ret = regmap_update_bits(st->regmap, ADMV8818_REG_SPI_CONFIG_B, + ADMV8818_SINGLE_INSTRUCTION_MSK, + FIELD_PREP(ADMV8818_SINGLE_INSTRUCTION_MSK, 1)); + if (ret) { + dev_err(&spi->dev, "ADMV8818 Single Instruction failed.\n"); + return ret; + } + + if (st->clkin) + return admv8818_rfin_band_select(st); + else + return 0; +} + +static int admv8818_clk_setup(struct admv8818_state *st) +{ + struct spi_device *spi = st->spi; + int ret; + + st->clkin = devm_clk_get_optional(&spi->dev, "rf_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the input clock\n"); + else if (!st->clkin) + return 0; + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, admv8818_clk_disable, st); + if (ret) + return ret; + + st->nb.notifier_call = admv8818_freq_change; + ret = clk_notifier_register(st->clkin, &st->nb); + if (ret < 0) + return ret; + + return devm_add_action_or_reset(&spi->dev, admv8818_clk_notifier_unreg, st); +} + +static int admv8818_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct regmap *regmap; + struct admv8818_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + regmap = devm_regmap_init_spi(spi, &admv8818_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + st = iio_priv(indio_dev); + st->regmap = regmap; + + indio_dev->info = &admv8818_info; + indio_dev->name = "admv8818"; + indio_dev->channels = admv8818_channels; + indio_dev->num_channels = ARRAY_SIZE(admv8818_channels); + + st->spi = spi; + + ret = admv8818_clk_setup(st); + if (ret) + return ret; + + mutex_init(&st->lock); + + ret = admv8818_init(st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id admv8818_id[] = { + { "admv8818", 0 }, + {} +}; +MODULE_DEVICE_TABLE(spi, admv8818_id); + +static const struct of_device_id admv8818_of_match[] = { + { .compatible = "adi,admv8818" }, + {} +}; +MODULE_DEVICE_TABLE(of, admv8818_of_match); + +static struct spi_driver admv8818_driver = { + .driver = { + .name = "admv8818", + .of_match_table = admv8818_of_match, + }, + .probe = admv8818_probe, + .id_table = admv8818_id, +}; +module_spi_driver(admv8818_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Tue, 7 Dec 2021 17:54:44 +0200 Subject: dt-bindings:iio:filter: add admv8818 doc Add device tree bindings for the ADMV8818 Filter. Signed-off-by: Antoniu Miclaus Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/filter/adi,admv8818.yaml | 66 ++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/filter/adi,admv8818.yaml diff --git a/Documentation/devicetree/bindings/iio/filter/adi,admv8818.yaml b/Documentation/devicetree/bindings/iio/filter/adi,admv8818.yaml new file mode 100644 index 000000000000..b77e855bd594 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/filter/adi,admv8818.yaml @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/filter/adi,admv8818.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADMV8818 Digitally Tunable, High-Pass and Low-Pass Filter + +maintainers: + - Antoniu Miclaus + +description: | + Fully monolithic microwave integrated circuit (MMIC) that + features a digitally selectable frequency of operation. + The device features four independently controlled high-pass + filters (HPFs) and four independently controlled low-pass filters + (LPFs) that span the 2 GHz to 18 GHz frequency range. + + https://www.analog.com/en/products/admv8818.html + +properties: + compatible: + enum: + - adi,admv8818 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 10000000 + + clocks: + description: + Definition of the external clock. + minItems: 1 + + clock-names: + items: + - const: rf_in + + clock-output-names: + maxItems: 1 + + '#clock-cells': + const: 0 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + admv8818@0 { + compatible = "adi,admv8818"; + reg = <0>; + spi-max-frequency = <10000000>; + clocks = <&admv8818_rfin>; + clock-names = "rf_in"; + }; + }; +... -- cgit v1.2.3 From bf92d87d7c678b29b18e65ae4a97ced4bbe18c06 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 7 Dec 2021 17:54:45 +0200 Subject: iio:filter:admv8818: Add sysfs ABI documentation Add initial ABI documentation for admv8818 filter sysfs interfaces. Signed-off-by: Antoniu Miclaus Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 b/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 new file mode 100644 index 000000000000..f6c035752639 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-filter-admv8818 @@ -0,0 +1,16 @@ +What: /sys/bus/iio/devices/iio:deviceX/filter_mode_available +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Reading this returns the valid values that can be written to the + on_altvoltage0_mode attribute: + + - auto -> Adjust bandpass filter to track changes in input clock rate. + - manual -> disable/unregister the clock rate notifier / input clock tracking. + +What: /sys/bus/iio/devices/iio:deviceX/filter_mode +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + This attribute configures the filter mode. + Reading returns the actual mode. -- cgit v1.2.3 From b0a96c5f599ecf263119d092e22a984d3248fd85 Mon Sep 17 00:00:00 2001 From: Mihail Chindris Date: Mon, 13 Dec 2021 11:08:24 +0000 Subject: dt-bindings: iio: dac: Add adi,ad3552r.yaml Add documentation for ad3552r and ad3542r Signed-off-by: Mihail Chindris Reviewed-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/dac/adi,ad3552r.yaml | 217 +++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml new file mode 100644 index 000000000000..501a463e5d88 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad3552r.yaml @@ -0,0 +1,217 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +# Copyright 2020 Analog Devices Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/dac/adi,ad3552r.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analog Devices AD2552R DAC device driver + +maintainers: + - Mihail Chindris + +description: | + Bindings for the Analog Devices AD3552R DAC device and similar. + Datasheet can be found here: + https://www.analog.com/media/en/technical-documentation/data-sheets/ad3542r.pdf + https://www.analog.com/media/en/technical-documentation/data-sheets/ad3552r.pdf + +properties: + compatible: + enum: + - adi,ad3542r + - adi,ad3552r + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 30000000 + + reset-gpios: + maxItems: 1 + + ldac-gpios: + description: | + LDAC pin to be used as a hardware trigger to update the DAC channels. + maxItems: 1 + + vref-supply: + description: + The regulator to use as an external reference. If it does not exists the + internal reference will be used. External reference must be 2.5V + + adi,vref-out-en: + description: Vref I/O driven by internal vref to 2.5V. If not set, Vref pin + will be floating. + type: boolean + + adi,sdo-drive-strength: + description: | + Configure SDIO0 and SDIO1 strength levels: + - 0: low SDO drive strength. + - 1: medium low SDO drive strength. + - 2: medium high SDO drive strength. + - 3: high SDO drive strength + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + +patternProperties: + "^channel@([0-1])$": + type: object + description: Configurations of the DAC Channels + + additionalProperties: false + + properties: + reg: + description: Channel number + enum: [0, 1] + + adi,output-range-microvolt: true + + custom-output-range-config: + type: object + description: Configuration of custom range when + adi,output-range-microvolt is not present. + The formulas for calculation the output voltages are + Vout_fs = 2.5 + [(GainN + Offset/1024) * 2.5 * Rfbx * 1.03] + Vout_zs = 2.5 - [(GainP + Offset/1024) * 2.5 * Rfbx * 1.03] + + properties: + adi,gain-offset: + description: Gain offset used in the above formula + $ref: /schemas/types.yaml#/definitions/int32 + maximum: 511 + minimum: -511 + + adi,gain-scaling-p-inv-log2: + description: GainP = 1 / ( 2 ^ adi,gain-scaling-p-inv-log2) + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + + adi,gain-scaling-n-inv-log2: + description: GainN = 1 / ( 2 ^ adi,gain-scaling-n-inv-log2) + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2, 3] + + adi,rfb-ohms: + description: Feedback Resistor + + required: + - adi,gain-offset + - adi,gain-scaling-p-inv-log2 + - adi,gain-scaling-n-inv-log2 + - adi,rfb-ohms + + required: + - reg + + oneOf: + # If adi,output-range-microvolt is missing, + # custom-output-range-config must be used + - required: + - adi,output-range-microvolt + + - required: + - custom-output-range-config + +allOf: + - if: + properties: + compatible: + contains: + const: adi,ad3542r + then: + patternProperties: + "^channel@([0-1])$": + type: object + properties: + adi,output-range-microvolt: + description: | + Voltage output range of the channel as + Required connections: + Rfb1x for: 0 to 2.5 V; 0 to 3V; 0 to 5 V; + Rfb2x for: 0 to 10 V; 2.5 to 7.5V; -5 to 5 V; + oneOf: + - items: + - const: 0 + - enum: [2500000, 3000000, 5000000, 10000000] + - items: + - const: -2500000 + - const: 7500000 + - items: + - const: -5000000 + - const: 5000000 + + required: + - adi,output-range-microvolt + + - if: + properties: + compatible: + contains: + const: adi,ad3552r + then: + patternProperties: + "^channel@([0-1])$": + type: object + properties: + adi,output-range-microvolt: + description: | + Voltage output range of the channel as + Required connections: + Rfb1x for: 0 to 2.5 V; 0 to 5 V; + Rfb2x for: 0 to 10 V; -5 to 5 V; + Rfb4x for: -10 to 10V + oneOf: + - items: + - const: 0 + - enum: [2500000, 5000000, 10000000] + - items: + - const: -5000000 + - const: 5000000 + - items: + - const: -10000000 + - const: 10000000 + +required: + - compatible + - reg + - spi-max-frequency + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + ad3552r@0 { + compatible = "adi,ad3552r"; + reg = <0>; + spi-max-frequency = <20000000>; + #address-cells = <1>; + #size-cells = <0>; + channel@0 { + reg = <0>; + adi,output-range-microvolt = <0 10000000>; + }; + channel@1 { + reg = <1>; + custom-output-range-config { + adi,gain-offset = <5>; + adi,gain-scaling-p-inv-log2 = <1>; + adi,gain-scaling-n-inv-log2 = <2>; + adi,rfb-ohms = <1>; + }; + }; + }; + }; +... -- cgit v1.2.3 From 8f2b54824b28ba8317c60947b5941d686e3df70d Mon Sep 17 00:00:00 2001 From: Mihail Chindris Date: Mon, 13 Dec 2021 11:08:25 +0000 Subject: drivers:iio:dac: Add AD3552R driver support The AD3552R-16 is a low drift ultrafast, 16-bit accuracy, current output digital-to-analog converter (DAC) designed to generate multiple output voltage span ranges. The AD3552R-16 operates with a fixed 2.5V reference. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ad3552r.pdf Signed-off-by: Mihail Chindris Link: https://lore.kernel.org/r/20211213110825.244347-3-mihail.chindris@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/dac/Kconfig | 10 + drivers/iio/dac/Makefile | 1 + drivers/iio/dac/ad3552r.c | 1138 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1149 insertions(+) create mode 100644 drivers/iio/dac/ad3552r.c diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index b95619f18fa5..bfcf7568de32 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -6,6 +6,16 @@ menu "Digital to analog converters" +config AD3552R + tristate "Analog Devices AD3552R DAC driver" + depends on SPI_MASTER + help + Say yes here to build support for Analog Devices AD3552R + Digital to Analog Converter. + + To compile this driver as a module, choose M here: the + module will be called ad3552r. + config AD5064 tristate "Analog Devices AD5064 and similar multi-channel DAC driver" depends on (SPI_MASTER && I2C!=m) || I2C diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 3c17246ee89b..01a50131572f 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD3552R) += ad3552r.o obj-$(CONFIG_AD5360) += ad5360.o obj-$(CONFIG_AD5380) += ad5380.o obj-$(CONFIG_AD5421) += ad5421.o diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c new file mode 100644 index 000000000000..97f13c0b9631 --- /dev/null +++ b/drivers/iio/dac/ad3552r.c @@ -0,0 +1,1138 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD3552R + * Digital to Analog converter driver + * + * Copyright 2021 Analog Devices Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register addresses */ +/* Primary address space */ +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_A 0x00 +#define AD3552R_MASK_SOFTWARE_RESET (BIT(7) | BIT(0)) +#define AD3552R_MASK_ADDR_ASCENSION BIT(5) +#define AD3552R_MASK_SDO_ACTIVE BIT(4) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_B 0x01 +#define AD3552R_MASK_SINGLE_INST BIT(7) +#define AD3552R_MASK_SHORT_INSTRUCTION BIT(3) +#define AD3552R_REG_ADDR_DEVICE_CONFIG 0x02 +#define AD3552R_MASK_DEVICE_STATUS(n) BIT(4 + (n)) +#define AD3552R_MASK_CUSTOM_MODES GENMASK(3, 2) +#define AD3552R_MASK_OPERATING_MODES GENMASK(1, 0) +#define AD3552R_REG_ADDR_CHIP_TYPE 0x03 +#define AD3552R_MASK_CLASS GENMASK(7, 0) +#define AD3552R_REG_ADDR_PRODUCT_ID_L 0x04 +#define AD3552R_REG_ADDR_PRODUCT_ID_H 0x05 +#define AD3552R_REG_ADDR_CHIP_GRADE 0x06 +#define AD3552R_MASK_GRADE GENMASK(7, 4) +#define AD3552R_MASK_DEVICE_REVISION GENMASK(3, 0) +#define AD3552R_REG_ADDR_SCRATCH_PAD 0x0A +#define AD3552R_REG_ADDR_SPI_REVISION 0x0B +#define AD3552R_REG_ADDR_VENDOR_L 0x0C +#define AD3552R_REG_ADDR_VENDOR_H 0x0D +#define AD3552R_REG_ADDR_STREAM_MODE 0x0E +#define AD3552R_MASK_LENGTH GENMASK(7, 0) +#define AD3552R_REG_ADDR_TRANSFER_REGISTER 0x0F +#define AD3552R_MASK_MULTI_IO_MODE GENMASK(7, 6) +#define AD3552R_MASK_STREAM_LENGTH_KEEP_VALUE BIT(2) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_C 0x10 +#define AD3552R_MASK_CRC_ENABLE (GENMASK(7, 6) |\ + GENMASK(1, 0)) +#define AD3552R_MASK_STRICT_REGISTER_ACCESS BIT(5) +#define AD3552R_REG_ADDR_INTERFACE_STATUS_A 0x11 +#define AD3552R_MASK_INTERFACE_NOT_READY BIT(7) +#define AD3552R_MASK_CLOCK_COUNTING_ERROR BIT(5) +#define AD3552R_MASK_INVALID_OR_NO_CRC BIT(3) +#define AD3552R_MASK_WRITE_TO_READ_ONLY_REGISTER BIT(2) +#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS BIT(1) +#define AD3552R_MASK_REGISTER_ADDRESS_INVALID BIT(0) +#define AD3552R_REG_ADDR_INTERFACE_CONFIG_D 0x14 +#define AD3552R_MASK_ALERT_ENABLE_PULLUP BIT(6) +#define AD3552R_MASK_MEM_CRC_EN BIT(4) +#define AD3552R_MASK_SDO_DRIVE_STRENGTH GENMASK(3, 2) +#define AD3552R_MASK_DUAL_SPI_SYNCHROUNOUS_EN BIT(1) +#define AD3552R_MASK_SPI_CONFIG_DDR BIT(0) +#define AD3552R_REG_ADDR_SH_REFERENCE_CONFIG 0x15 +#define AD3552R_MASK_IDUMP_FAST_MODE BIT(6) +#define AD3552R_MASK_SAMPLE_HOLD_DIFFERENTIAL_USER_EN BIT(5) +#define AD3552R_MASK_SAMPLE_HOLD_USER_TRIM GENMASK(4, 3) +#define AD3552R_MASK_SAMPLE_HOLD_USER_ENABLE BIT(2) +#define AD3552R_MASK_REFERENCE_VOLTAGE_SEL GENMASK(1, 0) +#define AD3552R_REG_ADDR_ERR_ALARM_MASK 0x16 +#define AD3552R_MASK_REF_RANGE_ALARM BIT(6) +#define AD3552R_MASK_CLOCK_COUNT_ERR_ALARM BIT(5) +#define AD3552R_MASK_MEM_CRC_ERR_ALARM BIT(4) +#define AD3552R_MASK_SPI_CRC_ERR_ALARM BIT(3) +#define AD3552R_MASK_WRITE_TO_READ_ONLY_ALARM BIT(2) +#define AD3552R_MASK_PARTIAL_REGISTER_ACCESS_ALARM BIT(1) +#define AD3552R_MASK_REGISTER_ADDRESS_INVALID_ALARM BIT(0) +#define AD3552R_REG_ADDR_ERR_STATUS 0x17 +#define AD3552R_MASK_REF_RANGE_ERR_STATUS BIT(6) +#define AD3552R_MASK_DUAL_SPI_STREAM_EXCEEDS_DAC_ERR_STATUS BIT(5) +#define AD3552R_MASK_MEM_CRC_ERR_STATUS BIT(4) +#define AD3552R_MASK_RESET_STATUS BIT(0) +#define AD3552R_REG_ADDR_POWERDOWN_CONFIG 0x18 +#define AD3552R_MASK_CH_DAC_POWERDOWN(ch) BIT(4 + (ch)) +#define AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(ch) BIT(ch) +#define AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE 0x19 +#define AD3552R_MASK_CH_OUTPUT_RANGE_SEL(ch) ((ch) ? GENMASK(7, 4) :\ + GENMASK(3, 0)) +#define AD3552R_REG_ADDR_CH_OFFSET(ch) (0x1B + (ch) * 2) +#define AD3552R_MASK_CH_OFFSET_BITS_0_7 GENMASK(7, 0) +#define AD3552R_REG_ADDR_CH_GAIN(ch) (0x1C + (ch) * 2) +#define AD3552R_MASK_CH_RANGE_OVERRIDE BIT(7) +#define AD3552R_MASK_CH_GAIN_SCALING_N GENMASK(6, 5) +#define AD3552R_MASK_CH_GAIN_SCALING_P GENMASK(4, 3) +#define AD3552R_MASK_CH_OFFSET_POLARITY BIT(2) +#define AD3552R_MASK_CH_OFFSET_BIT_8 BIT(0) +/* + * Secondary region + * For multibyte registers specify the highest address because the access is + * done in descending order + */ +#define AD3552R_SECONDARY_REGION_START 0x28 +#define AD3552R_REG_ADDR_HW_LDAC_16B 0x28 +#define AD3552R_REG_ADDR_CH_DAC_16B(ch) (0x2C - (1 - ch) * 2) +#define AD3552R_REG_ADDR_DAC_PAGE_MASK_16B 0x2E +#define AD3552R_REG_ADDR_CH_SELECT_16B 0x2F +#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_16B 0x31 +#define AD3552R_REG_ADDR_SW_LDAC_16B 0x32 +#define AD3552R_REG_ADDR_CH_INPUT_16B(ch) (0x36 - (1 - ch) * 2) +/* 3 bytes registers */ +#define AD3552R_REG_START_24B 0x37 +#define AD3552R_REG_ADDR_HW_LDAC_24B 0x37 +#define AD3552R_REG_ADDR_CH_DAC_24B(ch) (0x3D - (1 - ch) * 3) +#define AD3552R_REG_ADDR_DAC_PAGE_MASK_24B 0x40 +#define AD3552R_REG_ADDR_CH_SELECT_24B 0x41 +#define AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B 0x44 +#define AD3552R_REG_ADDR_SW_LDAC_24B 0x45 +#define AD3552R_REG_ADDR_CH_INPUT_24B(ch) (0x4B - (1 - ch) * 3) + +/* Useful defines */ +#define AD3552R_NUM_CH 2 +#define AD3552R_MASK_CH(ch) BIT(ch) +#define AD3552R_MASK_ALL_CH GENMASK(1, 0) +#define AD3552R_MAX_REG_SIZE 3 +#define AD3552R_READ_BIT BIT(7) +#define AD3552R_ADDR_MASK GENMASK(6, 0) +#define AD3552R_MASK_DAC_12B 0xFFF0 +#define AD3552R_DEFAULT_CONFIG_B_VALUE 0x8 +#define AD3552R_SCRATCH_PAD_TEST_VAL1 0x34 +#define AD3552R_SCRATCH_PAD_TEST_VAL2 0xB2 +#define AD3552R_GAIN_SCALE 1000 +#define AD3552R_LDAC_PULSE_US 100 + +enum ad3552r_ch_vref_select { + /* Internal source with Vref I/O floating */ + AD3552R_INTERNAL_VREF_PIN_FLOATING, + /* Internal source with Vref I/O at 2.5V */ + AD3552R_INTERNAL_VREF_PIN_2P5V, + /* External source with Vref I/O as input */ + AD3552R_EXTERNAL_VREF_PIN_INPUT +}; + +enum ad3542r_id { + AD3542R_ID = 0x4008, + AD3552R_ID = 0x4009, +}; + +enum ad3552r_ch_output_range { + /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ + AD3552R_CH_OUTPUT_RANGE_0__2P5V, + /* Range from 0 V to 5 V. Requires Rfb1x connection */ + AD3552R_CH_OUTPUT_RANGE_0__5V, + /* Range from 0 V to 10 V. Requires Rfb2x connection */ + AD3552R_CH_OUTPUT_RANGE_0__10V, + /* Range from -5 V to 5 V. Requires Rfb2x connection */ + AD3552R_CH_OUTPUT_RANGE_NEG_5__5V, + /* Range from -10 V to 10 V. Requires Rfb4x connection */ + AD3552R_CH_OUTPUT_RANGE_NEG_10__10V, +}; + +static const s32 ad3552r_ch_ranges[][2] = { + [AD3552R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500}, + [AD3552R_CH_OUTPUT_RANGE_0__5V] = {0, 5000}, + [AD3552R_CH_OUTPUT_RANGE_0__10V] = {0, 10000}, + [AD3552R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000}, + [AD3552R_CH_OUTPUT_RANGE_NEG_10__10V] = {-10000, 10000} +}; + +enum ad3542r_ch_output_range { + /* Range from 0 V to 2.5 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__2P5V, + /* Range from 0 V to 3 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__3V, + /* Range from 0 V to 5 V. Requires Rfb1x connection */ + AD3542R_CH_OUTPUT_RANGE_0__5V, + /* Range from 0 V to 10 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_0__10V, + /* Range from -2.5 V to 7.5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V, + /* Range from -5 V to 5 V. Requires Rfb2x connection */ + AD3542R_CH_OUTPUT_RANGE_NEG_5__5V, +}; + +static const s32 ad3542r_ch_ranges[][2] = { + [AD3542R_CH_OUTPUT_RANGE_0__2P5V] = {0, 2500}, + [AD3542R_CH_OUTPUT_RANGE_0__3V] = {0, 3000}, + [AD3542R_CH_OUTPUT_RANGE_0__5V] = {0, 5000}, + [AD3542R_CH_OUTPUT_RANGE_0__10V] = {0, 10000}, + [AD3542R_CH_OUTPUT_RANGE_NEG_2P5__7P5V] = {-2500, 7500}, + [AD3542R_CH_OUTPUT_RANGE_NEG_5__5V] = {-5000, 5000} +}; + +enum ad3552r_ch_gain_scaling { + /* Gain scaling of 1 */ + AD3552R_CH_GAIN_SCALING_1, + /* Gain scaling of 0.5 */ + AD3552R_CH_GAIN_SCALING_0_5, + /* Gain scaling of 0.25 */ + AD3552R_CH_GAIN_SCALING_0_25, + /* Gain scaling of 0.125 */ + AD3552R_CH_GAIN_SCALING_0_125, +}; + +/* Gain * AD3552R_GAIN_SCALE */ +static const s32 gains_scaling_table[] = { + [AD3552R_CH_GAIN_SCALING_1] = 1000, + [AD3552R_CH_GAIN_SCALING_0_5] = 500, + [AD3552R_CH_GAIN_SCALING_0_25] = 250, + [AD3552R_CH_GAIN_SCALING_0_125] = 125 +}; + +enum ad3552r_dev_attributes { + /* - Direct register values */ + /* From 0-3 */ + AD3552R_SDO_DRIVE_STRENGTH, + /* + * 0 -> Internal Vref, vref_io pin floating (default) + * 1 -> Internal Vref, vref_io driven by internal vref + * 2 or 3 -> External Vref + */ + AD3552R_VREF_SELECT, + /* Read registers in ascending order if set. Else descending */ + AD3552R_ADDR_ASCENSION, +}; + +enum ad3552r_ch_attributes { + /* DAC powerdown */ + AD3552R_CH_DAC_POWERDOWN, + /* DAC amplifier powerdown */ + AD3552R_CH_AMPLIFIER_POWERDOWN, + /* Select the output range. Select from enum ad3552r_ch_output_range */ + AD3552R_CH_OUTPUT_RANGE_SEL, + /* + * Over-rider the range selector in order to manually set the output + * voltage range + */ + AD3552R_CH_RANGE_OVERRIDE, + /* Manually set the offset voltage */ + AD3552R_CH_GAIN_OFFSET, + /* Sets the polarity of the offset. */ + AD3552R_CH_GAIN_OFFSET_POLARITY, + /* PDAC gain scaling */ + AD3552R_CH_GAIN_SCALING_P, + /* NDAC gain scaling */ + AD3552R_CH_GAIN_SCALING_N, + /* Rfb value */ + AD3552R_CH_RFB, + /* Channel select. When set allow Input -> DAC and Mask -> DAC */ + AD3552R_CH_SELECT, +}; + +struct ad3552r_ch_data { + s32 scale_int; + s32 scale_dec; + s32 offset_int; + s32 offset_dec; + s16 gain_offset; + u16 rfb; + u8 n; + u8 p; + u8 range; + bool range_override; +}; + +struct ad3552r_desc { + /* Used to look the spi bus for atomic operations where needed */ + struct mutex lock; + struct gpio_desc *gpio_reset; + struct gpio_desc *gpio_ldac; + struct spi_device *spi; + struct ad3552r_ch_data ch_data[AD3552R_NUM_CH]; + struct iio_chan_spec channels[AD3552R_NUM_CH + 1]; + unsigned long enabled_ch; + unsigned int num_ch; + enum ad3542r_id chip_id; +}; + +static const u16 addr_mask_map[][2] = { + [AD3552R_ADDR_ASCENSION] = { + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_ADDR_ASCENSION + }, + [AD3552R_SDO_DRIVE_STRENGTH] = { + AD3552R_REG_ADDR_INTERFACE_CONFIG_D, + AD3552R_MASK_SDO_DRIVE_STRENGTH + }, + [AD3552R_VREF_SELECT] = { + AD3552R_REG_ADDR_SH_REFERENCE_CONFIG, + AD3552R_MASK_REFERENCE_VOLTAGE_SEL + }, +}; + +/* 0 -> reg addr, 1->ch0 mask, 2->ch1 mask */ +static const u16 addr_mask_map_ch[][3] = { + [AD3552R_CH_DAC_POWERDOWN] = { + AD3552R_REG_ADDR_POWERDOWN_CONFIG, + AD3552R_MASK_CH_DAC_POWERDOWN(0), + AD3552R_MASK_CH_DAC_POWERDOWN(1) + }, + [AD3552R_CH_AMPLIFIER_POWERDOWN] = { + AD3552R_REG_ADDR_POWERDOWN_CONFIG, + AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(0), + AD3552R_MASK_CH_AMPLIFIER_POWERDOWN(1) + }, + [AD3552R_CH_OUTPUT_RANGE_SEL] = { + AD3552R_REG_ADDR_CH0_CH1_OUTPUT_RANGE, + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(0), + AD3552R_MASK_CH_OUTPUT_RANGE_SEL(1) + }, + [AD3552R_CH_SELECT] = { + AD3552R_REG_ADDR_CH_SELECT_16B, + AD3552R_MASK_CH(0), + AD3552R_MASK_CH(1) + } +}; + +static u8 _ad3552r_reg_len(u8 addr) +{ + switch (addr) { + case AD3552R_REG_ADDR_HW_LDAC_16B: + case AD3552R_REG_ADDR_CH_SELECT_16B: + case AD3552R_REG_ADDR_SW_LDAC_16B: + case AD3552R_REG_ADDR_HW_LDAC_24B: + case AD3552R_REG_ADDR_CH_SELECT_24B: + case AD3552R_REG_ADDR_SW_LDAC_24B: + return 1; + default: + break; + } + + if (addr > AD3552R_REG_ADDR_HW_LDAC_24B) + return 3; + if (addr > AD3552R_REG_ADDR_HW_LDAC_16B) + return 2; + + return 1; +} + +/* SPI transfer to device */ +static int ad3552r_transfer(struct ad3552r_desc *dac, u8 addr, u32 len, + u8 *data, bool is_read) +{ + /* Maximum transfer: Addr (1B) + 2 * (Data Reg (3B)) + SW LDAC(1B) */ + u8 buf[8]; + + buf[0] = addr & AD3552R_ADDR_MASK; + buf[0] |= is_read ? AD3552R_READ_BIT : 0; + if (is_read) + return spi_write_then_read(dac->spi, buf, 1, data, len); + + memcpy(buf + 1, data, len); + return spi_write_then_read(dac->spi, buf, len + 1, NULL, 0); +} + +static int ad3552r_write_reg(struct ad3552r_desc *dac, u8 addr, u16 val) +{ + u8 reg_len; + u8 buf[AD3552R_MAX_REG_SIZE] = { 0 }; + + reg_len = _ad3552r_reg_len(addr); + if (reg_len == 2) + /* Only DAC register are 2 bytes wide */ + val &= AD3552R_MASK_DAC_12B; + if (reg_len == 1) + buf[0] = val & 0xFF; + else + /* reg_len can be 2 or 3, but 3rd bytes needs to be set to 0 */ + put_unaligned_be16(val, buf); + + return ad3552r_transfer(dac, addr, reg_len, buf, false); +} + +static int ad3552r_read_reg(struct ad3552r_desc *dac, u8 addr, u16 *val) +{ + int err; + u8 reg_len, buf[AD3552R_MAX_REG_SIZE] = { 0 }; + + reg_len = _ad3552r_reg_len(addr); + err = ad3552r_transfer(dac, addr, reg_len, buf, true); + if (err) + return err; + + if (reg_len == 1) + *val = buf[0]; + else + /* reg_len can be 2 or 3, but only first 2 bytes are relevant */ + *val = get_unaligned_be16(buf); + + return 0; +} + +static u16 ad3552r_field_prep(u16 val, u16 mask) +{ + return (val << __ffs(mask)) & mask; +} + +/* Update field of a register, shift val if needed */ +static int ad3552r_update_reg_field(struct ad3552r_desc *dac, u8 addr, u16 mask, + u16 val) +{ + int ret; + u16 reg; + + ret = ad3552r_read_reg(dac, addr, ®); + if (ret < 0) + return ret; + + reg &= ~mask; + reg |= ad3552r_field_prep(val, mask); + + return ad3552r_write_reg(dac, addr, reg); +} + +static int ad3552r_set_ch_value(struct ad3552r_desc *dac, + enum ad3552r_ch_attributes attr, + u8 ch, + u16 val) +{ + /* Update register related to attributes in chip */ + return ad3552r_update_reg_field(dac, addr_mask_map_ch[attr][0], + addr_mask_map_ch[attr][ch + 1], val); +} + +#define AD3552R_CH_DAC(_idx) ((struct iio_chan_spec) { \ + .type = IIO_VOLTAGE, \ + .output = true, \ + .indexed = true, \ + .channel = _idx, \ + .scan_index = _idx, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_BE, \ + }, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_ENABLE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ +}) + +static int ad3552r_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad3552r_desc *dac = iio_priv(indio_dev); + u16 tmp_val; + int err; + u8 ch = chan->channel; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&dac->lock); + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_CH_DAC_24B(ch), + &tmp_val); + mutex_unlock(&dac->lock); + if (err < 0) + return err; + *val = tmp_val; + return IIO_VAL_INT; + case IIO_CHAN_INFO_ENABLE: + mutex_lock(&dac->lock); + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_POWERDOWN_CONFIG, + &tmp_val); + mutex_unlock(&dac->lock); + if (err < 0) + return err; + *val = !((tmp_val & AD3552R_MASK_CH_DAC_POWERDOWN(ch)) >> + __ffs(AD3552R_MASK_CH_DAC_POWERDOWN(ch))); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = dac->ch_data[ch].scale_int; + *val2 = dac->ch_data[ch].scale_dec; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_OFFSET: + *val = dac->ch_data[ch].offset_int; + *val2 = dac->ch_data[ch].offset_dec; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad3552r_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct ad3552r_desc *dac = iio_priv(indio_dev); + int err; + + mutex_lock(&dac->lock); + switch (mask) { + case IIO_CHAN_INFO_RAW: + err = ad3552r_write_reg(dac, + AD3552R_REG_ADDR_CH_DAC_24B(chan->channel), + val); + break; + case IIO_CHAN_INFO_ENABLE: + err = ad3552r_set_ch_value(dac, AD3552R_CH_DAC_POWERDOWN, + chan->channel, !val); + break; + default: + err = -EINVAL; + break; + } + mutex_unlock(&dac->lock); + + return err; +} + +static const struct iio_info ad3552r_iio_info = { + .read_raw = ad3552r_read_raw, + .write_raw = ad3552r_write_raw +}; + +static int32_t ad3552r_trigger_hw_ldac(struct gpio_desc *ldac) +{ + gpiod_set_value_cansleep(ldac, 0); + usleep_range(AD3552R_LDAC_PULSE_US, AD3552R_LDAC_PULSE_US + 10); + gpiod_set_value_cansleep(ldac, 1); + + return 0; +} + +static int ad3552r_write_all_channels(struct ad3552r_desc *dac, u8 *data) +{ + int err, len; + u8 addr, buff[AD3552R_NUM_CH * AD3552R_MAX_REG_SIZE + 1]; + + addr = AD3552R_REG_ADDR_CH_INPUT_24B(1); + /* CH1 */ + memcpy(buff, data + 2, 2); + buff[2] = 0; + /* CH0 */ + memcpy(buff + 3, data, 2); + buff[5] = 0; + len = 6; + if (!dac->gpio_ldac) { + /* Software LDAC */ + buff[6] = AD3552R_MASK_ALL_CH; + ++len; + } + err = ad3552r_transfer(dac, addr, len, buff, false); + if (err) + return err; + + if (dac->gpio_ldac) + return ad3552r_trigger_hw_ldac(dac->gpio_ldac); + + return 0; +} + +static int ad3552r_write_codes(struct ad3552r_desc *dac, u32 mask, u8 *data) +{ + int err; + u8 addr, buff[AD3552R_MAX_REG_SIZE]; + + if (mask == AD3552R_MASK_ALL_CH) { + if (memcmp(data, data + 2, 2) != 0) + return ad3552r_write_all_channels(dac, data); + + addr = AD3552R_REG_ADDR_INPUT_PAGE_MASK_24B; + } else { + addr = AD3552R_REG_ADDR_CH_INPUT_24B(__ffs(mask)); + } + + memcpy(buff, data, 2); + buff[2] = 0; + err = ad3552r_transfer(dac, addr, 3, data, false); + if (err) + return err; + + if (dac->gpio_ldac) + return ad3552r_trigger_hw_ldac(dac->gpio_ldac); + + return ad3552r_write_reg(dac, AD3552R_REG_ADDR_SW_LDAC_24B, mask); +} + +static irqreturn_t ad3552r_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct iio_buffer *buf = indio_dev->buffer; + struct ad3552r_desc *dac = iio_priv(indio_dev); + /* Maximum size of a scan */ + u8 buff[AD3552R_NUM_CH * AD3552R_MAX_REG_SIZE]; + int err; + + memset(buff, 0, sizeof(buff)); + err = iio_pop_from_buffer(buf, buff); + if (err) + goto end; + + mutex_lock(&dac->lock); + ad3552r_write_codes(dac, *indio_dev->active_scan_mask, buff); + mutex_unlock(&dac->lock); +end: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int ad3552r_check_scratch_pad(struct ad3552r_desc *dac) +{ + const u16 val1 = AD3552R_SCRATCH_PAD_TEST_VAL1; + const u16 val2 = AD3552R_SCRATCH_PAD_TEST_VAL2; + u16 val; + int err; + + err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val1); + if (err < 0) + return err; + + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val); + if (err < 0) + return err; + + if (val1 != val) + return -ENODEV; + + err = ad3552r_write_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, val2); + if (err < 0) + return err; + + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_SCRATCH_PAD, &val); + if (err < 0) + return err; + + if (val2 != val) + return -ENODEV; + + return 0; +} + +struct reg_addr_pool { + struct ad3552r_desc *dac; + u8 addr; +}; + +static int ad3552r_read_reg_wrapper(struct reg_addr_pool *addr) +{ + int err; + u16 val; + + err = ad3552r_read_reg(addr->dac, addr->addr, &val); + if (err) + return err; + + return val; +} + +static int ad3552r_reset(struct ad3552r_desc *dac) +{ + struct reg_addr_pool addr; + int ret; + u16 val; + + dac->gpio_reset = devm_gpiod_get_optional(&dac->spi->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(dac->gpio_reset)) + return dev_err_probe(&dac->spi->dev, PTR_ERR(dac->gpio_reset), + "Error while getting gpio reset"); + + if (dac->gpio_reset) { + /* Perform hardware reset */ + usleep_range(10, 20); + gpiod_set_value_cansleep(dac->gpio_reset, 1); + } else { + /* Perform software reset if no GPIO provided */ + ret = ad3552r_update_reg_field(dac, + AD3552R_REG_ADDR_INTERFACE_CONFIG_A, + AD3552R_MASK_SOFTWARE_RESET, + AD3552R_MASK_SOFTWARE_RESET); + if (ret < 0) + return ret; + + } + + addr.dac = dac; + addr.addr = AD3552R_REG_ADDR_INTERFACE_CONFIG_B; + ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val, + val == AD3552R_DEFAULT_CONFIG_B_VALUE || + val < 0, + 5000, 50000); + if (val < 0) + ret = val; + if (ret) { + dev_err(&dac->spi->dev, "Error while resetting"); + return ret; + } + + ret = readx_poll_timeout(ad3552r_read_reg_wrapper, &addr, val, + !(val & AD3552R_MASK_INTERFACE_NOT_READY) || + val < 0, + 5000, 50000); + if (val < 0) + ret = val; + if (ret) { + dev_err(&dac->spi->dev, "Error while resetting"); + return ret; + } + + return ad3552r_update_reg_field(dac, + addr_mask_map[AD3552R_ADDR_ASCENSION][0], + addr_mask_map[AD3552R_ADDR_ASCENSION][1], + val); +} + +static void ad3552r_get_custom_range(struct ad3552r_desc *dac, s32 i, s32 *v_min, + s32 *v_max) +{ + s64 vref, tmp, common, offset, gn, gp; + /* + * From datasheet formula (In Volts): + * Vmin = 2.5 + [(GainN + Offset / 1024) * 2.5 * Rfb * 1.03] + * Vmax = 2.5 - [(GainP + Offset / 1024) * 2.5 * Rfb * 1.03] + * Calculus are converted to milivolts + */ + vref = 2500; + /* 2.5 * 1.03 * 1000 (To mV) */ + common = 2575 * dac->ch_data[i].rfb; + offset = dac->ch_data[i].gain_offset; + + gn = gains_scaling_table[dac->ch_data[i].n]; + tmp = (1024 * gn + AD3552R_GAIN_SCALE * offset) * common; + tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); + *v_max = vref + tmp; + + gp = gains_scaling_table[dac->ch_data[i].p]; + tmp = (1024 * gp - AD3552R_GAIN_SCALE * offset) * common; + tmp = div_s64(tmp, 1024 * AD3552R_GAIN_SCALE); + *v_min = vref - tmp; +} + +static void ad3552r_calc_gain_and_offset(struct ad3552r_desc *dac, s32 ch) +{ + s32 idx, v_max, v_min, span, rem; + s64 tmp; + + if (dac->ch_data[ch].range_override) { + ad3552r_get_custom_range(dac, ch, &v_min, &v_max); + } else { + /* Normal range */ + idx = dac->ch_data[ch].range; + if (dac->chip_id == AD3542R_ID) { + v_min = ad3542r_ch_ranges[idx][0]; + v_max = ad3542r_ch_ranges[idx][1]; + } else { + v_min = ad3552r_ch_ranges[idx][0]; + v_max = ad3552r_ch_ranges[idx][1]; + } + } + + /* + * From datasheet formula: + * Vout = Span * (D / 65536) + Vmin + * Converted to scale and offset: + * Scale = Span / 65536 + * Offset = 65536 * Vmin / Span + * + * Reminders are in micros in order to be printed as + * IIO_VAL_INT_PLUS_MICRO + */ + span = v_max - v_min; + dac->ch_data[ch].scale_int = div_s64_rem(span, 65536, &rem); + /* Do operations in microvolts */ + dac->ch_data[ch].scale_dec = DIV_ROUND_CLOSEST((s64)rem * 1000000, + 65536); + + dac->ch_data[ch].offset_int = div_s64_rem(v_min * 65536, span, &rem); + tmp = (s64)rem * 1000000; + dac->ch_data[ch].offset_dec = div_s64(tmp, span); +} + +static int ad3552r_find_range(u16 id, s32 *vals) +{ + int i, len; + const s32 (*ranges)[2]; + + if (id == AD3542R_ID) { + len = ARRAY_SIZE(ad3542r_ch_ranges); + ranges = ad3542r_ch_ranges; + } else { + len = ARRAY_SIZE(ad3552r_ch_ranges); + ranges = ad3552r_ch_ranges; + } + + for (i = 0; i < len; i++) + if (vals[0] == ranges[i][0] * 1000 && + vals[1] == ranges[i][1] * 1000) + return i; + + return -EINVAL; +} + +static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, + struct fwnode_handle *child, + u32 ch) +{ + struct device *dev = &dac->spi->dev; + struct fwnode_handle *gain_child; + u32 val; + int err; + u8 addr; + u16 reg = 0, offset; + + gain_child = fwnode_get_named_child_node(child, + "custom-output-range-config"); + if (IS_ERR(gain_child)) { + dev_err(dev, + "mandatory custom-output-range-config property missing\n"); + return PTR_ERR(gain_child); + } + + dac->ch_data[ch].range_override = 1; + reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE); + + err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val); + if (err) { + dev_err(dev, "mandatory adi,gain-scaling-p property missing\n"); + goto put_child; + } + reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P); + dac->ch_data[ch].p = val; + + err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val); + if (err) { + dev_err(dev, "mandatory adi,gain-scaling-n property missing\n"); + goto put_child; + } + reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N); + dac->ch_data[ch].n = val; + + err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val); + if (err) { + dev_err(dev, "mandatory adi,rfb-ohms property missing\n"); + goto put_child; + } + dac->ch_data[ch].rfb = val; + + err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val); + if (err) { + dev_err(dev, "mandatory adi,gain-offset property missing\n"); + goto put_child; + } + dac->ch_data[ch].gain_offset = val; + + offset = abs((s32)val); + reg |= ad3552r_field_prep((offset >> 8), AD3552R_MASK_CH_OFFSET_BIT_8); + + reg |= ad3552r_field_prep((s32)val < 0, AD3552R_MASK_CH_OFFSET_POLARITY); + addr = AD3552R_REG_ADDR_CH_GAIN(ch); + err = ad3552r_write_reg(dac, addr, + offset & AD3552R_MASK_CH_OFFSET_BITS_0_7); + if (err) { + dev_err(dev, "Error writing register\n"); + goto put_child; + } + + err = ad3552r_write_reg(dac, addr, reg); + if (err) { + dev_err(dev, "Error writing register\n"); + goto put_child; + } + +put_child: + fwnode_handle_put(gain_child); + + return err; +} + +static void ad3552r_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static int ad3552r_configure_device(struct ad3552r_desc *dac) +{ + struct device *dev = &dac->spi->dev; + struct fwnode_handle *child; + struct regulator *vref; + int err, cnt = 0, voltage, delta = 100000; + u32 vals[2], val, ch; + + dac->gpio_ldac = devm_gpiod_get_optional(dev, "ldac", GPIOD_OUT_HIGH); + if (IS_ERR(dac->gpio_ldac)) + return dev_err_probe(dev, PTR_ERR(dac->gpio_ldac), + "Error getting gpio ldac"); + + vref = devm_regulator_get_optional(dev, "vref"); + if (IS_ERR(vref)) { + if (PTR_ERR(vref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(vref), + "Error getting vref"); + + if (device_property_read_bool(dev, "adi,vref-out-en")) + val = AD3552R_INTERNAL_VREF_PIN_2P5V; + else + val = AD3552R_INTERNAL_VREF_PIN_FLOATING; + } else { + err = regulator_enable(vref); + if (err) { + dev_err(dev, "Failed to enable external vref supply\n"); + return err; + } + + err = devm_add_action_or_reset(dev, ad3552r_reg_disable, vref); + if (err) { + regulator_disable(vref); + return err; + } + + voltage = regulator_get_voltage(vref); + if (voltage > 2500000 + delta || voltage < 2500000 - delta) { + dev_warn(dev, "vref-supply must be 2.5V"); + return -EINVAL; + } + val = AD3552R_EXTERNAL_VREF_PIN_INPUT; + } + + err = ad3552r_update_reg_field(dac, + addr_mask_map[AD3552R_VREF_SELECT][0], + addr_mask_map[AD3552R_VREF_SELECT][1], + val); + if (err) + return err; + + err = device_property_read_u32(dev, "adi,sdo-drive-strength", &val); + if (!err) { + if (val > 3) { + dev_err(dev, "adi,sdo-drive-strength must be less than 4\n"); + return -EINVAL; + } + + err = ad3552r_update_reg_field(dac, + addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][0], + addr_mask_map[AD3552R_SDO_DRIVE_STRENGTH][1], + val); + if (err) + return err; + } + + dac->num_ch = device_get_child_node_count(dev); + if (!dac->num_ch) { + dev_err(dev, "No channels defined\n"); + return -ENODEV; + } + + device_for_each_child_node(dev, child) { + err = fwnode_property_read_u32(child, "reg", &ch); + if (err) { + dev_err(dev, "mandatory reg property missing\n"); + goto put_child; + } + if (ch >= AD3552R_NUM_CH) { + dev_err(dev, "reg must be less than %d\n", + AD3552R_NUM_CH); + err = -EINVAL; + goto put_child; + } + + if (fwnode_property_present(child, "adi,output-range-microvolt")) { + err = fwnode_property_read_u32_array(child, + "adi,output-range-microvolt", + vals, + 2); + if (err) { + dev_err(dev, + "adi,output-range-microvolt property could not be parsed\n"); + goto put_child; + } + + err = ad3552r_find_range(dac->chip_id, vals); + if (err < 0) { + dev_err(dev, + "Invalid adi,output-range-microvolt value\n"); + goto put_child; + } + val = err; + err = ad3552r_set_ch_value(dac, + AD3552R_CH_OUTPUT_RANGE_SEL, + ch, val); + if (err) + goto put_child; + + dac->ch_data[ch].range = val; + } else if (dac->chip_id == AD3542R_ID) { + dev_err(dev, + "adi,output-range-microvolt is required for ad3542r\n"); + err = -EINVAL; + goto put_child; + } else { + err = ad3552r_configure_custom_gain(dac, child, ch); + if (err) + goto put_child; + } + + ad3552r_calc_gain_and_offset(dac, ch); + dac->enabled_ch |= BIT(ch); + + err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1); + if (err < 0) + goto put_child; + + dac->channels[cnt] = AD3552R_CH_DAC(ch); + ++cnt; + + } + + /* Disable unused channels */ + for_each_clear_bit(ch, &dac->enabled_ch, AD3552R_NUM_CH) { + err = ad3552r_set_ch_value(dac, AD3552R_CH_AMPLIFIER_POWERDOWN, + ch, 1); + if (err) + return err; + } + + dac->num_ch = cnt; + + return 0; +put_child: + fwnode_handle_put(child); + + return err; +} + +static int ad3552r_init(struct ad3552r_desc *dac) +{ + int err; + u16 val, id; + + err = ad3552r_reset(dac); + if (err) { + dev_err(&dac->spi->dev, "Reset failed\n"); + return err; + } + + err = ad3552r_check_scratch_pad(dac); + if (err) { + dev_err(&dac->spi->dev, "Scratch pad test failed\n"); + return err; + } + + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_L, &val); + if (err) { + dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_L\n"); + return err; + } + + id = val; + err = ad3552r_read_reg(dac, AD3552R_REG_ADDR_PRODUCT_ID_H, &val); + if (err) { + dev_err(&dac->spi->dev, "Fail read PRODUCT_ID_H\n"); + return err; + } + + id |= val << 8; + if (id != dac->chip_id) { + dev_err(&dac->spi->dev, "Product id not matching\n"); + return -ENODEV; + } + + return ad3552r_configure_device(dac); +} + +static int ad3552r_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + struct ad3552r_desc *dac; + struct iio_dev *indio_dev; + int err; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*dac)); + if (!indio_dev) + return -ENOMEM; + + dac = iio_priv(indio_dev); + dac->spi = spi; + dac->chip_id = id->driver_data; + + mutex_init(&dac->lock); + + err = ad3552r_init(dac); + if (err) + return err; + + /* Config triggered buffer device */ + if (dac->chip_id == AD3552R_ID) + indio_dev->name = "ad3552r"; + else + indio_dev->name = "ad3542r"; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ad3552r_iio_info; + indio_dev->num_channels = dac->num_ch; + indio_dev->channels = dac->channels; + indio_dev->modes = INDIO_DIRECT_MODE; + + err = devm_iio_triggered_buffer_setup_ext(&indio_dev->dev, indio_dev, NULL, + &ad3552r_trigger_handler, + IIO_BUFFER_DIRECTION_OUT, + NULL, + NULL); + if (err) + return err; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id ad3552r_id[] = { + { "ad3542r", AD3542R_ID }, + { "ad3552r", AD3552R_ID }, + { } +}; +MODULE_DEVICE_TABLE(spi, ad3552r_id); + +static const struct of_device_id ad3552r_of_match[] = { + { .compatible = "adi,ad3542r"}, + { .compatible = "adi,ad3552r"}, + { } +}; +MODULE_DEVICE_TABLE(of, ad3552r_of_match); + +static struct spi_driver ad3552r_driver = { + .driver = { + .name = "ad3552r", + .of_match_table = ad3552r_of_match, + }, + .probe = ad3552r_probe, + .id_table = ad3552r_id +}; +module_spi_driver(ad3552r_driver); + +MODULE_AUTHOR("Mihail Chindris "); +MODULE_DESCRIPTION("Analog Device AD3552R DAC"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 1155ed05756a4e0f8fbc1760d6ca79354fe034c1 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:34 +0000 Subject: iio:accel:bma180: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Stephan Gerhold Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-2-jic23@kernel.org --- drivers/iio/accel/bma180.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 09496f358ad9..d8a454c266d5 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -938,7 +938,7 @@ static int bma180_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; if (client->dev.of_node) - chip = (enum chip_ids)of_device_get_match_data(dev); + chip = (uintptr_t)of_device_get_match_data(dev); else chip = id->driver_data; data->part_info = &bma180_part_info[chip]; -- cgit v1.2.3 From 861dc0d7fd972f2064ff48b211955717163a11e0 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Sun, 12 Sep 2021 15:27:52 -0700 Subject: lkdtm: Note that lkdtm_kernel_info should be removed in the future As per Linus's request, remove lkdtm_kernel_info once sufficient reporting exists in CI systems: https://lore.kernel.org/lkml/CAHk-=wiFvfkoFixTapvvyPMN9pq5G-+Dys2eSyBa1vzDGAO5+A@mail.gmail.com Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Signed-off-by: Kees Cook --- drivers/misc/lkdtm/core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/misc/lkdtm/core.c b/drivers/misc/lkdtm/core.c index 609d9ee2acc0..d4c6cdced37b 100644 --- a/drivers/misc/lkdtm/core.c +++ b/drivers/misc/lkdtm/core.c @@ -212,7 +212,11 @@ module_param(cpoint_count, int, 0644); MODULE_PARM_DESC(cpoint_count, " Crash Point Count, number of times the "\ "crash point is to be hit to trigger action"); -/* For test debug reporting. */ +/* + * For test debug reporting when CI systems provide terse summaries. + * TODO: Remove this once reasonable reporting exists in most CI systems: + * https://lore.kernel.org/lkml/CAHk-=wiFvfkoFixTapvvyPMN9pq5G-+Dys2eSyBa1vzDGAO5+A@mail.gmail.com + */ char *lkdtm_kernel_info; /* Return the crashtype number or NULL if the name is invalid */ -- cgit v1.2.3 From 026c6fa1a525ca3f8a615052e45d766208989597 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Thu, 7 Oct 2021 10:12:35 +0200 Subject: lkdtm: avoid printk() in recursive_loop() The recursive_loop() function is intended as a diagnostic to ensure that exhausting the stack is caught and mitigated. Currently, it uses pr_info() to ensure that the function has side effects that the compiler cannot simply optimize away, so that the stack footprint does not get reduced inadvertently. The typical mitigation for stack overflow is to kill the task, and this overflow may occur inside the call to pr_info(), which means it could be holding the console lock when this happens. This means that the console lock is never going to be released again, preventing the diagnostic prints related to the stack overflow handling from being visible on the console. So let's replace the call to pr_info() with a call to memzero_explicit(), which is not a 'magic' function name like memset() or memcpy(), which the compiler may replace with plain loads and stores. To ensure that the stack frames are nested rather than tail-called, put the call to memzero_explicit() after the recursive call. Signed-off-by: Ard Biesheuvel Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20211007081235.382697-1-ardb@kernel.org --- drivers/misc/lkdtm/bugs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/misc/lkdtm/bugs.c b/drivers/misc/lkdtm/bugs.c index f4cb94a9aa9c..f21854ac5cc2 100644 --- a/drivers/misc/lkdtm/bugs.c +++ b/drivers/misc/lkdtm/bugs.c @@ -41,20 +41,22 @@ static DEFINE_SPINLOCK(lock_me_up); * Make sure compiler does not optimize this function or stack frame away: * - function marked noinline * - stack variables are marked volatile - * - stack variables are written (memset()) and read (pr_info()) - * - function has external effects (pr_info()) - * */ + * - stack variables are written (memset()) and read (buf[..] passed as arg) + * - function may have external effects (memzero_explicit()) + * - no tail recursion possible + */ static int noinline recursive_loop(int remaining) { volatile char buf[REC_STACK_SIZE]; + volatile int ret; memset((void *)buf, remaining & 0xFF, sizeof(buf)); - pr_info("loop %d/%d ...\n", (int)buf[remaining % sizeof(buf)], - recur_count); if (!remaining) - return 0; + ret = 0; else - return recursive_loop(remaining - 1); + ret = recursive_loop((int)buf[remaining % sizeof(buf)] - 1); + memzero_explicit((void *)buf, sizeof(buf)); + return ret; } /* If the depth is negative, use the default, otherwise keep parameter. */ -- cgit v1.2.3 From bc93a22a19eb2b68a16ecf04cdf4b2ed65aaf398 Mon Sep 17 00:00:00 2001 From: Christophe Leroy Date: Fri, 8 Oct 2021 18:58:40 +0200 Subject: lkdtm: Fix content of section containing lkdtm_rodata_do_nothing() On a kernel without CONFIG_STRICT_KERNEL_RWX, running EXEC_RODATA test leads to "Illegal instruction" failure. Looking at the content of rodata_objcopy.o, we see that the function content zeroes only: Disassembly of section .rodata: 0000000000000000 <.lkdtm_rodata_do_nothing>: 0: 00 00 00 00 .long 0x0 Add the contents flag in order to keep the content of the section while renaming it. Disassembly of section .rodata: 0000000000000000 <.lkdtm_rodata_do_nothing>: 0: 4e 80 00 20 blr Fixes: e9e08a07385e ("lkdtm: support llvm-objcopy") Cc: stable@vger.kernel.org Cc: Kees Cook Cc: Arnd Bergmann Cc: Greg Kroah-Hartman Cc: Nick Desaulniers Cc: Nathan Chancellor Signed-off-by: Christophe Leroy Reviewed-by: Nick Desaulniers Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/8900731fbc05fb8b0de18af7133a8fc07c3c53a1.1633712176.git.christophe.leroy@csgroup.eu --- drivers/misc/lkdtm/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index aa12097668d3..e2984ce51fe4 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -20,7 +20,7 @@ CFLAGS_REMOVE_rodata.o += $(CC_FLAGS_LTO) OBJCOPYFLAGS := OBJCOPYFLAGS_rodata_objcopy.o := \ - --rename-section .noinstr.text=.rodata,alloc,readonly,load + --rename-section .noinstr.text=.rodata,alloc,readonly,load,contents targets += rodata.o rodata_objcopy.o $(obj)/rodata_objcopy.o: $(obj)/rodata.o FORCE $(call if_changed,objcopy) -- cgit v1.2.3 From 90091c367e74d5b58d9ebe979cc363f7468f58d3 Mon Sep 17 00:00:00 2001 From: Misono Tomohiro Date: Thu, 5 Aug 2021 19:12:36 +0900 Subject: selftest/lkdtm: Skip stack-entropy test if lkdtm is not available Exit with return code 4 if lkdtm is not available like other tests in order to properly skip the test. Signed-off-by: Misono Tomohiro Signed-off-by: Kees Cook Link: https://lore.kernel.org/r/20210805101236.1140381-1-misono.tomohiro@jp.fujitsu.com --- tools/testing/selftests/lkdtm/stack-entropy.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/lkdtm/stack-entropy.sh b/tools/testing/selftests/lkdtm/stack-entropy.sh index 1b4d95d575f8..14fedeef762e 100755 --- a/tools/testing/selftests/lkdtm/stack-entropy.sh +++ b/tools/testing/selftests/lkdtm/stack-entropy.sh @@ -4,13 +4,27 @@ # Measure kernel stack entropy by sampling via LKDTM's REPORT_STACK test. set -e samples="${1:-1000}" +TRIGGER=/sys/kernel/debug/provoke-crash/DIRECT +KSELFTEST_SKIP_TEST=4 + +# Verify we have LKDTM available in the kernel. +if [ ! -r $TRIGGER ] ; then + /sbin/modprobe -q lkdtm || true + if [ ! -r $TRIGGER ] ; then + echo "Cannot find $TRIGGER (missing CONFIG_LKDTM?)" + else + echo "Cannot write $TRIGGER (need to run as root?)" + fi + # Skip this test + exit $KSELFTEST_SKIP_TEST +fi # Capture dmesg continuously since it may fill up depending on sample size. log=$(mktemp -t stack-entropy-XXXXXX) dmesg --follow >"$log" & pid=$! report=-1 for i in $(seq 1 $samples); do - echo "REPORT_STACK" >/sys/kernel/debug/provoke-crash/DIRECT + echo "REPORT_STACK" > $TRIGGER if [ -t 1 ]; then percent=$(( 100 * $i / $samples )) if [ "$percent" -ne "$report" ]; then -- cgit v1.2.3 From c9825e66000508baf07260fb53540da8cffb3471 Mon Sep 17 00:00:00 2001 From: Slark Xiao Date: Thu, 16 Dec 2021 13:42:18 +0530 Subject: bus: mhi: pci_generic: Add new device ID support for T99W175 Add new device ID 0xe0bf for T99W175. This device ID is created because it is using Qualcomm SDX55 new base line. Test evidence as below: root@jbd-ThinkPad-P1-Gen-4:/dev# lspci -nn | grep Foxconn 0000:08:00.0 Wireless controller [0d40]: Foxconn International, Inc. Device [105b:e0bf] root@jbd-ThinkPad-P1-Gen-4:/dev# cat wwan0at0 & echo -ne "ati\r" > wwan0at0 [2] 2977 root@jbd-ThinkPad-P1-Gen-4:/dev# ati Manufacturer: Qualcomm Model: T99W175 Revision: T99W175.F0.6.0.0.6.CC.005 1 [Oct 21 2021 10:00:00] IMEI: +GCAP: +CGSM OK Link: https://lore.kernel.org/r/20211029104918.3976-1-slark_xiao@163.com Reviewed-by: Manivannan Sadhasivam Signed-off-by: Slark Xiao Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-2-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/pci_generic.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/pci_generic.c index 4c577a731709..9d8e8f0e0123 100644 --- a/drivers/bus/mhi/pci_generic.c +++ b/drivers/bus/mhi/pci_generic.c @@ -423,6 +423,9 @@ static const struct pci_device_id mhi_pci_id_table[] = { /* DW5930e (sdx55), Non-eSIM, It's also T99W175 */ { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0b1), .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info }, + /* T99W175 (sdx55), Based on Qualcomm new baseline */ + { PCI_DEVICE(PCI_VENDOR_ID_FOXCONN, 0xe0bf), + .driver_data = (kernel_ulong_t) &mhi_foxconn_sdx55_info }, /* MV31-W (Cinterion) */ { PCI_DEVICE(0x1269, 0x00b3), .driver_data = (kernel_ulong_t) &mhi_mv31_info }, -- cgit v1.2.3 From f77097ec8c0141a4b5cf3722a246be0cb5677e29 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Thu, 16 Dec 2021 13:42:19 +0530 Subject: bus: mhi: pci_generic: Graceful shutdown on freeze There is no reason for shutting down MHI ungracefully on freeze, this causes the MHI host stack & device stack to not be aligned anymore since the proper MHI reset sequence is not performed for ungraceful shutdown. Link: https://lore.kernel.org/r/1635268180-13699-1-git-send-email-loic.poulain@linaro.org Fixes: 5f0c2ee1fe8d ("bus: mhi: pci-generic: Fix hibernation") Cc: stable@vger.kernel.org Suggested-by: Bhaumik Bhatt Reviewed-by: Bhaumik Bhatt Reviewed-by: Hemant Kumar Reviewed-by: Manivannan Sadhasivam Signed-off-by: Loic Poulain Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-3-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/pci_generic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/pci_generic.c index 9d8e8f0e0123..1ffd78113e47 100644 --- a/drivers/bus/mhi/pci_generic.c +++ b/drivers/bus/mhi/pci_generic.c @@ -1021,7 +1021,7 @@ static int __maybe_unused mhi_pci_freeze(struct device *dev) * context. */ if (test_and_clear_bit(MHI_PCI_DEV_STARTED, &mhi_pdev->status)) { - mhi_power_down(mhi_cntrl, false); + mhi_power_down(mhi_cntrl, true); mhi_unprepare_after_power_down(mhi_cntrl); } -- cgit v1.2.3 From 3e60c9f06803b52629d5c551ddbb5fddd60b8b65 Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Thu, 16 Dec 2021 13:42:20 +0530 Subject: bus: mhi: core: Use macros for execution environment features The implementation for execution environment specific functionality is spread out. Use macros that help determine the paths to be taken. Link: https://lore.kernel.org/r/1636409978-31847-1-git-send-email-quic_bbhatt@quicinc.com Reviewed-by: Manivannan Sadhasivam Signed-off-by: Bhaumik Bhatt Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-4-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/core/boot.c | 2 +- drivers/bus/mhi/core/internal.h | 3 ++- drivers/bus/mhi/core/pm.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/bus/mhi/core/boot.c b/drivers/bus/mhi/core/boot.c index 0a972620a403..74295d3cc662 100644 --- a/drivers/bus/mhi/core/boot.c +++ b/drivers/bus/mhi/core/boot.c @@ -417,7 +417,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) } /* wait for ready on pass through or any other execution environment */ - if (mhi_cntrl->ee != MHI_EE_EDL && mhi_cntrl->ee != MHI_EE_PBL) + if (!MHI_FW_LOAD_CAPABLE(mhi_cntrl->ee)) goto fw_load_ready_state; fw_name = (mhi_cntrl->ee == MHI_EE_EDL) ? diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h index 3a732afaf73e..9d72b1d1e986 100644 --- a/drivers/bus/mhi/core/internal.h +++ b/drivers/bus/mhi/core/internal.h @@ -390,7 +390,8 @@ extern const char * const mhi_ee_str[MHI_EE_MAX]; #define MHI_IN_PBL(ee) (ee == MHI_EE_PBL || ee == MHI_EE_PTHRU || \ ee == MHI_EE_EDL) - +#define MHI_POWER_UP_CAPABLE(ee) (MHI_IN_PBL(ee) || ee == MHI_EE_AMSS) +#define MHI_FW_LOAD_CAPABLE(ee) (ee == MHI_EE_PBL || ee == MHI_EE_EDL) #define MHI_IN_MISSION_MODE(ee) (ee == MHI_EE_AMSS || ee == MHI_EE_WFW || \ ee == MHI_EE_FP) diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c index 547e6e769546..606a77a64fd0 100644 --- a/drivers/bus/mhi/core/pm.c +++ b/drivers/bus/mhi/core/pm.c @@ -1083,7 +1083,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) write_unlock_irq(&mhi_cntrl->pm_lock); /* Confirm that the device is in valid exec env */ - if (!MHI_IN_PBL(current_ee) && current_ee != MHI_EE_AMSS) { + if (!MHI_POWER_UP_CAPABLE(current_ee)) { dev_err(dev, "%s is not a valid EE for power on\n", TO_MHI_EXEC_STR(current_ee)); ret = -EIO; -- cgit v1.2.3 From 85ec6094624c413c2f87f49e8fffca60100915ff Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Dec 2021 13:42:21 +0530 Subject: bus: mhi: core: Minor style and comment fixes This patch fixes the below checkpatch warnings in MHI bus: WARNING: Possible repeated word: 'events' + /* Process ctrl events events */ WARNING: Missing a blank line after declarations + struct mhi_buf_info info = { }; + buf = kmalloc(len, GFP_KERNEL); WARNING: Move const after static - use 'static const struct mhi_pm_transitions' +static struct mhi_pm_transitions const dev_state_transitions[] = { Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-5-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/core/main.c | 3 ++- drivers/bus/mhi/core/pm.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c index b15c5bc37dd4..930aba666b67 100644 --- a/drivers/bus/mhi/core/main.c +++ b/drivers/bus/mhi/core/main.c @@ -1065,7 +1065,7 @@ void mhi_ctrl_ev_task(unsigned long data) return; } - /* Process ctrl events events */ + /* Process ctrl events */ ret = mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); /* @@ -1464,6 +1464,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, while (nr_el--) { void *buf; struct mhi_buf_info info = { }; + buf = kmalloc(len, GFP_KERNEL); if (!buf) { ret = -ENOMEM; diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c index 606a77a64fd0..e70a3e3a0b46 100644 --- a/drivers/bus/mhi/core/pm.c +++ b/drivers/bus/mhi/core/pm.c @@ -42,7 +42,7 @@ * L3: LD_ERR_FATAL_DETECT <--> LD_ERR_FATAL_DETECT * LD_ERR_FATAL_DETECT -> DISABLE */ -static struct mhi_pm_transitions const dev_state_transitions[] = { +static const struct mhi_pm_transitions dev_state_transitions[] = { /* L0 States */ { MHI_PM_DISABLE, -- cgit v1.2.3 From f3d13397365d834b3f882627a7962c76593eeef2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Thu, 16 Dec 2021 13:42:22 +0530 Subject: bus: mhi: pci_generic: Simplify code and axe the use of a deprecated API The wrappers in include/linux/pci-dma-compat.h should go away. Replace 'pci_set_dma_mask/pci_set_consistent_dma_mask' by an equivalent and less verbose 'dma_set_mask_and_coherent()' call. Link: https://lore.kernel.org/r/bb3dc436fe142309a2334549db782c5ebb80a2be.1625718497.git.christophe.jaillet@wanadoo.fr Reviewed-by: Hemant Kumar Reviewed-by: Manivannan Sadhasivam Signed-off-by: Christophe JAILLET Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-6-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/pci_generic.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/pci_generic.c index 1ffd78113e47..759df02fb0be 100644 --- a/drivers/bus/mhi/pci_generic.c +++ b/drivers/bus/mhi/pci_generic.c @@ -532,18 +532,12 @@ static int mhi_pci_claim(struct mhi_controller *mhi_cntrl, mhi_cntrl->regs = pcim_iomap_table(pdev)[bar_num]; mhi_cntrl->reg_len = pci_resource_len(pdev, bar_num); - err = pci_set_dma_mask(pdev, dma_mask); + err = dma_set_mask_and_coherent(&pdev->dev, dma_mask); if (err) { dev_err(&pdev->dev, "Cannot set proper DMA mask\n"); return err; } - err = pci_set_consistent_dma_mask(pdev, dma_mask); - if (err) { - dev_err(&pdev->dev, "set consistent dma mask failed\n"); - return err; - } - pci_set_master(pdev); return 0; -- cgit v1.2.3 From 42c4668f7efe1485dfc382517b412c0c6ab102b8 Mon Sep 17 00:00:00 2001 From: Bhaumik Bhatt Date: Thu, 16 Dec 2021 13:42:23 +0530 Subject: bus: mhi: core: Fix reading wake_capable channel configuration The 'wake-capable' entry in channel configuration is not set when parsing the configuration specified by the controller driver. Add the missing entry to ensure channel is correctly specified as a 'wake-capable' channel. Link: https://lore.kernel.org/r/1638320491-13382-1-git-send-email-quic_bbhatt@quicinc.com Fixes: 0cbf260820fa ("bus: mhi: core: Add support for registering MHI controllers") Cc: stable@vger.kernel.org Reviewed-by: Manivannan Sadhasivam Signed-off-by: Bhaumik Bhatt Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-7-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/core/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c index 5aaca6d0f52b..f1ec34417592 100644 --- a/drivers/bus/mhi/core/init.c +++ b/drivers/bus/mhi/core/init.c @@ -788,6 +788,7 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl, mhi_chan->offload_ch = ch_cfg->offload_channel; mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch; mhi_chan->pre_alloc = ch_cfg->auto_queue; + mhi_chan->wake_capable = ch_cfg->wake_capable; /* * If MHI host allocates buffers, then the channel direction -- cgit v1.2.3 From d651ce8e917fa1bf6cfab8dca74c512edffc35d3 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Dec 2021 13:42:24 +0530 Subject: bus: mhi: core: Fix race while handling SYS_ERR at power up During SYS_ERR condition, as a response to the MHI_RESET from host, some devices tend to issue BHI interrupt without clearing the SYS_ERR state in the device. This creates a race condition and causes a failure in booting up the device. The issue is seen on the Sierra Wireless EM9191 modem during SYS_ERR handling in mhi_async_power_up(). Once the host detects that the device is in SYS_ERR state, it issues MHI_RESET and waits for the device to process the reset request. During this time, the device triggers the BHI interrupt to the host without clearing SYS_ERR condition. So the host starts handling the SYS_ERR condition again. To fix this issue, let's register the IRQ handler only after handling the SYS_ERR check to avoid getting spurious IRQs from the device. Fixes: e18d4e9fa79b ("bus: mhi: core: Handle syserr during power_up") Cc: stable@vger.kernel.org Reported-by: Aleksander Morgado Tested-by: Aleksander Morgado Tested-by: Thomas Perrot Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-8-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/core/pm.c | 35 ++++++++++++----------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/drivers/bus/mhi/core/pm.c b/drivers/bus/mhi/core/pm.c index e70a3e3a0b46..4aae0baea008 100644 --- a/drivers/bus/mhi/core/pm.c +++ b/drivers/bus/mhi/core/pm.c @@ -1053,7 +1053,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) enum mhi_ee_type current_ee; enum dev_st_transition next_state; struct device *dev = &mhi_cntrl->mhi_dev->dev; - u32 val; + u32 interval_us = 25000; /* poll register field every 25 milliseconds */ int ret; dev_info(dev, "Requested to power ON\n"); @@ -1070,10 +1070,6 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) mutex_lock(&mhi_cntrl->pm_mutex); mhi_cntrl->pm_state = MHI_PM_DISABLE; - ret = mhi_init_irq_setup(mhi_cntrl); - if (ret) - goto error_setup_irq; - /* Setup BHI INTVEC */ write_lock_irq(&mhi_cntrl->pm_lock); mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0); @@ -1087,7 +1083,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) dev_err(dev, "%s is not a valid EE for power on\n", TO_MHI_EXEC_STR(current_ee)); ret = -EIO; - goto error_async_power_up; + goto error_exit; } state = mhi_get_mhi_state(mhi_cntrl); @@ -1096,20 +1092,12 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) if (state == MHI_STATE_SYS_ERR) { mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); - ret = wait_event_timeout(mhi_cntrl->state_event, - MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state) || - mhi_read_reg_field(mhi_cntrl, - mhi_cntrl->regs, - MHICTRL, - MHICTRL_RESET_MASK, - MHICTRL_RESET_SHIFT, - &val) || - !val, - msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret) { - ret = -EIO; + ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, + MHICTRL_RESET_MASK, MHICTRL_RESET_SHIFT, 0, + interval_us); + if (ret) { dev_info(dev, "Failed to reset MHI due to syserr state\n"); - goto error_async_power_up; + goto error_exit; } /* @@ -1119,6 +1107,10 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0); } + ret = mhi_init_irq_setup(mhi_cntrl); + if (ret) + goto error_exit; + /* Transition to next state */ next_state = MHI_IN_PBL(current_ee) ? DEV_ST_TRANSITION_PBL : DEV_ST_TRANSITION_READY; @@ -1131,10 +1123,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) return 0; -error_async_power_up: - mhi_deinit_free_irq(mhi_cntrl); - -error_setup_irq: +error_exit: mhi_cntrl->pm_state = MHI_PM_DISABLE; mutex_unlock(&mhi_cntrl->pm_mutex); -- cgit v1.2.3 From 227fee5fc99eeb74d43bf68832f6d59d30ac07d8 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 16 Dec 2021 13:42:25 +0530 Subject: bus: mhi: core: Add an API for auto queueing buffers for DL channel Add a new API "mhi_prepare_for_transfer_autoqueue" for using with client drivers like QRTR to request MHI core to autoqueue buffers for the DL channel along with starting both UL and DL channels. So far, the "auto_queue" flag specified by the controller drivers in channel definition served this purpose but this will be removed at some point in future. Cc: netdev@vger.kernel.org Cc: Jakub Kicinski Cc: David S. Miller Cc: Greg Kroah-Hartman Co-developed-by: Loic Poulain Acked-by: Jakub Kicinski Signed-off-by: Loic Poulain Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-9-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/core/internal.h | 6 +++++- drivers/bus/mhi/core/main.c | 21 +++++++++++++++++---- include/linux/mhi.h | 21 ++++++++++++++++----- net/qrtr/mhi.c | 2 +- 4 files changed, 39 insertions(+), 11 deletions(-) diff --git a/drivers/bus/mhi/core/internal.h b/drivers/bus/mhi/core/internal.h index 9d72b1d1e986..e2e10474a9d9 100644 --- a/drivers/bus/mhi/core/internal.h +++ b/drivers/bus/mhi/core/internal.h @@ -682,8 +682,12 @@ void mhi_deinit_free_irq(struct mhi_controller *mhi_cntrl); void mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, struct image_info *img_info); void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl); + +/* Automatically allocate and queue inbound buffers */ +#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0) int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan); + struct mhi_chan *mhi_chan, unsigned int flags); + int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/core/main.c b/drivers/bus/mhi/core/main.c index 930aba666b67..ffde617f93a3 100644 --- a/drivers/bus/mhi/core/main.c +++ b/drivers/bus/mhi/core/main.c @@ -1430,7 +1430,7 @@ exit_unprepare_channel: } int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, - struct mhi_chan *mhi_chan) + struct mhi_chan *mhi_chan, unsigned int flags) { int ret = 0; struct device *dev = &mhi_chan->mhi_dev->dev; @@ -1455,6 +1455,9 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, if (ret) goto error_pm_state; + if (mhi_chan->dir == DMA_FROM_DEVICE) + mhi_chan->pre_alloc = !!(flags & MHI_CH_INBOUND_ALLOC_BUFS); + /* Pre-allocate buffer for xfer ring */ if (mhi_chan->pre_alloc) { int nr_el = get_nr_avail_ring_elements(mhi_cntrl, @@ -1610,8 +1613,7 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) read_unlock_bh(&mhi_cntrl->pm_lock); } -/* Move channel to start state */ -int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) +static int __mhi_prepare_for_transfer(struct mhi_device *mhi_dev, unsigned int flags) { int ret, dir; struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; @@ -1622,7 +1624,7 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) if (!mhi_chan) continue; - ret = mhi_prepare_channel(mhi_cntrl, mhi_chan); + ret = mhi_prepare_channel(mhi_cntrl, mhi_chan, flags); if (ret) goto error_open_chan; } @@ -1640,8 +1642,19 @@ error_open_chan: return ret; } + +int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) +{ + return __mhi_prepare_for_transfer(mhi_dev, 0); +} EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer); +int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) +{ + return __mhi_prepare_for_transfer(mhi_dev, MHI_CH_INBOUND_ALLOC_BUFS); +} +EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue); + void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) { struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; diff --git a/include/linux/mhi.h b/include/linux/mhi.h index a5cc4cdf9cc8..a5441ad33c74 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -730,15 +730,26 @@ void mhi_device_put(struct mhi_device *mhi_dev); /** * mhi_prepare_for_transfer - Setup UL and DL channels for data transfer. - * Allocate and initialize the channel context and - * also issue the START channel command to both - * channels. Channels can be started only if both - * host and device execution environments match and - * channels are in a DISABLED state. * @mhi_dev: Device associated with the channels + * + * Allocate and initialize the channel context and also issue the START channel + * command to both channels. Channels can be started only if both host and + * device execution environments match and channels are in a DISABLED state. */ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev); +/** + * mhi_prepare_for_transfer_autoqueue - Setup UL and DL channels with auto queue + * buffers for DL traffic + * @mhi_dev: Device associated with the channels + * + * Allocate and initialize the channel context and also issue the START channel + * command to both channels. Channels can be started only if both host and + * device execution environments match and channels are in a DISABLED state. + * The MHI core will automatically allocate and queue buffers for the DL traffic. + */ +int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev); + /** * mhi_unprepare_from_transfer - Reset UL and DL channels for data transfer. * Issue the RESET channel command and let the diff --git a/net/qrtr/mhi.c b/net/qrtr/mhi.c index fa611678af05..18196e1c8c2f 100644 --- a/net/qrtr/mhi.c +++ b/net/qrtr/mhi.c @@ -79,7 +79,7 @@ static int qcom_mhi_qrtr_probe(struct mhi_device *mhi_dev, int rc; /* start channels */ - rc = mhi_prepare_for_transfer(mhi_dev); + rc = mhi_prepare_for_transfer_autoqueue(mhi_dev); if (rc) return rc; -- cgit v1.2.3 From 5a717e93239fc373a314e03e45c43b62ebea1b26 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 16 Dec 2021 13:42:26 +0530 Subject: bus: mhi: core: Use correctly sized arguments for bit field The find.h APIs are designed to be used only on unsigned long arguments. This can technically result in a over-read, but it is harmless in this case. Regardless, fix it to avoid the warning seen under -Warray-bounds, which we'd like to enable globally: In file included from ./include/linux/bitmap.h:9, from ./include/linux/cpumask.h:12, from ./arch/x86/include/asm/cpumask.h:5, from ./arch/x86/include/asm/msr.h:11, from ./arch/x86/include/asm/processor.h:22, from ./arch/x86/include/asm/cpufeature.h:5, from ./arch/x86/include/asm/thread_info.h:53, from ./include/linux/thread_info.h:60, from ./arch/x86/include/asm/preempt.h:7, from ./include/linux/preempt.h:78, from ./include/linux/spinlock.h:55, from ./include/linux/wait.h:9, from ./include/linux/wait_bit.h:8, from ./include/linux/fs.h:6, from ./include/linux/debugfs.h:15, from drivers/bus/mhi/core/init.c:7: drivers/bus/mhi/core/init.c: In function 'to_mhi_pm_state_str': ./include/linux/find.h:187:37: warning: array subscript 'long unsigned int[0]' is partly outside array bounds of 'enum mhi_pm_state[1]' [-Warray-bounds] 187 | unsigned long val = *addr & GENMASK(size - 1, 0); | ^~~~~ drivers/bus/mhi/core/init.c:80:51: note: while referencing 'state' 80 | const char *to_mhi_pm_state_str(enum mhi_pm_state state) | ~~~~~~~~~~~~~~~~~~^~~~~ Link: https://lore.kernel.org/r/20211215232446.2069794-1-keescook@chromium.org [mani: changed the variable name "bits" to "pm_state"] Reviewed-by: Manivannan Sadhasivam Signed-off-by: Kees Cook Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-10-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/core/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/bus/mhi/core/init.c b/drivers/bus/mhi/core/init.c index f1ec34417592..046f407dc5d6 100644 --- a/drivers/bus/mhi/core/init.c +++ b/drivers/bus/mhi/core/init.c @@ -79,7 +79,8 @@ static const char * const mhi_pm_state_str[] = { const char *to_mhi_pm_state_str(enum mhi_pm_state state) { - int index = find_last_bit((unsigned long *)&state, 32); + unsigned long pm_state = state; + int index = find_last_bit(&pm_state, 32); if (index >= ARRAY_SIZE(mhi_pm_state_str)) return "Invalid State"; -- cgit v1.2.3 From 1dba0075fc3d2c2ae8503c3e213dc72a93e17761 Mon Sep 17 00:00:00 2001 From: Thomas Perrot Date: Thu, 16 Dec 2021 13:42:27 +0530 Subject: bus: mhi: pci_generic: Introduce Sierra EM919X support Add support for EM919X modems, this modem series is based on SDX55 qcom chip. It is mandatory to use the same ring for control+data and diag events. Link: https://lore.kernel.org/r/20211123081541.648426-1-thomas.perrot@bootlin.com Tested-by: Aleksander Morgado Reviewed-by: Manivannan Sadhasivam Signed-off-by: Thomas Perrot Signed-off-by: Manivannan Sadhasivam Link: https://lore.kernel.org/r/20211216081227.237749-11-manivannan.sadhasivam@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/bus/mhi/pci_generic.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/drivers/bus/mhi/pci_generic.c b/drivers/bus/mhi/pci_generic.c index 759df02fb0be..3a258a677df8 100644 --- a/drivers/bus/mhi/pci_generic.c +++ b/drivers/bus/mhi/pci_generic.c @@ -403,7 +403,50 @@ static const struct mhi_pci_dev_info mhi_mv31_info = { .dma_data_width = 32, }; +static const struct mhi_channel_config mhi_sierra_em919x_channels[] = { + MHI_CHANNEL_CONFIG_UL_SBL(2, "SAHARA", 32, 0), + MHI_CHANNEL_CONFIG_DL_SBL(3, "SAHARA", 256, 0), + MHI_CHANNEL_CONFIG_UL(4, "DIAG", 32, 0), + MHI_CHANNEL_CONFIG_DL(5, "DIAG", 32, 0), + MHI_CHANNEL_CONFIG_UL(12, "MBIM", 128, 0), + MHI_CHANNEL_CONFIG_DL(13, "MBIM", 128, 0), + MHI_CHANNEL_CONFIG_UL(14, "QMI", 32, 0), + MHI_CHANNEL_CONFIG_DL(15, "QMI", 32, 0), + MHI_CHANNEL_CONFIG_UL(32, "DUN", 32, 0), + MHI_CHANNEL_CONFIG_DL(33, "DUN", 32, 0), + MHI_CHANNEL_CONFIG_HW_UL(100, "IP_HW0", 512, 1), + MHI_CHANNEL_CONFIG_HW_DL(101, "IP_HW0", 512, 2), +}; + +static struct mhi_event_config modem_sierra_em919x_mhi_events[] = { + /* first ring is control+data and DIAG ring */ + MHI_EVENT_CONFIG_CTRL(0, 2048), + /* Hardware channels request dedicated hardware event rings */ + MHI_EVENT_CONFIG_HW_DATA(1, 2048, 100), + MHI_EVENT_CONFIG_HW_DATA(2, 2048, 101) +}; + +static const struct mhi_controller_config modem_sierra_em919x_config = { + .max_channels = 128, + .timeout_ms = 24000, + .num_channels = ARRAY_SIZE(mhi_sierra_em919x_channels), + .ch_cfg = mhi_sierra_em919x_channels, + .num_events = ARRAY_SIZE(modem_sierra_em919x_mhi_events), + .event_cfg = modem_sierra_em919x_mhi_events, +}; + +static const struct mhi_pci_dev_info mhi_sierra_em919x_info = { + .name = "sierra-em919x", + .config = &modem_sierra_em919x_config, + .bar_num = MHI_PCI_DEFAULT_BAR_NUM, + .dma_data_width = 32, + .sideband_wake = false, +}; + static const struct pci_device_id mhi_pci_id_table[] = { + /* EM919x (sdx55), use the same vid:pid as qcom-sdx55m */ + { PCI_DEVICE_SUB(PCI_VENDOR_ID_QCOM, 0x0306, 0x18d7, 0x0200), + .driver_data = (kernel_ulong_t) &mhi_sierra_em919x_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0306), .driver_data = (kernel_ulong_t) &mhi_qcom_sdx55_info }, { PCI_DEVICE(PCI_VENDOR_ID_QCOM, 0x0304), -- cgit v1.2.3 From b56ca501a4112b568102e6f910ed3d5e32dde52c Mon Sep 17 00:00:00 2001 From: Stephen Boyd Date: Thu, 16 Dec 2021 11:08:07 -0800 Subject: spmi: pmic-arb: Add sid and address to error messages It's useful to know what particular device/component is having trouble accessing the bus. Add the sid and address to error messages here so that debugging is a little simpler. Link: https://lore.kernel.org/r/20210920234849.3614036-1-swboyd@chromium.org Cc: Subbaraman Narayanamurthy Cc: satya priya Reviewed-by: Subbaraman Narayanamurthy Reviewed-by: Satya Priya Signed-off-by: Stephen Boyd Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20211216190812.1574801-2-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index bbbd311eda03..e397c2532c8d 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -261,20 +261,21 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, if (status & PMIC_ARB_STATUS_DONE) { if (status & PMIC_ARB_STATUS_DENIED) { - dev_err(&ctrl->dev, "%s: transaction denied (0x%x)\n", - __func__, status); + dev_err(&ctrl->dev, "%s: %#x %#x: transaction denied (%#x)\n", + __func__, sid, addr, status); return -EPERM; } if (status & PMIC_ARB_STATUS_FAILURE) { - dev_err(&ctrl->dev, "%s: transaction failed (0x%x)\n", - __func__, status); + dev_err(&ctrl->dev, "%s: %#x %#x: transaction failed (%#x)\n", + __func__, sid, addr, status); + WARN_ON(1); return -EIO; } if (status & PMIC_ARB_STATUS_DROPPED) { - dev_err(&ctrl->dev, "%s: transaction dropped (0x%x)\n", - __func__, status); + dev_err(&ctrl->dev, "%s: %#x %#x: transaction dropped (%#x)\n", + __func__, sid, addr, status); return -EIO; } @@ -283,8 +284,8 @@ static int pmic_arb_wait_for_done(struct spmi_controller *ctrl, udelay(1); } - dev_err(&ctrl->dev, "%s: timeout, status 0x%x\n", - __func__, status); + dev_err(&ctrl->dev, "%s: %#x %#x: timeout, status %#x\n", + __func__, sid, addr, status); return -ETIMEDOUT; } -- cgit v1.2.3 From ef8261dce39503cddf1fe1f44578815df8ee4b2e Mon Sep 17 00:00:00 2001 From: James Lo Date: Thu, 16 Dec 2021 11:08:08 -0800 Subject: dt-bindings: spmi: remove the constraint of reg property 'reg' is controller specific so we shouldn't even be specifying it here. Just remove it. Link: https://lore.kernel.org/r/20211119034613.32489-2-james.lo@mediatek.com Reviewed-by: Rob Herring Signed-off-by: James Lo Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20211216190812.1574801-3-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/spmi/spmi.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/Documentation/devicetree/bindings/spmi/spmi.yaml b/Documentation/devicetree/bindings/spmi/spmi.yaml index 1d243faef2f8..c1b06fa5c631 100644 --- a/Documentation/devicetree/bindings/spmi/spmi.yaml +++ b/Documentation/devicetree/bindings/spmi/spmi.yaml @@ -24,9 +24,6 @@ properties: $nodename: pattern: "^spmi@.*" - reg: - maxItems: 1 - "#address-cells": const: 2 -- cgit v1.2.3 From 312644352f53a22f6f11f16481a4f23694650aba Mon Sep 17 00:00:00 2001 From: James Lo Date: Thu, 16 Dec 2021 11:08:09 -0800 Subject: dt-bindings: spmi: document binding for the Mediatek SPMI controller This adds documentation for the SPMI controller found on Mediatek SoCs. Link: https://lore.kernel.org/r/20211119034613.32489-3-james.lo@mediatek.com Reviewed-by: Rob Herring Signed-off-by: James Lo Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20211216190812.1574801-4-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- .../bindings/spmi/mtk,spmi-mtk-pmif.yaml | 76 ++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml diff --git a/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml b/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml new file mode 100644 index 000000000000..2445c5e0b0ef --- /dev/null +++ b/Documentation/devicetree/bindings/spmi/mtk,spmi-mtk-pmif.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/spmi/mtk,spmi-mtk-pmif.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek SPMI Controller Device Tree Bindings + +maintainers: + - Hsin-Hsiung Wang + +description: |+ + On MediaTek SoCs the PMIC is connected via SPMI and the controller allows + for multiple SoCs to control a single SPMI master. + +allOf: + - $ref: "spmi.yaml" + +properties: + compatible: + enum: + - mediatek,mt6873-spmi + - mediatek,mt8195-spmi + + reg: + maxItems: 2 + + reg-names: + items: + - const: pmif + - const: spmimst + + clocks: + minItems: 3 + maxItems: 3 + + clock-names: + items: + - const: pmif_sys_ck + - const: pmif_tmr_ck + - const: spmimst_clk_mux + + assigned-clocks: + maxItems: 1 + + assigned-clock-parents: + maxItems: 1 + +required: + - compatible + - reg + - reg-names + - clocks + - clock-names + +unevaluatedProperties: false + +examples: + - | + #include + + spmi: spmi@10027000 { + compatible = "mediatek,mt6873-spmi"; + reg = <0x10027000 0xe00>, + <0x10029000 0x100>; + reg-names = "pmif", "spmimst"; + clocks = <&infracfg CLK_INFRA_PMIC_AP>, + <&infracfg CLK_INFRA_PMIC_TMR>, + <&topckgen CLK_TOP_SPMI_MST_SEL>; + clock-names = "pmif_sys_ck", + "pmif_tmr_ck", + "spmimst_clk_mux"; + assigned-clocks = <&topckgen CLK_TOP_PWRAP_ULPOSC_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_OSC_D10>; + }; +... -- cgit v1.2.3 From b45b3ccef8c063d21eb746d85337eaf71f6b5f07 Mon Sep 17 00:00:00 2001 From: James Lo Date: Thu, 16 Dec 2021 11:08:10 -0800 Subject: spmi: mediatek: Add support for MT6873/8192 Add spmi support for MT6873/8192. Refine indent in spmi-mtk-pmif.c. Link: https://lore.kernel.org/r/20211119034613.32489-4-james.lo@mediatek.com Acked-by: AngeloGioacchino Del Regno Signed-off-by: James Lo Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20211216190812.1574801-5-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/Kconfig | 11 ++ drivers/spmi/Makefile | 1 + drivers/spmi/spmi-mtk-pmif.c | 454 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 466 insertions(+) create mode 100644 drivers/spmi/spmi-mtk-pmif.c diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 2874b6c26028..737802046314 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -34,4 +34,15 @@ config SPMI_MSM_PMIC_ARB This is required for communicating with Qualcomm PMICs and other devices that have the SPMI interface. +config SPMI_MTK_PMIF + tristate "Mediatek SPMI Controller (PMIC Arbiter)" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + If you say yes to this option, support will be included for the + built-in SPMI PMIC Arbiter interface on Mediatek family + processors. + + This is required for communicating with Mediatek PMICs and + other devices that have the SPMI interface. + endif diff --git a/drivers/spmi/Makefile b/drivers/spmi/Makefile index 6e092e6f290c..9d974424c8c1 100644 --- a/drivers/spmi/Makefile +++ b/drivers/spmi/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_SPMI) += spmi.o obj-$(CONFIG_SPMI_HISI3670) += hisi-spmi-controller.o obj-$(CONFIG_SPMI_MSM_PMIC_ARB) += spmi-pmic-arb.o +obj-$(CONFIG_SPMI_MTK_PMIF) += spmi-mtk-pmif.o diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c new file mode 100644 index 000000000000..f6644104088d --- /dev/null +++ b/drivers/spmi/spmi-mtk-pmif.c @@ -0,0 +1,454 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (c) 2021 MediaTek Inc. + +#include +#include +#include +#include +#include +#include +#include + +#define SWINF_IDLE 0x00 +#define SWINF_WFVLDCLR 0x06 + +#define GET_SWINF(x) (((x) >> 1) & 0x7) + +#define PMIF_CMD_REG_0 0 +#define PMIF_CMD_REG 1 +#define PMIF_CMD_EXT_REG 2 +#define PMIF_CMD_EXT_REG_LONG 3 + +#define PMIF_DELAY_US 10 +#define PMIF_TIMEOUT_US (10 * 1000) + +#define PMIF_CHAN_OFFSET 0x5 + +#define PMIF_MAX_CLKS 3 + +#define SPMI_OP_ST_BUSY 1 + +struct ch_reg { + u32 ch_sta; + u32 wdata; + u32 rdata; + u32 ch_send; + u32 ch_rdy; +}; + +struct pmif_data { + const u32 *regs; + const u32 *spmimst_regs; + u32 soc_chan; +}; + +struct pmif { + void __iomem *base; + void __iomem *spmimst_base; + struct ch_reg chan; + struct clk_bulk_data clks[PMIF_MAX_CLKS]; + size_t nclks; + const struct pmif_data *data; +}; + +static const char * const pmif_clock_names[] = { + "pmif_sys_ck", "pmif_tmr_ck", "spmimst_clk_mux", +}; + +enum pmif_regs { + PMIF_INIT_DONE, + PMIF_INF_EN, + PMIF_ARB_EN, + PMIF_CMDISSUE_EN, + PMIF_TIMER_CTRL, + PMIF_SPI_MODE_CTRL, + PMIF_IRQ_EVENT_EN_0, + PMIF_IRQ_FLAG_0, + PMIF_IRQ_CLR_0, + PMIF_IRQ_EVENT_EN_1, + PMIF_IRQ_FLAG_1, + PMIF_IRQ_CLR_1, + PMIF_IRQ_EVENT_EN_2, + PMIF_IRQ_FLAG_2, + PMIF_IRQ_CLR_2, + PMIF_IRQ_EVENT_EN_3, + PMIF_IRQ_FLAG_3, + PMIF_IRQ_CLR_3, + PMIF_IRQ_EVENT_EN_4, + PMIF_IRQ_FLAG_4, + PMIF_IRQ_CLR_4, + PMIF_WDT_EVENT_EN_0, + PMIF_WDT_FLAG_0, + PMIF_WDT_EVENT_EN_1, + PMIF_WDT_FLAG_1, + PMIF_SWINF_0_STA, + PMIF_SWINF_0_WDATA_31_0, + PMIF_SWINF_0_RDATA_31_0, + PMIF_SWINF_0_ACC, + PMIF_SWINF_0_VLD_CLR, + PMIF_SWINF_1_STA, + PMIF_SWINF_1_WDATA_31_0, + PMIF_SWINF_1_RDATA_31_0, + PMIF_SWINF_1_ACC, + PMIF_SWINF_1_VLD_CLR, + PMIF_SWINF_2_STA, + PMIF_SWINF_2_WDATA_31_0, + PMIF_SWINF_2_RDATA_31_0, + PMIF_SWINF_2_ACC, + PMIF_SWINF_2_VLD_CLR, + PMIF_SWINF_3_STA, + PMIF_SWINF_3_WDATA_31_0, + PMIF_SWINF_3_RDATA_31_0, + PMIF_SWINF_3_ACC, + PMIF_SWINF_3_VLD_CLR, +}; + +static const u32 mt6873_regs[] = { + [PMIF_INIT_DONE] = 0x0000, + [PMIF_INF_EN] = 0x0024, + [PMIF_ARB_EN] = 0x0150, + [PMIF_CMDISSUE_EN] = 0x03B4, + [PMIF_TIMER_CTRL] = 0x03E0, + [PMIF_SPI_MODE_CTRL] = 0x0400, + [PMIF_IRQ_EVENT_EN_0] = 0x0418, + [PMIF_IRQ_FLAG_0] = 0x0420, + [PMIF_IRQ_CLR_0] = 0x0424, + [PMIF_IRQ_EVENT_EN_1] = 0x0428, + [PMIF_IRQ_FLAG_1] = 0x0430, + [PMIF_IRQ_CLR_1] = 0x0434, + [PMIF_IRQ_EVENT_EN_2] = 0x0438, + [PMIF_IRQ_FLAG_2] = 0x0440, + [PMIF_IRQ_CLR_2] = 0x0444, + [PMIF_IRQ_EVENT_EN_3] = 0x0448, + [PMIF_IRQ_FLAG_3] = 0x0450, + [PMIF_IRQ_CLR_3] = 0x0454, + [PMIF_IRQ_EVENT_EN_4] = 0x0458, + [PMIF_IRQ_FLAG_4] = 0x0460, + [PMIF_IRQ_CLR_4] = 0x0464, + [PMIF_WDT_EVENT_EN_0] = 0x046C, + [PMIF_WDT_FLAG_0] = 0x0470, + [PMIF_WDT_EVENT_EN_1] = 0x0474, + [PMIF_WDT_FLAG_1] = 0x0478, + [PMIF_SWINF_0_ACC] = 0x0C00, + [PMIF_SWINF_0_WDATA_31_0] = 0x0C04, + [PMIF_SWINF_0_RDATA_31_0] = 0x0C14, + [PMIF_SWINF_0_VLD_CLR] = 0x0C24, + [PMIF_SWINF_0_STA] = 0x0C28, + [PMIF_SWINF_1_ACC] = 0x0C40, + [PMIF_SWINF_1_WDATA_31_0] = 0x0C44, + [PMIF_SWINF_1_RDATA_31_0] = 0x0C54, + [PMIF_SWINF_1_VLD_CLR] = 0x0C64, + [PMIF_SWINF_1_STA] = 0x0C68, + [PMIF_SWINF_2_ACC] = 0x0C80, + [PMIF_SWINF_2_WDATA_31_0] = 0x0C84, + [PMIF_SWINF_2_RDATA_31_0] = 0x0C94, + [PMIF_SWINF_2_VLD_CLR] = 0x0CA4, + [PMIF_SWINF_2_STA] = 0x0CA8, + [PMIF_SWINF_3_ACC] = 0x0CC0, + [PMIF_SWINF_3_WDATA_31_0] = 0x0CC4, + [PMIF_SWINF_3_RDATA_31_0] = 0x0CD4, + [PMIF_SWINF_3_VLD_CLR] = 0x0CE4, + [PMIF_SWINF_3_STA] = 0x0CE8, +}; + +enum spmi_regs { + SPMI_OP_ST_CTRL, + SPMI_GRP_ID_EN, + SPMI_OP_ST_STA, + SPMI_MST_SAMPL, + SPMI_MST_REQ_EN, + SPMI_REC_CTRL, + SPMI_REC0, + SPMI_REC1, + SPMI_REC2, + SPMI_REC3, + SPMI_REC4, + SPMI_MST_DBG, +}; + +static const u32 mt6873_spmi_regs[] = { + [SPMI_OP_ST_CTRL] = 0x0000, + [SPMI_GRP_ID_EN] = 0x0004, + [SPMI_OP_ST_STA] = 0x0008, + [SPMI_MST_SAMPL] = 0x000c, + [SPMI_MST_REQ_EN] = 0x0010, + [SPMI_REC_CTRL] = 0x0040, + [SPMI_REC0] = 0x0044, + [SPMI_REC1] = 0x0048, + [SPMI_REC2] = 0x004c, + [SPMI_REC3] = 0x0050, + [SPMI_REC4] = 0x0054, + [SPMI_MST_DBG] = 0x00fc, +}; + +static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg) +{ + return readl(arb->base + arb->data->regs[reg]); +} + +static void pmif_writel(struct pmif *arb, u32 val, enum pmif_regs reg) +{ + writel(val, arb->base + arb->data->regs[reg]); +} + +static void mtk_spmi_writel(struct pmif *arb, u32 val, enum spmi_regs reg) +{ + writel(val, arb->spmimst_base + arb->data->spmimst_regs[reg]); +} + +static bool pmif_is_fsm_vldclr(struct pmif *arb) +{ + u32 reg_rdata; + + reg_rdata = pmif_readl(arb, arb->chan.ch_sta); + + return GET_SWINF(reg_rdata) == SWINF_WFVLDCLR; +} + +static int pmif_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) +{ + struct pmif *arb = spmi_controller_get_drvdata(ctrl); + u32 rdata, cmd; + int ret; + + /* Check the opcode */ + if (opc < SPMI_CMD_RESET || opc > SPMI_CMD_WAKEUP) + return -EINVAL; + + cmd = opc - SPMI_CMD_RESET; + + mtk_spmi_writel(arb, (cmd << 0x4) | sid, SPMI_OP_ST_CTRL); + ret = readl_poll_timeout_atomic(arb->spmimst_base + arb->data->spmimst_regs[SPMI_OP_ST_STA], + rdata, (rdata & SPMI_OP_ST_BUSY) == SPMI_OP_ST_BUSY, + PMIF_DELAY_US, PMIF_TIMEOUT_US); + if (ret < 0) + dev_err(&ctrl->dev, "timeout, err = %d\n", ret); + + return ret; +} + +static int pmif_spmi_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 addr, u8 *buf, size_t len) +{ + struct pmif *arb = spmi_controller_get_drvdata(ctrl); + struct ch_reg *inf_reg; + int ret; + u32 data, cmd; + + /* Check for argument validation. */ + if (sid & ~0xf) { + dev_err(&ctrl->dev, "exceed the max slv id\n"); + return -EINVAL; + } + + if (len > 4) { + dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len); + + return -EINVAL; + } + + if (opc >= 0x60 && opc <= 0x7f) + opc = PMIF_CMD_REG; + else if ((opc >= 0x20 && opc <= 0x2f) || (opc >= 0x38 && opc <= 0x3f)) + opc = PMIF_CMD_EXT_REG_LONG; + else + return -EINVAL; + + /* Wait for Software Interface FSM state to be IDLE. */ + inf_reg = &arb->chan; + ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], + data, GET_SWINF(data) == SWINF_IDLE, + PMIF_DELAY_US, PMIF_TIMEOUT_US); + if (ret < 0) { + /* set channel ready if the data has transferred */ + if (pmif_is_fsm_vldclr(arb)) + pmif_writel(arb, 1, inf_reg->ch_rdy); + dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n"); + return ret; + } + + /* Send the command. */ + cmd = (opc << 30) | (sid << 24) | ((len - 1) << 16) | addr; + pmif_writel(arb, cmd, inf_reg->ch_send); + + /* + * Wait for Software Interface FSM state to be WFVLDCLR, + * read the data and clear the valid flag. + */ + ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], + data, GET_SWINF(data) == SWINF_WFVLDCLR, + PMIF_DELAY_US, PMIF_TIMEOUT_US); + if (ret < 0) { + dev_err(&ctrl->dev, "failed to wait for SWINF_WFVLDCLR\n"); + return ret; + } + + data = pmif_readl(arb, inf_reg->rdata); + memcpy(buf, &data, len); + pmif_writel(arb, 1, inf_reg->ch_rdy); + + return 0; +} + +static int pmif_spmi_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 addr, const u8 *buf, size_t len) +{ + struct pmif *arb = spmi_controller_get_drvdata(ctrl); + struct ch_reg *inf_reg; + int ret; + u32 data, cmd; + + if (len > 4) { + dev_err(&ctrl->dev, "pmif supports 1..4 bytes per trans, but:%zu requested", len); + + return -EINVAL; + } + + /* Check the opcode */ + if (opc >= 0x40 && opc <= 0x5F) + opc = PMIF_CMD_REG; + else if ((opc <= 0xF) || (opc >= 0x30 && opc <= 0x37)) + opc = PMIF_CMD_EXT_REG_LONG; + else if (opc >= 0x80) + opc = PMIF_CMD_REG_0; + else + return -EINVAL; + + /* Wait for Software Interface FSM state to be IDLE. */ + inf_reg = &arb->chan; + ret = readl_poll_timeout_atomic(arb->base + arb->data->regs[inf_reg->ch_sta], + data, GET_SWINF(data) == SWINF_IDLE, + PMIF_DELAY_US, PMIF_TIMEOUT_US); + if (ret < 0) { + /* set channel ready if the data has transferred */ + if (pmif_is_fsm_vldclr(arb)) + pmif_writel(arb, 1, inf_reg->ch_rdy); + dev_err(&ctrl->dev, "failed to wait for SWINF_IDLE\n"); + return ret; + } + + /* Set the write data. */ + memcpy(&data, buf, len); + pmif_writel(arb, data, inf_reg->wdata); + + /* Send the command. */ + cmd = (opc << 30) | BIT(29) | (sid << 24) | ((len - 1) << 16) | addr; + pmif_writel(arb, cmd, inf_reg->ch_send); + + return 0; +} + +static const struct pmif_data mt6873_pmif_arb = { + .regs = mt6873_regs, + .spmimst_regs = mt6873_spmi_regs, + .soc_chan = 2, +}; + +static int mtk_spmi_probe(struct platform_device *pdev) +{ + struct pmif *arb; + struct spmi_controller *ctrl; + int err, i; + u32 chan_offset; + + ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*arb)); + if (!ctrl) + return -ENOMEM; + + arb = spmi_controller_get_drvdata(ctrl); + arb->data = device_get_match_data(&pdev->dev); + if (!arb->data) { + err = -EINVAL; + dev_err(&pdev->dev, "Cannot get drv_data\n"); + goto err_put_ctrl; + } + + arb->base = devm_platform_ioremap_resource_byname(pdev, "pmif"); + if (IS_ERR(arb->base)) { + err = PTR_ERR(arb->base); + goto err_put_ctrl; + } + + arb->spmimst_base = devm_platform_ioremap_resource_byname(pdev, "spmimst"); + if (IS_ERR(arb->spmimst_base)) { + err = PTR_ERR(arb->spmimst_base); + goto err_put_ctrl; + } + + arb->nclks = ARRAY_SIZE(pmif_clock_names); + for (i = 0; i < arb->nclks; i++) + arb->clks[i].id = pmif_clock_names[i]; + + err = devm_clk_bulk_get(&pdev->dev, arb->nclks, arb->clks); + if (err) { + dev_err(&pdev->dev, "Failed to get clocks: %d\n", err); + goto err_put_ctrl; + } + + err = clk_bulk_prepare_enable(arb->nclks, arb->clks); + if (err) { + dev_err(&pdev->dev, "Failed to enable clocks: %d\n", err); + goto err_put_ctrl; + } + + ctrl->cmd = pmif_arb_cmd; + ctrl->read_cmd = pmif_spmi_read_cmd; + ctrl->write_cmd = pmif_spmi_write_cmd; + + chan_offset = PMIF_CHAN_OFFSET * arb->data->soc_chan; + arb->chan.ch_sta = PMIF_SWINF_0_STA + chan_offset; + arb->chan.wdata = PMIF_SWINF_0_WDATA_31_0 + chan_offset; + arb->chan.rdata = PMIF_SWINF_0_RDATA_31_0 + chan_offset; + arb->chan.ch_send = PMIF_SWINF_0_ACC + chan_offset; + arb->chan.ch_rdy = PMIF_SWINF_0_VLD_CLR + chan_offset; + + platform_set_drvdata(pdev, ctrl); + + err = spmi_controller_add(ctrl); + if (err) + goto err_domain_remove; + + return 0; + +err_domain_remove: + clk_bulk_disable_unprepare(arb->nclks, arb->clks); +err_put_ctrl: + spmi_controller_put(ctrl); + return err; +} + +static int mtk_spmi_remove(struct platform_device *pdev) +{ + struct spmi_controller *ctrl = platform_get_drvdata(pdev); + struct pmif *arb = spmi_controller_get_drvdata(ctrl); + + clk_bulk_disable_unprepare(arb->nclks, arb->clks); + spmi_controller_remove(ctrl); + spmi_controller_put(ctrl); + return 0; +} + +static const struct of_device_id mtk_spmi_match_table[] = { + { + .compatible = "mediatek,mt6873-spmi", + .data = &mt6873_pmif_arb, + }, { + /* sentinel */ + }, +}; +MODULE_DEVICE_TABLE(of, mtk_spmi_match_table); + +static struct platform_driver mtk_spmi_driver = { + .driver = { + .name = "spmi-mtk", + .of_match_table = of_match_ptr(mtk_spmi_match_table), + }, + .probe = mtk_spmi_probe, + .remove = mtk_spmi_remove, +}; +module_platform_driver(mtk_spmi_driver); + +MODULE_AUTHOR("Hsin-Hsiung Wang "); +MODULE_DESCRIPTION("MediaTek SPMI Driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 504eb71e4717ddfedd877d33fce684f3ab6d657c Mon Sep 17 00:00:00 2001 From: James Lo Date: Thu, 16 Dec 2021 11:08:11 -0800 Subject: spmi: mediatek: Add support for MT8195 Add spmi support for MT8195. Refine indent in spmi-mtk-pmif.c. Link: https://lore.kernel.org/r/20211119034613.32489-5-james.lo@mediatek.com Acked-by: AngeloGioacchino Del Regno Signed-off-by: James Lo Signed-off-by: Henry Chen Signed-off-by: Hsin-Hsiung Wang Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20211216190812.1574801-6-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-mtk-pmif.c | 88 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/drivers/spmi/spmi-mtk-pmif.c b/drivers/spmi/spmi-mtk-pmif.c index f6644104088d..ad511f2c3324 100644 --- a/drivers/spmi/spmi-mtk-pmif.c +++ b/drivers/spmi/spmi-mtk-pmif.c @@ -152,6 +152,54 @@ static const u32 mt6873_regs[] = { [PMIF_SWINF_3_STA] = 0x0CE8, }; +static const u32 mt8195_regs[] = { + [PMIF_INIT_DONE] = 0x0000, + [PMIF_INF_EN] = 0x0024, + [PMIF_ARB_EN] = 0x0150, + [PMIF_CMDISSUE_EN] = 0x03B8, + [PMIF_TIMER_CTRL] = 0x03E4, + [PMIF_SPI_MODE_CTRL] = 0x0408, + [PMIF_IRQ_EVENT_EN_0] = 0x0420, + [PMIF_IRQ_FLAG_0] = 0x0428, + [PMIF_IRQ_CLR_0] = 0x042C, + [PMIF_IRQ_EVENT_EN_1] = 0x0430, + [PMIF_IRQ_FLAG_1] = 0x0438, + [PMIF_IRQ_CLR_1] = 0x043C, + [PMIF_IRQ_EVENT_EN_2] = 0x0440, + [PMIF_IRQ_FLAG_2] = 0x0448, + [PMIF_IRQ_CLR_2] = 0x044C, + [PMIF_IRQ_EVENT_EN_3] = 0x0450, + [PMIF_IRQ_FLAG_3] = 0x0458, + [PMIF_IRQ_CLR_3] = 0x045C, + [PMIF_IRQ_EVENT_EN_4] = 0x0460, + [PMIF_IRQ_FLAG_4] = 0x0468, + [PMIF_IRQ_CLR_4] = 0x046C, + [PMIF_WDT_EVENT_EN_0] = 0x0474, + [PMIF_WDT_FLAG_0] = 0x0478, + [PMIF_WDT_EVENT_EN_1] = 0x047C, + [PMIF_WDT_FLAG_1] = 0x0480, + [PMIF_SWINF_0_ACC] = 0x0800, + [PMIF_SWINF_0_WDATA_31_0] = 0x0804, + [PMIF_SWINF_0_RDATA_31_0] = 0x0814, + [PMIF_SWINF_0_VLD_CLR] = 0x0824, + [PMIF_SWINF_0_STA] = 0x0828, + [PMIF_SWINF_1_ACC] = 0x0840, + [PMIF_SWINF_1_WDATA_31_0] = 0x0844, + [PMIF_SWINF_1_RDATA_31_0] = 0x0854, + [PMIF_SWINF_1_VLD_CLR] = 0x0864, + [PMIF_SWINF_1_STA] = 0x0868, + [PMIF_SWINF_2_ACC] = 0x0880, + [PMIF_SWINF_2_WDATA_31_0] = 0x0884, + [PMIF_SWINF_2_RDATA_31_0] = 0x0894, + [PMIF_SWINF_2_VLD_CLR] = 0x08A4, + [PMIF_SWINF_2_STA] = 0x08A8, + [PMIF_SWINF_3_ACC] = 0x08C0, + [PMIF_SWINF_3_WDATA_31_0] = 0x08C4, + [PMIF_SWINF_3_RDATA_31_0] = 0x08D4, + [PMIF_SWINF_3_VLD_CLR] = 0x08E4, + [PMIF_SWINF_3_STA] = 0x08E8, +}; + enum spmi_regs { SPMI_OP_ST_CTRL, SPMI_GRP_ID_EN, @@ -165,6 +213,15 @@ enum spmi_regs { SPMI_REC3, SPMI_REC4, SPMI_MST_DBG, + + /* MT8195 spmi regs */ + SPMI_MST_RCS_CTRL, + SPMI_SLV_3_0_EINT, + SPMI_SLV_7_4_EINT, + SPMI_SLV_B_8_EINT, + SPMI_SLV_F_C_EINT, + SPMI_REC_CMD_DEC, + SPMI_DEC_DBG, }; static const u32 mt6873_spmi_regs[] = { @@ -182,6 +239,28 @@ static const u32 mt6873_spmi_regs[] = { [SPMI_MST_DBG] = 0x00fc, }; +static const u32 mt8195_spmi_regs[] = { + [SPMI_OP_ST_CTRL] = 0x0000, + [SPMI_GRP_ID_EN] = 0x0004, + [SPMI_OP_ST_STA] = 0x0008, + [SPMI_MST_SAMPL] = 0x000C, + [SPMI_MST_REQ_EN] = 0x0010, + [SPMI_MST_RCS_CTRL] = 0x0014, + [SPMI_SLV_3_0_EINT] = 0x0020, + [SPMI_SLV_7_4_EINT] = 0x0024, + [SPMI_SLV_B_8_EINT] = 0x0028, + [SPMI_SLV_F_C_EINT] = 0x002C, + [SPMI_REC_CTRL] = 0x0040, + [SPMI_REC0] = 0x0044, + [SPMI_REC1] = 0x0048, + [SPMI_REC2] = 0x004C, + [SPMI_REC3] = 0x0050, + [SPMI_REC4] = 0x0054, + [SPMI_REC_CMD_DEC] = 0x005C, + [SPMI_DEC_DBG] = 0x00F8, + [SPMI_MST_DBG] = 0x00FC, +}; + static u32 pmif_readl(struct pmif *arb, enum pmif_regs reg) { return readl(arb->base + arb->data->regs[reg]); @@ -345,6 +424,12 @@ static const struct pmif_data mt6873_pmif_arb = { .soc_chan = 2, }; +static const struct pmif_data mt8195_pmif_arb = { + .regs = mt8195_regs, + .spmimst_regs = mt8195_spmi_regs, + .soc_chan = 2, +}; + static int mtk_spmi_probe(struct platform_device *pdev) { struct pmif *arb; @@ -433,6 +518,9 @@ static const struct of_device_id mtk_spmi_match_table[] = { { .compatible = "mediatek,mt6873-spmi", .data = &mt6873_pmif_arb, + }, { + .compatible = "mediatek,mt8195-spmi", + .data = &mt8195_pmif_arb, }, { /* sentinel */ }, -- cgit v1.2.3 From 1b18af40c1db195619e611faaeae624d6319b1f1 Mon Sep 17 00:00:00 2001 From: David Collins Date: Thu, 16 Dec 2021 11:08:12 -0800 Subject: spmi: spmi-pmic-arb: fix irq_set_type race condition The qpnpint_irq_set_type() callback function configures the type (edge vs level) and polarity (high, low, or both) of a particular PMIC interrupt within a given peripheral. To do this, it reads the three consecutive IRQ configuration registers, modifies the specified IRQ bit within the register values, and finally writes the three modified register values back to the PMIC. While a spinlock is used to provide mutual exclusion on the SPMI bus during the register read and write calls, there is no locking around the overall read, modify, write sequence. This opens up the possibility of a race condition if two tasks set the type of a PMIC IRQ within the same peripheral simultaneously. When the race condition is encountered, both tasks will read the old value of the registers and IRQ bits set by one of the tasks will be dropped upon the register write of the other task. This then leads to PMIC IRQs being enabled with an incorrect type and polarity configured. Such misconfiguration can lead to an IRQ storm that overwhelms the system and causes it to crash. This race condition and IRQ storm have been observed when using a pair of pm8941-pwrkey devices to handle PMK8350 pwrkey and resin interrupts. The independent devices probe asynchronously in parallel and can simultaneously request and configure PMIC IRQs in the same PMIC peripheral. For a good case, the IRQ configuration calls end up serialized due to timing deltas and the register read/write sequence looks like this: 1. pwrkey probe: SPMI read(0x1311): 0x00, 0x00, 0x00 2. pwrkey probe: SPMI write(0x1311): 0x80, 0x80, 0x80 3. resin probe: SPMI read(0x1311): 0x80, 0x80, 0x80 4. resin probe: SPMI write(0x1311): 0xC0, 0xC0, 0xC0 The final register states after both devices have requested and enabled their respective IRQs is thus: 0x1311: 0xC0 0x1312: 0xC0 0x1313: 0xC0 0x1314: 0x00 0x1315: 0xC0 For a bad case, the IRQ configuration calls end up occurring simultaneously and the race condition is encountered. The register read/write sequence then looks like this: 1. pwrkey probe: SPMI read(0x1311): 0x00, 0x00, 0x00 2. resin probe: SPMI read(0x1311): 0x00, 0x00, 0x00 3. pwrkey probe: SPMI write(0x1311): 0x80, 0x80, 0x80 4. resin probe: SPMI write(0x1311): 0x40, 0x40, 0x40 In this case, the final register states after both devices have requested and enabled their respective IRQs is thus: 0x1311: 0x40 0x1312: 0x40 0x1313: 0x40 0x1314: 0x00 0x1315: 0xC0 This corresponds to the resin IRQ being configured for both rising and falling edges, as expected. However, the pwrkey IRQ is misconfigured as level type with both polarity high and low set to disabled. The PMIC IRQ triggering hardware treats this particular register configuration as if level low triggering is enabled. The raw pwrkey IRQ signal is low when the power key is not being pressed. Thus, the pwrkey IRQ begins firing continuously in an IRQ storm. Fix the race condition by holding the spmi-pmic-arb spinlock for the duration of the read, modify, write sequence performed in the qpnpint_irq_set_type() function. Split the pmic_arb_read_cmd() and pmic_arb_write_cmd() functions each into three parts so that hardware register IO is decoupled from spinlock locking. This allows a new function pmic_arb_masked_write() to be added which locks the spinlock and then calls register IO functions to perform SPMI read and write commands in a single atomic operation. Link: https://lore.kernel.org/r/20211118034719.28971-1-quic_collinsd@quicinc.com Signed-off-by: David Collins Signed-off-by: Stephen Boyd Link: https://lore.kernel.org/r/20211216190812.1574801-7-sboyd@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/spmi/spmi-pmic-arb.c | 176 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 140 insertions(+), 36 deletions(-) diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index e397c2532c8d..2113be40b5a9 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -334,24 +334,20 @@ static int pmic_arb_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid) return pmic_arb->ver_ops->non_data_cmd(ctrl, opc, sid); } -static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, - u16 addr, u8 *buf, size_t len) +static int pmic_arb_fmt_read_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, u8 sid, + u16 addr, size_t len, u32 *cmd, u32 *offset) { - struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); - unsigned long flags; u8 bc = len - 1; - u32 cmd; int rc; - u32 offset; rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, PMIC_ARB_CHANNEL_OBS); if (rc < 0) return rc; - offset = rc; + *offset = rc; if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { - dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", + dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", PMIC_ARB_MAX_TRANS_BYTES, len); return -EINVAL; } @@ -366,14 +362,24 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; - cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); + *cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); + + return 0; +} + +static int pmic_arb_read_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd, + u32 offset, u8 sid, u16 addr, u8 *buf, + size_t len) +{ + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); + u8 bc = len - 1; + int rc; - raw_spin_lock_irqsave(&pmic_arb->lock, flags); pmic_arb_set_rd_cmd(pmic_arb, offset + PMIC_ARB_CMD, cmd); rc = pmic_arb_wait_for_done(ctrl, pmic_arb->rd_base, sid, addr, PMIC_ARB_CHANNEL_OBS); if (rc) - goto done; + return rc; pmic_arb_read_data(pmic_arb, buf, offset + PMIC_ARB_RDATA0, min_t(u8, bc, 3)); @@ -381,30 +387,44 @@ static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, if (bc > 3) pmic_arb_read_data(pmic_arb, buf + 4, offset + PMIC_ARB_RDATA1, bc - 4); + return 0; +} -done: +static int pmic_arb_read_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 addr, u8 *buf, size_t len) +{ + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); + unsigned long flags; + u32 cmd, offset; + int rc; + + rc = pmic_arb_fmt_read_cmd(pmic_arb, opc, sid, addr, len, &cmd, + &offset); + if (rc) + return rc; + + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + rc = pmic_arb_read_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, len); raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); + return rc; } -static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, - u16 addr, const u8 *buf, size_t len) +static int pmic_arb_fmt_write_cmd(struct spmi_pmic_arb *pmic_arb, u8 opc, + u8 sid, u16 addr, size_t len, u32 *cmd, + u32 *offset) { - struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); - unsigned long flags; u8 bc = len - 1; - u32 cmd; int rc; - u32 offset; rc = pmic_arb->ver_ops->offset(pmic_arb, sid, addr, PMIC_ARB_CHANNEL_RW); if (rc < 0) return rc; - offset = rc; + *offset = rc; if (bc >= PMIC_ARB_MAX_TRANS_BYTES) { - dev_err(&ctrl->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", + dev_err(&pmic_arb->spmic->dev, "pmic-arb supports 1..%d bytes per trans, but:%zu requested", PMIC_ARB_MAX_TRANS_BYTES, len); return -EINVAL; } @@ -421,10 +441,19 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, else return -EINVAL; - cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); + *cmd = pmic_arb->ver_ops->fmt_cmd(opc, sid, addr, bc); + + return 0; +} + +static int pmic_arb_write_cmd_unlocked(struct spmi_controller *ctrl, u32 cmd, + u32 offset, u8 sid, u16 addr, + const u8 *buf, size_t len) +{ + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); + u8 bc = len - 1; /* Write data to FIFOs */ - raw_spin_lock_irqsave(&pmic_arb->lock, flags); pmic_arb_write_data(pmic_arb, buf, offset + PMIC_ARB_WDATA0, min_t(u8, bc, 3)); if (bc > 3) @@ -433,8 +462,62 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, /* Start the transaction */ pmic_arb_base_write(pmic_arb, offset + PMIC_ARB_CMD, cmd); - rc = pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr, - PMIC_ARB_CHANNEL_RW); + return pmic_arb_wait_for_done(ctrl, pmic_arb->wr_base, sid, addr, + PMIC_ARB_CHANNEL_RW); +} + +static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, + u16 addr, const u8 *buf, size_t len) +{ + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); + unsigned long flags; + u32 cmd, offset; + int rc; + + rc = pmic_arb_fmt_write_cmd(pmic_arb, opc, sid, addr, len, &cmd, + &offset); + if (rc) + return rc; + + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + rc = pmic_arb_write_cmd_unlocked(ctrl, cmd, offset, sid, addr, buf, + len); + raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); + + return rc; +} + +static int pmic_arb_masked_write(struct spmi_controller *ctrl, u8 sid, u16 addr, + const u8 *buf, const u8 *mask, size_t len) +{ + struct spmi_pmic_arb *pmic_arb = spmi_controller_get_drvdata(ctrl); + u32 read_cmd, read_offset, write_cmd, write_offset; + u8 temp[PMIC_ARB_MAX_TRANS_BYTES]; + unsigned long flags; + int rc, i; + + rc = pmic_arb_fmt_read_cmd(pmic_arb, SPMI_CMD_EXT_READL, sid, addr, len, + &read_cmd, &read_offset); + if (rc) + return rc; + + rc = pmic_arb_fmt_write_cmd(pmic_arb, SPMI_CMD_EXT_WRITEL, sid, addr, + len, &write_cmd, &write_offset); + if (rc) + return rc; + + raw_spin_lock_irqsave(&pmic_arb->lock, flags); + rc = pmic_arb_read_cmd_unlocked(ctrl, read_cmd, read_offset, sid, addr, + temp, len); + if (rc) + goto done; + + for (i = 0; i < len; i++) + temp[i] = (temp[i] & ~mask[i]) | (buf[i] & mask[i]); + + rc = pmic_arb_write_cmd_unlocked(ctrl, write_cmd, write_offset, sid, + addr, temp, len); +done: raw_spin_unlock_irqrestore(&pmic_arb->lock, flags); return rc; @@ -483,6 +566,23 @@ static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) d->irq); } +static int qpnpint_spmi_masked_write(struct irq_data *d, u8 reg, + const void *buf, const void *mask, + size_t len) +{ + struct spmi_pmic_arb *pmic_arb = irq_data_get_irq_chip_data(d); + u8 sid = hwirq_to_sid(d->hwirq); + u8 per = hwirq_to_per(d->hwirq); + int rc; + + rc = pmic_arb_masked_write(pmic_arb->spmic, sid, (per << 8) + reg, buf, + mask, len); + if (rc) + dev_err_ratelimited(&pmic_arb->spmic->dev, "failed irqchip transaction on %x rc=%d\n", + d->irq, rc); + return rc; +} + static void cleanup_irq(struct spmi_pmic_arb *pmic_arb, u16 apid, int id) { u16 ppid = pmic_arb->apid_data[apid].ppid; @@ -601,18 +701,18 @@ static void qpnpint_irq_unmask(struct irq_data *d) static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) { - struct spmi_pmic_arb_qpnpint_type type; + struct spmi_pmic_arb_qpnpint_type type = {0}; + struct spmi_pmic_arb_qpnpint_type mask; irq_flow_handler_t flow_handler; - u8 irq = hwirq_to_irq(d->hwirq); - - qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); + u8 irq_bit = BIT(hwirq_to_irq(d->hwirq)); + int rc; if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { - type.type |= BIT(irq); + type.type = irq_bit; if (flow_type & IRQF_TRIGGER_RISING) - type.polarity_high |= BIT(irq); + type.polarity_high = irq_bit; if (flow_type & IRQF_TRIGGER_FALLING) - type.polarity_low |= BIT(irq); + type.polarity_low = irq_bit; flow_handler = handle_edge_irq; } else { @@ -620,19 +720,23 @@ static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) (flow_type & (IRQF_TRIGGER_LOW))) return -EINVAL; - type.type &= ~BIT(irq); /* level trig */ if (flow_type & IRQF_TRIGGER_HIGH) - type.polarity_high |= BIT(irq); + type.polarity_high = irq_bit; else - type.polarity_low |= BIT(irq); + type.polarity_low = irq_bit; flow_handler = handle_level_irq; } - qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); + mask.type = irq_bit; + mask.polarity_high = irq_bit; + mask.polarity_low = irq_bit; + + rc = qpnpint_spmi_masked_write(d, QPNPINT_REG_SET_TYPE, &type, &mask, + sizeof(type)); irq_set_handler_locked(d, flow_handler); - return 0; + return rc; } static int qpnpint_irq_set_wake(struct irq_data *d, unsigned int on) -- cgit v1.2.3 From 9710b162c8b93cda554146520cddbc68c95dc6a6 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 15 Dec 2021 01:58:38 +0300 Subject: dt-bindings: phy: qcom,qmp: Add SM8450 PCIe PHY bindings There are two different PCIe PHYs on SM8450, one having one lane and another with two lanes. Add DT bindings for the first one. Support for second PCIe host and PHY will be submitted separately. Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211214225846.2043361-3-dmitry.baryshkov@linaro.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index d625a6fe0205..5887b046c640 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -50,6 +50,7 @@ properties: - qcom,sm8350-qmp-ufs-phy - qcom,sm8350-qmp-usb3-phy - qcom,sm8350-qmp-usb3-uni-phy + - qcom,sm8450-qmp-gen3x1-pcie-phy - qcom,sm8450-qmp-ufs-phy - qcom,sm8450-qmp-usb3-phy - qcom,sdx55-qmp-pcie-phy @@ -334,6 +335,7 @@ allOf: - qcom,sm8250-qmp-gen3x1-pcie-phy - qcom,sm8250-qmp-gen3x2-pcie-phy - qcom,sm8250-qmp-modem-pcie-phy + - qcom,sm8450-qmp-gen3x1-pcie-phy then: properties: clocks: -- cgit v1.2.3 From 107ba9bf49c211bebfab24b8e3525c320069f53a Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Wed, 15 Dec 2021 01:58:39 +0300 Subject: phy: qcom-qmp: Add SM8450 PCIe0 PHY support There are two different PCIe PHYs on SM8450, one having one lane (v5) and another with two lanes (v5.20). This commit adds support for the first PCIe phy only, support for the second PCIe PHY is coming in next commits. Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211214225846.2043361-4-dmitry.baryshkov@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 125 ++++++++++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 33 ++++++++++ 2 files changed, 158 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index 13a249ec8ab6..e73900ea2728 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -2866,6 +2866,97 @@ static const struct qmp_phy_init_tbl qcm2290_usb3_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), }; +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE0, 0x24), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE2_MODE1, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE1_MODE1, 0xb4), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_HSCLK_SEL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0x68), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x18), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xa2), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_BUF_ENABLE, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_TX_PI_QEC_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_1, 0x75), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_LANE_MODE_4, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_TX, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_TX_RES_CODE_LANE_OFFSET_RX, 0x04), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_LOW, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH, 0xff), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH3, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_00_HIGH4, 0xd8), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_LOW, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH, 0xdc), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH2, 0x5c), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH3, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_01_HIGH4, 0xa6), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH3, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_MODE_10_HIGH4, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_VGA_CAL_CNTRL2, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_GM_CAL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_PI_CONTROLS, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_TX_ADAPT_POST_THRESH, 0xf0), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_RX_EQU_ADAPTOR_CNTRL4, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V5_RX_UCDR_SO_GAIN, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RATE_SLEW_CNTRL1, 0x0b), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x05), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_EQ_CONFIG2, 0x0f), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), +}; + struct qmp_phy; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -4116,6 +4207,37 @@ static const struct qmp_phy_cfg sm8450_ufsphy_cfg = { .is_dual_lane_phy = true, }; +static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 1, + + .serdes_tbl = sm8450_qmp_gen3x1_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_serdes_tbl), + .tx_tbl = sm8450_qmp_gen3x1_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_tx_tbl), + .rx_tbl = sm8450_qmp_gen3x1_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_rx_tbl), + .pcs_tbl = sm8450_qmp_gen3x1_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_pcs_tbl), + .pcs_misc_tbl = sm8450_qmp_gen3x1_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8450_qmp_gen3x1_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, + + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -5774,6 +5896,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8350-qmp-usb3-uni-phy", .data = &sm8350_usb3_uniphy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-gen3x1-pcie-phy", + .data = &sm8450_qmp_gen3x1_pciephy_cfg, }, { .compatible = "qcom,sm8450-qmp-ufs-phy", .data = &sm8450_ufsphy_cfg, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index 3d123fbe42d2..eeeef8d40876 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -1070,6 +1070,15 @@ #define QPHY_V4_20_PCS_LANE1_INSIG_MX_CTRL2 0x828 /* Only for QMP V5 PHY - QSERDES COM registers */ +#define QSERDES_V5_COM_SSC_EN_CENTER 0x010 +#define QSERDES_V5_COM_SSC_PER1 0x01c +#define QSERDES_V5_COM_SSC_PER2 0x020 +#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0 0x024 +#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0 0x028 +#define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1 0x030 +#define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1 0x034 +#define QSERDES_V5_COM_CLK_ENABLE1 0x048 +#define QSERDES_V5_COM_SYSCLK_BUF_ENABLE 0x050 #define QSERDES_V5_COM_PLL_IVCO 0x058 #define QSERDES_V5_COM_CP_CTRL_MODE0 0x074 #define QSERDES_V5_COM_CP_CTRL_MODE1 0x078 @@ -1085,10 +1094,22 @@ #define QSERDES_V5_COM_DEC_START_MODE0 0x0bc #define QSERDES_V5_COM_LOCK_CMP2_MODE1 0x0b8 #define QSERDES_V5_COM_DEC_START_MODE1 0x0c4 +#define QSERDES_V5_COM_DIV_FRAC_START1_MODE0 0x0cc +#define QSERDES_V5_COM_DIV_FRAC_START2_MODE0 0x0d0 +#define QSERDES_V5_COM_DIV_FRAC_START3_MODE0 0x0d4 +#define QSERDES_V5_COM_DIV_FRAC_START1_MODE1 0x0d8 +#define QSERDES_V5_COM_DIV_FRAC_START2_MODE1 0x0dc +#define QSERDES_V5_COM_DIV_FRAC_START3_MODE1 0x0e0 #define QSERDES_V5_COM_VCO_TUNE_MAP 0x10c +#define QSERDES_V5_COM_VCO_TUNE1_MODE0 0x110 +#define QSERDES_V5_COM_VCO_TUNE2_MODE0 0x114 +#define QSERDES_V5_COM_VCO_TUNE1_MODE1 0x118 +#define QSERDES_V5_COM_VCO_TUNE2_MODE1 0x11c #define QSERDES_V5_COM_VCO_TUNE_INITVAL2 0x124 +#define QSERDES_V5_COM_CLK_SELECT 0x154 #define QSERDES_V5_COM_HSCLK_SEL 0x158 #define QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL 0x15c +#define QSERDES_V5_COM_CORECLK_DIV_MODE1 0x16c #define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac #define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 #define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 @@ -1131,6 +1152,7 @@ #define QSERDES_V5_RX_AC_JTAG_ENABLE 0x068 #define QSERDES_V5_RX_AC_JTAG_MODE 0x078 #define QSERDES_V5_RX_RX_TERM_BW 0x080 +#define QSERDES_V5_RX_TX_ADAPT_POST_THRESH 0x0cc #define QSERDES_V5_RX_VGA_CAL_CNTRL1 0x0d4 #define QSERDES_V5_RX_VGA_CAL_CNTRL2 0x0d8 #define QSERDES_V5_RX_GM_CAL 0x0dc @@ -1168,6 +1190,17 @@ #define QSERDES_V5_RX_DCC_CTRL1 0x1a8 #define QSERDES_V5_RX_VTH_CODE 0x1b0 +/* Only for QMP V5 PHY - USB/PCIe PCS registers */ +#define QPHY_V5_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V5_PCS_RX_SIGDET_LVL 0x188 +#define QPHY_V5_PCS_RATE_SLEW_CNTRL1 0x198 + +/* Only for QMP V5 PHY - PCS_PCIE registers */ +#define QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x20 +#define QPHY_V5_PCS_PCIE_INT_AUX_CLK_CONFIG1 0x54 +#define QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS 0x94 +#define QPHY_V5_PCS_PCIE_EQ_CONFIG2 0xa8 + /* Only for QMP V5 PHY - UFS PCS registers */ #define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c #define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010 -- cgit v1.2.3 From e80ca2e932056346f021d933b591d9d82b9cc93a Mon Sep 17 00:00:00 2001 From: Ajith P V Date: Wed, 15 Dec 2021 18:50:18 +0530 Subject: binder: use proper cacheflush header file binder.c uses instead of . Hence change cacheflush header file to proper one. This change also avoid warning from checkpatch that shown below: WARNING: Use #include instead of Signed-off-by: Ajith P V Link: https://lore.kernel.org/r/20211215132018.31522-1-ajithpv.linux@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/android/binder.c b/drivers/android/binder.c index e1fa6d17fe45..8351c5638880 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -69,7 +69,7 @@ #include -#include +#include #include "binder_internal.h" #include "binder_trace.h" -- cgit v1.2.3 From d185a3466f0cd5af8f1c5c782c53bc0e6f2e7136 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Mon, 18 Jun 2018 23:55:40 +0100 Subject: firmware: Update Kconfig help text for Google firmware The help text for GOOGLE_FIRMWARE states that it should only be enabled when building a kernel for Google's own servers. However, many of the drivers dependent on it are also useful on Chromebooks or on any platform using coreboot. Update the help text to reflect this double duty. Fixes: d384d6f43d1e ("firmware: google memconsole: Add coreboot support") Reviewed-by: Julius Werner Signed-off-by: Ben Hutchings Link: https://lore.kernel.org/r/20180618225540.GD14131@decadent.org.uk Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/google/Kconfig | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 97968aece54f..931544c9f63d 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -3,9 +3,9 @@ menuconfig GOOGLE_FIRMWARE bool "Google Firmware Drivers" default n help - These firmware drivers are used by Google's servers. They are - only useful if you are working directly on one of their - proprietary servers. If in doubt, say "N". + These firmware drivers are used by Google servers, + Chromebooks and other devices using coreboot firmware. + If in doubt, say "N". if GOOGLE_FIRMWARE -- cgit v1.2.3 From 909c648e03e8eda8eb72678b3e95042739d6eb71 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 12 Dec 2021 11:16:57 +0800 Subject: greybus: es2: fix typo in a comment The double `for' in the comment in line 81 is repeated. Remove one of them from the comment. Reviewed-by: Alex Elder Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20211212031657.41169-1-wangborong@cdjrlc.com Signed-off-by: Greg Kroah-Hartman --- drivers/greybus/es2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/greybus/es2.c b/drivers/greybus/es2.c index 15661c7f3633..e89cca015095 100644 --- a/drivers/greybus/es2.c +++ b/drivers/greybus/es2.c @@ -78,7 +78,7 @@ struct es2_cport_in { * @hd: pointer to our gb_host_device structure * * @cport_in: endpoint, urbs and buffer for cport in messages - * @cport_out_endpoint: endpoint for for cport out messages + * @cport_out_endpoint: endpoint for cport out messages * @cport_out_urb: array of urbs for the CPort out messages * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or * not. -- cgit v1.2.3 From 2d2802fb24de8cbacb4a2d6da2e002acc1c17143 Mon Sep 17 00:00:00 2001 From: Kai Ye Date: Mon, 6 Dec 2021 18:47:24 +0800 Subject: uacce: use sysfs_emit instead of sprintf Use the sysfs_emit to replace sprintf. sprintf may cause output defect in sysfs content, it is better to use new added sysfs_emit function which knows the size of the temporary buffer. Signed-off-by: Kai Ye Link: https://lore.kernel.org/r/20211206104724.11559-1-yekai13@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/uacce/uacce.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/uacce/uacce.c b/drivers/misc/uacce/uacce.c index 488eeb2811ae..281c54003edc 100644 --- a/drivers/misc/uacce/uacce.c +++ b/drivers/misc/uacce/uacce.c @@ -289,7 +289,7 @@ static ssize_t api_show(struct device *dev, { struct uacce_device *uacce = to_uacce_device(dev); - return sprintf(buf, "%s\n", uacce->api_ver); + return sysfs_emit(buf, "%s\n", uacce->api_ver); } static ssize_t flags_show(struct device *dev, @@ -297,7 +297,7 @@ static ssize_t flags_show(struct device *dev, { struct uacce_device *uacce = to_uacce_device(dev); - return sprintf(buf, "%u\n", uacce->flags); + return sysfs_emit(buf, "%u\n", uacce->flags); } static ssize_t available_instances_show(struct device *dev, @@ -309,7 +309,7 @@ static ssize_t available_instances_show(struct device *dev, if (!uacce->ops->get_available_instances) return -ENODEV; - return sprintf(buf, "%d\n", + return sysfs_emit(buf, "%d\n", uacce->ops->get_available_instances(uacce)); } @@ -318,7 +318,7 @@ static ssize_t algorithms_show(struct device *dev, { struct uacce_device *uacce = to_uacce_device(dev); - return sprintf(buf, "%s\n", uacce->algs); + return sysfs_emit(buf, "%s\n", uacce->algs); } static ssize_t region_mmio_size_show(struct device *dev, @@ -326,7 +326,7 @@ static ssize_t region_mmio_size_show(struct device *dev, { struct uacce_device *uacce = to_uacce_device(dev); - return sprintf(buf, "%lu\n", + return sysfs_emit(buf, "%lu\n", uacce->qf_pg_num[UACCE_QFRT_MMIO] << PAGE_SHIFT); } @@ -335,7 +335,7 @@ static ssize_t region_dus_size_show(struct device *dev, { struct uacce_device *uacce = to_uacce_device(dev); - return sprintf(buf, "%lu\n", + return sysfs_emit(buf, "%lu\n", uacce->qf_pg_num[UACCE_QFRT_DUS] << PAGE_SHIFT); } -- cgit v1.2.3 From cab00a3e5e5efabecdbfe9d54dc8d779b2e59be3 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Sun, 12 Dec 2021 15:18:38 +0800 Subject: applicom: unneed to initialise statics to 0 Static variables do not need to be initialised to 0, because compilers will initialise all uninitialised statics to 0. Thus, remove the unneeded initializations. Signed-off-by: Jason Wang Link: https://lore.kernel.org/r/20211212071838.304307-1-wangborong@cdjrlc.com Signed-off-by: Greg Kroah-Hartman --- drivers/char/applicom.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c index deb85a334c93..36203d3fa6ea 100644 --- a/drivers/char/applicom.c +++ b/drivers/char/applicom.c @@ -89,8 +89,8 @@ static struct applicom_board { spinlock_t mutex; } apbs[MAX_BOARD]; -static unsigned int irq = 0; /* interrupt number IRQ */ -static unsigned long mem = 0; /* physical segment of board */ +static unsigned int irq; /* interrupt number IRQ */ +static unsigned long mem; /* physical segment of board */ module_param_hw(irq, uint, irq, 0); MODULE_PARM_DESC(irq, "IRQ of the Applicom board"); -- cgit v1.2.3 From a57ac7acdcc1665662e369993898194def56e888 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Dec 2021 14:25:25 +0100 Subject: firmware: qemu_fw_cfg: fix NULL-pointer deref on duplicate entries Commit fe3c60684377 ("firmware: Fix a reference count leak.") "fixed" a kobject leak in the file registration helper by properly calling kobject_put() for the entry in case registration of the object fails (e.g. due to a name collision). This would however result in a NULL pointer dereference when the release function tries to remove the never added entry from the fw_cfg_entry_cache list. Fix this by moving the list-removal out of the release function. Note that the offending commit was one of the benign looking umn.edu fixes which was reviewed but not reverted. [1][2] [1] https://lore.kernel.org/r/202105051005.49BFABCE@keescook [2] https://lore.kernel.org/all/YIg7ZOZvS3a8LjSv@kroah.com Fixes: fe3c60684377 ("firmware: Fix a reference count leak.") Cc: stable@vger.kernel.org # 5.8 Cc: Qiushi Wu Cc: Kees Cook Cc: Greg Kroah-Hartman Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211201132528.30025-2-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/qemu_fw_cfg.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index 172c751a4f6c..a9c64ebfc49a 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -388,9 +388,7 @@ static void fw_cfg_sysfs_cache_cleanup(void) struct fw_cfg_sysfs_entry *entry, *next; list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) { - /* will end up invoking fw_cfg_sysfs_cache_delist() - * via each object's release() method (i.e. destructor) - */ + fw_cfg_sysfs_cache_delist(entry); kobject_put(&entry->kobj); } } @@ -448,7 +446,6 @@ static void fw_cfg_sysfs_release_entry(struct kobject *kobj) { struct fw_cfg_sysfs_entry *entry = to_entry(kobj); - fw_cfg_sysfs_cache_delist(entry); kfree(entry); } -- cgit v1.2.3 From 47a1db8e797da01a1309bf42e0c0d771d4e4d4f3 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Dec 2021 14:25:26 +0100 Subject: firmware: qemu_fw_cfg: fix kobject leak in probe error path An initialised kobject must be freed using kobject_put() to avoid leaking associated resources (e.g. the object name). Commit fe3c60684377 ("firmware: Fix a reference count leak.") "fixed" the leak in the first error path of the file registration helper but left the second one unchanged. This "fix" would however result in a NULL pointer dereference due to the release function also removing the never added entry from the fw_cfg_entry_cache list. This has now been addressed. Fix the remaining kobject leak by restoring the common error path and adding the missing kobject_put(). Fixes: 75f3e8e47f38 ("firmware: introduce sysfs driver for QEMU's fw_cfg device") Cc: stable@vger.kernel.org # 4.6 Cc: Gabriel Somlo Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211201132528.30025-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/qemu_fw_cfg.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index a9c64ebfc49a..ccb7ed62452f 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -603,15 +603,13 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype, fw_cfg_sel_ko, "%d", entry->select); - if (err) { - kobject_put(&entry->kobj); - return err; - } + if (err) + goto err_put_entry; /* add raw binary content access */ err = sysfs_create_bin_file(&entry->kobj, &fw_cfg_sysfs_attr_raw); if (err) - goto err_add_raw; + goto err_del_entry; /* try adding "/sys/firmware/qemu_fw_cfg/by_name/" symlink */ fw_cfg_build_symlink(fw_cfg_fname_kset, &entry->kobj, entry->name); @@ -620,9 +618,10 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) fw_cfg_sysfs_cache_enlist(entry); return 0; -err_add_raw: +err_del_entry: kobject_del(&entry->kobj); - kfree(entry); +err_put_entry: + kobject_put(&entry->kobj); return err; } -- cgit v1.2.3 From 433b7cd1e702b0918ef90cbf06c3da24313625d2 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Dec 2021 14:25:27 +0100 Subject: firmware: qemu_fw_cfg: fix sysfs information leak Make sure to always NUL-terminate file names retrieved from the firmware to avoid accessing data beyond the entry slab buffer and exposing it through sysfs in case the firmware data is corrupt. Fixes: 75f3e8e47f38 ("firmware: introduce sysfs driver for QEMU's fw_cfg device") Cc: stable@vger.kernel.org # 4.6 Cc: Gabriel Somlo Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211201132528.30025-4-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/qemu_fw_cfg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index ccb7ed62452f..f08e056ed0ae 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -598,7 +598,7 @@ static int fw_cfg_register_file(const struct fw_cfg_file *f) /* set file entry information */ entry->size = be32_to_cpu(f->size); entry->select = be16_to_cpu(f->select); - memcpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); + strscpy(entry->name, f->name, FW_CFG_MAX_FILE_PATH); /* register entry under "/sys/firmware/qemu_fw_cfg/by_key/" */ err = kobject_init_and_add(&entry->kobj, &fw_cfg_sysfs_entry_ktype, -- cgit v1.2.3 From bb84e64f8fb3b1341eb75075219a1f060afe9895 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Wed, 1 Dec 2021 14:25:28 +0100 Subject: firmware: qemu_fw_cfg: remove sysfs entries explicitly Explicitly remove the file entries from sysfs before dropping the final reference for symmetry reasons and for consistency with the rest of the driver. Signed-off-by: Johan Hovold Link: https://lore.kernel.org/r/20211201132528.30025-5-johan@kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/qemu_fw_cfg.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/qemu_fw_cfg.c b/drivers/firmware/qemu_fw_cfg.c index f08e056ed0ae..b436342115af 100644 --- a/drivers/firmware/qemu_fw_cfg.c +++ b/drivers/firmware/qemu_fw_cfg.c @@ -389,6 +389,7 @@ static void fw_cfg_sysfs_cache_cleanup(void) list_for_each_entry_safe(entry, next, &fw_cfg_entry_cache, list) { fw_cfg_sysfs_cache_delist(entry); + kobject_del(&entry->kobj); kobject_put(&entry->kobj); } } -- cgit v1.2.3 From 80a5ca99c5c04be6777df225ab932142a9d60c3f Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 16 Dec 2021 11:33:00 +0800 Subject: rapidio: remove not used macro definition in rio_ids.h The definition of RIO_VID_FREESCALE, RIO_DID_MPC8560, RIO_DID_TSI500, RIO_DID_TSI576 and RIO_DID_TSI721 are not used for many years in the current code, so just remove them. Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/1639625581-22867-2-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Greg Kroah-Hartman --- include/linux/rio_ids.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/linux/rio_ids.h b/include/linux/rio_ids.h index 4846f72759b2..e74d8840708a 100644 --- a/include/linux/rio_ids.h +++ b/include/linux/rio_ids.h @@ -9,15 +9,10 @@ #ifndef LINUX_RIO_IDS_H #define LINUX_RIO_IDS_H -#define RIO_VID_FREESCALE 0x0002 -#define RIO_DID_MPC8560 0x0003 - #define RIO_VID_TUNDRA 0x000d -#define RIO_DID_TSI500 0x0500 #define RIO_DID_TSI568 0x0568 #define RIO_DID_TSI572 0x0572 #define RIO_DID_TSI574 0x0574 -#define RIO_DID_TSI576 0x0578 /* Same ID as Tsi578 */ #define RIO_DID_TSI577 0x0577 #define RIO_DID_TSI578 0x0578 @@ -33,7 +28,6 @@ #define RIO_DID_IDTCPS1616 0x0379 #define RIO_DID_IDTVPS1616 0x0377 #define RIO_DID_IDTSPS1616 0x0378 -#define RIO_DID_TSI721 0x80ab #define RIO_DID_IDTRXS1632 0x80e5 #define RIO_DID_IDTRXS2448 0x80e6 -- cgit v1.2.3 From 612d4904191ff9aca01b1e087d8687b3a223cb33 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Thu, 16 Dec 2021 11:33:01 +0800 Subject: rapidio: remove not used code about RIO_VID_TUNDRA According to https://rapidio.org/vendor-id/, there is no 0x000d vendor id in the complete and current list of VendorIDs, it means that the related code is dead code now, so just remove them. Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/1639625581-22867-3-git-send-email-yangtiezhu@loongson.cn Signed-off-by: Greg Kroah-Hartman --- drivers/rapidio/switches/Kconfig | 11 -- drivers/rapidio/switches/Makefile | 2 - drivers/rapidio/switches/tsi568.c | 195 -------------------- drivers/rapidio/switches/tsi57x.c | 365 -------------------------------------- include/linux/rio_ids.h | 7 - 5 files changed, 580 deletions(-) delete mode 100644 drivers/rapidio/switches/tsi568.c delete mode 100644 drivers/rapidio/switches/tsi57x.c diff --git a/drivers/rapidio/switches/Kconfig b/drivers/rapidio/switches/Kconfig index 3e18f9c51e29..02771ba3e54f 100644 --- a/drivers/rapidio/switches/Kconfig +++ b/drivers/rapidio/switches/Kconfig @@ -2,22 +2,11 @@ # # RapidIO switches configuration # -config RAPIDIO_TSI57X - tristate "IDT Tsi57x SRIO switches support" - help - Includes support for IDT Tsi57x family of serial RapidIO switches. - config RAPIDIO_CPS_XX tristate "IDT CPS-xx SRIO switches support" help Includes support for IDT CPS-16/12/10/8 serial RapidIO switches. -config RAPIDIO_TSI568 - tristate "Tsi568 SRIO switch support" - default n - help - Includes support for IDT Tsi568 serial RapidIO switch. - config RAPIDIO_CPS_GEN2 tristate "IDT CPS Gen.2 SRIO switch support" default n diff --git a/drivers/rapidio/switches/Makefile b/drivers/rapidio/switches/Makefile index 69e7de31e41c..ef1749a79c2b 100644 --- a/drivers/rapidio/switches/Makefile +++ b/drivers/rapidio/switches/Makefile @@ -3,8 +3,6 @@ # Makefile for RIO switches # -obj-$(CONFIG_RAPIDIO_TSI57X) += tsi57x.o obj-$(CONFIG_RAPIDIO_CPS_XX) += idtcps.o -obj-$(CONFIG_RAPIDIO_TSI568) += tsi568.o obj-$(CONFIG_RAPIDIO_CPS_GEN2) += idt_gen2.o obj-$(CONFIG_RAPIDIO_RXS_GEN3) += idt_gen3.o diff --git a/drivers/rapidio/switches/tsi568.c b/drivers/rapidio/switches/tsi568.c deleted file mode 100644 index 103b48a24980..000000000000 --- a/drivers/rapidio/switches/tsi568.c +++ /dev/null @@ -1,195 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * RapidIO Tsi568 switch support - * - * Copyright 2009-2010 Integrated Device Technology, Inc. - * Alexandre Bounine - * - Added EM support - * - Modified switch operations initialization. - * - * Copyright 2005 MontaVista Software, Inc. - * Matt Porter - */ - -#include -#include -#include -#include -#include -#include "../rio.h" - -/* Global (broadcast) route registers */ -#define SPBC_ROUTE_CFG_DESTID 0x10070 -#define SPBC_ROUTE_CFG_PORT 0x10074 - -/* Per port route registers */ -#define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n) -#define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n) - -#define TSI568_SP_MODE(n) (0x11004 + 0x100*n) -#define TSI568_SP_MODE_PW_DIS 0x08000000 - -static int -tsi568_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 route_port) -{ - if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_DESTID, route_destid); - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_PORT, route_port); - } else { - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_DESTID(table), - route_destid); - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_PORT(table), route_port); - } - - udelay(10); - - return 0; -} - -static int -tsi568_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 *route_port) -{ - int ret = 0; - u32 result; - - if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_DESTID, route_destid); - rio_mport_read_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_PORT, &result); - } else { - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_DESTID(table), - route_destid); - rio_mport_read_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_PORT(table), &result); - } - - *route_port = result; - if (*route_port > 15) - ret = -1; - - return ret; -} - -static int -tsi568_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table) -{ - u32 route_idx; - u32 lut_size; - - lut_size = (mport->sys_size) ? 0x1ff : 0xff; - - if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_DESTID, 0x80000000); - for (route_idx = 0; route_idx <= lut_size; route_idx++) - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_PORT, - RIO_INVALID_ROUTE); - } else { - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_DESTID(table), - 0x80000000); - for (route_idx = 0; route_idx <= lut_size; route_idx++) - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_PORT(table), - RIO_INVALID_ROUTE); - } - - return 0; -} - -static int -tsi568_em_init(struct rio_dev *rdev) -{ - u32 regval; - int portnum; - - pr_debug("TSI568 %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount); - - /* Make sure that Port-Writes are disabled (for all ports) */ - for (portnum = 0; - portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) { - rio_read_config_32(rdev, TSI568_SP_MODE(portnum), ®val); - rio_write_config_32(rdev, TSI568_SP_MODE(portnum), - regval | TSI568_SP_MODE_PW_DIS); - } - - return 0; -} - -static struct rio_switch_ops tsi568_switch_ops = { - .owner = THIS_MODULE, - .add_entry = tsi568_route_add_entry, - .get_entry = tsi568_route_get_entry, - .clr_table = tsi568_route_clr_table, - .set_domain = NULL, - .get_domain = NULL, - .em_init = tsi568_em_init, - .em_handle = NULL, -}; - -static int tsi568_probe(struct rio_dev *rdev, const struct rio_device_id *id) -{ - pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - - spin_lock(&rdev->rswitch->lock); - - if (rdev->rswitch->ops) { - spin_unlock(&rdev->rswitch->lock); - return -EINVAL; - } - - rdev->rswitch->ops = &tsi568_switch_ops; - spin_unlock(&rdev->rswitch->lock); - return 0; -} - -static void tsi568_remove(struct rio_dev *rdev) -{ - pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - spin_lock(&rdev->rswitch->lock); - if (rdev->rswitch->ops != &tsi568_switch_ops) { - spin_unlock(&rdev->rswitch->lock); - return; - } - rdev->rswitch->ops = NULL; - spin_unlock(&rdev->rswitch->lock); -} - -static const struct rio_device_id tsi568_id_table[] = { - {RIO_DEVICE(RIO_DID_TSI568, RIO_VID_TUNDRA)}, - { 0, } /* terminate list */ -}; - -static struct rio_driver tsi568_driver = { - .name = "tsi568", - .id_table = tsi568_id_table, - .probe = tsi568_probe, - .remove = tsi568_remove, -}; - -static int __init tsi568_init(void) -{ - return rio_register_driver(&tsi568_driver); -} - -static void __exit tsi568_exit(void) -{ - rio_unregister_driver(&tsi568_driver); -} - -device_initcall(tsi568_init); -module_exit(tsi568_exit); - -MODULE_DESCRIPTION("IDT Tsi568 Serial RapidIO switch driver"); -MODULE_AUTHOR("Integrated Device Technology, Inc."); -MODULE_LICENSE("GPL"); diff --git a/drivers/rapidio/switches/tsi57x.c b/drivers/rapidio/switches/tsi57x.c deleted file mode 100644 index 271762046f8c..000000000000 --- a/drivers/rapidio/switches/tsi57x.c +++ /dev/null @@ -1,365 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * RapidIO Tsi57x switch family support - * - * Copyright 2009-2010 Integrated Device Technology, Inc. - * Alexandre Bounine - * - Added EM support - * - Modified switch operations initialization. - * - * Copyright 2005 MontaVista Software, Inc. - * Matt Porter - */ - -#include -#include -#include -#include -#include -#include "../rio.h" - -/* Global (broadcast) route registers */ -#define SPBC_ROUTE_CFG_DESTID 0x10070 -#define SPBC_ROUTE_CFG_PORT 0x10074 - -/* Per port route registers */ -#define SPP_ROUTE_CFG_DESTID(n) (0x11070 + 0x100*n) -#define SPP_ROUTE_CFG_PORT(n) (0x11074 + 0x100*n) - -#define TSI578_SP_MODE(n) (0x11004 + n*0x100) -#define TSI578_SP_MODE_GLBL 0x10004 -#define TSI578_SP_MODE_PW_DIS 0x08000000 -#define TSI578_SP_MODE_LUT_512 0x01000000 - -#define TSI578_SP_CTL_INDEP(n) (0x13004 + n*0x100) -#define TSI578_SP_LUT_PEINF(n) (0x13010 + n*0x100) -#define TSI578_SP_CS_TX(n) (0x13014 + n*0x100) -#define TSI578_SP_INT_STATUS(n) (0x13018 + n*0x100) - -#define TSI578_GLBL_ROUTE_BASE 0x10078 - -static int -tsi57x_route_add_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 route_port) -{ - if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_DESTID, route_destid); - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_PORT, route_port); - } else { - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_DESTID(table), route_destid); - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_PORT(table), route_port); - } - - udelay(10); - - return 0; -} - -static int -tsi57x_route_get_entry(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table, u16 route_destid, u8 *route_port) -{ - int ret = 0; - u32 result; - - if (table == RIO_GLOBAL_TABLE) { - /* Use local RT of the ingress port to avoid possible - race condition */ - rio_mport_read_config_32(mport, destid, hopcount, - RIO_SWP_INFO_CAR, &result); - table = (result & RIO_SWP_INFO_PORT_NUM_MASK); - } - - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_DESTID(table), route_destid); - rio_mport_read_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_PORT(table), &result); - - *route_port = (u8)result; - if (*route_port > 15) - ret = -1; - - return ret; -} - -static int -tsi57x_route_clr_table(struct rio_mport *mport, u16 destid, u8 hopcount, - u16 table) -{ - u32 route_idx; - u32 lut_size; - - lut_size = (mport->sys_size) ? 0x1ff : 0xff; - - if (table == RIO_GLOBAL_TABLE) { - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_DESTID, 0x80000000); - for (route_idx = 0; route_idx <= lut_size; route_idx++) - rio_mport_write_config_32(mport, destid, hopcount, - SPBC_ROUTE_CFG_PORT, - RIO_INVALID_ROUTE); - } else { - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_DESTID(table), 0x80000000); - for (route_idx = 0; route_idx <= lut_size; route_idx++) - rio_mport_write_config_32(mport, destid, hopcount, - SPP_ROUTE_CFG_PORT(table) , RIO_INVALID_ROUTE); - } - - return 0; -} - -static int -tsi57x_set_domain(struct rio_mport *mport, u16 destid, u8 hopcount, - u8 sw_domain) -{ - u32 regval; - - /* - * Switch domain configuration operates only at global level - */ - - /* Turn off flat (LUT_512) mode */ - rio_mport_read_config_32(mport, destid, hopcount, - TSI578_SP_MODE_GLBL, ®val); - rio_mport_write_config_32(mport, destid, hopcount, TSI578_SP_MODE_GLBL, - regval & ~TSI578_SP_MODE_LUT_512); - /* Set switch domain base */ - rio_mport_write_config_32(mport, destid, hopcount, - TSI578_GLBL_ROUTE_BASE, - (u32)(sw_domain << 24)); - return 0; -} - -static int -tsi57x_get_domain(struct rio_mport *mport, u16 destid, u8 hopcount, - u8 *sw_domain) -{ - u32 regval; - - /* - * Switch domain configuration operates only at global level - */ - rio_mport_read_config_32(mport, destid, hopcount, - TSI578_GLBL_ROUTE_BASE, ®val); - - *sw_domain = (u8)(regval >> 24); - - return 0; -} - -static int -tsi57x_em_init(struct rio_dev *rdev) -{ - u32 regval; - int portnum; - - pr_debug("TSI578 %s [%d:%d]\n", __func__, rdev->destid, rdev->hopcount); - - for (portnum = 0; - portnum < RIO_GET_TOTAL_PORTS(rdev->swpinfo); portnum++) { - /* Make sure that Port-Writes are enabled (for all ports) */ - rio_read_config_32(rdev, - TSI578_SP_MODE(portnum), ®val); - rio_write_config_32(rdev, - TSI578_SP_MODE(portnum), - regval & ~TSI578_SP_MODE_PW_DIS); - - /* Clear all pending interrupts */ - rio_read_config_32(rdev, - RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum), - ®val); - rio_write_config_32(rdev, - RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum), - regval & 0x07120214); - - rio_read_config_32(rdev, - TSI578_SP_INT_STATUS(portnum), ®val); - rio_write_config_32(rdev, - TSI578_SP_INT_STATUS(portnum), - regval & 0x000700bd); - - /* Enable all interrupts to allow ports to send a port-write */ - rio_read_config_32(rdev, - TSI578_SP_CTL_INDEP(portnum), ®val); - rio_write_config_32(rdev, - TSI578_SP_CTL_INDEP(portnum), - regval | 0x000b0000); - - /* Skip next (odd) port if the current port is in x4 mode */ - rio_read_config_32(rdev, - RIO_DEV_PORT_N_CTL_CSR(rdev, portnum), - ®val); - if ((regval & RIO_PORT_N_CTL_PWIDTH) == RIO_PORT_N_CTL_PWIDTH_4) - portnum++; - } - - /* set TVAL = ~50us */ - rio_write_config_32(rdev, - rdev->phys_efptr + RIO_PORT_LINKTO_CTL_CSR, 0x9a << 8); - - return 0; -} - -static int -tsi57x_em_handler(struct rio_dev *rdev, u8 portnum) -{ - struct rio_mport *mport = rdev->net->hport; - u32 intstat, err_status; - int sendcount, checkcount; - u8 route_port; - u32 regval; - - rio_read_config_32(rdev, - RIO_DEV_PORT_N_ERR_STS_CSR(rdev, portnum), - &err_status); - - if ((err_status & RIO_PORT_N_ERR_STS_PORT_OK) && - (err_status & (RIO_PORT_N_ERR_STS_OUT_ES | - RIO_PORT_N_ERR_STS_INP_ES))) { - /* Remove any queued packets by locking/unlocking port */ - rio_read_config_32(rdev, - RIO_DEV_PORT_N_CTL_CSR(rdev, portnum), - ®val); - if (!(regval & RIO_PORT_N_CTL_LOCKOUT)) { - rio_write_config_32(rdev, - RIO_DEV_PORT_N_CTL_CSR(rdev, portnum), - regval | RIO_PORT_N_CTL_LOCKOUT); - udelay(50); - rio_write_config_32(rdev, - RIO_DEV_PORT_N_CTL_CSR(rdev, portnum), - regval); - } - - /* Read from link maintenance response register to clear - * valid bit - */ - rio_read_config_32(rdev, - RIO_DEV_PORT_N_MNT_RSP_CSR(rdev, portnum), - ®val); - - /* Send a Packet-Not-Accepted/Link-Request-Input-Status control - * symbol to recover from IES/OES - */ - sendcount = 3; - while (sendcount) { - rio_write_config_32(rdev, - TSI578_SP_CS_TX(portnum), 0x40fc8000); - checkcount = 3; - while (checkcount--) { - udelay(50); - rio_read_config_32(rdev, - RIO_DEV_PORT_N_MNT_RSP_CSR(rdev, - portnum), - ®val); - if (regval & RIO_PORT_N_MNT_RSP_RVAL) - goto exit_es; - } - - sendcount--; - } - } - -exit_es: - /* Clear implementation specific error status bits */ - rio_read_config_32(rdev, TSI578_SP_INT_STATUS(portnum), &intstat); - pr_debug("TSI578[%x:%x] SP%d_INT_STATUS=0x%08x\n", - rdev->destid, rdev->hopcount, portnum, intstat); - - if (intstat & 0x10000) { - rio_read_config_32(rdev, - TSI578_SP_LUT_PEINF(portnum), ®val); - regval = (mport->sys_size) ? (regval >> 16) : (regval >> 24); - route_port = rdev->rswitch->route_table[regval]; - pr_debug("RIO: TSI578[%s] P%d LUT Parity Error (destID=%d)\n", - rio_name(rdev), portnum, regval); - tsi57x_route_add_entry(mport, rdev->destid, rdev->hopcount, - RIO_GLOBAL_TABLE, regval, route_port); - } - - rio_write_config_32(rdev, TSI578_SP_INT_STATUS(portnum), - intstat & 0x000700bd); - - return 0; -} - -static struct rio_switch_ops tsi57x_switch_ops = { - .owner = THIS_MODULE, - .add_entry = tsi57x_route_add_entry, - .get_entry = tsi57x_route_get_entry, - .clr_table = tsi57x_route_clr_table, - .set_domain = tsi57x_set_domain, - .get_domain = tsi57x_get_domain, - .em_init = tsi57x_em_init, - .em_handle = tsi57x_em_handler, -}; - -static int tsi57x_probe(struct rio_dev *rdev, const struct rio_device_id *id) -{ - pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - - spin_lock(&rdev->rswitch->lock); - - if (rdev->rswitch->ops) { - spin_unlock(&rdev->rswitch->lock); - return -EINVAL; - } - rdev->rswitch->ops = &tsi57x_switch_ops; - - if (rdev->do_enum) { - /* Ensure that default routing is disabled on startup */ - rio_write_config_32(rdev, RIO_STD_RTE_DEFAULT_PORT, - RIO_INVALID_ROUTE); - } - - spin_unlock(&rdev->rswitch->lock); - return 0; -} - -static void tsi57x_remove(struct rio_dev *rdev) -{ - pr_debug("RIO: %s for %s\n", __func__, rio_name(rdev)); - spin_lock(&rdev->rswitch->lock); - if (rdev->rswitch->ops != &tsi57x_switch_ops) { - spin_unlock(&rdev->rswitch->lock); - return; - } - rdev->rswitch->ops = NULL; - spin_unlock(&rdev->rswitch->lock); -} - -static const struct rio_device_id tsi57x_id_table[] = { - {RIO_DEVICE(RIO_DID_TSI572, RIO_VID_TUNDRA)}, - {RIO_DEVICE(RIO_DID_TSI574, RIO_VID_TUNDRA)}, - {RIO_DEVICE(RIO_DID_TSI577, RIO_VID_TUNDRA)}, - {RIO_DEVICE(RIO_DID_TSI578, RIO_VID_TUNDRA)}, - { 0, } /* terminate list */ -}; - -static struct rio_driver tsi57x_driver = { - .name = "tsi57x", - .id_table = tsi57x_id_table, - .probe = tsi57x_probe, - .remove = tsi57x_remove, -}; - -static int __init tsi57x_init(void) -{ - return rio_register_driver(&tsi57x_driver); -} - -static void __exit tsi57x_exit(void) -{ - rio_unregister_driver(&tsi57x_driver); -} - -device_initcall(tsi57x_init); -module_exit(tsi57x_exit); - -MODULE_DESCRIPTION("IDT Tsi57x Serial RapidIO switch family driver"); -MODULE_AUTHOR("Integrated Device Technology, Inc."); -MODULE_LICENSE("GPL"); diff --git a/include/linux/rio_ids.h b/include/linux/rio_ids.h index e74d8840708a..c7e2f21dd5c1 100644 --- a/include/linux/rio_ids.h +++ b/include/linux/rio_ids.h @@ -9,13 +9,6 @@ #ifndef LINUX_RIO_IDS_H #define LINUX_RIO_IDS_H -#define RIO_VID_TUNDRA 0x000d -#define RIO_DID_TSI568 0x0568 -#define RIO_DID_TSI572 0x0572 -#define RIO_DID_TSI574 0x0574 -#define RIO_DID_TSI577 0x0577 -#define RIO_DID_TSI578 0x0578 - #define RIO_VID_IDT 0x0038 #define RIO_DID_IDT70K200 0x0310 #define RIO_DID_IDTCPS8 0x035c -- cgit v1.2.3 From 6d1e4927dedf5bf8998759961e3b28b967321cdd Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 30 Nov 2021 09:46:26 +0100 Subject: paride: fix up build warning on mips platforms MIPS include files define "PC" so when building the paride driver the following build warning shows up: rivers/block/paride/bpck.c:32: warning: "PC" redefined Fix this by undefining PC before redefining it as is done for other defines in this driver. Cc: Tim Waugh Acked-by: Jens Axboe Link: https://lore.kernel.org/r/20211130084626.3215987-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/block/paride/bpck.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/block/paride/bpck.c b/drivers/block/paride/bpck.c index f5f63ca2889d..d880a9465e9b 100644 --- a/drivers/block/paride/bpck.c +++ b/drivers/block/paride/bpck.c @@ -28,6 +28,7 @@ #undef r2 #undef w2 +#undef PC #define PC pi->private #define r2() (PC=(in_p(2) & 0xff)) -- cgit v1.2.3 From 6da3f33770e08348691d90455ef6149e15551854 Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Wed, 15 Dec 2021 12:18:42 +0100 Subject: misc: vmw_vmci: Switch to kvfree_rcu() API Instead of invoking a synchronize_rcu() to free a pointer after a grace period we can directly make use of new API that does the same but in more efficient way. Signed-off-by: Uladzislau Rezki (Sony) Link: https://lore.kernel.org/r/20211215111845.2514-6-urezki@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/vmw_vmci/vmci_context.c | 6 ++---- drivers/misc/vmw_vmci/vmci_event.c | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/misc/vmw_vmci/vmci_context.c b/drivers/misc/vmw_vmci/vmci_context.c index c0b5e339d5a1..6cf3e21c7604 100644 --- a/drivers/misc/vmw_vmci/vmci_context.c +++ b/drivers/misc/vmw_vmci/vmci_context.c @@ -687,10 +687,8 @@ int vmci_ctx_remove_notification(u32 context_id, u32 remote_cid) } spin_unlock(&context->lock); - if (found) { - synchronize_rcu(); - kfree(notifier); - } + if (found) + kvfree_rcu(notifier); vmci_ctx_put(context); diff --git a/drivers/misc/vmw_vmci/vmci_event.c b/drivers/misc/vmw_vmci/vmci_event.c index e3436abf39f4..2100297c94ad 100644 --- a/drivers/misc/vmw_vmci/vmci_event.c +++ b/drivers/misc/vmw_vmci/vmci_event.c @@ -209,8 +209,7 @@ int vmci_event_unsubscribe(u32 sub_id) if (!s) return VMCI_ERROR_NOT_FOUND; - synchronize_rcu(); - kfree(s); + kvfree_rcu(s); return VMCI_SUCCESS; } -- cgit v1.2.3 From 81e7b7f5dfbdadab1ac9e0c60b0e30633bab1183 Mon Sep 17 00:00:00 2001 From: Minghao Chi Date: Wed, 15 Dec 2021 06:04:38 +0000 Subject: drivers/misc/ocxl: remove redundant rc variable Return value from ocxl_context_attach() directly instead of taking this in another redundant variable. Reported-by: Zeal Robot Acked-by: Andrew Donnellan Signed-off-by: Minghao Chi Link: https://lore.kernel.org/r/20211215060438.441918-1-chi.minghao@zte.com.cn Signed-off-by: Greg Kroah-Hartman --- drivers/misc/ocxl/file.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/ocxl/file.c b/drivers/misc/ocxl/file.c index e70525eedaae..d881f5e40ad9 100644 --- a/drivers/misc/ocxl/file.c +++ b/drivers/misc/ocxl/file.c @@ -74,7 +74,6 @@ static long afu_ioctl_attach(struct ocxl_context *ctx, { struct ocxl_ioctl_attach arg; u64 amr = 0; - int rc; pr_debug("%s for context %d\n", __func__, ctx->pasid); @@ -86,8 +85,7 @@ static long afu_ioctl_attach(struct ocxl_context *ctx, return -EINVAL; amr = arg.amr & mfspr(SPRN_UAMOR); - rc = ocxl_context_attach(ctx, amr, current->mm); - return rc; + return ocxl_context_attach(ctx, amr, current->mm); } static long afu_ioctl_get_metadata(struct ocxl_context *ctx, -- cgit v1.2.3 From ae807879e6be321308ee4196275bfb8408858ba7 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 9 Dec 2021 17:42:32 +0000 Subject: dt-bindings: nvmem: mediatek: add support bits property Add support bits property, will satisfy more consumers. Acked-by: Rob Herring Signed-off-by: Chunfeng Yun Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211209174235.14049-2-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/mtk-efuse.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt b/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt index b6791702bcfc..ec70c5b7a340 100644 --- a/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt +++ b/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt @@ -10,6 +10,7 @@ Required properties: "mediatek,mt8192-efuse", "mediatek,efuse": for MT8192 "mediatek,mt8516-efuse", "mediatek,efuse": for MT8516 - reg: Should contain registers location and length +- bits: contain the bits range by offset and size = Data cells = Are child nodes of MTK-EFUSE, bindings of which as described in -- cgit v1.2.3 From 9d87b0ac80e3949b372108eb6b7ae172dc71ec5c Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 9 Dec 2021 17:42:33 +0000 Subject: dt-bindings: nvmem: mediatek: add support for mt8195 Add compatible for mt8195 Acked-by: Rob Herring Signed-off-by: Chunfeng Yun Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211209174235.14049-3-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/mtk-efuse.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt b/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt index ec70c5b7a340..39d529599444 100644 --- a/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt +++ b/Documentation/devicetree/bindings/nvmem/mtk-efuse.txt @@ -8,6 +8,7 @@ Required properties: "mediatek,mt7623-efuse", "mediatek,efuse": for MT7623 "mediatek,mt8173-efuse" or "mediatek,efuse": for MT8173 "mediatek,mt8192-efuse", "mediatek,efuse": for MT8192 + "mediatek,mt8195-efuse", "mediatek,efuse": for MT8195 "mediatek,mt8516-efuse", "mediatek,efuse": for MT8516 - reg: Should contain registers location and length - bits: contain the bits range by offset and size -- cgit v1.2.3 From 98e2c4efae214fb7086cac9117616eb6ea11475d Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Thu, 9 Dec 2021 17:42:34 +0000 Subject: nvmem: mtk-efuse: support minimum one byte access stride and granularity In order to support nvmem bits property, should support minimum 1 byte read stride and minimum 1 byte read granularity at the same time. Signed-off-by: Chunfeng Yun Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211209174235.14049-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- drivers/nvmem/mtk-efuse.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/nvmem/mtk-efuse.c b/drivers/nvmem/mtk-efuse.c index 6a537d959f14..e9a375dd84af 100644 --- a/drivers/nvmem/mtk-efuse.c +++ b/drivers/nvmem/mtk-efuse.c @@ -19,11 +19,12 @@ static int mtk_reg_read(void *context, unsigned int reg, void *_val, size_t bytes) { struct mtk_efuse_priv *priv = context; - u32 *val = _val; - int i = 0, words = bytes / 4; + void __iomem *addr = priv->base + reg; + u8 *val = _val; + int i; - while (words--) - *val++ = readl(priv->base + reg + (i++ * 4)); + for (i = 0; i < bytes; i++, val++) + *val = readb(addr + i); return 0; } @@ -45,8 +46,8 @@ static int mtk_efuse_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - econfig.stride = 4; - econfig.word_size = 4; + econfig.stride = 1; + econfig.word_size = 1; econfig.reg_read = mtk_reg_read; econfig.size = resource_size(res); econfig.priv = priv; -- cgit v1.2.3 From 15c00b681760b4e0c0127439ab18cdce73ae1f0b Mon Sep 17 00:00:00 2001 From: Rob Herring Date: Thu, 9 Dec 2021 17:42:35 +0000 Subject: dt-bindings: nvmem: Add missing 'reg' property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With 'unevaluatedProperties' support implemented, the following warnings are generated in the nvmem examples: Documentation/devicetree/bindings/nvmem/st,stm32-romem.example.dt.yaml: efuse@1fff7800: Unevaluated properties are not allowed ('reg' was unexpected) Documentation/devicetree/bindings/nvmem/rmem.example.dt.yaml: nvram@10000000: Unevaluated properties are not allowed ('reg' was unexpected) Documentation/devicetree/bindings/nvmem/brcm,nvram.example.dt.yaml: nvram@1eff0000: Unevaluated properties are not allowed ('reg' was unexpected) Add the missing 'reg' property definition. Cc: Srinivas Kandagatla Cc: Maxime Coquelin Cc: Alexandre Torgue Cc: Rafał Miłecki Cc: Saenz Julienne Cc: Fabrice Gasnier Cc: linux-stm32@st-md-mailman.stormreply.com Cc: linux-arm-kernel@lists.infradead.org Reviewed-by: Fabrice Gasnier Reviewed-by: Thierry Reding Signed-off-by: Rob Herring Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20211209174235.14049-5-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml | 3 +++ Documentation/devicetree/bindings/nvmem/rmem.yaml | 3 +++ Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml | 3 +++ 3 files changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml b/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml index 58ff6b0bdb1a..8c3f0cd22821 100644 --- a/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml +++ b/Documentation/devicetree/bindings/nvmem/brcm,nvram.yaml @@ -24,6 +24,9 @@ properties: compatible: const: brcm,nvram + reg: + maxItems: 1 + unevaluatedProperties: false examples: diff --git a/Documentation/devicetree/bindings/nvmem/rmem.yaml b/Documentation/devicetree/bindings/nvmem/rmem.yaml index 1d85a0a30846..a4a755dcfc43 100644 --- a/Documentation/devicetree/bindings/nvmem/rmem.yaml +++ b/Documentation/devicetree/bindings/nvmem/rmem.yaml @@ -19,6 +19,9 @@ properties: - raspberrypi,bootloader-config - const: nvmem-rmem + reg: + maxItems: 1 + no-map: $ref: /schemas/types.yaml#/definitions/flag description: diff --git a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml index a48c8fa56bce..448a2678dc62 100644 --- a/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml +++ b/Documentation/devicetree/bindings/nvmem/st,stm32-romem.yaml @@ -24,6 +24,9 @@ properties: - st,stm32f4-otp - st,stm32mp15-bsec + reg: + maxItems: 1 + patternProperties: "^.*@[0-9a-f]+$": type: object -- cgit v1.2.3 From 0032ca576a79946492194ae4860b462d32815c66 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Tue, 21 Dec 2021 17:16:46 +0900 Subject: counter: Add the necessary colons and indents to the comments of counter_compi Since commit aaec1a0f76ec ("counter: Internalize sysfs interface code") introduce a warning as: linux-next/Documentation/driver-api/generic-counter:234: ./include/linux/counter.h:43: WARNING: Unexpected indentation. linux-next/Documentation/driver-api/generic-counter:234: ./include/linux/counter.h:45: WARNING: Block quote ends without a blank line; unexpected unindent. Add the necessary colons and indents. Fixes: aaec1a0f76ec ("counter: Internalize sysfs interface code") Signed-off-by: Yanteng Si Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/26011e814d6eca02c7ebdbb92f171a49928a7e89.1640072891.git.vilhelm.gray@gmail.com Signed-off-by: Greg Kroah-Hartman --- include/linux/counter.h | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/include/linux/counter.h b/include/linux/counter.h index b7d0a00a61cf..dfbde2808998 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -38,64 +38,64 @@ enum counter_comp_type { * @type: Counter component data type * @name: device-specific component name * @priv: component-relevant data - * @action_read Synapse action mode read callback. The read value of the + * @action_read: Synapse action mode read callback. The read value of the * respective Synapse action mode should be passed back via * the action parameter. - * @device_u8_read Device u8 component read callback. The read value of the + * @device_u8_read: Device u8 component read callback. The read value of the * respective Device u8 component should be passed back via * the val parameter. - * @count_u8_read Count u8 component read callback. The read value of the + * @count_u8_read: Count u8 component read callback. The read value of the * respective Count u8 component should be passed back via * the val parameter. - * @signal_u8_read Signal u8 component read callback. The read value of the + * @signal_u8_read: Signal u8 component read callback. The read value of the * respective Signal u8 component should be passed back via * the val parameter. - * @device_u32_read Device u32 component read callback. The read value of + * @device_u32_read: Device u32 component read callback. The read value of * the respective Device u32 component should be passed * back via the val parameter. - * @count_u32_read Count u32 component read callback. The read value of the + * @count_u32_read: Count u32 component read callback. The read value of the * respective Count u32 component should be passed back via * the val parameter. - * @signal_u32_read Signal u32 component read callback. The read value of + * @signal_u32_read: Signal u32 component read callback. The read value of * the respective Signal u32 component should be passed * back via the val parameter. - * @device_u64_read Device u64 component read callback. The read value of + * @device_u64_read: Device u64 component read callback. The read value of * the respective Device u64 component should be passed * back via the val parameter. - * @count_u64_read Count u64 component read callback. The read value of the + * @count_u64_read: Count u64 component read callback. The read value of the * respective Count u64 component should be passed back via * the val parameter. - * @signal_u64_read Signal u64 component read callback. The read value of + * @signal_u64_read: Signal u64 component read callback. The read value of * the respective Signal u64 component should be passed * back via the val parameter. - * @action_write Synapse action mode write callback. The write value of + * @action_write: Synapse action mode write callback. The write value of * the respective Synapse action mode is passed via the * action parameter. - * @device_u8_write Device u8 component write callback. The write value of + * @device_u8_write: Device u8 component write callback. The write value of * the respective Device u8 component is passed via the val * parameter. - * @count_u8_write Count u8 component write callback. The write value of + * @count_u8_write: Count u8 component write callback. The write value of * the respective Count u8 component is passed via the val * parameter. - * @signal_u8_write Signal u8 component write callback. The write value of + * @signal_u8_write: Signal u8 component write callback. The write value of * the respective Signal u8 component is passed via the val * parameter. - * @device_u32_write Device u32 component write callback. The write value of + * @device_u32_write: Device u32 component write callback. The write value of * the respective Device u32 component is passed via the * val parameter. - * @count_u32_write Count u32 component write callback. The write value of + * @count_u32_write: Count u32 component write callback. The write value of * the respective Count u32 component is passed via the val * parameter. - * @signal_u32_write Signal u32 component write callback. The write value of + * @signal_u32_write: Signal u32 component write callback. The write value of * the respective Signal u32 component is passed via the * val parameter. - * @device_u64_write Device u64 component write callback. The write value of + * @device_u64_write: Device u64 component write callback. The write value of * the respective Device u64 component is passed via the * val parameter. - * @count_u64_write Count u64 component write callback. The write value of + * @count_u64_write: Count u64 component write callback. The write value of * the respective Count u64 component is passed via the val * parameter. - * @signal_u64_write Signal u64 component write callback. The write value of + * @signal_u64_write: Signal u64 component write callback. The write value of * the respective Signal u64 component is passed via the * val parameter. */ -- cgit v1.2.3 From 60f07e74f86b47973bc1dc82e2128973932be55f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Tue, 21 Dec 2021 17:16:47 +0900 Subject: counter: ti-eqep: Use container_of instead of struct counter_device::priv MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using counter->priv is a memory read and so more expensive than container_of which is only an addition. (In this case even a noop because the offset is 0.) So container_of is expected to be a tad faster, it's type-safe, and produces smaller code (ARCH=arm allmodconfig): $ source/scripts/bloat-o-meter drivers/counter/ti-eqep.o-pre drivers/counter/ti-eqep.o add/remove: 0/0 grow/shrink: 0/9 up/down: 0/-108 (-108) Function old new delta ti_eqep_position_enable_write 132 120 -12 ti_eqep_position_enable_read 260 248 -12 ti_eqep_position_ceiling_write 132 120 -12 ti_eqep_position_ceiling_read 236 224 -12 ti_eqep_function_write 220 208 -12 ti_eqep_function_read 372 360 -12 ti_eqep_count_write 312 300 -12 ti_eqep_count_read 236 224 -12 ti_eqep_action_read 664 652 -12 Total: Before=4598, After=4490, chg -2.35% Acked-by: David Lechner Signed-off-by: Uwe Kleine-König Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/4bde7cbd9e43a5909208102094444219d3154466.1640072891.git.vilhelm.gray@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/ti-eqep.c | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index 09817c953f9a..9e0e46bca4c2 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -87,10 +87,15 @@ struct ti_eqep_cnt { struct regmap *regmap16; }; +static struct ti_eqep_cnt *ti_eqep_count_from_counter(struct counter_device *counter) +{ + return container_of(counter, struct ti_eqep_cnt, counter); +} + static int ti_eqep_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); u32 cnt; regmap_read(priv->regmap32, QPOSCNT, &cnt); @@ -102,7 +107,7 @@ static int ti_eqep_count_read(struct counter_device *counter, static int ti_eqep_count_write(struct counter_device *counter, struct counter_count *count, u64 val) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); u32 max; regmap_read(priv->regmap32, QPOSMAX, &max); @@ -116,7 +121,7 @@ static int ti_eqep_function_read(struct counter_device *counter, struct counter_count *count, enum counter_function *function) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); u32 qdecctl; regmap_read(priv->regmap16, QDECCTL, &qdecctl); @@ -143,7 +148,7 @@ static int ti_eqep_function_write(struct counter_device *counter, struct counter_count *count, enum counter_function function) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); enum ti_eqep_count_func qsrc; switch (function) { @@ -173,7 +178,7 @@ static int ti_eqep_action_read(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action *action) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); enum counter_function function; u32 qdecctl; int err; @@ -245,7 +250,7 @@ static int ti_eqep_position_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); u32 qposmax; regmap_read(priv->regmap32, QPOSMAX, &qposmax); @@ -259,7 +264,7 @@ static int ti_eqep_position_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); if (ceiling != (u32)ceiling) return -ERANGE; @@ -272,7 +277,7 @@ static int ti_eqep_position_ceiling_write(struct counter_device *counter, static int ti_eqep_position_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); u32 qepctl; regmap_read(priv->regmap16, QEPCTL, &qepctl); @@ -285,7 +290,7 @@ static int ti_eqep_position_enable_read(struct counter_device *counter, static int ti_eqep_position_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct ti_eqep_cnt *priv = counter->priv; + struct ti_eqep_cnt *priv = ti_eqep_count_from_counter(counter); regmap_write_bits(priv->regmap16, QEPCTL, QEPCTL_PHEN, enable ? -1 : 0); -- cgit v1.2.3 From c95cc0d95702523f8f361b802c9b7d4eeae07f5d Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Tue, 21 Dec 2021 17:16:48 +0900 Subject: counter: 104-quad-8: Fix persistent enabled events bug A bug exists if the user executes a COUNTER_ADD_WATCH_IOCTL ioctl call, and then executes a COUNTER_DISABLE_EVENTS_IOCTL ioctl call. Disabling the events should disable the 104-QUAD-8 interrupts, but because of this bug the interrupts are not disabling. The reason this bug is occurring is because quad8_events_configure() is called when COUNTER_DISABLE_EVENTS_IOCTL is handled, but the next_irq_trigger[] array has not been cleared before it is checked in the loop. This patch fixes the bug by removing the next_irq_trigger array and instead utilizing a different algorithm of walking the events_list list for the current requested events. When a COUNTER_DISABLE_EVENTS_IOCTL is handled, events_list will be empty and thus all device channels end up with interrupts disabled. Fixes: 7aa2ba0df651 ("counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8") Cc: Syed Nayyar Waris Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/5fd5731cec1c251acee30eefb7c19160d03c9d39.1640072891.git.vilhelm.gray@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/104-quad-8.c | 82 +++++++++++++++++++++----------------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 1cbd60aaed69..a97027db0446 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,6 @@ MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers"); * @ab_enable: array of A and B inputs enable configurations * @preset_enable: array of set_to_preset_on_index attribute configurations * @irq_trigger: array of current IRQ trigger function configurations - * @next_irq_trigger: array of next IRQ trigger function configurations * @synchronous_mode: array of index function synchronous mode configurations * @index_polarity: array of index function polarity configurations * @cable_fault_enable: differential encoder cable status enable configurations @@ -61,7 +61,6 @@ struct quad8 { unsigned int ab_enable[QUAD8_NUM_COUNTERS]; unsigned int preset_enable[QUAD8_NUM_COUNTERS]; unsigned int irq_trigger[QUAD8_NUM_COUNTERS]; - unsigned int next_irq_trigger[QUAD8_NUM_COUNTERS]; unsigned int synchronous_mode[QUAD8_NUM_COUNTERS]; unsigned int index_polarity[QUAD8_NUM_COUNTERS]; unsigned int cable_fault_enable; @@ -390,7 +389,6 @@ static int quad8_action_read(struct counter_device *counter, } enum { - QUAD8_EVENT_NONE = -1, QUAD8_EVENT_CARRY = 0, QUAD8_EVENT_COMPARE = 1, QUAD8_EVENT_CARRY_BORROW = 2, @@ -402,34 +400,49 @@ static int quad8_events_configure(struct counter_device *counter) struct quad8 *const priv = counter->priv; unsigned long irq_enabled = 0; unsigned long irqflags; - size_t channel; + struct counter_event_node *event_node; + unsigned int next_irq_trigger; unsigned long ior_cfg; unsigned long base_offset; spin_lock_irqsave(&priv->lock, irqflags); - /* Enable interrupts for the requested channels, disable for the rest */ - for (channel = 0; channel < QUAD8_NUM_COUNTERS; channel++) { - if (priv->next_irq_trigger[channel] == QUAD8_EVENT_NONE) - continue; + list_for_each_entry(event_node, &counter->events_list, l) { + switch (event_node->event) { + case COUNTER_EVENT_OVERFLOW: + next_irq_trigger = QUAD8_EVENT_CARRY; + break; + case COUNTER_EVENT_THRESHOLD: + next_irq_trigger = QUAD8_EVENT_COMPARE; + break; + case COUNTER_EVENT_OVERFLOW_UNDERFLOW: + next_irq_trigger = QUAD8_EVENT_CARRY_BORROW; + break; + case COUNTER_EVENT_INDEX: + next_irq_trigger = QUAD8_EVENT_INDEX; + break; + default: + /* should never reach this path */ + spin_unlock_irqrestore(&priv->lock, irqflags); + return -EINVAL; + } - if (priv->irq_trigger[channel] != priv->next_irq_trigger[channel]) { - /* Save new IRQ function configuration */ - priv->irq_trigger[channel] = priv->next_irq_trigger[channel]; + /* Skip configuration if it is the same as previously set */ + if (priv->irq_trigger[event_node->channel] == next_irq_trigger) + continue; - /* Load configuration to I/O Control Register */ - ior_cfg = priv->ab_enable[channel] | - priv->preset_enable[channel] << 1 | - priv->irq_trigger[channel] << 3; - base_offset = priv->base + 2 * channel + 1; - outb(QUAD8_CTR_IOR | ior_cfg, base_offset); - } + /* Save new IRQ function configuration */ + priv->irq_trigger[event_node->channel] = next_irq_trigger; - /* Reset next IRQ trigger function configuration */ - priv->next_irq_trigger[channel] = QUAD8_EVENT_NONE; + /* Load configuration to I/O Control Register */ + ior_cfg = priv->ab_enable[event_node->channel] | + priv->preset_enable[event_node->channel] << 1 | + priv->irq_trigger[event_node->channel] << 3; + base_offset = priv->base + 2 * event_node->channel + 1; + outb(QUAD8_CTR_IOR | ior_cfg, base_offset); /* Enable IRQ line */ - irq_enabled |= BIT(channel); + irq_enabled |= BIT(event_node->channel); } outb(irq_enabled, priv->base + QUAD8_REG_INDEX_INTERRUPT); @@ -442,35 +455,20 @@ static int quad8_events_configure(struct counter_device *counter) static int quad8_watch_validate(struct counter_device *counter, const struct counter_watch *watch) { - struct quad8 *const priv = counter->priv; + struct counter_event_node *event_node; if (watch->channel > QUAD8_NUM_COUNTERS - 1) return -EINVAL; switch (watch->event) { case COUNTER_EVENT_OVERFLOW: - if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) - priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY; - else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY) - return -EINVAL; - return 0; case COUNTER_EVENT_THRESHOLD: - if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) - priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_COMPARE; - else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_COMPARE) - return -EINVAL; - return 0; case COUNTER_EVENT_OVERFLOW_UNDERFLOW: - if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) - priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_CARRY_BORROW; - else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_CARRY_BORROW) - return -EINVAL; - return 0; case COUNTER_EVENT_INDEX: - if (priv->next_irq_trigger[watch->channel] == QUAD8_EVENT_NONE) - priv->next_irq_trigger[watch->channel] = QUAD8_EVENT_INDEX; - else if (priv->next_irq_trigger[watch->channel] != QUAD8_EVENT_INDEX) - return -EINVAL; + list_for_each_entry(event_node, &counter->next_events_list, l) + if (watch->channel == event_node->channel && + watch->event != event_node->event) + return -EINVAL; return 0; default: return -EINVAL; @@ -1183,8 +1181,6 @@ static int quad8_probe(struct device *dev, unsigned int id) outb(QUAD8_CTR_IOR, base_offset + 1); /* Disable index function; negative index polarity */ outb(QUAD8_CTR_IDR, base_offset + 1); - /* Initialize next IRQ trigger function configuration */ - priv->next_irq_trigger[i] = QUAD8_EVENT_NONE; } /* Disable Differential Encoder Cable Status for all channels */ outb(0xFF, base[id] + QUAD8_DIFF_ENCODER_CABLE_STATUS); -- cgit v1.2.3 From e233897b1f7a859092bd20b10bfd412013381a10 Mon Sep 17 00:00:00 2001 From: Yang Guang Date: Tue, 21 Dec 2021 17:15:59 +0800 Subject: w1: w1_therm: use swap() to make code cleaner Use the macro 'swap()' defined in 'include/linux/minmax.h' to avoid opencoding it. Reported-by: Zeal Robot Signed-off-by: David Yang Signed-off-by: Yang Guang Link: https://lore.kernel.org/r/cb14f9e6e86cf8494ed2ddce6eec8ebd988908d9.1640077704.git.yang.guang5@zte.com.cn Signed-off-by: Greg Kroah-Hartman --- drivers/w1/slaves/w1_therm.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index ca70c5f03206..565578002d79 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -1785,7 +1785,7 @@ static ssize_t alarms_store(struct device *device, u8 new_config_register[3]; /* array of data to be written */ int temp, ret; char *token = NULL; - s8 tl, th, tt; /* 1 byte per value + temp ring order */ + s8 tl, th; /* 1 byte per value + temp ring order */ char *p_args, *orig; p_args = orig = kmalloc(size, GFP_KERNEL); @@ -1836,9 +1836,8 @@ static ssize_t alarms_store(struct device *device, th = int_to_short(temp); /* Reorder if required th and tl */ - if (tl > th) { - tt = tl; tl = th; th = tt; - } + if (tl > th) + swap(tl, th); /* * Read the scratchpad to change only the required bits -- cgit v1.2.3 From 702bab85d6cdb8aba464f3c8758399edc7368ff2 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:35 +0000 Subject: iio:adc:ina2xx-adc: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-3-jic23@kernel.org --- drivers/iio/adc/ina2xx-adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 08f243f5b92b..4f9992a51e64 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -971,7 +971,7 @@ static int ina2xx_probe(struct i2c_client *client, } if (client->dev.of_node) - type = (enum ina2xx_ids)of_device_get_match_data(&client->dev); + type = (uintptr_t)of_device_get_match_data(&client->dev); else type = id->driver_data; chip->config = &ina2xx_config[type]; -- cgit v1.2.3 From 835122a333dc3a0866398b41047e5b293286a9ce Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:36 +0000 Subject: iio:adc:rcar: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-4-jic23@kernel.org --- drivers/iio/adc/rcar-gyroadc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index a48895046408..727ea6c68049 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -511,8 +511,7 @@ static int rcar_gyroadc_probe(struct platform_device *pdev) if (ret) return ret; - priv->model = (enum rcar_gyroadc_model) - of_device_get_match_data(&pdev->dev); + priv->model = (uintptr_t)of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); -- cgit v1.2.3 From 7926f8a8c7060896a45ffad9d0ac1154b9d67190 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:37 +0000 Subject: iio:adc:ti-ads1015: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-5-jic23@kernel.org --- drivers/iio/adc/ti-ads1015.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index b92d4cd1b823..068efbce1710 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -950,7 +950,7 @@ static int ads1015_probe(struct i2c_client *client, indio_dev->name = ADS1015_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - chip = (enum chip_ids)device_get_match_data(&client->dev); + chip = (uintptr_t)device_get_match_data(&client->dev); if (chip == ADSXXXX) chip = id->driver_data; switch (chip) { -- cgit v1.2.3 From dce71a5fe3b07c3ade6df2687704853fb6fcfd74 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:38 +0000 Subject: iio:amplifiers:hmc425a: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-6-jic23@kernel.org --- drivers/iio/amplifiers/hmc425a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/amplifiers/hmc425a.c b/drivers/iio/amplifiers/hmc425a.c index 9efa692151f0..16c0a77f6a1c 100644 --- a/drivers/iio/amplifiers/hmc425a.c +++ b/drivers/iio/amplifiers/hmc425a.c @@ -192,7 +192,7 @@ static int hmc425a_probe(struct platform_device *pdev) return -ENOMEM; st = iio_priv(indio_dev); - st->type = (enum hmc425a_type)of_device_get_match_data(&pdev->dev); + st->type = (uintptr_t)of_device_get_match_data(&pdev->dev); st->chip_info = &hmc425a_chip_info_tbl[st->type]; indio_dev->num_channels = st->chip_info->num_channels; -- cgit v1.2.3 From e064222dcc165ba2feee7f9ecc90121a7dfbc334 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:39 +0000 Subject: iio:dac:mcp4725: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-7-jic23@kernel.org --- drivers/iio/dac/mcp4725.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index 98b2c2f10bf3..842bad57cb88 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -386,7 +386,7 @@ static int mcp4725_probe(struct i2c_client *client, i2c_set_clientdata(client, indio_dev); data->client = client; if (dev_fwnode(&client->dev)) - data->id = (enum chip_id)device_get_match_data(&client->dev); + data->id = (uintptr_t)device_get_match_data(&client->dev); else data->id = id->driver_data; pdata = dev_get_platdata(&client->dev); -- cgit v1.2.3 From 072cc9816c902103bbc41112fe914e884b3f9882 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:40 +0000 Subject: iio:imu:inv_icm42600: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Jean-Baptiste Maneyrol Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-8-jic23@kernel.org --- drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c | 2 +- drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 85b1934cec60..33d9afb1ba91 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -58,7 +58,7 @@ static int inv_icm42600_probe(struct i2c_client *client) match = device_get_match_data(&client->dev); if (!match) return -EINVAL; - chip = (enum inv_icm42600_chip)match; + chip = (uintptr_t)match; regmap = devm_regmap_init_i2c(client, &inv_icm42600_regmap_config); if (IS_ERR(regmap)) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 323789697a08..e6305e5fa975 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -57,7 +57,7 @@ static int inv_icm42600_probe(struct spi_device *spi) match = device_get_match_data(&spi->dev); if (!match) return -EINVAL; - chip = (enum inv_icm42600_chip)match; + chip = (uintptr_t)match; regmap = devm_regmap_init_spi(spi, &inv_icm42600_regmap_config); if (IS_ERR(regmap)) -- cgit v1.2.3 From 6713847817e0ab66b853294137f58c4d3211ad24 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:41 +0000 Subject: iio:imu:inv_mpu6050: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Baptiste Mansuy Cc: Jean-Baptiste Maneyrol Cc: Linus Walleij Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-9-jic23@kernel.org --- drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c | 2 +- drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 3ef17e3f50e2..fe03707ec2d3 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -110,7 +110,7 @@ static int inv_mpu_probe(struct i2c_client *client, match = device_get_match_data(&client->dev); if (match) { - chip_type = (enum inv_devices)match; + chip_type = (uintptr_t)match; name = client->name; } else if (id) { chip_type = (enum inv_devices) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c index b056f3fe2561..6800356b25fb 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c @@ -45,7 +45,7 @@ static int inv_mpu_probe(struct spi_device *spi) chip_type = (enum inv_devices)spi_id->driver_data; name = spi_id->name; } else if ((match = device_get_match_data(&spi->dev))) { - chip_type = (enum inv_devices)match; + chip_type = (uintptr_t)match; name = dev_name(&spi->dev); } else { return -ENODEV; -- cgit v1.2.3 From ea011add51bc8980c067a1c2df9ec84219062b53 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:42 +0000 Subject: iio:magn:ak8975: Suppress clang W=1 warning about pointer to enum conversion. Cast to a uintptr_t rather than directly to the enum. As per the discussion in below linked media patch. Link: https://lore.kernel.org/linux-media/CAK8P3a2ez6nEw4d+Mqa3XXAz0RFTZHunqqRj6sCt7Y_Eqqs0rw@mail.gmail.com/ Signed-off-by: Jonathan Cameron Cc: Arnd Bergmann Cc: Mauro Carvalho Chehab Cc: Jonathan Albrieux Cc: Linus Walleij Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-10-jic23@kernel.org --- drivers/iio/magnetometer/ak8975.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index 6e82dc54a417..55879a20ae52 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -929,7 +929,7 @@ static int ak8975_probe(struct i2c_client *client, /* id will be NULL when enumerated via ACPI */ match = device_get_match_data(&client->dev); if (match) { - chipset = (enum asahi_compass_chipset)(match); + chipset = (uintptr_t)match; name = dev_name(&client->dev); } else if (id) { chipset = (enum asahi_compass_chipset)(id->driver_data); -- cgit v1.2.3 From e8ffca613cd8dfc27adbfc6cee08b659abed3d88 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:43 +0000 Subject: iio:dummy: Drop set but unused variable len. Not sure what the thinking was here, as lost to history, but the variable is clearly not used so get rid of it. Warning seen with clang W=1 tests (may be present with other compilers and build options). Signed-off-by: Jonathan Cameron Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-11-jic23@kernel.org --- drivers/iio/dummy/iio_simple_dummy_buffer.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index 59aa60d4ca37..d81c2b2dad82 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -45,7 +45,6 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - int len = 0; u16 *data; data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL); @@ -79,7 +78,6 @@ static irqreturn_t iio_simple_dummy_trigger_h(int irq, void *p) indio_dev->masklength, j); /* random access read from the 'device' */ data[i] = fakedata[j]; - len += 2; } } -- cgit v1.2.3 From f3d29c85e6eb5d83d29e2c2bbdf9c824df4cc442 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:44 +0000 Subject: iio:accel:bmc150: Mark structure __maybe_unused as only needed with for pm ops. If CONFIG_PM not set then clang warns this structure is unused. Signed-off-by: Jonathan Cameron Cc: Stephan Gerhold Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-12-jic23@kernel.org --- drivers/iio/accel/bmc150-accel-core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index b0678c351e82..e6081dd0a880 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -170,7 +170,7 @@ static const struct { {1000, 0, 0x0E}, {2000, 0, 0x0F} }; -static const struct { +static __maybe_unused const struct { int bw_bits; int msec; } bmc150_accel_sample_upd_time[] = { {0x08, 64}, -- cgit v1.2.3 From 8ebbfb9882f8f0e52195d08f02a030e617381b3b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 28 Nov 2021 17:24:45 +0000 Subject: iio:accel:kxcjk-1013: Mark struct __maybe_unused to avoid warning. This structure is only used in PM ops, so may not be used depending on build configuration. Signed-off-by: Jonathan Cameron Cc: Stephan Gerhold Reviewed-by: Nathan Chancellor Link: https://lore.kernel.org/r/20211128172445.2616166-13-jic23@kernel.org --- drivers/iio/accel/kxcjk-1013.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 88cf0c276893..460b1a89d575 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -315,7 +315,7 @@ static const char *const kxtf9_samp_freq_avail = "25 50 100 200 400 800"; /* Refer to section 4 of the specification */ -static const struct { +static __maybe_unused const struct { int odr_bits; int usec; } odr_start_up_times[KX_MAX_CHIPS][12] = { -- cgit v1.2.3 From eca6e2d4a4a4b824f055eeaaa24f1c2327fb91a2 Mon Sep 17 00:00:00 2001 From: Anand Ashok Dumbre Date: Fri, 3 Dec 2021 21:23:54 +0000 Subject: device property: Add fwnode_iomap() This patch introduces a new helper routine - fwnode_iomap(), which allows to map the memory mapped IO for a given device node. This implementation does not cover the ACPI case and may be expanded in the future. The main purpose here is to be able to develop resource provider agnostic drivers. Suggested-by: Andy Shevchenko Signed-off-by: Anand Ashok Dumbre Reviewed-by: Andy Shevchenko Acked-by: Rafael J. Wysocki Link: https://lore.kernel.org/r/20211203212358.31444-2-anand.ashok.dumbre@xilinx.com Signed-off-by: Jonathan Cameron --- drivers/base/property.c | 16 ++++++++++++++++ include/linux/property.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/drivers/base/property.c b/drivers/base/property.c index f1f35b48ab8b..ed4470410030 100644 --- a/drivers/base/property.c +++ b/drivers/base/property.c @@ -958,6 +958,22 @@ int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index) } EXPORT_SYMBOL(fwnode_irq_get); +/** + * fwnode_iomap - Maps the memory mapped IO for a given fwnode + * @fwnode: Pointer to the firmware node + * @index: Index of the IO range + * + * Returns a pointer to the mapped memory. + */ +void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index) +{ + if (IS_ENABLED(CONFIG_OF_ADDRESS) && is_of_node(fwnode)) + return of_iomap(to_of_node(fwnode), index); + + return NULL; +} +EXPORT_SYMBOL(fwnode_iomap); + /** * fwnode_graph_get_next_endpoint - Get next endpoint firmware node * @fwnode: Pointer to the parent firmware node diff --git a/include/linux/property.h b/include/linux/property.h index 88fa726a76df..6670d5a1ec2a 100644 --- a/include/linux/property.h +++ b/include/linux/property.h @@ -122,6 +122,8 @@ void fwnode_handle_put(struct fwnode_handle *fwnode); int fwnode_irq_get(const struct fwnode_handle *fwnode, unsigned int index); +void __iomem *fwnode_iomap(struct fwnode_handle *fwnode, int index); + unsigned int device_get_child_node_count(struct device *dev); static inline bool device_property_read_bool(struct device *dev, -- cgit v1.2.3 From d5c70627a79455154f5f636096abe6fe57510605 Mon Sep 17 00:00:00 2001 From: Anand Ashok Dumbre Date: Fri, 3 Dec 2021 21:23:56 +0000 Subject: iio: adc: Add Xilinx AMS driver The AMS includes an ADC as well as on-chip sensors that can be used to sample external voltages and monitor on-die operating conditions, such as temperature and supply voltage levels. The AMS has two SYSMON blocks. PL-SYSMON block is capable of monitoring off chip voltage and temperature. PL-SYSMON block has DRP, JTAG and I2C interface to enable monitoring from an external master. Out of these interfaces currently only DRP is supported. Other block PS-SYSMON is memory mapped to PS. The AMS can use internal channels to monitor voltage and temperature as well as one primary and up to 16 auxiliary channels for measuring external voltages. The voltage and temperature monitoring channels also have event capability which allows to generate an interrupt when their value falls below or raises above a set threshold. Co-developed-by: Manish Narani Signed-off-by: Manish Narani Signed-off-by: Anand Ashok Dumbre Link: https://lore.kernel.org/r/20211203212358.31444-4-anand.ashok.dumbre@xilinx.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 15 + drivers/iio/adc/Makefile | 1 + drivers/iio/adc/xilinx-ams.c | 1451 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1467 insertions(+) create mode 100644 drivers/iio/adc/xilinx-ams.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index c7de4632f24a..3570c4e41708 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1288,4 +1288,19 @@ config XILINX_XADC The driver can also be build as a module. If so, the module will be called xilinx-xadc. +config XILINX_AMS + tristate "Xilinx AMS driver" + depends on ARCH_ZYNQMP || COMPILE_TEST + depends on HAS_IOMEM + help + Say yes here to have support for the Xilinx AMS for Ultrascale/Ultrascale+ + System Monitor. With this you can measure and monitor the Voltages and + Temperature values on the SOC. + + The driver supports Voltage and Temperature monitoring on Xilinx Ultrascale + devices. + + The driver can also be built as a module. If so, the module will be called + xilinx-ams. + endmenu diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index d3f53549720c..4a8f1833993b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -115,4 +115,5 @@ obj-$(CONFIG_VF610_ADC) += vf610_adc.o obj-$(CONFIG_VIPERBOARD_ADC) += viperboard_adc.o xilinx-xadc-y := xilinx-xadc-core.o xilinx-xadc-events.o obj-$(CONFIG_XILINX_XADC) += xilinx-xadc.o +obj-$(CONFIG_XILINX_AMS) += xilinx-ams.o obj-$(CONFIG_SD_ADC_MODULATOR) += sd_adc_modulator.o diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c new file mode 100644 index 000000000000..8343c5f74121 --- /dev/null +++ b/drivers/iio/adc/xilinx-ams.c @@ -0,0 +1,1451 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Xilinx AMS driver + * + * Copyright (C) 2021 Xilinx, Inc. + * + * Manish Narani + * Rajnikant Bhojani + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* AMS registers definitions */ +#define AMS_ISR_0 0x010 +#define AMS_ISR_1 0x014 +#define AMS_IER_0 0x020 +#define AMS_IER_1 0x024 +#define AMS_IDR_0 0x028 +#define AMS_IDR_1 0x02C +#define AMS_PS_CSTS 0x040 +#define AMS_PL_CSTS 0x044 + +#define AMS_VCC_PSPLL0 0x060 +#define AMS_VCC_PSPLL3 0x06C +#define AMS_VCCINT 0x078 +#define AMS_VCCBRAM 0x07C +#define AMS_VCCAUX 0x080 +#define AMS_PSDDRPLL 0x084 +#define AMS_PSINTFPDDR 0x09C + +#define AMS_VCC_PSPLL0_CH 48 +#define AMS_VCC_PSPLL3_CH 51 +#define AMS_VCCINT_CH 54 +#define AMS_VCCBRAM_CH 55 +#define AMS_VCCAUX_CH 56 +#define AMS_PSDDRPLL_CH 57 +#define AMS_PSINTFPDDR_CH 63 + +#define AMS_REG_CONFIG0 0x100 +#define AMS_REG_CONFIG1 0x104 +#define AMS_REG_CONFIG3 0x10C +#define AMS_REG_CONFIG4 0x110 +#define AMS_REG_SEQ_CH0 0x120 +#define AMS_REG_SEQ_CH1 0x124 +#define AMS_REG_SEQ_CH2 0x118 + +#define AMS_VUSER0_MASK BIT(0) +#define AMS_VUSER1_MASK BIT(1) +#define AMS_VUSER2_MASK BIT(2) +#define AMS_VUSER3_MASK BIT(3) + +#define AMS_TEMP 0x000 +#define AMS_SUPPLY1 0x004 +#define AMS_SUPPLY2 0x008 +#define AMS_VP_VN 0x00C +#define AMS_VREFP 0x010 +#define AMS_VREFN 0x014 +#define AMS_SUPPLY3 0x018 +#define AMS_SUPPLY4 0x034 +#define AMS_SUPPLY5 0x038 +#define AMS_SUPPLY6 0x03C +#define AMS_SUPPLY7 0x200 +#define AMS_SUPPLY8 0x204 +#define AMS_SUPPLY9 0x208 +#define AMS_SUPPLY10 0x20C +#define AMS_VCCAMS 0x210 +#define AMS_TEMP_REMOTE 0x214 + +#define AMS_REG_VAUX(x) (0x40 + 4 * (x)) + +#define AMS_PS_RESET_VALUE 0xFFFF +#define AMS_PL_RESET_VALUE 0xFFFF + +#define AMS_CONF0_CHANNEL_NUM_MASK GENMASK(6, 0) + +#define AMS_CONF1_SEQ_MASK GENMASK(15, 12) +#define AMS_CONF1_SEQ_DEFAULT FIELD_PREP(AMS_CONF1_SEQ_MASK, 0) +#define AMS_CONF1_SEQ_CONTINUOUS FIELD_PREP(AMS_CONF1_SEQ_MASK, 1) +#define AMS_CONF1_SEQ_SINGLE_CHANNEL FIELD_PREP(AMS_CONF1_SEQ_MASK, 2) + +#define AMS_REG_SEQ0_MASK GENMASK(15, 0) +#define AMS_REG_SEQ2_MASK GENMASK(21, 16) +#define AMS_REG_SEQ1_MASK GENMASK_ULL(37, 22) + +#define AMS_PS_SEQ_MASK GENMASK(21, 0) +#define AMS_PL_SEQ_MASK GENMASK_ULL(59, 22) + +#define AMS_ALARM_TEMP 0x140 +#define AMS_ALARM_SUPPLY1 0x144 +#define AMS_ALARM_SUPPLY2 0x148 +#define AMS_ALARM_SUPPLY3 0x160 +#define AMS_ALARM_SUPPLY4 0x164 +#define AMS_ALARM_SUPPLY5 0x168 +#define AMS_ALARM_SUPPLY6 0x16C +#define AMS_ALARM_SUPPLY7 0x180 +#define AMS_ALARM_SUPPLY8 0x184 +#define AMS_ALARM_SUPPLY9 0x188 +#define AMS_ALARM_SUPPLY10 0x18C +#define AMS_ALARM_VCCAMS 0x190 +#define AMS_ALARM_TEMP_REMOTE 0x194 +#define AMS_ALARM_THRESHOLD_OFF_10 0x10 +#define AMS_ALARM_THRESHOLD_OFF_20 0x20 + +#define AMS_ALARM_THR_DIRECT_MASK BIT(1) +#define AMS_ALARM_THR_MIN 0x0000 +#define AMS_ALARM_THR_MAX (BIT(16) - 1) + +#define AMS_ALARM_MASK GENMASK_ULL(63, 0) +#define AMS_NO_OF_ALARMS 32 +#define AMS_PL_ALARM_START 16 +#define AMS_PL_ALARM_MASK GENMASK(31, 16) +#define AMS_ISR0_ALARM_MASK GENMASK(31, 0) +#define AMS_ISR1_ALARM_MASK (GENMASK(31, 29) | GENMASK(4, 0)) +#define AMS_ISR1_EOC_MASK BIT(3) +#define AMS_ISR1_INTR_MASK GENMASK_ULL(63, 32) +#define AMS_ISR0_ALARM_2_TO_0_MASK GENMASK(2, 0) +#define AMS_ISR0_ALARM_6_TO_3_MASK GENMASK(6, 3) +#define AMS_ISR0_ALARM_12_TO_7_MASK GENMASK(13, 8) +#define AMS_CONF1_ALARM_2_TO_0_MASK GENMASK(3, 1) +#define AMS_CONF1_ALARM_6_TO_3_MASK GENMASK(11, 8) +#define AMS_CONF1_ALARM_12_TO_7_MASK GENMASK(5, 0) +#define AMS_REGCFG1_ALARM_MASK \ + (AMS_CONF1_ALARM_2_TO_0_MASK | AMS_CONF1_ALARM_6_TO_3_MASK | BIT(0)) +#define AMS_REGCFG3_ALARM_MASK AMS_CONF1_ALARM_12_TO_7_MASK + +#define AMS_PS_CSTS_PS_READY (BIT(27) | BIT(16)) +#define AMS_PL_CSTS_ACCESS_MASK BIT(1) + +#define AMS_PL_MAX_FIXED_CHANNEL 10 +#define AMS_PL_MAX_EXT_CHANNEL 20 + +#define AMS_INIT_POLL_TIME_US 200 +#define AMS_INIT_TIMEOUT_US 10000 +#define AMS_UNMASK_TIMEOUT_MS 500 + +/* + * Following scale and offset value is derived from + * UG580 (v1.7) December 20, 2016 + */ +#define AMS_SUPPLY_SCALE_1VOLT_mV 1000 +#define AMS_SUPPLY_SCALE_3VOLT_mV 3000 +#define AMS_SUPPLY_SCALE_6VOLT_mV 6000 +#define AMS_SUPPLY_SCALE_DIV_BIT 16 + +#define AMS_TEMP_SCALE 509314 +#define AMS_TEMP_SCALE_DIV_BIT 16 +#define AMS_TEMP_OFFSET -((280230LL << 16) / 509314) + +enum ams_alarm_bit { + AMS_ALARM_BIT_TEMP = 0, + AMS_ALARM_BIT_SUPPLY1 = 1, + AMS_ALARM_BIT_SUPPLY2 = 2, + AMS_ALARM_BIT_SUPPLY3 = 3, + AMS_ALARM_BIT_SUPPLY4 = 4, + AMS_ALARM_BIT_SUPPLY5 = 5, + AMS_ALARM_BIT_SUPPLY6 = 6, + AMS_ALARM_BIT_RESERVED = 7, + AMS_ALARM_BIT_SUPPLY7 = 8, + AMS_ALARM_BIT_SUPPLY8 = 9, + AMS_ALARM_BIT_SUPPLY9 = 10, + AMS_ALARM_BIT_SUPPLY10 = 11, + AMS_ALARM_BIT_VCCAMS = 12, + AMS_ALARM_BIT_TEMP_REMOTE = 13, +}; + +enum ams_seq { + AMS_SEQ_VCC_PSPLL = 0, + AMS_SEQ_VCC_PSBATT = 1, + AMS_SEQ_VCCINT = 2, + AMS_SEQ_VCCBRAM = 3, + AMS_SEQ_VCCAUX = 4, + AMS_SEQ_PSDDRPLL = 5, + AMS_SEQ_INTDDR = 6, +}; + +enum ams_ps_pl_seq { + AMS_SEQ_CALIB = 0, + AMS_SEQ_RSVD_1 = 1, + AMS_SEQ_RSVD_2 = 2, + AMS_SEQ_TEST = 3, + AMS_SEQ_RSVD_4 = 4, + AMS_SEQ_SUPPLY4 = 5, + AMS_SEQ_SUPPLY5 = 6, + AMS_SEQ_SUPPLY6 = 7, + AMS_SEQ_TEMP = 8, + AMS_SEQ_SUPPLY2 = 9, + AMS_SEQ_SUPPLY1 = 10, + AMS_SEQ_VP_VN = 11, + AMS_SEQ_VREFP = 12, + AMS_SEQ_VREFN = 13, + AMS_SEQ_SUPPLY3 = 14, + AMS_SEQ_CURRENT_MON = 15, + AMS_SEQ_SUPPLY7 = 16, + AMS_SEQ_SUPPLY8 = 17, + AMS_SEQ_SUPPLY9 = 18, + AMS_SEQ_SUPPLY10 = 19, + AMS_SEQ_VCCAMS = 20, + AMS_SEQ_TEMP_REMOTE = 21, + AMS_SEQ_MAX = 22 +}; + +#define AMS_PS_SEQ_MAX AMS_SEQ_MAX +#define AMS_SEQ(x) (AMS_SEQ_MAX + (x)) +#define PS_SEQ(x) (x) +#define PL_SEQ(x) (AMS_PS_SEQ_MAX + (x)) +#define AMS_CTRL_SEQ_BASE (AMS_PS_SEQ_MAX * 3) + +#define AMS_CHAN_TEMP(_scan_index, _addr) { \ + .type = IIO_TEMP, \ + .indexed = 1, \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .event_spec = ams_temp_events, \ + .scan_index = _scan_index, \ + .num_event_specs = ARRAY_SIZE(ams_temp_events), \ +} + +#define AMS_CHAN_VOLTAGE(_scan_index, _addr, _alarm) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .address = (_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .event_spec = (_alarm) ? ams_voltage_events : NULL, \ + .scan_index = _scan_index, \ + .num_event_specs = (_alarm) ? ARRAY_SIZE(ams_voltage_events) : 0, \ +} + +#define AMS_PS_CHAN_TEMP(_scan_index, _addr) \ + AMS_CHAN_TEMP(PS_SEQ(_scan_index), _addr) +#define AMS_PS_CHAN_VOLTAGE(_scan_index, _addr) \ + AMS_CHAN_VOLTAGE(PS_SEQ(_scan_index), _addr, true) + +#define AMS_PL_CHAN_TEMP(_scan_index, _addr) \ + AMS_CHAN_TEMP(PL_SEQ(_scan_index), _addr) +#define AMS_PL_CHAN_VOLTAGE(_scan_index, _addr, _alarm) \ + AMS_CHAN_VOLTAGE(PL_SEQ(_scan_index), _addr, _alarm) +#define AMS_PL_AUX_CHAN_VOLTAGE(_auxno) \ + AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(_auxno)), AMS_REG_VAUX(_auxno), false) +#define AMS_CTRL_CHAN_VOLTAGE(_scan_index, _addr) \ + AMS_CHAN_VOLTAGE(PL_SEQ(AMS_SEQ(AMS_SEQ(_scan_index))), _addr, false) + +/** + * struct ams - This structure contains necessary state for xilinx-ams to operate + * @base: physical base address of device + * @ps_base: physical base address of PS device + * @pl_base: physical base address of PL device + * @clk: clocks associated with the device + * @dev: pointer to device struct + * @lock: to handle multiple user interaction + * @intr_lock: to protect interrupt mask values + * @alarm_mask: alarm configuration + * @current_masked_alarm: currently masked due to alarm + * @intr_mask: interrupt configuration + * @ams_unmask_work: re-enables event once the event condition disappears + * + */ +struct ams { + void __iomem *base; + void __iomem *ps_base; + void __iomem *pl_base; + struct clk *clk; + struct device *dev; + struct mutex lock; + spinlock_t intr_lock; + unsigned int alarm_mask; + unsigned int current_masked_alarm; + u64 intr_mask; + struct delayed_work ams_unmask_work; +}; + +static inline void ams_ps_update_reg(struct ams *ams, unsigned int offset, + u32 mask, u32 data) +{ + u32 val, regval; + + val = readl(ams->ps_base + offset); + regval = (val & ~mask) | (data & mask); + writel(regval, ams->ps_base + offset); +} + +static inline void ams_pl_update_reg(struct ams *ams, unsigned int offset, + u32 mask, u32 data) +{ + u32 val, regval; + + val = readl(ams->pl_base + offset); + regval = (val & ~mask) | (data & mask); + writel(regval, ams->pl_base + offset); +} + +static void ams_update_intrmask(struct ams *ams, u64 mask, u64 val) +{ + u32 regval; + + ams->intr_mask = (ams->intr_mask & ~mask) | (val & mask); + + regval = ~(ams->intr_mask | ams->current_masked_alarm); + writel(regval, ams->base + AMS_IER_0); + + regval = ~(FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask)); + writel(regval, ams->base + AMS_IER_1); + + regval = ams->intr_mask | ams->current_masked_alarm; + writel(regval, ams->base + AMS_IDR_0); + + regval = FIELD_GET(AMS_ISR1_INTR_MASK, ams->intr_mask); + writel(regval, ams->base + AMS_IDR_1); +} + +static void ams_disable_all_alarms(struct ams *ams) +{ + /* disable PS module alarm */ + if (ams->ps_base) { + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, + AMS_REGCFG1_ALARM_MASK); + ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, + AMS_REGCFG3_ALARM_MASK); + } + + /* disable PL module alarm */ + if (ams->pl_base) { + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, + AMS_REGCFG1_ALARM_MASK); + ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, + AMS_REGCFG3_ALARM_MASK); + } +} + +static void ams_update_ps_alarm(struct ams *ams, unsigned long alarm_mask) +{ + u32 cfg; + u32 val; + + val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val)); + + val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, alarm_mask); + cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val)); + + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg); + + val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val)); + ams_ps_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg); +} + +static void ams_update_pl_alarm(struct ams *ams, unsigned long alarm_mask) +{ + unsigned long pl_alarm_mask; + u32 cfg; + u32 val; + + pl_alarm_mask = FIELD_GET(AMS_PL_ALARM_MASK, alarm_mask); + + val = FIELD_GET(AMS_ISR0_ALARM_2_TO_0_MASK, pl_alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_2_TO_0_MASK, val)); + + val = FIELD_GET(AMS_ISR0_ALARM_6_TO_3_MASK, pl_alarm_mask); + cfg &= ~(FIELD_PREP(AMS_CONF1_ALARM_6_TO_3_MASK, val)); + + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_REGCFG1_ALARM_MASK, cfg); + + val = FIELD_GET(AMS_ISR0_ALARM_12_TO_7_MASK, pl_alarm_mask); + cfg = ~(FIELD_PREP(AMS_CONF1_ALARM_12_TO_7_MASK, val)); + ams_pl_update_reg(ams, AMS_REG_CONFIG3, AMS_REGCFG3_ALARM_MASK, cfg); +} + +static void ams_update_alarm(struct ams *ams, unsigned long alarm_mask) +{ + unsigned long flags; + + if (ams->ps_base) + ams_update_ps_alarm(ams, alarm_mask); + + if (ams->pl_base) + ams_update_pl_alarm(ams, alarm_mask); + + spin_lock_irqsave(&ams->intr_lock, flags); + ams_update_intrmask(ams, AMS_ISR0_ALARM_MASK, ~alarm_mask); + spin_unlock_irqrestore(&ams->intr_lock, flags); +} + +static void ams_enable_channel_sequence(struct iio_dev *indio_dev) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned long long scan_mask; + int i; + u32 regval; + + /* + * Enable channel sequence. First 22 bits of scan_mask represent + * PS channels, and next remaining bits represent PL channels. + */ + + /* Run calibration of PS & PL as part of the sequence */ + scan_mask = BIT(0) | BIT(AMS_PS_SEQ_MAX); + for (i = 0; i < indio_dev->num_channels; i++) + scan_mask |= BIT_ULL(indio_dev->channels[i].scan_index); + + if (ams->ps_base) { + /* put sysmon in a soft reset to change the sequence */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* configure basic channels */ + regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask); + writel(regval, ams->ps_base + AMS_REG_SEQ_CH0); + + regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask); + writel(regval, ams->ps_base + AMS_REG_SEQ_CH2); + + /* set continuous sequence mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_CONTINUOUS); + } + + if (ams->pl_base) { + /* put sysmon in a soft reset to change the sequence */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + + /* configure basic channels */ + scan_mask = FIELD_GET(AMS_PL_SEQ_MASK, scan_mask); + + regval = FIELD_GET(AMS_REG_SEQ0_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH0); + + regval = FIELD_GET(AMS_REG_SEQ1_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH1); + + regval = FIELD_GET(AMS_REG_SEQ2_MASK, scan_mask); + writel(regval, ams->pl_base + AMS_REG_SEQ_CH2); + + /* set continuous sequence mode */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_CONTINUOUS); + } +} + +static int ams_init_device(struct ams *ams) +{ + u32 expect = AMS_PS_CSTS_PS_READY; + u32 reg, value; + int ret; + + /* reset AMS */ + if (ams->ps_base) { + writel(AMS_PS_RESET_VALUE, ams->ps_base + AMS_VP_VN); + + ret = readl_poll_timeout(ams->base + AMS_PS_CSTS, reg, (reg & expect), + AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); + if (ret) + return ret; + + /* put sysmon in a default state */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + } + + if (ams->pl_base) { + value = readl(ams->base + AMS_PL_CSTS); + if (value == 0) + return 0; + + writel(AMS_PL_RESET_VALUE, ams->pl_base + AMS_VP_VN); + + /* put sysmon in a default state */ + ams_pl_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_DEFAULT); + } + + ams_disable_all_alarms(ams); + + /* Disable interrupt */ + ams_update_intrmask(ams, AMS_ALARM_MASK, AMS_ALARM_MASK); + + /* Clear any pending interrupt */ + writel(AMS_ISR0_ALARM_MASK, ams->base + AMS_ISR_0); + writel(AMS_ISR1_ALARM_MASK, ams->base + AMS_ISR_1); + + return 0; +} + +static int ams_enable_single_channel(struct ams *ams, unsigned int offset) +{ + u8 channel_num; + + switch (offset) { + case AMS_VCC_PSPLL0: + channel_num = AMS_VCC_PSPLL0_CH; + break; + case AMS_VCC_PSPLL3: + channel_num = AMS_VCC_PSPLL3_CH; + break; + case AMS_VCCINT: + channel_num = AMS_VCCINT_CH; + break; + case AMS_VCCBRAM: + channel_num = AMS_VCCBRAM_CH; + break; + case AMS_VCCAUX: + channel_num = AMS_VCCAUX_CH; + break; + case AMS_PSDDRPLL: + channel_num = AMS_PSDDRPLL_CH; + break; + case AMS_PSINTFPDDR: + channel_num = AMS_PSINTFPDDR_CH; + break; + default: + return -EINVAL; + } + + /* set single channel, sequencer off mode */ + ams_ps_update_reg(ams, AMS_REG_CONFIG1, AMS_CONF1_SEQ_MASK, + AMS_CONF1_SEQ_SINGLE_CHANNEL); + + /* write the channel number */ + ams_ps_update_reg(ams, AMS_REG_CONFIG0, AMS_CONF0_CHANNEL_NUM_MASK, + channel_num); + + return 0; +} + +static int ams_read_vcc_reg(struct ams *ams, unsigned int offset, u32 *data) +{ + u32 expect = AMS_ISR1_EOC_MASK; + u32 reg; + int ret; + + ret = ams_enable_single_channel(ams, offset); + if (ret) + return ret; + + ret = readl_poll_timeout(ams->base + AMS_ISR_1, reg, (reg & expect), + AMS_INIT_POLL_TIME_US, AMS_INIT_TIMEOUT_US); + if (ret) + return ret; + + *data = readl(ams->base + offset); + + return 0; +} + +static int ams_get_ps_scale(int address) +{ + int val; + + switch (address) { + case AMS_SUPPLY1: + case AMS_SUPPLY2: + case AMS_SUPPLY3: + case AMS_SUPPLY4: + case AMS_SUPPLY9: + case AMS_SUPPLY10: + case AMS_VCCAMS: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY5: + case AMS_SUPPLY6: + case AMS_SUPPLY7: + case AMS_SUPPLY8: + val = AMS_SUPPLY_SCALE_6VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_get_pl_scale(struct ams *ams, int address) +{ + int val, regval; + + switch (address) { + case AMS_SUPPLY1: + case AMS_SUPPLY2: + case AMS_SUPPLY3: + case AMS_SUPPLY4: + case AMS_SUPPLY5: + case AMS_SUPPLY6: + case AMS_VCCAMS: + case AMS_VREFP: + case AMS_VREFN: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY7: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER0_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY8: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER1_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY9: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER2_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_SUPPLY10: + regval = readl(ams->pl_base + AMS_REG_CONFIG4); + if (FIELD_GET(AMS_VUSER3_MASK, regval)) + val = AMS_SUPPLY_SCALE_6VOLT_mV; + else + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + case AMS_VP_VN: + case AMS_REG_VAUX(0) ... AMS_REG_VAUX(15): + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_get_ctrl_scale(int address) +{ + int val; + + switch (address) { + case AMS_VCC_PSPLL0: + case AMS_VCC_PSPLL3: + case AMS_VCCINT: + case AMS_VCCBRAM: + case AMS_VCCAUX: + case AMS_PSDDRPLL: + case AMS_PSINTFPDDR: + val = AMS_SUPPLY_SCALE_3VOLT_mV; + break; + default: + val = AMS_SUPPLY_SCALE_1VOLT_mV; + break; + } + + return val; +} + +static int ams_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ams *ams = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + mutex_lock(&ams->lock); + if (chan->scan_index >= AMS_CTRL_SEQ_BASE) { + ret = ams_read_vcc_reg(ams, chan->address, val); + if (ret) + goto unlock_mutex; + ams_enable_channel_sequence(indio_dev); + } else if (chan->scan_index >= AMS_PS_SEQ_MAX) + *val = readl(ams->pl_base + chan->address); + else + *val = readl(ams->ps_base + chan->address); + + ret = IIO_VAL_INT; +unlock_mutex: + mutex_unlock(&ams->lock); + return ret; + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + if (chan->scan_index < AMS_PS_SEQ_MAX) + *val = ams_get_ps_scale(chan->address); + else if (chan->scan_index >= AMS_PS_SEQ_MAX && + chan->scan_index < AMS_CTRL_SEQ_BASE) + *val = ams_get_pl_scale(ams, chan->address); + else + *val = ams_get_ctrl_scale(chan->address); + + *val2 = AMS_SUPPLY_SCALE_DIV_BIT; + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_TEMP: + *val = AMS_TEMP_SCALE; + *val2 = AMS_TEMP_SCALE_DIV_BIT; + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + /* Only the temperature channel has an offset */ + *val = AMS_TEMP_OFFSET; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ams_get_alarm_offset(int scan_index, enum iio_event_direction dir) +{ + int offset; + + if (scan_index >= AMS_PS_SEQ_MAX) + scan_index -= AMS_PS_SEQ_MAX; + + if (dir == IIO_EV_DIR_FALLING) { + if (scan_index < AMS_SEQ_SUPPLY7) + offset = AMS_ALARM_THRESHOLD_OFF_10; + else + offset = AMS_ALARM_THRESHOLD_OFF_20; + } else { + offset = 0; + } + + switch (scan_index) { + case AMS_SEQ_TEMP: + return AMS_ALARM_TEMP + offset; + case AMS_SEQ_SUPPLY1: + return AMS_ALARM_SUPPLY1 + offset; + case AMS_SEQ_SUPPLY2: + return AMS_ALARM_SUPPLY2 + offset; + case AMS_SEQ_SUPPLY3: + return AMS_ALARM_SUPPLY3 + offset; + case AMS_SEQ_SUPPLY4: + return AMS_ALARM_SUPPLY4 + offset; + case AMS_SEQ_SUPPLY5: + return AMS_ALARM_SUPPLY5 + offset; + case AMS_SEQ_SUPPLY6: + return AMS_ALARM_SUPPLY6 + offset; + case AMS_SEQ_SUPPLY7: + return AMS_ALARM_SUPPLY7 + offset; + case AMS_SEQ_SUPPLY8: + return AMS_ALARM_SUPPLY8 + offset; + case AMS_SEQ_SUPPLY9: + return AMS_ALARM_SUPPLY9 + offset; + case AMS_SEQ_SUPPLY10: + return AMS_ALARM_SUPPLY10 + offset; + case AMS_SEQ_VCCAMS: + return AMS_ALARM_VCCAMS + offset; + case AMS_SEQ_TEMP_REMOTE: + return AMS_ALARM_TEMP_REMOTE + offset; + default: + return 0; + } +} + +static const struct iio_chan_spec *ams_event_to_channel(struct iio_dev *dev, + u32 event) +{ + int scan_index = 0, i; + + if (event >= AMS_PL_ALARM_START) { + event -= AMS_PL_ALARM_START; + scan_index = AMS_PS_SEQ_MAX; + } + + switch (event) { + case AMS_ALARM_BIT_TEMP: + scan_index += AMS_SEQ_TEMP; + break; + case AMS_ALARM_BIT_SUPPLY1: + scan_index += AMS_SEQ_SUPPLY1; + break; + case AMS_ALARM_BIT_SUPPLY2: + scan_index += AMS_SEQ_SUPPLY2; + break; + case AMS_ALARM_BIT_SUPPLY3: + scan_index += AMS_SEQ_SUPPLY3; + break; + case AMS_ALARM_BIT_SUPPLY4: + scan_index += AMS_SEQ_SUPPLY4; + break; + case AMS_ALARM_BIT_SUPPLY5: + scan_index += AMS_SEQ_SUPPLY5; + break; + case AMS_ALARM_BIT_SUPPLY6: + scan_index += AMS_SEQ_SUPPLY6; + break; + case AMS_ALARM_BIT_SUPPLY7: + scan_index += AMS_SEQ_SUPPLY7; + break; + case AMS_ALARM_BIT_SUPPLY8: + scan_index += AMS_SEQ_SUPPLY8; + break; + case AMS_ALARM_BIT_SUPPLY9: + scan_index += AMS_SEQ_SUPPLY9; + break; + case AMS_ALARM_BIT_SUPPLY10: + scan_index += AMS_SEQ_SUPPLY10; + break; + case AMS_ALARM_BIT_VCCAMS: + scan_index += AMS_SEQ_VCCAMS; + break; + case AMS_ALARM_BIT_TEMP_REMOTE: + scan_index += AMS_SEQ_TEMP_REMOTE; + break; + default: + break; + } + + for (i = 0; i < dev->num_channels; i++) + if (dev->channels[i].scan_index == scan_index) + break; + + return &dev->channels[i]; +} + +static int ams_get_alarm_mask(int scan_index) +{ + int bit = 0; + + if (scan_index >= AMS_PS_SEQ_MAX) { + bit = AMS_PL_ALARM_START; + scan_index -= AMS_PS_SEQ_MAX; + } + + switch (scan_index) { + case AMS_SEQ_TEMP: + return BIT(AMS_ALARM_BIT_TEMP + bit); + case AMS_SEQ_SUPPLY1: + return BIT(AMS_ALARM_BIT_SUPPLY1 + bit); + case AMS_SEQ_SUPPLY2: + return BIT(AMS_ALARM_BIT_SUPPLY2 + bit); + case AMS_SEQ_SUPPLY3: + return BIT(AMS_ALARM_BIT_SUPPLY3 + bit); + case AMS_SEQ_SUPPLY4: + return BIT(AMS_ALARM_BIT_SUPPLY4 + bit); + case AMS_SEQ_SUPPLY5: + return BIT(AMS_ALARM_BIT_SUPPLY5 + bit); + case AMS_SEQ_SUPPLY6: + return BIT(AMS_ALARM_BIT_SUPPLY6 + bit); + case AMS_SEQ_SUPPLY7: + return BIT(AMS_ALARM_BIT_SUPPLY7 + bit); + case AMS_SEQ_SUPPLY8: + return BIT(AMS_ALARM_BIT_SUPPLY8 + bit); + case AMS_SEQ_SUPPLY9: + return BIT(AMS_ALARM_BIT_SUPPLY9 + bit); + case AMS_SEQ_SUPPLY10: + return BIT(AMS_ALARM_BIT_SUPPLY10 + bit); + case AMS_SEQ_VCCAMS: + return BIT(AMS_ALARM_BIT_VCCAMS + bit); + case AMS_SEQ_TEMP_REMOTE: + return BIT(AMS_ALARM_BIT_TEMP_REMOTE + bit); + default: + return 0; + } +} + +static int ams_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ams *ams = iio_priv(indio_dev); + + return !!(ams->alarm_mask & ams_get_alarm_mask(chan->scan_index)); +} + +static int ams_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int state) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int alarm; + + alarm = ams_get_alarm_mask(chan->scan_index); + + mutex_lock(&ams->lock); + + if (state) + ams->alarm_mask |= alarm; + else + ams->alarm_mask &= ~alarm; + + ams_update_alarm(ams, ams->alarm_mask); + + mutex_unlock(&ams->lock); + + return 0; +} + +static int ams_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int *val, int *val2) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int offset = ams_get_alarm_offset(chan->scan_index, dir); + + mutex_lock(&ams->lock); + + if (chan->scan_index >= AMS_PS_SEQ_MAX) + *val = readl(ams->pl_base + offset); + else + *val = readl(ams->ps_base + offset); + + mutex_unlock(&ams->lock); + + return IIO_VAL_INT; +} + +static int ams_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, int val, int val2) +{ + struct ams *ams = iio_priv(indio_dev); + unsigned int offset; + + mutex_lock(&ams->lock); + + /* Set temperature channel threshold to direct threshold */ + if (chan->type == IIO_TEMP) { + offset = ams_get_alarm_offset(chan->scan_index, IIO_EV_DIR_FALLING); + + if (chan->scan_index >= AMS_PS_SEQ_MAX) + ams_pl_update_reg(ams, offset, + AMS_ALARM_THR_DIRECT_MASK, + AMS_ALARM_THR_DIRECT_MASK); + else + ams_ps_update_reg(ams, offset, + AMS_ALARM_THR_DIRECT_MASK, + AMS_ALARM_THR_DIRECT_MASK); + } + + offset = ams_get_alarm_offset(chan->scan_index, dir); + if (chan->scan_index >= AMS_PS_SEQ_MAX) + writel(val, ams->pl_base + offset); + else + writel(val, ams->ps_base + offset); + + mutex_unlock(&ams->lock); + + return 0; +} + +static void ams_handle_event(struct iio_dev *indio_dev, u32 event) +{ + const struct iio_chan_spec *chan; + + chan = ams_event_to_channel(indio_dev, event); + + if (chan->type == IIO_TEMP) { + /* + * The temperature channel only supports over-temperature + * events. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_RISING), + iio_get_time_ns(indio_dev)); + } else { + /* + * For other channels we don't know whether it is a upper or + * lower threshold event. Userspace will have to check the + * channel value if it wants to know. + */ + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(chan->type, chan->channel, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(indio_dev)); + } +} + +static void ams_handle_events(struct iio_dev *indio_dev, unsigned long events) +{ + unsigned int bit; + + for_each_set_bit(bit, &events, AMS_NO_OF_ALARMS) + ams_handle_event(indio_dev, bit); +} + +/** + * ams_unmask_worker - ams alarm interrupt unmask worker + * @work: work to be done + * + * The ZynqMP threshold interrupts are level sensitive. Since we can't make the + * threshold condition go way from within the interrupt handler, this means as + * soon as a threshold condition is present we would enter the interrupt handler + * again and again. To work around this we mask all active threshold interrupts + * in the interrupt handler and start a timer. In this timer we poll the + * interrupt status and only if the interrupt is inactive we unmask it again. + */ +static void ams_unmask_worker(struct work_struct *work) +{ + struct ams *ams = container_of(work, struct ams, ams_unmask_work.work); + unsigned int status, unmask; + + spin_lock_irq(&ams->intr_lock); + + status = readl(ams->base + AMS_ISR_0); + + /* Clear those bits which are not active anymore */ + unmask = (ams->current_masked_alarm ^ status) & ams->current_masked_alarm; + + /* Clear status of disabled alarm */ + unmask |= ams->intr_mask; + + ams->current_masked_alarm &= status; + + /* Also clear those which are masked out anyway */ + ams->current_masked_alarm &= ~ams->intr_mask; + + /* Clear the interrupts before we unmask them */ + writel(unmask, ams->base + AMS_ISR_0); + + ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK); + + spin_unlock_irq(&ams->intr_lock); + + /* If still pending some alarm re-trigger the timer */ + if (ams->current_masked_alarm) + schedule_delayed_work(&ams->ams_unmask_work, + msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS)); +} + +static irqreturn_t ams_irq(int irq, void *data) +{ + struct iio_dev *indio_dev = data; + struct ams *ams = iio_priv(indio_dev); + u32 isr0; + + spin_lock(&ams->intr_lock); + + isr0 = readl(ams->base + AMS_ISR_0); + + /* Only process alarms that are not masked */ + isr0 &= ~((ams->intr_mask & AMS_ISR0_ALARM_MASK) | ams->current_masked_alarm); + if (!isr0) { + spin_unlock(&ams->intr_lock); + return IRQ_NONE; + } + + /* Clear interrupt */ + writel(isr0, ams->base + AMS_ISR_0); + + /* Mask the alarm interrupts until cleared */ + ams->current_masked_alarm |= isr0; + ams_update_intrmask(ams, ~AMS_ALARM_MASK, ~AMS_ALARM_MASK); + + ams_handle_events(indio_dev, isr0); + + schedule_delayed_work(&ams->ams_unmask_work, + msecs_to_jiffies(AMS_UNMASK_TIMEOUT_MS)); + + spin_unlock(&ams->intr_lock); + + return IRQ_HANDLED; +} + +static const struct iio_event_spec ams_temp_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | BIT(IIO_EV_INFO_VALUE), + }, +}; + +static const struct iio_event_spec ams_voltage_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec ams_ps_channels[] = { + AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP), + AMS_PS_CHAN_TEMP(AMS_SEQ_TEMP_REMOTE, AMS_TEMP_REMOTE), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10), + AMS_PS_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS), +}; + +static const struct iio_chan_spec ams_pl_channels[] = { + AMS_PL_CHAN_TEMP(AMS_SEQ_TEMP, AMS_TEMP), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY1, AMS_SUPPLY1, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY2, AMS_SUPPLY2, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFP, AMS_VREFP, false), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VREFN, AMS_VREFN, false), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY3, AMS_SUPPLY3, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY4, AMS_SUPPLY4, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY5, AMS_SUPPLY5, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY6, AMS_SUPPLY6, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VCCAMS, AMS_VCCAMS, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_VP_VN, AMS_VP_VN, false), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY7, AMS_SUPPLY7, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY8, AMS_SUPPLY8, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY9, AMS_SUPPLY9, true), + AMS_PL_CHAN_VOLTAGE(AMS_SEQ_SUPPLY10, AMS_SUPPLY10, true), + AMS_PL_AUX_CHAN_VOLTAGE(0), + AMS_PL_AUX_CHAN_VOLTAGE(1), + AMS_PL_AUX_CHAN_VOLTAGE(2), + AMS_PL_AUX_CHAN_VOLTAGE(3), + AMS_PL_AUX_CHAN_VOLTAGE(4), + AMS_PL_AUX_CHAN_VOLTAGE(5), + AMS_PL_AUX_CHAN_VOLTAGE(6), + AMS_PL_AUX_CHAN_VOLTAGE(7), + AMS_PL_AUX_CHAN_VOLTAGE(8), + AMS_PL_AUX_CHAN_VOLTAGE(9), + AMS_PL_AUX_CHAN_VOLTAGE(10), + AMS_PL_AUX_CHAN_VOLTAGE(11), + AMS_PL_AUX_CHAN_VOLTAGE(12), + AMS_PL_AUX_CHAN_VOLTAGE(13), + AMS_PL_AUX_CHAN_VOLTAGE(14), + AMS_PL_AUX_CHAN_VOLTAGE(15), +}; + +static const struct iio_chan_spec ams_ctrl_channels[] = { + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSPLL, AMS_VCC_PSPLL0), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCC_PSBATT, AMS_VCC_PSPLL3), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCINT, AMS_VCCINT), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCBRAM, AMS_VCCBRAM), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_VCCAUX, AMS_VCCAUX), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_PSDDRPLL, AMS_PSDDRPLL), + AMS_CTRL_CHAN_VOLTAGE(AMS_SEQ_INTDDR, AMS_PSINTFPDDR), +}; + +static int ams_get_ext_chan(struct fwnode_handle *chan_node, + struct iio_chan_spec *channels, int num_channels) +{ + struct iio_chan_spec *chan; + struct fwnode_handle *child; + unsigned int reg, ext_chan; + int ret; + + fwnode_for_each_child_node(chan_node, child) { + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret || reg > AMS_PL_MAX_EXT_CHANNEL + 30) + continue; + + chan = &channels[num_channels]; + ext_chan = reg + AMS_PL_MAX_FIXED_CHANNEL - 30; + memcpy(chan, &ams_pl_channels[ext_chan], sizeof(*channels)); + + if (fwnode_property_read_bool(child, "xlnx,bipolar")) + chan->scan_type.sign = 's'; + + num_channels++; + } + + return num_channels; +} + +static void ams_iounmap_ps(void *data) +{ + struct ams *ams = data; + + iounmap(ams->ps_base); +} + +static void ams_iounmap_pl(void *data) +{ + struct ams *ams = data; + + iounmap(ams->pl_base); +} + +static int ams_init_module(struct iio_dev *indio_dev, + struct fwnode_handle *fwnode, + struct iio_chan_spec *channels) +{ + struct device *dev = indio_dev->dev.parent; + struct ams *ams = iio_priv(indio_dev); + int num_channels = 0; + int ret; + + if (fwnode_property_match_string(fwnode, "compatible", + "xlnx,zynqmp-ams-ps") == 0) { + ams->ps_base = fwnode_iomap(fwnode, 0); + if (!ams->ps_base) + return -ENXIO; + ret = devm_add_action_or_reset(dev, ams_iounmap_ps, ams); + if (ret < 0) + return ret; + + /* add PS channels to iio device channels */ + memcpy(channels, ams_ps_channels, sizeof(ams_ps_channels)); + } else if (fwnode_property_match_string(fwnode, "compatible", + "xlnx,zynqmp-ams-pl") == 0) { + ams->pl_base = fwnode_iomap(fwnode, 0); + if (!ams->pl_base) + return -ENXIO; + + ret = devm_add_action_or_reset(dev, ams_iounmap_pl, ams); + if (ret < 0) + return ret; + + /* Copy only first 10 fix channels */ + memcpy(channels, ams_pl_channels, AMS_PL_MAX_FIXED_CHANNEL * sizeof(*channels)); + num_channels += AMS_PL_MAX_FIXED_CHANNEL; + num_channels = ams_get_ext_chan(fwnode, channels, + num_channels); + } else if (fwnode_property_match_string(fwnode, "compatible", + "xlnx,zynqmp-ams") == 0) { + /* add AMS channels to iio device channels */ + memcpy(channels, ams_ctrl_channels, sizeof(ams_ctrl_channels)); + num_channels += ARRAY_SIZE(ams_ctrl_channels); + } else { + return -EINVAL; + } + + return num_channels; +} + +static int ams_parse_firmware(struct iio_dev *indio_dev) +{ + struct ams *ams = iio_priv(indio_dev); + struct iio_chan_spec *ams_channels, *dev_channels; + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *child = NULL; + struct fwnode_handle *fwnode = dev_fwnode(dev); + size_t ams_size, dev_size; + int ret, ch_cnt = 0, i, rising_off, falling_off; + unsigned int num_channels = 0; + + ams_size = ARRAY_SIZE(ams_ps_channels) + ARRAY_SIZE(ams_pl_channels) + + ARRAY_SIZE(ams_ctrl_channels); + + /* Initialize buffer for channel specification */ + ams_channels = devm_kcalloc(dev, ams_size, sizeof(*ams_channels), GFP_KERNEL); + if (!ams_channels) + return -ENOMEM; + + if (fwnode_device_is_available(fwnode)) { + ret = ams_init_module(indio_dev, fwnode, ams_channels); + if (ret < 0) + return ret; + + num_channels += ret; + } + + fwnode_for_each_child_node(fwnode, child) { + if (fwnode_device_is_available(child)) { + ret = ams_init_module(indio_dev, child, ams_channels + num_channels); + if (ret < 0) { + fwnode_handle_put(child); + return ret; + } + + num_channels += ret; + } + } + + for (i = 0; i < num_channels; i++) { + ams_channels[i].channel = ch_cnt++; + + if (ams_channels[i].scan_index < AMS_CTRL_SEQ_BASE) { + /* set threshold to max and min for each channel */ + falling_off = + ams_get_alarm_offset(ams_channels[i].scan_index, + IIO_EV_DIR_FALLING); + rising_off = + ams_get_alarm_offset(ams_channels[i].scan_index, + IIO_EV_DIR_RISING); + if (ams_channels[i].scan_index >= AMS_PS_SEQ_MAX) { + writel(AMS_ALARM_THR_MIN, + ams->pl_base + falling_off); + writel(AMS_ALARM_THR_MAX, + ams->pl_base + rising_off); + } else { + writel(AMS_ALARM_THR_MIN, + ams->ps_base + falling_off); + writel(AMS_ALARM_THR_MAX, + ams->ps_base + rising_off); + } + } + } + + dev_size = array_size(sizeof(*dev_channels), num_channels); + if (dev_size == SIZE_MAX) + return -ENOMEM; + + dev_channels = devm_krealloc(dev, ams_channels, dev_size, GFP_KERNEL); + if (!dev_channels) + ret = -ENOMEM; + + indio_dev->channels = dev_channels; + indio_dev->num_channels = num_channels; + + return 0; +} + +static const struct iio_info iio_ams_info = { + .read_raw = &ams_read_raw, + .read_event_config = &ams_read_event_config, + .write_event_config = &ams_write_event_config, + .read_event_value = &ams_read_event_value, + .write_event_value = &ams_write_event_value, +}; + +static const struct of_device_id ams_of_match_table[] = { + { .compatible = "xlnx,zynqmp-ams" }, + { } +}; +MODULE_DEVICE_TABLE(of, ams_of_match_table); + +static void ams_clk_disable_unprepare(void *data) +{ + clk_disable_unprepare(data); +} + +static void ams_cancel_delayed_work(void *data) +{ + cancel_delayed_work(data); +} + +static int ams_probe(struct platform_device *pdev) +{ + struct iio_dev *indio_dev; + struct ams *ams; + int ret; + int irq; + + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*ams)); + if (!indio_dev) + return -ENOMEM; + + ams = iio_priv(indio_dev); + mutex_init(&ams->lock); + spin_lock_init(&ams->intr_lock); + + indio_dev->name = "xilinx-ams"; + + indio_dev->info = &iio_ams_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ams->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ams->base)) + return PTR_ERR(ams->base); + + ams->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(ams->clk)) + return PTR_ERR(ams->clk); + + ret = clk_prepare_enable(ams->clk); + if (ret < 0) + return ret; + + ret = devm_add_action_or_reset(&pdev->dev, ams_clk_disable_unprepare, ams->clk); + if (ret < 0) + return ret; + + INIT_DELAYED_WORK(&ams->ams_unmask_work, ams_unmask_worker); + ret = devm_add_action_or_reset(&pdev->dev, ams_cancel_delayed_work, + &ams->ams_unmask_work); + if (ret < 0) + return ret; + + ret = ams_parse_firmware(indio_dev); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failure in parsing DT\n"); + + ret = ams_init_device(ams); + if (ret) + return dev_err_probe(&pdev->dev, ret, "failed to initialize AMS\n"); + + ams_enable_channel_sequence(indio_dev); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return ret; + + ret = devm_request_irq(&pdev->dev, irq, &ams_irq, 0, "ams-irq", + indio_dev); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to register interrupt\n"); + + platform_set_drvdata(pdev, indio_dev); + + return devm_iio_device_register(&pdev->dev, indio_dev); +} + +static int __maybe_unused ams_suspend(struct device *dev) +{ + struct ams *ams = iio_priv(dev_get_drvdata(dev)); + + clk_disable_unprepare(ams->clk); + + return 0; +} + +static int __maybe_unused ams_resume(struct device *dev) +{ + struct ams *ams = iio_priv(dev_get_drvdata(dev)); + + return clk_prepare_enable(ams->clk); +} + +static SIMPLE_DEV_PM_OPS(ams_pm_ops, ams_suspend, ams_resume); + +static struct platform_driver ams_driver = { + .probe = ams_probe, + .driver = { + .name = "xilinx-ams", + .pm = &ams_pm_ops, + .of_match_table = ams_of_match_table, + }, +}; +module_platform_driver(ams_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Xilinx, Inc."); -- cgit v1.2.3 From 39dd2d1e251d3ad7910aadac1d723e93e201dafa Mon Sep 17 00:00:00 2001 From: Anand Ashok Dumbre Date: Fri, 3 Dec 2021 21:23:57 +0000 Subject: dt-bindings: iio: adc: Add Xilinx AMS binding documentation Xilinx AMS have several ADC channels that can be used for measurement of different voltages and temperatures. Document the same in the bindings. Signed-off-by: Anand Ashok Dumbre Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211203212358.31444-5-anand.ashok.dumbre@xilinx.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/xlnx,zynqmp-ams.yaml | 227 +++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml b/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml new file mode 100644 index 000000000000..87992db389b2 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml @@ -0,0 +1,227 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/xlnx,zynqmp-ams.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Xilinx Zynq Ultrascale AMS controller + +maintainers: + - Anand Ashok Dumbre + +description: | + The AMS (Analog Monitoring System) includes an ADC as well as on-chip sensors + that can be used to sample external voltages and monitor on-die operating + conditions, such as temperature and supply voltage levels. + The AMS has two SYSMON blocks which are PL (Programmable Logic) SYSMON and + PS (Processing System) SYSMON. + All designs should have AMS registers, but PS and PL are optional. The + AMS controller can work with only PS, only PL and both PS and PL + configurations. Please specify registers according to your design. Devicetree + should always have AMS module property. Providing PS & PL module is optional. + + AMS Channel Details + ``````````````````` + Sysmon Block |Channel| Details |Measurement + |Number | |Type + --------------------------------------------------------------------------------------------------------- + AMS CTRL |0 |System PLLs voltage measurement, VCC_PSPLL. |Voltage + |1 |Battery voltage measurement, VCC_PSBATT. |Voltage + |2 |PL Internal voltage measurement, VCCINT. |Voltage + |3 |Block RAM voltage measurement, VCCBRAM. |Voltage + |4 |PL Aux voltage measurement, VCCAUX. |Voltage + |5 |Voltage measurement for six DDR I/O PLLs, VCC_PSDDR_PLL. |Voltage + |6 |VCC_PSINTFP_DDR voltage measurement. |Voltage + --------------------------------------------------------------------------------------------------------- + PS Sysmon |7 |LPD temperature measurement. |Temperature + |8 |FPD temperature measurement (REMOTE). |Temperature + |9 |VCC PS LPD voltage measurement (supply1). |Voltage + |10 |VCC PS FPD voltage measurement (supply2). |Voltage + |11 |PS Aux voltage reference (supply3). |Voltage + |12 |DDR I/O VCC voltage measurement. |Voltage + |13 |PS IO Bank 503 voltage measurement (supply5). |Voltage + |14 |PS IO Bank 500 voltage measurement (supply6). |Voltage + |15 |VCCO_PSIO1 voltage measurement. |Voltage + |16 |VCCO_PSIO2 voltage measurement. |Voltage + |17 |VCC_PS_GTR voltage measurement (VPS_MGTRAVCC). |Voltage + |18 |VTT_PS_GTR voltage measurement (VPS_MGTRAVTT). |Voltage + |19 |VCC_PSADC voltage measurement. |Voltage + --------------------------------------------------------------------------------------------------------- + PL Sysmon |20 |PL temperature measurement. |Temperature + |21 |PL Internal voltage measurement, VCCINT. |Voltage + |22 |PL Auxiliary voltage measurement, VCCAUX. |Voltage + |23 |ADC Reference P+ voltage measurement. |Voltage + |24 |ADC Reference N- voltage measurement. |Voltage + |25 |PL Block RAM voltage measurement, VCCBRAM. |Voltage + |26 |LPD Internal voltage measurement, VCC_PSINTLP (supply4). |Voltage + |27 |FPD Internal voltage measurement, VCC_PSINTFP (supply5). |Voltage + |28 |PS Auxiliary voltage measurement (supply6). |Voltage + |29 |PL VCCADC voltage measurement (vccams). |Voltage + |30 |Differential analog input signal voltage measurment. |Voltage + |31 |VUser0 voltage measurement (supply7). |Voltage + |32 |VUser1 voltage measurement (supply8). |Voltage + |33 |VUser2 voltage measurement (supply9). |Voltage + |34 |VUser3 voltage measurement (supply10). |Voltage + |35 |Auxiliary ch 0 voltage measurement (VAux0). |Voltage + |36 |Auxiliary ch 1 voltage measurement (VAux1). |Voltage + |37 |Auxiliary ch 2 voltage measurement (VAux2). |Voltage + |38 |Auxiliary ch 3 voltage measurement (VAux3). |Voltage + |39 |Auxiliary ch 4 voltage measurement (VAux4). |Voltage + |40 |Auxiliary ch 5 voltage measurement (VAux5). |Voltage + |41 |Auxiliary ch 6 voltage measurement (VAux6). |Voltage + |42 |Auxiliary ch 7 voltage measurement (VAux7). |Voltage + |43 |Auxiliary ch 8 voltage measurement (VAux8). |Voltage + |44 |Auxiliary ch 9 voltage measurement (VAux9). |Voltage + |45 |Auxiliary ch 10 voltage measurement (VAux10). |Voltage + |46 |Auxiliary ch 11 voltage measurement (VAux11). |Voltage + |47 |Auxiliary ch 12 voltage measurement (VAux12). |Voltage + |48 |Auxiliary ch 13 voltage measurement (VAux13). |Voltage + |49 |Auxiliary ch 14 voltage measurement (VAux14). |Voltage + |50 |Auxiliary ch 15 voltage measurement (VAux15). |Voltage + -------------------------------------------------------------------------------------------------------- + +properties: + compatible: + enum: + - xlnx,zynqmp-ams + + interrupts: + maxItems: 1 + + reg: + description: AMS Controller register space + maxItems: 1 + + ranges: + description: + Maps the child address space for PS and/or PL. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + '#io-channel-cells': + const: 1 + + ams-ps@0: + type: object + description: | + PS (Processing System) SYSMON is memory mapped to PS. This block has + built-in alarm generation logic that is used to interrupt the processor + based on condition set. + + properties: + compatible: + enum: + - xlnx,zynqmp-ams-ps + + reg: + description: Register Space for PS-SYSMON + maxItems: 1 + + required: + - compatible + - reg + + additionalProperties: false + + ams-pl@400: + type: object + description: + PL-SYSMON is capable of monitoring off chip voltage and temperature. + PL-SYSMON block has DRP, JTAG and I2C interface to enable monitoring + from external master. Out of this interface currently only DRP is + supported. This block has alarm generation logic that is used to + interrupt the processor based on condition set. + + properties: + compatible: + items: + - enum: + - xlnx,zynqmp-ams-pl + + reg: + description: Register Space for PL-SYSMON. + maxItems: 1 + + '#address-cells': + const: 1 + + '#size-cells': + const: 0 + + patternProperties: + "^channel@([2-4][0-9]|50)$": + type: object + description: + Describes the external channels connected. + + properties: + reg: + description: + Pair of pins the channel is connected to. This value is + same as Channel Number for a particular channel. + minimum: 20 + maximum: 50 + + xlnx,bipolar: + $ref: /schemas/types.yaml#/definitions/flag + type: boolean + description: + If the set channel is used in bipolar mode. + + required: + - reg + + additionalProperties: false + +required: + - compatible + - reg + - ranges + +additionalProperties: false + +examples: + - | + bus { + #address-cells = <2>; + #size-cells = <2>; + + xilinx_ams: ams@ffa50000 { + compatible = "xlnx,zynqmp-ams"; + interrupt-parent = <&gic>; + interrupts = <0 56 4>; + reg = <0x0 0xffa50000 0x0 0x800>; + #address-cells = <1>; + #size-cells = <1>; + #io-channel-cells = <1>; + ranges = <0 0 0xffa50800 0x800>; + + ams_ps: ams-ps@0 { + compatible = "xlnx,zynqmp-ams-ps"; + reg = <0 0x400>; + }; + + ams_pl: ams-pl@400 { + compatible = "xlnx,zynqmp-ams-pl"; + reg = <0x400 0x400>; + #address-cells = <1>; + #size-cells = <0>; + channel@30 { + reg = <30>; + xlnx,bipolar; + }; + channel@31 { + reg = <31>; + }; + channel@38 { + reg = <38>; + xlnx,bipolar; + }; + }; + }; + }; -- cgit v1.2.3 From bfcacdd64df8df91311449642da70a8512431fa9 Mon Sep 17 00:00:00 2001 From: Anand Ashok Dumbre Date: Fri, 3 Dec 2021 21:23:58 +0000 Subject: MAINTAINERS: Add maintainer for xilinx-ams Add maintaner entry for xilinx-ams driver. Signed-off-by: Anand Ashok Dumbre Link: https://lore.kernel.org/r/20211203212358.31444-6-anand.ashok.dumbre@xilinx.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 57fb0f19ee08..2c043712600e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20871,6 +20871,13 @@ F: fs/xfs/ F: include/uapi/linux/dqblk_xfs.h F: include/uapi/linux/fsmap.h +XILINX AMS DRIVER +M: Anand Ashok Dumbre +L: linux-iio@vger.kernel.org +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/xlnx,zynqmp-ams.yaml +F: drivers/iio/adc/xilinx-ams.c + XILINX AXI ETHERNET DRIVER M: Radhey Shyam Pandey S: Maintained -- cgit v1.2.3 From f4a73a97accf5635815de148cf077fa6d076812d Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Mon, 20 Dec 2021 16:47:26 +0000 Subject: iio:addac:ad74413r: Fix uninitialized ret in a path that won't be hit. I don't believe it's possible to hit this, because we drop out of __iio_update_buffers() earlier in the event of an empty list. However, that is not visible to the compiler so lets return an error if we do hit the loop with an empty bitmask. Fixes: 5d97d9e9a703 ("iio: addac: ad74413r: fix off by one in ad74413r_parse_channel_config()") Signed-off-by: Jonathan Cameron Cc: Cosmin Tanislav Link: https://lore.kernel.org/r/20211220164726.3136307-1-jic23@kernel.org --- drivers/iio/addac/ad74413r.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 289d254943e1..5271073bb74e 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -843,7 +843,7 @@ static int ad74413r_update_scan_mode(struct iio_dev *indio_dev, u8 *rx_buf = &st->adc_samples_buf.rx_buf[-1 * AD74413R_FRAME_SIZE]; u8 *tx_buf = st->adc_samples_tx_buf; unsigned int channel; - int ret; + int ret = -EINVAL; mutex_lock(&st->lock); -- cgit v1.2.3 From c9791a94384af07592d29504004d2255dbaf8663 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Sun, 5 Dec 2021 17:27:28 +0000 Subject: iio: adc: ti-adc081c: Partial revert of removal of ACPI IDs Unfortuanately a non standards compliant ACPI ID is known to be in the wild on some AAEON boards. Partly revert the removal of these IDs so that ADC081C will again work + add a comment to that affect for future reference. Whilst here use generic firmware properties rather than the ACPI specific handling previously found in this driver. Reported-by: Kunyang Fan Fixes: c458b7ca3fd0 ("iio:adc:ti-adc081c: Drop ACPI ids that seem very unlikely to be official.") Signed-off-by: Jonathan Cameron Cc: Andy Shevchenko Tested-by: Kunyang Fan #UP-extremei11 Link: https://lore.kernel.org/r/20211205172728.2826512-1-jic23@kernel.org Cc: --- drivers/iio/adc/ti-adc081c.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index 16fc608db36a..bd48b073e720 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -156,13 +157,16 @@ static int adc081c_probe(struct i2c_client *client, { struct iio_dev *iio; struct adc081c *adc; - struct adcxx1c_model *model; + const struct adcxx1c_model *model; int err; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) return -EOPNOTSUPP; - model = &adcxx1c_models[id->driver_data]; + if (dev_fwnode(&client->dev)) + model = device_get_match_data(&client->dev); + else + model = &adcxx1c_models[id->driver_data]; iio = devm_iio_device_alloc(&client->dev, sizeof(*adc)); if (!iio) @@ -210,10 +214,17 @@ static const struct i2c_device_id adc081c_id[] = { }; MODULE_DEVICE_TABLE(i2c, adc081c_id); +static const struct acpi_device_id adc081c_acpi_match[] = { + /* Used on some AAEON boards */ + { "ADC081C", (kernel_ulong_t)&adcxx1c_models[ADC081C] }, + { } +}; +MODULE_DEVICE_TABLE(acpi, adc081c_acpi_match); + static const struct of_device_id adc081c_of_match[] = { - { .compatible = "ti,adc081c" }, - { .compatible = "ti,adc101c" }, - { .compatible = "ti,adc121c" }, + { .compatible = "ti,adc081c", .data = &adcxx1c_models[ADC081C] }, + { .compatible = "ti,adc101c", .data = &adcxx1c_models[ADC101C] }, + { .compatible = "ti,adc121c", .data = &adcxx1c_models[ADC121C] }, { } }; MODULE_DEVICE_TABLE(of, adc081c_of_match); @@ -222,6 +233,7 @@ static struct i2c_driver adc081c_driver = { .driver = { .name = "adc081c", .of_match_table = adc081c_of_match, + .acpi_match_table = adc081c_acpi_match, }, .probe = adc081c_probe, .id_table = adc081c_id, -- cgit v1.2.3 From ee4736e50ba261944ddae75469b3eb47a9e2847d Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 20 Dec 2021 12:19:00 +0100 Subject: gnss: add USB support Add a generic driver for GNSS receivers with a USB interface with two bulk endpoints. The driver currently assumes that the device protocol is NMEA (only) but this can be generalised later as needed. Link: https://lore.kernel.org/r/20211220111901.23206-2-johan@kernel.org Reviewed-by: Greg Kroah-Hartman Tested-by: Marc Ferland Signed-off-by: Johan Hovold --- drivers/gnss/Kconfig | 11 +++ drivers/gnss/Makefile | 3 + drivers/gnss/usb.c | 213 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 drivers/gnss/usb.c diff --git a/drivers/gnss/Kconfig b/drivers/gnss/Kconfig index bd12e3d57baa..d7fe265c2869 100644 --- a/drivers/gnss/Kconfig +++ b/drivers/gnss/Kconfig @@ -54,4 +54,15 @@ config GNSS_UBX_SERIAL If unsure, say N. +config GNSS_USB + tristate "USB GNSS receiver support" + depends on USB + help + Say Y here if you have a GNSS receiver which uses a USB interface. + + To compile this driver as a module, choose M here: the module will + be called gnss-usb. + + If unsure, say N. + endif # GNSS diff --git a/drivers/gnss/Makefile b/drivers/gnss/Makefile index 451f11401ecc..bb2cbada3435 100644 --- a/drivers/gnss/Makefile +++ b/drivers/gnss/Makefile @@ -17,3 +17,6 @@ gnss-sirf-y := sirf.o obj-$(CONFIG_GNSS_UBX_SERIAL) += gnss-ubx.o gnss-ubx-y := ubx.o + +obj-$(CONFIG_GNSS_USB) += gnss-usb.o +gnss-usb-y := usb.o diff --git a/drivers/gnss/usb.c b/drivers/gnss/usb.c new file mode 100644 index 000000000000..fb1dd04fecb6 --- /dev/null +++ b/drivers/gnss/usb.c @@ -0,0 +1,213 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Generic USB GNSS receiver driver + * + * Copyright (C) 2021 Johan Hovold + */ + +#include +#include +#include +#include +#include +#include +#include + +#define GNSS_USB_READ_BUF_LEN 512 +#define GNSS_USB_WRITE_TIMEOUT 1000 + +static const struct usb_device_id gnss_usb_id_table[] = { + { } +}; +MODULE_DEVICE_TABLE(usb, gnss_usb_id_table); + +struct gnss_usb { + struct usb_device *udev; + struct usb_interface *intf; + struct gnss_device *gdev; + struct urb *read_urb; + unsigned int write_pipe; +}; + +static void gnss_usb_rx_complete(struct urb *urb) +{ + struct gnss_usb *gusb = urb->context; + struct gnss_device *gdev = gusb->gdev; + int status = urb->status; + int len; + int ret; + + switch (status) { + case 0: + break; + case -ENOENT: + case -ECONNRESET: + case -ESHUTDOWN: + dev_dbg(&gdev->dev, "urb stopped: %d\n", status); + return; + case -EPIPE: + dev_err(&gdev->dev, "urb stopped: %d\n", status); + return; + default: + dev_dbg(&gdev->dev, "nonzero urb status: %d\n", status); + goto resubmit; + } + + len = urb->actual_length; + if (len == 0) + goto resubmit; + + ret = gnss_insert_raw(gdev, urb->transfer_buffer, len); + if (ret < len) + dev_dbg(&gdev->dev, "dropped %d bytes\n", len - ret); +resubmit: + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret && ret != -EPERM && ret != -ENODEV) + dev_err(&gdev->dev, "failed to resubmit urb: %d\n", ret); +} + +static int gnss_usb_open(struct gnss_device *gdev) +{ + struct gnss_usb *gusb = gnss_get_drvdata(gdev); + int ret; + + ret = usb_submit_urb(gusb->read_urb, GFP_KERNEL); + if (ret) { + if (ret != -EPERM && ret != -ENODEV) + dev_err(&gdev->dev, "failed to submit urb: %d\n", ret); + return ret; + } + + return 0; +} + +static void gnss_usb_close(struct gnss_device *gdev) +{ + struct gnss_usb *gusb = gnss_get_drvdata(gdev); + + usb_kill_urb(gusb->read_urb); +} + +static int gnss_usb_write_raw(struct gnss_device *gdev, + const unsigned char *buf, size_t count) +{ + struct gnss_usb *gusb = gnss_get_drvdata(gdev); + void *tbuf; + int ret; + + tbuf = kmemdup(buf, count, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + ret = usb_bulk_msg(gusb->udev, gusb->write_pipe, tbuf, count, NULL, + GNSS_USB_WRITE_TIMEOUT); + kfree(tbuf); + if (ret) + return ret; + + return count; +} + +static const struct gnss_operations gnss_usb_gnss_ops = { + .open = gnss_usb_open, + .close = gnss_usb_close, + .write_raw = gnss_usb_write_raw, +}; + +static int gnss_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct usb_endpoint_descriptor *in, *out; + struct gnss_device *gdev; + struct gnss_usb *gusb; + struct urb *urb; + size_t buf_len; + void *buf; + int ret; + + ret = usb_find_common_endpoints(intf->cur_altsetting, &in, &out, NULL, + NULL); + if (ret) + return ret; + + gusb = kzalloc(sizeof(*gusb), GFP_KERNEL); + if (!gusb) + return -ENOMEM; + + gdev = gnss_allocate_device(&intf->dev); + if (!gdev) { + ret = -ENOMEM; + goto err_free_gusb; + } + + gdev->ops = &gnss_usb_gnss_ops; + gdev->type = GNSS_TYPE_NMEA; + gnss_set_drvdata(gdev, gusb); + + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + ret = -ENOMEM; + goto err_put_gdev; + } + + buf_len = max(usb_endpoint_maxp(in), GNSS_USB_READ_BUF_LEN); + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) { + ret = -ENOMEM; + goto err_free_urb; + } + + usb_fill_bulk_urb(urb, udev, + usb_rcvbulkpipe(udev, usb_endpoint_num(in)), + buf, buf_len, gnss_usb_rx_complete, gusb); + + gusb->intf = intf; + gusb->udev = udev; + gusb->gdev = gdev; + gusb->read_urb = urb; + gusb->write_pipe = usb_sndbulkpipe(udev, usb_endpoint_num(out)); + + ret = gnss_register_device(gdev); + if (ret) + goto err_free_buf; + + usb_set_intfdata(intf, gusb); + + return 0; + +err_free_buf: + kfree(buf); +err_free_urb: + usb_free_urb(urb); +err_put_gdev: + gnss_put_device(gdev); +err_free_gusb: + kfree(gusb); + + return ret; +} + +static void gnss_usb_disconnect(struct usb_interface *intf) +{ + struct gnss_usb *gusb = usb_get_intfdata(intf); + + gnss_deregister_device(gusb->gdev); + + kfree(gusb->read_urb->transfer_buffer); + usb_free_urb(gusb->read_urb); + gnss_put_device(gusb->gdev); + kfree(gusb); +} + +static struct usb_driver gnss_usb_driver = { + .name = "gnss-usb", + .probe = gnss_usb_probe, + .disconnect = gnss_usb_disconnect, + .id_table = gnss_usb_id_table, +}; +module_usb_driver(gnss_usb_driver); + +MODULE_AUTHOR("Johan Hovold "); +MODULE_DESCRIPTION("Generic USB GNSS receiver driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 547d2167c5c3cd879ed5e86e88cfa7daaf1228a5 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Mon, 20 Dec 2021 12:19:01 +0100 Subject: gnss: usb: add support for Sierra Wireless XM1210 Add support for the USB interface of the Sierra Wireless XM1210 receiver. Note that the device only supports NMEA. Bus 002 Device 003: ID 1199:b000 Sierra Wireless, Inc. Sierra Wireless_GNSS Device Descriptor: bLength 18 bDescriptorType 1 bcdUSB 1.00 bDeviceClass 0 bDeviceSubClass 0 bDeviceProtocol 0 bMaxPacketSize0 64 idVendor 0x1199 Sierra Wireless, Inc. idProduct 0xb000 bcdDevice 0.01 iManufacturer 1 Sierra-wireless iProduct 2 Sierra Wireless_GNSS iSerial 0 bNumConfigurations 1 Configuration Descriptor: bLength 9 bDescriptorType 2 wTotalLength 0x0020 bNumInterfaces 1 bConfigurationValue 1 iConfiguration 0 bmAttributes 0xc0 Self Powered MaxPower 50mA Interface Descriptor: bLength 9 bDescriptorType 4 bInterfaceNumber 0 bAlternateSetting 0 bNumEndpoints 2 bInterfaceClass 0 bInterfaceSubClass 0 bInterfaceProtocol 0 iInterface 0 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x81 EP 1 IN bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 255 Endpoint Descriptor: bLength 7 bDescriptorType 5 bEndpointAddress 0x01 EP 1 OUT bmAttributes 2 Transfer Type Bulk Synch Type None Usage Type Data wMaxPacketSize 0x0040 1x 64 bytes bInterval 255 can't get debug descriptor: Resource temporarily unavailable Device Status: 0x0001 Self Powered Reported-by: Marc Ferland Link: https://lore.kernel.org/r/20211027200223.72701-1-ferlandm@amotus.ca Link: https://lore.kernel.org/r/20211220111901.23206-3-johan@kernel.org Reviewed-by: Greg Kroah-Hartman Tested-by: Marc Ferland Signed-off-by: Johan Hovold --- drivers/gnss/usb.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gnss/usb.c b/drivers/gnss/usb.c index fb1dd04fecb6..028ce56b20ea 100644 --- a/drivers/gnss/usb.c +++ b/drivers/gnss/usb.c @@ -17,6 +17,7 @@ #define GNSS_USB_WRITE_TIMEOUT 1000 static const struct usb_device_id gnss_usb_id_table[] = { + { USB_DEVICE(0x1199, 0xb000) }, /* Sierra Wireless XM1210 */ { } }; MODULE_DEVICE_TABLE(usb, gnss_usb_id_table); -- cgit v1.2.3 From 8eff5b99042dd2fe6494e82ab1dc39bcdd5b976c Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 15 Dec 2021 16:02:46 -0500 Subject: dt-bindings: phy: phy-rockchip-inno-usb2: add rk3568 documentation The rk3568 usb2phy node is a standalone node with a single muxed interrupt. Add documentation for it to phy-rockchip-inno-usb2. Signed-off-by: Peter Geis Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211215210252.120923-3-pgwipeout@gmail.com Signed-off-by: Vinod Koul --- .../bindings/phy/phy-rockchip-inno-usb2.yaml | 44 ++++++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml index 5bebd86bf8b6..4b75289735eb 100644 --- a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml +++ b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.yaml @@ -18,6 +18,7 @@ properties: - rockchip,rk3328-usb2phy - rockchip,rk3366-usb2phy - rockchip,rk3399-usb2phy + - rockchip,rk3568-usb2phy - rockchip,rv1108-usb2phy reg: @@ -50,6 +51,10 @@ properties: description: Phandle to the extcon device providing the cable state for the otg phy. + interrupts: + description: Muxed interrupt for both ports + maxItems: 1 + rockchip,usbgrf: $ref: /schemas/types.yaml#/definitions/phandle description: @@ -67,6 +72,7 @@ properties: interrupts: description: host linestate interrupt + maxItems: 1 interrupt-names: const: linestate @@ -78,8 +84,6 @@ properties: required: - "#phy-cells" - - interrupts - - interrupt-names otg-port: type: object @@ -109,8 +113,6 @@ properties: required: - "#phy-cells" - - interrupts - - interrupt-names required: - compatible @@ -120,6 +122,40 @@ required: - host-port - otg-port +allOf: + - if: + properties: + compatible: + contains: + const: rockchip,rk3568-usb2phy + + then: + properties: + host-port: + properties: + interrupts: false + + otg-port: + properties: + interrupts: false + + required: + - interrupts + + else: + properties: + interrupts: false + + host-port: + required: + - interrupts + - interrupt-names + + otg-port: + required: + - interrupts + - interrupt-names + additionalProperties: false examples: -- cgit v1.2.3 From 9c19c531dc98d7ba49b44802a607042e763ebe21 Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 15 Dec 2021 16:02:47 -0500 Subject: phy: phy-rockchip-inno-usb2: support #address_cells = 2 New Rockchip devices have the usb phy nodes as standalone devices. These nodes have register nodes with #address_cells = 2, but only use 32 bit addresses. Adjust the driver to check if the returned address is "0", and adjust the index in that case. Signed-off-by: Peter Geis Tested-by: Michael Riesch Link: https://lore.kernel.org/r/20211215210252.120923-4-pgwipeout@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 9f95b587e2c0..fac390e1f8de 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1090,12 +1090,21 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) rphy->usbgrf = NULL; } - if (of_property_read_u32(np, "reg", ®)) { + if (of_property_read_u32_index(np, "reg", 0, ®)) { dev_err(dev, "the reg property is not assigned in %pOFn node\n", np); return -EINVAL; } + /* support address_cells=2 */ + if (reg == 0) { + if (of_property_read_u32_index(np, "reg", 1, ®)) { + dev_err(dev, "the reg property is not assigned in %pOFn node\n", + np); + return -EINVAL; + } + } + rphy->dev = dev; phy_cfgs = match->data; rphy->chg_state = USB_CHG_STATE_UNDEFINED; -- cgit v1.2.3 From e6915e1acca57bc4fdb61dccd5cc2e49f72ef743 Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 15 Dec 2021 16:02:48 -0500 Subject: phy: phy-rockchip-inno-usb2: support standalone phy nodes New Rockchip devices have the usb2 phy devices as standalone nodes instead of children of the grf node. Allow the driver to find the grf node from a phandle. Signed-off-by: Peter Geis Tested-by: Michael Riesch Link: https://lore.kernel.org/r/20211215210252.120923-5-pgwipeout@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index fac390e1f8de..1cdd9ae0a230 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1073,12 +1073,19 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) return -EINVAL; } - if (!dev->parent || !dev->parent->of_node) - return -EINVAL; + if (!dev->parent || !dev->parent->of_node) { + rphy->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,usbgrf"); + if (IS_ERR(rphy->grf)) { + dev_err(dev, "failed to locate usbgrf\n"); + return PTR_ERR(rphy->grf); + } + } - rphy->grf = syscon_node_to_regmap(dev->parent->of_node); - if (IS_ERR(rphy->grf)) - return PTR_ERR(rphy->grf); + else { + rphy->grf = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(rphy->grf)) + return PTR_ERR(rphy->grf); + } if (of_device_is_compatible(np, "rockchip,rv1108-usb2phy")) { rphy->usbgrf = -- cgit v1.2.3 From ed2b5a8e6b98d042b323afbe177a5dc618921b31 Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 15 Dec 2021 16:02:49 -0500 Subject: phy: phy-rockchip-inno-usb2: support muxed interrupts The rk3568 usb2phy has a single muxed interrupt that handles all interrupts. Allow the driver to plug in only a single interrupt as necessary. Signed-off-by: Peter Geis Tested-by: Michael Riesch Link: https://lore.kernel.org/r/20211215210252.120923-6-pgwipeout@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 168 ++++++++++++++++++-------- 1 file changed, 119 insertions(+), 49 deletions(-) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 1cdd9ae0a230..17098a63d95e 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -204,6 +204,7 @@ struct rockchip_usb2phy_port { * @dcd_retries: The retry count used to track Data contact * detection process. * @edev: extcon device for notification registration + * @irq: muxed interrupt for single irq configuration * @phy_cfg: phy register configuration, assigned by driver data. * @ports: phy port instance. */ @@ -218,6 +219,7 @@ struct rockchip_usb2phy { enum power_supply_type chg_type; u8 dcd_retries; struct extcon_dev *edev; + int irq; const struct rockchip_usb2phy_cfg *phy_cfg; struct rockchip_usb2phy_port ports[USB2PHY_NUM_PORTS]; }; @@ -926,6 +928,102 @@ static irqreturn_t rockchip_usb2phy_otg_mux_irq(int irq, void *data) return IRQ_NONE; } +static irqreturn_t rockchip_usb2phy_irq(int irq, void *data) +{ + struct rockchip_usb2phy *rphy = data; + struct rockchip_usb2phy_port *rport; + irqreturn_t ret = IRQ_NONE; + unsigned int index; + + for (index = 0; index < rphy->phy_cfg->num_ports; index++) { + rport = &rphy->ports[index]; + if (!rport->phy) + continue; + + /* Handle linestate irq for both otg port and host port */ + ret = rockchip_usb2phy_linestate_irq(irq, rport); + } + + return ret; +} + +static int rockchip_usb2phy_port_irq_init(struct rockchip_usb2phy *rphy, + struct rockchip_usb2phy_port *rport, + struct device_node *child_np) +{ + int ret; + + /* + * If the usb2 phy used combined irq for otg and host port, + * don't need to init otg and host port irq separately. + */ + if (rphy->irq > 0) + return 0; + + switch (rport->port_id) { + case USB2PHY_PORT_HOST: + rport->ls_irq = of_irq_get_byname(child_np, "linestate"); + if (rport->ls_irq < 0) { + dev_err(rphy->dev, "no linestate irq provided\n"); + return rport->ls_irq; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, + rockchip_usb2phy_linestate_irq, + IRQF_ONESHOT, + "rockchip_usb2phy", rport); + if (ret) { + dev_err(rphy->dev, "failed to request linestate irq handle\n"); + return ret; + } + break; + case USB2PHY_PORT_OTG: + /* + * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate + * interrupts muxed together, so probe the otg-mux interrupt first, + * if not found, then look for the regular interrupts one by one. + */ + rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux"); + if (rport->otg_mux_irq > 0) { + ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq, + NULL, + rockchip_usb2phy_otg_mux_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_otg", + rport); + if (ret) { + dev_err(rphy->dev, + "failed to request otg-mux irq handle\n"); + return ret; + } + } else { + rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); + if (rport->bvalid_irq < 0) { + dev_err(rphy->dev, "no vbus valid irq provided\n"); + ret = rport->bvalid_irq; + return ret; + } + + ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, + NULL, + rockchip_usb2phy_bvalid_irq, + IRQF_ONESHOT, + "rockchip_usb2phy_bvalid", + rport); + if (ret) { + dev_err(rphy->dev, + "failed to request otg-bvalid irq handle\n"); + return ret; + } + } + break; + default: + return -EINVAL; + } + + return 0; +} + static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, struct rockchip_usb2phy_port *rport, struct device_node *child_np) @@ -939,18 +1037,9 @@ static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy, mutex_init(&rport->mutex); INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work); - rport->ls_irq = of_irq_get_byname(child_np, "linestate"); - if (rport->ls_irq < 0) { - dev_err(rphy->dev, "no linestate irq provided\n"); - return rport->ls_irq; - } - - ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL, - rockchip_usb2phy_linestate_irq, - IRQF_ONESHOT, - "rockchip_usb2phy", rport); + ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np); if (ret) { - dev_err(rphy->dev, "failed to request linestate irq handle\n"); + dev_err(rphy->dev, "failed to setup host irq\n"); return ret; } @@ -999,44 +1088,10 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, INIT_DELAYED_WORK(&rport->chg_work, rockchip_chg_detect_work); INIT_DELAYED_WORK(&rport->otg_sm_work, rockchip_usb2phy_otg_sm_work); - /* - * Some SoCs use one interrupt with otg-id/otg-bvalid/linestate - * interrupts muxed together, so probe the otg-mux interrupt first, - * if not found, then look for the regular interrupts one by one. - */ - rport->otg_mux_irq = of_irq_get_byname(child_np, "otg-mux"); - if (rport->otg_mux_irq > 0) { - ret = devm_request_threaded_irq(rphy->dev, rport->otg_mux_irq, - NULL, - rockchip_usb2phy_otg_mux_irq, - IRQF_ONESHOT, - "rockchip_usb2phy_otg", - rport); - if (ret) { - dev_err(rphy->dev, - "failed to request otg-mux irq handle\n"); - goto out; - } - } else { - rport->bvalid_irq = of_irq_get_byname(child_np, "otg-bvalid"); - if (rport->bvalid_irq < 0) { - dev_err(rphy->dev, "no vbus valid irq provided\n"); - ret = rport->bvalid_irq; - goto out; - } - - ret = devm_request_threaded_irq(rphy->dev, rport->bvalid_irq, - NULL, - rockchip_usb2phy_bvalid_irq, - IRQF_ONESHOT, - "rockchip_usb2phy_bvalid", - rport); - if (ret) { - dev_err(rphy->dev, - "failed to request otg-bvalid irq handle\n"); - goto out; - } - } + ret = rockchip_usb2phy_port_irq_init(rphy, rport, child_np); + if (ret) { + dev_err(rphy->dev, "failed to init irq for host port\n"); + goto out; if (!IS_ERR(rphy->edev)) { rport->event_nb.notifier_call = rockchip_otg_event; @@ -1116,6 +1171,7 @@ static int rockchip_usb2phy_probe(struct platform_device *pdev) phy_cfgs = match->data; rphy->chg_state = USB_CHG_STATE_UNDEFINED; rphy->chg_type = POWER_SUPPLY_TYPE_UNKNOWN; + rphy->irq = platform_get_irq_optional(pdev, 0); platform_set_drvdata(pdev, rphy); ret = rockchip_usb2phy_extcon_register(rphy); @@ -1195,6 +1251,20 @@ next_child: } provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + if (rphy->irq > 0) { + ret = devm_request_threaded_irq(rphy->dev, rphy->irq, NULL, + rockchip_usb2phy_irq, + IRQF_ONESHOT, + "rockchip_usb2phy", + rphy); + if (ret) { + dev_err(rphy->dev, + "failed to request usb2phy irq handle\n"); + goto put_child; + } + } + return PTR_ERR_OR_ZERO(provider); put_child: -- cgit v1.2.3 From 42b559727a45d79c811f493515eb9b7e56016421 Mon Sep 17 00:00:00 2001 From: Peter Geis Date: Wed, 15 Dec 2021 16:02:50 -0500 Subject: phy: phy-rockchip-inno-usb2: add rk3568 support The rk3568 usb2phy is a standalone device with a single muxed interrupt. Add support for the registers to the usb2phy driver. Signed-off-by: Peter Geis Tested-by: Michael Riesch Link: https://lore.kernel.org/r/20211215210252.120923-7-pgwipeout@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/rockchip/phy-rockchip-inno-usb2.c | 65 +++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c index 17098a63d95e..eca77e44a4c1 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-usb2.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-usb2.c @@ -1092,6 +1092,7 @@ static int rockchip_usb2phy_otg_port_init(struct rockchip_usb2phy *rphy, if (ret) { dev_err(rphy->dev, "failed to init irq for host port\n"); goto out; + } if (!IS_ERR(rphy->edev)) { rport->event_nb.notifier_call = rockchip_otg_event; @@ -1503,6 +1504,69 @@ static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = { { /* sentinel */ } }; +static const struct rockchip_usb2phy_cfg rk3568_phy_cfgs[] = { + { + .reg = 0xfe8a0000, + .num_ports = 2, + .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0, 0x1d1 }, + .bvalid_det_en = { 0x0080, 2, 2, 0, 1 }, + .bvalid_det_st = { 0x0084, 2, 2, 0, 1 }, + .bvalid_det_clr = { 0x0088, 2, 2, 0, 1 }, + .utmi_avalid = { 0x00c0, 10, 10, 0, 1 }, + .utmi_bvalid = { 0x00c0, 9, 9, 0, 1 }, + }, + [USB2PHY_PORT_HOST] = { + /* Select suspend control from controller */ + .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d2 }, + .ls_det_en = { 0x0080, 1, 1, 0, 1 }, + .ls_det_st = { 0x0084, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, + .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } + } + }, + .chg_det = { + .opmode = { 0x0000, 3, 0, 5, 1 }, + .cp_det = { 0x00c0, 24, 24, 0, 1 }, + .dcp_det = { 0x00c0, 23, 23, 0, 1 }, + .dp_det = { 0x00c0, 25, 25, 0, 1 }, + .idm_sink_en = { 0x0008, 8, 8, 0, 1 }, + .idp_sink_en = { 0x0008, 7, 7, 0, 1 }, + .idp_src_en = { 0x0008, 9, 9, 0, 1 }, + .rdm_pdwn_en = { 0x0008, 10, 10, 0, 1 }, + .vdm_src_en = { 0x0008, 12, 12, 0, 1 }, + .vdp_src_en = { 0x0008, 11, 11, 0, 1 }, + }, + }, + { + .reg = 0xfe8b0000, + .num_ports = 2, + .clkout_ctl = { 0x0008, 4, 4, 1, 0 }, + .port_cfgs = { + [USB2PHY_PORT_OTG] = { + .phy_sus = { 0x0000, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 0, 0, 0, 1 }, + .ls_det_st = { 0x0084, 0, 0, 0, 1 }, + .ls_det_clr = { 0x0088, 0, 0, 0, 1 }, + .utmi_ls = { 0x00c0, 5, 4, 0, 1 }, + .utmi_hstdet = { 0x00c0, 7, 7, 0, 1 } + }, + [USB2PHY_PORT_HOST] = { + .phy_sus = { 0x0004, 8, 0, 0x1d2, 0x1d1 }, + .ls_det_en = { 0x0080, 1, 1, 0, 1 }, + .ls_det_st = { 0x0084, 1, 1, 0, 1 }, + .ls_det_clr = { 0x0088, 1, 1, 0, 1 }, + .utmi_ls = { 0x00c0, 17, 16, 0, 1 }, + .utmi_hstdet = { 0x00c0, 19, 19, 0, 1 } + } + }, + }, + { /* sentinel */ } +}; + static const struct rockchip_usb2phy_cfg rv1108_phy_cfgs[] = { { .reg = 0x100, @@ -1552,6 +1616,7 @@ static const struct of_device_id rockchip_usb2phy_dt_match[] = { { .compatible = "rockchip,rk3328-usb2phy", .data = &rk3328_phy_cfgs }, { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs }, { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs }, + { .compatible = "rockchip,rk3568-usb2phy", .data = &rk3568_phy_cfgs }, { .compatible = "rockchip,rv1108-usb2phy", .data = &rv1108_phy_cfgs }, {} }; -- cgit v1.2.3 From da35a7b526d9b258a2cb8b7816f736a41b32176b Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 21 Dec 2021 13:22:04 +0200 Subject: iio: frequency: admv1013: add support for ADMV1013 The ADMV1013 is a wideband, microwave upconverter optimized for point to point microwave radio designs operating in the 24 GHz to 44 GHz radio frequency (RF) range. Datasheet: https://www.analog.com/media/en/technical-documentation/data-sheets/ADMV1013.pdf Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20211221112206.97066-1-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- drivers/iio/frequency/Kconfig | 10 + drivers/iio/frequency/Makefile | 1 + drivers/iio/frequency/admv1013.c | 656 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 667 insertions(+) create mode 100644 drivers/iio/frequency/admv1013.c diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig index 2c9e0559e8a4..b44036f843af 100644 --- a/drivers/iio/frequency/Kconfig +++ b/drivers/iio/frequency/Kconfig @@ -50,6 +50,16 @@ config ADF4371 To compile this driver as a module, choose M here: the module will be called adf4371. +config ADMV1013 + tristate "Analog Devices ADMV1013 Microwave Upconverter" + depends on SPI && COMMON_CLK + help + Say yes here to build support for Analog Devices ADMV1013 + 24 GHz to 44 GHz, Wideband, Microwave Upconverter. + + To compile this driver as a module, choose M here: the + module will be called admv1013. + config ADRF6780 tristate "Analog Devices ADRF6780 Microwave Upconverter" depends on SPI diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile index ae3136c79202..ae6899856c99 100644 --- a/drivers/iio/frequency/Makefile +++ b/drivers/iio/frequency/Makefile @@ -7,4 +7,5 @@ obj-$(CONFIG_AD9523) += ad9523.o obj-$(CONFIG_ADF4350) += adf4350.o obj-$(CONFIG_ADF4371) += adf4371.o +obj-$(CONFIG_ADMV1013) += admv1013.o obj-$(CONFIG_ADRF6780) += adrf6780.o diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c new file mode 100644 index 000000000000..6cdeb50143af --- /dev/null +++ b/drivers/iio/frequency/admv1013.c @@ -0,0 +1,656 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * ADMV1013 driver + * + * Copyright 2021 Analog Devices Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ADMV1013 Register Map */ +#define ADMV1013_REG_SPI_CONTROL 0x00 +#define ADMV1013_REG_ALARM 0x01 +#define ADMV1013_REG_ALARM_MASKS 0x02 +#define ADMV1013_REG_ENABLE 0x03 +#define ADMV1013_REG_LO_AMP_I 0x05 +#define ADMV1013_REG_LO_AMP_Q 0x06 +#define ADMV1013_REG_OFFSET_ADJUST_I 0x07 +#define ADMV1013_REG_OFFSET_ADJUST_Q 0x08 +#define ADMV1013_REG_QUAD 0x09 +#define ADMV1013_REG_VVA_TEMP_COMP 0x0A + +/* ADMV1013_REG_SPI_CONTROL Map */ +#define ADMV1013_PARITY_EN_MSK BIT(15) +#define ADMV1013_SPI_SOFT_RESET_MSK BIT(14) +#define ADMV1013_CHIP_ID_MSK GENMASK(11, 4) +#define ADMV1013_CHIP_ID 0xA +#define ADMV1013_REVISION_ID_MSK GENMASK(3, 0) + +/* ADMV1013_REG_ALARM Map */ +#define ADMV1013_PARITY_ERROR_MSK BIT(15) +#define ADMV1013_TOO_FEW_ERRORS_MSK BIT(14) +#define ADMV1013_TOO_MANY_ERRORS_MSK BIT(13) +#define ADMV1013_ADDRESS_RANGE_ERROR_MSK BIT(12) + +/* ADMV1013_REG_ENABLE Map */ +#define ADMV1013_VGA_PD_MSK BIT(15) +#define ADMV1013_MIXER_PD_MSK BIT(14) +#define ADMV1013_QUAD_PD_MSK GENMASK(13, 11) +#define ADMV1013_BG_PD_MSK BIT(10) +#define ADMV1013_MIXER_IF_EN_MSK BIT(7) +#define ADMV1013_DET_EN_MSK BIT(5) + +/* ADMV1013_REG_LO_AMP Map */ +#define ADMV1013_LOAMP_PH_ADJ_FINE_MSK GENMASK(13, 7) +#define ADMV1013_MIXER_VGATE_MSK GENMASK(6, 0) + +/* ADMV1013_REG_OFFSET_ADJUST Map */ +#define ADMV1013_MIXER_OFF_ADJ_P_MSK GENMASK(15, 9) +#define ADMV1013_MIXER_OFF_ADJ_N_MSK GENMASK(8, 2) + +/* ADMV1013_REG_QUAD Map */ +#define ADMV1013_QUAD_SE_MODE_MSK GENMASK(9, 6) +#define ADMV1013_QUAD_FILTERS_MSK GENMASK(3, 0) + +/* ADMV1013_REG_VVA_TEMP_COMP Map */ +#define ADMV1013_VVA_TEMP_COMP_MSK GENMASK(15, 0) + +/* ADMV1013 Miscellaneous Defines */ +#define ADMV1013_READ BIT(7) +#define ADMV1013_REG_ADDR_READ_MSK GENMASK(6, 1) +#define ADMV1013_REG_ADDR_WRITE_MSK GENMASK(22, 17) +#define ADMV1013_REG_DATA_MSK GENMASK(16, 1) + +enum { + ADMV1013_IQ_MODE, + ADMV1013_IF_MODE +}; + +enum { + ADMV1013_RFMOD_I_CALIBPHASE, + ADMV1013_RFMOD_Q_CALIBPHASE, +}; + +enum { + ADMV1013_SE_MODE_POS = 6, + ADMV1013_SE_MODE_NEG = 9, + ADMV1013_SE_MODE_DIFF = 12 +}; + +struct admv1013_state { + struct spi_device *spi; + struct clk *clkin; + /* Protect against concurrent accesses to the device and to data */ + struct mutex lock; + struct regulator *reg; + struct notifier_block nb; + unsigned int input_mode; + unsigned int quad_se_mode; + bool det_en; + u8 data[3] ____cacheline_aligned; +}; + +static int __admv1013_spi_read(struct admv1013_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + struct spi_transfer t = {0}; + + st->data[0] = ADMV1013_READ | FIELD_PREP(ADMV1013_REG_ADDR_READ_MSK, reg); + st->data[1] = 0x0; + st->data[2] = 0x0; + + t.rx_buf = &st->data[0]; + t.tx_buf = &st->data[0]; + t.len = 3; + + ret = spi_sync_transfer(st->spi, &t, 1); + if (ret) + return ret; + + *val = FIELD_GET(ADMV1013_REG_DATA_MSK, get_unaligned_be24(&st->data[0])); + + return ret; +} + +static int admv1013_spi_read(struct admv1013_state *st, unsigned int reg, + unsigned int *val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv1013_spi_read(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv1013_spi_write(struct admv1013_state *st, + unsigned int reg, + unsigned int val) +{ + put_unaligned_be24(FIELD_PREP(ADMV1013_REG_DATA_MSK, val) | + FIELD_PREP(ADMV1013_REG_ADDR_WRITE_MSK, reg), &st->data[0]); + + return spi_write(st->spi, &st->data[0], 3); +} + +static int admv1013_spi_write(struct admv1013_state *st, unsigned int reg, + unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv1013_spi_write(st, reg, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int __admv1013_spi_update_bits(struct admv1013_state *st, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + unsigned int data, temp; + + ret = __admv1013_spi_read(st, reg, &data); + if (ret) + return ret; + + temp = (data & ~mask) | (val & mask); + + return __admv1013_spi_write(st, reg, temp); +} + +static int admv1013_spi_update_bits(struct admv1013_state *st, unsigned int reg, + unsigned int mask, unsigned int val) +{ + int ret; + + mutex_lock(&st->lock); + ret = __admv1013_spi_update_bits(st, reg, mask, val); + mutex_unlock(&st->lock); + + return ret; +} + +static int admv1013_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct admv1013_state *st = iio_priv(indio_dev); + unsigned int data, addr; + int ret; + + switch (info) { + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->channel) { + case IIO_MOD_I: + addr = ADMV1013_REG_OFFSET_ADJUST_I; + break; + case IIO_MOD_Q: + addr = ADMV1013_REG_OFFSET_ADJUST_Q; + break; + default: + return -EINVAL; + } + + ret = admv1013_spi_read(st, addr, &data); + if (ret) + return ret; + + if (!chan->channel) + *val = FIELD_GET(ADMV1013_MIXER_OFF_ADJ_P_MSK, data); + else + *val = FIELD_GET(ADMV1013_MIXER_OFF_ADJ_N_MSK, data); + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int admv1013_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct admv1013_state *st = iio_priv(indio_dev); + unsigned int addr, data, msk; + + switch (info) { + case IIO_CHAN_INFO_CALIBBIAS: + switch (chan->channel2) { + case IIO_MOD_I: + addr = ADMV1013_REG_OFFSET_ADJUST_I; + break; + case IIO_MOD_Q: + addr = ADMV1013_REG_OFFSET_ADJUST_Q; + break; + default: + return -EINVAL; + } + + if (!chan->channel) { + msk = ADMV1013_MIXER_OFF_ADJ_P_MSK; + data = FIELD_PREP(ADMV1013_MIXER_OFF_ADJ_P_MSK, val); + } else { + msk = ADMV1013_MIXER_OFF_ADJ_N_MSK; + data = FIELD_PREP(ADMV1013_MIXER_OFF_ADJ_N_MSK, val); + } + + return admv1013_spi_update_bits(st, addr, msk, data); + default: + return -EINVAL; + } +} + +static ssize_t admv1013_read(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct admv1013_state *st = iio_priv(indio_dev); + unsigned int data, addr; + int ret; + + switch ((u32)private) { + case ADMV1013_RFMOD_I_CALIBPHASE: + addr = ADMV1013_REG_LO_AMP_I; + break; + case ADMV1013_RFMOD_Q_CALIBPHASE: + addr = ADMV1013_REG_LO_AMP_Q; + break; + default: + return -EINVAL; + } + + ret = admv1013_spi_read(st, addr, &data); + if (ret) + return ret; + + data = FIELD_GET(ADMV1013_LOAMP_PH_ADJ_FINE_MSK, data); + + return sysfs_emit(buf, "%u\n", data); +} + +static ssize_t admv1013_write(struct iio_dev *indio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct admv1013_state *st = iio_priv(indio_dev); + unsigned int data; + int ret; + + ret = kstrtou32(buf, 10, &data); + if (ret) + return ret; + + data = FIELD_PREP(ADMV1013_LOAMP_PH_ADJ_FINE_MSK, data); + + switch ((u32)private) { + case ADMV1013_RFMOD_I_CALIBPHASE: + ret = admv1013_spi_update_bits(st, ADMV1013_REG_LO_AMP_I, + ADMV1013_LOAMP_PH_ADJ_FINE_MSK, + data); + if (ret) + return ret; + break; + case ADMV1013_RFMOD_Q_CALIBPHASE: + ret = admv1013_spi_update_bits(st, ADMV1013_REG_LO_AMP_Q, + ADMV1013_LOAMP_PH_ADJ_FINE_MSK, + data); + if (ret) + return ret; + break; + default: + return -EINVAL; + } + + return ret ? ret : len; +} + +static int admv1013_update_quad_filters(struct admv1013_state *st) +{ + unsigned int filt_raw; + u64 rate = clk_get_rate(st->clkin); + + if (rate >= (5400 * HZ_PER_MHZ) && rate <= (7000 * HZ_PER_MHZ)) + filt_raw = 15; + else if (rate >= (5400 * HZ_PER_MHZ) && rate <= (8000 * HZ_PER_MHZ)) + filt_raw = 10; + else if (rate >= (6600 * HZ_PER_MHZ) && rate <= (9200 * HZ_PER_MHZ)) + filt_raw = 5; + else + filt_raw = 0; + + return __admv1013_spi_update_bits(st, ADMV1013_REG_QUAD, + ADMV1013_QUAD_FILTERS_MSK, + FIELD_PREP(ADMV1013_QUAD_FILTERS_MSK, filt_raw)); +} + +static int admv1013_update_mixer_vgate(struct admv1013_state *st) +{ + unsigned int vcm, mixer_vgate; + + vcm = regulator_get_voltage(st->reg); + + if (vcm >= 0 && vcm < 1800000) + mixer_vgate = (2389 * vcm / 1000000 + 8100) / 100; + else if (vcm > 1800000 && vcm < 2600000) + mixer_vgate = (2375 * vcm / 1000000 + 125) / 100; + else + return -EINVAL; + + return __admv1013_spi_update_bits(st, ADMV1013_REG_LO_AMP_I, + ADMV1013_MIXER_VGATE_MSK, + FIELD_PREP(ADMV1013_MIXER_VGATE_MSK, mixer_vgate)); +} + +static int admv1013_reg_access(struct iio_dev *indio_dev, + unsigned int reg, + unsigned int write_val, + unsigned int *read_val) +{ + struct admv1013_state *st = iio_priv(indio_dev); + + if (read_val) + return admv1013_spi_read(st, reg, read_val); + else + return admv1013_spi_write(st, reg, write_val); +} + +static const struct iio_info admv1013_info = { + .read_raw = admv1013_read_raw, + .write_raw = admv1013_write_raw, + .debugfs_reg_access = &admv1013_reg_access, +}; + +static int admv1013_freq_change(struct notifier_block *nb, unsigned long action, void *data) +{ + struct admv1013_state *st = container_of(nb, struct admv1013_state, nb); + int ret; + + if (action == POST_RATE_CHANGE) { + mutex_lock(&st->lock); + ret = notifier_from_errno(admv1013_update_quad_filters(st)); + mutex_unlock(&st->lock); + return ret; + } + + return NOTIFY_OK; +} + +#define _ADMV1013_EXT_INFO(_name, _shared, _ident) { \ + .name = _name, \ + .read = admv1013_read, \ + .write = admv1013_write, \ + .private = _ident, \ + .shared = _shared, \ +} + +static const struct iio_chan_spec_ext_info admv1013_ext_info[] = { + _ADMV1013_EXT_INFO("i_calibphase", IIO_SEPARATE, ADMV1013_RFMOD_I_CALIBPHASE), + _ADMV1013_EXT_INFO("q_calibphase", IIO_SEPARATE, ADMV1013_RFMOD_Q_CALIBPHASE), + { }, +}; + +#define ADMV1013_CHAN_PHASE(_channel, _channel2, _admv1013_ext_info) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 0, \ + .indexed = 1, \ + .channel2 = _channel2, \ + .channel = _channel, \ + .differential = 1, \ + .ext_info = _admv1013_ext_info, \ + } + +#define ADMV1013_CHAN_CALIB(_channel, rf_comp) { \ + .type = IIO_ALTVOLTAGE, \ + .output = 0, \ + .indexed = 1, \ + .channel = _channel, \ + .channel2 = IIO_MOD_##rf_comp, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_CALIBBIAS), \ + } + +static const struct iio_chan_spec admv1013_channels[] = { + ADMV1013_CHAN_PHASE(0, 1, admv1013_ext_info), + ADMV1013_CHAN_CALIB(0, I), + ADMV1013_CHAN_CALIB(0, Q), + ADMV1013_CHAN_CALIB(1, I), + ADMV1013_CHAN_CALIB(1, Q), +}; + +static int admv1013_init(struct admv1013_state *st) +{ + int ret; + unsigned int data; + struct spi_device *spi = st->spi; + + /* Perform a software reset */ + ret = __admv1013_spi_update_bits(st, ADMV1013_REG_SPI_CONTROL, + ADMV1013_SPI_SOFT_RESET_MSK, + FIELD_PREP(ADMV1013_SPI_SOFT_RESET_MSK, 1)); + if (ret) + return ret; + + ret = __admv1013_spi_update_bits(st, ADMV1013_REG_SPI_CONTROL, + ADMV1013_SPI_SOFT_RESET_MSK, + FIELD_PREP(ADMV1013_SPI_SOFT_RESET_MSK, 0)); + if (ret) + return ret; + + ret = __admv1013_spi_read(st, ADMV1013_REG_SPI_CONTROL, &data); + if (ret) + return ret; + + data = FIELD_GET(ADMV1013_CHIP_ID_MSK, data); + if (data != ADMV1013_CHIP_ID) { + dev_err(&spi->dev, "Invalid Chip ID.\n"); + return -EINVAL; + } + + ret = __admv1013_spi_write(st, ADMV1013_REG_VVA_TEMP_COMP, 0xE700); + if (ret) + return ret; + + data = FIELD_PREP(ADMV1013_QUAD_SE_MODE_MSK, st->quad_se_mode); + + ret = __admv1013_spi_update_bits(st, ADMV1013_REG_QUAD, + ADMV1013_QUAD_SE_MODE_MSK, data); + if (ret) + return ret; + + ret = admv1013_update_mixer_vgate(st); + if (ret) + return ret; + + ret = admv1013_update_quad_filters(st); + if (ret) + return ret; + + return __admv1013_spi_update_bits(st, ADMV1013_REG_ENABLE, + ADMV1013_DET_EN_MSK | + ADMV1013_MIXER_IF_EN_MSK, + st->det_en | + st->input_mode); +} + +static void admv1013_clk_disable(void *data) +{ + clk_disable_unprepare(data); +} + +static void admv1013_reg_disable(void *data) +{ + regulator_disable(data); +} + +static void admv1013_powerdown(void *data) +{ + unsigned int enable_reg, enable_reg_msk; + + /* Disable all components in the Enable Register */ + enable_reg_msk = ADMV1013_VGA_PD_MSK | + ADMV1013_MIXER_PD_MSK | + ADMV1013_QUAD_PD_MSK | + ADMV1013_BG_PD_MSK | + ADMV1013_MIXER_IF_EN_MSK | + ADMV1013_DET_EN_MSK; + + enable_reg = FIELD_PREP(ADMV1013_VGA_PD_MSK, 1) | + FIELD_PREP(ADMV1013_MIXER_PD_MSK, 1) | + FIELD_PREP(ADMV1013_QUAD_PD_MSK, 7) | + FIELD_PREP(ADMV1013_BG_PD_MSK, 1) | + FIELD_PREP(ADMV1013_MIXER_IF_EN_MSK, 0) | + FIELD_PREP(ADMV1013_DET_EN_MSK, 0); + + admv1013_spi_update_bits(data, ADMV1013_REG_ENABLE, enable_reg_msk, enable_reg); +} + +static int admv1013_properties_parse(struct admv1013_state *st) +{ + int ret; + const char *str; + struct spi_device *spi = st->spi; + + st->det_en = device_property_read_bool(&spi->dev, "adi,detector-enable"); + + ret = device_property_read_string(&spi->dev, "adi,input-mode", &str); + if (ret) + st->input_mode = ADMV1013_IQ_MODE; + + if (!strcmp(str, "iq")) + st->input_mode = ADMV1013_IQ_MODE; + else if (!strcmp(str, "if")) + st->input_mode = ADMV1013_IF_MODE; + else + return -EINVAL; + + ret = device_property_read_string(&spi->dev, "adi,quad-se-mode", &str); + if (ret) + st->quad_se_mode = ADMV1013_SE_MODE_DIFF; + + if (!strcmp(str, "diff")) + st->quad_se_mode = ADMV1013_SE_MODE_DIFF; + else if (!strcmp(str, "se-pos")) + st->quad_se_mode = ADMV1013_SE_MODE_POS; + else if (!strcmp(str, "se-neg")) + st->quad_se_mode = ADMV1013_SE_MODE_NEG; + else + return -EINVAL; + + st->reg = devm_regulator_get(&spi->dev, "vcm"); + if (IS_ERR(st->reg)) + return dev_err_probe(&spi->dev, PTR_ERR(st->reg), + "failed to get the common-mode voltage\n"); + + st->clkin = devm_clk_get(&spi->dev, "lo_in"); + if (IS_ERR(st->clkin)) + return dev_err_probe(&spi->dev, PTR_ERR(st->clkin), + "failed to get the LO input clock\n"); + + return 0; +} + +static int admv1013_probe(struct spi_device *spi) +{ + struct iio_dev *indio_dev; + struct admv1013_state *st; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + indio_dev->info = &admv1013_info; + indio_dev->name = "admv1013"; + indio_dev->channels = admv1013_channels; + indio_dev->num_channels = ARRAY_SIZE(admv1013_channels); + + st->spi = spi; + + ret = admv1013_properties_parse(st); + if (ret) + return ret; + + ret = regulator_enable(st->reg); + if (ret) { + dev_err(&spi->dev, "Failed to enable specified Common-Mode Voltage!\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, admv1013_reg_disable, + st->reg); + if (ret) + return ret; + + ret = clk_prepare_enable(st->clkin); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, admv1013_clk_disable, st->clkin); + if (ret) + return ret; + + st->nb.notifier_call = admv1013_freq_change; + ret = devm_clk_notifier_register(&spi->dev, st->clkin, &st->nb); + if (ret) + return ret; + + mutex_init(&st->lock); + + ret = admv1013_init(st); + if (ret) { + dev_err(&spi->dev, "admv1013 init failed\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, admv1013_powerdown, st); + if (ret) + return ret; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct spi_device_id admv1013_id[] = { + { "admv1013", 0}, + {} +}; +MODULE_DEVICE_TABLE(spi, admv1013_id); + +static const struct of_device_id admv1013_of_match[] = { + { .compatible = "adi,admv1013" }, + {}, +}; +MODULE_DEVICE_TABLE(of, admv1013_of_match); + +static struct spi_driver admv1013_driver = { + .driver = { + .name = "admv1013", + .of_match_table = admv1013_of_match, + }, + .probe = admv1013_probe, + .id_table = admv1013_id, +}; +module_spi_driver(admv1013_driver); + +MODULE_AUTHOR("Antoniu Miclaus Date: Tue, 21 Dec 2021 13:22:05 +0200 Subject: dt-bindings: iio: frequency: add admv1013 doc Add device tree bindings for the ADMV1013 Upconverter. Signed-off-by: Antoniu Miclaus Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211221112206.97066-2-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- .../bindings/iio/frequency/adi,admv1013.yaml | 91 ++++++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/frequency/adi,admv1013.yaml diff --git a/Documentation/devicetree/bindings/iio/frequency/adi,admv1013.yaml b/Documentation/devicetree/bindings/iio/frequency/adi,admv1013.yaml new file mode 100644 index 000000000000..23f1f3b55abb --- /dev/null +++ b/Documentation/devicetree/bindings/iio/frequency/adi,admv1013.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/frequency/adi,admv1013.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADMV1013 Microwave Upconverter + +maintainers: + - Antoniu Miclaus + +description: | + Wideband, microwave upconverter optimized for point to point microwave + radio designs operating in the 24 GHz to 44 GHz frequency range. + + https://www.analog.com/en/products/admv1013.html + +properties: + compatible: + enum: + - adi,admv1013 + + reg: + maxItems: 1 + + spi-max-frequency: + maximum: 1000000 + + clocks: + description: + Definition of the external clock. + minItems: 1 + + clock-names: + items: + - const: lo_in + + vcm-supply: + description: + Analog voltage regulator. + + adi,detector-enable: + description: + Enable the Envelope Detector available at output pins VENV_P and + VENV_N. Disable to reduce power consumption. + type: boolean + + adi,input-mode: + description: + Select the input mode. + iq - in-phase quadrature (I/Q) input + if - complex intermediate frequency (IF) input + enum: [iq, if] + + adi,quad-se-mode: + description: + Switch the LO path from differential to single-ended operation. + se-neg - Single-Ended Mode, Negative Side Disabled. + se-pos - Single-Ended Mode, Positive Side Disabled. + diff - Differential Mode. + enum: [se-neg, se-pos, diff] + + '#clock-cells': + const: 0 + +required: + - compatible + - reg + - clocks + - clock-names + - vcm-supply + +additionalProperties: false + +examples: + - | + spi { + #address-cells = <1>; + #size-cells = <0>; + admv1013@0{ + compatible = "adi,admv1013"; + reg = <0>; + spi-max-frequency = <1000000>; + clocks = <&admv1013_lo>; + clock-names = "lo_in"; + vcm-supply = <&vcm>; + adi,quad-se-mode = "diff"; + adi,detector-enable; + }; + }; +... -- cgit v1.2.3 From f407c2374af64202c7b278580d22f81eec6f33c0 Mon Sep 17 00:00:00 2001 From: Antoniu Miclaus Date: Tue, 21 Dec 2021 13:22:06 +0200 Subject: Documentation:ABI:testing:admv1013: add ABI docs Add documentation for the use of the Local Oscillator Feedthrough Offset calibration. Signed-off-by: Antoniu Miclaus Link: https://lore.kernel.org/r/20211221112206.97066-3-antoniu.miclaus@analog.com Signed-off-by: Jonathan Cameron --- .../ABI/testing/sysfs-bus-iio-frequency-admv1013 | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 diff --git a/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 b/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 new file mode 100644 index 000000000000..de1e323e5d47 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-iio-frequency-admv1013 @@ -0,0 +1,38 @@ +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_i_calibphase +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Read/write unscaled value for the Local Oscillatior path quadrature I phase shift. + +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0-1_q_calibphase +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Read/write unscaled value for the Local Oscillatior path quadrature Q phase shift. + +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0_i_calibbias +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Read/write value for the Local Oscillatior Feedthrough Offset Calibration I Positive + side. + +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage0_q_calibbias +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Read/write value for the Local Oscillatior Feedthrough Offset Calibration Q Positive side. + +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage1_i_calibbias +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Read/write raw value for the Local Oscillatior Feedthrough Offset Calibration I Negative + side. + +What: /sys/bus/iio/devices/iio:deviceX/in_altvoltage1_q_calibbias +KernelVersion: +Contact: linux-iio@vger.kernel.org +Description: + Read/write raw value for the Local Oscillatior Feedthrough Offset Calibration Q Negative + side. -- cgit v1.2.3 From bde65965b8ec59637c9b785dfbb9ce82a61af50f Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 21 Dec 2021 15:49:59 +0800 Subject: MAINTAINERS: add imx7d/imx6sx/imx6ul/imx8qxp and vf610 adc maintainer Add myself as imx7d/imx6sx/imx6ul/imx8qxp and vf610 adc maintainer. Signed-off-by: Haibo Chen Reviewed-by: Cai Huoqing Link: https://lore.kernel.org/r/1640073000-32629-1-git-send-email-haibo.chen@nxp.com Signed-off-by: Jonathan Cameron --- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 36e8dd46beed..d9e486f150f5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13719,11 +13719,23 @@ F: drivers/gpu/drm/imx/dcss/ NXP i.MX 8QXP ADC DRIVER M: Cai Huoqing +M: Haibo Chen +L: linux-imx@nxp.com L: linux-iio@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/iio/adc/nxp,imx8qxp-adc.yaml F: drivers/iio/adc/imx8qxp-adc.c +NXP i.MX 7D/6SX/6UL AND VF610 ADC DRIVER +M: Haibo Chen +L: linux-iio@vger.kernel.org +L: linux-imx@nxp.com +S: Maintained +F: Documentation/devicetree/bindings/iio/adc/fsl,imx7d-adc.yaml +F: Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml +F: drivers/iio/adc/imx7d_adc.c +F: drivers/iio/adc/vf610_adc.c + NXP PF8100/PF8121A/PF8200 PMIC REGULATOR DEVICE DRIVER M: Jagan Teki S: Maintained -- cgit v1.2.3 From 088879292a0a3672a6acc9215174fbc355ed3daa Mon Sep 17 00:00:00 2001 From: Haibo Chen Date: Tue, 21 Dec 2021 15:50:00 +0800 Subject: dt-bindings:iio:adc: update the maintainer of vf610-adc Drop Fugang Duan as the vf610-adc maintainer, and add my self as the maintainer. Signed-off-by: Haibo Chen Acked-by: Rob Herring Link: https://lore.kernel.org/r/1640073000-32629-2-git-send-email-haibo.chen@nxp.com Signed-off-by: Jonathan Cameron --- Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml b/Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml index 1ca571056ea9..925f355cc21f 100644 --- a/Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml +++ b/Documentation/devicetree/bindings/iio/adc/fsl,vf610-adc.yaml @@ -7,7 +7,7 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: ADC found on Freescale vf610 and similar SoCs maintainers: - - Fugang Duan + - Haibo Chen description: ADCs found on vf610/i.MX6slx and upward SoCs from Freescale. -- cgit v1.2.3 From 3ba4c0a8f4c91dc4c274bea7a8766463356d6704 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 18 Dec 2021 17:17:54 +0300 Subject: dt-bindings: phy: qcom,qmp: Add SM8450 PCIe PHY bindings There are two different PCIe PHYs on SM8450, one having one lane and another with two lanes. Add support for second (gen4, two lanes) PHY. Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211218141754.503661-2-dmitry.baryshkov@linaro.org Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml index 5887b046c640..e417cd667997 100644 --- a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml +++ b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml @@ -51,6 +51,7 @@ properties: - qcom,sm8350-qmp-usb3-phy - qcom,sm8350-qmp-usb3-uni-phy - qcom,sm8450-qmp-gen3x1-pcie-phy + - qcom,sm8450-qmp-gen4x2-pcie-phy - qcom,sm8450-qmp-ufs-phy - qcom,sm8450-qmp-usb3-phy - qcom,sdx55-qmp-pcie-phy @@ -336,6 +337,7 @@ allOf: - qcom,sm8250-qmp-gen3x2-pcie-phy - qcom,sm8250-qmp-modem-pcie-phy - qcom,sm8450-qmp-gen3x1-pcie-phy + - qcom,sm8450-qmp-gen4x2-pcie-phy then: properties: clocks: -- cgit v1.2.3 From 2c91bf6bf290ffd0a566fe5d7518d2484522816c Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 18 Dec 2021 17:17:55 +0300 Subject: phy: qcom-qmp: Add SM8450 PCIe1 PHY support There are two different PCIe PHYs on SM8450, one having one lane (v5) and another with two lanes (v5.20). This commit adds support for the second PCIe phy. Signed-off-by: Dmitry Baryshkov Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20211218141754.503661-3-dmitry.baryshkov@linaro.org Signed-off-by: Vinod Koul --- drivers/phy/qualcomm/phy-qcom-qmp.c | 153 ++++++++++++++++++++++++++++++++++++ drivers/phy/qualcomm/phy-qcom-qmp.h | 70 +++++++++++++++++ 2 files changed, 223 insertions(+) diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.c b/drivers/phy/qualcomm/phy-qcom-qmp.c index e73900ea2728..bad5dffc22b5 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp.c @@ -2957,6 +2957,124 @@ static const struct qmp_phy_init_tbl sm8450_qmp_gen3x1_pcie_pcs_misc_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), }; +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER1, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE0, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1, 0x97), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE0, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CP_CTRL_MODE1, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_EN, 0x46), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP_CFG, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE0, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP1_MODE1, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_LOCK_CMP2_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE0, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DEC_START_MODE1, 0xd0), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE0, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE0, 0x03), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_DIV_FRAC_START3_MODE1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_TUNE_MAP, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_SEL, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE0, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MISC1, 0x88), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CORE_CLK_EN, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_CMN_MODE, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V5_COM_VCO_DC_LEVEL_CTRL, 0x0f), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_1, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_LANE_MODE_2, 0xf6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX, 0x0c), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_PI_CONTROLS, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1, 0xcc), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3, 0xcc), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5, 0x4a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6, 0x29), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B0, 0xc5), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B1, 0xad), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B2, 0xb6), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B3, 0xc0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B4, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B5, 0xfb), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE2_B6, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B0, 0xc7), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B1, 0xef), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B2, 0xbf), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B3, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B4, 0x81), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B5, 0xde), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MODE_RATE3_B6, 0x7f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_PHPRE_CTRL, 0x20), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1, 0x3f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3, 0x37), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_3, 0x05), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3, 0x1f), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210, 0x1f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210, 0x1f), + + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2, 0x0c), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_VGA_CAL_MAN_VAL, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_RX_IDAC_SAOFFSET, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_DFE_DAC_ENABLE1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_GM_CAL, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2, 0x1f), +}; + +/* Register names should be validated, they might be different for this PHY */ +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG2, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG3, 0x22), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_G3S2_PRE_GAIN, 0x2e), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0x99), +}; + +static const struct qmp_phy_init_tbl sm8450_qmp_gen4x2_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xc1), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5, 0x02), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_EQ_CONFIG1, 0x16), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3, 0x28), + QMP_PHY_INIT_CFG(QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN, 0x2e), +}; + struct qmp_phy; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -4238,6 +4356,38 @@ static const struct qmp_phy_cfg sm8450_qmp_gen3x1_pciephy_cfg = { .pwrdn_delay_max = 1005, /* us */ }; +static const struct qmp_phy_cfg sm8450_qmp_gen4x2_pciephy_cfg = { + .type = PHY_TYPE_PCIE, + .nlanes = 2, + + .serdes_tbl = sm8450_qmp_gen4x2_pcie_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_serdes_tbl), + .tx_tbl = sm8450_qmp_gen4x2_pcie_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_tx_tbl), + .rx_tbl = sm8450_qmp_gen4x2_pcie_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_rx_tbl), + .pcs_tbl = sm8450_qmp_gen4x2_pcie_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_pcs_tbl), + .pcs_misc_tbl = sm8450_qmp_gen4x2_pcie_pcs_misc_tbl, + .pcs_misc_tbl_num = ARRAY_SIZE(sm8450_qmp_gen4x2_pcie_pcs_misc_tbl), + .clk_list = sdm845_pciephy_clk_l, + .num_clks = ARRAY_SIZE(sdm845_pciephy_clk_l), + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = sm8250_pcie_regs_layout, + + .start_ctrl = SERDES_START | PCS_START, + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS_4_20, + + .is_dual_lane_phy = true, + .has_pwrdn_delay = true, + .pwrdn_delay_min = 995, /* us */ + .pwrdn_delay_max = 1005, /* us */ +}; + static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { .type = PHY_TYPE_USB3, .nlanes = 1, @@ -5899,6 +6049,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = { }, { .compatible = "qcom,sm8450-qmp-gen3x1-pcie-phy", .data = &sm8450_qmp_gen3x1_pciephy_cfg, + }, { + .compatible = "qcom,sm8450-qmp-gen4x2-pcie-phy", + .data = &sm8450_qmp_gen4x2_pciephy_cfg, }, { .compatible = "qcom,sm8450-qmp-ufs-phy", .data = &sm8450_ufsphy_cfg, diff --git a/drivers/phy/qualcomm/phy-qcom-qmp.h b/drivers/phy/qualcomm/phy-qcom-qmp.h index eeeef8d40876..06b2556ed93a 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp.h @@ -1077,6 +1077,7 @@ #define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE0 0x028 #define QSERDES_V5_COM_SSC_STEP_SIZE1_MODE1 0x030 #define QSERDES_V5_COM_SSC_STEP_SIZE2_MODE1 0x034 +#define QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN 0x044 #define QSERDES_V5_COM_CLK_ENABLE1 0x048 #define QSERDES_V5_COM_SYSCLK_BUF_ENABLE 0x050 #define QSERDES_V5_COM_PLL_IVCO 0x058 @@ -1088,6 +1089,7 @@ #define QSERDES_V5_COM_PLL_CCTRL_MODE1 0x088 #define QSERDES_V5_COM_SYSCLK_EN_SEL 0x094 #define QSERDES_V5_COM_LOCK_CMP_EN 0x0a4 +#define QSERDES_V5_COM_LOCK_CMP_CFG 0x0a8 #define QSERDES_V5_COM_LOCK_CMP1_MODE0 0x0ac #define QSERDES_V5_COM_LOCK_CMP2_MODE0 0x0b0 #define QSERDES_V5_COM_LOCK_CMP1_MODE1 0x0b4 @@ -1109,7 +1111,13 @@ #define QSERDES_V5_COM_CLK_SELECT 0x154 #define QSERDES_V5_COM_HSCLK_SEL 0x158 #define QSERDES_V5_COM_HSCLK_HS_SWITCH_SEL 0x15c +#define QSERDES_V5_COM_CORECLK_DIV_MODE0 0x168 #define QSERDES_V5_COM_CORECLK_DIV_MODE1 0x16c +#define QSERDES_V5_COM_CORE_CLK_EN 0x174 +#define QSERDES_V5_COM_CMN_CONFIG 0x17c +#define QSERDES_V5_COM_CMN_MISC1 0x19c +#define QSERDES_V5_COM_CMN_MODE 0x1a4 +#define QSERDES_V5_COM_VCO_DC_LEVEL_CTRL 0x1a8 #define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE0 0x1ac #define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE2_MODE0 0x1b0 #define QSERDES_V5_COM_BIN_VCOCAL_CMP_CODE1_MODE1 0x1b4 @@ -1134,6 +1142,12 @@ #define QSERDES_V5_TX_PWM_GEAR_3_DIVIDER_BAND0_1 0x180 #define QSERDES_V5_TX_PWM_GEAR_4_DIVIDER_BAND0_1 0x184 +/* Only for QMP V5_20 PHY - TX registers */ +#define QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_TX 0x30 +#define QSERDES_V5_20_TX_RES_CODE_LANE_OFFSET_RX 0x34 +#define QSERDES_V5_20_TX_LANE_MODE_1 0x78 +#define QSERDES_V5_20_TX_LANE_MODE_2 0x7c + /* Only for QMP V5 PHY - RX registers */ #define QSERDES_V5_RX_UCDR_FO_GAIN 0x008 #define QSERDES_V5_RX_UCDR_SO_GAIN 0x014 @@ -1190,10 +1204,58 @@ #define QSERDES_V5_RX_DCC_CTRL1 0x1a8 #define QSERDES_V5_RX_VTH_CODE 0x1b0 +/* Only for QMP V5_20 PHY - RX registers */ +#define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE2 0x008 +#define QSERDES_V5_20_RX_UCDR_FO_GAIN_RATE3 0x00c +#define QSERDES_V5_20_RX_UCDR_PI_CONTROLS 0x020 +#define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_0_1 0x02c +#define QSERDES_V5_20_RX_AUX_DATA_THRESH_BIN_RATE_2_3 0x030 +#define QSERDES_V5_20_RX_RX_IDAC_SAOFFSET 0x07c +#define QSERDES_V5_20_RX_DFE_3 0x090 +#define QSERDES_V5_20_RX_DFE_DAC_ENABLE1 0x0b4 +#define QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH1 0x0c4 +#define QSERDES_V5_20_RX_TX_ADAPT_POST_THRESH2 0x0c8 +#define QSERDES_V5_20_RX_VGA_CAL_MAN_VAL 0x0dc +#define QSERDES_V5_20_RX_GM_CAL 0x0ec +#define QSERDES_V5_20_RX_RX_EQU_ADAPTOR_CNTRL4 0x108 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B1 0x164 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B2 0x168 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B3 0x16c +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B5 0x174 +#define QSERDES_V5_20_RX_RX_MODE_RATE_0_1_B6 0x178 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B0 0x17c +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B1 0x180 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B2 0x184 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B3 0x188 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B4 0x18c +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B5 0x190 +#define QSERDES_V5_20_RX_RX_MODE_RATE2_B6 0x194 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B0 0x198 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B1 0x19c +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B2 0x1a0 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B3 0x1a4 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B4 0x1a8 +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B5 0x1ac +#define QSERDES_V5_20_RX_RX_MODE_RATE3_B6 0x1b0 +#define QSERDES_V5_20_RX_PHPRE_CTRL 0x1b4 +#define QSERDES_V5_20_RX_DFE_CTLE_POST_CAL_OFFSET 0x1c0 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE210 0x1f4 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH1_RATE3 0x1f8 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE210 0x1fc +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH2_RATE3 0x200 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE210 0x204 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH3_RATE3 0x208 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH4_RATE3 0x210 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH5_RATE3 0x218 +#define QSERDES_V5_20_RX_RX_MARG_COARSE_THRESH6_RATE3 0x220 + /* Only for QMP V5 PHY - USB/PCIe PCS registers */ #define QPHY_V5_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V5_PCS_G3S2_PRE_GAIN 0x170 #define QPHY_V5_PCS_RX_SIGDET_LVL 0x188 #define QPHY_V5_PCS_RATE_SLEW_CNTRL1 0x198 +#define QPHY_V5_PCS_EQ_CONFIG2 0x1e0 +#define QPHY_V5_PCS_EQ_CONFIG3 0x1e4 /* Only for QMP V5 PHY - PCS_PCIE registers */ #define QPHY_V5_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x20 @@ -1201,6 +1263,14 @@ #define QPHY_V5_PCS_PCIE_OSC_DTCT_ACTIONS 0x94 #define QPHY_V5_PCS_PCIE_EQ_CONFIG2 0xa8 +/* Only for QMP V5_20 PHY - PCIe PCS registers */ +#define QPHY_V5_20_PCS_PCIE_ENDPOINT_REFCLK_DRIVE 0x01c +#define QPHY_V5_20_PCS_PCIE_OSC_DTCT_ACTIONS 0x090 +#define QPHY_V5_20_PCS_PCIE_EQ_CONFIG1 0x0a0 +#define QPHY_V5_20_PCS_PCIE_G4_EQ_CONFIG5 0x108 +#define QPHY_V5_20_PCS_PCIE_G4_PRE_GAIN 0x15c +#define QPHY_V5_20_PCS_PCIE_RX_MARGINING_CONFIG3 0x184 + /* Only for QMP V5 PHY - UFS PCS registers */ #define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB 0x00c #define QPHY_V5_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB 0x010 -- cgit v1.2.3 From c6d92a287ae718c3207dddea3f36ea1689ac59a8 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Sat, 18 Dec 2021 16:27:58 +0800 Subject: dt-bindings: phy: mediatek: tphy: support software efuse load Add optional property nvmem-cells and nvmem-cell-names to support software efuse load, this helps to fix the efuse bit shift issue on mt8195 etc. Acked-by: Rob Herring Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20211218082802.5256-1-chunfeng.yun@mediatek.com Signed-off-by: Vinod Koul --- .../devicetree/bindings/phy/mediatek,tphy.yaml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml b/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml index 9e6c0f43f1c6..05ee274b4b71 100644 --- a/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml +++ b/Documentation/devicetree/bindings/phy/mediatek,tphy.yaml @@ -160,6 +160,24 @@ patternProperties: - PHY_TYPE_PCIE - PHY_TYPE_SATA + nvmem-cells: + items: + - description: internal R efuse for U2 PHY or U3/PCIe PHY + - description: rx_imp_sel efuse for U3/PCIe PHY + - description: tx_imp_sel efuse for U3/PCIe PHY + description: | + Phandles to nvmem cell that contains the efuse data; + Available only for U2 PHY or U3/PCIe PHY of version 2/3, these + three items should be provided at the same time for U3/PCIe PHY, + when use software to load efuse; + If unspecified, will use hardware auto-load efuse. + + nvmem-cell-names: + items: + - const: intr + - const: rx_imp + - const: tx_imp + # The following optional vendor properties are only for debug or HQA test mediatek,eye-src: description: -- cgit v1.2.3 From 6f2b033cb883f64ad084a75f13634242c7e179a6 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Sat, 18 Dec 2021 16:27:59 +0800 Subject: phy: phy-mtk-tphy: add support efuse setting Due to some SoCs have a bit shift issue that will drop a bit for usb3 phy or pcie phy, fix it by adding software efuse reading and setting, but only support it optionally for version 2/3. Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20211218082802.5256-2-chunfeng.yun@mediatek.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/phy-mtk-tphy.c | 162 ++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index cdcef865fe9e..98a942c607a6 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,9 @@ #define SSUSB_SIFSLV_V2_U3PHYD 0x200 #define SSUSB_SIFSLV_V2_U3PHYA 0x400 +#define U3P_MISC_REG1 0x04 +#define MR1_EFUSE_AUTO_LOAD_DIS BIT(6) + #define U3P_USBPHYACR0 0x000 #define PA0_RG_U2PLL_FORCE_ON BIT(15) #define PA0_USB20_PLL_PREDIV GENMASK(7, 6) @@ -133,6 +137,8 @@ #define P3C_RG_SWRST_U3_PHYD_FORCE_EN BIT(24) #define U3P_U3_PHYA_REG0 0x000 +#define P3A_RG_IEXT_INTR GENMASK(15, 10) +#define P3A_RG_IEXT_INTR_VAL(x) ((0x3f & (x)) << 10) #define P3A_RG_CLKDRV_OFF GENMASK(3, 2) #define P3A_RG_CLKDRV_OFF_VAL(x) ((0x3 & (x)) << 2) @@ -187,6 +193,19 @@ #define P3D_RG_FWAKE_TH GENMASK(21, 16) #define P3D_RG_FWAKE_TH_VAL(x) ((0x3f & (x)) << 16) +#define U3P_U3_PHYD_IMPCAL0 0x010 +#define P3D_RG_FORCE_TX_IMPEL BIT(31) +#define P3D_RG_TX_IMPEL GENMASK(28, 24) +#define P3D_RG_TX_IMPEL_VAL(x) ((0x1f & (x)) << 24) + +#define U3P_U3_PHYD_IMPCAL1 0x014 +#define P3D_RG_FORCE_RX_IMPEL BIT(31) +#define P3D_RG_RX_IMPEL GENMASK(28, 24) +#define P3D_RG_RX_IMPEL_VAL(x) ((0x1f & (x)) << 24) + +#define U3P_U3_PHYD_RSV 0x054 +#define P3D_RG_EFUSE_AUTO_LOAD_DIS BIT(12) + #define U3P_U3_PHYD_CDR1 0x05c #define P3D_RG_CDR_BIR_LTD1 GENMASK(28, 24) #define P3D_RG_CDR_BIR_LTD1_VAL(x) ((0x1f & (x)) << 24) @@ -307,6 +326,11 @@ struct mtk_phy_pdata { * 48M PLL, fix it by switching PLL to 26M from default 48M */ bool sw_pll_48m_to_26m; + /* + * Some SoCs (e.g. mt8195) drop a bit when use auto load efuse, + * support sw way, also support it for v2/v3 optionally. + */ + bool sw_efuse_supported; enum mtk_phy_version version; }; @@ -336,6 +360,10 @@ struct mtk_phy_instance { struct regmap *type_sw; u32 type_sw_reg; u32 type_sw_index; + u32 efuse_sw_en; + u32 efuse_intr; + u32 efuse_tx_imp; + u32 efuse_rx_imp; int eye_src; int eye_vrt; int eye_term; @@ -1040,6 +1068,130 @@ static int phy_type_set(struct mtk_phy_instance *instance) return 0; } +static int phy_efuse_get(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) +{ + struct device *dev = &instance->phy->dev; + int ret = 0; + + /* tphy v1 doesn't support sw efuse, skip it */ + if (!tphy->pdata->sw_efuse_supported) { + instance->efuse_sw_en = 0; + return 0; + } + + /* software efuse is optional */ + instance->efuse_sw_en = device_property_read_bool(dev, "nvmem-cells"); + if (!instance->efuse_sw_en) + return 0; + + switch (instance->type) { + case PHY_TYPE_USB2: + ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); + if (ret) { + dev_err(dev, "fail to get u2 intr efuse, %d\n", ret); + break; + } + + /* no efuse, ignore it */ + if (!instance->efuse_intr) { + dev_warn(dev, "no u2 intr efuse, but dts enable it\n"); + instance->efuse_sw_en = 0; + break; + } + + dev_dbg(dev, "u2 efuse - intr %x\n", instance->efuse_intr); + break; + + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: + ret = nvmem_cell_read_variable_le_u32(dev, "intr", &instance->efuse_intr); + if (ret) { + dev_err(dev, "fail to get u3 intr efuse, %d\n", ret); + break; + } + + ret = nvmem_cell_read_variable_le_u32(dev, "rx_imp", &instance->efuse_rx_imp); + if (ret) { + dev_err(dev, "fail to get u3 rx_imp efuse, %d\n", ret); + break; + } + + ret = nvmem_cell_read_variable_le_u32(dev, "tx_imp", &instance->efuse_tx_imp); + if (ret) { + dev_err(dev, "fail to get u3 tx_imp efuse, %d\n", ret); + break; + } + + /* no efuse, ignore it */ + if (!instance->efuse_intr && + !instance->efuse_rx_imp && + !instance->efuse_rx_imp) { + dev_warn(dev, "no u3 intr efuse, but dts enable it\n"); + instance->efuse_sw_en = 0; + break; + } + + dev_dbg(dev, "u3 efuse - intr %x, rx_imp %x, tx_imp %x\n", + instance->efuse_intr, instance->efuse_rx_imp,instance->efuse_tx_imp); + break; + default: + dev_err(dev, "no sw efuse for type %d\n", instance->type); + ret = -EINVAL; + } + + return ret; +} + +static void phy_efuse_set(struct mtk_phy_instance *instance) +{ + struct device *dev = &instance->phy->dev; + struct u2phy_banks *u2_banks = &instance->u2_banks; + struct u3phy_banks *u3_banks = &instance->u3_banks; + u32 tmp; + + if (!instance->efuse_sw_en) + return; + + switch (instance->type) { + case PHY_TYPE_USB2: + tmp = readl(u2_banks->misc + U3P_MISC_REG1); + tmp |= MR1_EFUSE_AUTO_LOAD_DIS; + writel(tmp, u2_banks->misc + U3P_MISC_REG1); + + tmp = readl(u2_banks->com + U3P_USBPHYACR1); + tmp &= ~PA1_RG_INTR_CAL; + tmp |= PA1_RG_INTR_CAL_VAL(instance->efuse_intr); + writel(tmp, u2_banks->com + U3P_USBPHYACR1); + break; + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); + tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); + + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); + tmp &= ~P3D_RG_TX_IMPEL; + tmp |= P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp); + tmp |= P3D_RG_FORCE_TX_IMPEL; + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); + + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); + tmp &= ~P3D_RG_RX_IMPEL; + tmp |= P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp); + tmp |= P3D_RG_FORCE_RX_IMPEL; + writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); + + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); + tmp &= ~P3A_RG_IEXT_INTR; + tmp |= P3A_RG_IEXT_INTR_VAL(instance->efuse_intr); + writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); + break; + default: + dev_warn(dev, "no sw efuse for type %d\n", instance->type); + break; + } +} + static int mtk_phy_init(struct phy *phy) { struct mtk_phy_instance *instance = phy_get_drvdata(phy); @@ -1050,6 +1202,8 @@ static int mtk_phy_init(struct phy *phy) if (ret) return ret; + phy_efuse_set(instance); + switch (instance->type) { case PHY_TYPE_USB2: u2_phy_instance_init(tphy, instance); @@ -1134,6 +1288,7 @@ static struct phy *mtk_phy_xlate(struct device *dev, struct mtk_phy_instance *instance = NULL; struct device_node *phy_np = args->np; int index; + int ret; if (args->args_count != 1) { dev_err(dev, "invalid number of cells in 'phy' property\n"); @@ -1174,6 +1329,10 @@ static struct phy *mtk_phy_xlate(struct device *dev, return ERR_PTR(-EINVAL); } + ret = phy_efuse_get(tphy, instance); + if (ret) + return ERR_PTR(ret); + phy_parse_property(tphy, instance); phy_type_set(instance); @@ -1196,10 +1355,12 @@ static const struct mtk_phy_pdata tphy_v1_pdata = { static const struct mtk_phy_pdata tphy_v2_pdata = { .avoid_rx_sen_degradation = false, + .sw_efuse_supported = true, .version = MTK_PHY_V2, }; static const struct mtk_phy_pdata tphy_v3_pdata = { + .sw_efuse_supported = true, .version = MTK_PHY_V3, }; @@ -1210,6 +1371,7 @@ static const struct mtk_phy_pdata mt8173_pdata = { static const struct mtk_phy_pdata mt8195_pdata = { .sw_pll_48m_to_26m = true, + .sw_efuse_supported = true, .version = MTK_PHY_V3, }; -- cgit v1.2.3 From 1371b9a5632a637a93e3a7592c91f64d7067c369 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Sat, 18 Dec 2021 16:28:00 +0800 Subject: phy: mediatek: add helpers to update bits of registers Add three helpers mtk_phy_clear/set/update_bits() for registers operation Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20211218082802.5256-3-chunfeng.yun@mediatek.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/phy-mtk-io.h | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 drivers/phy/mediatek/phy-mtk-io.h diff --git a/drivers/phy/mediatek/phy-mtk-io.h b/drivers/phy/mediatek/phy-mtk-io.h new file mode 100644 index 000000000000..500fcdab165d --- /dev/null +++ b/drivers/phy/mediatek/phy-mtk-io.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) 2021 MediaTek Inc. + * + * Author: Chunfeng Yun + */ + +#ifndef __PHY_MTK_H__ +#define __PHY_MTK_H__ + +#include + +static inline void mtk_phy_clear_bits(void __iomem *reg, u32 bits) +{ + u32 tmp = readl(reg); + + tmp &= ~bits; + writel(tmp, reg); +} + +static inline void mtk_phy_set_bits(void __iomem *reg, u32 bits) +{ + u32 tmp = readl(reg); + + tmp |= bits; + writel(tmp, reg); +} + +static inline void mtk_phy_update_bits(void __iomem *reg, u32 mask, u32 val) +{ + u32 tmp = readl(reg); + + tmp &= ~mask; + tmp |= val & mask; + writel(tmp, reg); +} + +#endif -- cgit v1.2.3 From 9520bbf3cb2c12fdc41096262fb384b360279329 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Sat, 18 Dec 2021 16:28:01 +0800 Subject: phy: phy-mtk-xsphy: use new io helpers to access register Use new helpers mtk_phy_clear/set/update_bits() to access registers Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20211218082802.5256-4-chunfeng.yun@mediatek.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/phy-mtk-xsphy.c | 140 ++++++++++++----------------------- 1 file changed, 46 insertions(+), 94 deletions(-) diff --git a/drivers/phy/mediatek/phy-mtk-xsphy.c b/drivers/phy/mediatek/phy-mtk-xsphy.c index 8c51131945c0..c0cdb78f77fa 100644 --- a/drivers/phy/mediatek/phy-mtk-xsphy.c +++ b/drivers/phy/mediatek/phy-mtk-xsphy.c @@ -10,13 +10,14 @@ #include #include #include -#include #include #include #include #include #include +#include "phy-mtk-io.h" + /* u2 phy banks */ #define SSUSB_SIFSLV_MISC 0x000 #define SSUSB_SIFSLV_U2FREQ 0x100 @@ -126,26 +127,18 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, return; /* enable USB ring oscillator */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp |= P2A5_RG_HSTX_SRCAL_EN; - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_set_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); udelay(1); /* wait clock stable */ /* enable free run clock */ - tmp = readl(pbase + XSP_U2FREQ_FMMONR1); - tmp |= P2F_RG_FRCK_EN; - writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + mtk_phy_set_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); /* set cycle count as 1024 */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp &= ~(P2F_RG_CYCLECNT); - tmp |= P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT); - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_update_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_CYCLECNT, + P2F_RG_CYCLECNT_VAL(XSP_FM_DET_CYCLE_CNT)); /* enable frequency meter */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp |= P2F_RG_FREQDET_EN; - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_set_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* ignore return value */ readl_poll_timeout(pbase + XSP_U2FREQ_FMMONR1, tmp, @@ -154,14 +147,10 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, fm_out = readl(pbase + XSP_U2FREQ_MMONR0); /* disable frequency meter */ - tmp = readl(pbase + XSP_U2FREQ_FMCR0); - tmp &= ~P2F_RG_FREQDET_EN; - writel(tmp, pbase + XSP_U2FREQ_FMCR0); + mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* disable free run clock */ - tmp = readl(pbase + XSP_U2FREQ_FMMONR1); - tmp &= ~P2F_RG_FRCK_EN; - writel(tmp, pbase + XSP_U2FREQ_FMMONR1); + mtk_phy_clear_bits(pbase + XSP_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); if (fm_out) { /* (1024 / FM_OUT) x reference clock frequency x coefficient */ @@ -177,31 +166,22 @@ static void u2_phy_slew_rate_calibrate(struct mtk_xsphy *xsphy, xsphy->src_ref_clk, xsphy->src_coef); /* set HS slew rate */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCTRL; - tmp |= P2A5_RG_HSTX_SRCTRL_VAL(calib_val); - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_update_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, + P2A5_RG_HSTX_SRCTRL_VAL(calib_val)); /* disable USB ring oscillator */ - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCAL_EN; - writel(tmp, pbase + XSP_USBPHYACR5); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCAL_EN); } static void u2_phy_instance_init(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; /* DP/DM BC1.1 path Disable */ - tmp = readl(pbase + XSP_USBPHYACR6); - tmp &= ~P2A6_RG_BC11_SW_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_BC11_SW_EN); - tmp = readl(pbase + XSP_USBPHYACR0); - tmp |= P2A0_RG_INTR_EN; - writel(tmp, pbase + XSP_USBPHYACR0); + mtk_phy_set_bits(pbase + XSP_USBPHYACR0, P2A0_RG_INTR_EN); } static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, @@ -209,16 +189,12 @@ static void u2_phy_instance_power_on(struct mtk_xsphy *xsphy, { void __iomem *pbase = inst->port_base; u32 index = inst->index; - u32 tmp; - tmp = readl(pbase + XSP_USBPHYACR6); - tmp |= P2A6_RG_OTG_VBUSCMP_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_set_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); - tmp = readl(pbase + XSP_U2PHYDTM1); - tmp |= P2D_RG_VBUSVALID | P2D_RG_AVALID; - tmp &= ~P2D_RG_SESSEND; - writel(tmp, pbase + XSP_U2PHYDTM1); + mtk_phy_update_bits(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_VBUSVALID | P2D_RG_AVALID); dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); } @@ -228,16 +204,12 @@ static void u2_phy_instance_power_off(struct mtk_xsphy *xsphy, { void __iomem *pbase = inst->port_base; u32 index = inst->index; - u32 tmp; - tmp = readl(pbase + XSP_USBPHYACR6); - tmp &= ~P2A6_RG_OTG_VBUSCMP_EN; - writel(tmp, pbase + XSP_USBPHYACR6); + mtk_phy_clear_bits(pbase + XSP_USBPHYACR6, P2A6_RG_OTG_VBUSCMP_EN); - tmp = readl(pbase + XSP_U2PHYDTM1); - tmp &= ~(P2D_RG_VBUSVALID | P2D_RG_AVALID); - tmp |= P2D_RG_SESSEND; - writel(tmp, pbase + XSP_U2PHYDTM1); + mtk_phy_update_bits(pbase + XSP_U2PHYDTM1, + P2D_RG_VBUSVALID | P2D_RG_AVALID | P2D_RG_SESSEND, + P2D_RG_SESSEND); dev_dbg(xsphy->dev, "%s(%d)\n", __func__, index); } @@ -306,63 +278,43 @@ static void u2_phy_props_set(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; - if (inst->efuse_intr) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_INTR_CAL; - tmp |= P2A1_RG_INTR_CAL_VAL(inst->efuse_intr); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->efuse_intr) + mtk_phy_update_bits(pbase + XSP_USBPHYACR1, P2A1_RG_INTR_CAL, + P2A1_RG_INTR_CAL_VAL(inst->efuse_intr)); - if (inst->eye_src) { - tmp = readl(pbase + XSP_USBPHYACR5); - tmp &= ~P2A5_RG_HSTX_SRCTRL; - tmp |= P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src); - writel(tmp, pbase + XSP_USBPHYACR5); - } + if (inst->eye_src) + mtk_phy_update_bits(pbase + XSP_USBPHYACR5, P2A5_RG_HSTX_SRCTRL, + P2A5_RG_HSTX_SRCTRL_VAL(inst->eye_src)); - if (inst->eye_vrt) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_VRT_SEL; - tmp |= P2A1_RG_VRT_SEL_VAL(inst->eye_vrt); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->eye_vrt) + mtk_phy_update_bits(pbase + XSP_USBPHYACR1, P2A1_RG_VRT_SEL, + P2A1_RG_VRT_SEL_VAL(inst->eye_vrt)); - if (inst->eye_term) { - tmp = readl(pbase + XSP_USBPHYACR1); - tmp &= ~P2A1_RG_TERM_SEL; - tmp |= P2A1_RG_TERM_SEL_VAL(inst->eye_term); - writel(tmp, pbase + XSP_USBPHYACR1); - } + if (inst->eye_term) + mtk_phy_update_bits(pbase + XSP_USBPHYACR1, P2A1_RG_TERM_SEL, + P2A1_RG_TERM_SEL_VAL(inst->eye_term)); } static void u3_phy_props_set(struct mtk_xsphy *xsphy, struct xsphy_instance *inst) { void __iomem *pbase = inst->port_base; - u32 tmp; - if (inst->efuse_intr) { - tmp = readl(xsphy->glb_base + SSPXTP_PHYA_GLB_00); - tmp &= ~RG_XTP_GLB_BIAS_INTR_CTRL; - tmp |= RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr); - writel(tmp, xsphy->glb_base + SSPXTP_PHYA_GLB_00); - } + if (inst->efuse_intr) + mtk_phy_update_bits(xsphy->glb_base + SSPXTP_PHYA_GLB_00, + RG_XTP_GLB_BIAS_INTR_CTRL, + RG_XTP_GLB_BIAS_INTR_CTRL_VAL(inst->efuse_intr)); - if (inst->efuse_tx_imp) { - tmp = readl(pbase + SSPXTP_PHYA_LN_04); - tmp &= ~RG_XTP_LN0_TX_IMPSEL; - tmp |= RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp); - writel(tmp, pbase + SSPXTP_PHYA_LN_04); - } + if (inst->efuse_tx_imp) + mtk_phy_update_bits(pbase + SSPXTP_PHYA_LN_04, + RG_XTP_LN0_TX_IMPSEL, + RG_XTP_LN0_TX_IMPSEL_VAL(inst->efuse_tx_imp)); - if (inst->efuse_rx_imp) { - tmp = readl(pbase + SSPXTP_PHYA_LN_14); - tmp &= ~RG_XTP_LN0_RX_IMPSEL; - tmp |= RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp); - writel(tmp, pbase + SSPXTP_PHYA_LN_14); - } + if (inst->efuse_rx_imp) + mtk_phy_update_bits(pbase + SSPXTP_PHYA_LN_14, + RG_XTP_LN0_RX_IMPSEL, + RG_XTP_LN0_RX_IMPSEL_VAL(inst->efuse_rx_imp)); } static int mtk_phy_init(struct phy *phy) -- cgit v1.2.3 From 33d18746fa514d21df9931a88d20530981f8a064 Mon Sep 17 00:00:00 2001 From: Chunfeng Yun Date: Sat, 18 Dec 2021 16:28:02 +0800 Subject: phy: phy-mtk-tphy: use new io helpers to access register Use new helpers mtk_phy_clear/set/update_bits() to access registers Signed-off-by: Chunfeng Yun Link: https://lore.kernel.org/r/20211218082802.5256-5-chunfeng.yun@mediatek.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/phy-mtk-tphy.c | 504 +++++++++++++----------------------- 1 file changed, 179 insertions(+), 325 deletions(-) diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index 98a942c607a6..6d307102f4f6 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include @@ -19,6 +18,8 @@ #include #include +#include "phy-mtk-io.h" + /* version V1 sub-banks offset base address */ /* banks shared by multiple phys */ #define SSUSB_SIFSLV_V1_SPLLC 0x000 /* shared by u3 phys */ @@ -401,15 +402,11 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, return; /* enable USB ring oscillator */ - tmp = readl(com + U3P_USBPHYACR5); - tmp |= PA5_RG_U2_HSTX_SRCAL_EN; - writel(tmp, com + U3P_USBPHYACR5); + mtk_phy_set_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCAL_EN); udelay(1); /*enable free run clock */ - tmp = readl(fmreg + U3P_U2FREQ_FMMONR1); - tmp |= P2F_RG_FRCK_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMMONR1); + mtk_phy_set_bits(fmreg + U3P_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); /* set cycle count as 1024, and select u2 channel */ tmp = readl(fmreg + U3P_U2FREQ_FMCR0); @@ -421,9 +418,7 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, writel(tmp, fmreg + U3P_U2FREQ_FMCR0); /* enable frequency meter */ - tmp = readl(fmreg + U3P_U2FREQ_FMCR0); - tmp |= P2F_RG_FREQDET_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMCR0); + mtk_phy_set_bits(fmreg + U3P_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /* ignore return value */ readl_poll_timeout(fmreg + U3P_U2FREQ_FMMONR1, tmp, @@ -432,14 +427,10 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, fm_out = readl(fmreg + U3P_U2FREQ_VALUE); /* disable frequency meter */ - tmp = readl(fmreg + U3P_U2FREQ_FMCR0); - tmp &= ~P2F_RG_FREQDET_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMCR0); + mtk_phy_clear_bits(fmreg + U3P_U2FREQ_FMCR0, P2F_RG_FREQDET_EN); /*disable free run clock */ - tmp = readl(fmreg + U3P_U2FREQ_FMMONR1); - tmp &= ~P2F_RG_FRCK_EN; - writel(tmp, fmreg + U3P_U2FREQ_FMMONR1); + mtk_phy_clear_bits(fmreg + U3P_U2FREQ_FMMONR1, P2F_RG_FRCK_EN); if (fm_out) { /* ( 1024 / FM_OUT ) x reference clock frequency x coef */ @@ -455,63 +446,44 @@ static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, tphy->src_ref_clk, tphy->src_coef); /* set HS slew rate */ - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HSTX_SRCTRL; - tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val); - writel(tmp, com + U3P_USBPHYACR5); + mtk_phy_update_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL, + PA5_RG_U2_HSTX_SRCTRL_VAL(calibration_val)); /* disable USB ring oscillator */ - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HSTX_SRCAL_EN; - writel(tmp, com + U3P_USBPHYACR5); + mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCAL_EN); } static void u3_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { struct u3phy_banks *u3_banks = &instance->u3_banks; - u32 tmp; /* gating PCIe Analog XTAL clock */ - tmp = readl(u3_banks->spllc + U3P_SPLLC_XTALCTL3); - tmp |= XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD; - writel(tmp, u3_banks->spllc + U3P_SPLLC_XTALCTL3); + mtk_phy_set_bits(u3_banks->spllc + U3P_SPLLC_XTALCTL3, + XC3_RG_U3_XTAL_RX_PWD | XC3_RG_U3_FRC_XTAL_RX_PWD); /* gating XSQ */ - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0); - tmp &= ~P3A_RG_XTAL_EXT_EN_U3; - tmp |= P3A_RG_XTAL_EXT_EN_U3_VAL(2); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG9); - tmp &= ~P3A_RG_RX_DAC_MUX; - tmp |= P3A_RG_RX_DAC_MUX_VAL(4); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG9); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG6); - tmp &= ~P3A_RG_TX_EIDLE_CM; - tmp |= P3A_RG_TX_EIDLE_CM_VAL(0xe); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG6); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_CDR1); - tmp &= ~(P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1); - tmp |= P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_CDR1); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_LFPS1); - tmp &= ~P3D_RG_FWAKE_TH; - tmp |= P3D_RG_FWAKE_TH_VAL(0x34); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_LFPS1); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1); - tmp &= ~P3D_RG_RXDET_STB2_SET; - tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2); - tmp &= ~P3D_RG_RXDET_STB2_SET_P3; - tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2); + mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_DA_REG0, + P3A_RG_XTAL_EXT_EN_U3, P3A_RG_XTAL_EXT_EN_U3_VAL(2)); + + mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_REG9, + P3A_RG_RX_DAC_MUX, P3A_RG_RX_DAC_MUX_VAL(4)); + + mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_REG6, + P3A_RG_TX_EIDLE_CM, P3A_RG_TX_EIDLE_CM_VAL(0xe)); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_CDR1, + P3D_RG_CDR_BIR_LTD0 | P3D_RG_CDR_BIR_LTD1, + P3D_RG_CDR_BIR_LTD0_VAL(0xc) | P3D_RG_CDR_BIR_LTD1_VAL(0x3)); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_LFPS1, + P3D_RG_FWAKE_TH, P3D_RG_FWAKE_TH_VAL(0x34)); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET1, + P3D_RG_RXDET_STB2_SET, P3D_RG_RXDET_STB2_SET_VAL(0x10)); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET2, + P3D_RG_RXDET_STB2_SET_P3, P3D_RG_RXDET_STB2_SET_P3_VAL(0x10)); dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); } @@ -521,26 +493,20 @@ static void u2_phy_pll_26m_set(struct mtk_tphy *tphy, { struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; - u32 tmp; if (!tphy->pdata->sw_pll_48m_to_26m) return; - tmp = readl(com + U3P_USBPHYACR0); - tmp &= ~PA0_USB20_PLL_PREDIV; - tmp |= PA0_USB20_PLL_PREDIV_VAL(0); - writel(tmp, com + U3P_USBPHYACR0); + mtk_phy_update_bits(com + U3P_USBPHYACR0, PA0_USB20_PLL_PREDIV, + PA0_USB20_PLL_PREDIV_VAL(0)); - tmp = readl(com + U3P_USBPHYACR2); - tmp &= ~PA2_RG_U2PLL_BW; - tmp |= PA2_RG_U2PLL_BW_VAL(3); - writel(tmp, com + U3P_USBPHYACR2); + mtk_phy_update_bits(com + U3P_USBPHYACR2, PA2_RG_U2PLL_BW, + PA2_RG_U2PLL_BW_VAL(3)); writel(P2R_RG_U2PLL_FBDIV_26M, com + U3P_U2PHYA_RESV); - tmp = readl(com + U3P_U2PHYA_RESV1); - tmp |= P2R_RG_U2PLL_FRA_EN | P2R_RG_U2PLL_REFCLK_SEL; - writel(tmp, com + U3P_U2PHYA_RESV1); + mtk_phy_set_bits(com + U3P_U2PHYA_RESV1, + P2R_RG_U2PLL_FRA_EN | P2R_RG_U2PLL_REFCLK_SEL); } static void u2_phy_instance_init(struct mtk_tphy *tphy, @@ -549,58 +515,40 @@ static void u2_phy_instance_init(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; /* switch to USB function, and enable usb pll */ - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM); - tmp |= P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0); - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_FORCE_UART_EN | P2C_FORCE_SUSPENDM); + + mtk_phy_update_bits(com + U3P_U2PHYDTM0, P2C_RG_XCVRSEL | P2C_RG_DATAIN, + P2C_RG_XCVRSEL_VAL(1) | P2C_RG_DATAIN_VAL(0)); - tmp = readl(com + U3P_U2PHYDTM1); - tmp &= ~P2C_RG_UART_EN; - writel(tmp, com + U3P_U2PHYDTM1); + mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_UART_EN); - tmp = readl(com + U3P_USBPHYACR0); - tmp |= PA0_RG_USB20_INTR_EN; - writel(tmp, com + U3P_USBPHYACR0); + mtk_phy_set_bits(com + U3P_USBPHYACR0, PA0_RG_USB20_INTR_EN); /* disable switch 100uA current to SSUSB */ - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HS_100U_U3_EN; - writel(tmp, com + U3P_USBPHYACR5); - - if (!index) { - tmp = readl(com + U3P_U2PHYACR4); - tmp &= ~P2C_U2_GPIO_CTR_MSK; - writel(tmp, com + U3P_U2PHYACR4); - } + mtk_phy_clear_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HS_100U_U3_EN); + + if (!index) + mtk_phy_clear_bits(com + U3P_U2PHYACR4, P2C_U2_GPIO_CTR_MSK); if (tphy->pdata->avoid_rx_sen_degradation) { if (!index) { - tmp = readl(com + U3P_USBPHYACR2); - tmp |= PA2_RG_SIF_U2PLL_FORCE_EN; - writel(tmp, com + U3P_USBPHYACR2); + mtk_phy_set_bits(com + U3P_USBPHYACR2, PA2_RG_SIF_U2PLL_FORCE_EN); - tmp = readl(com + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); } else { - tmp = readl(com + U3D_U2PHYDCR0); - tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_set_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); - tmp = readl(com + U3P_U2PHYDTM0); - tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_set_bits(com + U3P_U2PHYDTM0, + P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); } } - tmp = readl(com + U3P_USBPHYACR6); - tmp &= ~PA6_RG_U2_BC11_SW_EN; /* DP/DM BC1.1 path Disable */ - tmp &= ~PA6_RG_U2_SQTH; - tmp |= PA6_RG_U2_SQTH_VAL(2); - writel(tmp, com + U3P_USBPHYACR6); + /* DP/DM BC1.1 path Disable */ + mtk_phy_clear_bits(com + U3P_USBPHYACR6, PA6_RG_U2_BC11_SW_EN); + + mtk_phy_update_bits(com + U3P_USBPHYACR6, PA6_RG_U2_SQTH, PA6_RG_U2_SQTH_VAL(2)); /* Workaround only for mt8195, HW fix it for others (V3) */ u2_phy_pll_26m_set(tphy, instance); @@ -614,30 +562,21 @@ static void u2_phy_instance_power_on(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK); - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, + P2C_RG_XCVRSEL | P2C_RG_DATAIN | P2C_DTM0_PART_MASK); /* OTG Enable */ - tmp = readl(com + U3P_USBPHYACR6); - tmp |= PA6_RG_U2_OTG_VBUSCMP_EN; - writel(tmp, com + U3P_USBPHYACR6); + mtk_phy_set_bits(com + U3P_USBPHYACR6, PA6_RG_U2_OTG_VBUSCMP_EN); + + mtk_phy_set_bits(com + U3P_U2PHYDTM1, P2C_RG_VBUSVALID | P2C_RG_AVALID); - tmp = readl(com + U3P_U2PHYDTM1); - tmp |= P2C_RG_VBUSVALID | P2C_RG_AVALID; - tmp &= ~P2C_RG_SESSEND; - writel(tmp, com + U3P_U2PHYDTM1); + mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_SESSEND); if (tphy->pdata->avoid_rx_sen_degradation && index) { - tmp = readl(com + U3D_U2PHYDCR0); - tmp |= P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_set_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); - tmp = readl(com + U3P_U2PHYDTM0); - tmp |= P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM; - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_set_bits(com + U3P_U2PHYDTM0, P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); } dev_dbg(tphy->dev, "%s(%d)\n", __func__, index); } @@ -648,30 +587,20 @@ static void u2_phy_instance_power_off(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_RG_XCVRSEL | P2C_RG_DATAIN); - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_RG_XCVRSEL | P2C_RG_DATAIN); /* OTG Disable */ - tmp = readl(com + U3P_USBPHYACR6); - tmp &= ~PA6_RG_U2_OTG_VBUSCMP_EN; - writel(tmp, com + U3P_USBPHYACR6); + mtk_phy_clear_bits(com + U3P_USBPHYACR6, PA6_RG_U2_OTG_VBUSCMP_EN); + + mtk_phy_clear_bits(com + U3P_U2PHYDTM1, P2C_RG_VBUSVALID | P2C_RG_AVALID); - tmp = readl(com + U3P_U2PHYDTM1); - tmp &= ~(P2C_RG_VBUSVALID | P2C_RG_AVALID); - tmp |= P2C_RG_SESSEND; - writel(tmp, com + U3P_U2PHYDTM1); + mtk_phy_set_bits(com + U3P_U2PHYDTM1, P2C_RG_SESSEND); if (tphy->pdata->avoid_rx_sen_degradation && index) { - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~(P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_RG_SUSPENDM | P2C_FORCE_SUSPENDM); - tmp = readl(com + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); } dev_dbg(tphy->dev, "%s(%d)\n", __func__, index); @@ -683,16 +612,11 @@ static void u2_phy_instance_exit(struct mtk_tphy *tphy, struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; u32 index = instance->index; - u32 tmp; if (tphy->pdata->avoid_rx_sen_degradation && index) { - tmp = readl(com + U3D_U2PHYDCR0); - tmp &= ~P2C_RG_SIF_U2PLL_FORCE_ON; - writel(tmp, com + U3D_U2PHYDCR0); + mtk_phy_clear_bits(com + U3D_U2PHYDCR0, P2C_RG_SIF_U2PLL_FORCE_ON); - tmp = readl(com + U3P_U2PHYDTM0); - tmp &= ~P2C_FORCE_SUSPENDM; - writel(tmp, com + U3P_U2PHYDTM0); + mtk_phy_clear_bits(com + U3P_U2PHYDTM0, P2C_FORCE_SUSPENDM); } } @@ -725,69 +649,50 @@ static void pcie_phy_instance_init(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { struct u3phy_banks *u3_banks = &instance->u3_banks; - u32 tmp; + void __iomem *phya = u3_banks->phya; if (tphy->pdata->version != MTK_PHY_V1) return; - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG0); - tmp &= ~(P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H); - tmp |= P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG0); + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG0, + P3A_RG_XTAL_EXT_PE1H | P3A_RG_XTAL_EXT_PE2H, + P3A_RG_XTAL_EXT_PE1H_VAL(0x2) | P3A_RG_XTAL_EXT_PE2H_VAL(0x2)); /* ref clk drive */ - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG1); - tmp &= ~P3A_RG_CLKDRV_AMP; - tmp |= P3A_RG_CLKDRV_AMP_VAL(0x4); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG1); + mtk_phy_update_bits(phya + U3P_U3_PHYA_REG1, P3A_RG_CLKDRV_AMP, + P3A_RG_CLKDRV_AMP_VAL(0x4)); - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); - tmp &= ~P3A_RG_CLKDRV_OFF; - tmp |= P3A_RG_CLKDRV_OFF_VAL(0x1); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); + mtk_phy_update_bits(phya + U3P_U3_PHYA_REG0, P3A_RG_CLKDRV_OFF, + P3A_RG_CLKDRV_OFF_VAL(0x1)); /* SSC delta -5000ppm */ - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG20); - tmp &= ~P3A_RG_PLL_DELTA1_PE2H; - tmp |= P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG20); + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG20, P3A_RG_PLL_DELTA1_PE2H, + P3A_RG_PLL_DELTA1_PE2H_VAL(0x3c)); - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG25); - tmp &= ~P3A_RG_PLL_DELTA_PE2H; - tmp |= P3A_RG_PLL_DELTA_PE2H_VAL(0x36); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG25); + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG25, P3A_RG_PLL_DELTA_PE2H, + P3A_RG_PLL_DELTA_PE2H_VAL(0x36)); /* change pll BW 0.6M */ - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG5); - tmp &= ~(P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H); - tmp |= P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG5); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG4); - tmp &= ~(P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H); - tmp |= P3A_RG_PLL_BC_PE2H_VAL(0x3); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG4); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG6); - tmp &= ~P3A_RG_PLL_IR_PE2H; - tmp |= P3A_RG_PLL_IR_PE2H_VAL(0x2); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG6); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_DA_REG7); - tmp &= ~P3A_RG_PLL_BP_PE2H; - tmp |= P3A_RG_PLL_BP_PE2H_VAL(0xa); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_DA_REG7); + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG5, + P3A_RG_PLL_BR_PE2H | P3A_RG_PLL_IC_PE2H, + P3A_RG_PLL_BR_PE2H_VAL(0x1) | P3A_RG_PLL_IC_PE2H_VAL(0x1)); + + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG4, + P3A_RG_PLL_DIVEN_PE2H | P3A_RG_PLL_BC_PE2H, + P3A_RG_PLL_BC_PE2H_VAL(0x3)); + + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG6, P3A_RG_PLL_IR_PE2H, + P3A_RG_PLL_IR_PE2H_VAL(0x2)); + + mtk_phy_update_bits(phya + U3P_U3_PHYA_DA_REG7, P3A_RG_PLL_BP_PE2H, + P3A_RG_PLL_BP_PE2H_VAL(0xa)); /* Tx Detect Rx Timing: 10us -> 5us */ - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET1); - tmp &= ~P3D_RG_RXDET_STB2_SET; - tmp |= P3D_RG_RXDET_STB2_SET_VAL(0x10); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET1); + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET1, + P3D_RG_RXDET_STB2_SET, P3D_RG_RXDET_STB2_SET_VAL(0x10)); - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RXDET2); - tmp &= ~P3D_RG_RXDET_STB2_SET_P3; - tmp |= P3D_RG_RXDET_STB2_SET_P3_VAL(0x10); - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RXDET2); + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_RXDET2, + P3D_RG_RXDET_STB2_SET_P3, P3D_RG_RXDET_STB2_SET_P3_VAL(0x10)); /* wait for PCIe subsys register to active */ usleep_range(2500, 3000); @@ -798,15 +703,12 @@ static void pcie_phy_instance_power_on(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { struct u3phy_banks *bank = &instance->u3_banks; - u32 tmp; - tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); - tmp &= ~(P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); - writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); + mtk_phy_clear_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); - tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); - tmp &= ~(P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); - writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); + mtk_phy_clear_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); } static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, @@ -814,15 +716,12 @@ static void pcie_phy_instance_power_off(struct mtk_tphy *tphy, { struct u3phy_banks *bank = &instance->u3_banks; - u32 tmp; - tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLD); - tmp |= P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST; - writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLD); + mtk_phy_set_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLD, + P3C_FORCE_IP_SW_RST | P3C_REG_IP_SW_RST); - tmp = readl(bank->chip + U3P_U3_CHIP_GPIO_CTLE); - tmp |= P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD; - writel(tmp, bank->chip + U3P_U3_CHIP_GPIO_CTLE); + mtk_phy_set_bits(bank->chip + U3P_U3_CHIP_GPIO_CTLE, + P3C_RG_SWRST_U3_PHYD_FORCE_EN | P3C_RG_SWRST_U3_PHYD); } static void sata_phy_instance_init(struct mtk_tphy *tphy, @@ -830,55 +729,42 @@ static void sata_phy_instance_init(struct mtk_tphy *tphy, { struct u3phy_banks *u3_banks = &instance->u3_banks; void __iomem *phyd = u3_banks->phyd; - u32 tmp; /* charge current adjustment */ - tmp = readl(phyd + ANA_RG_CTRL_SIGNAL6); - tmp &= ~(RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK); - tmp |= RG_CDR_BIRLTR_GEN1_VAL(0x6) | RG_CDR_BC_GEN1_VAL(0x1a); - writel(tmp, phyd + ANA_RG_CTRL_SIGNAL6); - - tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL4); - tmp &= ~RG_CDR_BIRLTD0_GEN1_MSK; - tmp |= RG_CDR_BIRLTD0_GEN1_VAL(0x18); - writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL4); - - tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL5); - tmp &= ~RG_CDR_BIRLTD0_GEN3_MSK; - tmp |= RG_CDR_BIRLTD0_GEN3_VAL(0x06); - writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL5); - - tmp = readl(phyd + ANA_RG_CTRL_SIGNAL4); - tmp &= ~(RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK); - tmp |= RG_CDR_BICLTR_GEN1_VAL(0x0c) | RG_CDR_BR_GEN2_VAL(0x07); - writel(tmp, phyd + ANA_RG_CTRL_SIGNAL4); - - tmp = readl(phyd + PHYD_CTRL_SIGNAL_MODE4); - tmp &= ~(RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK); - tmp |= RG_CDR_BICLTD0_GEN1_VAL(0x08) | RG_CDR_BICLTD1_GEN1_VAL(0x02); - writel(tmp, phyd + PHYD_CTRL_SIGNAL_MODE4); - - tmp = readl(phyd + PHYD_DESIGN_OPTION2); - tmp &= ~RG_LOCK_CNT_SEL_MSK; - tmp |= RG_LOCK_CNT_SEL_VAL(0x02); - writel(tmp, phyd + PHYD_DESIGN_OPTION2); - - tmp = readl(phyd + PHYD_DESIGN_OPTION9); - tmp &= ~(RG_T2_MIN_MSK | RG_TG_MIN_MSK | - RG_T2_MAX_MSK | RG_TG_MAX_MSK); - tmp |= RG_T2_MIN_VAL(0x12) | RG_TG_MIN_VAL(0x04) | - RG_T2_MAX_VAL(0x31) | RG_TG_MAX_VAL(0x0e); - writel(tmp, phyd + PHYD_DESIGN_OPTION9); - - tmp = readl(phyd + ANA_RG_CTRL_SIGNAL1); - tmp &= ~RG_IDRV_0DB_GEN1_MSK; - tmp |= RG_IDRV_0DB_GEN1_VAL(0x20); - writel(tmp, phyd + ANA_RG_CTRL_SIGNAL1); - - tmp = readl(phyd + ANA_EQ_EYE_CTRL_SIGNAL1); - tmp &= ~RG_EQ_DLEQ_LFI_GEN1_MSK; - tmp |= RG_EQ_DLEQ_LFI_GEN1_VAL(0x03); - writel(tmp, phyd + ANA_EQ_EYE_CTRL_SIGNAL1); + mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL6, + RG_CDR_BIRLTR_GEN1_MSK | RG_CDR_BC_GEN1_MSK, + RG_CDR_BIRLTR_GEN1_VAL(0x6) | RG_CDR_BC_GEN1_VAL(0x1a)); + + mtk_phy_update_bits(phyd + ANA_EQ_EYE_CTRL_SIGNAL4, RG_CDR_BIRLTD0_GEN1_MSK, + RG_CDR_BIRLTD0_GEN1_VAL(0x18)); + + mtk_phy_update_bits(phyd + ANA_EQ_EYE_CTRL_SIGNAL5, RG_CDR_BIRLTD0_GEN3_MSK, + RG_CDR_BIRLTD0_GEN3_VAL(0x06)); + + mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL4, + RG_CDR_BICLTR_GEN1_MSK | RG_CDR_BR_GEN2_MSK, + RG_CDR_BICLTR_GEN1_VAL(0x0c) | RG_CDR_BR_GEN2_VAL(0x07)); + + mtk_phy_update_bits(phyd + PHYD_CTRL_SIGNAL_MODE4, + RG_CDR_BICLTD0_GEN1_MSK | RG_CDR_BICLTD1_GEN1_MSK, + RG_CDR_BICLTD0_GEN1_VAL(0x08) | RG_CDR_BICLTD1_GEN1_VAL(0x02)); + + mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION2, RG_LOCK_CNT_SEL_MSK, + RG_LOCK_CNT_SEL_VAL(0x02)); + + mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9, + RG_T2_MIN_MSK | RG_TG_MIN_MSK, + RG_T2_MIN_VAL(0x12) | RG_TG_MIN_VAL(0x04)); + + mtk_phy_update_bits(phyd + PHYD_DESIGN_OPTION9, + RG_T2_MAX_MSK | RG_TG_MAX_MSK, + RG_T2_MAX_VAL(0x31) | RG_TG_MAX_VAL(0x0e)); + + mtk_phy_update_bits(phyd + ANA_RG_CTRL_SIGNAL1, RG_IDRV_0DB_GEN1_MSK, + RG_IDRV_0DB_GEN1_VAL(0x20)); + + mtk_phy_update_bits(phyd + ANA_EQ_EYE_CTRL_SIGNAL1, RG_EQ_DLEQ_LFI_GEN1_MSK, + RG_EQ_DLEQ_LFI_GEN1_VAL(0x03)); dev_dbg(tphy->dev, "%s(%d)\n", __func__, instance->index); } @@ -966,48 +852,29 @@ static void u2_phy_props_set(struct mtk_tphy *tphy, { struct u2phy_banks *u2_banks = &instance->u2_banks; void __iomem *com = u2_banks->com; - u32 tmp; - if (instance->bc12_en) { - tmp = readl(com + U3P_U2PHYBC12C); - tmp |= P2C_RG_CHGDT_EN; /* BC1.2 path Enable */ - writel(tmp, com + U3P_U2PHYBC12C); - } + if (instance->bc12_en) /* BC1.2 path Enable */ + mtk_phy_set_bits(com + U3P_U2PHYBC12C, P2C_RG_CHGDT_EN); - if (tphy->pdata->version < MTK_PHY_V3 && instance->eye_src) { - tmp = readl(com + U3P_USBPHYACR5); - tmp &= ~PA5_RG_U2_HSTX_SRCTRL; - tmp |= PA5_RG_U2_HSTX_SRCTRL_VAL(instance->eye_src); - writel(tmp, com + U3P_USBPHYACR5); - } + if (tphy->pdata->version < MTK_PHY_V3 && instance->eye_src) + mtk_phy_update_bits(com + U3P_USBPHYACR5, PA5_RG_U2_HSTX_SRCTRL, + PA5_RG_U2_HSTX_SRCTRL_VAL(instance->eye_src)); - if (instance->eye_vrt) { - tmp = readl(com + U3P_USBPHYACR1); - tmp &= ~PA1_RG_VRT_SEL; - tmp |= PA1_RG_VRT_SEL_VAL(instance->eye_vrt); - writel(tmp, com + U3P_USBPHYACR1); - } + if (instance->eye_vrt) + mtk_phy_update_bits(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL, + PA1_RG_VRT_SEL_VAL(instance->eye_vrt)); - if (instance->eye_term) { - tmp = readl(com + U3P_USBPHYACR1); - tmp &= ~PA1_RG_TERM_SEL; - tmp |= PA1_RG_TERM_SEL_VAL(instance->eye_term); - writel(tmp, com + U3P_USBPHYACR1); - } + if (instance->eye_term) + mtk_phy_update_bits(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL, + PA1_RG_TERM_SEL_VAL(instance->eye_term)); - if (instance->intr) { - tmp = readl(com + U3P_USBPHYACR1); - tmp &= ~PA1_RG_INTR_CAL; - tmp |= PA1_RG_INTR_CAL_VAL(instance->intr); - writel(tmp, com + U3P_USBPHYACR1); - } + if (instance->intr) + mtk_phy_update_bits(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, + PA1_RG_INTR_CAL_VAL(instance->intr)); - if (instance->discth) { - tmp = readl(com + U3P_USBPHYACR6); - tmp &= ~PA6_RG_U2_DISCTH; - tmp |= PA6_RG_U2_DISCTH_VAL(instance->discth); - writel(tmp, com + U3P_USBPHYACR6); - } + if (instance->discth) + mtk_phy_update_bits(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH, + PA6_RG_U2_DISCTH_VAL(instance->discth)); } /* type switch for usb3/pcie/sgmii/sata */ @@ -1147,44 +1014,31 @@ static void phy_efuse_set(struct mtk_phy_instance *instance) struct device *dev = &instance->phy->dev; struct u2phy_banks *u2_banks = &instance->u2_banks; struct u3phy_banks *u3_banks = &instance->u3_banks; - u32 tmp; if (!instance->efuse_sw_en) return; switch (instance->type) { case PHY_TYPE_USB2: - tmp = readl(u2_banks->misc + U3P_MISC_REG1); - tmp |= MR1_EFUSE_AUTO_LOAD_DIS; - writel(tmp, u2_banks->misc + U3P_MISC_REG1); - - tmp = readl(u2_banks->com + U3P_USBPHYACR1); - tmp &= ~PA1_RG_INTR_CAL; - tmp |= PA1_RG_INTR_CAL_VAL(instance->efuse_intr); - writel(tmp, u2_banks->com + U3P_USBPHYACR1); + mtk_phy_set_bits(u2_banks->misc + U3P_MISC_REG1, MR1_EFUSE_AUTO_LOAD_DIS); + + mtk_phy_update_bits(u2_banks->com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, + PA1_RG_INTR_CAL_VAL(instance->efuse_intr)); break; case PHY_TYPE_USB3: case PHY_TYPE_PCIE: - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); - tmp |= P3D_RG_EFUSE_AUTO_LOAD_DIS; - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_RSV); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); - tmp &= ~P3D_RG_TX_IMPEL; - tmp |= P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp); - tmp |= P3D_RG_FORCE_TX_IMPEL; - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); - - tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); - tmp &= ~P3D_RG_RX_IMPEL; - tmp |= P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp); - tmp |= P3D_RG_FORCE_RX_IMPEL; - writel(tmp, u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); - - tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); - tmp &= ~P3A_RG_IEXT_INTR; - tmp |= P3A_RG_IEXT_INTR_VAL(instance->efuse_intr); - writel(tmp, u3_banks->phya + U3P_U3_PHYA_REG0); + mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_RSV, P3D_RG_EFUSE_AUTO_LOAD_DIS); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL, + P3D_RG_TX_IMPEL_VAL(instance->efuse_tx_imp)); + mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL); + + mtk_phy_update_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL, + P3D_RG_RX_IMPEL_VAL(instance->efuse_rx_imp)); + mtk_phy_set_bits(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL); + + mtk_phy_update_bits(u3_banks->phya + U3P_U3_PHYA_REG0, P3A_RG_IEXT_INTR, + P3A_RG_IEXT_INTR_VAL(instance->efuse_intr)); break; default: dev_warn(dev, "no sw efuse for type %d\n", instance->type); -- cgit v1.2.3 From 898c7a9ec81620125f2463714a0f4dea18ad6e54 Mon Sep 17 00:00:00 2001 From: Ryuta NAKANISHI Date: Wed, 22 Dec 2021 14:19:29 +0900 Subject: phy: uniphier-usb3ss: fix unintended writing zeros to PHY register Similar to commit 4a90bbb478db ("phy: uniphier-pcie: Fix updating phy parameters"), in function uniphier_u3ssphy_set_param(), unintentionally write zeros to other fields when writing PHY registers. Fixes: 5ab43d0f8697 ("phy: socionext: add USB3 PHY driver for UniPhier SoC") Signed-off-by: Ryuta NAKANISHI Signed-off-by: Kunihiko Hayashi Link: https://lore.kernel.org/r/1640150369-4134-1-git-send-email-hayashi.kunihiko@socionext.com Signed-off-by: Vinod Koul --- drivers/phy/socionext/phy-uniphier-usb3ss.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/phy/socionext/phy-uniphier-usb3ss.c b/drivers/phy/socionext/phy-uniphier-usb3ss.c index 7ce611c2088b..f402ed8732fd 100644 --- a/drivers/phy/socionext/phy-uniphier-usb3ss.c +++ b/drivers/phy/socionext/phy-uniphier-usb3ss.c @@ -22,11 +22,13 @@ #include #define SSPHY_TESTI 0x0 -#define SSPHY_TESTO 0x4 #define TESTI_DAT_MASK GENMASK(13, 6) #define TESTI_ADR_MASK GENMASK(5, 1) #define TESTI_WR_EN BIT(0) +#define SSPHY_TESTO 0x4 +#define TESTO_DAT_MASK GENMASK(7, 0) + #define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) } #define CDR_CPD_TRIM PHY_F(7, 3, 0) /* RxPLL charge pump current */ @@ -84,12 +86,12 @@ static void uniphier_u3ssphy_set_param(struct uniphier_u3ssphy_priv *priv, val = FIELD_PREP(TESTI_DAT_MASK, 1); val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); uniphier_u3ssphy_testio_write(priv, val); - val = readl(priv->base + SSPHY_TESTO); + val = readl(priv->base + SSPHY_TESTO) & TESTO_DAT_MASK; /* update value */ - val &= ~FIELD_PREP(TESTI_DAT_MASK, field_mask); + val &= ~field_mask; data = field_mask & (p->value << p->field.lsb); - val = FIELD_PREP(TESTI_DAT_MASK, data); + val = FIELD_PREP(TESTI_DAT_MASK, data | val); val |= FIELD_PREP(TESTI_ADR_MASK, p->field.reg_no); uniphier_u3ssphy_testio_write(priv, val); uniphier_u3ssphy_testio_write(priv, val | TESTI_WR_EN); -- cgit v1.2.3 From 38b1a3c6197af61b41a166279bc8f5859106b116 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 14 Nov 2021 19:05:36 -0800 Subject: extcon: usb-gpio: fix a non-kernel-doc comment Do not use "/**" to begin a non-kernel-doc comment. Fixes this build warning: drivers/extcon/extcon-usb-gpio.c:23: warning: expecting prototype for drivers/extcon/extcon-usb-gpio.c(). Signed-off-by: Randy Dunlap Reported-by: kernel test robot Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon-usb-gpio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index 0cb440bdd5cb..f2b65d967384 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * drivers/extcon/extcon-usb-gpio.c - USB GPIO extcon driver * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com -- cgit v1.2.3 From 2da3db7f498d8b6137566b4869d289938b69de13 Mon Sep 17 00:00:00 2001 From: Alexander Stein Date: Tue, 23 Nov 2021 15:53:01 +0100 Subject: extcon: Deduplicate code in extcon_set_state_sync() Finding the cable index and checking for changed status is also done in extcon_set_state(). So calling extcon_set_state_sync() will do these checks twice. Remove them and use these checks from extcon_set_state(). Signed-off-by: Alexander Stein Signed-off-by: Chanwoo Choi --- drivers/extcon/extcon.c | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index e7a9561a826d..a09e704fd0fa 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -576,19 +576,7 @@ EXPORT_SYMBOL_GPL(extcon_set_state); */ int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id, bool state) { - int ret, index; - unsigned long flags; - - index = find_cable_index_by_id(edev, id); - if (index < 0) - return index; - - /* Check whether the external connector's state is changed. */ - spin_lock_irqsave(&edev->lock, flags); - ret = is_extcon_changed(edev, index, state); - spin_unlock_irqrestore(&edev->lock, flags); - if (!ret) - return 0; + int ret; ret = extcon_set_state(edev, id, state); if (ret < 0) -- cgit v1.2.3 From c9d1383c75c95be55d9207e8a8d5c7c1659a029e Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Sun, 17 Oct 2021 08:40:28 +0300 Subject: habanalabs: modify wait for boot fit in dynamic FW load In the dynamic FW load protocol the boot status is updated to "Ready to Boot" once uboot is active. Polling on other boot status values is a residue of code duplication from the static protocol and should be removed. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 4e68fb9d2a6b..025707a21882 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -2060,7 +2060,6 @@ static int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev, hdev, le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), status, - (status == CPU_BOOT_STATUS_NIC_FW_RDY) || (status == CPU_BOOT_STATUS_READY_TO_BOOT), FW_CPU_STATUS_POLL_INTERVAL_USEC, dyn_loader->wait_for_bl_timeout); -- cgit v1.2.3 From 4cd454a205069965463515e2068190f56b0e4206 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 21 Oct 2021 14:02:40 +0300 Subject: habanalabs/gaudi: recover from CPU WD event There are rare cases where the device CPU's watchdog has expired and as a result, the watchdog reset has happened and the CPU will now move to running its preboot f/w. When that happens, the driver will only know that a heartbeat failure occurred. As a result, the driver will send a message to the CPU's main f/w asking it to reset the device, but because the CPU is now running preboot, it won't respond and the re-initialization process will later fail when trying to load the f/w. The solution is to send the request to the preboot as well, only if the reset was caused because of HB failure. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 825737dfe381..d2b7ecb45497 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2020 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -4296,6 +4296,24 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset WREG32(irq_handler_offset, gaudi_irq_map_table[GAUDI_EVENT_HALT_MACHINE].cpu_id); + + /* This is a hail-mary attempt to revive the card in the small chance that the + * f/w has experienced a watchdog event, which caused it to return back to preboot. + * In that case, triggering reset through GIC won't help. We need to trigger the + * reset as if Linux wasn't loaded. + * + * We do it only if the reset cause was HB, because that would be the indication + * of such an event. + * + * In case watchdog hasn't expired but we still got HB, then this won't do any + * damage. + */ + if (hdev->curr_reset_cause == HL_RESET_CAUSE_HEARTBEAT) { + if (hdev->asic_prop.hard_reset_done_by_fw) + hl_fw_ask_hard_reset_without_linux(hdev); + else + hl_fw_ask_halt_machine_without_linux(hdev); + } } else { if (hdev->asic_prop.hard_reset_done_by_fw) hl_fw_ask_hard_reset_without_linux(hdev); -- cgit v1.2.3 From ba3aca31f91ceef072970c1688bff40afc2ea275 Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Thu, 14 Oct 2021 12:10:31 +0300 Subject: habanalabs: print va_range in vm node debugfs VA range info could assist in debugging VA allocation bugs. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/debugfs.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 1f2a3dc6c4e2..a239c5679f95 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -235,6 +235,8 @@ static int vm_show(struct seq_file *s, void *data) struct hl_vm_hash_node *hnode; struct hl_userptr *userptr; struct hl_vm_phys_pg_pack *phys_pg_pack = NULL; + struct hl_va_range *va_range; + struct hl_vm_va_block *va_block; enum vm_type *vm_type; bool once = true; u64 j; @@ -314,6 +316,29 @@ static int vm_show(struct seq_file *s, void *data) spin_unlock(&dev_entry->ctx_mem_hash_spinlock); + mutex_lock(&dev_entry->hdev->fpriv_list_lock); + ctx = dev_entry->hdev->compute_ctx; + if (ctx) + hl_ctx_get(dev_entry->hdev, ctx); + mutex_unlock(&dev_entry->hdev->fpriv_list_lock); + if (ctx) { + seq_puts(s, "\nVA ranges:\n\n"); + for (i = HL_VA_RANGE_TYPE_HOST ; i < HL_VA_RANGE_TYPE_MAX ; ++i) { + va_range = ctx->va_range[i]; + seq_printf(s, " va_range %d\n", i); + seq_puts(s, "---------------------\n"); + mutex_lock(&va_range->lock); + list_for_each_entry(va_block, &va_range->list, node) { + seq_printf(s, "%#16llx - %#16llx (%#llx)\n", + va_block->start, va_block->end, + va_block->size); + } + mutex_unlock(&va_range->lock); + seq_puts(s, "\n"); + } + hl_ctx_put(ctx); + } + if (!once) seq_puts(s, "\n"); -- cgit v1.2.3 From bfd5110682ca75cece49fe0e3f5ef478ec43c9ae Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Sun, 17 Oct 2021 09:00:43 +0300 Subject: habanalabs: revise and document use of boot status flags The boot status flag "SRAM available" can be set by f/w Linux (in the general case) or by f/w uboot (in some specific debug scenario) but never by f/w preboot. Hence, when polling the boot status flags in the preboot stage we do not want to poll on "SRAM Avialable". The special case in which uboot set this flag is when we are running special debug scenario without Linux. In this case, at some point during the boot, the uboot relocates its code to the DRAM and then set the specified flag. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 025707a21882..482bed152c39 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1101,7 +1101,6 @@ static int hl_fw_read_preboot_caps(struct hl_device *hdev, (status == CPU_BOOT_STATUS_DRAM_RDY) || (status == CPU_BOOT_STATUS_NIC_FW_RDY) || (status == CPU_BOOT_STATUS_READY_TO_BOOT) || - (status == CPU_BOOT_STATUS_SRAM_AVAIL) || (status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT), FW_CPU_STATUS_POLL_INTERVAL_USEC, timeout); @@ -2055,12 +2054,20 @@ static int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev, dyn_loader = &fw_loader->dynamic_loader; - /* Make sure CPU boot-loader is running */ + /* + * Make sure CPU boot-loader is running + * Note that the CPU_BOOT_STATUS_SRAM_AVAIL is generally set by Linux + * yet there is a debug scenario in which we loading uboot (without Linux) + * which at later stage is relocated to DRAM. In this case we expect + * uboot to set the CPU_BOOT_STATUS_SRAM_AVAIL and so we add it to the + * poll flags + */ rc = hl_poll_timeout( hdev, le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), status, - (status == CPU_BOOT_STATUS_READY_TO_BOOT), + (status == CPU_BOOT_STATUS_READY_TO_BOOT) || + (status == CPU_BOOT_STATUS_SRAM_AVAIL), FW_CPU_STATUS_POLL_INTERVAL_USEC, dyn_loader->wait_for_bl_timeout); if (rc) { @@ -2081,7 +2088,7 @@ static int hl_fw_dynamic_wait_for_linux_active(struct hl_device *hdev, dyn_loader = &fw_loader->dynamic_loader; - /* Make sure CPU boot-loader is running */ + /* Make sure CPU linux is running */ rc = hl_poll_timeout( hdev, @@ -2415,7 +2422,14 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev, WREG32(msg_to_cpu_reg, KMD_MSG_NA); } - /* Make sure CPU boot-loader is running */ + /* + * Make sure CPU boot-loader is running + * Note that the CPU_BOOT_STATUS_SRAM_AVAIL is generally set by Linux + * yet there is a debug scenario in which we loading uboot (without Linux) + * which at later stage is relocated to DRAM. In this case we expect + * uboot to set the CPU_BOOT_STATUS_SRAM_AVAIL and so we add it to the + * poll flags + */ rc = hl_poll_timeout( hdev, cpu_boot_status_reg, -- cgit v1.2.3 From 90d283b6726fc2e963042b6884951aa81afd0ff7 Mon Sep 17 00:00:00 2001 From: Guy Zadicario Date: Tue, 12 Oct 2021 10:30:28 +0300 Subject: habanalabs/gaudi: fix debugfs dma channel selection Do not use a dma channel for debugfs requested transfer if it's QM is not idle. Signed-off-by: Guy Zadicario Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index d2b7ecb45497..92d55a0a10c1 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -6430,6 +6430,7 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, { u32 dma_core_sts0, err_cause, cfg1, size_left, pos, size_to_dma; struct gaudi_device *gaudi = hdev->asic_specific; + u32 qm_glbl_sts0, qm_cgm_sts; u64 dma_offset, qm_offset; dma_addr_t dma_addr; void *kernel_addr; @@ -6454,14 +6455,20 @@ static int gaudi_debugfs_read_dma(struct hl_device *hdev, u64 addr, u32 size, dma_offset = dma_id * DMA_CORE_OFFSET; qm_offset = dma_id * DMA_QMAN_OFFSET; dma_core_sts0 = RREG32(mmDMA0_CORE_STS0 + dma_offset); - is_eng_idle = IS_DMA_IDLE(dma_core_sts0); + qm_glbl_sts0 = RREG32(mmDMA0_QM_GLBL_STS0 + qm_offset); + qm_cgm_sts = RREG32(mmDMA0_QM_CGM_STS + qm_offset); + is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_cgm_sts) && + IS_DMA_IDLE(dma_core_sts0); if (!is_eng_idle) { dma_id = gaudi_dma_assignment[GAUDI_PCI_DMA_2]; dma_offset = dma_id * DMA_CORE_OFFSET; qm_offset = dma_id * DMA_QMAN_OFFSET; dma_core_sts0 = RREG32(mmDMA0_CORE_STS0 + dma_offset); - is_eng_idle = IS_DMA_IDLE(dma_core_sts0); + qm_glbl_sts0 = RREG32(mmDMA0_QM_GLBL_STS0 + qm_offset); + qm_cgm_sts = RREG32(mmDMA0_QM_CGM_STS + qm_offset); + is_eng_idle = IS_QM_IDLE(qm_glbl_sts0, qm_cgm_sts) && + IS_DMA_IDLE(dma_core_sts0); if (!is_eng_idle) { dev_err_ratelimited(hdev->dev, -- cgit v1.2.3 From f06bad02b58733ed9e65b4c8d083270c8e9d0fa7 Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Thu, 14 Oct 2021 10:33:27 +0300 Subject: habanalabs: wrong VA size calculation VA blocks are currently stored in an inconsistent way. Sometimes block end is inclusive, sometimes exclusive. This leads to wrong size calculations in certain cases, plus could lead to a segmentation fault in case mapping process fails in the middle and we try to roll it back. Need to make this consistent - start inclusive till end inclusive. For example, the regions table may now look like this: 0x0000 - 0x1fff : allocated 0x2000 - 0x2fff : free 0x3000 - 0x3fff : allocated Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 2 +- drivers/misc/habanalabs/common/habanalabs.h | 16 ++-------------- drivers/misc/habanalabs/common/memory.c | 22 ++++++++++++++-------- 3 files changed, 17 insertions(+), 23 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index 8132a84698d5..41a12bcd26e5 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -57,7 +57,7 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) } va_block->start = virt_addr; - va_block->end = virt_addr + page_size; + va_block->end = virt_addr + page_size - 1; va_block->size = page_size; list_add_tail(&va_block->node, &cb->va_block_list); } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index a2002cbf794b..4f3c228c9b9d 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2757,21 +2757,9 @@ static inline bool hl_mem_area_inside_range(u64 address, u64 size, static inline bool hl_mem_area_crosses_range(u64 address, u32 size, u64 range_start_address, u64 range_end_address) { - u64 end_address = address + size; + u64 end_address = address + size - 1; - if ((address >= range_start_address) && - (address < range_end_address)) - return true; - - if ((end_address >= range_start_address) && - (end_address < range_end_address)) - return true; - - if ((address < range_start_address) && - (end_address >= range_end_address)) - return true; - - return false; + return ((address <= range_end_address) && (range_start_address <= end_address)); } int hl_device_open(struct inode *inode, struct file *filp); diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 9bd626a00de3..1185f9aec989 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -477,7 +477,7 @@ static int add_va_block_locked(struct hl_device *hdev, struct list_head *va_list, u64 start, u64 end) { struct hl_vm_va_block *va_block, *res = NULL; - u64 size = end - start; + u64 size = end - start + 1; print_va_list_locked(hdev, va_list); @@ -644,7 +644,7 @@ static u64 get_va_block(struct hl_device *hdev, continue; } - valid_size = va_block->end - valid_start; + valid_size = va_block->end - valid_start + 1; if (valid_size < size) continue; @@ -707,7 +707,7 @@ static u64 get_va_block(struct hl_device *hdev, if (new_va_block->size > size) { new_va_block->start += size; - new_va_block->size = new_va_block->end - new_va_block->start; + new_va_block->size = new_va_block->end - new_va_block->start + 1; } else { list_del(&new_va_block->node); kfree(new_va_block); @@ -2388,8 +2388,14 @@ static int va_range_init(struct hl_device *hdev, struct hl_va_range *va_range, start += PAGE_SIZE; } - if (end & (PAGE_SIZE - 1)) - end &= PAGE_MASK; + /* + * The end of the range is inclusive, hence we need to align it + * to the end of the last full page in the range. For example if + * end = 0x3ff5 with page size 0x1000, we need to align it to + * 0x2fff. The remainig 0xff5 bytes do not form a full page. + */ + if ((end + 1) & (PAGE_SIZE - 1)) + end = ((end + 1) & PAGE_MASK) - 1; } if (start >= end) { @@ -2564,14 +2570,14 @@ int hl_vm_ctx_init(struct hl_ctx *ctx) return 0; dram_range_start = prop->dmmu.start_addr; - dram_range_end = prop->dmmu.end_addr; + dram_range_end = prop->dmmu.end_addr - 1; dram_page_size = prop->dram_page_size ? prop->dram_page_size : prop->dmmu.page_size; host_range_start = prop->pmmu.start_addr; - host_range_end = prop->pmmu.end_addr; + host_range_end = prop->pmmu.end_addr - 1; host_page_size = prop->pmmu.page_size; host_huge_range_start = prop->pmmu_huge.start_addr; - host_huge_range_end = prop->pmmu_huge.end_addr; + host_huge_range_end = prop->pmmu_huge.end_addr - 1; host_huge_page_size = prop->pmmu_huge.page_size; return vm_ctx_init_with_ranges(ctx, host_range_start, host_range_end, -- cgit v1.2.3 From 89d6decdb7346082c1f168a27d1386c34550bbd3 Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Thu, 21 Oct 2021 15:08:51 +0300 Subject: habanalabs: make last_mask an MMU property Currently LAST_MASK is a global, but really it is an MMU implementation specific. We need this change for future ASICs. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ drivers/misc/habanalabs/common/mmu/mmu_v1.c | 10 +++++----- drivers/misc/habanalabs/gaudi/gaudi.c | 1 + drivers/misc/habanalabs/goya/goya.c | 2 ++ 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 4f3c228c9b9d..6dd7d9ee7a44 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -382,6 +382,7 @@ enum hl_device_hw_state { * @hop3_mask: mask to get the PTE address in hop 3. * @hop4_mask: mask to get the PTE address in hop 4. * @hop5_mask: mask to get the PTE address in hop 5. + * @last_mask: mask to get the bit indicating this is the last hop. * @page_size: default page size used to allocate memory. * @num_hops: The amount of hops supported by the translation table. * @host_resident: Should the MMU page table reside in host memory or in the @@ -402,6 +403,7 @@ struct hl_mmu_properties { u64 hop3_mask; u64 hop4_mask; u64 hop5_mask; + u64 last_mask; u32 page_size; u32 num_hops; u8 host_resident; diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v1.c b/drivers/misc/habanalabs/common/mmu/mmu_v1.c index 0f536f79dd9c..159da2fafd79 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu_v1.c +++ b/drivers/misc/habanalabs/common/mmu/mmu_v1.c @@ -573,7 +573,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx, curr_pte = *(u64 *) (uintptr_t) hop3_pte_addr; - is_huge = curr_pte & LAST_MASK; + is_huge = curr_pte & mmu_prop->last_mask; if (is_dram_addr && !is_huge) { dev_err(hdev->dev, @@ -597,7 +597,7 @@ static int _hl_mmu_v1_unmap(struct hl_ctx *ctx, if (hdev->dram_default_page_mapping && is_dram_addr) { u64 default_pte = (prop->mmu_dram_default_page_addr & - HOP_PHYS_ADDR_MASK) | LAST_MASK | + HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask | PAGE_PRESENT_MASK; if (curr_pte == default_pte) { dev_err(hdev->dev, @@ -729,7 +729,7 @@ static int _hl_mmu_v1_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, if (hdev->dram_default_page_mapping && is_dram_addr) { u64 default_pte = (prop->mmu_dram_default_page_addr & - HOP_PHYS_ADDR_MASK) | LAST_MASK | + HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask | PAGE_PRESENT_MASK; if (curr_pte != default_pte) { @@ -769,7 +769,7 @@ static int _hl_mmu_v1_map(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, goto err; } - curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | LAST_MASK + curr_pte = (phys_addr & HOP_PHYS_ADDR_MASK) | mmu_prop->last_mask | PAGE_PRESENT_MASK; if (is_huge) @@ -930,7 +930,7 @@ static int hl_mmu_v1_get_tlb_info(struct hl_ctx *ctx, u64 virt_addr, if (!(hops->hop_info[i].hop_pte_val & PAGE_PRESENT_MASK)) return -EFAULT; - if (hops->hop_info[i].hop_pte_val & LAST_MASK) + if (hops->hop_info[i].hop_pte_val & mmu_prop->last_mask) break; } diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 92d55a0a10c1..52fffd76f5cf 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -613,6 +613,7 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev) (VA_HOST_SPACE_START + VA_HOST_SPACE_SIZE / 2) - 1; prop->pmmu.page_size = PAGE_SIZE_4KB; prop->pmmu.num_hops = MMU_ARCH_5_HOPS; + prop->pmmu.last_mask = LAST_MASK; /* PMMU and HPMMU are the same except of page size */ memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu)); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 5536e8c27bd5..59bb12fcc935 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -429,6 +429,7 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->dmmu.end_addr = VA_DDR_SPACE_END; prop->dmmu.page_size = PAGE_SIZE_2MB; prop->dmmu.num_hops = MMU_ARCH_5_HOPS; + prop->dmmu.last_mask = LAST_MASK; /* shifts and masks are the same in PMMU and DMMU */ memcpy(&prop->pmmu, &prop->dmmu, sizeof(prop->dmmu)); @@ -436,6 +437,7 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->pmmu.end_addr = VA_HOST_SPACE_END; prop->pmmu.page_size = PAGE_SIZE_4KB; prop->pmmu.num_hops = MMU_ARCH_5_HOPS; + prop->pmmu.last_mask = LAST_MASK; /* PMMU and HPMMU are the same except of page size */ memcpy(&prop->pmmu_huge, &prop->pmmu, sizeof(prop->pmmu)); -- cgit v1.2.3 From 82e5169e8adfff331169613808b45a6cfb030e81 Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Thu, 30 Sep 2021 15:52:25 +0300 Subject: habanalabs: add enum mmu_op_flags The enum vm_type was abused, used once as a value (indication memory type for map) and once as a flag (for cache invalidation). This makes it hard to add new and still keep it meaningful, hence it is better to split into one enum for values and one for flags. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 6 +++--- drivers/misc/habanalabs/common/habanalabs.h | 11 +++++++++++ drivers/misc/habanalabs/common/memory.c | 4 ++-- drivers/misc/habanalabs/gaudi/gaudi.c | 4 ++-- drivers/misc/habanalabs/goya/goya.c | 2 +- 5 files changed, 19 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index 41a12bcd26e5..fab499d252d4 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -80,7 +80,7 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) offset += va_block->size; } - hdev->asic_funcs->mmu_invalidate_cache(hdev, false, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); @@ -97,7 +97,7 @@ err_va_umap: offset -= va_block->size; } - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); @@ -126,7 +126,7 @@ static void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb) "Failed to unmap CB's va 0x%llx\n", va_block->start); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 6dd7d9ee7a44..202c7f7948f5 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -352,6 +352,17 @@ enum vm_type { VM_TYPE_PHYS_PACK = 0x2 }; +/** + * enum mmu_op_flags - mmu operation relevant information. + * @MMU_OP_USERPTR: operation on user memory (host resident). + * @MMU_OP_PHYS_PACK: operation on DRAM (device resident). + */ +enum mmu_op_flags { + MMU_OP_USERPTR = 0x1, + MMU_OP_PHYS_PACK = 0x2 +}; + + /** * enum hl_device_hw_state - H/W device state. use this to understand whether * to do reset before hw_init or not diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 1185f9aec989..40f2197388fe 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -2639,8 +2639,8 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) mutex_lock(&ctx->mmu_lock); /* invalidate the cache once after the unmapping loop */ - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_PHYS_PACK); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK); mutex_unlock(&ctx->mmu_lock); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 52fffd76f5cf..2e39514ee102 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -8688,7 +8688,7 @@ static int gaudi_internal_cb_pool_init(struct hl_device *hdev, hdev->internal_cb_pool_dma_addr, HOST_SPACE_INTERNAL_CB_SZ); - hdev->asic_funcs->mmu_invalidate_cache(hdev, false, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); if (rc) @@ -8723,7 +8723,7 @@ static void gaudi_internal_cb_pool_fini(struct hl_device *hdev, HOST_SPACE_INTERNAL_CB_SZ); hl_unreserve_va_block(hdev, ctx, hdev->internal_cb_va_base, HOST_SPACE_INTERNAL_CB_SZ); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, VM_TYPE_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); gen_pool_destroy(hdev->internal_cb_pool); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 59bb12fcc935..6ee6d5b915a1 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2621,7 +2621,7 @@ int goya_mmu_init(struct hl_device *hdev) (~STLB_STLB_FEATURE_EN_FOLLOWER_EN_MASK)); hdev->asic_funcs->mmu_invalidate_cache(hdev, true, - VM_TYPE_USERPTR | VM_TYPE_PHYS_PACK); + MMU_OP_USERPTR | MMU_OP_PHYS_PACK); WREG32(mmMMU_MMU_ENABLE, 1); WREG32(mmMMU_SPI_MASK, 0xF); -- cgit v1.2.3 From 6ccba9a3bca95a24fd936e3c3542cf2ff2941b0f Mon Sep 17 00:00:00 2001 From: Yuri Nudelman Date: Mon, 25 Oct 2021 11:37:25 +0300 Subject: habanalabs: partly skip cache flush when in PMMU map flow The PCI MMU cache is two layered. The upper layer, memcache, uses cache lines, the bottom layer doesn't. Hence, after PMMU map operation we have to invalidate memcache, to avoid the situation where the new entry is already in the cache due to its cache line being fully in the cache. However, we do not have to invalidate the lower cache, and here we can optimize, since cache invalidation is time consuming. Signed-off-by: Yuri Nudelman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 3 ++- drivers/misc/habanalabs/common/habanalabs.h | 6 +++++- drivers/misc/habanalabs/common/memory.c | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index fab499d252d4..71910f7809bd 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -80,7 +80,8 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) offset += va_block->size; } - hdev->asic_funcs->mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR); + hdev->asic_funcs->mmu_invalidate_cache(hdev, false, + MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV); mutex_unlock(&ctx->mmu_lock); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 202c7f7948f5..aac73c8d2e1d 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -356,10 +356,14 @@ enum vm_type { * enum mmu_op_flags - mmu operation relevant information. * @MMU_OP_USERPTR: operation on user memory (host resident). * @MMU_OP_PHYS_PACK: operation on DRAM (device resident). + * @MMU_OP_CLEAR_MEMCACHE: operation has to clear memcache. + * @MMU_OP_SKIP_LOW_CACHE_INV: operation is allowed to skip parts of cache invalidation. */ enum mmu_op_flags { MMU_OP_USERPTR = 0x1, - MMU_OP_PHYS_PACK = 0x2 + MMU_OP_PHYS_PACK = 0x2, + MMU_OP_CLEAR_MEMCACHE = 0x4, + MMU_OP_SKIP_LOW_CACHE_INV = 0x8, }; diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 40f2197388fe..cd3640617d02 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1202,7 +1202,8 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, } rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, false, - *vm_type, ctx->asid, ret_vaddr, phys_pg_pack->total_size); + *vm_type | MMU_OP_SKIP_LOW_CACHE_INV, + ctx->asid, ret_vaddr, phys_pg_pack->total_size); mutex_unlock(&ctx->mmu_lock); -- cgit v1.2.3 From 8f82ff75dfd27afecb90246889c0c15d28e15ca7 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Thu, 21 Oct 2021 11:24:41 +0300 Subject: habanalabs: adding indication of boot fit loaded Up until now the driver stored indication if Linux was loaded on the device CPU. This was needed in order to coordinate some tasks that are performed by the Linux. In future ASICs, many of those tasks will be performed by the boot fit, so now we need the same indication of boot fit load status. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 4 ++-- drivers/misc/habanalabs/common/firmware_if.c | 4 +++- drivers/misc/habanalabs/common/habanalabs.h | 7 +++++-- drivers/misc/habanalabs/gaudi/gaudi.c | 4 ++-- drivers/misc/habanalabs/goya/goya.c | 2 +- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 2022e5d7b3ad..9674e2520532 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1138,7 +1138,7 @@ kill_processes: hdev->asic_funcs->hw_fini(hdev, hard_reset, fw_reset); if (hard_reset) { - hdev->fw_loader.linux_loaded = false; + hdev->fw_loader.fw_comp_loaded = FW_TYPE_NONE; /* Release kernel context */ if (hdev->kernel_ctx && hl_ctx_put(hdev->kernel_ctx) == 1) @@ -1692,7 +1692,7 @@ void hl_device_fini(struct hl_device *hdev) /* Reset the H/W. It will be in idle state after this returns */ hdev->asic_funcs->hw_fini(hdev, true, false); - hdev->fw_loader.linux_loaded = false; + hdev->fw_loader.fw_comp_loaded = FW_TYPE_NONE; /* Release kernel context */ if ((hdev->kernel_ctx) && (hl_ctx_put(hdev->kernel_ctx) != 1)) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 482bed152c39..8cbec10cddb1 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1919,6 +1919,8 @@ static void hl_fw_boot_fit_update_state(struct hl_device *hdev, { struct asic_fixed_properties *prop = &hdev->asic_prop; + hdev->fw_loader.fw_comp_loaded |= FW_TYPE_BOOT_CPU; + /* Clear reset status since we need to read it again from boot CPU */ prop->hard_reset_done_by_fw = false; @@ -2127,7 +2129,7 @@ static void hl_fw_linux_update_state(struct hl_device *hdev, { struct asic_fixed_properties *prop = &hdev->asic_prop; - hdev->fw_loader.linux_loaded = true; + hdev->fw_loader.fw_comp_loaded |= FW_TYPE_LINUX; /* Clear reset status since we need to read again from app */ prop->hard_reset_done_by_fw = false; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index aac73c8d2e1d..b3c6b660c7aa 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -219,6 +219,7 @@ enum hl_fw_component { /** * enum hl_fw_types - F/W types present in the system + * @FW_TYPE_NONE: no FW component indication * @FW_TYPE_LINUX: Linux image for device CPU * @FW_TYPE_BOOT_CPU: Boot image for device CPU * @FW_TYPE_PREBOOT_CPU: Indicates pre-loaded CPUs are present in the system @@ -226,6 +227,7 @@ enum hl_fw_component { * @FW_TYPE_ALL_TYPES: Mask for all types */ enum hl_fw_types { + FW_TYPE_NONE = 0x0, FW_TYPE_LINUX = 0x1, FW_TYPE_BOOT_CPU = 0x2, FW_TYPE_PREBOOT_CPU = 0x4, @@ -1059,7 +1061,8 @@ struct fw_image_props { * @skip_bmc: should BMC be skipped * @sram_bar_id: SRAM bar ID * @dram_bar_id: DRAM bar ID - * @linux_loaded: true if linux was loaded so far + * @fw_comp_loaded: bitmask of loaded FW components. set bit meaning loaded + * component. values are set according to enum hl_fw_types. */ struct fw_load_mgr { union { @@ -1073,7 +1076,7 @@ struct fw_load_mgr { u8 skip_bmc; u8 sram_bar_id; u8 dram_bar_id; - u8 linux_loaded; + u8 fw_comp_loaded; }; /** diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 2e39514ee102..1dcce1bc976f 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -4007,7 +4007,7 @@ static void gaudi_init_firmware_loader(struct hl_device *hdev) struct fw_load_mgr *fw_loader = &hdev->fw_loader; /* fill common fields */ - fw_loader->linux_loaded = false; + fw_loader->fw_comp_loaded = FW_TYPE_NONE; fw_loader->boot_fit_img.image_name = GAUDI_BOOT_FIT_FILE; fw_loader->linux_img.image_name = GAUDI_LINUX_FW_FILE; fw_loader->cpu_timeout = GAUDI_CPU_TIMEOUT_USEC; @@ -4290,7 +4290,7 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset * via the GIC. Otherwise, we need to use COMMS or the MSG_TO_CPU * registers in case of old F/Ws */ - if (hdev->fw_loader.linux_loaded) { + if (hdev->fw_loader.fw_comp_loaded & FW_TYPE_LINUX) { irq_handler_offset = hdev->asic_prop.gic_interrupts_enable ? mmGIC_DISTRIBUTOR__5_GICD_SETSPI_NSR : le32_to_cpu(dyn_regs->gic_host_halt_irq); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 6ee6d5b915a1..ce06103292a0 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -2504,7 +2504,7 @@ static void goya_init_firmware_loader(struct hl_device *hdev) struct fw_load_mgr *fw_loader = &hdev->fw_loader; /* fill common fields */ - fw_loader->linux_loaded = false; + fw_loader->fw_comp_loaded = FW_TYPE_NONE; fw_loader->boot_fit_img.image_name = GOYA_BOOT_FIT_FILE; fw_loader->linux_img.image_name = GOYA_LINUX_FW_FILE; fw_loader->cpu_timeout = GOYA_CPU_TIMEOUT_USEC; -- cgit v1.2.3 From f4e7906dbe7e922b057e4533a585f7943fe90c90 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Tue, 26 Oct 2021 15:33:23 +0300 Subject: habanalabs: use variable poll interval for fw loading Using a variable poll interval for fw loading allows us to support much slower environments (emulation) while changing only a single line in the code, instead of choosing a different interval in each function that polls. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 35 ++++++++++++++----------- drivers/misc/habanalabs/common/habanalabs.h | 5 ++++ drivers/misc/habanalabs/common/habanalabs_drv.c | 3 +++ 3 files changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 8cbec10cddb1..c68ad4d7b1bb 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -15,8 +15,6 @@ #define FW_FILE_MAX_SIZE 0x1400000 /* maximum size of 20MB */ -#define FW_CPU_STATUS_POLL_INTERVAL_USEC 10000 - static char *extract_fw_ver_from_str(const char *fw_str) { char *str, *fw_ver, *whitespace; @@ -1102,7 +1100,7 @@ static int hl_fw_read_preboot_caps(struct hl_device *hdev, (status == CPU_BOOT_STATUS_NIC_FW_RDY) || (status == CPU_BOOT_STATUS_READY_TO_BOOT) || (status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT), - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, timeout); if (rc) { @@ -1286,11 +1284,7 @@ int hl_fw_read_preboot_status(struct hl_device *hdev, u32 cpu_boot_status_reg, { int rc; - /* pldm was added for cases in which we use preboot on pldm and want - * to load boot fit, but we can't wait for preboot because it runs - * very slowly - */ - if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU) || hdev->pldm) + if (!(hdev->fw_components & FW_TYPE_PREBOOT_CPU)) return 0; /* @@ -1436,7 +1430,7 @@ static int hl_fw_dynamic_wait_for_status(struct hl_device *hdev, le32_to_cpu(dyn_regs->cpu_cmd_status_to_host), status, FIELD_GET(COMMS_STATUS_STATUS_MASK, status) == expected_status, - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, timeout); if (rc) { @@ -2070,7 +2064,7 @@ static int hl_fw_dynamic_wait_for_boot_fit_active(struct hl_device *hdev, status, (status == CPU_BOOT_STATUS_READY_TO_BOOT) || (status == CPU_BOOT_STATUS_SRAM_AVAIL), - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, dyn_loader->wait_for_bl_timeout); if (rc) { dev_err(hdev->dev, "failed to wait for boot\n"); @@ -2097,7 +2091,7 @@ static int hl_fw_dynamic_wait_for_linux_active(struct hl_device *hdev, le32_to_cpu(dyn_loader->comm_desc.cpu_dyn_regs.cpu_boot_status), status, (status == CPU_BOOT_STATUS_SRAM_AVAIL), - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, fw_loader->cpu_timeout); if (rc) { dev_err(hdev->dev, "failed to wait for Linux\n"); @@ -2296,6 +2290,15 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, goto protocol_err; } + /* + * when testing FW load (without Linux) on PLDM we don't want to + * wait until boot fit is active as it may take several hours. + * instead, we load the bootfit and let it do all initializations in + * the background. + */ + if (hdev->pldm && !(hdev->fw_components & FW_TYPE_LINUX)) + return 0; + rc = hl_fw_dynamic_wait_for_boot_fit_active(hdev, fw_loader); if (rc) goto protocol_err; @@ -2388,7 +2391,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev, cpu_boot_status_reg, status, status == CPU_BOOT_STATUS_WAITING_FOR_BOOT_FIT, - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, fw_loader->boot_fit_timeout); if (rc) { @@ -2411,7 +2414,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev, cpu_msg_status_reg, status, status == CPU_MSG_OK, - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, fw_loader->boot_fit_timeout); if (rc) { @@ -2440,7 +2443,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev, (status == CPU_BOOT_STATUS_NIC_FW_RDY) || (status == CPU_BOOT_STATUS_READY_TO_BOOT) || (status == CPU_BOOT_STATUS_SRAM_AVAIL), - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, cpu_timeout); dev_dbg(hdev->dev, "uboot status = %d\n", status); @@ -2489,7 +2492,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev, cpu_boot_status_reg, status, (status == CPU_BOOT_STATUS_BMC_WAITING_SKIPPED), - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, cpu_timeout); if (rc) { @@ -2509,7 +2512,7 @@ static int hl_fw_static_init_cpu(struct hl_device *hdev, cpu_boot_status_reg, status, (status == CPU_BOOT_STATUS_SRAM_AVAIL), - FW_CPU_STATUS_POLL_INTERVAL_USEC, + hdev->fw_poll_interval_usec, cpu_timeout); /* Clear message */ diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index b3c6b660c7aa..5fc9cfd892e8 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -61,6 +61,9 @@ #define HL_CPUCP_INFO_TIMEOUT_USEC 10000000 /* 10s */ #define HL_CPUCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */ +#define HL_FW_STATUS_POLL_INTERVAL_USEC 10000 /* 10ms */ +#define HL_FW_STATUS_PLDM_POLL_INTERVAL_USEC 300000000 /* 300s */ + #define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */ #define HL_SIM_MAX_TIMEOUT_US 10000000 /* 10s */ @@ -2459,6 +2462,7 @@ struct multi_cs_data { * @last_open_session_duration_jif: duration (jiffies) of the last device open * session. * @open_counter: number of successful device open operations. + * @fw_poll_interval_usec: FW status poll interval in usec. * @in_reset: is device in reset flow. * @curr_pll_profile: current PLL profile. * @card_type: Various ASICs have several card types. This indicates the card @@ -2607,6 +2611,7 @@ struct hl_device { u64 last_successful_open_jif; u64 last_open_session_duration_jif; u64 open_counter; + u64 fw_poll_interval_usec; atomic_t in_reset; enum hl_pll_frequency curr_pll_profile; enum cpucp_card_types card_type; diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 949d1b5c5c41..5989826701bc 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -345,6 +345,9 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, set_driver_behavior_per_device(hdev); + hdev->fw_poll_interval_usec = hdev->pldm ? HL_FW_STATUS_PLDM_POLL_INTERVAL_USEC : + HL_FW_STATUS_POLL_INTERVAL_USEC; + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; -- cgit v1.2.3 From 5edd95a4abb332fb683cf7a35eed2ae4ff7b4dcb Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Tue, 26 Oct 2021 10:42:24 +0300 Subject: habanalabs: don't clear previous f/w indications Once we read indication of whether f/w is doing the reset, we don't want to clear it, until the next time we read this indication. Otherwise, we might be in a state of wrong indication. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index c68ad4d7b1bb..9addcfba6a8b 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1247,8 +1247,7 @@ static void hl_fw_preboot_update_state(struct hl_device *hdev) * 3. FW application - a. Fetch fw application security status * b. Check whether hard reset is done by fw app */ - prop->hard_reset_done_by_fw = - !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); + prop->hard_reset_done_by_fw = !!(cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); dev_dbg(hdev->dev, "Firmware preboot boot device status0 %#x\n", cpu_boot_dev_sts0); @@ -1915,17 +1914,13 @@ static void hl_fw_boot_fit_update_state(struct hl_device *hdev, hdev->fw_loader.fw_comp_loaded |= FW_TYPE_BOOT_CPU; - /* Clear reset status since we need to read it again from boot CPU */ - prop->hard_reset_done_by_fw = false; - /* Read boot_cpu status bits */ if (prop->fw_preboot_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_ENABLED) { prop->fw_bootfit_cpu_boot_dev_sts0 = RREG32(cpu_boot_dev_sts0_reg); - if (prop->fw_bootfit_cpu_boot_dev_sts0 & - CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) - prop->hard_reset_done_by_fw = true; + prop->hard_reset_done_by_fw = !!(prop->fw_bootfit_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); dev_dbg(hdev->dev, "Firmware boot CPU status0 %#x\n", prop->fw_bootfit_cpu_boot_dev_sts0); @@ -2125,16 +2120,12 @@ static void hl_fw_linux_update_state(struct hl_device *hdev, hdev->fw_loader.fw_comp_loaded |= FW_TYPE_LINUX; - /* Clear reset status since we need to read again from app */ - prop->hard_reset_done_by_fw = false; - /* Read FW application security bits */ if (prop->fw_cpu_boot_dev_sts0_valid) { prop->fw_app_cpu_boot_dev_sts0 = RREG32(cpu_boot_dev_sts0_reg); - if (prop->fw_app_cpu_boot_dev_sts0 & - CPU_BOOT_DEV_STS0_FW_HARD_RST_EN) - prop->hard_reset_done_by_fw = true; + prop->hard_reset_done_by_fw = !!(prop->fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_FW_HARD_RST_EN); if (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_GIC_PRIVILEGED_EN) -- cgit v1.2.3 From 138858226414bd026e63acebb7540093c97c69fd Mon Sep 17 00:00:00 2001 From: Bharat Jauhari Date: Wed, 8 Sep 2021 17:16:51 +0300 Subject: habanalabs: handle abort scenario for user interrupt In case of device reset, the driver does a force trigger on all waiting users to release them from waiting. However, the driver does not handle error scenario while waiting. hl_interrupt_wait_ioctl() now exits the wait in case of an error with abort status. Signed-off-by: Bharat Jauhari Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 34 ++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 4c8000fd246c..41b48929cd59 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2768,7 +2768,7 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u32 timeout_us, u64 user_address, u64 target_value, u16 interrupt_offset, - enum hl_cs_wait_status *status, + u32 *status, u64 *timestamp) { struct hl_user_pending_interrupt *pend; @@ -2815,13 +2815,14 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, } if (completion_value >= target_value) { - *status = CS_WAIT_STATUS_COMPLETED; + *status = HL_WAIT_CS_STATUS_COMPLETED; /* There was no interrupt, we assume the completion is now. */ pend->fence.timestamp = ktime_get(); - } else - *status = CS_WAIT_STATUS_BUSY; + } else { + *status = HL_WAIT_CS_STATUS_BUSY; + } - if (!timeout_us || (*status == CS_WAIT_STATUS_COMPLETED)) + if (!timeout_us || (*status == HL_WAIT_CS_STATUS_COMPLETED)) goto remove_pending_user_interrupt; wait_again: @@ -2850,7 +2851,13 @@ wait_again: } if (completion_value >= target_value) { - *status = CS_WAIT_STATUS_COMPLETED; + *status = HL_WAIT_CS_STATUS_COMPLETED; + } else if (pend->fence.error) { + dev_err_ratelimited(hdev->dev, + "interrupt based wait ioctl aborted(error:%d) due to a reset cycle initiated\n", + pend->fence.error); + /* set the command completion status as ABORTED */ + *status = HL_WAIT_CS_STATUS_ABORTED; } else { timeout = completion_rc; goto wait_again; @@ -2861,7 +2868,7 @@ wait_again: interrupt->interrupt_id); rc = -EINTR; } else { - *status = CS_WAIT_STATUS_BUSY; + *status = HL_WAIT_CS_STATUS_BUSY; } remove_pending_user_interrupt: @@ -2883,7 +2890,7 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) struct hl_device *hdev = hpriv->hdev; struct asic_fixed_properties *prop; union hl_wait_cs_args *args = data; - enum hl_cs_wait_status status; + u32 status = HL_WAIT_CS_STATUS_BUSY; u64 timestamp; int rc; @@ -2926,22 +2933,13 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) } memset(args, 0, sizeof(*args)); + args->out.status = status; if (timestamp) { args->out.timestamp_nsec = timestamp; args->out.flags |= HL_WAIT_CS_STATUS_FLAG_TIMESTAMP_VLD; } - switch (status) { - case CS_WAIT_STATUS_COMPLETED: - args->out.status = HL_WAIT_CS_STATUS_COMPLETED; - break; - case CS_WAIT_STATUS_BUSY: - default: - args->out.status = HL_WAIT_CS_STATUS_BUSY; - break; - } - return 0; } -- cgit v1.2.3 From e84e31a9123bda35a1e61f391e7c30e8b3a8ea5b Mon Sep 17 00:00:00 2001 From: Rajaravi Krishna Katta Date: Tue, 26 Oct 2021 14:11:06 +0300 Subject: habanalabs: add dedicated message towards f/w to set power CPUCP_PACKET_POWER_GET packet type was used for both hl_get_power() and hl_set_power(). To align with other sensor functions hl_set_power() should use CPUCP_PACKET_POWER_SET. This packet will only be used with newer ASICs, so need to add a compatibility flag to the asic properties to indicate whether to use this packet or the GET packet. Signed-off-by: Rajaravi Krishna Katta Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 3 +++ drivers/misc/habanalabs/common/hwmon.c | 8 +++++++- drivers/misc/habanalabs/gaudi/gaudi.c | 2 ++ drivers/misc/habanalabs/goya/goya.c | 2 ++ drivers/misc/habanalabs/include/common/cpucp_if.h | 4 ++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 5fc9cfd892e8..dc61f7031c38 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -546,6 +546,8 @@ struct hl_hints_range { * @dynamic_fw_load: is dynamic FW load is supported. * @gic_interrupts_enable: true if FW is not blocking GIC controller, * false otherwise. + * @use_get_power_for_reset_history: To support backward compatibility for Goya + * and Gaudi */ struct asic_fixed_properties { struct hw_queue_properties *hw_queues_props; @@ -626,6 +628,7 @@ struct asic_fixed_properties { u8 iatu_done_by_fw; u8 dynamic_fw_load; u8 gic_interrupts_enable; + u8 use_get_power_for_reset_history; }; /** diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c index e33f65be8a00..70182b42940d 100644 --- a/drivers/misc/habanalabs/common/hwmon.c +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -677,12 +677,18 @@ int hl_set_power(struct hl_device *hdev, int sensor_index, u32 attr, long value) { struct cpucp_packet pkt; + struct asic_fixed_properties *prop = &hdev->asic_prop; int rc; memset(&pkt, 0, sizeof(pkt)); - pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET << + if (prop->use_get_power_for_reset_history) + pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); + else + pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_SET << + CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.sensor_index = __cpu_to_le16(sensor_index); pkt.type = __cpu_to_le16(attr); pkt.value = __cpu_to_le64(value); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 1dcce1bc976f..738ad2498439 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -665,6 +665,8 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev) prop->clk_pll_index = HL_GAUDI_MME_PLL; prop->max_freq_value = GAUDI_MAX_CLK_FREQ; + prop->use_get_power_for_reset_history = true; + return 0; } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index ce06103292a0..959eb21dcc69 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -475,6 +475,8 @@ int goya_set_fixed_properties(struct hl_device *hdev) prop->clk_pll_index = HL_GOYA_MME_PLL; + prop->use_get_power_for_reset_history = true; + return 0; } diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index ae13231fda94..17927968e19a 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -376,6 +376,9 @@ enum pq_init_status { * and QMANs. The f/w will return a bitmask where each bit represents * a different engine or QMAN according to enum cpucp_idle_mask. * The bit will be 1 if the engine is NOT idle. + * + * CPUCP_PACKET_POWER_SET - + * Resets power history of device to 0 */ enum cpucp_packet_id { @@ -421,6 +424,7 @@ enum cpucp_packet_id { CPUCP_PACKET_NIC_STAT_REGS_CLR, /* internal */ CPUCP_PACKET_NIC_STAT_REGS_ALL_GET, /* internal */ CPUCP_PACKET_IS_IDLE_CHECK, /* internal */ + CPUCP_PACKET_POWER_SET, /* internal */ }; #define CPUCP_PACKET_FENCE_VAL 0xFE8CE7A5 -- cgit v1.2.3 From 234caa52736b8d413892fb1b2471066dc4b46629 Mon Sep 17 00:00:00 2001 From: Bharat Jauhari Date: Thu, 16 Sep 2021 14:00:38 +0300 Subject: habanalabs: rename reset flags Rename reset flags for better readability as compared to HL_RESET_CAUSE* enum shared with the f/w. Signed-off-by: Bharat Jauhari Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 2 +- drivers/misc/habanalabs/common/device.c | 40 +++++++++++----------- drivers/misc/habanalabs/common/habanalabs.h | 28 +++++++-------- drivers/misc/habanalabs/common/memory.c | 2 +- drivers/misc/habanalabs/common/sysfs.c | 2 +- drivers/misc/habanalabs/gaudi/gaudi.c | 14 ++++---- drivers/misc/habanalabs/goya/goya.c | 10 +++--- 7 files changed, 50 insertions(+), 48 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 41b48929cd59..9ebcd9894d83 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -767,7 +767,7 @@ static void cs_timedout(struct work_struct *work) if (likely(!skip_reset_on_timeout)) { if (hdev->reset_on_lockup) - hl_device_reset(hdev, HL_RESET_TDR); + hl_device_reset(hdev, HL_DRV_RESET_TDR); else hdev->needs_reset = true; } diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 9674e2520532..eb5800b403b6 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -95,7 +95,7 @@ static void hpriv_release(struct kref *ref) if ((hdev->reset_if_device_not_idle && !device_is_idle) || hdev->reset_upon_device_release) - hl_device_reset(hdev, HL_RESET_DEVICE_RELEASE); + hl_device_reset(hdev, HL_DRV_RESET_DEV_RELEASE); /* Now we can mark the compute_ctx as empty. Even if a reset is running in a different * thread, we don't care because the in_reset is marked so if a user will try to open @@ -330,10 +330,10 @@ static void device_hard_reset_pending(struct work_struct *work) u32 flags; int rc; - flags = HL_RESET_HARD | HL_RESET_FROM_RESET_THREAD; + flags = HL_DRV_RESET_HARD | HL_DRV_RESET_FROM_RESET_THR; if (device_reset_work->fw_reset) - flags |= HL_RESET_FW; + flags |= HL_DRV_RESET_BYPASS_REQ_TO_FW; rc = hl_device_reset(hdev, flags); if ((rc == -EBUSY) && !hdev->device_fini_pending) { @@ -541,7 +541,7 @@ static void hl_device_heartbeat(struct work_struct *work) goto reschedule; dev_err(hdev->dev, "Device heartbeat failed!\n"); - hl_device_reset(hdev, HL_RESET_HARD | HL_RESET_HEARTBEAT); + hl_device_reset(hdev, HL_DRV_RESET_HARD | HL_DRV_RESET_HEARTBEAT); return; @@ -552,7 +552,7 @@ reschedule: * If control reached here, then at least one heartbeat work has been * scheduled since last reset/init cycle. * So if the device is not already in reset cycle, reset the flag - * prev_reset_trigger as no reset occurred with HL_RESET_FW_FATAL_ERR + * prev_reset_trigger as no reset occurred with HL_DRV_RESET_FW_FATAL_ERR * status for at least one heartbeat. From this point driver restarts * tracking future consecutive fatal errors. */ @@ -831,7 +831,7 @@ int hl_device_resume(struct hl_device *hdev) hdev->disabled = false; atomic_set(&hdev->in_reset, 0); - rc = hl_device_reset(hdev, HL_RESET_HARD); + rc = hl_device_reset(hdev, HL_DRV_RESET_HARD); if (rc) { dev_err(hdev->dev, "Failed to reset device during resume\n"); goto disable_device; @@ -948,15 +948,15 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) * ('in_reset' makes sure of it). This makes sure that * 'reset_cause' will continue holding its 1st recorded reason! */ - if (flags & HL_RESET_HEARTBEAT) { + if (flags & HL_DRV_RESET_HEARTBEAT) { hdev->curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT; - cur_reset_trigger = HL_RESET_HEARTBEAT; - } else if (flags & HL_RESET_TDR) { + cur_reset_trigger = HL_DRV_RESET_HEARTBEAT; + } else if (flags & HL_DRV_RESET_TDR) { hdev->curr_reset_cause = HL_RESET_CAUSE_TDR; - cur_reset_trigger = HL_RESET_TDR; - } else if (flags & HL_RESET_FW_FATAL_ERR) { + cur_reset_trigger = HL_DRV_RESET_TDR; + } else if (flags & HL_DRV_RESET_FW_FATAL_ERR) { hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; - cur_reset_trigger = HL_RESET_FW_FATAL_ERR; + cur_reset_trigger = HL_DRV_RESET_FW_FATAL_ERR; } else { hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; } @@ -979,8 +979,8 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) * If F/W is performing the reset, no need to send it a message to disable * PCI access */ - if ((flags & HL_RESET_HARD) && - !(flags & (HL_RESET_HEARTBEAT | HL_RESET_FW))) { + if ((flags & HL_DRV_RESET_HARD) && + !(flags & (HL_DRV_RESET_HEARTBEAT | HL_DRV_RESET_BYPASS_REQ_TO_FW))) { /* Disable PCI access from device F/W so he won't send * us additional interrupts. We disable MSI/MSI-X at * the halt_engines function and we can't have the F/W @@ -1025,9 +1025,9 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) return 0; } - hard_reset = !!(flags & HL_RESET_HARD); - from_hard_reset_thread = !!(flags & HL_RESET_FROM_RESET_THREAD); - fw_reset = !!(flags & HL_RESET_FW); + hard_reset = !!(flags & HL_DRV_RESET_HARD); + from_hard_reset_thread = !!(flags & HL_DRV_RESET_FROM_RESET_THR); + fw_reset = !!(flags & HL_DRV_RESET_BYPASS_REQ_TO_FW); if (!hard_reset && !hdev->supports_soft_reset) { hard_instead_soft = true; @@ -1035,7 +1035,7 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) } if (hdev->reset_upon_device_release && - (flags & HL_RESET_DEVICE_RELEASE)) { + (flags & HL_DRV_RESET_DEV_RELEASE)) { dev_dbg(hdev->dev, "Perform %s-reset upon device release\n", hard_reset ? "hard" : "soft"); @@ -1075,7 +1075,7 @@ do_reset: if (hard_reset) dev_info(hdev->dev, "Going to reset device\n"); - else if (flags & HL_RESET_DEVICE_RELEASE) + else if (flags & HL_DRV_RESET_DEV_RELEASE) dev_info(hdev->dev, "Going to reset device after it was released by user\n"); else @@ -1171,7 +1171,7 @@ kill_processes: hdev->hard_reset_pending = false; if (hdev->reset_trigger_repeated && - (hdev->prev_reset_trigger == HL_RESET_FW_FATAL_ERR)) { + (hdev->prev_reset_trigger == HL_DRV_RESET_FW_FATAL_ERR)) { /* if there 2 back to back resets from FW, * ensure driver puts the driver in a unusable state */ diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index dc61f7031c38..92d12c8ba569 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -120,37 +120,37 @@ enum hl_mmu_page_table_location { /* * Reset Flags * - * - HL_RESET_HARD + * - HL_DRV_RESET_HARD * If set do hard reset to all engines. If not set reset just * compute/DMA engines. * - * - HL_RESET_FROM_RESET_THREAD + * - HL_DRV_RESET_FROM_RESET_THR * Set if the caller is the hard-reset thread * - * - HL_RESET_HEARTBEAT + * - HL_DRV_RESET_HEARTBEAT * Set if reset is due to heartbeat * - * - HL_RESET_TDR + * - HL_DRV_RESET_TDR * Set if reset is due to TDR * - * - HL_RESET_DEVICE_RELEASE + * - HL_DRV_RESET_DEV_RELEASE * Set if reset is due to device release * - * - HL_RESET_FW + * - HL_DRV_RESET_BYPASS_REQ_TO_FW * F/W will perform the reset. No need to ask it to reset the device. This is relevant * only when running with secured f/w * - * - HL_RESET_FW_FATAL_ERR + * - HL_DRV_RESET_FW_FATAL_ERR * Set if reset is due to a fatal error from FW */ -#define HL_RESET_HARD (1 << 0) -#define HL_RESET_FROM_RESET_THREAD (1 << 1) -#define HL_RESET_HEARTBEAT (1 << 2) -#define HL_RESET_TDR (1 << 3) -#define HL_RESET_DEVICE_RELEASE (1 << 4) -#define HL_RESET_FW (1 << 5) -#define HL_RESET_FW_FATAL_ERR (1 << 6) +#define HL_DRV_RESET_HARD (1 << 0) +#define HL_DRV_RESET_FROM_RESET_THR (1 << 1) +#define HL_DRV_RESET_HEARTBEAT (1 << 2) +#define HL_DRV_RESET_TDR (1 << 3) +#define HL_DRV_RESET_DEV_RELEASE (1 << 4) +#define HL_DRV_RESET_BYPASS_REQ_TO_FW (1 << 5) +#define HL_DRV_RESET_FW_FATAL_ERR (1 << 6) #define HL_MAX_SOBS_PER_MONITOR 8 diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index cd3640617d02..530f8b4fadd2 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -316,7 +316,7 @@ static int free_phys_pg_pack(struct hl_device *hdev, } if (rc && !hdev->disabled) - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_DRV_RESET_HARD); end: kvfree(phys_pg_pack->pages); diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 42c1769ad25d..aee0cc4d6155 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -236,7 +236,7 @@ static ssize_t hard_reset_store(struct device *dev, dev_warn(hdev->dev, "Hard-Reset requested through sysfs\n"); - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_DRV_RESET_HARD); out: return count; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 738ad2498439..2724ab3747f2 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -8003,7 +8003,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_NIC0_CS_DBG_DERR ... GAUDI_EVENT_NIC4_CS_DBG_DERR: gaudi_print_irq_info(hdev, event_type, true); gaudi_handle_ecc_event(hdev, event_type, &eq_entry->ecc_data); - fw_fatal_err_flag = HL_RESET_FW_FATAL_ERR; + fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR; goto reset_device; case GAUDI_EVENT_GIC500: @@ -8011,7 +8011,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, case GAUDI_EVENT_L2_RAM_ECC: case GAUDI_EVENT_PLL0 ... GAUDI_EVENT_PLL17: gaudi_print_irq_info(hdev, event_type, false); - fw_fatal_err_flag = HL_RESET_FW_FATAL_ERR; + fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR; goto reset_device; case GAUDI_EVENT_HBM0_SPI_0: @@ -8022,7 +8022,7 @@ static void gaudi_handle_eqe(struct hl_device *hdev, gaudi_hbm_read_interrupts(hdev, gaudi_hbm_event_to_dev(event_type), &eq_entry->hbm_ecc_data); - fw_fatal_err_flag = HL_RESET_FW_FATAL_ERR; + fw_fatal_err_flag = HL_DRV_RESET_FW_FATAL_ERR; goto reset_device; case GAUDI_EVENT_HBM0_SPI_1: @@ -8205,9 +8205,11 @@ static void gaudi_handle_eqe(struct hl_device *hdev, reset_device: if (hdev->asic_prop.fw_security_enabled) - hl_device_reset(hdev, HL_RESET_HARD | HL_RESET_FW | fw_fatal_err_flag); + hl_device_reset(hdev, HL_DRV_RESET_HARD + | HL_DRV_RESET_BYPASS_REQ_TO_FW + | fw_fatal_err_flag); else if (hdev->hard_reset_on_fw_events) - hl_device_reset(hdev, HL_RESET_HARD | fw_fatal_err_flag); + hl_device_reset(hdev, HL_DRV_RESET_HARD | fw_fatal_err_flag); else hl_fw_unmask_irq(hdev, event_type); } @@ -8260,7 +8262,7 @@ static int gaudi_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, if (rc) { dev_err_ratelimited(hdev->dev, "MMU cache invalidation timeout\n"); - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_DRV_RESET_HARD); } return rc; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 959eb21dcc69..3bbcab7da25e 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -4838,14 +4838,14 @@ void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) case GOYA_ASYNC_EVENT_ID_L2_RAM_ECC: goya_print_irq_info(hdev, event_type, false); if (hdev->hard_reset_on_fw_events) - hl_device_reset(hdev, (HL_RESET_HARD | - HL_RESET_FW_FATAL_ERR)); + hl_device_reset(hdev, (HL_DRV_RESET_HARD | + HL_DRV_RESET_FW_FATAL_ERR)); break; case GOYA_ASYNC_EVENT_ID_PSOC_GPIO_05_SW_RESET: goya_print_irq_info(hdev, event_type, false); if (hdev->hard_reset_on_fw_events) - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_DRV_RESET_HARD); break; case GOYA_ASYNC_EVENT_ID_PCIE_DEC: @@ -4905,7 +4905,7 @@ void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) goya_print_irq_info(hdev, event_type, false); goya_print_out_of_sync_info(hdev, &eq_entry->pkt_sync_err); if (hdev->hard_reset_on_fw_events) - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_DRV_RESET_HARD); else hl_fw_unmask_irq(hdev, event_type); break; @@ -5239,7 +5239,7 @@ static int goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, if (rc) { dev_err_ratelimited(hdev->dev, "MMU cache invalidation timeout\n"); - hl_device_reset(hdev, HL_RESET_HARD); + hl_device_reset(hdev, HL_DRV_RESET_HARD); } return rc; -- cgit v1.2.3 From 48f31169830f589e4c7ac475ccc7414951ded3f0 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Thu, 14 Oct 2021 22:38:41 +0300 Subject: habanalabs: change wait for interrupt timeout to 64 bit In order to increase maximum wait-for-interrupt timeout, change it to 64 bit variable. This wait is used only by newer ASICs, so no problem in changing this interface at this time. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 22 +++++++++++++++++----- include/uapi/misc/habanalabs.h | 18 +++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 9ebcd9894d83..54a5425a77a0 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2765,8 +2765,23 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) return 0; } +static inline unsigned long hl_usecs64_to_jiffies(const u64 usecs) +{ + if (usecs <= U32_MAX) + return usecs_to_jiffies(usecs); + + /* + * If the value in nanoseconds is larger than 64 bit, use the largest + * 64 bit value. + */ + if (usecs >= ((u64)(U64_MAX / NSEC_PER_USEC))) + return nsecs_to_jiffies(U64_MAX); + + return nsecs_to_jiffies(usecs * NSEC_PER_USEC); +} + static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, - u32 timeout_us, u64 user_address, + u64 timeout_us, u64 user_address, u64 target_value, u16 interrupt_offset, u32 *status, u64 *timestamp) @@ -2778,10 +2793,7 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, long completion_rc; int rc = 0; - if (timeout_us == U32_MAX) - timeout = timeout_us; - else - timeout = usecs_to_jiffies(timeout_us); + timeout = hl_usecs64_to_jiffies(timeout_us); hl_ctx_get(hdev, ctx); diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 00b309590499..c5760acebdd1 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -911,14 +911,18 @@ struct hl_wait_cs_in { */ __u32 flags; - /* Multi CS API info- valid entries in multi-CS array */ - __u8 seq_arr_len; - __u8 pad[3]; + union { + struct { + /* Multi CS API info- valid entries in multi-CS array */ + __u8 seq_arr_len; + __u8 pad[7]; + }; - /* Absolute timeout to wait for an interrupt in microseconds. - * Relevant only when HL_WAIT_CS_FLAGS_INTERRUPT is set - */ - __u32 interrupt_timeout_us; + /* Absolute timeout to wait for an interrupt in microseconds. + * Relevant only when HL_WAIT_CS_FLAGS_INTERRUPT is set + */ + __u64 interrupt_timeout_us; + }; }; #define HL_WAIT_CS_STATUS_COMPLETED 0 -- cgit v1.2.3 From 1679c7ee580fdaa2a5df398a526b2eddc857f2a1 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 25 Oct 2021 09:47:04 +0300 Subject: habanalabs: expand clock throttling information uAPI In addition to the clock throttling reason, user should be able to obtain also the start time and the duration of the throttling event. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 3 +++ drivers/misc/habanalabs/common/habanalabs.h | 31 +++++++++++++++++++++-- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 27 ++++++++++++++++++-- drivers/misc/habanalabs/gaudi/gaudi.c | 22 +++++++++++++--- drivers/misc/habanalabs/goya/goya.c | 25 +++++++++++++++--- include/uapi/misc/habanalabs.h | 16 ++++++++++-- 6 files changed, 110 insertions(+), 14 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index eb5800b403b6..0da5a55490ff 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -455,6 +455,7 @@ static int device_early_init(struct hl_device *hdev) INIT_LIST_HEAD(&hdev->fpriv_list); mutex_init(&hdev->fpriv_list_lock); atomic_set(&hdev->in_reset, 0); + mutex_init(&hdev->clk_throttling.lock); return 0; @@ -495,6 +496,8 @@ static void device_early_fini(struct hl_device *hdev) mutex_destroy(&hdev->fpriv_list_lock); + mutex_destroy(&hdev->clk_throttling.lock); + hl_cb_mgr_fini(hdev, &hdev->kernel_cb_mgr); kfree(hdev->hl_chip_info); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 92d12c8ba569..fc201537f7a9 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2378,6 +2378,32 @@ struct multi_cs_data { u8 update_ts; }; +/** + * struct hl_clk_throttle_timestamp - current/last clock throttling timestamp + * @start: timestamp taken when 'start' event is received in driver + * @end: timestamp taken when 'end' event is received in driver + */ +struct hl_clk_throttle_timestamp { + ktime_t start; + ktime_t end; +}; + +/** + * struct hl_clk_throttle - keeps current/last clock throttling timestamps + * @timestamp: timestamp taken by driver and firmware, index 0 refers to POWER + * index 1 refers to THERMAL + * @lock: protects this structure as it can be accessed from both event queue + * context and info_ioctl context + * @current_reason: bitmask represents the current clk throttling reasons + * @aggregated_reason: bitmask represents aggregated clk throttling reasons since driver load + */ +struct hl_clk_throttle { + struct hl_clk_throttle_timestamp timestamp[HL_CLK_THROTTLE_TYPE_MAX]; + struct mutex lock; + u32 current_reason; + u32 aggregated_reason; +}; + /** * struct hl_device - habanalabs device structure. * @pdev: pointer to PCI device, can be NULL in case of simulator device. @@ -2445,6 +2471,7 @@ struct multi_cs_data { * @pci_mem_region: array of memory regions in the PCI * @state_dump_specs: constants and dictionaries needed to dump system state. * @multi_cs_completion: array of multi-CS completion. + * @clk_throttling: holds information about current/previous clock throttling events * @dram_used_mem: current DRAM memory consumption. * @timeout_jiffies: device CS timeout value. * @max_power: the max power of the device, as configured by the sysadmin. This @@ -2474,7 +2501,6 @@ struct multi_cs_data { * @high_pll: high PLL profile frequency. * @soft_reset_cnt: number of soft reset since the driver was loaded. * @hard_reset_cnt: number of hard reset since the driver was loaded. - * @clk_throttling_reason: bitmask represents the current clk throttling reasons * @id: device minor. * @id_control: minor of the control device * @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit @@ -2604,6 +2630,8 @@ struct hl_device { struct multi_cs_completion multi_cs_completion[ MULTI_CS_MAX_USER_CTX]; + struct hl_clk_throttle clk_throttling; + u32 *stream_master_qid_arr; atomic64_t dram_used_mem; u64 timeout_jiffies; @@ -2622,7 +2650,6 @@ struct hl_device { u32 high_pll; u32 soft_reset_cnt; u32 hard_reset_cnt; - u32 clk_throttling_reason; u16 id; u16 id_control; u16 cpu_pci_msb_addr; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 86c3257d9ae1..19726c6b642a 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -313,15 +313,38 @@ static int pci_counters_info(struct hl_fpriv *hpriv, struct hl_info_args *args) static int clk_throttle_info(struct hl_fpriv *hpriv, struct hl_info_args *args) { + void __user *out = (void __user *) (uintptr_t) args->return_pointer; struct hl_device *hdev = hpriv->hdev; struct hl_info_clk_throttle clk_throttle = {0}; + ktime_t end_time, zero_time = ktime_set(0, 0); u32 max_size = args->return_size; - void __user *out = (void __user *) (uintptr_t) args->return_pointer; + int i; if ((!max_size) || (!out)) return -EINVAL; - clk_throttle.clk_throttling_reason = hdev->clk_throttling_reason; + mutex_lock(&hdev->clk_throttling.lock); + + clk_throttle.clk_throttling_reason = hdev->clk_throttling.current_reason; + + for (i = 0 ; i < HL_CLK_THROTTLE_TYPE_MAX ; i++) { + if (!(hdev->clk_throttling.aggregated_reason & BIT(i))) + continue; + + clk_throttle.clk_throttling_timestamp_us[i] = + ktime_to_us(hdev->clk_throttling.timestamp[i].start); + + if (ktime_compare(hdev->clk_throttling.timestamp[i].end, zero_time)) + end_time = ktime_get(); + else + end_time = hdev->clk_throttling.timestamp[i].end; + + clk_throttle.clk_throttling_duration_ns[i] = + ktime_to_ns(ktime_sub(end_time, + hdev->clk_throttling.timestamp[i].start)); + + } + mutex_unlock(&hdev->clk_throttling.lock); return copy_to_user(out, &clk_throttle, min((size_t) max_size, sizeof(clk_throttle))) ? -EFAULT : 0; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 2724ab3747f2..b4814369062e 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7925,27 +7925,39 @@ static int tpc_krn_event_to_tpc_id(u16 tpc_dec_event_type) static void gaudi_print_clk_change_info(struct hl_device *hdev, u16 event_type) { + ktime_t zero_time = ktime_set(0, 0); + + mutex_lock(&hdev->clk_throttling.lock); + switch (event_type) { case GAUDI_EVENT_FIX_POWER_ENV_S: - hdev->clk_throttling_reason |= HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.current_reason |= HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].start = ktime_get(); + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = zero_time; dev_info_ratelimited(hdev->dev, "Clock throttling due to power consumption\n"); break; case GAUDI_EVENT_FIX_POWER_ENV_E: - hdev->clk_throttling_reason &= ~HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = ktime_get(); dev_info_ratelimited(hdev->dev, "Power envelop is safe, back to optimal clock\n"); break; case GAUDI_EVENT_FIX_THERMAL_ENV_S: - hdev->clk_throttling_reason |= HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.current_reason |= HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].start = ktime_get(); + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = zero_time; dev_info_ratelimited(hdev->dev, "Clock throttling due to overheating\n"); break; case GAUDI_EVENT_FIX_THERMAL_ENV_E: - hdev->clk_throttling_reason &= ~HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = ktime_get(); dev_info_ratelimited(hdev->dev, "Thermal envelop is safe, back to optimal clock\n"); break; @@ -7955,6 +7967,8 @@ static void gaudi_print_clk_change_info(struct hl_device *hdev, event_type); break; } + + mutex_unlock(&hdev->clk_throttling.lock); } static void gaudi_handle_eqe(struct hl_device *hdev, diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 3bbcab7da25e..7b3683f2a6dc 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -4768,24 +4768,39 @@ static int goya_unmask_irq(struct hl_device *hdev, u16 event_type) static void goya_print_clk_change_info(struct hl_device *hdev, u16 event_type) { + ktime_t zero_time = ktime_set(0, 0); + + mutex_lock(&hdev->clk_throttling.lock); + switch (event_type) { case GOYA_ASYNC_EVENT_ID_FIX_POWER_ENV_S: - hdev->clk_throttling_reason |= HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.current_reason |= HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].start = ktime_get(); + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = zero_time; dev_info_ratelimited(hdev->dev, "Clock throttling due to power consumption\n"); break; + case GOYA_ASYNC_EVENT_ID_FIX_POWER_ENV_E: - hdev->clk_throttling_reason &= ~HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_POWER; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_POWER].end = ktime_get(); dev_info_ratelimited(hdev->dev, "Power envelop is safe, back to optimal clock\n"); break; + case GOYA_ASYNC_EVENT_ID_FIX_THERMAL_ENV_S: - hdev->clk_throttling_reason |= HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.current_reason |= HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.aggregated_reason |= HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].start = ktime_get(); + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = zero_time; dev_info_ratelimited(hdev->dev, "Clock throttling due to overheating\n"); break; + case GOYA_ASYNC_EVENT_ID_FIX_THERMAL_ENV_E: - hdev->clk_throttling_reason &= ~HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.current_reason &= ~HL_CLK_THROTTLE_THERMAL; + hdev->clk_throttling.timestamp[HL_CLK_THROTTLE_TYPE_THERMAL].end = ktime_get(); dev_info_ratelimited(hdev->dev, "Thermal envelop is safe, back to optimal clock\n"); break; @@ -4795,6 +4810,8 @@ static void goya_print_clk_change_info(struct hl_device *hdev, u16 event_type) event_type); break; } + + mutex_unlock(&hdev->clk_throttling.lock); } void goya_handle_eqe(struct hl_device *hdev, struct hl_eq_entry *eq_entry) diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index c5760acebdd1..257b9630773e 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -473,15 +473,27 @@ struct hl_info_pci_counters { __u64 replay_cnt; }; -#define HL_CLK_THROTTLE_POWER 0x1 -#define HL_CLK_THROTTLE_THERMAL 0x2 +enum hl_clk_throttling_type { + HL_CLK_THROTTLE_TYPE_POWER, + HL_CLK_THROTTLE_TYPE_THERMAL, + HL_CLK_THROTTLE_TYPE_MAX +}; + +/* clk_throttling_reason masks */ +#define HL_CLK_THROTTLE_POWER (1 << HL_CLK_THROTTLE_TYPE_POWER) +#define HL_CLK_THROTTLE_THERMAL (1 << HL_CLK_THROTTLE_TYPE_THERMAL) /** * struct hl_info_clk_throttle - clock throttling reason * @clk_throttling_reason: each bit represents a clk throttling reason + * @clk_throttling_timestamp_us: represents CPU timestamp in microseconds of the start-event + * @clk_throttling_duration_ns: the clock throttle time in nanosec */ struct hl_info_clk_throttle { __u32 clk_throttling_reason; + __u32 pad; + __u64 clk_throttling_timestamp_us[HL_CLK_THROTTLE_TYPE_MAX]; + __u64 clk_throttling_duration_ns[HL_CLK_THROTTLE_TYPE_MAX]; }; /** -- cgit v1.2.3 From 792512459fb2a62a5ea08264a0cdfb7e46a391a9 Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Wed, 3 Nov 2021 13:15:55 +0200 Subject: habanalabs/gaudi: Fix collective wait bug In Signaling-From-Graph case, the driver didn't set the hw_sob pointer at the right place, which is needed for the cs completion check prior to start sending all the master/slaves jobs to device. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index b4814369062e..a9e279bfebae 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -1276,6 +1276,7 @@ static int gaudi_collective_wait_init_cs(struct hl_cs *cs) container_of(cs->signal_fence, struct hl_cs_compl, base_fence); struct hl_cs_compl *cs_cmpl = container_of(cs->fence, struct hl_cs_compl, base_fence); + struct hl_cs_encaps_sig_handle *handle = cs->encaps_sig_hdl; struct gaudi_collective_properties *cprop; u32 stream, queue_id, sob_group_offset; struct gaudi_device *gaudi; @@ -1288,10 +1289,16 @@ static int gaudi_collective_wait_init_cs(struct hl_cs *cs) gaudi = hdev->asic_specific; cprop = &gaudi->collective_props; - /* In encaps signals case the SOB info will be retrieved from - * the handle in gaudi_collective_slave_init_job. - */ - if (!cs->encaps_signals) { + if (cs->encaps_signals) { + cs_cmpl->hw_sob = handle->hw_sob; + /* at this checkpoint we only need the hw_sob pointer + * for the completion check before start going over the jobs + * of the master/slaves, the sob_value will be taken later on + * in gaudi_collective_slave_init_job depends on each + * job wait offset value. + */ + cs_cmpl->sob_val = 0; + } else { /* copy the SOB id and value of the signal CS */ cs_cmpl->hw_sob = signal_cs_cmpl->hw_sob; cs_cmpl->sob_val = signal_cs_cmpl->sob_val; -- cgit v1.2.3 From d4194f21400e9b2caef2d48c63ec5ef102eead22 Mon Sep 17 00:00:00 2001 From: Bharat Jauhari Date: Wed, 8 Sep 2021 17:32:54 +0300 Subject: habanalabs: refactor wait-for-user-interrupt function Refactor the wait-for-user-interrupt routine to make it more generic for re-use for other user exposed h/w interfaces in future ASICs. Signed-off-by: Bharat Jauhari Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 54a5425a77a0..e97b21988dea 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2782,12 +2782,12 @@ static inline unsigned long hl_usecs64_to_jiffies(const u64 usecs) static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u64 timeout_us, u64 user_address, - u64 target_value, u16 interrupt_offset, + u64 target_value, struct hl_user_interrupt *interrupt, + u32 *status, u64 *timestamp) { struct hl_user_pending_interrupt *pend; - struct hl_user_interrupt *interrupt; unsigned long timeout, flags; u64 completion_value; long completion_rc; @@ -2805,11 +2805,6 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, hl_fence_init(&pend->fence, ULONG_MAX); - if (interrupt_offset == HL_COMMON_USER_INTERRUPT_ID) - interrupt = &hdev->common_user_interrupt; - else - interrupt = &hdev->user_interrupt[interrupt_offset]; - /* Add pending user interrupt to relevant list for the interrupt * handler to monitor */ @@ -2898,9 +2893,10 @@ remove_pending_user_interrupt: static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) { - u16 interrupt_id, interrupt_offset, first_interrupt, last_interrupt; + u16 interrupt_id, first_interrupt, last_interrupt; struct hl_device *hdev = hpriv->hdev; struct asic_fixed_properties *prop; + struct hl_user_interrupt *interrupt; union hl_wait_cs_args *args = data; u32 status = HL_WAIT_CS_STATUS_BUSY; u64 timestamp; @@ -2913,8 +2909,7 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) return -EPERM; } - interrupt_id = - FIELD_GET(HL_WAIT_CS_FLAGS_INTERRUPT_MASK, args->in.flags); + interrupt_id = FIELD_GET(HL_WAIT_CS_FLAGS_INTERRUPT_MASK, args->in.flags); first_interrupt = prop->first_available_user_msix_interrupt; last_interrupt = prop->first_available_user_msix_interrupt + @@ -2927,15 +2922,14 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) } if (interrupt_id == HL_COMMON_USER_INTERRUPT_ID) - interrupt_offset = HL_COMMON_USER_INTERRUPT_ID; + interrupt = &hdev->common_user_interrupt; else - interrupt_offset = interrupt_id - first_interrupt; + interrupt = &hdev->user_interrupt[interrupt_id - first_interrupt]; rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, args->in.interrupt_timeout_us, args->in.addr, - args->in.target, interrupt_offset, &status, + args->in.target, interrupt, &status, ×tamp); - if (rc) { if (rc != -EINTR) dev_err_ratelimited(hdev->dev, -- cgit v1.2.3 From 49c052dad691ba1a3dc3559b74e99f2ec2fa0319 Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Sun, 24 Oct 2021 19:02:32 +0300 Subject: habanalabs: add new opcodes for INFO IOCTL Add implementation for new opcodes in the INFO IOCTL: 1. Retrieve the replaced DRAM rows from f/w. 2. Retrieve the pending DRAM rows from f/w. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 66 +++++++++++++++++++++++ drivers/misc/habanalabs/common/habanalabs.h | 3 ++ drivers/misc/habanalabs/common/habanalabs_ioctl.c | 43 +++++++++++++++ drivers/misc/habanalabs/include/common/cpucp_if.h | 33 +++++++++++- include/uapi/misc/habanalabs.h | 4 ++ 5 files changed, 148 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 9addcfba6a8b..70e992bdbde7 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -972,6 +972,72 @@ int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power) return rc; } +int hl_fw_dram_replaced_row_get(struct hl_device *hdev, + struct cpucp_hbm_row_info *info) +{ + struct cpucp_hbm_row_info *cpucp_repl_rows_info_cpu_addr; + dma_addr_t cpucp_repl_rows_info_dma_addr; + struct cpucp_packet pkt = {}; + u64 result; + int rc; + + cpucp_repl_rows_info_cpu_addr = + hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, + sizeof(struct cpucp_hbm_row_info), + &cpucp_repl_rows_info_dma_addr); + if (!cpucp_repl_rows_info_cpu_addr) { + dev_err(hdev->dev, + "Failed to allocate DMA memory for CPU-CP replaced rows info packet\n"); + return -ENOMEM; + } + + memset(cpucp_repl_rows_info_cpu_addr, 0, sizeof(struct cpucp_hbm_row_info)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_HBM_REPLACED_ROWS_INFO_GET << + CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.addr = cpu_to_le64(cpucp_repl_rows_info_dma_addr); + pkt.data_max_size = cpu_to_le32(sizeof(struct cpucp_hbm_row_info)); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_CPUCP_INFO_TIMEOUT_USEC, &result); + if (rc) { + dev_err(hdev->dev, + "Failed to handle CPU-CP replaced rows info pkt, error %d\n", rc); + goto out; + } + + memcpy(info, cpucp_repl_rows_info_cpu_addr, sizeof(*info)); + +out: + hdev->asic_funcs->cpu_accessible_dma_pool_free(hdev, + sizeof(struct cpucp_hbm_row_info), + cpucp_repl_rows_info_cpu_addr); + + return rc; +} + +int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num) +{ + struct cpucp_packet pkt; + u64 result; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_HBM_PENDING_ROWS_STATUS << CPUCP_PKT_CTL_OPCODE_SHIFT); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); + if (rc) { + dev_err(hdev->dev, + "Failed to handle CPU-CP pending rows info pkt, error %d\n", rc); + goto out; + } + + *pend_rows_num = (u32) result; +out: + return rc; +} + void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev) { struct static_fw_load_mgr *static_loader = diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index fc201537f7a9..a19563c416ac 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -3012,6 +3012,9 @@ int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev, struct fw_load_mgr *fw_loader, enum comms_cmd cmd, unsigned int size, bool wait_ok, u32 timeout); +int hl_fw_dram_replaced_row_get(struct hl_device *hdev, + struct cpucp_hbm_row_info *info); +int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num); int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], bool is_wc[3]); int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data); diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 19726c6b642a..68c655acdec8 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -503,6 +503,43 @@ static int open_stats_info(struct hl_fpriv *hpriv, struct hl_info_args *args) min((size_t) max_size, sizeof(open_stats_info))) ? -EFAULT : 0; } +static int dram_pending_rows_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + u32 max_size = args->return_size; + u32 pend_rows_num = 0; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + int rc; + + if ((!max_size) || (!out)) + return -EINVAL; + + rc = hl_fw_dram_pending_row_get(hdev, &pend_rows_num); + if (rc) + return rc; + + return copy_to_user(out, &pend_rows_num, + min_t(size_t, max_size, sizeof(pend_rows_num))) ? -EFAULT : 0; +} + +static int dram_replaced_rows_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + u32 max_size = args->return_size; + struct cpucp_hbm_row_info info = {0}; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + int rc; + + if ((!max_size) || (!out)) + return -EINVAL; + + rc = hl_fw_dram_replaced_row_get(hdev, &info); + if (rc) + return rc; + + return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -589,6 +626,12 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_OPEN_STATS: return open_stats_info(hpriv, args); + case HL_INFO_DRAM_REPLACED_ROWS: + return dram_replaced_rows_info(hpriv, args); + + case HL_INFO_DRAM_PENDING_ROWS: + return dram_pending_rows_info(hpriv, args); + default: dev_err(dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 17927968e19a..5e19c763f3f0 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2020 HabanaLabs, Ltd. + * Copyright 2021 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -377,6 +377,13 @@ enum pq_init_status { * a different engine or QMAN according to enum cpucp_idle_mask. * The bit will be 1 if the engine is NOT idle. * + * CPUCP_PACKET_HBM_REPLACED_ROWS_INFO_GET - + * Fetch all HBM replaced-rows and prending to be replaced rows data. + * + * CPUCP_PACKET_HBM_PENDING_ROWS_STATUS - + * Fetch status of HBM rows pending replacement and need a reboot to + * be replaced. + * * CPUCP_PACKET_POWER_SET - * Resets power history of device to 0 */ @@ -424,6 +431,8 @@ enum cpucp_packet_id { CPUCP_PACKET_NIC_STAT_REGS_CLR, /* internal */ CPUCP_PACKET_NIC_STAT_REGS_ALL_GET, /* internal */ CPUCP_PACKET_IS_IDLE_CHECK, /* internal */ + CPUCP_PACKET_HBM_REPLACED_ROWS_INFO_GET,/* internal */ + CPUCP_PACKET_HBM_PENDING_ROWS_STATUS, /* internal */ CPUCP_PACKET_POWER_SET, /* internal */ }; @@ -692,6 +701,7 @@ struct eq_generic_event { #define CPUCP_MAX_NIC_LANES (CPUCP_MAX_NICS * CPUCP_LANES_PER_NIC) #define CPUCP_NIC_MASK_ARR_LEN ((CPUCP_MAX_NICS + 63) / 64) #define CPUCP_NIC_POLARITY_ARR_LEN ((CPUCP_MAX_NIC_LANES + 63) / 64) +#define CPUCP_HBM_ROW_REPLACE_MAX 32 struct cpucp_sensor { __le32 type; @@ -837,4 +847,25 @@ struct cpucp_nic_status { __le32 high_ber_cnt; }; +enum cpucp_hbm_row_replace_cause { + REPLACE_CAUSE_DOUBLE_ECC_ERR, + REPLACE_CAUSE_MULTI_SINGLE_ECC_ERR, +}; + +struct cpucp_hbm_row_info { + __u8 hbm_idx; + __u8 pc; + __u8 sid; + __u8 bank_idx; + __le16 row_addr; + __u8 replaced_row_cause; /* enum cpucp_hbm_row_replace_cause */ + __u8 pad; +}; + +struct cpucp_hbm_row_replaced_rows_info { + __le16 num_replaced_rows; + __u8 pad[6]; + struct cpucp_hbm_row_info replaced_rows[CPUCP_HBM_ROW_REPLACE_MAX]; +}; + #endif /* CPUCP_IF_H */ diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 257b9630773e..9b4d72897061 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -334,6 +334,8 @@ enum hl_server_type { * HL_INFO_TOTAL_ENERGY - Retrieve total energy consumption * HL_INFO_PLL_FREQUENCY - Retrieve PLL frequency * HL_INFO_OPEN_STATS - Retrieve info regarding recent device open calls + * HL_INFO_DRAM_REPLACED_ROWS - Retrieve DRAM replaced rows info + * HL_INFO_DRAM_PENDING_ROWS - Retrieve DRAM pending rows num */ #define HL_INFO_HW_IP_INFO 0 #define HL_INFO_HW_EVENTS 1 @@ -353,6 +355,8 @@ enum hl_server_type { #define HL_INFO_PLL_FREQUENCY 16 #define HL_INFO_POWER 17 #define HL_INFO_OPEN_STATS 18 +#define HL_INFO_DRAM_REPLACED_ROWS 21 +#define HL_INFO_DRAM_PENDING_ROWS 22 #define HL_INFO_VERSION_MAX_LEN 128 #define HL_INFO_CARD_NAME_MAX_LEN 16 -- cgit v1.2.3 From e617f5f4c144c3f185da67292dff09dc6cbb3296 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Thu, 4 Nov 2021 09:48:22 +0200 Subject: habanalabs: make hdev creation code more readable Divide the code into 3 different parts: - Copy kernel parameters - Setting device behaivor per asic - Fixup of various device parameters according to the device behaivor. In addition, remove non-relevant code for upstream (simulator support). Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 6 +- drivers/misc/habanalabs/common/habanalabs_drv.c | 123 ++++++++++++------------ 2 files changed, 61 insertions(+), 68 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index a19563c416ac..6b33fbd72fd8 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -62,7 +62,6 @@ #define HL_CPUCP_EEPROM_TIMEOUT_USEC 10000000 /* 10s */ #define HL_FW_STATUS_POLL_INTERVAL_USEC 10000 /* 10ms */ -#define HL_FW_STATUS_PLDM_POLL_INTERVAL_USEC 300000000 /* 300s */ #define HL_PCI_ELBI_TIMEOUT_MSEC 10 /* 10ms */ @@ -2823,9 +2822,6 @@ bool hl_device_operational(struct hl_device *hdev, enum hl_device_status *status); enum hl_device_status hl_device_status(struct hl_device *hdev); int hl_device_set_debug_mode(struct hl_device *hdev, bool enable); -int create_hdev(struct hl_device **dev, struct pci_dev *pdev, - enum hl_asic_type asic_type, int minor); -void destroy_hdev(struct hl_device *hdev); int hl_hw_queues_create(struct hl_device *hdev); void hl_hw_queues_destroy(struct hl_device *hdev); int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 5989826701bc..85034f2f2e89 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -263,6 +263,7 @@ out_err: static void set_driver_behavior_per_device(struct hl_device *hdev) { + hdev->pldm = 0; hdev->fw_components = FW_TYPE_ALL_TYPES; hdev->cpu_queues_enable = 1; hdev->heartbeat = 1; @@ -279,23 +280,53 @@ static void set_driver_behavior_per_device(struct hl_device *hdev) hdev->axi_drain = 0; } -/* +static void copy_kernel_module_params_to_device(struct hl_device *hdev) +{ + hdev->major = hl_major; + hdev->memory_scrub = memory_scrub; + hdev->reset_on_lockup = reset_on_lockup; + hdev->boot_error_status_mask = boot_error_status_mask; + + if (timeout_locked) + hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); + else + hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; + +} + +static int fixup_device_params(struct hl_device *hdev) +{ + hdev->asic_prop.fw_security_enabled = is_asic_secured(hdev->asic_type); + + hdev->fw_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC; + + hdev->stop_on_err = true; + hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; + + /* Enable only after the initialization of the device */ + hdev->disabled = true; + + /* Set default DMA mask to 32 bits */ + hdev->dma_mask = 32; + + return 0; +} + +/** * create_hdev - create habanalabs device instance * * @dev: will hold the pointer to the new habanalabs device structure * @pdev: pointer to the pci device - * @asic_type: in case of simulator device, which device is it - * @minor: in case of simulator device, the minor of the device * * Allocate memory for habanalabs device and initialize basic fields * Identify the ASIC type * Allocate ID (minor) for the device (only for real devices) */ -int create_hdev(struct hl_device **dev, struct pci_dev *pdev, - enum hl_asic_type asic_type, int minor) +static int create_hdev(struct hl_device **dev, struct pci_dev *pdev) { + int main_id, ctrl_id = 0, rc = 0; struct hl_device *hdev; - int rc, main_id, ctrl_id = 0; *dev = NULL; @@ -303,72 +334,39 @@ int create_hdev(struct hl_device **dev, struct pci_dev *pdev, if (!hdev) return -ENOMEM; - /* First, we must find out which ASIC are we handling. This is needed - * to configure the behavior of the driver (kernel parameters) - */ - if (pdev) { - hdev->asic_type = get_asic_type(pdev->device); - if (hdev->asic_type == ASIC_INVALID) { - dev_err(&pdev->dev, "Unsupported ASIC\n"); - rc = -ENODEV; - goto free_hdev; - } - } else { - hdev->asic_type = asic_type; - } - - if (pdev) - hdev->asic_prop.fw_security_enabled = - is_asic_secured(hdev->asic_type); - else - hdev->asic_prop.fw_security_enabled = false; + /* can be NULL in case of simulator device */ + hdev->pdev = pdev; /* Assign status description string */ - strncpy(hdev->status[HL_DEVICE_STATUS_OPERATIONAL], - "operational", HL_STR_MAX); - strncpy(hdev->status[HL_DEVICE_STATUS_IN_RESET], - "in reset", HL_STR_MAX); - strncpy(hdev->status[HL_DEVICE_STATUS_MALFUNCTION], - "disabled", HL_STR_MAX); - strncpy(hdev->status[HL_DEVICE_STATUS_NEEDS_RESET], - "needs reset", HL_STR_MAX); + strncpy(hdev->status[HL_DEVICE_STATUS_OPERATIONAL], "operational", HL_STR_MAX); + strncpy(hdev->status[HL_DEVICE_STATUS_IN_RESET], "in reset", HL_STR_MAX); + strncpy(hdev->status[HL_DEVICE_STATUS_MALFUNCTION], "disabled", HL_STR_MAX); + strncpy(hdev->status[HL_DEVICE_STATUS_NEEDS_RESET], "needs reset", HL_STR_MAX); strncpy(hdev->status[HL_DEVICE_STATUS_IN_DEVICE_CREATION], "in device creation", HL_STR_MAX); - hdev->major = hl_major; - hdev->reset_on_lockup = reset_on_lockup; - hdev->memory_scrub = memory_scrub; - hdev->boot_error_status_mask = boot_error_status_mask; - hdev->stop_on_err = true; + /* First, we must find out which ASIC are we handling. This is needed + * to configure the behavior of the driver (kernel parameters) + */ + hdev->asic_type = get_asic_type(pdev->device); + if (hdev->asic_type == ASIC_INVALID) { + dev_err(&pdev->dev, "Unsupported ASIC\n"); + rc = -ENODEV; + goto free_hdev; + } - hdev->pldm = 0; + copy_kernel_module_params_to_device(hdev); set_driver_behavior_per_device(hdev); - hdev->fw_poll_interval_usec = hdev->pldm ? HL_FW_STATUS_PLDM_POLL_INTERVAL_USEC : - HL_FW_STATUS_POLL_INTERVAL_USEC; - - hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; - hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; - - if (timeout_locked) - hdev->timeout_jiffies = msecs_to_jiffies(timeout_locked * 1000); - else - hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT; - - hdev->disabled = true; - hdev->pdev = pdev; /* can be NULL in case of simulator device */ - - /* Set default DMA mask to 32 bits */ - hdev->dma_mask = 32; + fixup_device_params(hdev); mutex_lock(&hl_devs_idr_lock); /* Always save 2 numbers, 1 for main device and 1 for control. * They must be consecutive */ - main_id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, - GFP_KERNEL); + main_id = idr_alloc(&hl_devs_idr, hdev, 0, HL_MAX_MINORS, GFP_KERNEL); if (main_id >= 0) ctrl_id = idr_alloc(&hl_devs_idr, hdev, main_id + 1, @@ -408,7 +406,7 @@ free_hdev: * @dev: pointer to the habanalabs device structure * */ -void destroy_hdev(struct hl_device *hdev) +static void destroy_hdev(struct hl_device *hdev) { /* Remove device from the device list */ mutex_lock(&hl_devs_idr_lock); @@ -447,7 +445,7 @@ static int hl_pmops_resume(struct device *dev) return hl_device_resume(hdev); } -/* +/** * hl_pci_probe - probe PCI habanalabs devices * * @pdev: pointer to pci device @@ -457,8 +455,7 @@ static int hl_pmops_resume(struct device *dev) * Create a new habanalabs device and initialize it according to the * device's type */ -static int hl_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) +static int hl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) { struct hl_device *hdev; int rc; @@ -467,7 +464,7 @@ static int hl_pci_probe(struct pci_dev *pdev, " device found [%04x:%04x] (rev %x)\n", (int)pdev->vendor, (int)pdev->device, (int)pdev->revision); - rc = create_hdev(&hdev, pdev, ASIC_INVALID, -1); + rc = create_hdev(&hdev, pdev); if (rc) return rc; -- cgit v1.2.3 From 3eb7754ff43827294bebcb2760969e9dc2283027 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 12 Oct 2021 20:52:46 +0300 Subject: habanalabs: debugfs support for larger I2C transactions I2C debugfs support is limited to 1 byte. We extend functionality to more than 1 byte by using one of the pad fields as a length. No backward compatibility issues as new F/W versions will treat 0 length as a 1 byte length transaction. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../ABI/testing/debugfs-driver-habanalabs | 7 +++ drivers/misc/habanalabs/common/debugfs.c | 50 ++++++++++++++-------- drivers/misc/habanalabs/common/habanalabs.h | 2 + drivers/misc/habanalabs/include/common/cpucp_if.h | 9 +++- 4 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Documentation/ABI/testing/debugfs-driver-habanalabs b/Documentation/ABI/testing/debugfs-driver-habanalabs index 63c46d9d538f..6085ee506135 100644 --- a/Documentation/ABI/testing/debugfs-driver-habanalabs +++ b/Documentation/ABI/testing/debugfs-driver-habanalabs @@ -155,6 +155,13 @@ Description: Triggers an I2C transaction that is generated by the device's CPU. Writing to this file generates a write transaction while reading from the file generates a read transaction +What: /sys/kernel/debug/habanalabs/hl/i2c_len +Date: Dec 2021 +KernelVersion: 5.17 +Contact: obitton@habana.ai +Description: Sets I2C length in bytes for I2C transaction that is generated by + the device's CPU + What: /sys/kernel/debug/habanalabs/hl/i2c_reg Date: Jan 2019 KernelVersion: 5.1 diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index a239c5679f95..9727d82b121f 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -15,19 +15,25 @@ #define MMU_ADDR_BUF_SIZE 40 #define MMU_ASID_BUF_SIZE 10 #define MMU_KBUF_SIZE (MMU_ADDR_BUF_SIZE + MMU_ASID_BUF_SIZE) +#define I2C_MAX_TRANSACTION_LEN 8 static struct dentry *hl_debug_root; static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, - u8 i2c_reg, long *val) + u8 i2c_reg, u8 i2c_len, u64 *val) { struct cpucp_packet pkt; - u64 result; int rc; if (!hl_device_operational(hdev, NULL)) return -EBUSY; + if (i2c_len > I2C_MAX_TRANSACTION_LEN) { + dev_err(hdev->dev, "I2C transaction length %u, exceeds maximum of %u\n", + i2c_len, I2C_MAX_TRANSACTION_LEN); + return -EINVAL; + } + memset(&pkt, 0, sizeof(pkt)); pkt.ctl = cpu_to_le32(CPUCP_PACKET_I2C_RD << @@ -35,12 +41,10 @@ static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, pkt.i2c_bus = i2c_bus; pkt.i2c_addr = i2c_addr; pkt.i2c_reg = i2c_reg; + pkt.i2c_len = i2c_len; rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), - 0, &result); - - *val = (long) result; - + 0, val); if (rc) dev_err(hdev->dev, "Failed to read from I2C, error %d\n", rc); @@ -48,7 +52,7 @@ static int hl_debugfs_i2c_read(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, } static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, - u8 i2c_reg, u32 val) + u8 i2c_reg, u8 i2c_len, u64 val) { struct cpucp_packet pkt; int rc; @@ -56,6 +60,12 @@ static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, if (!hl_device_operational(hdev, NULL)) return -EBUSY; + if (i2c_len > I2C_MAX_TRANSACTION_LEN) { + dev_err(hdev->dev, "I2C transaction length %u, exceeds maximum of %u\n", + i2c_len, I2C_MAX_TRANSACTION_LEN); + return -EINVAL; + } + memset(&pkt, 0, sizeof(pkt)); pkt.ctl = cpu_to_le32(CPUCP_PACKET_I2C_WR << @@ -63,6 +73,7 @@ static int hl_debugfs_i2c_write(struct hl_device *hdev, u8 i2c_bus, u8 i2c_addr, pkt.i2c_bus = i2c_bus; pkt.i2c_addr = i2c_addr; pkt.i2c_reg = i2c_reg; + pkt.i2c_len = i2c_len; pkt.value = cpu_to_le64(val); rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), @@ -899,22 +910,22 @@ static ssize_t hl_i2c_data_read(struct file *f, char __user *buf, struct hl_dbg_device_entry *entry = file_inode(f)->i_private; struct hl_device *hdev = entry->hdev; char tmp_buf[32]; - long val; + u64 val; ssize_t rc; if (*ppos) return 0; rc = hl_debugfs_i2c_read(hdev, entry->i2c_bus, entry->i2c_addr, - entry->i2c_reg, &val); + entry->i2c_reg, entry->i2c_len, &val); if (rc) { dev_err(hdev->dev, - "Failed to read from I2C bus %d, addr %d, reg %d\n", - entry->i2c_bus, entry->i2c_addr, entry->i2c_reg); + "Failed to read from I2C bus %d, addr %d, reg %d, len %d\n", + entry->i2c_bus, entry->i2c_addr, entry->i2c_reg, entry->i2c_len); return rc; } - sprintf(tmp_buf, "0x%02lx\n", val); + sprintf(tmp_buf, "%#02llx\n", val); rc = simple_read_from_buffer(buf, count, ppos, tmp_buf, strlen(tmp_buf)); @@ -926,19 +937,19 @@ static ssize_t hl_i2c_data_write(struct file *f, const char __user *buf, { struct hl_dbg_device_entry *entry = file_inode(f)->i_private; struct hl_device *hdev = entry->hdev; - u32 value; + u64 value; ssize_t rc; - rc = kstrtouint_from_user(buf, count, 16, &value); + rc = kstrtou64_from_user(buf, count, 16, &value); if (rc) return rc; rc = hl_debugfs_i2c_write(hdev, entry->i2c_bus, entry->i2c_addr, - entry->i2c_reg, value); + entry->i2c_reg, entry->i2c_len, value); if (rc) { dev_err(hdev->dev, - "Failed to write 0x%02x to I2C bus %d, addr %d, reg %d\n", - value, entry->i2c_bus, entry->i2c_addr, entry->i2c_reg); + "Failed to write %#02llx to I2C bus %d, addr %d, reg %d, len %d\n", + value, entry->i2c_bus, entry->i2c_addr, entry->i2c_reg, entry->i2c_len); return rc; } @@ -1421,6 +1432,11 @@ void hl_debugfs_add_device(struct hl_device *hdev) dev_entry->root, &dev_entry->i2c_reg); + debugfs_create_u8("i2c_len", + 0644, + dev_entry->root, + &dev_entry->i2c_len); + debugfs_create_file("i2c_data", 0644, dev_entry->root, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 6b33fbd72fd8..9aa144d2fe40 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1889,6 +1889,7 @@ struct hl_debugfs_entry { * @i2c_bus: generic u8 debugfs file for bus value to use in i2c_data_read. * @i2c_addr: generic u8 debugfs file for address value to use in i2c_data_read. * @i2c_reg: generic u8 debugfs file for register value to use in i2c_data_read. + * @i2c_len: generic u8 debugfs file for length value to use in i2c_data_read. */ struct hl_dbg_device_entry { struct dentry *root; @@ -1917,6 +1918,7 @@ struct hl_dbg_device_entry { u8 i2c_bus; u8 i2c_addr; u8 i2c_reg; + u8 i2c_len; }; /** diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 5e19c763f3f0..078fb4bd0316 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -493,7 +493,14 @@ struct cpucp_packet { __u8 i2c_bus; __u8 i2c_addr; __u8 i2c_reg; - __u8 pad; /* unused */ + /* + * In legacy implemetations, i2c_len was not present, + * was unused and just added as pad. + * So if i2c_len is 0, it is treated as legacy + * and r/w 1 Byte, else if i2c_len is specified, + * its treated as new multibyte r/w support. + */ + __u8 i2c_len; }; struct {/* For PLL info fetch */ -- cgit v1.2.3 From e2637fdca70aa5357b26c57e44fcec0ed673eb22 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 10 Nov 2021 11:41:43 +0200 Subject: habanalabs: handle device TPM boot error as warning AS TPM error indication is not fatal, driver should dump a warning and continue booting. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 9 +++++++++ drivers/misc/habanalabs/include/common/hl_boot_if.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 70e992bdbde7..aea5904332fd 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -529,6 +529,15 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, err_exists = true; } + if (err_val & CPU_BOOT_ERR0_TPM_FAIL) { + dev_warn(hdev->dev, + "Device boot warning - TPM failure\n"); + /* This is a warning so we don't want it to disable the + * device + */ + err_val &= ~CPU_BOOT_ERR0_TPM_FAIL; + } + /* return error only if it's in the predefined mask */ if (err_exists && ((err_val & ~CPU_BOOT_ERR0_ENABLED) & lower_32_bits(hdev->boot_error_status_mask))) diff --git a/drivers/misc/habanalabs/include/common/hl_boot_if.h b/drivers/misc/habanalabs/include/common/hl_boot_if.h index 2626df6ef3ef..135e21d6edc9 100644 --- a/drivers/misc/habanalabs/include/common/hl_boot_if.h +++ b/drivers/misc/habanalabs/include/common/hl_boot_if.h @@ -32,6 +32,7 @@ enum cpu_boot_err { CPU_BOOT_ERR_DEVICE_UNUSABLE_FAIL = 13, CPU_BOOT_ERR_BOOT_FW_CRIT_ERR = 18, CPU_BOOT_ERR_BINNING_FAIL = 19, + CPU_BOOT_ERR_TPM_FAIL = 20, CPU_BOOT_ERR_ENABLED = 31, CPU_BOOT_ERR_SCND_EN = 63, CPU_BOOT_ERR_LAST = 64 /* we have 2 registers of 32 bits */ @@ -108,6 +109,8 @@ enum cpu_boot_err { * malfunctioning components might still be * in use. * + * CPU_BOOT_ERR0_TPM_FAIL TPM verification flow failed. + * * CPU_BOOT_ERR0_ENABLED Error registers enabled. * This is a main indication that the * running FW populates the error @@ -130,6 +133,7 @@ enum cpu_boot_err { #define CPU_BOOT_ERR0_DEVICE_UNUSABLE_FAIL (1 << CPU_BOOT_ERR_DEVICE_UNUSABLE_FAIL) #define CPU_BOOT_ERR0_BOOT_FW_CRIT_ERR (1 << CPU_BOOT_ERR_BOOT_FW_CRIT_ERR) #define CPU_BOOT_ERR0_BINNING_FAIL (1 << CPU_BOOT_ERR_BINNING_FAIL) +#define CPU_BOOT_ERR0_TPM_FAIL (1 << CPU_BOOT_ERR_TPM_FAIL) #define CPU_BOOT_ERR0_ENABLED (1 << CPU_BOOT_ERR_ENABLED) #define CPU_BOOT_ERR1_ENABLED (1 << CPU_BOOT_ERR_ENABLED) -- cgit v1.2.3 From 3e55b5dbf929a40966b8eb7d4de94fad3bb404bd Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Wed, 3 Nov 2021 10:09:59 +0200 Subject: habanalabs: add support for fetching historic errors A new uAPI is added for debug purposes of the user-space to retrieve errors related data from previous session (before device reset was performed). Inforamtion is filled when a razwi or CS timeout happens and can contain one of the following: 1. Retrieve timestamp of last time the device was opened and razwi or CS timeout happened. 2. Retrieve information about last CS timeout. 3. Retrieve information about last razwi error. This information doesn't contain user data, so no danger of data leakage between users. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 8 + drivers/misc/habanalabs/common/habanalabs.h | 37 +++++ drivers/misc/habanalabs/common/habanalabs_drv.c | 4 + drivers/misc/habanalabs/common/habanalabs_ioctl.c | 60 ++++++++ drivers/misc/habanalabs/gaudi/gaudi.c | 167 +++++++++++++++------ include/uapi/misc/habanalabs.h | 58 ++++++- 6 files changed, 290 insertions(+), 44 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index e97b21988dea..c1fd4ba14c60 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -733,6 +733,14 @@ static void cs_timedout(struct work_struct *work) hdev = cs->ctx->hdev; + /* Save only the first CS timeout parameters */ + rc = atomic_cmpxchg(&hdev->last_error.cs_write_disable, 0, 1); + if (!rc) { + hdev->last_error.open_dev_timestamp = hdev->last_successful_open_ktime; + hdev->last_error.cs_timeout_timestamp = ktime_get(); + hdev->last_error.cs_timeout_seq = cs->sequence; + } + switch (cs->type) { case CS_TYPE_SIGNAL: dev_err(hdev->dev, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 9aa144d2fe40..612a9f461b38 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2405,6 +2405,40 @@ struct hl_clk_throttle { u32 aggregated_reason; }; +/** + * struct last_error_session_info - info about last session in which CS timeout or + * razwi error occurred. + * @open_dev_timestamp: device open timestamp. + * @cs_timeout_timestamp: CS timeout timestamp. + * @razwi_timestamp: razwi timestamp. + * @cs_write_disable: if set writing to CS parameters in the structure is disabled so the + * first (root cause) CS timeout will not be overwritten. + * @razwi_write_disable: if set writing to razwi parameters in the structure is disabled so the + * first (root cause) razwi will not be overwritten. + * @cs_timeout_seq: CS timeout sequence number. + * @razwi_addr: address that caused razwi. + * @razwi_engine_id_1: engine id of the razwi initiator, if it was initiated by engine that does + * not have engine id it will be set to U16_MAX. + * @razwi_engine_id_2: second engine id of razwi initiator. Might happen that razwi have 2 possible + * engines which one them caused the razwi. In that case, it will contain the + * second possible engine id, otherwise it will be set to U16_MAX. + * @razwi_non_engine_initiator: in case the initiator of the razwi does not have engine id. + * @razwi_type: cause of razwi, page fault or access error, otherwise it will be set to U8_MAX. + */ +struct last_error_session_info { + ktime_t open_dev_timestamp; + ktime_t cs_timeout_timestamp; + ktime_t razwi_timestamp; + atomic_t cs_write_disable; + atomic_t razwi_write_disable; + u64 cs_timeout_seq; + u64 razwi_addr; + u16 razwi_engine_id_1; + u16 razwi_engine_id_2; + u8 razwi_non_engine_initiator; + u8 razwi_type; +}; + /** * struct hl_device - habanalabs device structure. * @pdev: pointer to PCI device, can be NULL in case of simulator device. @@ -2488,6 +2522,7 @@ struct hl_clk_throttle { * device initialization. Mainly used to debug and * workaround firmware bugs * @dram_pci_bar_start: start bus address of PCIe bar towards DRAM. + * @last_successful_open_ktime: timestamp (ktime) of the last successful device open. * @last_successful_open_jif: timestamp (jiffies) of the last successful * device open. * @last_open_session_duration_jif: duration (jiffies) of the last device open @@ -2632,6 +2667,7 @@ struct hl_device { struct multi_cs_completion multi_cs_completion[ MULTI_CS_MAX_USER_CTX]; struct hl_clk_throttle clk_throttling; + struct last_error_session_info last_error; u32 *stream_master_qid_arr; atomic64_t dram_used_mem; @@ -2645,6 +2681,7 @@ struct hl_device { u64 open_counter; u64 fw_poll_interval_usec; atomic_t in_reset; + ktime_t last_successful_open_ktime; enum hl_pll_frequency curr_pll_profile; enum cpucp_card_types card_type; u32 major; diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 85034f2f2e89..1070c80d739c 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -187,8 +187,12 @@ int hl_device_open(struct inode *inode, struct file *filp) hl_debugfs_add_file(hpriv); + atomic_set(&hdev->last_error.cs_write_disable, 0); + atomic_set(&hdev->last_error.razwi_write_disable, 0); + hdev->open_counter++; hdev->last_successful_open_jif = jiffies; + hdev->last_successful_open_ktime = ktime_get(); return 0; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 68c655acdec8..360a1e9bbd5d 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -540,6 +540,57 @@ static int dram_replaced_rows_info(struct hl_fpriv *hpriv, struct hl_info_args * return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; } +static int last_err_open_dev_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_info_last_err_open_dev_time info = {0}; + struct hl_device *hdev = hpriv->hdev; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + info.timestamp = ktime_to_ns(hdev->last_error.open_dev_timestamp); + + return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; +} + +static int cs_timeout_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_info_cs_timeout_event info = {0}; + struct hl_device *hdev = hpriv->hdev; + u32 max_size = args->return_size; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + info.seq = hdev->last_error.cs_timeout_seq; + info.timestamp = ktime_to_ns(hdev->last_error.cs_timeout_timestamp); + + return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; +} + +static int razwi_info(struct hl_fpriv *hpriv, struct hl_info_args *args) +{ + struct hl_device *hdev = hpriv->hdev; + u32 max_size = args->return_size; + struct hl_info_razwi_event info = {0}; + void __user *out = (void __user *) (uintptr_t) args->return_pointer; + + if ((!max_size) || (!out)) + return -EINVAL; + + info.timestamp = ktime_to_ns(hdev->last_error.razwi_timestamp); + info.addr = hdev->last_error.razwi_addr; + info.engine_id_1 = hdev->last_error.razwi_engine_id_1; + info.engine_id_2 = hdev->last_error.razwi_engine_id_2; + info.no_engine_id = hdev->last_error.razwi_non_engine_initiator; + info.error_type = hdev->last_error.razwi_type; + + return copy_to_user(out, &info, min_t(size_t, max_size, sizeof(info))) ? -EFAULT : 0; +} + static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, struct device *dev) { @@ -632,6 +683,15 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_DRAM_PENDING_ROWS: return dram_pending_rows_info(hpriv, args); + case HL_INFO_LAST_ERR_OPEN_DEV_TIME: + return last_err_open_dev_info(hpriv, args); + + case HL_INFO_CS_TIMEOUT_EVENT: + return cs_timeout_info(hpriv, args); + + case HL_INFO_RAZWI_EVENT: + return razwi_info(hpriv, args); + default: dev_err(dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index a9e279bfebae..aed55db368d7 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -6970,8 +6970,9 @@ event_not_supported: snprintf(desc, size, "N/A"); } -static const char *gaudi_get_razwi_initiator_dma_name(struct hl_device *hdev, - u32 x_y, bool is_write) +static const char *gaudi_get_razwi_initiator_dma_name(struct hl_device *hdev, u32 x_y, + bool is_write, s32 *engine_id_1, + s32 *engine_id_2) { u32 dma_id[2], dma_offset, err_cause[2], mask, i; @@ -7011,44 +7012,64 @@ static const char *gaudi_get_razwi_initiator_dma_name(struct hl_device *hdev, switch (x_y) { case RAZWI_INITIATOR_ID_X_Y_DMA_IF_W_S_0: case RAZWI_INITIATOR_ID_X_Y_DMA_IF_W_S_1: - if ((err_cause[0] & mask) && !(err_cause[1] & mask)) + if ((err_cause[0] & mask) && !(err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_0; return "DMA0"; - else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) + } else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_2; return "DMA2"; - else + } else { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_0; + *engine_id_2 = GAUDI_ENGINE_ID_DMA_2; return "DMA0 or DMA2"; + } case RAZWI_INITIATOR_ID_X_Y_DMA_IF_E_S_0: case RAZWI_INITIATOR_ID_X_Y_DMA_IF_E_S_1: - if ((err_cause[0] & mask) && !(err_cause[1] & mask)) + if ((err_cause[0] & mask) && !(err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_1; return "DMA1"; - else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) + } else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_3; return "DMA3"; - else + } else { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_1; + *engine_id_2 = GAUDI_ENGINE_ID_DMA_3; return "DMA1 or DMA3"; + } case RAZWI_INITIATOR_ID_X_Y_DMA_IF_W_N_0: case RAZWI_INITIATOR_ID_X_Y_DMA_IF_W_N_1: - if ((err_cause[0] & mask) && !(err_cause[1] & mask)) + if ((err_cause[0] & mask) && !(err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_4; return "DMA4"; - else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) + } else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_6; return "DMA6"; - else + } else { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_4; + *engine_id_2 = GAUDI_ENGINE_ID_DMA_6; return "DMA4 or DMA6"; + } case RAZWI_INITIATOR_ID_X_Y_DMA_IF_E_N_0: case RAZWI_INITIATOR_ID_X_Y_DMA_IF_E_N_1: - if ((err_cause[0] & mask) && !(err_cause[1] & mask)) + if ((err_cause[0] & mask) && !(err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_5; return "DMA5"; - else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) + } else if (!(err_cause[0] & mask) && (err_cause[1] & mask)) { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_7; return "DMA7"; - else + } else { + *engine_id_1 = GAUDI_ENGINE_ID_DMA_5; + *engine_id_2 = GAUDI_ENGINE_ID_DMA_7; return "DMA5 or DMA7"; + } } unknown_initiator: return "unknown initiator"; } -static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, - bool is_write) +static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, bool is_write, + u32 *engine_id_1, u32 *engine_id_2) { u32 val, x_y, axi_id; @@ -7061,24 +7082,35 @@ static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, switch (x_y) { case RAZWI_INITIATOR_ID_X_Y_TPC0_NIC0: - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) { + *engine_id_1 = GAUDI_ENGINE_ID_TPC_0; return "TPC0"; - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC)) + } + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC)) { + *engine_id_1 = GAUDI_ENGINE_ID_NIC_0; return "NIC0"; + } break; case RAZWI_INITIATOR_ID_X_Y_TPC1: + *engine_id_1 = GAUDI_ENGINE_ID_TPC_1; return "TPC1"; case RAZWI_INITIATOR_ID_X_Y_MME0_0: case RAZWI_INITIATOR_ID_X_Y_MME0_1: + *engine_id_1 = GAUDI_ENGINE_ID_MME_0; return "MME0"; case RAZWI_INITIATOR_ID_X_Y_MME1_0: case RAZWI_INITIATOR_ID_X_Y_MME1_1: + *engine_id_1 = GAUDI_ENGINE_ID_MME_1; return "MME1"; case RAZWI_INITIATOR_ID_X_Y_TPC2: + *engine_id_1 = GAUDI_ENGINE_ID_TPC_2; return "TPC2"; case RAZWI_INITIATOR_ID_X_Y_TPC3_PCI_CPU_PSOC: - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) { + *engine_id_1 = GAUDI_ENGINE_ID_TPC_3; return "TPC3"; + } + /* PCI, CPU or PSOC does not have engine id*/ if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_PCI)) return "PCI"; if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_CPU)) @@ -7094,32 +7126,49 @@ static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, case RAZWI_INITIATOR_ID_X_Y_DMA_IF_W_N_1: case RAZWI_INITIATOR_ID_X_Y_DMA_IF_E_N_0: case RAZWI_INITIATOR_ID_X_Y_DMA_IF_E_N_1: - return gaudi_get_razwi_initiator_dma_name(hdev, x_y, is_write); + return gaudi_get_razwi_initiator_dma_name(hdev, x_y, is_write, + engine_id_1, engine_id_2); case RAZWI_INITIATOR_ID_X_Y_TPC4_NIC1_NIC2: - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) { + *engine_id_1 = GAUDI_ENGINE_ID_TPC_4; return "TPC4"; - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC)) + } + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC)) { + *engine_id_1 = GAUDI_ENGINE_ID_NIC_1; return "NIC1"; - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC_FT)) + } + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC_FT)) { + *engine_id_1 = GAUDI_ENGINE_ID_NIC_2; return "NIC2"; + } break; case RAZWI_INITIATOR_ID_X_Y_TPC5: + *engine_id_1 = GAUDI_ENGINE_ID_TPC_5; return "TPC5"; case RAZWI_INITIATOR_ID_X_Y_MME2_0: case RAZWI_INITIATOR_ID_X_Y_MME2_1: + *engine_id_1 = GAUDI_ENGINE_ID_MME_2; return "MME2"; case RAZWI_INITIATOR_ID_X_Y_MME3_0: case RAZWI_INITIATOR_ID_X_Y_MME3_1: + *engine_id_1 = GAUDI_ENGINE_ID_MME_3; return "MME3"; case RAZWI_INITIATOR_ID_X_Y_TPC6: + *engine_id_1 = GAUDI_ENGINE_ID_TPC_6; return "TPC6"; case RAZWI_INITIATOR_ID_X_Y_TPC7_NIC4_NIC5: - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_TPC)) { + *engine_id_1 = GAUDI_ENGINE_ID_TPC_7; return "TPC7"; - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC)) + } + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC)) { + *engine_id_1 = GAUDI_ENGINE_ID_NIC_4; return "NIC4"; - if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC_FT)) + } + if (axi_id == RAZWI_INITIATOR_ID_AXI_ID(AXI_ID_NIC_FT)) { + *engine_id_1 = GAUDI_ENGINE_ID_NIC_5; return "NIC5"; + } break; default: break; @@ -7136,27 +7185,28 @@ static const char *gaudi_get_razwi_initiator_name(struct hl_device *hdev, return "unknown initiator"; } -static void gaudi_print_razwi_info(struct hl_device *hdev) +static void gaudi_print_and_get_razwi_info(struct hl_device *hdev, u32 *engine_id_1, + u32 *engine_id_2) { + if (RREG32(mmMMU_UP_RAZWI_WRITE_VLD)) { dev_err_ratelimited(hdev->dev, "RAZWI event caused by illegal write of %s\n", - gaudi_get_razwi_initiator_name(hdev, true)); + gaudi_get_razwi_initiator_name(hdev, true, engine_id_1, engine_id_2)); WREG32(mmMMU_UP_RAZWI_WRITE_VLD, 0); } if (RREG32(mmMMU_UP_RAZWI_READ_VLD)) { dev_err_ratelimited(hdev->dev, "RAZWI event caused by illegal read of %s\n", - gaudi_get_razwi_initiator_name(hdev, false)); + gaudi_get_razwi_initiator_name(hdev, false, engine_id_1, engine_id_2)); WREG32(mmMMU_UP_RAZWI_READ_VLD, 0); } } -static void gaudi_print_mmu_error_info(struct hl_device *hdev) +static void gaudi_print_and_get_mmu_error_info(struct hl_device *hdev, u64 *addr, u8 *type) { struct gaudi_device *gaudi = hdev->asic_specific; - u64 addr; u32 val; if (!(gaudi->hw_cap_initialized & HW_CAP_MMU)) @@ -7164,24 +7214,24 @@ static void gaudi_print_mmu_error_info(struct hl_device *hdev) val = RREG32(mmMMU_UP_PAGE_ERROR_CAPTURE); if (val & MMU_UP_PAGE_ERROR_CAPTURE_ENTRY_VALID_MASK) { - addr = val & MMU_UP_PAGE_ERROR_CAPTURE_VA_49_32_MASK; - addr <<= 32; - addr |= RREG32(mmMMU_UP_PAGE_ERROR_CAPTURE_VA); + *addr = val & MMU_UP_PAGE_ERROR_CAPTURE_VA_49_32_MASK; + *addr <<= 32; + *addr |= RREG32(mmMMU_UP_PAGE_ERROR_CAPTURE_VA); - dev_err_ratelimited(hdev->dev, "MMU page fault on va 0x%llx\n", - addr); + dev_err_ratelimited(hdev->dev, "MMU page fault on va 0x%llx\n", *addr); + *type = HL_RAZWI_PAGE_FAULT; WREG32(mmMMU_UP_PAGE_ERROR_CAPTURE, 0); } val = RREG32(mmMMU_UP_ACCESS_ERROR_CAPTURE); if (val & MMU_UP_ACCESS_ERROR_CAPTURE_ENTRY_VALID_MASK) { - addr = val & MMU_UP_ACCESS_ERROR_CAPTURE_VA_49_32_MASK; - addr <<= 32; - addr |= RREG32(mmMMU_UP_ACCESS_ERROR_CAPTURE_VA); + *addr = val & MMU_UP_ACCESS_ERROR_CAPTURE_VA_49_32_MASK; + *addr <<= 32; + *addr |= RREG32(mmMMU_UP_ACCESS_ERROR_CAPTURE_VA); - dev_err_ratelimited(hdev->dev, - "MMU access error on va 0x%llx\n", addr); + dev_err_ratelimited(hdev->dev, "MMU access error on va 0x%llx\n", *addr); + *type = HL_RAZWI_MMU_ACCESS_ERROR; WREG32(mmMMU_UP_ACCESS_ERROR_CAPTURE, 0); } @@ -7700,15 +7750,46 @@ static void gaudi_handle_qman_err(struct hl_device *hdev, u16 event_type) static void gaudi_print_irq_info(struct hl_device *hdev, u16 event_type, bool razwi) { + u32 engine_id_1, engine_id_2; char desc[64] = ""; + u64 razwi_addr = 0; + u8 razwi_type; + int rc; + + /* + * Init engine id by default as not valid and only if razwi initiated from engine with + * engine id it will get valid value. + * Init razwi type to default, will be changed only if razwi caused by page fault of + * MMU access error + */ + engine_id_1 = U16_MAX; + engine_id_2 = U16_MAX; + razwi_type = U8_MAX; gaudi_get_event_desc(event_type, desc, sizeof(desc)); dev_err_ratelimited(hdev->dev, "Received H/W interrupt %d [\"%s\"]\n", event_type, desc); if (razwi) { - gaudi_print_razwi_info(hdev); - gaudi_print_mmu_error_info(hdev); + gaudi_print_and_get_razwi_info(hdev, &engine_id_1, &engine_id_2); + gaudi_print_and_get_mmu_error_info(hdev, &razwi_addr, &razwi_type); + + /* In case it's the first razwi, save its parameters*/ + rc = atomic_cmpxchg(&hdev->last_error.razwi_write_disable, 0, 1); + if (!rc) { + hdev->last_error.open_dev_timestamp = hdev->last_successful_open_ktime; + hdev->last_error.razwi_timestamp = ktime_get(); + hdev->last_error.razwi_addr = razwi_addr; + hdev->last_error.razwi_engine_id_1 = engine_id_1; + hdev->last_error.razwi_engine_id_2 = engine_id_2; + /* + * If first engine id holds non valid value the razwi initiator + * does not have engine id + */ + hdev->last_error.razwi_non_engine_initiator = (engine_id_1 == U16_MAX); + hdev->last_error.razwi_type = razwi_type; + + } } } diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 9b4d72897061..eb8565fdae70 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -336,6 +336,14 @@ enum hl_server_type { * HL_INFO_OPEN_STATS - Retrieve info regarding recent device open calls * HL_INFO_DRAM_REPLACED_ROWS - Retrieve DRAM replaced rows info * HL_INFO_DRAM_PENDING_ROWS - Retrieve DRAM pending rows num + * HL_INFO_LAST_ERR_OPEN_DEV_TIME - Retrieve timestamp of the last time the device was opened + * and CS timeout or razwi error occurred. + * HL_INFO_CS_TIMEOUT_EVENT - Retrieve CS timeout timestamp and its related CS sequence number. + * HL_INFO_RAZWI_EVENT - Retrieve parameters of razwi: + * Timestamp of razwi. + * The address which accessing it caused the razwi. + * Razwi initiator. + * Razwi cause, was it a page fault or MMU access error. */ #define HL_INFO_HW_IP_INFO 0 #define HL_INFO_HW_EVENTS 1 @@ -357,8 +365,11 @@ enum hl_server_type { #define HL_INFO_OPEN_STATS 18 #define HL_INFO_DRAM_REPLACED_ROWS 21 #define HL_INFO_DRAM_PENDING_ROWS 22 +#define HL_INFO_LAST_ERR_OPEN_DEV_TIME 23 +#define HL_INFO_CS_TIMEOUT_EVENT 24 +#define HL_INFO_RAZWI_EVENT 25 -#define HL_INFO_VERSION_MAX_LEN 128 +#define HL_INFO_VERSION_MAX_LEN 128 #define HL_INFO_CARD_NAME_MAX_LEN 16 /** @@ -575,6 +586,51 @@ struct hl_info_cs_counters { __u64 ctx_validation_drop_cnt; }; +/** + * struct hl_info_last_err_open_dev_time - last error boot information. + * @timestamp: timestamp of last time the device was opened and error occurred. + */ +struct hl_info_last_err_open_dev_time { + __s64 timestamp; +}; + +/** + * struct hl_info_cs_timeout_event - last CS timeout information. + * @timestamp: timestamp when last CS timeout event occurred. + * @seq: sequence number of last CS timeout event. + */ +struct hl_info_cs_timeout_event { + __s64 timestamp; + __u64 seq; +}; + +#define HL_RAZWI_PAGE_FAULT 0 +#define HL_RAZWI_MMU_ACCESS_ERROR 1 + +/** + * struct hl_info_razwi_event - razwi information. + * @timestamp: timestamp of razwi. + * @addr: address which accessing it caused razwi. + * @engine_id_1: engine id of the razwi initiator, if it was initiated by engine that does not + * have engine id it will be set to U16_MAX. + * @engine_id_2: second engine id of razwi initiator. Might happen that razwi have 2 possible + * engines which one them caused the razwi. In that case, it will contain the + * second possible engine id, otherwise it will be set to U16_MAX. + * @no_engine_id: if razwi initiator does not have engine id, this field will be set to 1, + * otherwise 0. + * @error_type: cause of razwi, page fault or access error, otherwise it will be set to U8_MAX. + * @pad: padding to 64 bit. + */ +struct hl_info_razwi_event { + __s64 timestamp; + __u64 addr; + __u16 engine_id_1; + __u16 engine_id_2; + __u8 no_engine_id; + __u8 error_type; + __u8 pad[2]; +}; + enum gaudi_dcores { HL_GAUDI_WS_DCORE, HL_GAUDI_WN_DCORE, -- cgit v1.2.3 From fe8d70873c4919086d5929c49e1c6cd6bb7d1de3 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sat, 13 Nov 2021 17:58:43 +0200 Subject: habanalabs: prevent false heartbeat message If a device reset has started, there is a chance that the heartbeat function will fail because the device is disabled at the beginning of the reset function. In that case, we don't want the error message to appear in the log. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 0da5a55490ff..ca74d7815a67 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -543,7 +543,9 @@ static void hl_device_heartbeat(struct work_struct *work) if (!hdev->asic_funcs->send_heartbeat(hdev)) goto reschedule; - dev_err(hdev->dev, "Device heartbeat failed!\n"); + if (hl_device_operational(hdev, NULL)) + dev_err(hdev->dev, "Device heartbeat failed!\n"); + hl_device_reset(hdev, HL_DRV_RESET_HARD | HL_DRV_RESET_HEARTBEAT); return; -- cgit v1.2.3 From a9ecddb9e30af1485c8f41afb0264ec53ae6f5ed Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Sun, 14 Nov 2021 09:29:48 +0200 Subject: habanalabs: align debugfs documentation to alphabetical order Move an entry in the debugfs documentation to align with the alphabetical order which is kept this file. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- Documentation/ABI/testing/debugfs-driver-habanalabs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Documentation/ABI/testing/debugfs-driver-habanalabs b/Documentation/ABI/testing/debugfs-driver-habanalabs index 6085ee506135..2667cbf940f3 100644 --- a/Documentation/ABI/testing/debugfs-driver-habanalabs +++ b/Documentation/ABI/testing/debugfs-driver-habanalabs @@ -21,11 +21,11 @@ Description: Allow the root user to disable/enable in runtime the clock a different engine to disable/enable its clock gating feature. The bitmask is composed of 20 bits: - ======= ============ + ======= ============ 0 - 7 DMA channels 8 - 11 MME engines 12 - 19 TPC engines - ======= ============ + ======= ============ The bit's location of a specific engine can be determined using (1 << GAUDI_ENGINE_ID_*). GAUDI_ENGINE_ID_* values @@ -233,12 +233,6 @@ Description: Gets the state dump occurring on a CS timeout or failure. Writing an integer X discards X state dumps, so that the next read would return X+1-st newest state dump. -What: /sys/kernel/debug/habanalabs/hl/timeout_locked -Date: Sep 2021 -KernelVersion: 5.16 -Contact: obitton@habana.ai -Description: Sets the command submission timeout value in seconds. - What: /sys/kernel/debug/habanalabs/hl/stop_on_err Date: Mar 2020 KernelVersion: 5.6 @@ -246,6 +240,12 @@ Contact: ogabbay@kernel.org Description: Sets the stop-on_error option for the device engines. Value of "0" is for disable, otherwise enable. +What: /sys/kernel/debug/habanalabs/hl/timeout_locked +Date: Sep 2021 +KernelVersion: 5.16 +Contact: obitton@habana.ai +Description: Sets the command submission timeout value in seconds. + What: /sys/kernel/debug/habanalabs/hl/userptr Date: Jan 2019 KernelVersion: 5.1 -- cgit v1.2.3 From 6f61e47a68b403f8aa7956b4b6502511fcf19bb7 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Sun, 14 Nov 2021 09:37:33 +0200 Subject: habanalabs: skip PLL freq fetch Getting the used PLL index with which to send the CPUPU packet relies on the CPUCP info packet. In case CPU queues are not enabled getting the PLL index will issue an error and in some ASICs will also fail the driver load. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 5 +++++ drivers/misc/habanalabs/goya/goya.c | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index aed55db368d7..465540d064b6 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -881,6 +881,11 @@ static int gaudi_fetch_psoc_frequency(struct hl_device *hdev) int rc; if (hdev->asic_prop.fw_security_enabled) { + struct gaudi_device *gaudi = hdev->asic_specific; + + if (!(gaudi->hw_cap_initialized & HW_CAP_CPU_Q)) + return 0; + rc = hl_fw_cpucp_pll_info_get(hdev, HL_GAUDI_CPU_PLL, pll_freq_arr); if (rc) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 7b3683f2a6dc..2347de2f426a 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -739,6 +739,11 @@ static void goya_fetch_psoc_frequency(struct hl_device *hdev) int rc; if (hdev->asic_prop.fw_security_enabled) { + struct goya_device *goya = hdev->asic_specific; + + if (!(goya->hw_cap_initialized & HW_CAP_CPU_Q)) + return; + rc = hl_fw_cpucp_pll_info_get(hdev, HL_GOYA_PCI_PLL, pll_freq_arr); -- cgit v1.2.3 From a1b838adb080ee4320f257a8280821e47bfb9a1f Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 9 Nov 2021 13:12:38 +0200 Subject: habanalabs: fix possible deadlock in cache invl failure Currently there is a deadlock in driver in scenarios where MMU cache invalidation fails. The issue is basically device reset being performed without releasing the MMU mutex. The solution is to skip device reset as it is not necessary. In addition we introduce a slight code refactor that prints the invalidation error from a single location. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 9 ++++----- drivers/misc/habanalabs/common/habanalabs.h | 3 +++ drivers/misc/habanalabs/common/memory.c | 25 +++++++------------------ drivers/misc/habanalabs/common/mmu/mmu.c | 25 +++++++++++++++++++++++++ drivers/misc/habanalabs/gaudi/gaudi.c | 6 ------ drivers/misc/habanalabs/goya/goya.c | 6 ------ 6 files changed, 39 insertions(+), 35 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index 71910f7809bd..c591f0487272 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -80,14 +80,13 @@ static int cb_map_mem(struct hl_ctx *ctx, struct hl_cb *cb) offset += va_block->size; } - hdev->asic_funcs->mmu_invalidate_cache(hdev, false, - MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV); + rc = hl_mmu_invalidate_cache(hdev, false, MMU_OP_USERPTR | MMU_OP_SKIP_LOW_CACHE_INV); mutex_unlock(&ctx->mmu_lock); cb->is_mmu_mapped = true; - return 0; + return rc; err_va_umap: list_for_each_entry(va_block, &cb->va_block_list, node) { @@ -98,7 +97,7 @@ err_va_umap: offset -= va_block->size; } - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); + rc = hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); @@ -127,7 +126,7 @@ static void cb_unmap_mem(struct hl_ctx *ctx, struct hl_cb *cb) "Failed to unmap CB's va 0x%llx\n", va_block->start); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); + hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); mutex_unlock(&ctx->mmu_lock); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 612a9f461b38..406ca50f192a 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2993,6 +2993,9 @@ int hl_mmu_unmap_page(struct hl_ctx *ctx, u64 virt_addr, u32 page_size, int hl_mmu_map_contiguous(struct hl_ctx *ctx, u64 virt_addr, u64 phys_addr, u32 size); int hl_mmu_unmap_contiguous(struct hl_ctx *ctx, u64 virt_addr, u32 size); +int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags); +int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard, + u32 flags, u32 asid, u64 va, u64 size); void hl_mmu_swap_out(struct hl_ctx *ctx); void hl_mmu_swap_in(struct hl_ctx *ctx); int hl_mmu_if_set_funcs(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 530f8b4fadd2..315594e96dcd 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -1201,18 +1201,13 @@ static int map_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, goto map_err; } - rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, false, - *vm_type | MMU_OP_SKIP_LOW_CACHE_INV, - ctx->asid, ret_vaddr, phys_pg_pack->total_size); + rc = hl_mmu_invalidate_cache_range(hdev, false, *vm_type | MMU_OP_SKIP_LOW_CACHE_INV, + ctx->asid, ret_vaddr, phys_pg_pack->total_size); mutex_unlock(&ctx->mmu_lock); - if (rc) { - dev_err(hdev->dev, - "mapping handle %u failed due to MMU cache invalidation\n", - handle); + if (rc) goto map_err; - } ret_vaddr += phys_pg_pack->offset; @@ -1350,9 +1345,8 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, * at the loop end rather than for each iteration */ if (!ctx_free) - rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, true, - *vm_type, ctx->asid, vaddr, - phys_pg_pack->total_size); + rc = hl_mmu_invalidate_cache_range(hdev, true, *vm_type, ctx->asid, vaddr, + phys_pg_pack->total_size); mutex_unlock(&ctx->mmu_lock); @@ -1365,11 +1359,6 @@ static int unmap_device_va(struct hl_ctx *ctx, struct hl_mem_in *args, if (!ctx_free) { int tmp_rc; - if (rc) - dev_err(hdev->dev, - "unmapping vaddr 0x%llx failed due to MMU cache invalidation\n", - vaddr); - tmp_rc = add_va_block(hdev, va_range, vaddr, vaddr + phys_pg_pack->total_size - 1); if (tmp_rc) { @@ -2640,8 +2629,8 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) mutex_lock(&ctx->mmu_lock); /* invalidate the cache once after the unmapping loop */ - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); - hdev->asic_funcs->mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK); + hl_mmu_invalidate_cache(hdev, true, MMU_OP_USERPTR); + hl_mmu_invalidate_cache(hdev, true, MMU_OP_PHYS_PACK); mutex_unlock(&ctx->mmu_lock); diff --git a/drivers/misc/habanalabs/common/mmu/mmu.c b/drivers/misc/habanalabs/common/mmu/mmu.c index aa96917f62e5..9153a1f55175 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu.c +++ b/drivers/misc/habanalabs/common/mmu/mmu.c @@ -637,3 +637,28 @@ u64 hl_mmu_descramble_addr(struct hl_device *hdev, u64 addr) { return addr; } + +int hl_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, u32 flags) +{ + int rc; + + rc = hdev->asic_funcs->mmu_invalidate_cache(hdev, is_hard, flags); + if (rc) + dev_err_ratelimited(hdev->dev, "MMU cache invalidation failed\n"); + + return rc; +} + +int hl_mmu_invalidate_cache_range(struct hl_device *hdev, bool is_hard, + u32 flags, u32 asid, u64 va, u64 size) +{ + int rc; + + rc = hdev->asic_funcs->mmu_invalidate_cache_range(hdev, is_hard, flags, + asid, va, size); + if (rc) + dev_err_ratelimited(hdev->dev, "MMU cache range invalidation failed\n"); + + return rc; +} + diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 465540d064b6..b101a46076b8 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -8366,12 +8366,6 @@ static int gaudi_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, WREG32(mmSTLB_INV_SET, 0); - if (rc) { - dev_err_ratelimited(hdev->dev, - "MMU cache invalidation timeout\n"); - hl_device_reset(hdev, HL_DRV_RESET_HARD); - } - return rc; } diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 2347de2f426a..5e6998d21adb 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -5258,12 +5258,6 @@ static int goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, 1000, timeout_usec); - if (rc) { - dev_err_ratelimited(hdev->dev, - "MMU cache invalidation timeout\n"); - hl_device_reset(hdev, HL_DRV_RESET_HARD); - } - return rc; } -- cgit v1.2.3 From ab440d3e39f61018b1f4c1c6bed6ab037f69a82e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Mon, 15 Nov 2021 17:13:37 +0200 Subject: habanalabs: abort reset on invalid request Hard-reset is mutually exclusive with reset-on-device-release. Therefore, if such a request arrives to the reset function, abort the reset and return an error to the callee. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index ca74d7815a67..a3d5617da64c 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -1020,8 +1020,8 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) */ int hl_device_reset(struct hl_device *hdev, u32 flags) { - u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false; + u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; int i, rc; if (!hdev->init_done) { @@ -1039,11 +1039,13 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) hard_reset = true; } - if (hdev->reset_upon_device_release && - (flags & HL_DRV_RESET_DEV_RELEASE)) { - dev_dbg(hdev->dev, - "Perform %s-reset upon device release\n", - hard_reset ? "hard" : "soft"); + if (hdev->reset_upon_device_release && (flags & HL_DRV_RESET_DEV_RELEASE)) { + if (hard_reset) { + dev_crit(hdev->dev, + "Aborting reset because hard-reset is mutually exclusive with reset-on-device-release\n"); + return -EINVAL; + } + goto do_reset; } -- cgit v1.2.3 From d8eb50f31cc7b0f01e610327376a49ac3f0865a2 Mon Sep 17 00:00:00 2001 From: Rajaravi Krishna Katta Date: Thu, 5 Aug 2021 10:24:16 +0300 Subject: habanalabs: Move frequency change thread to goya_late_init Changing the frequency automatically is only done in Goya. In future ASICs this is done inside the firmware. Therefore, move the common code into the Goya specific files. Main changes as part of the commit are: 1. The thread for setting frequency is moved from device_late_init to goya_late_init 2. hl_device_set_frequency is removed from hl_device_open as it is not relevant for other ASICs and for Goya it is taken care by the thread 3. hl_device_set_frequency is renamed as goya_set_frequency Signed-off-by: Rajaravi Krishna Katta Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 58 -------------------- drivers/misc/habanalabs/common/habanalabs.h | 7 --- drivers/misc/habanalabs/common/habanalabs_drv.c | 7 --- drivers/misc/habanalabs/common/sysfs.c | 5 -- drivers/misc/habanalabs/gaudi/gaudi.c | 2 + drivers/misc/habanalabs/goya/goya.c | 73 +++++++++++++++++++++++++ drivers/misc/habanalabs/goya/goyaP.h | 10 ++++ drivers/misc/habanalabs/goya/goya_hwmgr.c | 27 +++++---- 8 files changed, 100 insertions(+), 89 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index a3d5617da64c..484e0446381e 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -516,22 +516,6 @@ static void device_early_fini(struct hl_device *hdev) hdev->asic_funcs->early_fini(hdev); } -static void set_freq_to_low_job(struct work_struct *work) -{ - struct hl_device *hdev = container_of(work, struct hl_device, - work_freq.work); - - mutex_lock(&hdev->fpriv_list_lock); - - if (!hdev->compute_ctx) - hl_device_set_frequency(hdev, PLL_LOW); - - mutex_unlock(&hdev->fpriv_list_lock); - - schedule_delayed_work(&hdev->work_freq, - usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); -} - static void hl_device_heartbeat(struct work_struct *work) { struct hl_device *hdev = container_of(work, struct hl_device, @@ -591,18 +575,6 @@ static int device_late_init(struct hl_device *hdev) hdev->high_pll = hdev->asic_prop.high_pll; - /* force setting to low frequency */ - hdev->curr_pll_profile = PLL_LOW; - - if (hdev->pm_mng_profile == PM_AUTO) - hdev->asic_funcs->set_pll_profile(hdev, PLL_LOW); - else - hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST); - - INIT_DELAYED_WORK(&hdev->work_freq, set_freq_to_low_job); - schedule_delayed_work(&hdev->work_freq, - usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); - if (hdev->heartbeat) { INIT_DELAYED_WORK(&hdev->work_heartbeat, hl_device_heartbeat); schedule_delayed_work(&hdev->work_heartbeat, @@ -625,7 +597,6 @@ static void device_late_fini(struct hl_device *hdev) if (!hdev->late_init_done) return; - cancel_delayed_work_sync(&hdev->work_freq); if (hdev->heartbeat) cancel_delayed_work_sync(&hdev->work_heartbeat); @@ -655,35 +626,6 @@ int hl_device_utilization(struct hl_device *hdev, u32 *utilization) return 0; } -/* - * hl_device_set_frequency - set the frequency of the device - * - * @hdev: pointer to habanalabs device structure - * @freq: the new frequency value - * - * Change the frequency if needed. This function has no protection against - * concurrency, therefore it is assumed that the calling function has protected - * itself against the case of calling this function from multiple threads with - * different values - * - * Returns 0 if no change was done, otherwise returns 1 - */ -int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq) -{ - if ((hdev->pm_mng_profile == PM_MANUAL) || - (hdev->curr_pll_profile == freq)) - return 0; - - dev_dbg(hdev->dev, "Changing device frequency to %s\n", - freq == PLL_HIGH ? "high" : "low"); - - hdev->asic_funcs->set_pll_profile(hdev, freq); - - hdev->curr_pll_profile = freq; - - return 1; -} - int hl_device_set_debug_mode(struct hl_device *hdev, bool enable) { int rc = 0; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 406ca50f192a..1a7f8d37f684 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2450,7 +2450,6 @@ struct last_error_session_info { * @cdev_ctrl: char device for control operations only (INFO IOCTL) * @dev: related kernel basic device structure. * @dev_ctrl: related kernel device structure for the control device - * @work_freq: delayed work to lower device frequency if possible. * @work_heartbeat: delayed work for CPU-CP is-alive check. * @device_reset_work: delayed work which performs hard reset * @asic_name: ASIC specific name. @@ -2485,7 +2484,6 @@ struct last_error_session_info { * @asic_specific: ASIC specific information to use only from ASIC files. * @vm: virtual memory manager for MMU. * @hwmon_dev: H/W monitor device. - * @pm_mng_profile: current power management profile. * @hl_chip_info: ASIC's sensors information. * @device_status_description: device status description. * @hl_debugfs: device's debugfs manager. @@ -2530,7 +2528,6 @@ struct last_error_session_info { * @open_counter: number of successful device open operations. * @fw_poll_interval_usec: FW status poll interval in usec. * @in_reset: is device in reset flow. - * @curr_pll_profile: current PLL profile. * @card_type: Various ASICs have several card types. This indicates the card * type of the current device. * @major: habanalabs kernel driver major. @@ -2604,7 +2601,6 @@ struct hl_device { struct cdev cdev_ctrl; struct device *dev; struct device *dev_ctrl; - struct delayed_work work_freq; struct delayed_work work_heartbeat; struct hl_device_reset_work device_reset_work; char asic_name[HL_STR_MAX]; @@ -2635,7 +2631,6 @@ struct hl_device { void *asic_specific; struct hl_vm vm; struct device *hwmon_dev; - enum hl_pm_mng_profile pm_mng_profile; struct hwmon_chip_info *hl_chip_info; struct hl_dbg_device_entry hl_debugfs; @@ -2682,7 +2677,6 @@ struct hl_device { u64 fw_poll_interval_usec; atomic_t in_reset; ktime_t last_successful_open_ktime; - enum hl_pll_frequency curr_pll_profile; enum cpucp_card_types card_type; u32 major; u32 high_pll; @@ -2912,7 +2906,6 @@ int hl_device_resume(struct hl_device *hdev); int hl_device_reset(struct hl_device *hdev, u32 flags); void hl_hpriv_get(struct hl_fpriv *hpriv); int hl_hpriv_put(struct hl_fpriv *hpriv); -int hl_device_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq); int hl_device_utilization(struct hl_device *hdev, u32 *utilization); int hl_build_hwmon_channel_info(struct hl_device *hdev, diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 1070c80d739c..d4ef99952d15 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -175,13 +175,6 @@ int hl_device_open(struct inode *inode, struct file *filp) goto out_err; } - /* Device is IDLE at this point so it is legal to change PLLs. - * There is no need to check anything because if the PLL is - * already HIGH, the set function will return without doing - * anything - */ - hl_device_set_frequency(hdev, PLL_HIGH); - list_add(&hpriv->dev_node, &hdev->fpriv_list); mutex_unlock(&hdev->fpriv_list_lock); diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index aee0cc4d6155..15e4ae65e515 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -449,11 +449,6 @@ int hl_sysfs_init(struct hl_device *hdev) { int rc; - if (hdev->asic_type == ASIC_GOYA) - hdev->pm_mng_profile = PM_AUTO; - else - hdev->pm_mng_profile = PM_MANUAL; - hdev->max_power = hdev->asic_prop.max_power_default; hdev->asic_funcs->add_device_attr(hdev, &hl_dev_clks_attr_group); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index b101a46076b8..f29afcca74fc 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -1636,6 +1636,8 @@ static int gaudi_late_init(struct hl_device *hdev) */ gaudi_mmu_prepare(hdev, 1); + hdev->asic_funcs->set_pll_profile(hdev, PLL_LAST); + return 0; disable_pci_access: diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 5e6998d21adb..bbee6739ce87 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -787,9 +787,59 @@ static void goya_fetch_psoc_frequency(struct hl_device *hdev) prop->psoc_pci_pll_div_factor = div_fctr; } +/* + * goya_set_frequency - set the frequency of the device + * + * @hdev: pointer to habanalabs device structure + * @freq: the new frequency value + * + * Change the frequency if needed. This function has no protection against + * concurrency, therefore it is assumed that the calling function has protected + * itself against the case of calling this function from multiple threads with + * different values + * + * Returns 0 if no change was done, otherwise returns 1 + */ +int goya_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq) +{ + struct goya_device *goya = hdev->asic_specific; + + if ((goya->pm_mng_profile == PM_MANUAL) || + (goya->curr_pll_profile == freq)) + return 0; + + dev_dbg(hdev->dev, "Changing device frequency to %s\n", + freq == PLL_HIGH ? "high" : "low"); + + goya_set_pll_profile(hdev, freq); + + goya->curr_pll_profile = freq; + + return 1; +} + +static void goya_set_freq_to_low_job(struct work_struct *work) +{ + struct goya_work_freq *goya_work = container_of(work, + struct goya_work_freq, + work_freq.work); + struct hl_device *hdev = goya_work->hdev; + + mutex_lock(&hdev->fpriv_list_lock); + + if (!hdev->compute_ctx) + goya_set_frequency(hdev, PLL_LOW); + + mutex_unlock(&hdev->fpriv_list_lock); + + schedule_delayed_work(&goya_work->work_freq, + usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); +} + int goya_late_init(struct hl_device *hdev) { struct asic_fixed_properties *prop = &hdev->asic_prop; + struct goya_device *goya = hdev->asic_specific; int rc; goya_fetch_psoc_frequency(hdev); @@ -838,6 +888,16 @@ int goya_late_init(struct hl_device *hdev) return rc; } + /* force setting to low frequency */ + goya->curr_pll_profile = PLL_LOW; + + goya->pm_mng_profile = PM_AUTO; + + hdev->asic_funcs->set_pll_profile(hdev, PLL_LOW); + + schedule_delayed_work(&goya->goya_work->work_freq, + usecs_to_jiffies(HL_PLL_LOW_JOB_FREQ_USEC)); + return 0; } @@ -851,8 +911,11 @@ int goya_late_init(struct hl_device *hdev) void goya_late_fini(struct hl_device *hdev) { const struct hwmon_channel_info **channel_info_arr; + struct goya_device *goya = hdev->asic_specific; int i = 0; + cancel_delayed_work_sync(&goya->goya_work->work_freq); + if (!hdev->hl_chip_info->info) return; @@ -976,6 +1039,15 @@ static int goya_sw_init(struct hl_device *hdev) hdev->asic_funcs->set_pci_memory_regions(hdev); + goya->goya_work = kmalloc(sizeof(struct goya_work_freq), GFP_KERNEL); + if (!goya->goya_work) { + rc = -ENOMEM; + goto free_cpu_accessible_dma_pool; + } + + goya->goya_work->hdev = hdev; + INIT_DELAYED_WORK(&goya->goya_work->work_freq, goya_set_freq_to_low_job); + return 0; free_cpu_accessible_dma_pool: @@ -1012,6 +1084,7 @@ static int goya_sw_fini(struct hl_device *hdev) dma_pool_destroy(hdev->dma_pool); + kfree(goya->goya_work); kfree(goya); return 0; diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index 97add7b04f82..f0c3c6df04d5 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -153,9 +153,15 @@ #define HW_CAP_GOLDEN 0x00000400 #define HW_CAP_TPC 0x00000800 +struct goya_work_freq { + struct hl_device *hdev; + struct delayed_work work_freq; +}; + struct goya_device { /* TODO: remove hw_queues_lock after moving to scheduler code */ spinlock_t hw_queues_lock; + struct goya_work_freq *goya_work; u64 mme_clk; u64 tpc_clk; @@ -166,6 +172,9 @@ struct goya_device { u32 events_stat_aggregate[GOYA_ASYNC_EVENT_ID_SIZE]; u32 hw_cap_initialized; u8 device_cpu_mmu_mappings_done; + + enum hl_pll_frequency curr_pll_profile; + enum hl_pm_mng_profile pm_mng_profile; }; int goya_set_fixed_properties(struct hl_device *hdev); @@ -237,5 +246,6 @@ void goya_mmu_remove_device_cpu_mappings(struct hl_device *hdev); u32 goya_get_queue_id_for_cq(struct hl_device *hdev, u32 cq_idx); u64 goya_get_device_time(struct hl_device *hdev); +int goya_set_frequency(struct hl_device *hdev, enum hl_pll_frequency freq); #endif /* GOYAP_H_ */ diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/misc/habanalabs/goya/goya_hwmgr.c index 59b2624ff81a..42985a85b625 100644 --- a/drivers/misc/habanalabs/goya/goya_hwmgr.c +++ b/drivers/misc/habanalabs/goya/goya_hwmgr.c @@ -62,7 +62,7 @@ static ssize_t mme_clk_store(struct device *dev, struct device_attribute *attr, goto fail; } - if (hdev->pm_mng_profile == PM_AUTO) { + if (goya->pm_mng_profile == PM_AUTO) { count = -EPERM; goto fail; } @@ -111,7 +111,7 @@ static ssize_t tpc_clk_store(struct device *dev, struct device_attribute *attr, goto fail; } - if (hdev->pm_mng_profile == PM_AUTO) { + if (goya->pm_mng_profile == PM_AUTO) { count = -EPERM; goto fail; } @@ -160,7 +160,7 @@ static ssize_t ic_clk_store(struct device *dev, struct device_attribute *attr, goto fail; } - if (hdev->pm_mng_profile == PM_AUTO) { + if (goya->pm_mng_profile == PM_AUTO) { count = -EPERM; goto fail; } @@ -234,13 +234,14 @@ static ssize_t pm_mng_profile_show(struct device *dev, struct device_attribute *attr, char *buf) { struct hl_device *hdev = dev_get_drvdata(dev); + struct goya_device *goya = hdev->asic_specific; if (!hl_device_operational(hdev, NULL)) return -ENODEV; return sprintf(buf, "%s\n", - (hdev->pm_mng_profile == PM_AUTO) ? "auto" : - (hdev->pm_mng_profile == PM_MANUAL) ? "manual" : + (goya->pm_mng_profile == PM_AUTO) ? "auto" : + (goya->pm_mng_profile == PM_MANUAL) ? "manual" : "unknown"); } @@ -248,6 +249,7 @@ static ssize_t pm_mng_profile_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct hl_device *hdev = dev_get_drvdata(dev); + struct goya_device *goya = hdev->asic_specific; if (!hl_device_operational(hdev, NULL)) { count = -ENODEV; @@ -265,26 +267,27 @@ static ssize_t pm_mng_profile_store(struct device *dev, if (strncmp("auto", buf, strlen("auto")) == 0) { /* Make sure we are in LOW PLL when changing modes */ - if (hdev->pm_mng_profile == PM_MANUAL) { - hdev->curr_pll_profile = PLL_HIGH; - hdev->pm_mng_profile = PM_AUTO; - hl_device_set_frequency(hdev, PLL_LOW); + if (goya->pm_mng_profile == PM_MANUAL) { + goya->curr_pll_profile = PLL_HIGH; + goya->pm_mng_profile = PM_AUTO; + goya_set_frequency(hdev, PLL_LOW); } } else if (strncmp("manual", buf, strlen("manual")) == 0) { - if (hdev->pm_mng_profile == PM_AUTO) { + if (goya->pm_mng_profile == PM_AUTO) { /* Must release the lock because the work thread also * takes this lock. But before we release it, set * the mode to manual so nothing will change if a user * suddenly opens the device */ - hdev->pm_mng_profile = PM_MANUAL; + goya->pm_mng_profile = PM_MANUAL; mutex_unlock(&hdev->fpriv_list_lock); /* Flush the current work so we can return to the user * knowing that he is the only one changing frequencies */ - flush_delayed_work(&hdev->work_freq); + if (goya->goya_work) + flush_delayed_work(&goya->goya_work->work_freq); return count; } -- cgit v1.2.3 From 60e0431f41fff930537b4292c711200da87b195f Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 16 Nov 2021 09:46:02 +0200 Subject: habanalabs: fix soft reset accounting Reset upon device release is not a soft-reset from user/system point of view. As such, we shouldn't count that reset in the statistics we gather and expose to the monitoring applications. We also shouldn't print soft-reset when doing the reset upon device release. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 50 ++++++++++++++++----------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 484e0446381e..2b208007c26f 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -962,13 +962,13 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) */ int hl_device_reset(struct hl_device *hdev, u32 flags) { - bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false; + bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false, + reset_upon_device_release = false; u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; int i, rc; if (!hdev->init_done) { - dev_err(hdev->dev, - "Can't reset before initialization is done\n"); + dev_err(hdev->dev, "Can't reset before initialization is done\n"); return 0; } @@ -988,6 +988,8 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) return -EINVAL; } + reset_upon_device_release = true; + goto do_reset; } @@ -1024,12 +1026,10 @@ do_reset: if (hard_reset) dev_info(hdev->dev, "Going to reset device\n"); - else if (flags & HL_DRV_RESET_DEV_RELEASE) - dev_info(hdev->dev, - "Going to reset device after it was released by user\n"); + else if (reset_upon_device_release) + dev_info(hdev->dev, "Going to reset device after release by user\n"); else - dev_info(hdev->dev, - "Going to reset compute engines of inference device\n"); + dev_info(hdev->dev, "Going to reset engines of inference device\n"); } again: @@ -1174,16 +1174,14 @@ kill_processes: rc = hdev->asic_funcs->hw_init(hdev); if (rc) { - dev_err(hdev->dev, - "failed to initialize the H/W after reset\n"); + dev_err(hdev->dev, "failed to initialize the H/W after reset\n"); goto out_err; } /* If device is not idle fail the reset process */ if (!hdev->asic_funcs->is_device_idle(hdev, idle_mask, HL_BUSY_ENGINES_MASK_EXT_SIZE, NULL)) { - dev_err(hdev->dev, - "device is not idle (mask 0x%llx_%llx) after reset\n", + dev_err(hdev->dev, "device is not idle (mask 0x%llx_%llx) after reset\n", idle_mask[1], idle_mask[0]); rc = -EIO; goto out_err; @@ -1192,23 +1190,20 @@ kill_processes: /* Check that the communication with the device is working */ rc = hdev->asic_funcs->test_queues(hdev); if (rc) { - dev_err(hdev->dev, - "Failed to detect if device is alive after reset\n"); + dev_err(hdev->dev, "Failed to detect if device is alive after reset\n"); goto out_err; } if (hard_reset) { rc = device_late_init(hdev); if (rc) { - dev_err(hdev->dev, - "Failed late init after hard reset\n"); + dev_err(hdev->dev, "Failed late init after hard reset\n"); goto out_err; } rc = hl_vm_init(hdev); if (rc) { - dev_err(hdev->dev, - "Failed to init memory module after hard reset\n"); + dev_err(hdev->dev, "Failed to init memory module after hard reset\n"); goto out_err; } @@ -1216,8 +1211,11 @@ kill_processes: } else { rc = hdev->asic_funcs->soft_reset_late_init(hdev); if (rc) { - dev_err(hdev->dev, - "Failed late init after soft reset\n"); + if (reset_upon_device_release) + dev_err(hdev->dev, + "Failed late init in reset after device release\n"); + else + dev_err(hdev->dev, "Failed late init after soft reset\n"); goto out_err; } } @@ -1236,7 +1234,7 @@ kill_processes: * the device will be operational although it shouldn't be */ hdev->asic_funcs->enable_events_from_fw(hdev); - } else { + } else if (!reset_upon_device_release) { hdev->soft_reset_cnt++; } @@ -1246,12 +1244,14 @@ out_err: hdev->disabled = true; if (hard_reset) { - dev_err(hdev->dev, - "Failed to reset! Device is NOT usable\n"); + dev_err(hdev->dev, "Failed to reset! Device is NOT usable\n"); hdev->hard_reset_cnt++; + } else if (reset_upon_device_release) { + dev_err(hdev->dev, "Failed to reset device after user release\n"); + hard_reset = true; + goto again; } else { - dev_err(hdev->dev, - "Failed to do soft-reset, trying hard reset\n"); + dev_err(hdev->dev, "Failed to do soft-reset\n"); hdev->soft_reset_cnt++; hard_reset = true; goto again; -- cgit v1.2.3 From 6c1bad35e691d908785e20258027d29c8b8beb08 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 16 Nov 2021 09:59:32 +0200 Subject: habanalabs: rename late init after reset function The ASIC-specific soft_reset_late_init() is now called after either soft-reset or reset-upon-device-release. Therefore, it needs a more appropriate name. No need to split it to two functions, as an ASIC either supports soft-reset or reset-upon-device-release. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 2 +- drivers/misc/habanalabs/common/habanalabs.h | 4 ++-- drivers/misc/habanalabs/gaudi/gaudi.c | 4 ++-- drivers/misc/habanalabs/goya/goya.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 2b208007c26f..822d9cec5aaf 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1209,7 +1209,7 @@ kill_processes: hl_set_max_power(hdev); } else { - rc = hdev->asic_funcs->soft_reset_late_init(hdev); + rc = hdev->asic_funcs->non_hard_reset_late_init(hdev); if (rc) { if (reset_upon_device_release) dev_err(hdev->dev, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 1a7f8d37f684..a465b4a5f31d 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1153,7 +1153,7 @@ struct fw_load_mgr { * @disable_clock_gating: disable clock gating completely * @debug_coresight: perform certain actions on Coresight for debugging. * @is_device_idle: return true if device is idle, false otherwise. - * @soft_reset_late_init: perform certain actions needed after soft reset. + * @non_hard_reset_late_init: perform certain actions needed after a reset which is not hard-reset * @hw_queues_lock: acquire H/W queues lock. * @hw_queues_unlock: release H/W queues lock. * @get_pci_id: retrieve PCI ID. @@ -1289,7 +1289,7 @@ struct hl_asic_funcs { int (*debug_coresight)(struct hl_device *hdev, void *data); bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, struct seq_file *s); - int (*soft_reset_late_init)(struct hl_device *hdev); + int (*non_hard_reset_late_init)(struct hl_device *hdev); void (*hw_queues_lock)(struct hl_device *hdev); void (*hw_queues_unlock)(struct hl_device *hdev); u32 (*get_pci_id)(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index f29afcca74fc..464d205a26ed 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7819,7 +7819,7 @@ static void gaudi_print_fw_alive_info(struct hl_device *hdev, fw_alive->thread_id, fw_alive->uptime_seconds); } -static int gaudi_soft_reset_late_init(struct hl_device *hdev) +static int gaudi_non_hard_reset_late_init(struct hl_device *hdev) { struct gaudi_device *gaudi = hdev->asic_specific; @@ -9591,7 +9591,7 @@ static const struct hl_asic_funcs gaudi_funcs = { .disable_clock_gating = gaudi_disable_clock_gating, .debug_coresight = gaudi_debug_coresight, .is_device_idle = gaudi_is_device_idle, - .soft_reset_late_init = gaudi_soft_reset_late_init, + .non_hard_reset_late_init = gaudi_non_hard_reset_late_init, .hw_queues_lock = gaudi_hw_queues_lock, .hw_queues_unlock = gaudi_hw_queues_unlock, .get_pci_id = gaudi_get_pci_id, diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index bbee6739ce87..e54d60e75854 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -4813,7 +4813,7 @@ static int goya_unmask_irq_arr(struct hl_device *hdev, u32 *irq_arr, return rc; } -static int goya_soft_reset_late_init(struct hl_device *hdev) +static int goya_non_hard_reset_late_init(struct hl_device *hdev) { /* * Unmask all IRQs since some could have been received @@ -5738,7 +5738,7 @@ static const struct hl_asic_funcs goya_funcs = { .disable_clock_gating = goya_disable_clock_gating, .debug_coresight = goya_debug_coresight, .is_device_idle = goya_is_device_idle, - .soft_reset_late_init = goya_soft_reset_late_init, + .non_hard_reset_late_init = goya_non_hard_reset_late_init, .hw_queues_lock = goya_hw_queues_lock, .hw_queues_unlock = goya_hw_queues_unlock, .get_pci_id = goya_get_pci_id, -- cgit v1.2.3 From 9eade72e7246a25b8a13678d52a947033d6de710 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 16 Nov 2021 10:30:26 +0200 Subject: habanalabs/gaudi: return EPERM on non hard-reset GAUDI supports only hard-reset. Therefore, this function should return an error of operation not permitted. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/gaudi/gaudi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 464d205a26ed..07e03d44930e 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -7821,12 +7821,8 @@ static void gaudi_print_fw_alive_info(struct hl_device *hdev, static int gaudi_non_hard_reset_late_init(struct hl_device *hdev) { - struct gaudi_device *gaudi = hdev->asic_specific; - - /* Unmask all IRQs since some could have been received - * during the soft reset - */ - return hl_fw_unmask_irq_arr(hdev, gaudi->events, sizeof(gaudi->events)); + /* GAUDI doesn't support any reset except hard-reset */ + return -EPERM; } static int gaudi_hbm_read_interrupts(struct hl_device *hdev, int device, -- cgit v1.2.3 From cad9eb4a8d9f745c2548f905534f981758e2afec Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 16 Nov 2021 15:48:42 +0200 Subject: habanalabs: move device boot warnings to the correct location As device boot warnings clears the indication from the error mask, they must be located together before the unknown error validation. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 45 ++++++++++++++-------------- 1 file changed, 23 insertions(+), 22 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index aea5904332fd..cf67800f2b47 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -443,15 +443,6 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, err_exists = true; } - if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) { - dev_warn(hdev->dev, - "Device boot warning - Skipped DRAM initialization\n"); - /* This is a warning so we don't want it to disable the - * device - */ - err_val &= ~CPU_BOOT_ERR0_DRAM_SKIPPED; - } - if (err_val & CPU_BOOT_ERR0_BMC_WAIT_SKIPPED) { if (hdev->bmc_enable) { dev_err(hdev->dev, @@ -495,15 +486,6 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, err_exists = true; } - if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL) { - dev_warn(hdev->dev, - "Device boot warning - Failed to load preboot primary image\n"); - /* This is a warning so we don't want it to disable the - * device as we have a secondary preboot image - */ - err_val &= ~CPU_BOOT_ERR0_PRI_IMG_VER_FAIL; - } - if (err_val & CPU_BOOT_ERR0_SEC_IMG_VER_FAIL) { dev_err(hdev->dev, "Device boot error - Failed to load preboot secondary image\n"); err_exists = true; @@ -523,10 +505,23 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, if (sts_val & CPU_BOOT_DEV_STS0_ENABLED) dev_dbg(hdev->dev, "Device status0 %#x\n", sts_val); - if (!err_exists && (err_val & ~CPU_BOOT_ERR0_ENABLED)) { - dev_err(hdev->dev, - "Device boot error - unknown ERR0 error 0x%08x\n", err_val); - err_exists = true; + /* All warnings should go here in order not to reach the unknown error validation */ + if (err_val & CPU_BOOT_ERR0_DRAM_SKIPPED) { + dev_warn(hdev->dev, + "Device boot warning - Skipped DRAM initialization\n"); + /* This is a warning so we don't want it to disable the + * device + */ + err_val &= ~CPU_BOOT_ERR0_DRAM_SKIPPED; + } + + if (err_val & CPU_BOOT_ERR0_PRI_IMG_VER_FAIL) { + dev_warn(hdev->dev, + "Device boot warning - Failed to load preboot primary image\n"); + /* This is a warning so we don't want it to disable the + * device as we have a secondary preboot image + */ + err_val &= ~CPU_BOOT_ERR0_PRI_IMG_VER_FAIL; } if (err_val & CPU_BOOT_ERR0_TPM_FAIL) { @@ -538,6 +533,12 @@ static bool fw_report_boot_dev0(struct hl_device *hdev, u32 err_val, err_val &= ~CPU_BOOT_ERR0_TPM_FAIL; } + if (!err_exists && (err_val & ~CPU_BOOT_ERR0_ENABLED)) { + dev_err(hdev->dev, + "Device boot error - unknown ERR0 error 0x%08x\n", err_val); + err_exists = true; + } + /* return error only if it's in the predefined mask */ if (err_exists && ((err_val & ~CPU_BOOT_ERR0_ENABLED) & lower_32_bits(hdev->boot_error_status_mask))) -- cgit v1.2.3 From 3beaf903a3a07dc5c6500691b0b465d36292e3f8 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Wed, 17 Nov 2021 09:59:10 +0200 Subject: habanalabs: fix race condition in multi CS completion Race example scenario: 1. User have 2 threads that waits on multi CS: - thread_0 waits on QID 0 and uses multi CS context 0. - thread_1 waits on QID 1 and uses multi CS context 1. 2. thread_1 got completion and release multi CS context 1. 3. CS related to multi CS of thread_0 starts executing complete_multi_cs function, the first iteration of the loop completes the multi CS of thread_0, hence multi CS context 0 is released. 4. thread_1 waits on QID 1 and uses multi CS context 0. 5. thread_0 waits on QID 0 and uses multi CS context 1. 6. The second iterattion of the loop (from step 3) starts, which means, start checking multi CS context 1: - multi CS contetxt is being used by thread_0 waiting on QID 0. - The fence of the CS (still CS from step 3) has QID map the same as the multi CS context 1. - multi CS context 1 (thread_0) gets completion on CS that triggered already thread_0 (with multi CS context 0) and is no longer being waited on. Fixed by exiting the loop in complete_multi_cs after getting completion Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index c1fd4ba14c60..4e893364a3cc 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -545,6 +545,13 @@ static void complete_multi_cs(struct hl_device *hdev, struct hl_cs *cs) * mcs fences. */ fence->mcs_handling_done = true; + /* + * Since CS (and its related fence) can be associated with only one + * multi CS context, once it triggered multi CS completion no need to + * continue checking other multi CS contexts. + */ + spin_unlock(&mcs_compl->lock); + break; } spin_unlock(&mcs_compl->lock); -- cgit v1.2.3 From 411943344599d1a3340b4f720157cd24f4768c92 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 15 Nov 2021 19:36:25 +0200 Subject: habanalabs: add more info ioctls support during reset Some info ioctls can be served even if the device is disabled or in reset. Hence, we enable more info ioctls during reset, as these ioctls do not require any H/W nor F/W communication. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 55 +++++++++++------------ 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 360a1e9bbd5d..15797d55b4e8 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -614,6 +614,33 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_RESET_COUNT: return get_reset_count(hdev, args); + case HL_INFO_HW_EVENTS: + return hw_events_info(hdev, false, args); + + case HL_INFO_HW_EVENTS_AGGREGATE: + return hw_events_info(hdev, true, args); + + case HL_INFO_CS_COUNTERS: + return cs_counters_info(hpriv, args); + + case HL_INFO_CLK_THROTTLE_REASON: + return clk_throttle_info(hpriv, args); + + case HL_INFO_SYNC_MANAGER: + return sync_manager_info(hpriv, args); + + case HL_INFO_OPEN_STATS: + return open_stats_info(hpriv, args); + + case HL_INFO_LAST_ERR_OPEN_DEV_TIME: + return last_err_open_dev_info(hpriv, args); + + case HL_INFO_CS_TIMEOUT_EVENT: + return cs_timeout_info(hpriv, args); + + case HL_INFO_RAZWI_EVENT: + return razwi_info(hpriv, args); + default: break; } @@ -626,10 +653,6 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, } switch (args->op) { - case HL_INFO_HW_EVENTS: - rc = hw_events_info(hdev, false, args); - break; - case HL_INFO_DRAM_USAGE: rc = dram_usage_info(hpriv, args); break; @@ -642,10 +665,6 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, rc = device_utilization(hdev, args); break; - case HL_INFO_HW_EVENTS_AGGREGATE: - rc = hw_events_info(hdev, true, args); - break; - case HL_INFO_CLK_RATE: rc = get_clk_rate(hdev, args); break; @@ -653,18 +672,9 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_TIME_SYNC: return time_sync_info(hdev, args); - case HL_INFO_CS_COUNTERS: - return cs_counters_info(hpriv, args); - case HL_INFO_PCI_COUNTERS: return pci_counters_info(hpriv, args); - case HL_INFO_CLK_THROTTLE_REASON: - return clk_throttle_info(hpriv, args); - - case HL_INFO_SYNC_MANAGER: - return sync_manager_info(hpriv, args); - case HL_INFO_TOTAL_ENERGY: return total_energy_consumption_info(hpriv, args); @@ -674,8 +684,6 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_POWER: return power_info(hpriv, args); - case HL_INFO_OPEN_STATS: - return open_stats_info(hpriv, args); case HL_INFO_DRAM_REPLACED_ROWS: return dram_replaced_rows_info(hpriv, args); @@ -683,15 +691,6 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, case HL_INFO_DRAM_PENDING_ROWS: return dram_pending_rows_info(hpriv, args); - case HL_INFO_LAST_ERR_OPEN_DEV_TIME: - return last_err_open_dev_info(hpriv, args); - - case HL_INFO_CS_TIMEOUT_EVENT: - return cs_timeout_info(hpriv, args); - - case HL_INFO_RAZWI_EVENT: - return razwi_info(hpriv, args); - default: dev_err(dev, "Invalid request %d\n", args->op); rc = -ENOTTY; -- cgit v1.2.3 From 75a5c44d143bc1818e8004a8bee6993aba3a75cf Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 18 Nov 2021 10:44:05 +0200 Subject: habanalabs: add power information type to POWER_GET packet In new f/w versions, it is required to explicitly indicate the power information type when querying the F/W for power info. When getting the current power level it should be set to power_input. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 1 + include/uapi/misc/habanalabs.h | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index cf67800f2b47..ac5bd017d294 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -969,6 +969,7 @@ int hl_fw_cpucp_power_get(struct hl_device *hdev, u64 *power) pkt.ctl = cpu_to_le32(CPUCP_PACKET_POWER_GET << CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.type = cpu_to_le16(CPUCP_POWER_INPUT); rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), HL_CPUCP_INFO_TIMEOUT_USEC, &result); diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index eb8565fdae70..cd86937c572d 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -333,6 +333,7 @@ enum hl_server_type { * HL_INFO_SYNC_MANAGER - Retrieve sync manager info per dcore * HL_INFO_TOTAL_ENERGY - Retrieve total energy consumption * HL_INFO_PLL_FREQUENCY - Retrieve PLL frequency + * HL_INFO_POWER - Retrieve power information * HL_INFO_OPEN_STATS - Retrieve info regarding recent device open calls * HL_INFO_DRAM_REPLACED_ROWS - Retrieve DRAM replaced rows info * HL_INFO_DRAM_PENDING_ROWS - Retrieve DRAM pending rows num -- cgit v1.2.3 From b13bef204158e0c9d8a9149d134b260cec7ff6a9 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Thu, 18 Nov 2021 08:46:15 +0200 Subject: habanalabs: change misleading IRQ warning during reset Currently we dump the physical IRQ line index in host if an event is received during reset. This ID is confusing as it means nothing to the user. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/irq.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 96d82b682674..9fd4c18e274e 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -246,9 +246,7 @@ irqreturn_t hl_irq_handler_eq(int irq, void *arg) dma_rmb(); if (hdev->disabled) { - dev_warn(hdev->dev, - "Device disabled but received IRQ %d for EQ\n", - irq); + dev_warn(hdev->dev, "Device disabled but received an EQ event\n"); goto skip_irq; } -- cgit v1.2.3 From 3416d4b59b8fbf0ad360353da4fa0f7293831230 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Sun, 21 Nov 2021 16:02:32 +0200 Subject: habanalabs: handle events during soft-reset Driver should handle events during soft-reset as F/W is not going through reset and it keeps sending events towards host. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 4 ++++ drivers/misc/habanalabs/common/habanalabs.h | 2 ++ drivers/misc/habanalabs/common/irq.c | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 822d9cec5aaf..720eea0b7e9c 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -1019,6 +1019,8 @@ do_reset: handle_reset_trigger(hdev, flags); + hdev->is_in_soft_reset = !hard_reset; + /* This also blocks future CS/VM/JOB completion operations */ hdev->disabled = true; @@ -1171,6 +1173,7 @@ kill_processes: * is required for the initialization itself */ hdev->disabled = false; + hdev->is_in_soft_reset = false; rc = hdev->asic_funcs->hw_init(hdev); if (rc) { @@ -1242,6 +1245,7 @@ kill_processes: out_err: hdev->disabled = true; + hdev->is_in_soft_reset = false; if (hard_reset) { dev_err(hdev->dev, "Failed to reset! Device is NOT usable\n"); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index a465b4a5f31d..c2129c9fe9e4 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2591,6 +2591,7 @@ struct last_error_session_info { * protocol will throw an error. Relevant only for * cases where Linux was not loaded to device CPU * @supports_wait_for_multi_cs: true if wait for multi CS is supported + * @is_in_soft_reset: Device is currently in soft reset process. */ struct hl_device { struct pci_dev *pdev; @@ -2719,6 +2720,7 @@ struct hl_device { u8 device_cpu_is_halted; u8 supports_wait_for_multi_cs; u8 stream_master_qid_arr_size; + u8 is_in_soft_reset; /* Parameters for bring-up */ u64 nic_ports_mask; diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 9fd4c18e274e..64e0d9de21bd 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -245,7 +245,7 @@ irqreturn_t hl_irq_handler_eq(int irq, void *arg) */ dma_rmb(); - if (hdev->disabled) { + if (hdev->disabled && !hdev->is_in_soft_reset) { dev_warn(hdev->dev, "Device disabled but received an EQ event\n"); goto skip_irq; } -- cgit v1.2.3 From 4fac990f604e6c10538026835a8a30f3c1b6fcf5 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Mon, 22 Nov 2021 12:23:51 +0200 Subject: habanalabs: skip read fw errors if dynamic descriptor invalid Reporting FW errors involves reading of the error registers. In case we have a corrupted FW descriptor we cannot do that since the dynamic scratchpad is potentially corrupted as well and may cause kernel crush when attempting access to a corrupted register offset. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 17 +++++++++++++++-- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index ac5bd017d294..76741898d922 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1772,6 +1772,9 @@ static int hl_fw_dynamic_validate_descriptor(struct hl_device *hdev, return rc; } + /* here we can mark the descriptor as valid as the content has been validated */ + fw_loader->dynamic_loader.fw_desc_valid = true; + return 0; } @@ -1828,7 +1831,13 @@ static int hl_fw_dynamic_read_and_validate_descriptor(struct hl_device *hdev, return rc; } - /* extract address copy the descriptor from */ + /* + * extract address to copy the descriptor from + * in addition, as the descriptor value is going to be over-ridden by new data- we mark it + * as invalid. + * it will be marked again as valid once validated + */ + fw_loader->dynamic_loader.fw_desc_valid = false; src = hdev->pcie_bar[region->bar_id] + region->offset_in_bar + response->ram_offset; memcpy_fromio(fw_desc, src, sizeof(struct lkd_fw_comms_desc)); @@ -2317,6 +2326,9 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, dev_info(hdev->dev, "Loading firmware to device, may take some time...\n"); + /* initialize FW descriptor as invalid */ + fw_loader->dynamic_loader.fw_desc_valid = false; + /* * In this stage, "cpu_dyn_regs" contains only LKD's hard coded values! * It will be updated from FW after hl_fw_dynamic_request_descriptor(). @@ -2412,7 +2424,8 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, return 0; protocol_err: - fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0), + if (fw_loader->dynamic_loader.fw_desc_valid) + fw_read_errors(hdev, le32_to_cpu(dyn_regs->cpu_boot_err0), le32_to_cpu(dyn_regs->cpu_boot_err1), le32_to_cpu(dyn_regs->cpu_boot_dev_sts0), le32_to_cpu(dyn_regs->cpu_boot_dev_sts1)); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index c2129c9fe9e4..77ac4bb98137 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1034,6 +1034,7 @@ struct fw_response { * @image_region: region to copy the FW image to * @fw_image_size: size of FW image to load * @wait_for_bl_timeout: timeout for waiting for boot loader to respond + * @fw_desc_valid: true if FW descriptor has been validated and hence the data can be used */ struct dynamic_fw_load_mgr { struct fw_response response; @@ -1041,6 +1042,7 @@ struct dynamic_fw_load_mgr { struct pci_mem_region *image_region; size_t fw_image_size; u32 wait_for_bl_timeout; + bool fw_desc_valid; }; /** -- cgit v1.2.3 From 1880f7acd7e0edacbd46385036253801ddc4273f Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Tue, 9 Nov 2021 11:33:28 +0200 Subject: habanalabs: add SOB information to signal submission uAPI For debug purpose, add SOB address and SOB initial counter value before current submission to uAPI output. Using SOB address and initial counter, user can calculate how much of the submmision has been completed. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 37 ++++++++++++++++++---- drivers/misc/habanalabs/common/habanalabs.h | 5 +++ drivers/misc/habanalabs/common/hw_queue.c | 3 ++ include/uapi/misc/habanalabs.h | 10 +++++- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 4e893364a3cc..7a277f442207 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -1277,7 +1277,8 @@ static u32 get_stream_master_qid_mask(struct hl_device *hdev, u32 qid) static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, u32 num_chunks, u64 *cs_seq, u32 flags, - u32 encaps_signals_handle, u32 timeout) + u32 encaps_signals_handle, u32 timeout, + u16 *signal_initial_sob_count) { bool staged_mid, int_queues_only = true; struct hl_device *hdev = hpriv->hdev; @@ -1444,6 +1445,8 @@ static int cs_ioctl_default(struct hl_fpriv *hpriv, void __user *chunks, goto free_cs_object; } + *signal_initial_sob_count = cs->initial_sob_count; + rc = HL_CS_STATUS_SUCCESS; goto put_cs; @@ -1472,6 +1475,7 @@ static int hl_cs_ctx_switch(struct hl_fpriv *hpriv, union hl_cs_args *args, int rc = 0, do_ctx_switch; void __user *chunks; u32 num_chunks, tmp; + u16 sob_count; int ret; do_ctx_switch = atomic_cmpxchg(&ctx->thread_ctx_switch_token, 1, 0); @@ -1512,7 +1516,7 @@ static int hl_cs_ctx_switch(struct hl_fpriv *hpriv, union hl_cs_args *args, rc = 0; } else { rc = cs_ioctl_default(hpriv, chunks, num_chunks, - cs_seq, 0, 0, hdev->timeout_jiffies); + cs_seq, 0, 0, hdev->timeout_jiffies, &sob_count); } mutex_unlock(&hpriv->restore_phase_mutex); @@ -1963,7 +1967,8 @@ out: static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, void __user *chunks, u32 num_chunks, - u64 *cs_seq, u32 flags, u32 timeout) + u64 *cs_seq, u32 flags, u32 timeout, + u32 *signal_sob_addr_offset, u16 *signal_initial_sob_count) { struct hl_cs_encaps_sig_handle *encaps_sig_hdl = NULL; bool handle_found = false, is_wait_cs = false, @@ -2195,6 +2200,9 @@ static int cs_ioctl_signal_wait(struct hl_fpriv *hpriv, enum hl_cs_type cs_type, goto free_cs_object; } + *signal_sob_addr_offset = cs->sob_addr_offset; + *signal_initial_sob_count = cs->initial_sob_count; + rc = HL_CS_STATUS_SUCCESS; if (is_wait_cs) wait_cs_submitted = true; @@ -2225,6 +2233,7 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) void __user *chunks; u32 num_chunks, flags, timeout, signals_count = 0, sob_addr = 0, handle_id = 0; + u16 sob_initial_count = 0; int rc; rc = hl_cs_sanity_checks(hpriv, args); @@ -2255,7 +2264,8 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) case CS_TYPE_WAIT: case CS_TYPE_COLLECTIVE_WAIT: rc = cs_ioctl_signal_wait(hpriv, cs_type, chunks, num_chunks, - &cs_seq, args->in.cs_flags, timeout); + &cs_seq, args->in.cs_flags, timeout, + &sob_addr, &sob_initial_count); break; case CS_RESERVE_SIGNALS: rc = cs_ioctl_reserve_signals(hpriv, @@ -2271,20 +2281,33 @@ int hl_cs_ioctl(struct hl_fpriv *hpriv, void *data) rc = cs_ioctl_default(hpriv, chunks, num_chunks, &cs_seq, args->in.cs_flags, args->in.encaps_sig_handle_id, - timeout); + timeout, &sob_initial_count); break; } out: if (rc != -EAGAIN) { memset(args, 0, sizeof(*args)); - if (cs_type == CS_RESERVE_SIGNALS) { + switch (cs_type) { + case CS_RESERVE_SIGNALS: args->out.handle_id = handle_id; args->out.sob_base_addr_offset = sob_addr; args->out.count = signals_count; - } else { + break; + case CS_TYPE_SIGNAL: + args->out.sob_base_addr_offset = sob_addr; + args->out.sob_count_before_submission = sob_initial_count; + args->out.seq = cs_seq; + break; + case CS_TYPE_DEFAULT: + args->out.sob_count_before_submission = sob_initial_count; + args->out.seq = cs_seq; + break; + default: args->out.seq = cs_seq; + break; } + args->out.status = rc; } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 77ac4bb98137..93d0a85265be 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1545,6 +1545,9 @@ struct hl_userptr { * @submission_time_jiffies: submission time of the cs * @type: CS_TYPE_*. * @encaps_sig_hdl_id: encaps signals handle id, set for the first staged cs. + * @sob_addr_offset: sob offset from the configuration base address. + * @initial_sob_count: count of completed signals in SOB before current submission of signal or + * cs with encaps signals. * @submitted: true if CS was submitted to H/W. * @completed: true if CS was completed by device. * @timedout : true if CS was timedout. @@ -1580,6 +1583,8 @@ struct hl_cs { u64 submission_time_jiffies; enum hl_cs_type type; u32 encaps_sig_hdl_id; + u32 sob_addr_offset; + u16 initial_sob_count; u8 submitted; u8 completed; u8 timedout; diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/misc/habanalabs/common/hw_queue.c index 0743319b10c7..fc841d651210 100644 --- a/drivers/misc/habanalabs/common/hw_queue.c +++ b/drivers/misc/habanalabs/common/hw_queue.c @@ -429,6 +429,9 @@ static int init_signal_cs(struct hl_device *hdev, rc = hl_cs_signal_sob_wraparound_handler(hdev, q_idx, &hw_sob, 1, false); + job->cs->sob_addr_offset = hw_sob->sob_addr; + job->cs->initial_sob_count = prop->next_sob_val - 1; + return rc; } diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index cd86937c572d..648850b954a3 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -929,9 +929,17 @@ struct hl_cs_out { /* * SOB base address offset - * Valid only when HL_CS_FLAGS_RESERVE_SIGNALS_ONLY is set + * Valid only when HL_CS_FLAGS_RESERVE_SIGNALS_ONLY or HL_CS_FLAGS_SIGNAL is set */ __u32 sob_base_addr_offset; + + /* + * Count of completed signals in SOB before current signal submission. + * Valid only when (HL_CS_FLAGS_ENCAP_SIGNALS & HL_CS_FLAGS_STAGED_SUBMISSION) + * or HL_CS_FLAGS_SIGNAL is set + */ + __u16 sob_count_before_submission; + __u16 pad[3]; }; union hl_cs_args { -- cgit v1.2.3 From 2487f4a2812e520cb5b77b2b5dfcdc05c215cd83 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Mon, 22 Nov 2021 21:47:30 +0200 Subject: habanalabs: enable access to info ioctl during hard reset Because info ioctl is used to retrieve data, some of its opcodes may be used during hard reset. Other ioctls should be blocked while device is not operational. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 5 +---- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 7 ------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 7a277f442207..8be547b0926c 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -1146,9 +1146,6 @@ static int hl_cs_sanity_checks(struct hl_fpriv *hpriv, union hl_cs_args *args) enum hl_cs_type cs_type; if (!hl_device_operational(hdev, &status)) { - dev_warn_ratelimited(hdev->dev, - "Device is %s. Can't submit new CS\n", - hdev->status[status]); return -EBUSY; } @@ -2997,7 +2994,7 @@ int hl_wait_ioctl(struct hl_fpriv *hpriv, void *data) * user interrupt */ if (!hl_device_operational(hpriv->hdev, NULL)) - return -EPERM; + return -EBUSY; if (flags & HL_WAIT_CS_FLAGS_INTERRUPT) rc = hl_interrupt_wait_ioctl(hpriv, data); diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 15797d55b4e8..6c7339978bae 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -774,7 +774,6 @@ static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg, const struct hl_ioctl_desc *ioctl, struct device *dev) { struct hl_fpriv *hpriv = filep->private_data; - struct hl_device *hdev = hpriv->hdev; unsigned int nr = _IOC_NR(cmd); char stack_kdata[128] = {0}; char *kdata = NULL; @@ -783,12 +782,6 @@ static long _hl_ioctl(struct file *filep, unsigned int cmd, unsigned long arg, u32 hl_size; int retcode; - if (hdev->hard_reset_pending) { - dev_crit_ratelimited(dev, - "Device HARD reset pending! Please close FD\n"); - return -ENODEV; - } - /* Do not trust userspace, use our own definition */ func = ioctl->func; -- cgit v1.2.3 From d214636be8a6102d726c8aeb59000f2fb80d94a9 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Mon, 22 Nov 2021 12:29:22 +0200 Subject: habanalabs: pass reset flags to reset thread The reset flags used by the reset thread are currently a mix of hard-coded values and a specific flag which is passed from the context that initiates the reset. To make it easier to pass more flags in future from this context to the reset thread, modify it to pass all the original reset flags to the thread. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 10 +++------- drivers/misc/habanalabs/common/habanalabs.h | 4 ++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 720eea0b7e9c..db4168f35c18 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -324,16 +324,12 @@ put_devices: static void device_hard_reset_pending(struct work_struct *work) { struct hl_device_reset_work *device_reset_work = - container_of(work, struct hl_device_reset_work, - reset_work.work); + container_of(work, struct hl_device_reset_work, reset_work.work); struct hl_device *hdev = device_reset_work->hdev; u32 flags; int rc; - flags = HL_DRV_RESET_HARD | HL_DRV_RESET_FROM_RESET_THR; - - if (device_reset_work->fw_reset) - flags |= HL_DRV_RESET_BYPASS_REQ_TO_FW; + flags = device_reset_work->flags | HL_DRV_RESET_FROM_RESET_THR; rc = hl_device_reset(hdev, flags); if ((rc == -EBUSY) && !hdev->device_fini_pending) { @@ -1040,7 +1036,7 @@ again: hdev->process_kill_trial_cnt = 0; - hdev->device_reset_work.fw_reset = fw_reset; + hdev->device_reset_work.flags = flags; /* * Because the reset function can't run from heartbeat work, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 93d0a85265be..722fc8e69fd6 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2214,13 +2214,13 @@ struct hwmon_chip_info; * @wq: work queue for device reset procedure. * @reset_work: reset work to be done. * @hdev: habanalabs device structure. - * @fw_reset: whether f/w will do the reset without us sending them a message to do it. + * @flags: reset flags. */ struct hl_device_reset_work { struct workqueue_struct *wq; struct delayed_work reset_work; struct hl_device *hdev; - bool fw_reset; + u32 flags; }; /** -- cgit v1.2.3 From b166465452ac27415bc747c4c47c96d1314d06f1 Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Mon, 29 Nov 2021 11:20:27 +0200 Subject: habanalabs: add missing kernel-doc comments for hl_device fields Add missing kernel-doc comments for the "last_error" and "stream_master_qid_arr" fields of the "hl_device" structure". Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 722fc8e69fd6..57bc55c2ddac 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2512,6 +2512,8 @@ struct last_error_session_info { * @state_dump_specs: constants and dictionaries needed to dump system state. * @multi_cs_completion: array of multi-CS completion. * @clk_throttling: holds information about current/previous clock throttling events + * @last_error: holds information about last session in which CS timeout or razwi error occurred. + * @stream_master_qid_arr: pointer to array with QIDs of master streams. * @dram_used_mem: current DRAM memory consumption. * @timeout_jiffies: device CS timeout value. * @max_power: the max power of the device, as configured by the sysadmin. This -- cgit v1.2.3 From fee187fe460b6b72a62e7d7b7193f8d675752544 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 30 Nov 2021 14:54:53 +0200 Subject: habanalabs: free signal handle on failure Fix a bug where in case of failure to allocate idr, the handle's memory wasn't freed as part of the error handling code. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 8be547b0926c..d169418197c0 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -1838,7 +1838,7 @@ static int cs_ioctl_reserve_signals(struct hl_fpriv *hpriv, if (hdl_id < 0) { dev_err(hdev->dev, "Failed to allocate IDR for a new signal reservation\n"); rc = -EINVAL; - goto out; + goto free_handle; } handle->id = hdl_id; @@ -1891,7 +1891,9 @@ remove_idr: idr_remove(&mgr->handles, hdl_id); spin_unlock(&mgr->lock); +free_handle: kfree(handle); + out: return rc; } -- cgit v1.2.3 From a4dd2ecf36c4458db14df3aae81ec3e3f4b4688e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 30 Nov 2021 17:04:13 +0200 Subject: habanalabs: remove redundant check on ctx_fini The driver supports only a single context. Therefore, no need to check if the user context that is closed is the compute context. The user context, if exists, is always the compute context. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index d0aaccd4df2c..4f7d39a29a42 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -97,10 +97,8 @@ static void hl_ctx_fini(struct hl_ctx *ctx) /* The engines are stopped as there is no executing CS, but the * Coresight might be still working by accessing addresses * related to the stopped engines. Hence stop it explicitly. - * Stop only if this is the compute context, as there can be - * only one compute context */ - if ((hdev->in_debug) && (hdev->compute_ctx == ctx)) + if (hdev->in_debug) hl_device_set_debug_mode(hdev, false); hdev->asic_funcs->ctx_fini(ctx); -- cgit v1.2.3 From 357ff3dc9ae5dc1a0d990801b32431f5eecc7ee9 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 30 Nov 2021 15:28:23 +0200 Subject: habanalabs: save ctx inside encaps signal Compute context pointer in hdev shouldn't be used for fetching the context's pointer. If an object needs the context's pointer, it should get it while incrementing its kref, and when the object is released, put it. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 11 ++++++++--- drivers/misc/habanalabs/common/context.c | 10 +++++----- drivers/misc/habanalabs/common/habanalabs.h | 2 ++ drivers/misc/habanalabs/common/hw_queue.c | 2 +- 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index d169418197c0..a63ebbc04787 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -1829,6 +1829,9 @@ static int cs_ioctl_reserve_signals(struct hl_fpriv *hpriv, } handle->count = count; + + hl_ctx_get(hdev, hpriv->ctx); + handle->ctx = hpriv->ctx; mgr = &hpriv->ctx->sig_mgr; spin_lock(&mgr->lock); @@ -1838,7 +1841,7 @@ static int cs_ioctl_reserve_signals(struct hl_fpriv *hpriv, if (hdl_id < 0) { dev_err(hdev->dev, "Failed to allocate IDR for a new signal reservation\n"); rc = -EINVAL; - goto free_handle; + goto put_ctx; } handle->id = hdl_id; @@ -1891,7 +1894,8 @@ remove_idr: idr_remove(&mgr->handles, hdl_id); spin_unlock(&mgr->lock); -free_handle: +put_ctx: + hl_ctx_put(handle->ctx); kfree(handle); out: @@ -1953,6 +1957,7 @@ static int cs_ioctl_unreserve_signals(struct hl_fpriv *hpriv, u32 handle_id) /* Release the id and free allocated memory of the handle */ idr_remove(&mgr->handles, handle_id); + hl_ctx_put(encaps_sig_hdl->ctx); kfree(encaps_sig_hdl); } else { rc = -EINVAL; diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 4f7d39a29a42..8291151948ef 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -13,13 +13,13 @@ void hl_encaps_handle_do_release(struct kref *ref) { struct hl_cs_encaps_sig_handle *handle = container_of(ref, struct hl_cs_encaps_sig_handle, refcount); - struct hl_ctx *ctx = handle->hdev->compute_ctx; - struct hl_encaps_signals_mgr *mgr = &ctx->sig_mgr; + struct hl_encaps_signals_mgr *mgr = &handle->ctx->sig_mgr; spin_lock(&mgr->lock); idr_remove(&mgr->handles, handle->id); spin_unlock(&mgr->lock); + hl_ctx_put(handle->ctx); kfree(handle); } @@ -27,8 +27,7 @@ static void hl_encaps_handle_do_release_sob(struct kref *ref) { struct hl_cs_encaps_sig_handle *handle = container_of(ref, struct hl_cs_encaps_sig_handle, refcount); - struct hl_ctx *ctx = handle->hdev->compute_ctx; - struct hl_encaps_signals_mgr *mgr = &ctx->sig_mgr; + struct hl_encaps_signals_mgr *mgr = &handle->ctx->sig_mgr; /* if we're here, then there was a signals reservation but cs with * encaps signals wasn't submitted, so need to put refcount @@ -40,6 +39,7 @@ static void hl_encaps_handle_do_release_sob(struct kref *ref) idr_remove(&mgr->handles, handle->id); spin_unlock(&mgr->lock); + hl_ctx_put(handle->ctx); kfree(handle); } diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 57bc55c2ddac..0ad08fdc89ea 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2757,6 +2757,7 @@ struct hl_device { * wait cs are used to wait of the reserved encaps signals. * @hdev: pointer to habanalabs device structure. * @hw_sob: pointer to H/W SOB used in the reservation. + * @ctx: pointer to the user's context data structure * @cs_seq: staged cs sequence which contains encapsulated signals * @id: idr handler id to be used to fetch the handler info * @q_idx: stream queue index @@ -2767,6 +2768,7 @@ struct hl_cs_encaps_sig_handle { struct kref refcount; struct hl_device *hdev; struct hl_hw_sob *hw_sob; + struct hl_ctx *ctx; u64 cs_seq; u32 id; u32 q_idx; diff --git a/drivers/misc/habanalabs/common/hw_queue.c b/drivers/misc/habanalabs/common/hw_queue.c index fc841d651210..6103e479e855 100644 --- a/drivers/misc/habanalabs/common/hw_queue.c +++ b/drivers/misc/habanalabs/common/hw_queue.c @@ -574,7 +574,7 @@ static int encaps_sig_first_staged_cs_handler struct hl_encaps_signals_mgr *mgr; int rc = 0; - mgr = &hdev->compute_ctx->sig_mgr; + mgr = &cs->ctx->sig_mgr; spin_lock(&mgr->lock); encaps_sig_hdl = idr_find(&mgr->handles, cs->encaps_sig_hdl_id); -- cgit v1.2.3 From 6798676f7ef5916133e0c915be73b7a3b7e2a312 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 30 Nov 2021 22:32:13 +0200 Subject: habanalabs: fix etr asid configuration Pass the user's context pointer into the etr configuration function to extract its ASID. Using the compute_ctx pointer is an error as it is just an indication of whether a user has opened the compute device. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 2 +- drivers/misc/habanalabs/common/device.c | 4 ++-- drivers/misc/habanalabs/common/habanalabs.h | 6 +++--- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 13 +++++++------ drivers/misc/habanalabs/gaudi/gaudiP.h | 4 ++-- drivers/misc/habanalabs/gaudi/gaudi_coresight.c | 4 ++-- drivers/misc/habanalabs/goya/goyaP.h | 4 ++-- drivers/misc/habanalabs/goya/goya_coresight.c | 4 ++-- 8 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 8291151948ef..8de1217b2ed2 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -99,7 +99,7 @@ static void hl_ctx_fini(struct hl_ctx *ctx) * related to the stopped engines. Hence stop it explicitly. */ if (hdev->in_debug) - hl_device_set_debug_mode(hdev, false); + hl_device_set_debug_mode(hdev, ctx, false); hdev->asic_funcs->ctx_fini(ctx); hl_cb_va_pool_fini(ctx); diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index db4168f35c18..bc5736ae6b70 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -622,7 +622,7 @@ int hl_device_utilization(struct hl_device *hdev, u32 *utilization) return 0; } -int hl_device_set_debug_mode(struct hl_device *hdev, bool enable) +int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool enable) { int rc = 0; @@ -637,7 +637,7 @@ int hl_device_set_debug_mode(struct hl_device *hdev, bool enable) } if (!hdev->hard_reset_pending) - hdev->asic_funcs->halt_coresight(hdev); + hdev->asic_funcs->halt_coresight(hdev, ctx); hdev->in_debug = 0; diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 0ad08fdc89ea..670fad9b4ca0 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1288,7 +1288,7 @@ struct hl_asic_funcs { int (*send_heartbeat)(struct hl_device *hdev); void (*set_clock_gating)(struct hl_device *hdev); void (*disable_clock_gating)(struct hl_device *hdev); - int (*debug_coresight)(struct hl_device *hdev, void *data); + int (*debug_coresight)(struct hl_device *hdev, struct hl_ctx *ctx, void *data); bool (*is_device_idle)(struct hl_device *hdev, u64 *mask_arr, u8 mask_len, struct seq_file *s); int (*non_hard_reset_late_init)(struct hl_device *hdev); @@ -1303,7 +1303,7 @@ struct hl_asic_funcs { int (*init_iatu)(struct hl_device *hdev); u32 (*rreg)(struct hl_device *hdev, u32 reg); void (*wreg)(struct hl_device *hdev, u32 reg, u32 val); - void (*halt_coresight)(struct hl_device *hdev); + void (*halt_coresight)(struct hl_device *hdev, struct hl_ctx *ctx); int (*ctx_init)(struct hl_ctx *ctx); void (*ctx_fini)(struct hl_ctx *ctx); int (*get_clk_rate)(struct hl_device *hdev, u32 *cur_clk, u32 *max_clk); @@ -2867,7 +2867,7 @@ int hl_device_open_ctrl(struct inode *inode, struct file *filp); bool hl_device_operational(struct hl_device *hdev, enum hl_device_status *status); enum hl_device_status hl_device_status(struct hl_device *hdev); -int hl_device_set_debug_mode(struct hl_device *hdev, bool enable); +int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool enable); int hl_hw_queues_create(struct hl_device *hdev); void hl_hw_queues_destroy(struct hl_device *hdev); int hl_hw_queue_send_cb_no_cmpl(struct hl_device *hdev, u32 hw_queue_id, diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 6c7339978bae..9210114beefe 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -158,7 +158,7 @@ static int hw_idle(struct hl_device *hdev, struct hl_info_args *args) min((size_t) max_size, sizeof(hw_idle))) ? -EFAULT : 0; } -static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args) +static int debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, struct hl_debug_args *args) { struct hl_debug_params *params; void *input = NULL, *output = NULL; @@ -200,7 +200,7 @@ static int debug_coresight(struct hl_device *hdev, struct hl_debug_args *args) params->output_size = args->output_size; } - rc = hdev->asic_funcs->debug_coresight(hdev, params); + rc = hdev->asic_funcs->debug_coresight(hdev, ctx, params); if (rc) { dev_err(hdev->dev, "debug coresight operation failed %d\n", rc); @@ -738,13 +738,14 @@ static int hl_debug_ioctl(struct hl_fpriv *hpriv, void *data) "Rejecting debug configuration request because device not in debug mode\n"); return -EFAULT; } - args->input_size = - min(args->input_size, hl_debug_struct_size[args->op]); - rc = debug_coresight(hdev, args); + args->input_size = min(args->input_size, hl_debug_struct_size[args->op]); + rc = debug_coresight(hdev, hpriv->ctx, args); break; + case HL_DEBUG_OP_SET_MODE: - rc = hl_device_set_debug_mode(hdev, (bool) args->enable); + rc = hl_device_set_debug_mode(hdev, hpriv->ctx, (bool) args->enable); break; + default: dev_err(hdev->dev, "Invalid request %d\n", args->op); rc = -ENOTTY; diff --git a/drivers/misc/habanalabs/gaudi/gaudiP.h b/drivers/misc/habanalabs/gaudi/gaudiP.h index f325e36a71e6..8ac16a9b7d15 100644 --- a/drivers/misc/habanalabs/gaudi/gaudiP.h +++ b/drivers/misc/habanalabs/gaudi/gaudiP.h @@ -357,8 +357,8 @@ void gaudi_init_security(struct hl_device *hdev); void gaudi_ack_protection_bits_errors(struct hl_device *hdev); void gaudi_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_attr_grp); -int gaudi_debug_coresight(struct hl_device *hdev, void *data); -void gaudi_halt_coresight(struct hl_device *hdev); +int gaudi_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data); +void gaudi_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx); void gaudi_mmu_prepare_reg(struct hl_device *hdev, u64 reg, u32 asid); #endif /* GAUDIP_H_ */ diff --git a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c index 5349c1be13f9..08108f5fed67 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi_coresight.c +++ b/drivers/misc/habanalabs/gaudi/gaudi_coresight.c @@ -848,7 +848,7 @@ static int gaudi_config_spmu(struct hl_device *hdev, return 0; } -int gaudi_debug_coresight(struct hl_device *hdev, void *data) +int gaudi_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data) { struct hl_debug_params *params = data; int rc = 0; @@ -887,7 +887,7 @@ int gaudi_debug_coresight(struct hl_device *hdev, void *data) return rc; } -void gaudi_halt_coresight(struct hl_device *hdev) +void gaudi_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx) { struct hl_debug_params params = {}; int i, rc; diff --git a/drivers/misc/habanalabs/goya/goyaP.h b/drivers/misc/habanalabs/goya/goyaP.h index f0c3c6df04d5..3740fd25bf84 100644 --- a/drivers/misc/habanalabs/goya/goyaP.h +++ b/drivers/misc/habanalabs/goya/goyaP.h @@ -220,8 +220,8 @@ void goya_set_pll_profile(struct hl_device *hdev, enum hl_pll_frequency freq); void goya_add_device_attr(struct hl_device *hdev, struct attribute_group *dev_attr_grp); int goya_cpucp_info_get(struct hl_device *hdev); -int goya_debug_coresight(struct hl_device *hdev, void *data); -void goya_halt_coresight(struct hl_device *hdev); +int goya_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data); +void goya_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx); int goya_suspend(struct hl_device *hdev); int goya_resume(struct hl_device *hdev); diff --git a/drivers/misc/habanalabs/goya/goya_coresight.c b/drivers/misc/habanalabs/goya/goya_coresight.c index c55c100fdd24..2c5133cfae65 100644 --- a/drivers/misc/habanalabs/goya/goya_coresight.c +++ b/drivers/misc/habanalabs/goya/goya_coresight.c @@ -652,7 +652,7 @@ static int goya_config_spmu(struct hl_device *hdev, return 0; } -int goya_debug_coresight(struct hl_device *hdev, void *data) +int goya_debug_coresight(struct hl_device *hdev, struct hl_ctx *ctx, void *data) { struct hl_debug_params *params = data; int rc = 0; @@ -691,7 +691,7 @@ int goya_debug_coresight(struct hl_device *hdev, void *data) return rc; } -void goya_halt_coresight(struct hl_device *hdev) +void goya_halt_coresight(struct hl_device *hdev, struct hl_ctx *ctx) { struct hl_debug_params params = {}; int i, rc; -- cgit v1.2.3 From 4337b50b5fe5ee64c821790f601ee6153bb9f027 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 30 Nov 2021 23:02:21 +0200 Subject: habanalabs: add helper to get compute context There are multiple places where the code needs to get the context's pointer and increment its ref cnt. This is the proper way instead of using the compute context pointer in the device structure. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 23 +++++++++++++++++++++++ drivers/misc/habanalabs/common/debugfs.c | 14 ++++++-------- drivers/misc/habanalabs/common/device.c | 13 ++++++------- drivers/misc/habanalabs/common/habanalabs.h | 1 + 4 files changed, 36 insertions(+), 15 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 8de1217b2ed2..b2884107fa15 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -272,6 +272,29 @@ int hl_ctx_put(struct hl_ctx *ctx) return kref_put(&ctx->refcount, hl_ctx_do_release); } +struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev) +{ + struct hl_ctx *ctx = NULL; + struct hl_fpriv *hpriv; + + mutex_lock(&hdev->fpriv_list_lock); + + list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) { + /* There can only be a single user which has opened the compute device, so exit + * immediately once we find him + */ + if (!hpriv->is_control) { + ctx = hpriv->ctx; + hl_ctx_get(hdev, ctx); + break; + } + } + + mutex_unlock(&hdev->fpriv_list_lock); + + return ctx; +} + /* * hl_ctx_get_fence_locked - get CS fence under CS lock * diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 9727d82b121f..2e9c31d79d5e 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -327,11 +327,7 @@ static int vm_show(struct seq_file *s, void *data) spin_unlock(&dev_entry->ctx_mem_hash_spinlock); - mutex_lock(&dev_entry->hdev->fpriv_list_lock); - ctx = dev_entry->hdev->compute_ctx; - if (ctx) - hl_ctx_get(dev_entry->hdev, ctx); - mutex_unlock(&dev_entry->hdev->fpriv_list_lock); + ctx = hl_get_compute_ctx(dev_entry->hdev); if (ctx) { seq_puts(s, "\nVA ranges:\n\n"); for (i = HL_VA_RANGE_TYPE_HOST ; i < HL_VA_RANGE_TYPE_MAX ; ++i) { @@ -443,7 +439,7 @@ static int mmu_show(struct seq_file *s, void *data) if (dev_entry->mmu_asid == HL_KERNEL_ASID_ID) ctx = hdev->kernel_ctx; else - ctx = hdev->compute_ctx; + ctx = hl_get_compute_ctx(hdev); if (!ctx) { dev_err(hdev->dev, "no ctx available\n"); @@ -596,7 +592,7 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, u32 size, u64 *phys_addr) { struct hl_vm_phys_pg_pack *phys_pg_pack; - struct hl_ctx *ctx = hdev->compute_ctx; + struct hl_ctx *ctx; struct hl_vm_hash_node *hnode; u64 end_address, range_size; struct hl_userptr *userptr; @@ -604,6 +600,8 @@ static int device_va_to_pa(struct hl_device *hdev, u64 virt_addr, u32 size, bool valid = false; int i, rc = 0; + ctx = hl_get_compute_ctx(hdev); + if (!ctx) { dev_err(hdev->dev, "no ctx available\n"); return -EINVAL; diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index bc5736ae6b70..407f6c5020c7 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -961,6 +961,7 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false, reset_upon_device_release = false; u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; + struct hl_ctx *ctx; int i, rc; if (!hdev->init_done) { @@ -1101,16 +1102,14 @@ kill_processes: for (i = 0 ; i < hdev->asic_prop.completion_queues_count ; i++) hl_cq_reset(hdev, &hdev->completion_queue[i]); - mutex_lock(&hdev->fpriv_list_lock); - /* Make sure the context switch phase will run again */ - if (hdev->compute_ctx) { - atomic_set(&hdev->compute_ctx->thread_ctx_switch_token, 1); - hdev->compute_ctx->thread_ctx_switch_wait_token = 0; + ctx = hl_get_compute_ctx(hdev); + if (ctx) { + atomic_set(&ctx->thread_ctx_switch_token, 1); + ctx->thread_ctx_switch_wait_token = 0; + hl_ctx_put(ctx); } - mutex_unlock(&hdev->fpriv_list_lock); - /* Finished tear-down, starting to re-initialize */ if (hard_reset) { diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 670fad9b4ca0..eec96e506bb0 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2906,6 +2906,7 @@ int hl_ctx_init(struct hl_device *hdev, struct hl_ctx *ctx, bool is_kernel_ctx); void hl_ctx_do_release(struct kref *ref); void hl_ctx_get(struct hl_device *hdev, struct hl_ctx *ctx); int hl_ctx_put(struct hl_ctx *ctx); +struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev); struct hl_fence *hl_ctx_get_fence(struct hl_ctx *ctx, u64 seq); int hl_ctx_get_fences(struct hl_ctx *ctx, u64 *seq_arr, struct hl_fence **fence, u32 arr_len); -- cgit v1.2.3 From 5b90e59d55d94aa939fae941db4a0e613e6ecc1e Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Tue, 30 Nov 2021 23:08:21 +0200 Subject: habanalabs: remove compute context pointer It was an error to save the compute context's pointer in the device structure, as it allowed its use without proper ref-cnt. Change the variable to a flag that only indicates whether there is an active compute context. Code that needs the pointer will now be forced to use proper internal APIs to get the pointer. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 2 +- drivers/misc/habanalabs/common/device.c | 10 +++++----- drivers/misc/habanalabs/common/habanalabs.h | 5 ++--- drivers/misc/habanalabs/common/habanalabs_drv.c | 2 +- drivers/misc/habanalabs/goya/goya.c | 4 ++-- drivers/misc/habanalabs/goya/goya_hwmgr.c | 4 ++-- 6 files changed, 13 insertions(+), 14 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index b2884107fa15..49e6f1172d18 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -165,7 +165,7 @@ int hl_ctx_create(struct hl_device *hdev, struct hl_fpriv *hpriv) hpriv->ctx = ctx; /* TODO: remove the following line for multiple process support */ - hdev->compute_ctx = ctx; + hdev->is_compute_ctx_active = true; return 0; diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 407f6c5020c7..bea05a59425f 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -97,12 +97,12 @@ static void hpriv_release(struct kref *ref) || hdev->reset_upon_device_release) hl_device_reset(hdev, HL_DRV_RESET_DEV_RELEASE); - /* Now we can mark the compute_ctx as empty. Even if a reset is running in a different + /* Now we can mark the compute_ctx as not active. Even if a reset is running in a different * thread, we don't care because the in_reset is marked so if a user will try to open - * the device it will fail on that, even if compute_ctx is NULL. + * the device it will fail on that, even if compute_ctx is false. */ mutex_lock(&hdev->fpriv_list_lock); - hdev->compute_ctx = NULL; + hdev->is_compute_ctx_active = false; mutex_unlock(&hdev->fpriv_list_lock); kfree(hpriv); @@ -1150,7 +1150,7 @@ kill_processes: goto out_err; } - hdev->compute_ctx = NULL; + hdev->is_compute_ctx_active = false; rc = hl_ctx_init(hdev, hdev->kernel_ctx, true); if (rc) { @@ -1403,7 +1403,7 @@ int hl_device_init(struct hl_device *hdev, struct class *hclass) goto mmu_fini; } - hdev->compute_ctx = NULL; + hdev->is_compute_ctx_active = false; hdev->asic_funcs->state_dump_init(hdev); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index eec96e506bb0..df1935952c28 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2503,7 +2503,6 @@ struct last_error_session_info { * @fpriv_list: list of file private data structures. Each structure is created * when a user opens the device * @fpriv_list_lock: protects the fpriv_list - * @compute_ctx: current compute context executing. * @aggregated_cs_counters: aggregated cs counters among all contexts * @mmu_priv: device-specific MMU data. * @mmu_func: device-related MMU functions. @@ -2601,6 +2600,7 @@ struct last_error_session_info { * cases where Linux was not loaded to device CPU * @supports_wait_for_multi_cs: true if wait for multi CS is supported * @is_in_soft_reset: Device is currently in soft reset process. + * @is_compute_ctx_active: Whether there is an active compute context executing. */ struct hl_device { struct pci_dev *pdev; @@ -2656,8 +2656,6 @@ struct hl_device { struct list_head fpriv_list; struct mutex fpriv_list_lock; - struct hl_ctx *compute_ctx; - struct hl_cs_counters_atomic aggregated_cs_counters; struct hl_mmu_priv mmu_priv; @@ -2730,6 +2728,7 @@ struct hl_device { u8 supports_wait_for_multi_cs; u8 stream_master_qid_arr_size; u8 is_in_soft_reset; + u8 is_compute_ctx_active; /* Parameters for bring-up */ u64 nic_ports_mask; diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index d4ef99952d15..62a02ef43bb7 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -161,7 +161,7 @@ int hl_device_open(struct inode *inode, struct file *filp) goto out_err; } - if (hdev->compute_ctx) { + if (hdev->is_compute_ctx_active) { dev_dbg_ratelimited(hdev->dev, "Can't open %s because another user is working on it\n", dev_name(hdev->dev)); diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index e54d60e75854..8d0f2cd608fc 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -827,7 +827,7 @@ static void goya_set_freq_to_low_job(struct work_struct *work) mutex_lock(&hdev->fpriv_list_lock); - if (!hdev->compute_ctx) + if (!hdev->is_compute_ctx_active) goya_set_frequency(hdev, PLL_LOW); mutex_unlock(&hdev->fpriv_list_lock); diff --git a/drivers/misc/habanalabs/goya/goya_hwmgr.c b/drivers/misc/habanalabs/goya/goya_hwmgr.c index 42985a85b625..76b47749affe 100644 --- a/drivers/misc/habanalabs/goya/goya_hwmgr.c +++ b/drivers/misc/habanalabs/goya/goya_hwmgr.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright 2016-2019 HabanaLabs, Ltd. + * Copyright 2016-2021 HabanaLabs, Ltd. * All Rights Reserved. */ @@ -258,7 +258,7 @@ static ssize_t pm_mng_profile_store(struct device *dev, mutex_lock(&hdev->fpriv_list_lock); - if (hdev->compute_ctx) { + if (hdev->is_compute_ctx_active) { dev_err(hdev->dev, "Can't change PM profile while compute context is opened on the device\n"); count = -EPERM; -- cgit v1.2.3 From b02220536cb66ce1e357d78c944d6be07f1e1051 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Wed, 1 Dec 2021 10:52:27 +0200 Subject: habanalabs: wait again for multi-CS if no CS completed The original multi-CS design assumption that stream masters are used exclusively (i.e. multi-CS with set of stream master QIDs will not get completed by CS not from the multi-CS set) is inaccurate. Thus multi-CS behavior is now modified not to treat such case as an error. Instead, if we have multi-CS completion but we detect that no CS from the list is actually completed we will do another multi-CS wait (with modified timeout). Signed-off-by: Ohad Sharabi Reviewed-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 97 +++++++++++----------- drivers/misc/habanalabs/common/habanalabs.h | 4 +- 2 files changed, 50 insertions(+), 51 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index a63ebbc04787..f58fff3671d6 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -545,13 +545,6 @@ static void complete_multi_cs(struct hl_device *hdev, struct hl_cs *cs) * mcs fences. */ fence->mcs_handling_done = true; - /* - * Since CS (and its related fence) can be associated with only one - * multi CS context, once it triggered multi CS completion no need to - * continue checking other multi CS contexts. - */ - spin_unlock(&mcs_compl->lock); - break; } spin_unlock(&mcs_compl->lock); @@ -2498,6 +2491,21 @@ static int _hl_cs_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, return rc; } +static inline unsigned long hl_usecs64_to_jiffies(const u64 usecs) +{ + if (usecs <= U32_MAX) + return usecs_to_jiffies(usecs); + + /* + * If the value in nanoseconds is larger than 64 bit, use the largest + * 64 bit value. + */ + if (usecs >= ((u64)(U64_MAX / NSEC_PER_USEC))) + return nsecs_to_jiffies(U64_MAX); + + return nsecs_to_jiffies(usecs * NSEC_PER_USEC); +} + /* * hl_wait_multi_cs_completion_init - init completion structure * @@ -2534,8 +2542,7 @@ static struct multi_cs_completion *hl_wait_multi_cs_completion_init( } if (i == MULTI_CS_MAX_USER_CTX) { - dev_err(hdev->dev, - "no available multi-CS completion structure\n"); + dev_err(hdev->dev, "no available multi-CS completion structure\n"); return ERR_PTR(-ENOMEM); } return mcs_compl; @@ -2566,27 +2573,18 @@ static void hl_wait_multi_cs_completion_fini( * * @return 0 on success, otherwise non 0 error code */ -static int hl_wait_multi_cs_completion(struct multi_cs_data *mcs_data) +static int hl_wait_multi_cs_completion(struct multi_cs_data *mcs_data, + struct multi_cs_completion *mcs_compl) { - struct hl_device *hdev = mcs_data->ctx->hdev; - struct multi_cs_completion *mcs_compl; long completion_rc; - mcs_compl = hl_wait_multi_cs_completion_init(hdev, - mcs_data->stream_master_qid_map); - if (IS_ERR(mcs_compl)) - return PTR_ERR(mcs_compl); - - completion_rc = wait_for_completion_interruptible_timeout( - &mcs_compl->completion, - usecs_to_jiffies(mcs_data->timeout_us)); + completion_rc = wait_for_completion_interruptible_timeout(&mcs_compl->completion, + mcs_data->timeout_jiffies); /* update timestamp */ if (completion_rc > 0) mcs_data->timestamp = mcs_compl->timestamp; - hl_wait_multi_cs_completion_fini(mcs_compl); - mcs_data->wait_status = completion_rc; return 0; @@ -2619,6 +2617,7 @@ void hl_multi_cs_completion_init(struct hl_device *hdev) */ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) { + struct multi_cs_completion *mcs_compl; struct hl_device *hdev = hpriv->hdev; struct multi_cs_data mcs_data = {0}; union hl_wait_cs_args *args = data; @@ -2686,12 +2685,19 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) goto put_ctx; /* wait (with timeout) for the first CS to be completed */ - mcs_data.timeout_us = args->in.timeout_us; - rc = hl_wait_multi_cs_completion(&mcs_data); - if (rc) + mcs_data.timeout_jiffies = hl_usecs64_to_jiffies(args->in.timeout_us); + + mcs_compl = hl_wait_multi_cs_completion_init(hdev, mcs_data.stream_master_qid_map); + if (IS_ERR(mcs_compl)) { + rc = PTR_ERR(mcs_compl); goto put_ctx; + } + + while (true) { + rc = hl_wait_multi_cs_completion(&mcs_data, mcs_compl); + if (rc || (mcs_data.wait_status == 0)) + break; - if (mcs_data.wait_status > 0) { /* * poll fences once again to update the CS map. * no timestamp should be updated this time. @@ -2699,18 +2705,26 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) mcs_data.update_ts = false; rc = hl_cs_poll_fences(&mcs_data); + if (mcs_data.completion_bitmap) + break; + /* * if hl_wait_multi_cs_completion returned before timeout (i.e. - * it got a completion) we expect to see at least one CS - * completed after the poll function. + * it got a completion) it either got completed by CS in the multi CS list + * (in which case the indication will be non empty completion_bitmap) or it + * got completed by CS submitted to one of the shared stream master but + * not in the multi CS list (in which case we should wait again but reinit + * the completion, modify the timeout and set timestamp as zero to let a CS + * related to the current multi-CS set a new, relevant, timestamp) */ - if (!mcs_data.completion_bitmap) { - dev_warn_ratelimited(hdev->dev, - "Multi-CS got completion on wait but no CS completed\n"); - rc = -EFAULT; - } + /* wait again with modified timeout */ + mcs_data.timeout_jiffies = mcs_data.wait_status; + reinit_completion(&mcs_compl->completion); + mcs_compl->timestamp = 0; } + hl_wait_multi_cs_completion_fini(mcs_compl); + put_ctx: hl_ctx_put(ctx); kfree(fence_arr); @@ -2741,7 +2755,7 @@ free_seq_arr: } /* update if some CS was gone */ - if (mcs_data.timestamp) + if (!mcs_data.timestamp) args->out.flags |= HL_WAIT_CS_STATUS_FLAG_GONE; } else { args->out.status = HL_WAIT_CS_STATUS_BUSY; @@ -2807,21 +2821,6 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) return 0; } -static inline unsigned long hl_usecs64_to_jiffies(const u64 usecs) -{ - if (usecs <= U32_MAX) - return usecs_to_jiffies(usecs); - - /* - * If the value in nanoseconds is larger than 64 bit, use the largest - * 64 bit value. - */ - if (usecs >= ((u64)(U64_MAX / NSEC_PER_USEC))) - return nsecs_to_jiffies(U64_MAX); - - return nsecs_to_jiffies(usecs * NSEC_PER_USEC); -} - static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, u64 timeout_us, u64 user_address, u64 target_value, struct hl_user_interrupt *interrupt, diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index df1935952c28..eda1c70f6966 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2362,7 +2362,7 @@ struct multi_cs_completion { * @ctx: pointer to the context structure * @fence_arr: array of fences of all CSs * @seq_arr: array of CS sequence numbers - * @timeout_us: timeout in usec for waiting for CS to complete + * @timeout_jiffies: timeout in jiffies for waiting for CS to complete * @timestamp: timestamp of first completed CS * @wait_status: wait for CS status * @completion_bitmap: bitmap of completed CSs (1- completed, otherwise 0) @@ -2376,7 +2376,7 @@ struct multi_cs_data { struct hl_ctx *ctx; struct hl_fence **fence_arr; u64 *seq_arr; - s64 timeout_us; + s64 timeout_jiffies; s64 timestamp; long wait_status; u32 completion_bitmap; -- cgit v1.2.3 From 7c623ef732bdba440b1f0e74a99265cb7587df7e Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 7 Dec 2021 11:20:46 +0200 Subject: habanalabs: return correct clock throttling period Current clock throttling period returned from driver was wrong due to wrong time comparison. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 9210114beefe..f571641c19ae 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -335,9 +335,9 @@ static int clk_throttle_info(struct hl_fpriv *hpriv, struct hl_info_args *args) ktime_to_us(hdev->clk_throttling.timestamp[i].start); if (ktime_compare(hdev->clk_throttling.timestamp[i].end, zero_time)) - end_time = ktime_get(); - else end_time = hdev->clk_throttling.timestamp[i].end; + else + end_time = ktime_get(); clk_throttle.clk_throttling_duration_ns[i] = ktime_to_ns(ktime_sub(end_time, -- cgit v1.2.3 From 7363805b8a52c9f5650f957a34a30788cc7ce4c2 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Wed, 8 Dec 2021 16:25:07 +0200 Subject: habanalabs: remove in_debug check in device open The driver supports only a single user anyway, so there is no point in checking whether we are in_debug state when a user tries to open the device, because if we are in_debug, it means a user is already using the device. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/habanalabs.h | 5 +++-- drivers/misc/habanalabs/common/habanalabs_drv.c | 8 -------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index eda1c70f6966..362eee3f028c 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2561,8 +2561,9 @@ struct last_error_session_info { * @init_done: is the initialization of the device done. * @device_cpu_disabled: is the device CPU disabled (due to timeouts) * @dma_mask: the dma mask that was set for this device - * @in_debug: is device under debug. This, together with fpriv_list, enforces - * that only a single user is configuring the debug infrastructure. + * @in_debug: whether the device is in a state where the profiling/tracing infrastructure + * can be used. This indication is needed because in some ASICs we need to do + * specific operations to enable that infrastructure. * @power9_64bit_dma_enable: true to enable 64-bit DMA mask support. Relevant * only to POWER9 machines. * @cdev_sysfs_created: were char devices and sysfs nodes created. diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index 62a02ef43bb7..d59201f93de9 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -153,14 +153,6 @@ int hl_device_open(struct inode *inode, struct file *filp) goto out_err; } - if (hdev->in_debug) { - dev_err_ratelimited(hdev->dev, - "Can't open %s because it is being debugged by another user\n", - dev_name(hdev->dev)); - rc = -EPERM; - goto out_err; - } - if (hdev->is_compute_ctx_active) { dev_dbg_ratelimited(hdev->dev, "Can't open %s because another user is working on it\n", -- cgit v1.2.3 From 9acdc21b0b04f370c306b7d95c296c7f22660fc0 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 8 Dec 2021 21:46:29 +0200 Subject: habanalabs: add current PI value to cpu packets In order to increase cpucp messaging reliability we will add the current PI value to the descriptor sent to F/W. F/W will wait for the PI value as an indication of a valid packet. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 76741898d922..34e70cca37c1 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -212,7 +212,8 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, struct asic_fixed_properties *prop = &hdev->asic_prop; struct cpucp_packet *pkt; dma_addr_t pkt_dma_addr; - u32 tmp, expected_ack_val; + struct hl_bd *sent_bd; + u32 tmp, expected_ack_val, pi; int rc = 0; pkt = hdev->asic_funcs->cpu_accessible_dma_pool_alloc(hdev, len, @@ -237,6 +238,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, /* set fence to a non valid value */ pkt->fence = cpu_to_le32(UINT_MAX); + pi = queue->pi; /* * The CPU queue is a synchronous queue with an effective depth of @@ -246,7 +248,7 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, * Which means that we don't need to lock the access to the entire H/W * queues module when submitting a JOB to the CPU queue. */ - hl_hw_queue_submit_bd(hdev, queue, 0, len, pkt_dma_addr); + hl_hw_queue_submit_bd(hdev, queue, hl_queue_inc_ptr(queue->pi), len, pkt_dma_addr); if (prop->fw_app_cpu_boot_dev_sts0 & CPU_BOOT_DEV_STS0_PKT_PI_ACK_EN) expected_ack_val = queue->pi; @@ -278,6 +280,14 @@ int hl_fw_send_cpu_message(struct hl_device *hdev, u32 hw_queue_id, u32 *msg, *result = le64_to_cpu(pkt->result); } + /* Scrub previous buffer descriptor 'ctl' field which contains the + * previous PI value written during packet submission. + * We must do this or else F/W can read an old value upon queue wraparound. + */ + sent_bd = queue->kernel_address; + sent_bd += hl_pi_2_offset(pi); + sent_bd->ctl = cpu_to_le32(UINT_MAX); + out: mutex_unlock(&hdev->send_cpu_message_lock); -- cgit v1.2.3 From bb099a805104568c8babbf94824507b0d72ba232 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 12 Dec 2021 16:40:24 +0200 Subject: habanalabs: fix hwmon handling for legacy f/w In legacy f/w that use old hwmon.h file, the values of the hwmon enums are different than the values that are in newer kernels (5.6 and above). Therefore, to support working with those f/w, we need to do some fixup before registering with the hwmon subsystem and also when calling the functions that communicate with the f/w to retrieve sensors information. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/hwmon.c | 201 +++++++++++++++++++++++++++------ 1 file changed, 169 insertions(+), 32 deletions(-) diff --git a/drivers/misc/habanalabs/common/hwmon.c b/drivers/misc/habanalabs/common/hwmon.c index 70182b42940d..57f5d2c48330 100644 --- a/drivers/misc/habanalabs/common/hwmon.c +++ b/drivers/misc/habanalabs/common/hwmon.c @@ -10,17 +10,148 @@ #include #include -#define HWMON_NR_SENSOR_TYPES (hwmon_pwm + 1) +#define HWMON_NR_SENSOR_TYPES (hwmon_max) -int hl_build_hwmon_channel_info(struct hl_device *hdev, - struct cpucp_sensor *sensors_arr) +#ifdef _HAS_HWMON_HWMON_T_ENABLE + +static u32 fixup_flags_legacy_fw(struct hl_device *hdev, enum hwmon_sensor_types type, + u32 cpucp_flags) { - u32 counts[HWMON_NR_SENSOR_TYPES] = {0}; - u32 *sensors_by_type[HWMON_NR_SENSOR_TYPES] = {NULL}; + u32 flags; + + switch (type) { + case hwmon_temp: + flags = (cpucp_flags << 1) | HWMON_T_ENABLE; + break; + + case hwmon_in: + flags = (cpucp_flags << 1) | HWMON_I_ENABLE; + break; + + case hwmon_curr: + flags = (cpucp_flags << 1) | HWMON_C_ENABLE; + break; + + case hwmon_fan: + flags = (cpucp_flags << 1) | HWMON_F_ENABLE; + break; + + case hwmon_power: + flags = (cpucp_flags << 1) | HWMON_P_ENABLE; + break; + + case hwmon_pwm: + /* enable bit was here from day 1, so no need to adjust */ + flags = cpucp_flags; + break; + + default: + dev_err(hdev->dev, "unsupported h/w sensor type %d\n", type); + flags = cpucp_flags; + break; + } + + return flags; +} + +static u32 fixup_attr_legacy_fw(u32 attr) +{ + return (attr - 1); +} + +#else + +static u32 fixup_flags_legacy_fw(struct hl_device *hdev, enum hwmon_sensor_types type, + u32 cpucp_flags) +{ + return cpucp_flags; +} + +static u32 fixup_attr_legacy_fw(u32 attr) +{ + return attr; +} + +#endif /* !_HAS_HWMON_HWMON_T_ENABLE */ + +static u32 adjust_hwmon_flags(struct hl_device *hdev, enum hwmon_sensor_types type, u32 cpucp_flags) +{ + u32 flags, cpucp_input_val; + bool use_cpucp_enum; + + use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false; + + /* If f/w is using it's own enum, we need to check if the properties values are aligned. + * If not, it means we need to adjust the values to the new format that is used in the + * kernel since 5.6 (enum values were incremented by 1 by adding a new enable value). + */ + if (use_cpucp_enum) { + switch (type) { + case hwmon_temp: + cpucp_input_val = cpucp_temp_input; + if (cpucp_input_val == hwmon_temp_input) + flags = cpucp_flags; + else + flags = (cpucp_flags << 1) | HWMON_T_ENABLE; + break; + + case hwmon_in: + cpucp_input_val = cpucp_in_input; + if (cpucp_input_val == hwmon_in_input) + flags = cpucp_flags; + else + flags = (cpucp_flags << 1) | HWMON_I_ENABLE; + break; + + case hwmon_curr: + cpucp_input_val = cpucp_curr_input; + if (cpucp_input_val == hwmon_curr_input) + flags = cpucp_flags; + else + flags = (cpucp_flags << 1) | HWMON_C_ENABLE; + break; + + case hwmon_fan: + cpucp_input_val = cpucp_fan_input; + if (cpucp_input_val == hwmon_fan_input) + flags = cpucp_flags; + else + flags = (cpucp_flags << 1) | HWMON_F_ENABLE; + break; + + case hwmon_pwm: + /* enable bit was here from day 1, so no need to adjust */ + flags = cpucp_flags; + break; + + case hwmon_power: + cpucp_input_val = CPUCP_POWER_INPUT; + if (cpucp_input_val == hwmon_power_input) + flags = cpucp_flags; + else + flags = (cpucp_flags << 1) | HWMON_P_ENABLE; + break; + + default: + dev_err(hdev->dev, "unsupported h/w sensor type %d\n", type); + flags = cpucp_flags; + break; + } + } else { + flags = fixup_flags_legacy_fw(hdev, type, cpucp_flags); + } + + return flags; +} + +int hl_build_hwmon_channel_info(struct hl_device *hdev, struct cpucp_sensor *sensors_arr) +{ + u32 num_sensors_for_type, flags, num_active_sensor_types = 0, arr_size = 0, *curr_arr; u32 sensors_by_type_next_index[HWMON_NR_SENSOR_TYPES] = {0}; + u32 *sensors_by_type[HWMON_NR_SENSOR_TYPES] = {NULL}; struct hwmon_channel_info **channels_info; - u32 num_sensors_for_type, num_active_sensor_types = 0, - arr_size = 0, *curr_arr; + u32 counts[HWMON_NR_SENSOR_TYPES] = {0}; enum hwmon_sensor_types type; int rc, i, j; @@ -31,8 +162,7 @@ int hl_build_hwmon_channel_info(struct hl_device *hdev, break; if (type >= HWMON_NR_SENSOR_TYPES) { - dev_err(hdev->dev, - "Got wrong sensor type %d from device\n", type); + dev_err(hdev->dev, "Got wrong sensor type %d from device\n", type); return -EINVAL; } @@ -45,8 +175,9 @@ int hl_build_hwmon_channel_info(struct hl_device *hdev, continue; num_sensors_for_type = counts[i] + 1; - curr_arr = kcalloc(num_sensors_for_type, sizeof(*curr_arr), - GFP_KERNEL); + dev_dbg(hdev->dev, "num_sensors_for_type %d = %d\n", i, num_sensors_for_type); + + curr_arr = kcalloc(num_sensors_for_type, sizeof(*curr_arr), GFP_KERNEL); if (!curr_arr) { rc = -ENOMEM; goto sensors_type_err; @@ -59,20 +190,18 @@ int hl_build_hwmon_channel_info(struct hl_device *hdev, for (i = 0 ; i < arr_size ; i++) { type = le32_to_cpu(sensors_arr[i].type); curr_arr = sensors_by_type[type]; - curr_arr[sensors_by_type_next_index[type]++] = - le32_to_cpu(sensors_arr[i].flags); + flags = adjust_hwmon_flags(hdev, type, le32_to_cpu(sensors_arr[i].flags)); + curr_arr[sensors_by_type_next_index[type]++] = flags; } - channels_info = kcalloc(num_active_sensor_types + 1, - sizeof(*channels_info), GFP_KERNEL); + channels_info = kcalloc(num_active_sensor_types + 1, sizeof(*channels_info), GFP_KERNEL); if (!channels_info) { rc = -ENOMEM; goto channels_info_array_err; } for (i = 0 ; i < num_active_sensor_types ; i++) { - channels_info[i] = kzalloc(sizeof(*channels_info[i]), - GFP_KERNEL); + channels_info[i] = kzalloc(sizeof(*channels_info[i]), GFP_KERNEL); if (!channels_info[i]) { rc = -ENOMEM; goto channel_info_err; @@ -88,18 +217,19 @@ int hl_build_hwmon_channel_info(struct hl_device *hdev, j++; } - hdev->hl_chip_info->info = - (const struct hwmon_channel_info **)channels_info; + hdev->hl_chip_info->info = (const struct hwmon_channel_info **)channels_info; return 0; channel_info_err: - for (i = 0 ; i < num_active_sensor_types ; i++) + for (i = 0 ; i < num_active_sensor_types ; i++) { if (channels_info[i]) { kfree(channels_info[i]->config); kfree(channels_info[i]); } + } kfree(channels_info); + channels_info_array_err: sensors_type_err: for (i = 0 ; i < HWMON_NR_SENSOR_TYPES ; i++) @@ -112,14 +242,16 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, u32 attr, int channel, long *val) { struct hl_device *hdev = dev_get_drvdata(dev); - int rc; + bool use_cpucp_enum; u32 cpucp_attr; - bool use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & - CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false; + int rc; if (!hl_device_operational(hdev, NULL)) return -ENODEV; + use_cpucp_enum = (hdev->asic_prop.fw_app_cpu_boot_dev_sts0 & + CPU_BOOT_DEV_STS0_MAP_HWMON_EN) ? true : false; + switch (type) { case hwmon_temp: switch (attr) { @@ -151,7 +283,7 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) rc = hl_get_temperature(hdev, channel, cpucp_attr, val); else - rc = hl_get_temperature(hdev, channel, attr, val); + rc = hl_get_temperature(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_in: switch (attr) { @@ -174,7 +306,7 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) rc = hl_get_voltage(hdev, channel, cpucp_attr, val); else - rc = hl_get_voltage(hdev, channel, attr, val); + rc = hl_get_voltage(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_curr: switch (attr) { @@ -197,7 +329,7 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) rc = hl_get_current(hdev, channel, cpucp_attr, val); else - rc = hl_get_current(hdev, channel, attr, val); + rc = hl_get_current(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_fan: switch (attr) { @@ -217,7 +349,7 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) rc = hl_get_fan_speed(hdev, channel, cpucp_attr, val); else - rc = hl_get_fan_speed(hdev, channel, attr, val); + rc = hl_get_fan_speed(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_pwm: switch (attr) { @@ -234,6 +366,7 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) rc = hl_get_pwm_info(hdev, channel, cpucp_attr, val); else + /* no need for fixup as pwm was aligned from day 1 */ rc = hl_get_pwm_info(hdev, channel, attr, val); break; case hwmon_power: @@ -251,7 +384,7 @@ static int hl_read(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) rc = hl_get_power(hdev, channel, cpucp_attr, val); else - rc = hl_get_power(hdev, channel, attr, val); + rc = hl_get_power(hdev, channel, fixup_attr_legacy_fw(attr), val); break; default: return -EINVAL; @@ -286,7 +419,7 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) hl_set_temperature(hdev, channel, cpucp_attr, val); else - hl_set_temperature(hdev, channel, attr, val); + hl_set_temperature(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_pwm: switch (attr) { @@ -303,6 +436,7 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) hl_set_pwm_info(hdev, channel, cpucp_attr, val); else + /* no need for fixup as pwm was aligned from day 1 */ hl_set_pwm_info(hdev, channel, attr, val); break; case hwmon_in: @@ -317,7 +451,7 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) hl_set_voltage(hdev, channel, cpucp_attr, val); else - hl_set_voltage(hdev, channel, attr, val); + hl_set_voltage(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_curr: switch (attr) { @@ -331,7 +465,7 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) hl_set_current(hdev, channel, cpucp_attr, val); else - hl_set_current(hdev, channel, attr, val); + hl_set_current(hdev, channel, fixup_attr_legacy_fw(attr), val); break; case hwmon_power: switch (attr) { @@ -345,7 +479,7 @@ static int hl_write(struct device *dev, enum hwmon_sensor_types type, if (use_cpucp_enum) hl_set_power(hdev, channel, cpucp_attr, val); else - hl_set_power(hdev, channel, attr, val); + hl_set_power(hdev, channel, fixup_attr_legacy_fw(attr), val); break; default: return -EINVAL; @@ -444,6 +578,9 @@ int hl_get_temperature(struct hl_device *hdev, pkt.sensor_index = __cpu_to_le16(sensor_index); pkt.type = __cpu_to_le16(attr); + dev_dbg(hdev->dev, "get temp, ctl 0x%x, sensor %d, type %d\n", + pkt.ctl, pkt.sensor_index, pkt.type); + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), 0, &result); -- cgit v1.2.3 From 707c1252868d885c47b80613b60bdcb19e133397 Mon Sep 17 00:00:00 2001 From: Dani Liberman Date: Wed, 8 Dec 2021 09:52:03 +0200 Subject: habanalabs: keep control device alive during hard reset Need to allow user retrieve data during reset and afterwards without the need to reopen the device. Did it by seperating the user peocesses list into two lists: 1. fpriv_list which contains list of user processes that opened the device (currently only one). 2. fpriv_ctrl_list which contains list of user processes that opened the control device. This processes in this list shall not be killed during reset, only when the device is suddenly removed from PCI chain. Signed-off-by: Dani Liberman Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/context.c | 8 ++-- drivers/misc/habanalabs/common/device.c | 56 +++++++++++++++++-------- drivers/misc/habanalabs/common/habanalabs.h | 7 +++- drivers/misc/habanalabs/common/habanalabs_drv.c | 9 ++-- 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/drivers/misc/habanalabs/common/context.c b/drivers/misc/habanalabs/common/context.c index 49e6f1172d18..c6360e33bce8 100644 --- a/drivers/misc/habanalabs/common/context.c +++ b/drivers/misc/habanalabs/common/context.c @@ -283,11 +283,9 @@ struct hl_ctx *hl_get_compute_ctx(struct hl_device *hdev) /* There can only be a single user which has opened the compute device, so exit * immediately once we find him */ - if (!hpriv->is_control) { - ctx = hpriv->ctx; - hl_ctx_get(hdev, ctx); - break; - } + ctx = hpriv->ctx; + hl_ctx_get(hdev, ctx); + break; } mutex_unlock(&hdev->fpriv_list_lock); diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index bea05a59425f..f1f482c5cdcb 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -169,9 +169,9 @@ static int hl_device_release_ctrl(struct inode *inode, struct file *filp) goto out; } - mutex_lock(&hdev->fpriv_list_lock); + mutex_lock(&hdev->fpriv_ctrl_list_lock); list_del(&hpriv->dev_node); - mutex_unlock(&hdev->fpriv_list_lock); + mutex_unlock(&hdev->fpriv_ctrl_list_lock); out: put_pid(hpriv->taskpid); @@ -449,7 +449,9 @@ static int device_early_init(struct hl_device *hdev) INIT_LIST_HEAD(&hdev->cs_mirror_list); spin_lock_init(&hdev->cs_mirror_lock); INIT_LIST_HEAD(&hdev->fpriv_list); + INIT_LIST_HEAD(&hdev->fpriv_ctrl_list); mutex_init(&hdev->fpriv_list_lock); + mutex_init(&hdev->fpriv_ctrl_list_lock); atomic_set(&hdev->in_reset, 0); mutex_init(&hdev->clk_throttling.lock); @@ -491,6 +493,7 @@ static void device_early_fini(struct hl_device *hdev) mutex_destroy(&hdev->send_cpu_message_lock); mutex_destroy(&hdev->fpriv_list_lock); + mutex_destroy(&hdev->fpriv_ctrl_list_lock); mutex_destroy(&hdev->clk_throttling.lock); @@ -678,6 +681,8 @@ static void take_release_locks(struct hl_device *hdev) /* Flush anyone that is inside device open */ mutex_lock(&hdev->fpriv_list_lock); mutex_unlock(&hdev->fpriv_list_lock); + mutex_lock(&hdev->fpriv_ctrl_list_lock); + mutex_unlock(&hdev->fpriv_ctrl_list_lock); } static void cleanup_resources(struct hl_device *hdev, bool hard_reset, bool fw_reset) @@ -789,17 +794,21 @@ disable_device: return rc; } -static int device_kill_open_processes(struct hl_device *hdev, u32 timeout) +static int device_kill_open_processes(struct hl_device *hdev, u32 timeout, bool control_dev) { - struct hl_fpriv *hpriv; struct task_struct *task = NULL; + struct list_head *fd_list; + struct hl_fpriv *hpriv; + struct mutex *fd_lock; u32 pending_cnt; + fd_lock = control_dev ? &hdev->fpriv_ctrl_list_lock : &hdev->fpriv_list_lock; + fd_list = control_dev ? &hdev->fpriv_ctrl_list : &hdev->fpriv_list; /* Giving time for user to close FD, and for processes that are inside * hl_device_open to finish */ - if (!list_empty(&hdev->fpriv_list)) + if (!list_empty(fd_list)) ssleep(1); if (timeout) { @@ -815,12 +824,12 @@ static int device_kill_open_processes(struct hl_device *hdev, u32 timeout) } } - mutex_lock(&hdev->fpriv_list_lock); + mutex_lock(fd_lock); /* This section must be protected because we are dereferencing * pointers that are freed if the process exits */ - list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) { + list_for_each_entry(hpriv, fd_list, dev_node) { task = get_pid_task(hpriv->taskpid, PIDTYPE_PID); if (task) { dev_info(hdev->dev, "Killing user process pid=%d\n", @@ -832,12 +841,12 @@ static int device_kill_open_processes(struct hl_device *hdev, u32 timeout) } else { dev_warn(hdev->dev, "Can't get task struct for PID so giving up on killing process\n"); - mutex_unlock(&hdev->fpriv_list_lock); + mutex_unlock(fd_lock); return -ETIME; } } - mutex_unlock(&hdev->fpriv_list_lock); + mutex_unlock(fd_lock); /* * We killed the open users, but that doesn't mean they are closed. @@ -849,7 +858,7 @@ static int device_kill_open_processes(struct hl_device *hdev, u32 timeout) */ wait_for_processes: - while ((!list_empty(&hdev->fpriv_list)) && (pending_cnt)) { + while ((!list_empty(fd_list)) && (pending_cnt)) { dev_dbg(hdev->dev, "Waiting for all unmap operations to finish before hard reset\n"); @@ -859,7 +868,7 @@ wait_for_processes: } /* All processes exited successfully */ - if (list_empty(&hdev->fpriv_list)) + if (list_empty(fd_list)) return 0; /* Give up waiting for processes to exit */ @@ -871,14 +880,19 @@ wait_for_processes: return -EBUSY; } -static void device_disable_open_processes(struct hl_device *hdev) +static void device_disable_open_processes(struct hl_device *hdev, bool control_dev) { + struct list_head *fd_list; struct hl_fpriv *hpriv; + struct mutex *fd_lock; - mutex_lock(&hdev->fpriv_list_lock); - list_for_each_entry(hpriv, &hdev->fpriv_list, dev_node) + fd_lock = control_dev ? &hdev->fpriv_ctrl_list_lock : &hdev->fpriv_list_lock; + fd_list = control_dev ? &hdev->fpriv_ctrl_list : &hdev->fpriv_list; + + mutex_lock(fd_lock); + list_for_each_entry(hpriv, fd_list, dev_node) hpriv->hdev = NULL; - mutex_unlock(&hdev->fpriv_list_lock); + mutex_unlock(fd_lock); } static void handle_reset_trigger(struct hl_device *hdev, u32 flags) @@ -1057,7 +1071,7 @@ kill_processes: * process can't really exit until all its CSs are done, which * is what we do in cs rollback */ - rc = device_kill_open_processes(hdev, 0); + rc = device_kill_open_processes(hdev, 0, false); if (rc == -EBUSY) { if (hdev->device_fini_pending) { @@ -1629,10 +1643,16 @@ void hl_device_fini(struct hl_device *hdev) "Waiting for all processes to exit (timeout of %u seconds)", HL_PENDING_RESET_LONG_SEC); - rc = device_kill_open_processes(hdev, HL_PENDING_RESET_LONG_SEC); + rc = device_kill_open_processes(hdev, HL_PENDING_RESET_LONG_SEC, false); if (rc) { dev_crit(hdev->dev, "Failed to kill all open processes\n"); - device_disable_open_processes(hdev); + device_disable_open_processes(hdev, false); + } + + rc = device_kill_open_processes(hdev, 0, true); + if (rc) { + dev_crit(hdev->dev, "Failed to kill all control device open processes\n"); + device_disable_open_processes(hdev, true); } hl_cb_pool_fini(hdev); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 362eee3f028c..015aa1ee8ce0 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -1824,7 +1824,6 @@ struct hl_debug_params { * @dev_node: node in the device list of file private data * @refcount: number of related contexts. * @restore_phase_mutex: lock for context switch and restore phase. - * @is_control: true for control device, false otherwise */ struct hl_fpriv { struct hl_device *hdev; @@ -1837,7 +1836,6 @@ struct hl_fpriv { struct list_head dev_node; struct kref refcount; struct mutex restore_phase_mutex; - u8 is_control; }; @@ -2502,7 +2500,10 @@ struct last_error_session_info { * @internal_cb_va_base: internal cb pool mmu virtual address base * @fpriv_list: list of file private data structures. Each structure is created * when a user opens the device + * @fpriv_ctrl_list: list of file private data structures. Each structure is created + * when a user opens the control device * @fpriv_list_lock: protects the fpriv_list + * @fpriv_ctrl_list_lock: protects the fpriv_ctrl_list * @aggregated_cs_counters: aggregated cs counters among all contexts * @mmu_priv: device-specific MMU data. * @mmu_func: device-related MMU functions. @@ -2655,7 +2656,9 @@ struct hl_device { u64 internal_cb_va_base; struct list_head fpriv_list; + struct list_head fpriv_ctrl_list; struct mutex fpriv_list_lock; + struct mutex fpriv_ctrl_list_lock; struct hl_cs_counters_atomic aggregated_cs_counters; diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index d59201f93de9..aa4e07b1f839 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -220,12 +220,11 @@ int hl_device_open_ctrl(struct inode *inode, struct file *filp) hpriv->hdev = hdev; filp->private_data = hpriv; hpriv->filp = filp; - hpriv->is_control = true; nonseekable_open(inode, filp); hpriv->taskpid = find_get_pid(current->pid); - mutex_lock(&hdev->fpriv_list_lock); + mutex_lock(&hdev->fpriv_ctrl_list_lock); if (!hl_device_operational(hdev, NULL)) { dev_err_ratelimited(hdev->dev_ctrl, @@ -235,13 +234,13 @@ int hl_device_open_ctrl(struct inode *inode, struct file *filp) goto out_err; } - list_add(&hpriv->dev_node, &hdev->fpriv_list); - mutex_unlock(&hdev->fpriv_list_lock); + list_add(&hpriv->dev_node, &hdev->fpriv_ctrl_list); + mutex_unlock(&hdev->fpriv_ctrl_list_lock); return 0; out_err: - mutex_unlock(&hdev->fpriv_list_lock); + mutex_unlock(&hdev->fpriv_ctrl_list_lock); filp->private_data = NULL; put_pid(hpriv->taskpid); -- cgit v1.2.3 From b5c92b88823028bea4c74f3516c640406205933c Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 8 Dec 2021 15:00:10 +0200 Subject: habanalabs: sysfs support for two infineon versions Currently sysfs support dumping a single infineon version, in future asics we will have two infineon versions. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/sysfs.c | 9 +++++++-- drivers/misc/habanalabs/include/common/cpucp_if.h | 13 ++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 15e4ae65e515..6f575032f675 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -163,8 +163,13 @@ static ssize_t infineon_ver_show(struct device *dev, { struct hl_device *hdev = dev_get_drvdata(dev); - return sprintf(buf, "0x%04x\n", - hdev->asic_prop.cpucp_info.infineon_version); + if (hdev->asic_prop.cpucp_info.infineon_second_stage_version) + return sprintf(buf, "%#04x %#04x\n", + le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_version), + le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_second_stage_version)); + else + return sprintf(buf, "%#04x\n", + le32_to_cpu(hdev->asic_prop.cpucp_info.infineon_version)); } static ssize_t fuse_ver_show(struct device *dev, struct device_attribute *attr, diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 078fb4bd0316..0114cb52faad 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 * - * Copyright 2021 HabanaLabs, Ltd. + * Copyright 2020-2021 HabanaLabs, Ltd. * All Rights Reserved. * */ @@ -761,6 +761,7 @@ struct cpucp_security_info { * @fuse_version: silicon production FUSE information. * @thermal_version: thermald S/W version. * @cpucp_version: CpuCP S/W version. + * @infineon_second_stage_version: Infineon 2nd stage DC-DC version. * @dram_size: available DRAM size. * @card_name: card name that will be displayed in HWMON subsystem on the host * @sec_info: security information @@ -770,6 +771,10 @@ struct cpucp_security_info { * @dram_binning_mask: DRAM binning mask, 1 bit per dram instance * (0 = functional 1 = binned) * @memory_repair_flag: eFuse flag indicating memory repair + * @edma_binning_mask: EDMA binning mask, 1 bit per EDMA instance + * (0 = functional 1 = binned) + * @xbar_binning_mask: Xbar binning mask, 1 bit per Xbar instance + * (0 = functional 1 = binned) */ struct cpucp_info { struct cpucp_sensor sensors[CPUCP_MAX_SENSORS]; @@ -782,7 +787,7 @@ struct cpucp_info { __u8 fuse_version[VERSION_MAX_LEN]; __u8 thermal_version[VERSION_MAX_LEN]; __u8 cpucp_version[VERSION_MAX_LEN]; - __le32 reserved2; + __le32 infineon_second_stage_version; __le64 dram_size; char card_name[CARD_NAME_MAX_LEN]; __le64 reserved3; @@ -790,7 +795,9 @@ struct cpucp_info { __u8 reserved5; __u8 dram_binning_mask; __u8 memory_repair_flag; - __u8 pad[5]; + __u8 edma_binning_mask; + __u8 xbar_binning_mask; + __u8 pad[3]; struct cpucp_security_info sec_info; __le32 reserved6; __u8 pll_map[PLL_MAP_LEN]; -- cgit v1.2.3 From 9993f27de104d8d0f83c332ec5bc7642de20fae4 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Sun, 12 Dec 2021 17:46:21 +0200 Subject: habanalabs: expose soft reset sysfs nodes for inference ASIC As we allow soft-reset to be performed only on inference devices, having the sysfs nodes may cause a confusion. Hence, we remove those nodes on training ASICs. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/sysfs.c | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 6f575032f675..2f6de734ce37 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -424,8 +424,6 @@ static struct attribute *hl_dev_attrs[] = { &dev_attr_max_power.attr, &dev_attr_pci_addr.attr, &dev_attr_preboot_btl_ver.attr, - &dev_attr_soft_reset.attr, - &dev_attr_soft_reset_cnt.attr, &dev_attr_status.attr, &dev_attr_thermal_ver.attr, &dev_attr_uboot_ver.attr, @@ -450,6 +448,21 @@ static const struct attribute_group *hl_dev_attr_groups[] = { NULL, }; +static struct attribute *hl_dev_inference_attrs[] = { + &dev_attr_soft_reset.attr, + &dev_attr_soft_reset_cnt.attr, + NULL, +}; + +static struct attribute_group hl_dev_inference_attr_group = { + .attrs = hl_dev_inference_attrs, +}; + +static const struct attribute_group *hl_dev_inference_attr_groups[] = { + &hl_dev_inference_attr_group, + NULL, +}; + int hl_sysfs_init(struct hl_device *hdev) { int rc; @@ -465,10 +478,25 @@ int hl_sysfs_init(struct hl_device *hdev) return rc; } + if (!hdev->allow_inference_soft_reset) + return 0; + + rc = device_add_groups(hdev->dev, hl_dev_inference_attr_groups); + if (rc) { + dev_err(hdev->dev, + "Failed to add groups to device, error %d\n", rc); + return rc; + } + return 0; } void hl_sysfs_fini(struct hl_device *hdev) { device_remove_groups(hdev->dev, hl_dev_attr_groups); + + if (!hdev->allow_inference_soft_reset) + return; + + device_remove_groups(hdev->dev, hl_dev_inference_attr_groups); } -- cgit v1.2.3 From d636a932b3ab96523fe09c6148a0fa01f938b4f6 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Wed, 8 Dec 2021 09:06:03 +0200 Subject: habanalabs: clean MMU headers definitions During the MMU development the MMU header files were left with unclean definitions: - MMU "version specific" definitions that were left in the mmu_general file - unused definitions This patch attempts, where possible, to keep definitions that can serve multiple MMU versions (but that are not tightly bound with specific MMU arch) in the mmu_general header file (e.g. different definitions for number of HOPs). Otherwise, move MMU version specific definitions (e.g. HOPs masks and shifts) to the specific MMU version file. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/mmu/mmu_v1.c | 8 ++++---- drivers/misc/habanalabs/gaudi/gaudi.c | 24 +++++++++++----------- drivers/misc/habanalabs/goya/goya.c | 24 +++++++++++----------- .../habanalabs/include/hw_ip/mmu/mmu_general.h | 19 +++++------------ .../misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h | 18 +++++++++++++--- .../misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h | 20 ++++++++++++++---- 6 files changed, 64 insertions(+), 49 deletions(-) diff --git a/drivers/misc/habanalabs/common/mmu/mmu_v1.c b/drivers/misc/habanalabs/common/mmu/mmu_v1.c index 159da2fafd79..6134b6ae7615 100644 --- a/drivers/misc/habanalabs/common/mmu/mmu_v1.c +++ b/drivers/misc/habanalabs/common/mmu/mmu_v1.c @@ -269,7 +269,7 @@ static int dram_default_mapping_init(struct hl_ctx *ctx) num_of_hop3 = prop->dram_size_for_default_page_mapping; do_div(num_of_hop3, prop->dram_page_size); - do_div(num_of_hop3, PTE_ENTRIES_IN_HOP); + do_div(num_of_hop3, HOP_PTE_ENTRIES_512); /* add hop1 and hop2 */ total_hops = num_of_hop3 + 2; @@ -330,7 +330,7 @@ static int dram_default_mapping_init(struct hl_ctx *ctx) for (i = 0 ; i < num_of_hop3 ; i++) { hop3_pte_addr = ctx->dram_default_hops[i]; - for (j = 0 ; j < PTE_ENTRIES_IN_HOP ; j++) { + for (j = 0 ; j < HOP_PTE_ENTRIES_512 ; j++) { write_final_pte(ctx, hop3_pte_addr, pte_val); get_pte(ctx, ctx->dram_default_hops[i]); hop3_pte_addr += HL_PTE_SIZE; @@ -369,7 +369,7 @@ static void dram_default_mapping_fini(struct hl_ctx *ctx) num_of_hop3 = prop->dram_size_for_default_page_mapping; do_div(num_of_hop3, prop->dram_page_size); - do_div(num_of_hop3, PTE_ENTRIES_IN_HOP); + do_div(num_of_hop3, HOP_PTE_ENTRIES_512); hop0_addr = get_hop0_addr(ctx); /* add hop1 and hop2 */ @@ -379,7 +379,7 @@ static void dram_default_mapping_fini(struct hl_ctx *ctx) for (i = 0 ; i < num_of_hop3 ; i++) { hop3_pte_addr = ctx->dram_default_hops[i]; - for (j = 0 ; j < PTE_ENTRIES_IN_HOP ; j++) { + for (j = 0 ; j < HOP_PTE_ENTRIES_512 ; j++) { clear_pte(ctx, hop3_pte_addr); put_pte(ctx, ctx->dram_default_hops[i]); hop3_pte_addr += HL_PTE_SIZE; diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index 07e03d44930e..b3431eac4f04 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -593,21 +593,21 @@ static int gaudi_set_fixed_properties(struct hl_device *hdev) else prop->mmu_pgt_size = MMU_PAGE_TABLES_SIZE; prop->mmu_pte_size = HL_PTE_SIZE; - prop->mmu_hop_table_size = HOP_TABLE_SIZE; - prop->mmu_hop0_tables_total_size = HOP0_TABLES_TOTAL_SIZE; + prop->mmu_hop_table_size = HOP_TABLE_SIZE_512_PTE; + prop->mmu_hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE; prop->dram_page_size = PAGE_SIZE_2MB; prop->dram_supports_virtual_memory = false; - prop->pmmu.hop0_shift = HOP0_SHIFT; - prop->pmmu.hop1_shift = HOP1_SHIFT; - prop->pmmu.hop2_shift = HOP2_SHIFT; - prop->pmmu.hop3_shift = HOP3_SHIFT; - prop->pmmu.hop4_shift = HOP4_SHIFT; - prop->pmmu.hop0_mask = HOP0_MASK; - prop->pmmu.hop1_mask = HOP1_MASK; - prop->pmmu.hop2_mask = HOP2_MASK; - prop->pmmu.hop3_mask = HOP3_MASK; - prop->pmmu.hop4_mask = HOP4_MASK; + prop->pmmu.hop0_shift = MMU_V1_1_HOP0_SHIFT; + prop->pmmu.hop1_shift = MMU_V1_1_HOP1_SHIFT; + prop->pmmu.hop2_shift = MMU_V1_1_HOP2_SHIFT; + prop->pmmu.hop3_shift = MMU_V1_1_HOP3_SHIFT; + prop->pmmu.hop4_shift = MMU_V1_1_HOP4_SHIFT; + prop->pmmu.hop0_mask = MMU_V1_1_HOP0_MASK; + prop->pmmu.hop1_mask = MMU_V1_1_HOP1_MASK; + prop->pmmu.hop2_mask = MMU_V1_1_HOP2_MASK; + prop->pmmu.hop3_mask = MMU_V1_1_HOP3_MASK; + prop->pmmu.hop4_mask = MMU_V1_1_HOP4_MASK; prop->pmmu.start_addr = VA_HOST_SPACE_START; prop->pmmu.end_addr = (VA_HOST_SPACE_START + VA_HOST_SPACE_SIZE / 2) - 1; diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index 8d0f2cd608fc..f4473013f1ee 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -410,21 +410,21 @@ int goya_set_fixed_properties(struct hl_device *hdev) else prop->mmu_pgt_size = MMU_PAGE_TABLES_SIZE; prop->mmu_pte_size = HL_PTE_SIZE; - prop->mmu_hop_table_size = HOP_TABLE_SIZE; - prop->mmu_hop0_tables_total_size = HOP0_TABLES_TOTAL_SIZE; + prop->mmu_hop_table_size = HOP_TABLE_SIZE_512_PTE; + prop->mmu_hop0_tables_total_size = HOP0_512_PTE_TABLES_TOTAL_SIZE; prop->dram_page_size = PAGE_SIZE_2MB; prop->dram_supports_virtual_memory = true; - prop->dmmu.hop0_shift = HOP0_SHIFT; - prop->dmmu.hop1_shift = HOP1_SHIFT; - prop->dmmu.hop2_shift = HOP2_SHIFT; - prop->dmmu.hop3_shift = HOP3_SHIFT; - prop->dmmu.hop4_shift = HOP4_SHIFT; - prop->dmmu.hop0_mask = HOP0_MASK; - prop->dmmu.hop1_mask = HOP1_MASK; - prop->dmmu.hop2_mask = HOP2_MASK; - prop->dmmu.hop3_mask = HOP3_MASK; - prop->dmmu.hop4_mask = HOP4_MASK; + prop->dmmu.hop0_shift = MMU_V1_0_HOP0_SHIFT; + prop->dmmu.hop1_shift = MMU_V1_0_HOP1_SHIFT; + prop->dmmu.hop2_shift = MMU_V1_0_HOP2_SHIFT; + prop->dmmu.hop3_shift = MMU_V1_0_HOP3_SHIFT; + prop->dmmu.hop4_shift = MMU_V1_0_HOP4_SHIFT; + prop->dmmu.hop0_mask = MMU_V1_0_HOP0_MASK; + prop->dmmu.hop1_mask = MMU_V1_0_HOP1_MASK; + prop->dmmu.hop2_mask = MMU_V1_0_HOP2_MASK; + prop->dmmu.hop3_mask = MMU_V1_0_HOP3_MASK; + prop->dmmu.hop4_mask = MMU_V1_0_HOP4_MASK; prop->dmmu.start_addr = VA_DDR_SPACE_START; prop->dmmu.end_addr = VA_DDR_SPACE_END; prop->dmmu.page_size = PAGE_SIZE_2MB; diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h index dedf20e8f956..758f246627f8 100644 --- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h +++ b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_general.h @@ -16,27 +16,18 @@ #define PAGE_PRESENT_MASK 0x0000000000001ull #define SWAP_OUT_MASK 0x0000000000004ull #define LAST_MASK 0x0000000000800ull -#define HOP0_MASK 0x3000000000000ull -#define HOP1_MASK 0x0FF8000000000ull -#define HOP2_MASK 0x0007FC0000000ull -#define HOP3_MASK 0x000003FE00000ull -#define HOP4_MASK 0x00000001FF000ull #define FLAGS_MASK 0x0000000000FFFull -#define HOP0_SHIFT 48 -#define HOP1_SHIFT 39 -#define HOP2_SHIFT 30 -#define HOP3_SHIFT 21 -#define HOP4_SHIFT 12 - #define MMU_ARCH_5_HOPS 5 #define HOP_PHYS_ADDR_MASK (~FLAGS_MASK) #define HL_PTE_SIZE sizeof(u64) -#define HOP_TABLE_SIZE PAGE_SIZE_4KB -#define PTE_ENTRIES_IN_HOP (HOP_TABLE_SIZE / HL_PTE_SIZE) -#define HOP0_TABLES_TOTAL_SIZE (HOP_TABLE_SIZE * MAX_ASID) + +/* definitions for HOP with 512 PTE entries */ +#define HOP_PTE_ENTRIES_512 512 +#define HOP_TABLE_SIZE_512_PTE (HOP_PTE_ENTRIES_512 * HL_PTE_SIZE) +#define HOP0_512_PTE_TABLES_TOTAL_SIZE (HOP_TABLE_SIZE_512_PTE * MAX_ASID) #define MMU_HOP0_PA43_12_SHIFT 12 #define MMU_HOP0_PA49_44_SHIFT (12 + 32) diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h index 8539dd041f2c..86511002e367 100644 --- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h +++ b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_0.h @@ -8,8 +8,20 @@ #ifndef INCLUDE_MMU_V1_0_H_ #define INCLUDE_MMU_V1_0_H_ -#define MMU_HOP0_PA43_12 0x490004 -#define MMU_HOP0_PA49_44 0x490008 -#define MMU_ASID_BUSY 0x490000 +#define MMU_V1_0_HOP0_MASK 0x3000000000000ull +#define MMU_V1_0_HOP1_MASK 0x0FF8000000000ull +#define MMU_V1_0_HOP2_MASK 0x0007FC0000000ull +#define MMU_V1_0_HOP3_MASK 0x000003FE00000ull +#define MMU_V1_0_HOP4_MASK 0x00000001FF000ull + +#define MMU_V1_0_HOP0_SHIFT 48 +#define MMU_V1_0_HOP1_SHIFT 39 +#define MMU_V1_0_HOP2_SHIFT 30 +#define MMU_V1_0_HOP3_SHIFT 21 +#define MMU_V1_0_HOP4_SHIFT 12 + +#define MMU_HOP0_PA43_12 0x490004 +#define MMU_HOP0_PA49_44 0x490008 +#define MMU_ASID_BUSY 0x490000 #endif /* INCLUDE_MMU_V1_0_H_ */ diff --git a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h index b2a9570583ac..9c727a5d47b4 100644 --- a/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h +++ b/drivers/misc/habanalabs/include/hw_ip/mmu/mmu_v1_1.h @@ -8,9 +8,21 @@ #ifndef INCLUDE_MMU_V1_1_H_ #define INCLUDE_MMU_V1_1_H_ -#define MMU_ASID 0xC12004 -#define MMU_HOP0_PA43_12 0xC12008 -#define MMU_HOP0_PA49_44 0xC1200C -#define MMU_BUSY 0xC12000 +#define MMU_V1_1_HOP0_MASK 0x3000000000000ull +#define MMU_V1_1_HOP1_MASK 0x0FF8000000000ull +#define MMU_V1_1_HOP2_MASK 0x0007FC0000000ull +#define MMU_V1_1_HOP3_MASK 0x000003FE00000ull +#define MMU_V1_1_HOP4_MASK 0x00000001FF000ull + +#define MMU_V1_1_HOP0_SHIFT 48 +#define MMU_V1_1_HOP1_SHIFT 39 +#define MMU_V1_1_HOP2_SHIFT 30 +#define MMU_V1_1_HOP3_SHIFT 21 +#define MMU_V1_1_HOP4_SHIFT 12 + +#define MMU_ASID 0xC12004 +#define MMU_HOP0_PA43_12 0xC12008 +#define MMU_HOP0_PA49_44 0xC1200C +#define MMU_BUSY 0xC12000 #endif /* INCLUDE_MMU_V1_1_H_ */ -- cgit v1.2.3 From 86c00b2c3639e33a7b51a06b1ebff0bae87686b7 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Mon, 13 Dec 2021 15:43:06 +0200 Subject: habanalabs: modify cpu boot status error print As BTL can be replaced by ROM we should modify relevant error print. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 34e70cca37c1..1d0d228d4872 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1113,7 +1113,7 @@ static void detect_cpu_boot_status(struct hl_device *hdev, u32 status) switch (status) { case CPU_BOOT_STATUS_NA: dev_err(hdev->dev, - "Device boot progress - BTL did NOT run\n"); + "Device boot progress - BTL/ROM did NOT run\n"); break; case CPU_BOOT_STATUS_IN_WFE: dev_err(hdev->dev, -- cgit v1.2.3 From e2558f0f84d85bfe2407b91d57798f133d8ad32a Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Tue, 7 Dec 2021 14:30:20 +0200 Subject: habanalabs: prevent wait if CS in multi-CS list completed By the original design we assumed that if we "miss" multi CS completion it is of no severe consequence as we'll just call wait_for_multi_cs again. Sequence of events for such scenario: 1. user submit CS with sequence N 2. user calls wait for multi-CS with only CS #N in the list 3. the multi CS call starts with poll of the CSs but find that none completed (while CS #N did not completed yet) 4. now, multi CS #N complete but multi CS CTX was not yet created for the above multi-CS. so, attempt to complete multi-CS fails (as no multi CS CTX exist) 5. wait_for_multi_cs call now does init_wait_multi_cs_completion (and for this create the multi-CS CTX) 6. wait_for_multi_cs wits on completion but will not get one as CS #N already completed To fix the issue we initialize the multi-CS CTX prior polling the fences. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- .../misc/habanalabs/common/command_submission.c | 85 ++++++++++++++-------- drivers/misc/habanalabs/common/habanalabs.h | 3 - 2 files changed, 54 insertions(+), 34 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index f58fff3671d6..b9fed6b6d1ab 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -533,8 +533,8 @@ static void complete_multi_cs(struct hl_device *hdev, struct hl_cs *cs) mcs_compl->stream_master_qid_map)) { /* extract the timestamp only of first completed CS */ if (!mcs_compl->timestamp) - mcs_compl->timestamp = - ktime_to_ns(fence->timestamp); + mcs_compl->timestamp = ktime_to_ns(fence->timestamp); + complete_all(&mcs_compl->completion); /* @@ -2369,16 +2369,18 @@ static int hl_wait_for_fence(struct hl_ctx *ctx, u64 seq, struct hl_fence *fence * hl_cs_poll_fences - iterate CS fences to check for CS completion * * @mcs_data: multi-CS internal data + * @mcs_compl: multi-CS completion structure * * @return 0 on success, otherwise non 0 error code * * The function iterates on all CS sequence in the list and set bit in * completion_bitmap for each completed CS. - * while iterating, the function can extracts the stream map to be later - * used by the waiting function. - * this function shall be called after taking context ref + * While iterating, the function sets the stream map of each fence in the fence + * array in the completion QID stream map to be used by CSs to perform + * completion to the multi-CS context. + * This function shall be called after taking context ref */ -static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) +static int hl_cs_poll_fences(struct multi_cs_data *mcs_data, struct multi_cs_completion *mcs_compl) { struct hl_fence **fence_ptr = mcs_data->fence_arr; struct hl_device *hdev = mcs_data->ctx->hdev; @@ -2394,6 +2396,15 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) if (rc) return rc; + /* + * re-initialize the completion here to handle 2 possible cases: + * 1. CS will complete the multi-CS prior clearing the completion. in which + * case the fence iteration is guaranteed to catch the CS completion. + * 2. the completion will occur after re-init of the completion. + * in which case we will wake up immediately in wait_for_completion. + */ + reinit_completion(&mcs_compl->completion); + /* * set to maximum time to verify timestamp is valid: if at the end * this value is maintained- no timestamp was updated @@ -2404,6 +2415,21 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) for (i = 0; i < arr_len; i++, fence_ptr++) { struct hl_fence *fence = *fence_ptr; + /* + * In order to prevent case where we wait until timeout even though a CS associated + * with the multi-CS actually completed we do things in the below order: + * 1. for each fence set it's QID map in the multi-CS completion QID map. This way + * any CS can, potentially, complete the multi CS for the specific QID (note + * that once completion is initialized, calling complete* and then wait on the + * completion will cause it to return at once) + * 2. only after allowing multi-CS completion for the specific QID we check whether + * the specific CS already completed (and thus the wait for completion part will + * be skipped). if the CS not completed it is guaranteed that completing CS will + * wake up the completion. + */ + if (fence) + mcs_compl->stream_master_qid_map |= fence->stream_master_qid_map; + /* * function won't sleep as it is called with timeout 0 (i.e. * poll the fence) @@ -2419,9 +2445,7 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data) switch (status) { case CS_WAIT_STATUS_BUSY: - /* CS did not finished, keep waiting on its QID*/ - mcs_data->stream_master_qid_map |= - fence->stream_master_qid_map; + /* CS did not finished, QID to wait on already stored */ break; case CS_WAIT_STATUS_COMPLETED: /* @@ -2519,9 +2543,7 @@ static inline unsigned long hl_usecs64_to_jiffies(const u64 usecs) * the function gets the first available completion (by marking it "used") * and initialize its values. */ -static struct multi_cs_completion *hl_wait_multi_cs_completion_init( - struct hl_device *hdev, - u8 stream_master_bitmap) +static struct multi_cs_completion *hl_wait_multi_cs_completion_init(struct hl_device *hdev) { struct multi_cs_completion *mcs_compl; int i; @@ -2533,8 +2555,11 @@ static struct multi_cs_completion *hl_wait_multi_cs_completion_init( if (!mcs_compl->used) { mcs_compl->used = 1; mcs_compl->timestamp = 0; - mcs_compl->stream_master_qid_map = stream_master_bitmap; - reinit_completion(&mcs_compl->completion); + /* + * init QID map to 0 to avoid completion by CSs. the actual QID map + * to multi-CS CSs will be set incrementally at a later stage + */ + mcs_compl->stream_master_qid_map = 0; spin_unlock(&mcs_compl->lock); break; } @@ -2672,9 +2697,17 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) hl_ctx_get(hdev, ctx); + /* wait (with timeout) for the first CS to be completed */ + mcs_data.timeout_jiffies = hl_usecs64_to_jiffies(args->in.timeout_us); + mcs_compl = hl_wait_multi_cs_completion_init(hdev); + if (IS_ERR(mcs_compl)) { + rc = PTR_ERR(mcs_compl); + goto put_ctx; + } + /* poll all CS fences, extract timestamp */ mcs_data.update_ts = true; - rc = hl_cs_poll_fences(&mcs_data); + rc = hl_cs_poll_fences(&mcs_data, mcs_compl); /* * skip wait for CS completion when one of the below is true: * - an error on the poll function @@ -2682,16 +2715,7 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) * - the user called ioctl with timeout 0 */ if (rc || mcs_data.completion_bitmap || !args->in.timeout_us) - goto put_ctx; - - /* wait (with timeout) for the first CS to be completed */ - mcs_data.timeout_jiffies = hl_usecs64_to_jiffies(args->in.timeout_us); - - mcs_compl = hl_wait_multi_cs_completion_init(hdev, mcs_data.stream_master_qid_map); - if (IS_ERR(mcs_compl)) { - rc = PTR_ERR(mcs_compl); - goto put_ctx; - } + goto completion_fini; while (true) { rc = hl_wait_multi_cs_completion(&mcs_data, mcs_compl); @@ -2703,7 +2727,7 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) * no timestamp should be updated this time. */ mcs_data.update_ts = false; - rc = hl_cs_poll_fences(&mcs_data); + rc = hl_cs_poll_fences(&mcs_data, mcs_compl); if (mcs_data.completion_bitmap) break; @@ -2713,16 +2737,15 @@ static int hl_multi_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) * it got a completion) it either got completed by CS in the multi CS list * (in which case the indication will be non empty completion_bitmap) or it * got completed by CS submitted to one of the shared stream master but - * not in the multi CS list (in which case we should wait again but reinit - * the completion, modify the timeout and set timestamp as zero to let a CS - * related to the current multi-CS set a new, relevant, timestamp) + * not in the multi CS list (in which case we should wait again but modify + * the timeout and set timestamp as zero to let a CS related to the current + * multi-CS set a new, relevant, timestamp) */ - /* wait again with modified timeout */ mcs_data.timeout_jiffies = mcs_data.wait_status; - reinit_completion(&mcs_compl->completion); mcs_compl->timestamp = 0; } +completion_fini: hl_wait_multi_cs_completion_fini(mcs_compl); put_ctx: diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 015aa1ee8ce0..4d4986177776 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2364,8 +2364,6 @@ struct multi_cs_completion { * @timestamp: timestamp of first completed CS * @wait_status: wait for CS status * @completion_bitmap: bitmap of completed CSs (1- completed, otherwise 0) - * @stream_master_qid_map: bitmap of all stream master QIDs on which the - * multi-CS is waiting * @arr_len: fence_arr and seq_arr array length * @gone_cs: indication of gone CS (1- there was gone CS, otherwise 0) * @update_ts: update timestamp. 1- update the timestamp, otherwise 0. @@ -2378,7 +2376,6 @@ struct multi_cs_data { s64 timestamp; long wait_status; u32 completion_bitmap; - u32 stream_master_qid_map; u8 arr_len; u8 gone_cs; u8 update_ts; -- cgit v1.2.3 From b9d31cada7d9f137028c11534fff77fec8511690 Mon Sep 17 00:00:00 2001 From: farah kassabri Date: Tue, 2 Nov 2021 11:34:18 +0200 Subject: habanalabs: change wait_for_interrupt implementation Currently the cq counters are allocated in userspace memory, and mapped by the driver to the device address space. A new requirement that is part of new future API related to this one, requires that cq counters will be allocated in kernel memory. We leverage the existing cb_create API with KERNEL_MAPPED flag set to allocate this memory. That way we gain two things: 1. The memory cannot be freed while in use since it's protected by refcount in driver. 2. No need to wake up the user thread upon each interrupt from CQ, because the kernel has direct access to the counter. Therefore, it can make comparison with the target value in the interrupt handler and wake up the user thread only if the counter reaches the target value. This is instead of waking the thread up to copy counter value from user then go sleep again if target value wasn't reached. Signed-off-by: farah kassabri Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 31 ++++-- .../misc/habanalabs/common/command_submission.c | 111 ++++++++++++++++++++- drivers/misc/habanalabs/common/habanalabs.h | 5 + drivers/misc/habanalabs/common/irq.c | 8 +- include/uapi/misc/habanalabs.h | 61 +++++++---- 5 files changed, 189 insertions(+), 27 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index c591f0487272..d4eb9fb9ea12 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -380,8 +380,9 @@ int hl_cb_destroy(struct hl_device *hdev, struct hl_cb_mgr *mgr, u64 cb_handle) } static int hl_cb_info(struct hl_device *hdev, struct hl_cb_mgr *mgr, - u64 cb_handle, u32 *usage_cnt) + u64 cb_handle, u32 flags, u32 *usage_cnt, u64 *device_va) { + struct hl_vm_va_block *va_block; struct hl_cb *cb; u32 handle; int rc = 0; @@ -402,7 +403,18 @@ static int hl_cb_info(struct hl_device *hdev, struct hl_cb_mgr *mgr, goto out; } - *usage_cnt = atomic_read(&cb->cs_cnt); + if (flags & HL_CB_FLAGS_GET_DEVICE_VA) { + va_block = list_first_entry(&cb->va_block_list, struct hl_vm_va_block, node); + if (va_block) { + *device_va = va_block->start; + } else { + dev_err(hdev->dev, "CB is not mapped to the device's MMU\n"); + rc = -EINVAL; + goto out; + } + } else { + *usage_cnt = atomic_read(&cb->cs_cnt); + } out: spin_unlock(&mgr->cb_lock); @@ -414,7 +426,7 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) union hl_cb_args *args = data; struct hl_device *hdev = hpriv->hdev; enum hl_device_status status; - u64 handle = 0; + u64 handle = 0, device_va; u32 usage_cnt = 0; int rc; @@ -450,9 +462,16 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) case HL_CB_OP_INFO: rc = hl_cb_info(hdev, &hpriv->cb_mgr, args->in.cb_handle, - &usage_cnt); - memset(args, 0, sizeof(*args)); - args->out.usage_cnt = usage_cnt; + args->in.flags, + &usage_cnt, + &device_va); + + memset(&args->out, 0, sizeof(args->out)); + + if (args->in.flags & HL_CB_FLAGS_GET_DEVICE_VA) + args->out.device_va = device_va; + else + args->out.usage_cnt = usage_cnt; break; default: diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index b9fed6b6d1ab..7073fa6b9f0f 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2845,6 +2845,106 @@ static int hl_cs_wait_ioctl(struct hl_fpriv *hpriv, void *data) } static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, + struct hl_cb_mgr *cb_mgr, u64 timeout_us, + u64 cq_counters_handle, u64 cq_counters_offset, + u64 target_value, struct hl_user_interrupt *interrupt, + u32 *status, + u64 *timestamp) +{ + struct hl_user_pending_interrupt *pend; + unsigned long timeout, flags; + long completion_rc; + struct hl_cb *cb; + int rc = 0; + u32 handle; + + timeout = hl_usecs64_to_jiffies(timeout_us); + + hl_ctx_get(hdev, ctx); + + cq_counters_handle >>= PAGE_SHIFT; + handle = (u32) cq_counters_handle; + + cb = hl_cb_get(hdev, cb_mgr, handle); + if (!cb) { + hl_ctx_put(ctx); + return -EINVAL; + } + + pend = kzalloc(sizeof(*pend), GFP_KERNEL); + if (!pend) { + hl_cb_put(cb); + hl_ctx_put(ctx); + return -ENOMEM; + } + + hl_fence_init(&pend->fence, ULONG_MAX); + + pend->cq_kernel_addr = (u64 *) cb->kernel_address + cq_counters_offset; + pend->cq_target_value = target_value; + + /* We check for completion value as interrupt could have been received + * before we added the node to the wait list + */ + if (*pend->cq_kernel_addr >= target_value) { + *status = HL_WAIT_CS_STATUS_COMPLETED; + /* There was no interrupt, we assume the completion is now. */ + pend->fence.timestamp = ktime_get(); + } + + if (!timeout_us || (*status == HL_WAIT_CS_STATUS_COMPLETED)) + goto set_timestamp; + + /* Add pending user interrupt to relevant list for the interrupt + * handler to monitor + */ + spin_lock_irqsave(&interrupt->wait_list_lock, flags); + list_add_tail(&pend->wait_list_node, &interrupt->wait_list_head); + spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); + + /* Wait for interrupt handler to signal completion */ + completion_rc = wait_for_completion_interruptible_timeout(&pend->fence.completion, + timeout); + if (completion_rc > 0) { + *status = HL_WAIT_CS_STATUS_COMPLETED; + } else { + if (completion_rc == -ERESTARTSYS) { + dev_err_ratelimited(hdev->dev, + "user process got signal while waiting for interrupt ID %d\n", + interrupt->interrupt_id); + rc = -EINTR; + *status = HL_WAIT_CS_STATUS_ABORTED; + } else { + if (pend->fence.error == -EIO) { + dev_err_ratelimited(hdev->dev, + "interrupt based wait ioctl aborted(error:%d) due to a reset cycle initiated\n", + pend->fence.error); + rc = -EIO; + *status = HL_WAIT_CS_STATUS_ABORTED; + } else { + dev_err_ratelimited(hdev->dev, "Waiting for interrupt ID %d timedout\n", + interrupt->interrupt_id); + rc = -ETIMEDOUT; + } + *status = HL_WAIT_CS_STATUS_BUSY; + } + } + + spin_lock_irqsave(&interrupt->wait_list_lock, flags); + list_del(&pend->wait_list_node); + spin_unlock_irqrestore(&interrupt->wait_list_lock, flags); + +set_timestamp: + *timestamp = ktime_to_ns(pend->fence.timestamp); + + kfree(pend); + hl_cb_put(cb); + hl_ctx_put(ctx); + + return rc; +} + +static int _hl_interrupt_wait_ioctl_user_addr(struct hl_device *hdev, struct hl_ctx *ctx, u64 timeout_us, u64 user_address, u64 target_value, struct hl_user_interrupt *interrupt, @@ -2861,7 +2961,7 @@ static int _hl_interrupt_wait_ioctl(struct hl_device *hdev, struct hl_ctx *ctx, hl_ctx_get(hdev, ctx); - pend = kmalloc(sizeof(*pend), GFP_KERNEL); + pend = kzalloc(sizeof(*pend), GFP_KERNEL); if (!pend) { hl_ctx_put(ctx); return -ENOMEM; @@ -2990,7 +3090,14 @@ static int hl_interrupt_wait_ioctl(struct hl_fpriv *hpriv, void *data) else interrupt = &hdev->user_interrupt[interrupt_id - first_interrupt]; - rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, + if (args->in.flags & HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ) + rc = _hl_interrupt_wait_ioctl(hdev, hpriv->ctx, &hpriv->cb_mgr, + args->in.interrupt_timeout_us, args->in.cq_counters_handle, + args->in.cq_counters_offset, + args->in.target, interrupt, &status, + ×tamp); + else + rc = _hl_interrupt_wait_ioctl_user_addr(hdev, hpriv->ctx, args->in.interrupt_timeout_us, args->in.addr, args->in.target, interrupt, &status, ×tamp); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 4d4986177776..78772fe548b9 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -876,10 +876,15 @@ struct hl_user_interrupt { * pending on an interrupt * @wait_list_node: node in the list of user threads pending on an interrupt * @fence: hl fence object for interrupt completion + * @cq_target_value: CQ target value + * @cq_kernel_addr: CQ kernel address, to be used in the cq interrupt + * handler for taget value comparison */ struct hl_user_pending_interrupt { struct list_head wait_list_node; struct hl_fence fence; + u64 cq_target_value; + u64 *cq_kernel_addr; }; /** diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 64e0d9de21bd..6454ea12bf3a 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -145,8 +145,12 @@ static void handle_user_cq(struct hl_device *hdev, spin_lock(&user_cq->wait_list_lock); list_for_each_entry(pend, &user_cq->wait_list_head, wait_list_node) { - pend->fence.timestamp = now; - complete_all(&pend->fence.completion); + if ((pend->cq_kernel_addr && + *(pend->cq_kernel_addr) >= pend->cq_target_value) || + !pend->cq_kernel_addr) { + pend->fence.timestamp = now; + complete_all(&pend->fence.completion); + } } spin_unlock(&user_cq->wait_list_lock); } diff --git a/include/uapi/misc/habanalabs.h b/include/uapi/misc/habanalabs.h index 648850b954a3..371dfc4243b3 100644 --- a/include/uapi/misc/habanalabs.h +++ b/include/uapi/misc/habanalabs.h @@ -680,7 +680,10 @@ struct hl_info_args { #define HL_MAX_CB_SIZE (0x200000 - 32) /* Indicates whether the command buffer should be mapped to the device's MMU */ -#define HL_CB_FLAGS_MAP 0x1 +#define HL_CB_FLAGS_MAP 0x1 + +/* Used with HL_CB_OP_INFO opcode to get the device va address for kernel mapped CB */ +#define HL_CB_FLAGS_GET_DEVICE_VA 0x2 struct hl_cb_in { /* Handle of CB or 0 if we want to create one */ @@ -702,11 +705,16 @@ struct hl_cb_out { /* Handle of CB */ __u64 cb_handle; - /* Information about CB */ - struct { - /* Usage count of CB */ - __u32 usage_cnt; - __u32 pad; + union { + /* Information about CB */ + struct { + /* Usage count of CB */ + __u32 usage_cnt; + __u32 pad; + }; + + /* CB mapped address to device MMU */ + __u64 device_va; }; }; }; @@ -947,9 +955,10 @@ union hl_cs_args { struct hl_cs_out out; }; -#define HL_WAIT_CS_FLAGS_INTERRUPT 0x2 -#define HL_WAIT_CS_FLAGS_INTERRUPT_MASK 0xFFF00000 -#define HL_WAIT_CS_FLAGS_MULTI_CS 0x4 +#define HL_WAIT_CS_FLAGS_INTERRUPT 0x2 +#define HL_WAIT_CS_FLAGS_INTERRUPT_MASK 0xFFF00000 +#define HL_WAIT_CS_FLAGS_MULTI_CS 0x4 +#define HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ 0x10 #define HL_WAIT_MULTI_CS_LIST_MAX_LEN 32 @@ -969,14 +978,23 @@ struct hl_wait_cs_in { }; struct { - /* User address for completion comparison. - * upon interrupt, driver will compare the value pointed - * by this address with the supplied target value. - * in order not to perform any comparison, set address - * to all 1s. - * Relevant only when HL_WAIT_CS_FLAGS_INTERRUPT is set - */ - __u64 addr; + union { + /* User address for completion comparison. + * upon interrupt, driver will compare the value pointed + * by this address with the supplied target value. + * in order not to perform any comparison, set address + * to all 1s. + * Relevant only when HL_WAIT_CS_FLAGS_INTERRUPT is set + */ + __u64 addr; + + /* cq_counters_handle to a kernel mapped cb which contains + * cq counters. + * Relevant only when HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ is set + */ + __u64 cq_counters_handle; + }; + /* Target value for completion comparison */ __u64 target; }; @@ -1004,6 +1022,15 @@ struct hl_wait_cs_in { */ __u64 interrupt_timeout_us; }; + + /* + * cq counter offset inside the counters cb pointed by cq_counters_handle above. + * upon interrupt, driver will compare the value pointed + * by this address (cq_counters_handle + cq_counters_offset) + * with the supplied target value. + * relevant only when HL_WAIT_CS_FLAGS_INTERRUPT_KERNEL_CQ is set + */ + __u64 cq_counters_offset; }; #define HL_WAIT_CS_STATUS_COMPLETED 0 -- cgit v1.2.3 From a7224c21161b3576cb6875ac86f5ba5e757e4fce Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Wed, 15 Dec 2021 14:48:27 +0200 Subject: habanalabs: fix endianness when reading cpld version Current sysfs implementation does not take endianness into consideration when dumping the cpld version. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 2f6de734ce37..1af568e46f46 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -139,7 +139,7 @@ static ssize_t cpld_ver_show(struct device *dev, struct device_attribute *attr, struct hl_device *hdev = dev_get_drvdata(dev); return sprintf(buf, "0x%08x\n", - hdev->asic_prop.cpucp_info.cpld_version); + le32_to_cpu(hdev->asic_prop.cpucp_info.cpld_version)); } static ssize_t cpucp_kernel_ver_show(struct device *dev, -- cgit v1.2.3 From 0a63ac769b4cb79dfe68efd06528e9174fb88162 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Sun, 19 Dec 2021 11:38:01 +0200 Subject: habanalabs: fix comments according to kernel-doc Fix missing fields, descriptions not according to kernel-doc style. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/memory.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index 315594e96dcd..e5f7b23cbf94 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -518,7 +518,7 @@ static int add_va_block_locked(struct hl_device *hdev, /** * add_va_block() - wrapper for add_va_block_locked. * @hdev: pointer to the habanalabs device structure. - * @va_list: pointer to the virtual addresses block list. + * @va_range: pointer to the virtual addresses range object. * @start: start virtual address. * @end: end virtual address. * @@ -538,8 +538,11 @@ static inline int add_va_block(struct hl_device *hdev, } /** - * is_hint_crossing_range() - check if hint address crossing specified reserved - * range. + * is_hint_crossing_range() - check if hint address crossing specified reserved. + * @range_type: virtual space range type. + * @start_addr: start virtual address. + * @size: block size. + * @prop: asic properties structure to retrieve reserved ranges from. */ static inline bool is_hint_crossing_range(enum hl_va_range_type range_type, u64 start_addr, u32 size, struct asic_fixed_properties *prop) { @@ -749,6 +752,7 @@ u64 hl_reserve_va_block(struct hl_device *hdev, struct hl_ctx *ctx, /** * hl_get_va_range_type() - get va_range type for the given address and size. + * @ctx: context to fetch va_range from. * @address: the start address of the area we want to validate. * @size: the size in bytes of the area we want to validate. * @type: returned va_range type. @@ -776,8 +780,8 @@ static int hl_get_va_range_type(struct hl_ctx *ctx, u64 address, u64 size, * hl_unreserve_va_block() - wrapper for add_va_block to unreserve a va block. * @hdev: pointer to the habanalabs device structure * @ctx: pointer to the context structure. - * @start: start virtual address. - * @end: end virtual address. + * @start_addr: start virtual address. + * @size: number of bytes to unreserve. * * This function does the following: * - Takes the list lock and calls add_va_block_locked. @@ -2329,6 +2333,8 @@ void hl_userptr_delete_list(struct hl_device *hdev, /** * hl_userptr_is_pinned() - returns whether the given userptr is pinned. * @hdev: pointer to the habanalabs device structure. + * @addr: user address to check. + * @size: user block size to check. * @userptr_list: pointer to the list to clear. * @userptr: pointer to userptr to check. * @@ -2351,9 +2357,10 @@ bool hl_userptr_is_pinned(struct hl_device *hdev, u64 addr, /** * va_range_init() - initialize virtual addresses range. * @hdev: pointer to the habanalabs device structure. - * @va_range: pointer to the range to initialize. + * @va_ranges: pointer to va_ranges array. * @start: range start address. * @end: range end address. + * @page_size: page size for this va_range. * * This function does the following: * - Initializes the virtual addresses list of the given range with the given @@ -2410,7 +2417,7 @@ static int va_range_init(struct hl_device *hdev, struct hl_va_range *va_range, /** * va_range_fini() - clear a virtual addresses range. * @hdev: pointer to the habanalabs structure. - * va_range: pointer to virtual addresses rang.e + * @va_range: pointer to virtual addresses range. * * This function does the following: * - Frees the virtual addresses block list and its lock. @@ -2430,12 +2437,15 @@ static void va_range_fini(struct hl_device *hdev, struct hl_va_range *va_range) * @ctx: pointer to the habanalabs context structure. * @host_range_start: host virtual addresses range start. * @host_range_end: host virtual addresses range end. + * @host_page_size: host page size. * @host_huge_range_start: host virtual addresses range start for memory * allocated with huge pages. * @host_huge_range_end: host virtual addresses range end for memory allocated * with huge pages. + * @host_huge_page_size: host huge page size. * @dram_range_start: dram virtual addresses range start. * @dram_range_end: dram virtual addresses range end. + * @dram_page_size: dram page size. * * This function initializes the following: * - MMU for context. -- cgit v1.2.3 From 519f4ed0a09cdf3834c5cbde1416acd9a979a709 Mon Sep 17 00:00:00 2001 From: Oded Gabbay Date: Sun, 19 Dec 2021 16:06:59 +0200 Subject: habanalabs: replace some -ENOTTY with -EINVAL -ENOTTY is returned in case of error in the ioctl arguments themselves, such as function that doesn't exists. In all other cases, where the error is in the arguments of the custom data structures that we define that are passed in the various ioctls, we need to return -EINVAL. Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 2 +- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 4 ++-- drivers/misc/habanalabs/common/memory.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index d4eb9fb9ea12..e7534b5129fa 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -475,7 +475,7 @@ int hl_cb_ioctl(struct hl_fpriv *hpriv, void *data) break; default: - rc = -ENOTTY; + rc = -EINVAL; break; } diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index f571641c19ae..7ddf70a0ca8a 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -693,7 +693,7 @@ static int _hl_info_ioctl(struct hl_fpriv *hpriv, void *data, default: dev_err(dev, "Invalid request %d\n", args->op); - rc = -ENOTTY; + rc = -EINVAL; break; } @@ -748,7 +748,7 @@ static int hl_debug_ioctl(struct hl_fpriv *hpriv, void *data) default: dev_err(hdev->dev, "Invalid request %d\n", args->op); - rc = -ENOTTY; + rc = -EINVAL; break; } diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index e5f7b23cbf94..b8596846f3dc 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -2031,7 +2031,7 @@ static int mem_ioctl_no_mmu(struct hl_fpriv *hpriv, union hl_mem_args *args) default: dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); - rc = -ENOTTY; + rc = -EINVAL; break; } @@ -2156,7 +2156,7 @@ int hl_mem_ioctl(struct hl_fpriv *hpriv, void *data) default: dev_err(hdev->dev, "Unknown opcode for memory IOCTL\n"); - rc = -ENOTTY; + rc = -EINVAL; break; } -- cgit v1.2.3 From f297a0e9fe7d4b4d8a24d2ce97446f2faaf9d51b Mon Sep 17 00:00:00 2001 From: Tomer Tayar Date: Thu, 16 Dec 2021 16:31:18 +0200 Subject: habanalabs: add CPU-CP packet for engine core ASID cfg In some cases the driver cannot configure ASID of some engines due to the security level of the relevant registers. For this a new CPU-CP packet is introduced, which will allow the driver to ask the F/W to do this configuration instead. Signed-off-by: Tomer Tayar Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/firmware_if.c | 20 ++++++++++++++++++++ drivers/misc/habanalabs/common/habanalabs.h | 1 + drivers/misc/habanalabs/include/common/cpucp_if.h | 5 +++++ 3 files changed, 26 insertions(+) diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 1d0d228d4872..2cc2015c2416 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -1059,6 +1059,26 @@ out: return rc; } +int hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid) +{ + struct cpucp_packet pkt; + int rc; + + memset(&pkt, 0, sizeof(pkt)); + + pkt.ctl = cpu_to_le32(CPUCP_PACKET_ENGINE_CORE_ASID_SET << CPUCP_PKT_CTL_OPCODE_SHIFT); + pkt.value = cpu_to_le64(asid); + + rc = hdev->asic_funcs->send_cpu_message(hdev, (u32 *) &pkt, sizeof(pkt), + HL_CPUCP_INFO_TIMEOUT_USEC, NULL); + if (rc) + dev_err(hdev->dev, + "Failed on ASID configuration request for engine core, error %d\n", + rc); + + return rc; +} + void hl_fw_ask_hard_reset_without_linux(struct hl_device *hdev) { struct static_fw_load_mgr *static_loader = diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 78772fe548b9..fc1bdc07a169 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -3065,6 +3065,7 @@ int hl_fw_dynamic_send_protocol_cmd(struct hl_device *hdev, int hl_fw_dram_replaced_row_get(struct hl_device *hdev, struct cpucp_hbm_row_info *info); int hl_fw_dram_pending_row_get(struct hl_device *hdev, u32 *pend_rows_num); +int hl_fw_cpucp_engine_core_asid_set(struct hl_device *hdev, u32 asid); int hl_pci_bars_map(struct hl_device *hdev, const char * const name[3], bool is_wc[3]); int hl_pci_elbi_read(struct hl_device *hdev, u64 addr, u32 *data); diff --git a/drivers/misc/habanalabs/include/common/cpucp_if.h b/drivers/misc/habanalabs/include/common/cpucp_if.h index 0114cb52faad..737c39f33f05 100644 --- a/drivers/misc/habanalabs/include/common/cpucp_if.h +++ b/drivers/misc/habanalabs/include/common/cpucp_if.h @@ -386,6 +386,9 @@ enum pq_init_status { * * CPUCP_PACKET_POWER_SET - * Resets power history of device to 0 + * + * CPUCP_PACKET_ENGINE_CORE_ASID_SET - + * Packet to perform engine core ASID configuration */ enum cpucp_packet_id { @@ -434,6 +437,8 @@ enum cpucp_packet_id { CPUCP_PACKET_HBM_REPLACED_ROWS_INFO_GET,/* internal */ CPUCP_PACKET_HBM_PENDING_ROWS_STATUS, /* internal */ CPUCP_PACKET_POWER_SET, /* internal */ + CPUCP_PACKET_RESERVED, /* not used */ + CPUCP_PACKET_ENGINE_CORE_ASID_SET, /* internal */ }; #define CPUCP_PACKET_FENCE_VAL 0xFE8CE7A5 -- cgit v1.2.3 From 60bf3bfb5a37965fc33fa00f19a2074dd48077c5 Mon Sep 17 00:00:00 2001 From: Ohad Sharabi Date: Mon, 20 Dec 2021 13:30:35 +0200 Subject: habanalabs: handle skip multi-CS if handling not done This patch fixes issue in which we have timeout for multi-CS although the CS in the list actually completed. Example scenario (the two threads marked as WAIT for the thread that handles the wait_for_multi_cs and CMPL as the thread that signal completion for both CS and multi-CS): 1. Submit CS with sequence X 2. [WAIT]: call wait_for_multi_cs with single CS X 3. [CMPL]: CS X do invoke complete_all for both CS and multi-CS (multi_cs_completion_done still false) 4. [WAIT]: enter poll_fences, reinit the completion and find the CS as completed when asking on the fence but multi_cs_done is still false it returns that no CS actually completed 5. [CMPL]: set multi_cs_handling_done as true 6. [WAIT]: wait for completion but no CS to awake the wait context and hence wait till timeout Solution: if CS detected as completed in poll_fences but multi_cs_done is still false invoke complete_all to the multi-CS completion and so it will not go to sleep in wait_for_completion but rather will have a "second chance" to wait for multi_cs_completion_done. Signed-off-by: Ohad Sharabi Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_submission.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index 7073fa6b9f0f..d39343f90bc2 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -2453,9 +2453,19 @@ static int hl_cs_poll_fences(struct multi_cs_data *mcs_data, struct multi_cs_com * returns to user indicating CS completed before it finished * all of its mcs handling, to avoid race the next time the * user waits for mcs. + * note: when reaching this case fence is definitely not NULL + * but NULL check was added to overcome static analysis */ - if (!fence->mcs_handling_done) + if (fence && !fence->mcs_handling_done) { + /* + * in case multi CS is completed but MCS handling not done + * we "complete" the multi CS to prevent it from waiting + * until time-out and the "multi-CS handling done" will have + * another chance at the next iteration + */ + complete_all(&mcs_compl->completion); break; + } mcs_data->completion_bitmap |= BIT(i); /* -- cgit v1.2.3 From eb135291912f7554e2a2472befc44818098baa8d Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 23 Nov 2021 15:15:22 +0200 Subject: habanalabs: refactor reset information variables Unify variables related to device reset, which will help us to add some new reset functionality in future patches. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 2 +- .../misc/habanalabs/common/command_submission.c | 4 +- drivers/misc/habanalabs/common/debugfs.c | 18 ++--- drivers/misc/habanalabs/common/device.c | 76 +++++++++++---------- drivers/misc/habanalabs/common/firmware_if.c | 6 +- drivers/misc/habanalabs/common/habanalabs.h | 79 ++++++++++++---------- drivers/misc/habanalabs/common/habanalabs_drv.c | 4 +- drivers/misc/habanalabs/common/habanalabs_ioctl.c | 4 +- drivers/misc/habanalabs/common/irq.c | 2 +- drivers/misc/habanalabs/common/memory.c | 2 +- drivers/misc/habanalabs/common/sysfs.c | 10 +-- drivers/misc/habanalabs/gaudi/gaudi.c | 8 +-- drivers/misc/habanalabs/goya/goya.c | 10 +-- 13 files changed, 119 insertions(+), 106 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index e7534b5129fa..649380bb189f 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -250,7 +250,7 @@ int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, * Can't use generic function to check this because of special case * where we create a CB as part of the reset process */ - if ((hdev->disabled) || ((atomic_read(&hdev->in_reset)) && + if ((hdev->disabled) || ((atomic_read(&hdev->reset_info.in_reset)) && (ctx_id != HL_KERNEL_ASID_ID))) { dev_warn_ratelimited(hdev->dev, "Device is disabled or in reset. Can't create new CBs\n"); diff --git a/drivers/misc/habanalabs/common/command_submission.c b/drivers/misc/habanalabs/common/command_submission.c index d39343f90bc2..0a4ef13d9ac4 100644 --- a/drivers/misc/habanalabs/common/command_submission.c +++ b/drivers/misc/habanalabs/common/command_submission.c @@ -777,7 +777,7 @@ static void cs_timedout(struct work_struct *work) if (hdev->reset_on_lockup) hl_device_reset(hdev, HL_DRV_RESET_TDR); else - hdev->needs_reset = true; + hdev->reset_info.needs_reset = true; } } @@ -814,7 +814,7 @@ static int allocate_cs(struct hl_device *hdev, struct hl_ctx *ctx, cs->encaps_signals = !!(flags & HL_CS_FLAGS_ENCAP_SIGNALS); cs->timeout_jiffies = timeout; cs->skip_reset_on_timeout = - hdev->skip_reset_on_timeout || + hdev->reset_info.skip_reset_on_timeout || !!(flags & HL_CS_FLAGS_SKIP_RESET_ON_TIMEOUT); cs->submission_time_jiffies = jiffies; INIT_LIST_HEAD(&cs->job_list); diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 2e9c31d79d5e..746d1a18de63 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -527,7 +527,7 @@ static int engines_show(struct seq_file *s, void *data) struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_device *hdev = dev_entry->hdev; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't check device idle during reset\n"); return 0; @@ -658,7 +658,7 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf, ssize_t rc; u32 val; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); return 0; } @@ -694,7 +694,7 @@ static ssize_t hl_data_write32(struct file *f, const char __user *buf, u32 value; ssize_t rc; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); return 0; } @@ -731,7 +731,7 @@ static ssize_t hl_data_read64(struct file *f, char __user *buf, ssize_t rc; u64 val; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); return 0; } @@ -767,7 +767,7 @@ static ssize_t hl_data_write64(struct file *f, const char __user *buf, u64 value; ssize_t rc; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); return 0; } @@ -802,7 +802,7 @@ static ssize_t hl_dma_size_write(struct file *f, const char __user *buf, ssize_t rc; u32 size; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't DMA during reset\n"); return 0; } @@ -1077,7 +1077,7 @@ static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf, u64 value; ssize_t rc; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't change clock gating during reset\n"); return 0; @@ -1119,7 +1119,7 @@ static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf, u32 value; ssize_t rc; - if (atomic_read(&hdev->in_reset)) { + if (atomic_read(&hdev->reset_info.in_reset)) { dev_warn_ratelimited(hdev->dev, "Can't change stop on error during reset\n"); return 0; @@ -1497,7 +1497,7 @@ void hl_debugfs_add_device(struct hl_device *hdev) debugfs_create_x8("skip_reset_on_timeout", 0644, dev_entry->root, - &hdev->skip_reset_on_timeout); + &hdev->reset_info.skip_reset_on_timeout); debugfs_create_file("state_dump", 0600, diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index f1f482c5cdcb..f8f9eb7a934f 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -17,9 +17,9 @@ enum hl_device_status hl_device_status(struct hl_device *hdev) { enum hl_device_status status; - if (atomic_read(&hdev->in_reset)) + if (atomic_read(&hdev->reset_info.in_reset)) status = HL_DEVICE_STATUS_IN_RESET; - else if (hdev->needs_reset) + else if (hdev->reset_info.needs_reset) status = HL_DEVICE_STATUS_NEEDS_RESET; else if (hdev->disabled) status = HL_DEVICE_STATUS_MALFUNCTION; @@ -452,7 +452,7 @@ static int device_early_init(struct hl_device *hdev) INIT_LIST_HEAD(&hdev->fpriv_ctrl_list); mutex_init(&hdev->fpriv_list_lock); mutex_init(&hdev->fpriv_ctrl_list_lock); - atomic_set(&hdev->in_reset, 0); + atomic_set(&hdev->reset_info.in_reset, 0); mutex_init(&hdev->clk_throttling.lock); return 0; @@ -544,8 +544,8 @@ reschedule: * status for at least one heartbeat. From this point driver restarts * tracking future consecutive fatal errors. */ - if (!(atomic_read(&hdev->in_reset))) - hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; + if (!(atomic_read(&hdev->reset_info.in_reset))) + hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; schedule_delayed_work(&hdev->work_heartbeat, usecs_to_jiffies(HL_HEARTBEAT_PER_USEC)); @@ -639,12 +639,12 @@ int hl_device_set_debug_mode(struct hl_device *hdev, struct hl_ctx *ctx, bool en goto out; } - if (!hdev->hard_reset_pending) + if (!hdev->reset_info.hard_reset_pending) hdev->asic_funcs->halt_coresight(hdev, ctx); hdev->in_debug = 0; - if (!hdev->hard_reset_pending) + if (!hdev->reset_info.hard_reset_pending) hdev->asic_funcs->set_clock_gating(hdev); goto out; @@ -722,7 +722,7 @@ int hl_device_suspend(struct hl_device *hdev) pci_save_state(hdev->pdev); /* Block future CS/VM/JOB completion operations */ - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); if (rc) { dev_err(hdev->dev, "Can't suspend while in reset\n"); return -EIO; @@ -777,7 +777,7 @@ int hl_device_resume(struct hl_device *hdev) hdev->disabled = false; - atomic_set(&hdev->in_reset, 0); + atomic_set(&hdev->reset_info.in_reset, 0); rc = hl_device_reset(hdev, HL_DRV_RESET_HARD); if (rc) { @@ -906,16 +906,16 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) * 'reset_cause' will continue holding its 1st recorded reason! */ if (flags & HL_DRV_RESET_HEARTBEAT) { - hdev->curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT; + hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_HEARTBEAT; cur_reset_trigger = HL_DRV_RESET_HEARTBEAT; } else if (flags & HL_DRV_RESET_TDR) { - hdev->curr_reset_cause = HL_RESET_CAUSE_TDR; + hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_TDR; cur_reset_trigger = HL_DRV_RESET_TDR; } else if (flags & HL_DRV_RESET_FW_FATAL_ERR) { - hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; cur_reset_trigger = HL_DRV_RESET_FW_FATAL_ERR; } else { - hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; } /* @@ -923,11 +923,11 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) * is set and if this reset is due to a fatal FW error * device is set to an unstable state. */ - if (hdev->prev_reset_trigger != cur_reset_trigger) { - hdev->prev_reset_trigger = cur_reset_trigger; - hdev->reset_trigger_repeated = 0; + if (hdev->reset_info.prev_reset_trigger != cur_reset_trigger) { + hdev->reset_info.prev_reset_trigger = cur_reset_trigger; + hdev->reset_info.reset_trigger_repeated = 0; } else { - hdev->reset_trigger_repeated = 1; + hdev->reset_info.reset_trigger_repeated = 1; } /* If reset is due to heartbeat, device CPU is no responsive in @@ -987,7 +987,7 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) from_hard_reset_thread = !!(flags & HL_DRV_RESET_FROM_RESET_THR); fw_reset = !!(flags & HL_DRV_RESET_BYPASS_REQ_TO_FW); - if (!hard_reset && !hdev->supports_soft_reset) { + if (!hard_reset && !hdev->asic_prop.supports_soft_reset) { hard_instead_soft = true; hard_reset = true; } @@ -1004,7 +1004,7 @@ int hl_device_reset(struct hl_device *hdev, u32 flags) goto do_reset; } - if (!hard_reset && !hdev->allow_inference_soft_reset) { + if (!hard_reset && !hdev->asic_prop.allow_inference_soft_reset) { hard_instead_soft = true; hard_reset = true; } @@ -1024,13 +1024,14 @@ do_reset: */ if (!from_hard_reset_thread) { /* Block future CS/VM/JOB completion operations */ - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); if (rc) return 0; handle_reset_trigger(hdev, flags); - hdev->is_in_soft_reset = !hard_reset; + /* This still allows the completion of some KDMA ops */ + hdev->reset_info.is_in_soft_reset = !hard_reset; /* This also blocks future CS/VM/JOB completion operations */ hdev->disabled = true; @@ -1047,7 +1048,7 @@ do_reset: again: if ((hard_reset) && (!from_hard_reset_thread)) { - hdev->hard_reset_pending = true; + hdev->reset_info.hard_reset_pending = true; hdev->process_kill_trial_cnt = 0; @@ -1128,10 +1129,11 @@ kill_processes: if (hard_reset) { hdev->device_cpu_disabled = false; - hdev->hard_reset_pending = false; + hdev->reset_info.hard_reset_pending = false; - if (hdev->reset_trigger_repeated && - (hdev->prev_reset_trigger == HL_DRV_RESET_FW_FATAL_ERR)) { + if (hdev->reset_info.reset_trigger_repeated && + (hdev->reset_info.prev_reset_trigger == + HL_DRV_RESET_FW_FATAL_ERR)) { /* if there 2 back to back resets from FW, * ensure driver puts the driver in a unusable state */ @@ -1182,7 +1184,7 @@ kill_processes: * is required for the initialization itself */ hdev->disabled = false; - hdev->is_in_soft_reset = false; + hdev->reset_info.is_in_soft_reset = false; rc = hdev->asic_funcs->hw_init(hdev); if (rc) { @@ -1232,13 +1234,13 @@ kill_processes: } } - atomic_set(&hdev->in_reset, 0); - hdev->needs_reset = false; + atomic_set(&hdev->reset_info.in_reset, 0); + hdev->reset_info.needs_reset = false; dev_notice(hdev->dev, "Successfully finished resetting the device\n"); if (hard_reset) { - hdev->hard_reset_cnt++; + hdev->reset_info.hard_reset_cnt++; /* After reset is done, we are ready to receive events from * the F/W. We can't do it before because we will ignore events @@ -1247,30 +1249,30 @@ kill_processes: */ hdev->asic_funcs->enable_events_from_fw(hdev); } else if (!reset_upon_device_release) { - hdev->soft_reset_cnt++; + hdev->reset_info.soft_reset_cnt++; } return 0; out_err: hdev->disabled = true; - hdev->is_in_soft_reset = false; + hdev->reset_info.is_in_soft_reset = false; if (hard_reset) { dev_err(hdev->dev, "Failed to reset! Device is NOT usable\n"); - hdev->hard_reset_cnt++; + hdev->reset_info.hard_reset_cnt++; } else if (reset_upon_device_release) { dev_err(hdev->dev, "Failed to reset device after user release\n"); hard_reset = true; goto again; } else { dev_err(hdev->dev, "Failed to do soft-reset\n"); - hdev->soft_reset_cnt++; + hdev->reset_info.soft_reset_cnt++; hard_reset = true; goto again; } - atomic_set(&hdev->in_reset, 0); + atomic_set(&hdev->reset_info.in_reset, 0); return rc; } @@ -1604,10 +1606,10 @@ void hl_device_fini(struct hl_device *hdev) */ timeout = ktime_add_us(ktime_get(), reset_sec * 1000 * 1000); - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); while (rc) { usleep_range(50, 200); - rc = atomic_cmpxchg(&hdev->in_reset, 0, 1); + rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); if (ktime_compare(ktime_get(), timeout) > 0) { dev_crit(hdev->dev, "Failed to remove device because reset function did not finish\n"); @@ -1629,7 +1631,7 @@ void hl_device_fini(struct hl_device *hdev) take_release_locks(hdev); - hdev->hard_reset_pending = true; + hdev->reset_info.hard_reset_pending = true; hl_hwmon_fini(hdev); diff --git a/drivers/misc/habanalabs/common/firmware_if.c b/drivers/misc/habanalabs/common/firmware_if.c index 2cc2015c2416..6775c5c3166b 100644 --- a/drivers/misc/habanalabs/common/firmware_if.c +++ b/drivers/misc/habanalabs/common/firmware_if.c @@ -2371,14 +2371,14 @@ static int hl_fw_dynamic_init_cpu(struct hl_device *hdev, if (rc) goto protocol_err; - if (hdev->curr_reset_cause) { + if (hdev->reset_info.curr_reset_cause) { rc = hl_fw_dynamic_send_msg(hdev, fw_loader, - HL_COMMS_RESET_CAUSE_TYPE, &hdev->curr_reset_cause); + HL_COMMS_RESET_CAUSE_TYPE, &hdev->reset_info.curr_reset_cause); if (rc) goto protocol_err; /* Clear current reset cause */ - hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; } if (!(hdev->fw_components & FW_TYPE_BOOT_CPU)) { diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index fc1bdc07a169..47eaeff9e924 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -547,6 +547,13 @@ struct hl_hints_range { * false otherwise. * @use_get_power_for_reset_history: To support backward compatibility for Goya * and Gaudi + * @supports_soft_reset: is soft reset supported. + * @allow_inference_soft_reset: true if the ASIC supports soft reset that is + * initiated by user or TDR. This is only true + * in inference ASICs, as there is no real-world + * use-case of doing soft-reset in training (due + * to the fact that training runs on multiple + * devices) */ struct asic_fixed_properties { struct hw_queue_properties *hw_queues_props; @@ -628,6 +635,8 @@ struct asic_fixed_properties { u8 dynamic_fw_load; u8 gic_interrupts_enable; u8 use_get_power_for_reset_history; + u8 supports_soft_reset; + u8 allow_inference_soft_reset; }; /** @@ -2446,6 +2455,39 @@ struct last_error_session_info { u8 razwi_type; }; +/** + * struct hl_reset_info - holds current device reset information. + * @in_reset: is device in reset flow. + * @soft_reset_cnt: number of soft reset since the driver was loaded. + * @hard_reset_cnt: number of hard reset since the driver was loaded. + * @is_in_soft_reset: Device is currently in soft reset process. + * @needs_reset: true if reset_on_lockup is false and device should be reset + * due to lockup. + * @hard_reset_pending: is there a hard reset work pending. + * @curr_reset_cause: saves an enumerated reset cause when a hard reset is + * triggered, and cleared after it is shared with preboot. + * @prev_reset_trigger: saves the previous trigger which caused a reset, overidden + * with a new value on next reset + * @reset_trigger_repeated: set if device reset is triggered more than once with + * same cause. + * @skip_reset_on_timeout: Skip device reset if CS has timed out, wait for it to + * complete instead. + */ +struct hl_reset_info { + atomic_t in_reset; + u32 soft_reset_cnt; + u32 hard_reset_cnt; + u8 is_in_soft_reset; + u8 needs_reset; + u8 hard_reset_pending; + + u8 curr_reset_cause; + u8 prev_reset_trigger; + u8 reset_trigger_repeated; + + u8 skip_reset_on_timeout; +}; + /** * struct hl_device - habanalabs device structure. * @pdev: pointer to PCI device, can be NULL in case of simulator device. @@ -2514,6 +2556,7 @@ struct last_error_session_info { * @state_dump_specs: constants and dictionaries needed to dump system state. * @multi_cs_completion: array of multi-CS completion. * @clk_throttling: holds information about current/previous clock throttling events + * @reset_info: holds current device reset information. * @last_error: holds information about last session in which CS timeout or razwi error occurred. * @stream_master_qid_arr: pointer to array with QIDs of master streams. * @dram_used_mem: current DRAM memory consumption. @@ -2538,13 +2581,10 @@ struct last_error_session_info { * session. * @open_counter: number of successful device open operations. * @fw_poll_interval_usec: FW status poll interval in usec. - * @in_reset: is device in reset flow. * @card_type: Various ASICs have several card types. This indicates the card * type of the current device. * @major: habanalabs kernel driver major. * @high_pll: high PLL profile frequency. - * @soft_reset_cnt: number of soft reset since the driver was loaded. - * @hard_reset_cnt: number of hard reset since the driver was loaded. * @id: device minor. * @id_control: minor of the control device * @cpu_pci_msb_addr: 50-bit extension bits for the device CPU's 40-bit @@ -2552,7 +2592,6 @@ struct last_error_session_info { * @disabled: is device disabled. * @late_init_done: is late init stage was done during initialization. * @hwmon_initialized: is H/W monitor sensors was initialized. - * @hard_reset_pending: is there a hard reset work pending. * @heartbeat: is heartbeat sanity check towards CPU-CP enabled. * @reset_on_lockup: true if a reset should be done in case of stuck CS, false * otherwise. @@ -2575,35 +2614,17 @@ struct last_error_session_info { * @sync_stream_queue_idx: helper index for sync stream queues initialization. * @collective_mon_idx: helper index for collective initialization * @supports_coresight: is CoreSight supported. - * @supports_soft_reset: is soft reset supported. - * @allow_inference_soft_reset: true if the ASIC supports soft reset that is - * initiated by user or TDR. This is only true - * in inference ASICs, as there is no real-world - * use-case of doing soft-reset in training (due - * to the fact that training runs on multiple - * devices) * @supports_cb_mapping: is mapping a CB to the device's MMU supported. - * @needs_reset: true if reset_on_lockup is false and device should be reset - * due to lockup. * @process_kill_trial_cnt: number of trials reset thread tried killing * user processes * @device_fini_pending: true if device_fini was called and might be * waiting for the reset thread to finish * @supports_staged_submission: true if staged submissions are supported - * @curr_reset_cause: saves an enumerated reset cause when a hard reset is - * triggered, and cleared after it is shared with preboot. - * @prev_reset_trigger: saves the previous trigger which caused a reset, overidden - * with a new value on next reset - * @reset_trigger_repeated: set if device reset is triggered more than once with - * same cause. - * @skip_reset_on_timeout: Skip device reset if CS has timed out, wait for it to - * complete instead. * @device_cpu_is_halted: Flag to indicate whether the device CPU was already * halted. We can't halt it again because the COMMS * protocol will throw an error. Relevant only for * cases where Linux was not loaded to device CPU * @supports_wait_for_multi_cs: true if wait for multi CS is supported - * @is_in_soft_reset: Device is currently in soft reset process. * @is_compute_ctx_active: Whether there is an active compute context executing. */ struct hl_device { @@ -2678,6 +2699,8 @@ struct hl_device { struct hl_clk_throttle clk_throttling; struct last_error_session_info last_error; + struct hl_reset_info reset_info; + u32 *stream_master_qid_arr; atomic64_t dram_used_mem; u64 timeout_jiffies; @@ -2689,20 +2712,16 @@ struct hl_device { u64 last_open_session_duration_jif; u64 open_counter; u64 fw_poll_interval_usec; - atomic_t in_reset; ktime_t last_successful_open_ktime; enum cpucp_card_types card_type; u32 major; u32 high_pll; - u32 soft_reset_cnt; - u32 hard_reset_cnt; u16 id; u16 id_control; u16 cpu_pci_msb_addr; u8 disabled; u8 late_init_done; u8 hwmon_initialized; - u8 hard_reset_pending; u8 heartbeat; u8 reset_on_lockup; u8 dram_default_page_mapping; @@ -2719,21 +2738,13 @@ struct hl_device { u8 sync_stream_queue_idx; u8 collective_mon_idx; u8 supports_coresight; - u8 supports_soft_reset; - u8 allow_inference_soft_reset; u8 supports_cb_mapping; - u8 needs_reset; u8 process_kill_trial_cnt; u8 device_fini_pending; u8 supports_staged_submission; - u8 curr_reset_cause; - u8 prev_reset_trigger; - u8 reset_trigger_repeated; - u8 skip_reset_on_timeout; u8 device_cpu_is_halted; u8 supports_wait_for_multi_cs; u8 stream_master_qid_arr_size; - u8 is_in_soft_reset; u8 is_compute_ctx_active; /* Parameters for bring-up */ diff --git a/drivers/misc/habanalabs/common/habanalabs_drv.c b/drivers/misc/habanalabs/common/habanalabs_drv.c index aa4e07b1f839..690b763c7a95 100644 --- a/drivers/misc/habanalabs/common/habanalabs_drv.c +++ b/drivers/misc/habanalabs/common/habanalabs_drv.c @@ -289,8 +289,8 @@ static int fixup_device_params(struct hl_device *hdev) hdev->fw_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC; hdev->stop_on_err = true; - hdev->curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; - hdev->prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; + hdev->reset_info.curr_reset_cause = HL_RESET_CAUSE_UNKNOWN; + hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; /* Enable only after the initialization of the device */ hdev->disabled = true; diff --git a/drivers/misc/habanalabs/common/habanalabs_ioctl.c b/drivers/misc/habanalabs/common/habanalabs_ioctl.c index 7ddf70a0ca8a..3ba3a8ffda3e 100644 --- a/drivers/misc/habanalabs/common/habanalabs_ioctl.c +++ b/drivers/misc/habanalabs/common/habanalabs_ioctl.c @@ -269,8 +269,8 @@ static int get_reset_count(struct hl_device *hdev, struct hl_info_args *args) if ((!max_size) || (!out)) return -EINVAL; - reset_count.hard_reset_cnt = hdev->hard_reset_cnt; - reset_count.soft_reset_cnt = hdev->soft_reset_cnt; + reset_count.hard_reset_cnt = hdev->reset_info.hard_reset_cnt; + reset_count.soft_reset_cnt = hdev->reset_info.soft_reset_cnt; return copy_to_user(out, &reset_count, min((size_t) max_size, sizeof(reset_count))) ? -EFAULT : 0; diff --git a/drivers/misc/habanalabs/common/irq.c b/drivers/misc/habanalabs/common/irq.c index 6454ea12bf3a..1b6bdc900c26 100644 --- a/drivers/misc/habanalabs/common/irq.c +++ b/drivers/misc/habanalabs/common/irq.c @@ -249,7 +249,7 @@ irqreturn_t hl_irq_handler_eq(int irq, void *arg) */ dma_rmb(); - if (hdev->disabled && !hdev->is_in_soft_reset) { + if (hdev->disabled && !hdev->reset_info.is_in_soft_reset) { dev_warn(hdev->dev, "Device disabled but received an EQ event\n"); goto skip_irq; } diff --git a/drivers/misc/habanalabs/common/memory.c b/drivers/misc/habanalabs/common/memory.c index b8596846f3dc..c1eefaebacb6 100644 --- a/drivers/misc/habanalabs/common/memory.c +++ b/drivers/misc/habanalabs/common/memory.c @@ -2624,7 +2624,7 @@ void hl_vm_ctx_fini(struct hl_ctx *ctx) * Clearly something went wrong on hard reset so no point in printing * another side effect error */ - if (!hdev->hard_reset_pending && !hash_empty(ctx->mem_hash)) + if (!hdev->reset_info.hard_reset_pending && !hash_empty(ctx->mem_hash)) dev_dbg(hdev->dev, "user released device without removing its memory mappings\n"); diff --git a/drivers/misc/habanalabs/common/sysfs.c b/drivers/misc/habanalabs/common/sysfs.c index 1af568e46f46..45c715325e2a 100644 --- a/drivers/misc/habanalabs/common/sysfs.c +++ b/drivers/misc/habanalabs/common/sysfs.c @@ -211,7 +211,7 @@ static ssize_t soft_reset_store(struct device *dev, goto out; } - if (!hdev->allow_inference_soft_reset) { + if (!hdev->asic_prop.allow_inference_soft_reset) { dev_err(hdev->dev, "Device does not support inference soft-reset\n"); goto out; } @@ -303,7 +303,7 @@ static ssize_t soft_reset_cnt_show(struct device *dev, { struct hl_device *hdev = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", hdev->soft_reset_cnt); + return sprintf(buf, "%d\n", hdev->reset_info.soft_reset_cnt); } static ssize_t hard_reset_cnt_show(struct device *dev, @@ -311,7 +311,7 @@ static ssize_t hard_reset_cnt_show(struct device *dev, { struct hl_device *hdev = dev_get_drvdata(dev); - return sprintf(buf, "%d\n", hdev->hard_reset_cnt); + return sprintf(buf, "%d\n", hdev->reset_info.hard_reset_cnt); } static ssize_t max_power_show(struct device *dev, struct device_attribute *attr, @@ -478,7 +478,7 @@ int hl_sysfs_init(struct hl_device *hdev) return rc; } - if (!hdev->allow_inference_soft_reset) + if (!hdev->asic_prop.allow_inference_soft_reset) return 0; rc = device_add_groups(hdev->dev, hl_dev_inference_attr_groups); @@ -495,7 +495,7 @@ void hl_sysfs_fini(struct hl_device *hdev) { device_remove_groups(hdev->dev, hl_dev_attr_groups); - if (!hdev->allow_inference_soft_reset) + if (!hdev->asic_prop.allow_inference_soft_reset) return; device_remove_groups(hdev->dev, hl_dev_inference_attr_groups); diff --git a/drivers/misc/habanalabs/gaudi/gaudi.c b/drivers/misc/habanalabs/gaudi/gaudi.c index b3431eac4f04..013c6da2e3ca 100644 --- a/drivers/misc/habanalabs/gaudi/gaudi.c +++ b/drivers/misc/habanalabs/gaudi/gaudi.c @@ -4325,7 +4325,7 @@ static void gaudi_hw_fini(struct hl_device *hdev, bool hard_reset, bool fw_reset * In case watchdog hasn't expired but we still got HB, then this won't do any * damage. */ - if (hdev->curr_reset_cause == HL_RESET_CAUSE_HEARTBEAT) { + if (hdev->reset_info.curr_reset_cause == HL_RESET_CAUSE_HEARTBEAT) { if (hdev->asic_prop.hard_reset_done_by_fw) hl_fw_ask_hard_reset_without_linux(hdev); else @@ -6564,7 +6564,7 @@ static u64 gaudi_read_pte(struct hl_device *hdev, u64 addr) { struct gaudi_device *gaudi = hdev->asic_specific; - if (hdev->hard_reset_pending) + if (hdev->reset_info.hard_reset_pending) return U64_MAX; return readq(hdev->pcie_bar[HBM_BAR_ID] + @@ -6575,7 +6575,7 @@ static void gaudi_write_pte(struct hl_device *hdev, u64 addr, u64 val) { struct gaudi_device *gaudi = hdev->asic_specific; - if (hdev->hard_reset_pending) + if (hdev->reset_info.hard_reset_pending) return; writeq(val, hdev->pcie_bar[HBM_BAR_ID] + @@ -8341,7 +8341,7 @@ static int gaudi_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, int rc; if (!(gaudi->hw_cap_initialized & HW_CAP_MMU) || - hdev->hard_reset_pending) + hdev->reset_info.hard_reset_pending) return 0; if (hdev->pldm) diff --git a/drivers/misc/habanalabs/goya/goya.c b/drivers/misc/habanalabs/goya/goya.c index f4473013f1ee..fbcc7bbf44b3 100644 --- a/drivers/misc/habanalabs/goya/goya.c +++ b/drivers/misc/habanalabs/goya/goya.c @@ -1033,8 +1033,8 @@ static int goya_sw_init(struct hl_device *hdev) spin_lock_init(&goya->hw_queues_lock); hdev->supports_coresight = true; - hdev->supports_soft_reset = true; - hdev->allow_inference_soft_reset = true; + hdev->asic_prop.supports_soft_reset = true; + hdev->asic_prop.allow_inference_soft_reset = true; hdev->supports_wait_for_multi_cs = false; hdev->asic_funcs->set_pci_memory_regions(hdev); @@ -4477,7 +4477,7 @@ static u64 goya_read_pte(struct hl_device *hdev, u64 addr) { struct goya_device *goya = hdev->asic_specific; - if (hdev->hard_reset_pending) + if (hdev->reset_info.hard_reset_pending) return U64_MAX; return readq(hdev->pcie_bar[DDR_BAR_ID] + @@ -4488,7 +4488,7 @@ static void goya_write_pte(struct hl_device *hdev, u64 addr, u64 val) { struct goya_device *goya = hdev->asic_specific; - if (hdev->hard_reset_pending) + if (hdev->reset_info.hard_reset_pending) return; writeq(val, hdev->pcie_bar[DDR_BAR_ID] + @@ -5308,7 +5308,7 @@ static int goya_mmu_invalidate_cache(struct hl_device *hdev, bool is_hard, int rc; if (!(goya->hw_cap_initialized & HW_CAP_MMU) || - hdev->hard_reset_pending) + hdev->reset_info.hard_reset_pending) return 0; /* no need in L1 only invalidation in Goya */ -- cgit v1.2.3 From 42eb2872e0867679c996bb19ee9063e6141fa974 Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 23 Nov 2021 15:15:22 +0200 Subject: habanalabs: add a lock to protect multiple reset variables Atomic operations during reset are replaced by a spinlock in order to have the ability to protect more than a single variable. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/command_buffer.c | 3 +- drivers/misc/habanalabs/common/debugfs.c | 16 ++++---- drivers/misc/habanalabs/common/device.c | 50 ++++++++++++++++++------- drivers/misc/habanalabs/common/habanalabs.h | 6 ++- 4 files changed, 49 insertions(+), 26 deletions(-) diff --git a/drivers/misc/habanalabs/common/command_buffer.c b/drivers/misc/habanalabs/common/command_buffer.c index 649380bb189f..3c0ae07a2d80 100644 --- a/drivers/misc/habanalabs/common/command_buffer.c +++ b/drivers/misc/habanalabs/common/command_buffer.c @@ -250,8 +250,7 @@ int hl_cb_create(struct hl_device *hdev, struct hl_cb_mgr *mgr, * Can't use generic function to check this because of special case * where we create a CB as part of the reset process */ - if ((hdev->disabled) || ((atomic_read(&hdev->reset_info.in_reset)) && - (ctx_id != HL_KERNEL_ASID_ID))) { + if ((hdev->disabled) || (hdev->reset_info.in_reset && (ctx_id != HL_KERNEL_ASID_ID))) { dev_warn_ratelimited(hdev->dev, "Device is disabled or in reset. Can't create new CBs\n"); rc = -EBUSY; diff --git a/drivers/misc/habanalabs/common/debugfs.c b/drivers/misc/habanalabs/common/debugfs.c index 746d1a18de63..fc084ee5106e 100644 --- a/drivers/misc/habanalabs/common/debugfs.c +++ b/drivers/misc/habanalabs/common/debugfs.c @@ -527,7 +527,7 @@ static int engines_show(struct seq_file *s, void *data) struct hl_dbg_device_entry *dev_entry = entry->dev_entry; struct hl_device *hdev = dev_entry->hdev; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't check device idle during reset\n"); return 0; @@ -658,7 +658,7 @@ static ssize_t hl_data_read32(struct file *f, char __user *buf, ssize_t rc; u32 val; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); return 0; } @@ -694,7 +694,7 @@ static ssize_t hl_data_write32(struct file *f, const char __user *buf, u32 value; ssize_t rc; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); return 0; } @@ -731,7 +731,7 @@ static ssize_t hl_data_read64(struct file *f, char __user *buf, ssize_t rc; u64 val; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't read during reset\n"); return 0; } @@ -767,7 +767,7 @@ static ssize_t hl_data_write64(struct file *f, const char __user *buf, u64 value; ssize_t rc; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't write during reset\n"); return 0; } @@ -802,7 +802,7 @@ static ssize_t hl_dma_size_write(struct file *f, const char __user *buf, ssize_t rc; u32 size; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't DMA during reset\n"); return 0; } @@ -1077,7 +1077,7 @@ static ssize_t hl_clk_gate_write(struct file *f, const char __user *buf, u64 value; ssize_t rc; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't change clock gating during reset\n"); return 0; @@ -1119,7 +1119,7 @@ static ssize_t hl_stop_on_err_write(struct file *f, const char __user *buf, u32 value; ssize_t rc; - if (atomic_read(&hdev->reset_info.in_reset)) { + if (hdev->reset_info.in_reset) { dev_warn_ratelimited(hdev->dev, "Can't change stop on error during reset\n"); return 0; diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index f8f9eb7a934f..84621ad765bc 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -17,7 +17,7 @@ enum hl_device_status hl_device_status(struct hl_device *hdev) { enum hl_device_status status; - if (atomic_read(&hdev->reset_info.in_reset)) + if (hdev->reset_info.in_reset) status = HL_DEVICE_STATUS_IN_RESET; else if (hdev->reset_info.needs_reset) status = HL_DEVICE_STATUS_NEEDS_RESET; @@ -448,11 +448,11 @@ static int device_early_init(struct hl_device *hdev) mutex_init(&hdev->debug_lock); INIT_LIST_HEAD(&hdev->cs_mirror_list); spin_lock_init(&hdev->cs_mirror_lock); + spin_lock_init(&hdev->reset_info.lock); INIT_LIST_HEAD(&hdev->fpriv_list); INIT_LIST_HEAD(&hdev->fpriv_ctrl_list); mutex_init(&hdev->fpriv_list_lock); mutex_init(&hdev->fpriv_ctrl_list_lock); - atomic_set(&hdev->reset_info.in_reset, 0); mutex_init(&hdev->clk_throttling.lock); return 0; @@ -544,7 +544,7 @@ reschedule: * status for at least one heartbeat. From this point driver restarts * tracking future consecutive fatal errors. */ - if (!(atomic_read(&hdev->reset_info.in_reset))) + if (!hdev->reset_info.in_reset) hdev->reset_info.prev_reset_trigger = HL_RESET_TRIGGER_DEFAULT; schedule_delayed_work(&hdev->work_heartbeat, @@ -722,11 +722,14 @@ int hl_device_suspend(struct hl_device *hdev) pci_save_state(hdev->pdev); /* Block future CS/VM/JOB completion operations */ - rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); - if (rc) { + spin_lock(&hdev->reset_info.lock); + if (hdev->reset_info.in_reset) { + spin_unlock(&hdev->reset_info.lock); dev_err(hdev->dev, "Can't suspend while in reset\n"); return -EIO; } + hdev->reset_info.in_reset = 1; + spin_unlock(&hdev->reset_info.lock); /* This blocks all other stuff that is not blocked by in_reset */ hdev->disabled = true; @@ -776,8 +779,10 @@ int hl_device_resume(struct hl_device *hdev) } - hdev->disabled = false; - atomic_set(&hdev->reset_info.in_reset, 0); + /* 'in_reset' was set to true during suspend, now we must clear it in order + * for hard reset to be performed + */ + hdev->reset_info.in_reset = 0; rc = hl_device_reset(hdev, HL_DRV_RESET_HARD); if (rc) { @@ -1024,9 +1029,13 @@ do_reset: */ if (!from_hard_reset_thread) { /* Block future CS/VM/JOB completion operations */ - rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); - if (rc) + spin_lock(&hdev->reset_info.lock); + if (hdev->reset_info.in_reset) { + spin_unlock(&hdev->reset_info.lock); return 0; + } + hdev->reset_info.in_reset = 1; + spin_unlock(&hdev->reset_info.lock); handle_reset_trigger(hdev, flags); @@ -1234,7 +1243,7 @@ kill_processes: } } - atomic_set(&hdev->reset_info.in_reset, 0); + hdev->reset_info.in_reset = 0; hdev->reset_info.needs_reset = false; dev_notice(hdev->dev, "Successfully finished resetting the device\n"); @@ -1272,7 +1281,7 @@ out_err: goto again; } - atomic_set(&hdev->reset_info.in_reset, 0); + hdev->reset_info.in_reset = 0; return rc; } @@ -1583,6 +1592,7 @@ out_disabled: */ void hl_device_fini(struct hl_device *hdev) { + bool device_in_reset; ktime_t timeout; u64 reset_sec; int i, rc; @@ -1606,10 +1616,22 @@ void hl_device_fini(struct hl_device *hdev) */ timeout = ktime_add_us(ktime_get(), reset_sec * 1000 * 1000); - rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); - while (rc) { + + spin_lock(&hdev->reset_info.lock); + device_in_reset = !!hdev->reset_info.in_reset; + if (!device_in_reset) + hdev->reset_info.in_reset = 1; + spin_unlock(&hdev->reset_info.lock); + + while (device_in_reset) { usleep_range(50, 200); - rc = atomic_cmpxchg(&hdev->reset_info.in_reset, 0, 1); + + spin_lock(&hdev->reset_info.lock); + device_in_reset = !!hdev->reset_info.in_reset; + if (!device_in_reset) + hdev->reset_info.in_reset = 1; + spin_unlock(&hdev->reset_info.lock); + if (ktime_compare(ktime_get(), timeout) > 0) { dev_crit(hdev->dev, "Failed to remove device because reset function did not finish\n"); diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 47eaeff9e924..37a3a469b42f 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2457,9 +2457,10 @@ struct last_error_session_info { /** * struct hl_reset_info - holds current device reset information. - * @in_reset: is device in reset flow. + * @lock: lock to protect critical reset flows. * @soft_reset_cnt: number of soft reset since the driver was loaded. * @hard_reset_cnt: number of hard reset since the driver was loaded. + * @in_reset: is device in reset flow. * @is_in_soft_reset: Device is currently in soft reset process. * @needs_reset: true if reset_on_lockup is false and device should be reset * due to lockup. @@ -2474,9 +2475,10 @@ struct last_error_session_info { * complete instead. */ struct hl_reset_info { - atomic_t in_reset; + spinlock_t lock; u32 soft_reset_cnt; u32 hard_reset_cnt; + u8 in_reset; u8 is_in_soft_reset; u8 needs_reset; u8 hard_reset_pending; -- cgit v1.2.3 From ce80098db2439ee44403ec6fccd3a10be21c7aff Mon Sep 17 00:00:00 2001 From: Ofir Bitton Date: Tue, 23 Nov 2021 16:34:28 +0200 Subject: habanalabs: support hard-reset scheduling during soft-reset As hard-reset can be requested during soft-reset, driver must allow it or else critical events received during soft-reset will be ignored. Signed-off-by: Ofir Bitton Reviewed-by: Oded Gabbay Signed-off-by: Oded Gabbay --- drivers/misc/habanalabs/common/device.c | 31 ++++++++++++++++++++++++++--- drivers/misc/habanalabs/common/habanalabs.h | 3 +++ 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/drivers/misc/habanalabs/common/device.c b/drivers/misc/habanalabs/common/device.c index 84621ad765bc..733338ab6f1d 100644 --- a/drivers/misc/habanalabs/common/device.c +++ b/drivers/misc/habanalabs/common/device.c @@ -978,7 +978,7 @@ static void handle_reset_trigger(struct hl_device *hdev, u32 flags) int hl_device_reset(struct hl_device *hdev, u32 flags) { bool hard_reset, from_hard_reset_thread, fw_reset, hard_instead_soft = false, - reset_upon_device_release = false; + reset_upon_device_release = false, schedule_hard_reset = false; u64 idle_mask[HL_BUSY_ENGINES_MASK_EXT_SIZE] = {0}; struct hl_ctx *ctx; int i, rc; @@ -1031,6 +1031,9 @@ do_reset: /* Block future CS/VM/JOB completion operations */ spin_lock(&hdev->reset_info.lock); if (hdev->reset_info.in_reset) { + /* We only allow scheduling of a hard reset during soft reset */ + if (hard_reset && hdev->reset_info.is_in_soft_reset) + hdev->reset_info.hard_reset_schedule_flags = flags; spin_unlock(&hdev->reset_info.lock); return 0; } @@ -1193,7 +1196,6 @@ kill_processes: * is required for the initialization itself */ hdev->disabled = false; - hdev->reset_info.is_in_soft_reset = false; rc = hdev->asic_funcs->hw_init(hdev); if (rc) { @@ -1243,7 +1245,20 @@ kill_processes: } } - hdev->reset_info.in_reset = 0; + spin_lock(&hdev->reset_info.lock); + hdev->reset_info.is_in_soft_reset = false; + + /* Schedule hard reset only if requested and if not already in hard reset. + * We keep 'in_reset' enabled, so no other reset can go in during the hard + * reset schedule + */ + if (!hard_reset && hdev->reset_info.hard_reset_schedule_flags) + schedule_hard_reset = true; + else + hdev->reset_info.in_reset = 0; + + spin_unlock(&hdev->reset_info.lock); + hdev->reset_info.needs_reset = false; dev_notice(hdev->dev, "Successfully finished resetting the device\n"); @@ -1261,6 +1276,16 @@ kill_processes: hdev->reset_info.soft_reset_cnt++; } + if (schedule_hard_reset) { + dev_info(hdev->dev, "Performing hard reset scheduled during soft reset\n"); + flags = hdev->reset_info.hard_reset_schedule_flags; + hdev->reset_info.hard_reset_schedule_flags = 0; + hdev->disabled = true; + hard_reset = true; + handle_reset_trigger(hdev, flags); + goto again; + } + return 0; out_err: diff --git a/drivers/misc/habanalabs/common/habanalabs.h b/drivers/misc/habanalabs/common/habanalabs.h index 37a3a469b42f..cb710fd478b6 100644 --- a/drivers/misc/habanalabs/common/habanalabs.h +++ b/drivers/misc/habanalabs/common/habanalabs.h @@ -2460,6 +2460,8 @@ struct last_error_session_info { * @lock: lock to protect critical reset flows. * @soft_reset_cnt: number of soft reset since the driver was loaded. * @hard_reset_cnt: number of hard reset since the driver was loaded. + * @hard_reset_schedule_flags: hard reset is scheduled to after current soft reset, + * here we hold the hard reset flags. * @in_reset: is device in reset flow. * @is_in_soft_reset: Device is currently in soft reset process. * @needs_reset: true if reset_on_lockup is false and device should be reset @@ -2478,6 +2480,7 @@ struct hl_reset_info { spinlock_t lock; u32 soft_reset_cnt; u32 hard_reset_cnt; + u32 hard_reset_schedule_flags; u8 in_reset; u8 is_in_soft_reset; u8 needs_reset; -- cgit v1.2.3 From 38be5687da839bfcafeabb34a5a21e8396613ce3 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 23 Dec 2021 11:47:04 +0200 Subject: mei: add POWERING_DOWN into device state print The POWERING_DOWN state string was missing from the device states list, add it. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20211223094705.204624-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/init.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 5c8cb679b997..f79076c67256 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -24,6 +24,7 @@ const char *mei_dev_state_str(int state) MEI_DEV_STATE(ENABLED); MEI_DEV_STATE(RESETTING); MEI_DEV_STATE(DISABLED); + MEI_DEV_STATE(POWERING_DOWN); MEI_DEV_STATE(POWER_DOWN); MEI_DEV_STATE(POWER_UP); default: -- cgit v1.2.3 From 43aa323e315bec40779fe2899f7b531773d7b733 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Thu, 23 Dec 2021 11:47:05 +0200 Subject: mei: cleanup status before client dma setup call The upper layer may retry call to mei_cl_dma_alloc_and_map(), in that case the client status may be non-zero after the previous call and the wait condition will be true immediately. Set cl->status to zero to allow waiting for an actual result from the firmware. Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20211223094705.204624-2-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/client.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 96f4e59c32a5..22be86a205bf 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -2327,6 +2327,8 @@ int mei_cl_dma_alloc_and_map(struct mei_cl *cl, const struct file *fp, list_move_tail(&cb->list, &dev->ctrl_rd_list); } + cl->status = 0; + mutex_unlock(&dev->device_lock); wait_event_timeout(cl->wait, cl->dma_mapped || cl->status, @@ -2404,6 +2406,8 @@ int mei_cl_dma_unmap(struct mei_cl *cl, const struct file *fp) list_move_tail(&cb->list, &dev->ctrl_rd_list); } + cl->status = 0; + mutex_unlock(&dev->device_lock); wait_event_timeout(cl->wait, !cl->dma_mapped || cl->status, -- cgit v1.2.3 From 399c91c3f30531593e5ff6ca7b53f47092128669 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Fri, 24 Dec 2021 08:21:03 +0000 Subject: phy: mediatek: Fix missing check in mtk_mipi_tx_probe The of_device_get_match_data() function may return NULL. Add check to prevent potential null dereference. Signed-off-by: Miaoqian Lin Reviewed-by: AngeloGioacchino Del Regno Link: https://lore.kernel.org/r/20211224082103.7658-1-linmq006@gmail.com Signed-off-by: Vinod Koul --- drivers/phy/mediatek/phy-mtk-mipi-dsi.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c index 28ad9403c441..67b005d5b9e3 100644 --- a/drivers/phy/mediatek/phy-mtk-mipi-dsi.c +++ b/drivers/phy/mediatek/phy-mtk-mipi-dsi.c @@ -146,6 +146,8 @@ static int mtk_mipi_tx_probe(struct platform_device *pdev) return -ENOMEM; mipi_tx->driver_data = of_device_get_match_data(dev); + if (!mipi_tx->driver_data) + return -ENODEV; mipi_tx->regs = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(mipi_tx->regs)) -- cgit v1.2.3 From c3c11d5534343e56ce8f4eb8cadfabe455150e3d Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:23 +0100 Subject: phy: cadence: Sierra: Use of_device_get_match_data() to get driver data Use of_device_get_match_data() to get driver data instead of boilerplate code. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-2-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index e93818e3991f..54d1c63932ac 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -253,7 +253,7 @@ struct cdns_regmap_cdb_context { struct cdns_sierra_phy { struct device *dev; struct regmap *regmap; - struct cdns_sierra_data *init_data; + const struct cdns_sierra_data *init_data; struct cdns_sierra_inst phys[SIERRA_MAX_LANES]; struct reset_control *phy_rst; struct reset_control *apb_rst; @@ -595,8 +595,6 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, return 0; } -static const struct of_device_id cdns_sierra_id_table[]; - static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base, u32 block_offset, u8 reg_offset_shift, const struct regmap_config *config) @@ -829,8 +827,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) struct cdns_sierra_phy *sp; struct phy_provider *phy_provider; struct device *dev = &pdev->dev; - const struct of_device_id *match; - struct cdns_sierra_data *data; + const struct cdns_sierra_data *data; unsigned int id_value; int i, ret, node = 0; void __iomem *base; @@ -840,12 +837,10 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) return -ENODEV; /* Get init data for this PHY */ - match = of_match_device(cdns_sierra_id_table, dev); - if (!match) + data = of_device_get_match_data(dev); + if (!data) return -EINVAL; - data = (struct cdns_sierra_data *)match->data; - sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL); if (!sp) return -ENOMEM; -- cgit v1.2.3 From 078e9e92119ae39e5c816242a7a5cf73b3cc4f5c Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:24 +0100 Subject: phy: cadence: Sierra: Prepare driver to add support for multilink configurations Sierra driver currently supports single link configurations only. Prepare driver to support multilink multiprotocol configurations along with different SSC modes. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-3-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 195 ++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 56 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 54d1c63932ac..c82ac6716f5e 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -23,6 +23,9 @@ #include #include +#define NUM_SSC_MODE 3 +#define NUM_PHY_TYPE 3 + /* PHY register offsets */ #define SIERRA_COMMON_CDB_OFFSET 0x0 #define SIERRA_MACRO_ID_REG 0x0 @@ -217,9 +220,21 @@ static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = { static u32 cdns_sierra_pll_mux_table[] = { 0, 1 }; +enum cdns_sierra_phy_type { + TYPE_NONE, + TYPE_PCIE, + TYPE_USB +}; + +enum cdns_sierra_ssc_mode { + NO_SSC, + EXTERNAL_SSC, + INTERNAL_SSC +}; + struct cdns_sierra_inst { struct phy *phy; - u32 phy_type; + enum cdns_sierra_phy_type phy_type; u32 num_lanes; u32 mlane; struct reset_control *lnk_rst; @@ -230,18 +245,19 @@ struct cdns_reg_pairs { u32 off; }; +struct cdns_sierra_vals { + const struct cdns_reg_pairs *reg_pairs; + u32 num_regs; +}; + struct cdns_sierra_data { - u32 id_value; - u8 block_offset_shift; - u8 reg_offset_shift; - u32 pcie_cmn_regs; - u32 pcie_ln_regs; - u32 usb_cmn_regs; - u32 usb_ln_regs; - const struct cdns_reg_pairs *pcie_cmn_vals; - const struct cdns_reg_pairs *pcie_ln_vals; - const struct cdns_reg_pairs *usb_cmn_vals; - const struct cdns_reg_pairs *usb_ln_vals; + u32 id_value; + u8 block_offset_shift; + u8 reg_offset_shift; + struct cdns_sierra_vals *pma_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; + struct cdns_sierra_vals *pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; }; struct cdns_regmap_cdb_context { @@ -341,10 +357,14 @@ static int cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent); + const struct cdns_sierra_data *init_data = phy->init_data; + struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; + enum cdns_sierra_phy_type phy_type = ins->phy_type; + enum cdns_sierra_ssc_mode ssc = EXTERNAL_SSC; + const struct cdns_reg_pairs *reg_pairs; struct regmap *regmap; + u32 num_regs; int i, j; - const struct cdns_reg_pairs *cmn_vals, *ln_vals; - u32 num_cmn_regs, num_ln_regs; /* Initialise the PHY registers, unless auto configured */ if (phy->autoconf) @@ -352,28 +372,26 @@ static int cdns_sierra_phy_init(struct phy *gphy) clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000); clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000); - if (ins->phy_type == PHY_TYPE_PCIE) { - num_cmn_regs = phy->init_data->pcie_cmn_regs; - num_ln_regs = phy->init_data->pcie_ln_regs; - cmn_vals = phy->init_data->pcie_cmn_vals; - ln_vals = phy->init_data->pcie_ln_vals; - } else if (ins->phy_type == PHY_TYPE_USB3) { - num_cmn_regs = phy->init_data->usb_cmn_regs; - num_ln_regs = phy->init_data->usb_ln_regs; - cmn_vals = phy->init_data->usb_cmn_vals; - ln_vals = phy->init_data->usb_ln_vals; - } else { - return -EINVAL; - } - regmap = phy->regmap_common_cdb; - for (j = 0; j < num_cmn_regs ; j++) - regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val); + /* PMA common registers configurations */ + pma_cmn_vals = init_data->pma_cmn_vals[phy_type][TYPE_NONE][ssc]; + if (pma_cmn_vals) { + reg_pairs = pma_cmn_vals->reg_pairs; + num_regs = pma_cmn_vals->num_regs; + regmap = phy->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } - for (i = 0; i < ins->num_lanes; i++) { - for (j = 0; j < num_ln_regs ; j++) { + /* PMA lane registers configurations */ + pma_ln_vals = init_data->pma_ln_vals[phy_type][TYPE_NONE][ssc]; + if (pma_ln_vals) { + reg_pairs = pma_ln_vals->reg_pairs; + num_regs = pma_ln_vals->num_regs; + for (i = 0; i < ins->num_lanes; i++) { regmap = phy->regmap_lane_cdb[i + ins->mlane]; - regmap_write(regmap, ln_vals[j].off, ln_vals[j].val); + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); } } @@ -583,15 +601,28 @@ static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp) static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, struct device_node *child) { + u32 phy_type; + if (of_property_read_u32(child, "reg", &inst->mlane)) return -EINVAL; if (of_property_read_u32(child, "cdns,num-lanes", &inst->num_lanes)) return -EINVAL; - if (of_property_read_u32(child, "cdns,phy-type", &inst->phy_type)) + if (of_property_read_u32(child, "cdns,phy-type", &phy_type)) return -EINVAL; + switch (phy_type) { + case PHY_TYPE_PCIE: + inst->phy_type = TYPE_PCIE; + break; + case PHY_TYPE_USB3: + inst->phy_type = TYPE_USB; + break; + default: + return -EINVAL; + } + return 0; } @@ -1006,6 +1037,16 @@ static const struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = { {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG} }; +static struct cdns_sierra_vals pcie_100_ext_ssc_cmn_vals = { + .reg_pairs = cdns_pcie_cmn_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), +}; + +static struct cdns_sierra_vals pcie_100_ext_ssc_ln_vals = { + .reg_pairs = cdns_pcie_ln_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), +}; + /* refclk100MHz_20b_USB_cmn_pll_ext_ssc */ static const struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = { {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, @@ -1113,32 +1154,74 @@ static const struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = { {0x4243, SIERRA_RXBUFFER_DFECTRL_PREG} }; +static struct cdns_sierra_vals usb_100_ext_ssc_cmn_vals = { + .reg_pairs = cdns_usb_cmn_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), +}; + +static struct cdns_sierra_vals usb_100_ext_ssc_ln_vals = { + .reg_pairs = cdns_usb_ln_regs_ext_ssc, + .num_regs = ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), +}; + static const struct cdns_sierra_data cdns_map_sierra = { - SIERRA_MACRO_ID, - 0x2, - 0x2, - ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), - cdns_pcie_cmn_regs_ext_ssc, - cdns_pcie_ln_regs_ext_ssc, - cdns_usb_cmn_regs_ext_ssc, - cdns_usb_ln_regs_ext_ssc, + .id_value = SIERRA_MACRO_ID, + .block_offset_shift = 0x2, + .reg_offset_shift = 0x2, + .pma_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals, + }, + }, + }, + .pma_ln_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals, + }, + }, + }, }; static const struct cdns_sierra_data cdns_ti_map_sierra = { - SIERRA_MACRO_ID, - 0x0, - 0x1, - ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc), - ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc), - cdns_pcie_cmn_regs_ext_ssc, - cdns_pcie_ln_regs_ext_ssc, - cdns_usb_cmn_regs_ext_ssc, - cdns_usb_ln_regs_ext_ssc, + .id_value = SIERRA_MACRO_ID, + .block_offset_shift = 0x0, + .reg_offset_shift = 0x1, + .pma_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals, + }, + }, + }, + .pma_ln_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, + }, + }, + [TYPE_USB] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals, + }, + }, + }, }; static const struct of_device_id cdns_sierra_id_table[] = { -- cgit v1.2.3 From 253f06c7b1c1729b50e7ec52638e046239327bb1 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:25 +0100 Subject: dt-bindings: phy: cadence-torrent: Rename SSC macros to use generic names Rename SSC macros to use generic names instead of PHY specific names, so that they can be used to specify SSC modes for both Torrent and Sierra. Renaming the macros should not affect the things as these are not being used in any DTS file yet. Signed-off-by: Swapnil Jakhade Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211223060137.9252-4-sjakhade@cadence.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml | 4 ++-- include/dt-bindings/phy/phy-cadence.h | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml index bd9ae11c9994..2fec9e54ad0e 100644 --- a/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml +++ b/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml @@ -202,7 +202,7 @@ examples: #phy-cells = <0>; cdns,phy-type = ; cdns,num-lanes = <2>; - cdns,ssc-mode = ; + cdns,ssc-mode = ; }; phy@2 { @@ -211,7 +211,7 @@ examples: #phy-cells = <0>; cdns,phy-type = ; cdns,num-lanes = <1>; - cdns,ssc-mode = ; + cdns,ssc-mode = ; }; }; }; diff --git a/include/dt-bindings/phy/phy-cadence.h b/include/dt-bindings/phy/phy-cadence.h index 24fdc9e11bd6..d55fe6e6b936 100644 --- a/include/dt-bindings/phy/phy-cadence.h +++ b/include/dt-bindings/phy/phy-cadence.h @@ -6,11 +6,11 @@ #ifndef _DT_BINDINGS_CADENCE_SERDES_H #define _DT_BINDINGS_CADENCE_SERDES_H -/* Torrent */ -#define TORRENT_SERDES_NO_SSC 0 -#define TORRENT_SERDES_EXTERNAL_SSC 1 -#define TORRENT_SERDES_INTERNAL_SSC 2 +#define CDNS_SERDES_NO_SSC 0 +#define CDNS_SERDES_EXTERNAL_SSC 1 +#define CDNS_SERDES_INTERNAL_SSC 2 +/* Torrent */ #define CDNS_TORRENT_REFCLK_DRIVER 0 #define CDNS_TORRENT_DERIVED_REFCLK 1 #define CDNS_TORRENT_RECEIVED_REFCLK 2 -- cgit v1.2.3 From 262303b92945f4d05bec78ead17f5c7c0d098d54 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:26 +0100 Subject: dt-bindings: phy: cadence-sierra: Add binding to specify SSC mode Add binding to specify Spread Spectrum Clocking mode used. Signed-off-by: Swapnil Jakhade Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20211223060137.9252-5-sjakhade@cadence.com Signed-off-by: Vinod Koul --- Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml index e71b32c9c0d1..a9e227d8b076 100644 --- a/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml +++ b/Documentation/devicetree/bindings/phy/phy-cadence-sierra.yaml @@ -113,6 +113,15 @@ patternProperties: minimum: 1 maximum: 16 + cdns,ssc-mode: + description: + Specifies the Spread Spectrum Clocking mode used. It can be NO_SSC, + EXTERNAL_SSC or INTERNAL_SSC. + Refer include/dt-bindings/phy/phy-cadence.h for the constants to be used. + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [0, 1, 2] + default: 1 + required: - reg - resets -- cgit v1.2.3 From 1e902b2ae3e9c0873d38b415b2af0811b4897ad4 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:27 +0100 Subject: phy: cadence: Sierra: Add support to get SSC type from device tree Add support to get SSC type from DT. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-6-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index c82ac6716f5e..4674328574f5 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -238,6 +238,7 @@ struct cdns_sierra_inst { u32 num_lanes; u32 mlane; struct reset_control *lnk_rst; + enum cdns_sierra_ssc_mode ssc_mode; }; struct cdns_reg_pairs { @@ -360,7 +361,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) const struct cdns_sierra_data *init_data = phy->init_data; struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; enum cdns_sierra_phy_type phy_type = ins->phy_type; - enum cdns_sierra_ssc_mode ssc = EXTERNAL_SSC; + enum cdns_sierra_ssc_mode ssc = ins->ssc_mode; const struct cdns_reg_pairs *reg_pairs; struct regmap *regmap; u32 num_regs; @@ -623,6 +624,9 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, return -EINVAL; } + inst->ssc_mode = EXTERNAL_SSC; + of_property_read_u32(child, "cdns,ssc-mode", &inst->ssc_mode); + return 0; } -- cgit v1.2.3 From 8c95e1722689f1b1e63a6206acba2b6200ed7864 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:28 +0100 Subject: phy: cadence: Sierra: Rename some regmap variables to be in sync with Sierra documentation No functional change. Rename some regmap variables as mentioned in Sierra register description documentation. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-7-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 4674328574f5..9f2b8aefee9c 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -145,8 +145,9 @@ #define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG 0x14F #define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 -#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset) \ - (0xc000 << (block_offset)) +/* PHY PCS common registers */ +#define SIERRA_PHY_PCS_COMMON_OFFSET(block_offset) \ + (0xc000 << (block_offset)) #define SIERRA_PHY_PLL_CFG 0xe #define SIERRA_MACRO_ID 0x00007364 @@ -275,7 +276,7 @@ struct cdns_sierra_phy { struct reset_control *phy_rst; struct reset_control *apb_rst; struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES]; - struct regmap *regmap_phy_config_ctrl; + struct regmap *regmap_phy_pcs_common_cdb; struct regmap *regmap_common_cdb; struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; @@ -346,8 +347,8 @@ static const struct regmap_config cdns_sierra_common_cdb_config = { .reg_read = cdns_regmap_read, }; -static const struct regmap_config cdns_sierra_phy_config_ctrl_config = { - .name = "sierra_phy_config_ctrl", +static const struct regmap_config cdns_sierra_phy_pcs_cmn_cdb_config = { + .name = "sierra_phy_pcs_cmn_cdb", .reg_stride = 1, .fast_io = true, .reg_write = cdns_regmap_write, @@ -689,7 +690,7 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) sp->cmn_refrcv_refclk_termen_preg[i] = field; } - regmap = sp->regmap_phy_config_ctrl; + regmap = sp->regmap_phy_pcs_common_cdb; field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1); if (IS_ERR(field)) { dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n"); @@ -741,14 +742,14 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, } sp->regmap_common_cdb = regmap; - block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift); + block_offset = SIERRA_PHY_PCS_COMMON_OFFSET(block_offset_shift); regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, - &cdns_sierra_phy_config_ctrl_config); + &cdns_sierra_phy_pcs_cmn_cdb_config); if (IS_ERR(regmap)) { - dev_err(dev, "Failed to init PHY config and control regmap\n"); + dev_err(dev, "Failed to init PHY PCS common CDB regmap\n"); return PTR_ERR(regmap); } - sp->regmap_phy_config_ctrl = regmap; + sp->regmap_phy_pcs_common_cdb = regmap; return 0; } -- cgit v1.2.3 From fa10517211f72f9480677796b97cbe5a8f3a298f Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:29 +0100 Subject: phy: cadence: Sierra: Add PHY PCS common register configurations Add PHY PCS common register configuration sequences for single link. Update single link PCIe register sequence accordingly. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-8-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 38 ++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 9f2b8aefee9c..0bfd13843f2e 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -148,6 +148,7 @@ /* PHY PCS common registers */ #define SIERRA_PHY_PCS_COMMON_OFFSET(block_offset) \ (0xc000 << (block_offset)) +#define SIERRA_PHY_PIPE_CMN_CTRL1 0x0 #define SIERRA_PHY_PLL_CFG 0xe #define SIERRA_MACRO_ID 0x00007364 @@ -256,6 +257,8 @@ struct cdns_sierra_data { u32 id_value; u8 block_offset_shift; u8 reg_offset_shift; + struct cdns_sierra_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; struct cdns_sierra_vals *pma_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] [NUM_SSC_MODE]; struct cdns_sierra_vals *pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] @@ -364,6 +367,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) enum cdns_sierra_phy_type phy_type = ins->phy_type; enum cdns_sierra_ssc_mode ssc = ins->ssc_mode; const struct cdns_reg_pairs *reg_pairs; + struct cdns_sierra_vals *pcs_cmn_vals; struct regmap *regmap; u32 num_regs; int i, j; @@ -375,6 +379,16 @@ static int cdns_sierra_phy_init(struct phy *gphy) clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000); clk_set_rate(phy->input_clks[CMN_REFCLK1_DIG_DIV], 25000000); + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_type][TYPE_NONE][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = phy->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } + /* PMA common registers configurations */ pma_cmn_vals = init_data->pma_cmn_vals[phy_type][TYPE_NONE][ssc]; if (pma_cmn_vals) { @@ -1022,6 +1036,16 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev) return 0; } +/* PCIE PHY PCS common configuration */ +static struct cdns_reg_pairs pcie_phy_pcs_cmn_regs[] = { + {0x0430, SIERRA_PHY_PIPE_CMN_CTRL1} +}; + +static struct cdns_sierra_vals pcie_phy_pcs_cmn_vals = { + .reg_pairs = pcie_phy_pcs_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_phy_pcs_cmn_regs), +}; + /* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */ static const struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, @@ -1173,6 +1197,13 @@ static const struct cdns_sierra_data cdns_map_sierra = { .id_value = SIERRA_MACRO_ID, .block_offset_shift = 0x2, .reg_offset_shift = 0x2, + .pcs_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + }, + }, .pma_cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { @@ -1203,6 +1234,13 @@ static const struct cdns_sierra_data cdns_ti_map_sierra = { .id_value = SIERRA_MACRO_ID, .block_offset_shift = 0x0, .reg_offset_shift = 0x1, + .pcs_cmn_vals = { + [TYPE_PCIE] = { + [TYPE_NONE] = { + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + }, + }, .pma_cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { -- cgit v1.2.3 From f1cc6c3f082c3d84d73c67a39407ff71210bcb49 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:30 +0100 Subject: phy: cadence: Sierra: Check cmn_ready assertion during PHY power on Check if PMA cmn_ready is set indicating the startup process is complete. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-9-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 45 ++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 0bfd13843f2e..13176208e6d7 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -151,6 +151,11 @@ #define SIERRA_PHY_PIPE_CMN_CTRL1 0x0 #define SIERRA_PHY_PLL_CFG 0xe +/* PHY PMA common registers */ +#define SIERRA_PHY_PMA_COMMON_OFFSET(block_offset) \ + (0xE000 << (block_offset)) +#define SIERRA_PHY_PMA_CMN_CTRL 0x000 + #define SIERRA_MACRO_ID 0x00007364 #define SIERRA_MAX_LANES 16 #define PLL_LOCK_TIME 100000 @@ -172,6 +177,8 @@ static const struct reg_field macro_id_type = REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15); static const struct reg_field phy_pll_cfg_1 = REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1); +static const struct reg_field pma_cmn_ready = + REG_FIELD(SIERRA_PHY_PMA_CMN_CTRL, 0, 0); static const struct reg_field pllctrl_lock = REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0); @@ -280,9 +287,11 @@ struct cdns_sierra_phy { struct reset_control *apb_rst; struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES]; struct regmap *regmap_phy_pcs_common_cdb; + struct regmap *regmap_phy_pma_common_cdb; struct regmap *regmap_common_cdb; struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; + struct regmap_field *pma_cmn_ready; struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC]; struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC]; @@ -358,6 +367,14 @@ static const struct regmap_config cdns_sierra_phy_pcs_cmn_cdb_config = { .reg_read = cdns_regmap_read, }; +static const struct regmap_config cdns_sierra_phy_pma_cmn_cdb_config = { + .name = "sierra_phy_pma_cmn_cdb", + .reg_stride = 1, + .fast_io = true, + .reg_write = cdns_regmap_write, + .reg_read = cdns_regmap_read, +}; + static int cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); @@ -435,6 +452,17 @@ static int cdns_sierra_phy_on(struct phy *gphy) return ret; } + /* + * Wait for cmn_ready assertion + * PHY_PMA_CMN_CTRL[0] == 1 + */ + ret = regmap_field_read_poll_timeout(sp->pma_cmn_ready, val, val, + 1000, PLL_LOCK_TIME); + if (ret) { + dev_err(dev, "Timeout waiting for CMN ready\n"); + return ret; + } + ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane], val, val, 1000, PLL_LOCK_TIME); if (ret < 0) @@ -712,6 +740,14 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) } sp->phy_pll_cfg_1 = field; + regmap = sp->regmap_phy_pma_common_cdb; + field = devm_regmap_field_alloc(dev, regmap, pma_cmn_ready); + if (IS_ERR(field)) { + dev_err(dev, "PHY_PMA_CMN_CTRL reg field init failed\n"); + return PTR_ERR(field); + } + sp->pma_cmn_ready = field; + for (i = 0; i < SIERRA_MAX_LANES; i++) { regmap = sp->regmap_lane_cdb[i]; field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock); @@ -765,6 +801,15 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, } sp->regmap_phy_pcs_common_cdb = regmap; + block_offset = SIERRA_PHY_PMA_COMMON_OFFSET(block_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, + &cdns_sierra_phy_pma_cmn_cdb_config); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PMA common CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_pma_common_cdb = regmap; + return 0; } -- cgit v1.2.3 From 36ce416330da5b27d84af519f61e94b73596a297 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:31 +0100 Subject: phy: cadence: Sierra: Check PIPE mode PHY status to be ready for operation PIPE phy status is used to communicate the completion of several PHY functions. Check if PHY is ready for operation while configured for PIPE mode during startup. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-10-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 73 +++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 13176208e6d7..59458388a855 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -151,6 +151,13 @@ #define SIERRA_PHY_PIPE_CMN_CTRL1 0x0 #define SIERRA_PHY_PLL_CFG 0xe +/* PHY PCS lane registers */ +#define SIERRA_PHY_PCS_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0xD000 << (block_offset)) + \ + (((ln) << 8) << (reg_offset))) + +#define SIERRA_PHY_ISO_LINK_CTRL 0xB + /* PHY PMA common registers */ #define SIERRA_PHY_PMA_COMMON_OFFSET(block_offset) \ (0xE000 << (block_offset)) @@ -181,6 +188,8 @@ static const struct reg_field pma_cmn_ready = REG_FIELD(SIERRA_PHY_PMA_CMN_CTRL, 0, 0); static const struct reg_field pllctrl_lock = REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0); +static const struct reg_field phy_iso_link_ctrl_1 = + REG_FIELD(SIERRA_PHY_ISO_LINK_CTRL, 1, 1); static const char * const clk_names[] = { [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc", @@ -287,12 +296,14 @@ struct cdns_sierra_phy { struct reset_control *apb_rst; struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES]; struct regmap *regmap_phy_pcs_common_cdb; + struct regmap *regmap_phy_pcs_lane_cdb[SIERRA_MAX_LANES]; struct regmap *regmap_phy_pma_common_cdb; struct regmap *regmap_common_cdb; struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; struct regmap_field *pma_cmn_ready; struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES]; + struct regmap_field *phy_iso_link_ctrl_1[SIERRA_MAX_LANES]; struct regmap_field *cmn_refrcv_refclk_plllc1en_preg[SIERRA_NUM_CMN_PLLC]; struct regmap_field *cmn_refrcv_refclk_termen_preg[SIERRA_NUM_CMN_PLLC]; struct regmap_field *cmn_plllc_pfdclk1_sel_preg[SIERRA_NUM_CMN_PLLC]; @@ -367,6 +378,34 @@ static const struct regmap_config cdns_sierra_phy_pcs_cmn_cdb_config = { .reg_read = cdns_regmap_read, }; +#define SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "sierra_phy_pcs_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static const struct regmap_config cdns_sierra_phy_pcs_lane_cdb_config[] = { + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("0"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("1"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("2"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("3"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("4"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("5"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("6"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("7"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("8"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("9"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("10"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("11"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("12"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("13"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("14"), + SIERRA_PHY_PCS_LANE_CDB_REGMAP_CONF("15"), +}; + static const struct regmap_config cdns_sierra_phy_pma_cmn_cdb_config = { .name = "sierra_phy_pma_cmn_cdb", .reg_stride = 1, @@ -452,6 +491,15 @@ static int cdns_sierra_phy_on(struct phy *gphy) return ret; } + if (ins->phy_type == TYPE_PCIE || ins->phy_type == TYPE_USB) { + ret = regmap_field_read_poll_timeout(sp->phy_iso_link_ctrl_1[ins->mlane], + val, !val, 1000, PLL_LOCK_TIME); + if (ret) { + dev_err(dev, "Timeout waiting for PHY status ready\n"); + return ret; + } + } + /* * Wait for cmn_ready assertion * PHY_PMA_CMN_CTRL[0] == 1 @@ -755,7 +803,17 @@ static int cdns_regfield_init(struct cdns_sierra_phy *sp) dev_err(dev, "P%d_ENABLE reg field init failed\n", i); return PTR_ERR(field); } - sp->pllctrl_lock[i] = field; + sp->pllctrl_lock[i] = field; + } + + for (i = 0; i < SIERRA_MAX_LANES; i++) { + regmap = sp->regmap_phy_pcs_lane_cdb[i]; + field = devm_regmap_field_alloc(dev, regmap, phy_iso_link_ctrl_1); + if (IS_ERR(field)) { + dev_err(dev, "PHY_ISO_LINK_CTRL reg field init for lane %d failed\n", i); + return PTR_ERR(field); + } + sp->phy_iso_link_ctrl_1[i] = field; } return 0; @@ -801,6 +859,19 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, } sp->regmap_phy_pcs_common_cdb = regmap; + for (i = 0; i < SIERRA_MAX_LANES; i++) { + block_offset = SIERRA_PHY_PCS_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, + reg_offset_shift, + &cdns_sierra_phy_pcs_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PCS lane CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_pcs_lane_cdb[i] = regmap; + } + block_offset = SIERRA_PHY_PMA_COMMON_OFFSET(block_offset_shift); regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift, &cdns_sierra_phy_pma_cmn_cdb_config); -- cgit v1.2.3 From 7a5ad9b4b98cd95f02ec12c895e80bc521fbf9ec Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:32 +0100 Subject: phy: cadence: Sierra: Update single link PCIe register configuration Add single link PCIe register configurations for no SSC and internal SSC. Also, add missing PMA lane registers for external SSC. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-11-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 214 ++++++++++++++++++++++++++++++- 1 file changed, 213 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 59458388a855..728abd14aa79 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -36,7 +36,12 @@ #define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50 +#define SIERRA_CMN_PLLLC_DSMCORR_PREG 0x51 +#define SIERRA_CMN_PLLLC_SS_PREG 0x52 +#define SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG 0x53 +#define SIERRA_CMN_PLLLC_SSTWOPT_PREG 0x54 #define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG 0x62 +#define SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG 0x63 #define SIERRA_CMN_REFRCV_PREG 0x98 #define SIERRA_CMN_REFRCV1_PREG 0xB8 #define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2 @@ -52,6 +57,7 @@ #define SIERRA_DET_STANDEC_E_PREG 0x004 #define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG 0x008 #define SIERRA_PSM_A0IN_TMR_PREG 0x009 +#define SIERRA_PSM_A3IN_TMR_PREG 0x00C #define SIERRA_PSM_DIAG_PREG 0x015 #define SIERRA_PSC_TX_A0_PREG 0x028 #define SIERRA_PSC_TX_A1_PREG 0x029 @@ -68,12 +74,15 @@ #define SIERRA_CLKPATH_BIASTRIM_PREG 0x04B #define SIERRA_DFE_BIASTRIM_PREG 0x04C #define SIERRA_DRVCTRL_ATTEN_PREG 0x06A +#define SIERRA_DRVCTRL_BOOST_PREG 0x06F #define SIERRA_CLKPATHCTRL_TMR_PREG 0x081 #define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG 0x085 #define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG 0x086 #define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG 0x087 #define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG 0x088 +#define SIERRA_CREQ_DCBIASATTEN_OVR_PREG 0x08C #define SIERRA_CREQ_CCLKDET_MODE01_PREG 0x08E +#define SIERRA_RX_CTLE_CAL_PREG 0x08F #define SIERRA_RX_CTLE_MAINTENANCE_PREG 0x091 #define SIERRA_CREQ_FSMCLK_SEL_PREG 0x092 #define SIERRA_CREQ_EQ_CTRL_PREG 0x093 @@ -123,15 +132,27 @@ #define SIERRA_DEQ_ALUT12 0x114 #define SIERRA_DEQ_ALUT13 0x115 #define SIERRA_DEQ_DFETAP_CTRL_PREG 0x128 +#define SIERRA_DEQ_DFETAP0 0x129 +#define SIERRA_DEQ_DFETAP1 0x12B +#define SIERRA_DEQ_DFETAP2 0x12D +#define SIERRA_DEQ_DFETAP3 0x12F +#define SIERRA_DEQ_DFETAP4 0x131 #define SIERRA_DFE_EN_1010_IGNORE_PREG 0x134 +#define SIERRA_DEQ_PRECUR_PREG 0x138 +#define SIERRA_DEQ_POSTCUR_PREG 0x140 +#define SIERRA_DEQ_POSTCUR_DECR_PREG 0x142 #define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG 0x150 #define SIERRA_DEQ_TAU_CTRL2_PREG 0x151 +#define SIERRA_DEQ_TAU_CTRL3_PREG 0x152 +#define SIERRA_DEQ_OPENEYE_CTRL_PREG 0x158 #define SIERRA_DEQ_PICTRL_PREG 0x161 #define SIERRA_CPICAL_TMRVAL_MODE1_PREG 0x170 #define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171 #define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174 #define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C +#define SIERRA_CPI_TRIM_PREG 0x17F #define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183 +#define SIERRA_EPI_CTRL_PREG 0x187 #define SIERRA_LFPSDET_SUPPORT_PREG 0x188 #define SIERRA_LFPSFILT_NS_PREG 0x18A #define SIERRA_LFPSFILT_RD_PREG 0x18B @@ -1162,6 +1183,146 @@ static struct cdns_sierra_vals pcie_phy_pcs_cmn_vals = { .num_regs = ARRAY_SIZE(pcie_phy_pcs_cmn_regs), }; +/* refclk100MHz_32b_PCIe_cmn_pll_no_ssc */ +static const struct cdns_reg_pairs cdns_pcie_cmn_regs_no_ssc[] = { + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG} +}; + +/* refclk100MHz_32b_PCIe_ln_no_ssc */ +static const struct cdns_reg_pairs cdns_pcie_ln_regs_no_ssc[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_no_ssc_cmn_vals = { + .reg_pairs = cdns_pcie_cmn_regs_no_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_no_ssc), +}; + +static struct cdns_sierra_vals pcie_100_no_ssc_ln_vals = { + .reg_pairs = cdns_pcie_ln_regs_no_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_no_ssc), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_int_ssc */ +static const struct cdns_reg_pairs cdns_pcie_cmn_regs_int_ssc[] = { + {0x000E, SIERRA_CMN_PLLLC_MODE_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x0581, SIERRA_CMN_PLLLC_DSMCORR_PREG}, + {0x7F80, SIERRA_CMN_PLLLC_SS_PREG}, + {0x0041, SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG}, + {0x0464, SIERRA_CMN_PLLLC_SSTWOPT_PREG}, + {0x0D0D, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}, + {0x0060, SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG} +}; + +/* refclk100MHz_32b_PCIe_ln_int_ssc */ +static const struct cdns_reg_pairs cdns_pcie_ln_regs_int_ssc[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_int_ssc_cmn_vals = { + .reg_pairs = cdns_pcie_cmn_regs_int_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_cmn_regs_int_ssc), +}; + +static struct cdns_sierra_vals pcie_100_int_ssc_ln_vals = { + .reg_pairs = cdns_pcie_ln_regs_int_ssc, + .num_regs = ARRAY_SIZE(cdns_pcie_ln_regs_int_ssc), +}; + /* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */ static const struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, @@ -1173,13 +1334,52 @@ static const struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = { /* refclk100MHz_32b_PCIe_ln_ext_ssc */ static const struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, - {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG} + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} }; static struct cdns_sierra_vals pcie_100_ext_ssc_cmn_vals = { @@ -1316,14 +1516,18 @@ static const struct cdns_sierra_data cdns_map_sierra = { .pcs_cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, }, }, }, .pma_cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, }, }, [TYPE_USB] = { @@ -1335,7 +1539,9 @@ static const struct cdns_sierra_data cdns_map_sierra = { .pma_ln_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_ln_vals, [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals, }, }, [TYPE_USB] = { @@ -1353,14 +1559,18 @@ static const struct cdns_sierra_data cdns_ti_map_sierra = { .pcs_cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, }, }, }, .pma_cmn_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_cmn_vals, [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, }, }, [TYPE_USB] = { @@ -1372,7 +1582,9 @@ static const struct cdns_sierra_data cdns_ti_map_sierra = { .pma_ln_vals = { [TYPE_PCIE] = { [TYPE_NONE] = { + [NO_SSC] = &pcie_100_no_ssc_ln_vals, [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals, }, }, [TYPE_USB] = { -- cgit v1.2.3 From da08aab940092a050a4fb2857ed9479d2b0e03c4 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:33 +0100 Subject: phy: cadence: Sierra: Fix to get correct parent for mux clocks Fix get_parent() callback to return the correct index of the parent for PLL_CMNLC1 clock. Add a separate table of register values corresponding to the parent index for PLL_CMNLC1. Update set_parent() callback accordingly. Fixes: 28081b72859f ("phy: cadence: Sierra: Model PLL_CMNLC and PLL_CMNLC1 as clocks (mux clocks)") Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-12-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 728abd14aa79..abdbc6ebd5a8 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -257,7 +257,10 @@ static const int pll_mux_parent_index[][SIERRA_NUM_CMN_PLLC_PARENTS] = { [CMN_PLLLC1] = { PLL1_REFCLK, PLL0_REFCLK }, }; -static u32 cdns_sierra_pll_mux_table[] = { 0, 1 }; +static u32 cdns_sierra_pll_mux_table[][SIERRA_NUM_CMN_PLLC_PARENTS] = { + [CMN_PLLLC] = { 0, 1 }, + [CMN_PLLLC1] = { 1, 0 }, +}; enum cdns_sierra_phy_type { TYPE_NONE, @@ -567,11 +570,25 @@ static const struct phy_ops ops = { static u8 cdns_sierra_pll_mux_get_parent(struct clk_hw *hw) { struct cdns_sierra_pll_mux *mux = to_cdns_sierra_pll_mux(hw); + struct regmap_field *plllc1en_field = mux->plllc1en_field; + struct regmap_field *termen_field = mux->termen_field; struct regmap_field *field = mux->pfdclk_sel_preg; unsigned int val; + int index; regmap_field_read(field, &val); - return clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table, 0, val); + + if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) { + index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC1], 0, val); + if (index == 1) { + regmap_field_write(plllc1en_field, 1); + regmap_field_write(termen_field, 1); + } + } else { + index = clk_mux_val_to_index(hw, cdns_sierra_pll_mux_table[CMN_PLLLC], 0, val); + } + + return index; } static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index) @@ -589,7 +606,11 @@ static int cdns_sierra_pll_mux_set_parent(struct clk_hw *hw, u8 index) ret |= regmap_field_write(termen_field, 1); } - val = cdns_sierra_pll_mux_table[index]; + if (strstr(clk_hw_get_name(hw), clk_names[CDNS_SIERRA_PLL_CMNLC1])) + val = cdns_sierra_pll_mux_table[CMN_PLLLC1][index]; + else + val = cdns_sierra_pll_mux_table[CMN_PLLLC][index]; + ret |= regmap_field_write(field, val); return ret; @@ -627,8 +648,8 @@ static int cdns_sierra_pll_mux_register(struct cdns_sierra_phy *sp, for (i = 0; i < num_parents; i++) { clk = sp->input_clks[pll_mux_parent_index[clk_index][i]]; if (IS_ERR_OR_NULL(clk)) { - dev_err(dev, "No parent clock for derived_refclk\n"); - return PTR_ERR(clk); + dev_err(dev, "No parent clock for PLL mux clocks\n"); + return IS_ERR(clk) ? PTR_ERR(clk) : -ENOENT; } parent_names[i] = __clk_get_name(clk); } -- cgit v1.2.3 From 6b81f05a8755a63d1acbcc1604f1e0f4534d36d8 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:34 +0100 Subject: phy: cadence: Sierra: Add support for PHY multilink configurations Add support for multilink configuration of Sierra PHY. Currently, maximum two links are supported. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-13-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 198 +++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 8 deletions(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index abdbc6ebd5a8..8f356bf38be8 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -24,7 +24,7 @@ #include #define NUM_SSC_MODE 3 -#define NUM_PHY_TYPE 3 +#define NUM_PHY_TYPE 4 /* PHY register offsets */ #define SIERRA_COMMON_CDB_OFFSET 0x0 @@ -184,6 +184,13 @@ (0xE000 << (block_offset)) #define SIERRA_PHY_PMA_CMN_CTRL 0x000 +/* PHY PMA lane registers */ +#define SIERRA_PHY_PMA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ + ((0xF000 << (block_offset)) + \ + (((ln) << 8) << (reg_offset))) + +#define SIERRA_PHY_PMA_XCVR_CTRL 0x000 + #define SIERRA_MACRO_ID 0x00007364 #define SIERRA_MAX_LANES 16 #define PLL_LOCK_TIME 100000 @@ -299,6 +306,8 @@ struct cdns_sierra_data { u8 reg_offset_shift; struct cdns_sierra_vals *pcs_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] [NUM_SSC_MODE]; + struct cdns_sierra_vals *phy_pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] + [NUM_SSC_MODE]; struct cdns_sierra_vals *pma_cmn_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] [NUM_SSC_MODE]; struct cdns_sierra_vals *pma_ln_vals[NUM_PHY_TYPE][NUM_PHY_TYPE] @@ -322,6 +331,7 @@ struct cdns_sierra_phy { struct regmap *regmap_phy_pcs_common_cdb; struct regmap *regmap_phy_pcs_lane_cdb[SIERRA_MAX_LANES]; struct regmap *regmap_phy_pma_common_cdb; + struct regmap *regmap_phy_pma_lane_cdb[SIERRA_MAX_LANES]; struct regmap *regmap_common_cdb; struct regmap_field *macro_id_type; struct regmap_field *phy_pll_cfg_1; @@ -438,6 +448,34 @@ static const struct regmap_config cdns_sierra_phy_pma_cmn_cdb_config = { .reg_read = cdns_regmap_read, }; +#define SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF(n) \ +{ \ + .name = "sierra_phy_pma_lane" n "_cdb", \ + .reg_stride = 1, \ + .fast_io = true, \ + .reg_write = cdns_regmap_write, \ + .reg_read = cdns_regmap_read, \ +} + +static const struct regmap_config cdns_sierra_phy_pma_lane_cdb_config[] = { + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("0"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("1"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("2"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("3"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("4"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("5"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("6"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("7"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("8"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("9"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("10"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("11"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("12"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("13"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("14"), + SIERRA_PHY_PMA_LANE_CDB_REGMAP_CONF("15"), +}; + static int cdns_sierra_phy_init(struct phy *gphy) { struct cdns_sierra_inst *ins = phy_get_drvdata(gphy); @@ -446,6 +484,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; enum cdns_sierra_phy_type phy_type = ins->phy_type; enum cdns_sierra_ssc_mode ssc = ins->ssc_mode; + struct cdns_sierra_vals *phy_pma_ln_vals; const struct cdns_reg_pairs *reg_pairs; struct cdns_sierra_vals *pcs_cmn_vals; struct regmap *regmap; @@ -453,7 +492,7 @@ static int cdns_sierra_phy_init(struct phy *gphy) int i, j; /* Initialise the PHY registers, unless auto configured */ - if (phy->autoconf) + if (phy->autoconf || phy->nsubnodes > 1) return 0; clk_set_rate(phy->input_clks[CMN_REFCLK_DIG_DIV], 25000000); @@ -469,6 +508,18 @@ static int cdns_sierra_phy_init(struct phy *gphy) regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); } + /* PHY PMA lane registers configurations */ + phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_type][TYPE_NONE][ssc]; + if (phy_pma_ln_vals) { + reg_pairs = phy_pma_ln_vals->reg_pairs; + num_regs = phy_pma_ln_vals->num_regs; + for (i = 0; i < ins->num_lanes; i++) { + regmap = phy->regmap_phy_pma_lane_cdb[i + ins->mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); + } + } + /* PMA common registers configurations */ pma_cmn_vals = init_data->pma_cmn_vals[phy_type][TYPE_NONE][ssc]; if (pma_cmn_vals) { @@ -502,10 +553,13 @@ static int cdns_sierra_phy_on(struct phy *gphy) u32 val; int ret; - ret = reset_control_deassert(sp->phy_rst); - if (ret) { - dev_err(dev, "Failed to take the PHY out of reset\n"); - return ret; + if (sp->nsubnodes == 1) { + /* Take the PHY out of reset */ + ret = reset_control_deassert(sp->phy_rst); + if (ret) { + dev_err(dev, "Failed to take the PHY out of reset\n"); + return ret; + } } /* Take the PHY lane group out of reset */ @@ -923,6 +977,19 @@ static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp, } sp->regmap_phy_pma_common_cdb = regmap; + for (i = 0; i < SIERRA_MAX_LANES; i++) { + block_offset = SIERRA_PHY_PMA_LANE_CDB_OFFSET(i, block_offset_shift, + reg_offset_shift); + regmap = cdns_regmap_init(dev, base, block_offset, + reg_offset_shift, + &cdns_sierra_phy_pma_lane_cdb_config[i]); + if (IS_ERR(regmap)) { + dev_err(dev, "Failed to init PHY PMA lane CDB regmap\n"); + return PTR_ERR(regmap); + } + sp->regmap_phy_pma_lane_cdb[i] = regmap; + } + return 0; } @@ -1030,6 +1097,118 @@ static int cdns_sierra_phy_get_resets(struct cdns_sierra_phy *sp, return 0; } +static int cdns_sierra_phy_configure_multilink(struct cdns_sierra_phy *sp) +{ + const struct cdns_sierra_data *init_data = sp->init_data; + struct cdns_sierra_vals *pma_cmn_vals, *pma_ln_vals; + enum cdns_sierra_phy_type phy_t1, phy_t2; + struct cdns_sierra_vals *phy_pma_ln_vals; + const struct cdns_reg_pairs *reg_pairs; + struct cdns_sierra_vals *pcs_cmn_vals; + int i, j, node, mlane, num_lanes, ret; + enum cdns_sierra_ssc_mode ssc; + struct regmap *regmap; + u32 num_regs; + + /* Maximum 2 links (subnodes) are supported */ + if (sp->nsubnodes != 2) + return -EINVAL; + + clk_set_rate(sp->input_clks[CMN_REFCLK_DIG_DIV], 25000000); + clk_set_rate(sp->input_clks[CMN_REFCLK1_DIG_DIV], 25000000); + + /* PHY configured to use both PLL LC and LC1 */ + regmap_field_write(sp->phy_pll_cfg_1, 0x1); + + phy_t1 = sp->phys[0].phy_type; + phy_t2 = sp->phys[1].phy_type; + + /* + * PHY configuration for multi-link operation is done in two steps. + * e.g. Consider a case for a 4 lane PHY with PCIe using 2 lanes and QSGMII other 2 lanes. + * Sierra PHY has 2 PLLs, viz. PLLLC and PLLLC1. So in this case, PLLLC is used for PCIe + * and PLLLC1 is used for QSGMII. PHY is configured in two steps as described below. + * + * [1] For first step, phy_t1 = TYPE_PCIE and phy_t2 = TYPE_QSGMII + * So the register values are selected as [TYPE_PCIE][TYPE_QSGMII][ssc]. + * This will configure PHY registers associated for PCIe (i.e. first protocol) + * involving PLLLC registers and registers for first 2 lanes of PHY. + * [2] In second step, the variables phy_t1 and phy_t2 are swapped. So now, + * phy_t1 = TYPE_QSGMII and phy_t2 = TYPE_PCIE. And the register values are selected as + * [TYPE_QSGMII][TYPE_PCIE][ssc]. + * This will configure PHY registers associated for QSGMII (i.e. second protocol) + * involving PLLLC1 registers and registers for other 2 lanes of PHY. + * + * This completes the PHY configuration for multilink operation. This approach enables + * dividing the large number of PHY register configurations into protocol specific + * smaller groups. + */ + for (node = 0; node < sp->nsubnodes; node++) { + if (node == 1) { + /* + * If first link with phy_t1 is configured, then configure the PHY for + * second link with phy_t2. Get the array values as [phy_t2][phy_t1][ssc]. + */ + swap(phy_t1, phy_t2); + } + + mlane = sp->phys[node].mlane; + ssc = sp->phys[node].ssc_mode; + num_lanes = sp->phys[node].num_lanes; + + /* PHY PCS common registers configurations */ + pcs_cmn_vals = init_data->pcs_cmn_vals[phy_t1][phy_t2][ssc]; + if (pcs_cmn_vals) { + reg_pairs = pcs_cmn_vals->reg_pairs; + num_regs = pcs_cmn_vals->num_regs; + regmap = sp->regmap_phy_pcs_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } + + /* PHY PMA lane registers configurations */ + phy_pma_ln_vals = init_data->phy_pma_ln_vals[phy_t1][phy_t2][ssc]; + if (phy_pma_ln_vals) { + reg_pairs = phy_pma_ln_vals->reg_pairs; + num_regs = phy_pma_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = sp->regmap_phy_pma_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); + } + } + + /* PMA common registers configurations */ + pma_cmn_vals = init_data->pma_cmn_vals[phy_t1][phy_t2][ssc]; + if (pma_cmn_vals) { + reg_pairs = pma_cmn_vals->reg_pairs; + num_regs = pma_cmn_vals->num_regs; + regmap = sp->regmap_common_cdb; + for (i = 0; i < num_regs; i++) + regmap_write(regmap, reg_pairs[i].off, reg_pairs[i].val); + } + + /* PMA lane registers configurations */ + pma_ln_vals = init_data->pma_ln_vals[phy_t1][phy_t2][ssc]; + if (pma_ln_vals) { + reg_pairs = pma_ln_vals->reg_pairs; + num_regs = pma_ln_vals->num_regs; + for (i = 0; i < num_lanes; i++) { + regmap = sp->regmap_lane_cdb[i + mlane]; + for (j = 0; j < num_regs; j++) + regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); + } + } + } + + /* Take the PHY out of reset */ + ret = reset_control_deassert(sp->phy_rst); + if (ret) + return ret; + + return 0; +} + static int cdns_sierra_phy_probe(struct platform_device *pdev) { struct cdns_sierra_phy *sp; @@ -1149,8 +1328,11 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev) } /* If more than one subnode, configure the PHY as multilink */ - if (!sp->autoconf && sp->nsubnodes > 1) - regmap_field_write(sp->phy_pll_cfg_1, 0x1); + if (!sp->autoconf && sp->nsubnodes > 1) { + ret = cdns_sierra_phy_configure_multilink(sp); + if (ret) + goto put_child2; + } pm_runtime_enable(dev); phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); -- cgit v1.2.3 From 8a1b82d744a97949f13acee6644b19eb3b5a4102 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:35 +0100 Subject: phy: cadence: Sierra: Add PCIe + QSGMII PHY multilink configuration Add register sequences for PCIe + QSGMII PHY multilink configuration. PHY configuration for multi-link operation is done in two steps. e.g. Consider a case for a 4 lane PHY with PCIe using 2 lanes and QSGMII other 2 lanes. Sierra PHY has 2 PLLs, viz. PLLLC and PLLLC1. So in this case, PLLLC is used for PCIe and PLLLC1 is used for QSGMII. PHY is configured in two steps as described below. [1] For first step, the register values are selected as [TYPE_PCIE][TYPE_QSGMII][ssc]. This will configure PHY registers associated for PCIe involving PLLLC registers and registers for first 2 lanes of PHY. [2] In second step, the register values are selected as [TYPE_QSGMII][TYPE_PCIE][ssc]. This will configure PHY registers associated for QSGMII involving PLLLC1 registers and registers for other 2 lanes of PHY. This completes the PHY configuration for multilink operation. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-14-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 377 ++++++++++++++++++++++++++++++- 1 file changed, 376 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 8f356bf38be8..e2b0530ca04c 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -45,6 +45,9 @@ #define SIERRA_CMN_REFRCV_PREG 0x98 #define SIERRA_CMN_REFRCV1_PREG 0xB8 #define SIERRA_CMN_PLLLC1_GEN_PREG 0xC2 +#define SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG 0xCA +#define SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG 0xD0 +#define SIERRA_CMN_PLLLC1_SS_TIME_STEPSIZE_MODE_PREG 0xE2 #define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ ((0x4000 << (block_offset)) + \ @@ -59,6 +62,9 @@ #define SIERRA_PSM_A0IN_TMR_PREG 0x009 #define SIERRA_PSM_A3IN_TMR_PREG 0x00C #define SIERRA_PSM_DIAG_PREG 0x015 +#define SIERRA_PSC_LN_A3_PREG 0x023 +#define SIERRA_PSC_LN_A4_PREG 0x024 +#define SIERRA_PSC_LN_IDLE_PREG 0x026 #define SIERRA_PSC_TX_A0_PREG 0x028 #define SIERRA_PSC_TX_A1_PREG 0x029 #define SIERRA_PSC_TX_A2_PREG 0x02A @@ -68,6 +74,7 @@ #define SIERRA_PSC_RX_A2_PREG 0x032 #define SIERRA_PSC_RX_A3_PREG 0x033 #define SIERRA_PLLCTRL_SUBRATE_PREG 0x03A +#define SIERRA_PLLCTRL_GEN_A_PREG 0x03B #define SIERRA_PLLCTRL_GEN_D_PREG 0x03E #define SIERRA_PLLCTRL_CPGAIN_MODE_PREG 0x03F #define SIERRA_PLLCTRL_STATUS_PREG 0x044 @@ -150,6 +157,7 @@ #define SIERRA_CPICAL_TMRVAL_MODE0_PREG 0x171 #define SIERRA_CPICAL_PICNT_MODE1_PREG 0x174 #define SIERRA_CPI_OUTBUF_RATESEL_PREG 0x17C +#define SIERRA_CPI_RESBIAS_BIN_PREG 0x17E #define SIERRA_CPI_TRIM_PREG 0x17F #define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG 0x183 #define SIERRA_EPI_CTRL_PREG 0x187 @@ -272,7 +280,8 @@ static u32 cdns_sierra_pll_mux_table[][SIERRA_NUM_CMN_PLLC_PARENTS] = { enum cdns_sierra_phy_type { TYPE_NONE, TYPE_PCIE, - TYPE_USB + TYPE_USB, + TYPE_QSGMII }; enum cdns_sierra_ssc_mode { @@ -807,6 +816,9 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst, case PHY_TYPE_USB3: inst->phy_type = TYPE_USB; break; + case PHY_TYPE_QSGMII: + inst->phy_type = TYPE_QSGMII; + break; default: return -EINVAL; } @@ -1199,6 +1211,9 @@ static int cdns_sierra_phy_configure_multilink(struct cdns_sierra_phy *sp) regmap_write(regmap, reg_pairs[j].off, reg_pairs[j].val); } } + + if (phy_t1 == TYPE_QSGMII) + reset_control_deassert(sp->phys[node].lnk_rst); } /* Take the PHY out of reset */ @@ -1376,6 +1391,72 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev) return 0; } +/* QSGMII PHY PMA lane configuration */ +static struct cdns_reg_pairs qsgmii_phy_pma_ln_regs[] = { + {0x9010, SIERRA_PHY_PMA_XCVR_CTRL} +}; + +static struct cdns_sierra_vals qsgmii_phy_pma_ln_vals = { + .reg_pairs = qsgmii_phy_pma_ln_regs, + .num_regs = ARRAY_SIZE(qsgmii_phy_pma_ln_regs), +}; + +/* QSGMII refclk 100MHz, 20b, opt1, No BW cal, no ssc, PLL LC1 */ +static const struct cdns_reg_pairs qsgmii_100_no_ssc_plllc1_cmn_regs[] = { + {0x2085, SIERRA_CMN_PLLLC1_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC1_BWCAL_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC1_SS_TIME_STEPSIZE_MODE_PREG} +}; + +static const struct cdns_reg_pairs qsgmii_100_no_ssc_plllc1_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x0252, SIERRA_DET_STANDEC_E_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x0FFE, SIERRA_PSC_RX_A0_PREG}, + {0x0011, SIERRA_PLLCTRL_SUBRATE_PREG}, + {0x0001, SIERRA_PLLCTRL_GEN_A_PREG}, + {0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG}, + {0x0000, SIERRA_DRVCTRL_ATTEN_PREG}, + {0x0089, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x3C3C, SIERRA_CREQ_CCLKDET_MODE01_PREG}, + {0x3222, SIERRA_CREQ_FSMCLK_SEL_PREG}, + {0x0000, SIERRA_CREQ_EQ_CTRL_PREG}, + {0x8422, SIERRA_CTLELUT_CTRL_PREG}, + {0x4111, SIERRA_DFE_ECMP_RATESEL_PREG}, + {0x4111, SIERRA_DFE_SMP_RATESEL_PREG}, + {0x0002, SIERRA_DEQ_PHALIGN_CTRL}, + {0x9595, SIERRA_DEQ_VGATUNE_CTRL_PREG}, + {0x0186, SIERRA_DEQ_GLUT0}, + {0x0186, SIERRA_DEQ_GLUT1}, + {0x0186, SIERRA_DEQ_GLUT2}, + {0x0186, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x0861, SIERRA_DEQ_ALUT0}, + {0x07E0, SIERRA_DEQ_ALUT1}, + {0x079E, SIERRA_DEQ_ALUT2}, + {0x071D, SIERRA_DEQ_ALUT3}, + {0x03F5, SIERRA_DEQ_DFETAP_CTRL_PREG}, + {0x0C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG}, + {0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C04, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0033, SIERRA_DEQ_PICTRL_PREG}, + {0x0660, SIERRA_CPICAL_TMRVAL_MODE0_PREG}, + {0x00D5, SIERRA_CPI_OUTBUF_RATESEL_PREG}, + {0x0B6D, SIERRA_CPI_RESBIAS_BIN_PREG}, + {0x0102, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x0002, SIERRA_RXBUFFER_RCDFECTRL_PREG} +}; + +static struct cdns_sierra_vals qsgmii_100_no_ssc_plllc1_cmn_vals = { + .reg_pairs = qsgmii_100_no_ssc_plllc1_cmn_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_plllc1_cmn_regs), +}; + +static struct cdns_sierra_vals qsgmii_100_no_ssc_plllc1_ln_vals = { + .reg_pairs = qsgmii_100_no_ssc_plllc1_ln_regs, + .num_regs = ARRAY_SIZE(qsgmii_100_no_ssc_plllc1_ln_regs), +}; + /* PCIE PHY PCS common configuration */ static struct cdns_reg_pairs pcie_phy_pcs_cmn_regs[] = { {0x0430, SIERRA_PHY_PIPE_CMN_CTRL1} @@ -1386,6 +1467,233 @@ static struct cdns_sierra_vals pcie_phy_pcs_cmn_vals = { .num_regs = ARRAY_SIZE(pcie_phy_pcs_cmn_regs), }; +/* refclk100MHz_32b_PCIe_cmn_pll_no_ssc, pcie_links_using_plllc, pipe_bw_3 */ +static const struct cdns_reg_pairs pcie_100_no_ssc_plllc_cmn_regs[] = { + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG} +}; + +/* + * refclk100MHz_32b_PCIe_ln_no_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ml_pcie_100_no_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x8055, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x80BB, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x8351, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x8349, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_no_ssc_plllc_cmn_vals = { + .reg_pairs = pcie_100_no_ssc_plllc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_no_ssc_plllc_cmn_regs), +}; + +static struct cdns_sierra_vals ml_pcie_100_no_ssc_ln_vals = { + .reg_pairs = ml_pcie_100_no_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ml_pcie_100_no_ssc_ln_regs), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_int_ssc, pcie_links_using_plllc, pipe_bw_3 */ +static const struct cdns_reg_pairs pcie_100_int_ssc_plllc_cmn_regs[] = { + {0x000E, SIERRA_CMN_PLLLC_MODE_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x4006, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x0581, SIERRA_CMN_PLLLC_DSMCORR_PREG}, + {0x7F80, SIERRA_CMN_PLLLC_SS_PREG}, + {0x0041, SIERRA_CMN_PLLLC_SS_AMP_STEP_SIZE_PREG}, + {0x0464, SIERRA_CMN_PLLLC_SSTWOPT_PREG}, + {0x0D0D, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}, + {0x0060, SIERRA_CMN_PLLLC_LOCK_DELAY_CTRL_PREG} +}; + +/* + * refclk100MHz_32b_PCIe_ln_int_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ml_pcie_100_int_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_int_ssc_plllc_cmn_vals = { + .reg_pairs = pcie_100_int_ssc_plllc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_int_ssc_plllc_cmn_regs), +}; + +static struct cdns_sierra_vals ml_pcie_100_int_ssc_ln_vals = { + .reg_pairs = ml_pcie_100_int_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ml_pcie_100_int_ssc_ln_regs), +}; + +/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc, pcie_links_using_plllc, pipe_bw_3 */ +static const struct cdns_reg_pairs pcie_100_ext_ssc_plllc_cmn_regs[] = { + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, + {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG}, + {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG}, + {0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG} +}; + +/* + * refclk100MHz_32b_PCIe_ln_ext_ssc, multilink, using_plllc, + * cmn_pllcy_anaclk0_1Ghz, xcvr_pllclk_fullrt_500mhz + */ +static const struct cdns_reg_pairs ml_pcie_100_ext_ssc_ln_regs[] = { + {0xFC08, SIERRA_DET_STANDEC_A_PREG}, + {0x001D, SIERRA_PSM_A3IN_TMR_PREG}, + {0x0004, SIERRA_PSC_LN_A3_PREG}, + {0x0004, SIERRA_PSC_LN_A4_PREG}, + {0x0004, SIERRA_PSC_LN_IDLE_PREG}, + {0x1555, SIERRA_DFE_BIASTRIM_PREG}, + {0x9703, SIERRA_DRVCTRL_BOOST_PREG}, + {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG}, + {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG}, + {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG}, + {0x0002, SIERRA_CREQ_DCBIASATTEN_OVR_PREG}, + {0x9800, SIERRA_RX_CTLE_CAL_PREG}, + {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG}, + {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}, + {0x5624, SIERRA_DEQ_CONCUR_CTRL2_PREG}, + {0x000F, SIERRA_DEQ_EPIPWR_CTRL2_PREG}, + {0x00FF, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG}, + {0x4C4C, SIERRA_DEQ_ERRCMP_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_OFFSET_CTRL_PREG}, + {0x02FA, SIERRA_DEQ_GAIN_CTRL_PREG}, + {0x0041, SIERRA_DEQ_GLUT0}, + {0x0082, SIERRA_DEQ_GLUT1}, + {0x00C3, SIERRA_DEQ_GLUT2}, + {0x0145, SIERRA_DEQ_GLUT3}, + {0x0186, SIERRA_DEQ_GLUT4}, + {0x09E7, SIERRA_DEQ_ALUT0}, + {0x09A6, SIERRA_DEQ_ALUT1}, + {0x0965, SIERRA_DEQ_ALUT2}, + {0x08E3, SIERRA_DEQ_ALUT3}, + {0x00FA, SIERRA_DEQ_DFETAP0}, + {0x00FA, SIERRA_DEQ_DFETAP1}, + {0x00FA, SIERRA_DEQ_DFETAP2}, + {0x00FA, SIERRA_DEQ_DFETAP3}, + {0x00FA, SIERRA_DEQ_DFETAP4}, + {0x000F, SIERRA_DEQ_PRECUR_PREG}, + {0x0280, SIERRA_DEQ_POSTCUR_PREG}, + {0x8F00, SIERRA_DEQ_POSTCUR_DECR_PREG}, + {0x3C0F, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG}, + {0x1C0C, SIERRA_DEQ_TAU_CTRL2_PREG}, + {0x0100, SIERRA_DEQ_TAU_CTRL3_PREG}, + {0x5E82, SIERRA_DEQ_OPENEYE_CTRL_PREG}, + {0x002B, SIERRA_CPI_TRIM_PREG}, + {0x0003, SIERRA_EPI_CTRL_PREG}, + {0x803F, SIERRA_SDFILT_H2L_A_PREG}, + {0x0004, SIERRA_RXBUFFER_CTLECTRL_PREG}, + {0x2010, SIERRA_RXBUFFER_RCDFECTRL_PREG}, + {0x4432, SIERRA_RXBUFFER_DFECTRL_PREG} +}; + +static struct cdns_sierra_vals pcie_100_ext_ssc_plllc_cmn_vals = { + .reg_pairs = pcie_100_ext_ssc_plllc_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_100_ext_ssc_plllc_cmn_regs), +}; + +static struct cdns_sierra_vals ml_pcie_100_ext_ssc_ln_vals = { + .reg_pairs = ml_pcie_100_ext_ssc_ln_regs, + .num_regs = ARRAY_SIZE(ml_pcie_100_ext_ssc_ln_regs), +}; + /* refclk100MHz_32b_PCIe_cmn_pll_no_ssc */ static const struct cdns_reg_pairs cdns_pcie_cmn_regs_no_ssc[] = { {0x2105, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG}, @@ -1723,6 +2031,11 @@ static const struct cdns_sierra_data cdns_map_sierra = { [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, }, }, .pma_cmn_vals = { @@ -1732,12 +2045,24 @@ static const struct cdns_sierra_data cdns_map_sierra = { [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_plllc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_plllc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_plllc_cmn_vals, + }, }, [TYPE_USB] = { [TYPE_NONE] = { [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals, }, }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + }, + }, }, .pma_ln_vals = { [TYPE_PCIE] = { @@ -1746,12 +2071,24 @@ static const struct cdns_sierra_data cdns_map_sierra = { [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals, }, + [TYPE_QSGMII] = { + [NO_SSC] = &ml_pcie_100_no_ssc_ln_vals, + [EXTERNAL_SSC] = &ml_pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &ml_pcie_100_int_ssc_ln_vals, + }, }, [TYPE_USB] = { [TYPE_NONE] = { [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals, }, }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + }, + }, }, }; @@ -1766,6 +2103,20 @@ static const struct cdns_sierra_data cdns_ti_map_sierra = { [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_phy_pcs_cmn_vals, + [EXTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + [INTERNAL_SSC] = &pcie_phy_pcs_cmn_vals, + }, + }, + }, + .phy_pma_ln_vals = { + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_phy_pma_ln_vals, + [EXTERNAL_SSC] = &qsgmii_phy_pma_ln_vals, + [INTERNAL_SSC] = &qsgmii_phy_pma_ln_vals, + }, }, }, .pma_cmn_vals = { @@ -1775,12 +2126,24 @@ static const struct cdns_sierra_data cdns_ti_map_sierra = { [EXTERNAL_SSC] = &pcie_100_ext_ssc_cmn_vals, [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, }, + [TYPE_QSGMII] = { + [NO_SSC] = &pcie_100_no_ssc_plllc_cmn_vals, + [EXTERNAL_SSC] = &pcie_100_ext_ssc_plllc_cmn_vals, + [INTERNAL_SSC] = &pcie_100_int_ssc_plllc_cmn_vals, + }, }, [TYPE_USB] = { [TYPE_NONE] = { [EXTERNAL_SSC] = &usb_100_ext_ssc_cmn_vals, }, }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_cmn_vals, + }, + }, }, .pma_ln_vals = { [TYPE_PCIE] = { @@ -1789,12 +2152,24 @@ static const struct cdns_sierra_data cdns_ti_map_sierra = { [EXTERNAL_SSC] = &pcie_100_ext_ssc_ln_vals, [INTERNAL_SSC] = &pcie_100_int_ssc_ln_vals, }, + [TYPE_QSGMII] = { + [NO_SSC] = &ml_pcie_100_no_ssc_ln_vals, + [EXTERNAL_SSC] = &ml_pcie_100_ext_ssc_ln_vals, + [INTERNAL_SSC] = &ml_pcie_100_int_ssc_ln_vals, + }, }, [TYPE_USB] = { [TYPE_NONE] = { [EXTERNAL_SSC] = &usb_100_ext_ssc_ln_vals, }, }, + [TYPE_QSGMII] = { + [TYPE_PCIE] = { + [NO_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [EXTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + [INTERNAL_SSC] = &qsgmii_100_no_ssc_plllc1_ln_vals, + }, + }, }, }; -- cgit v1.2.3 From 637feefb8ac53fbe1147edb707b03dc09839fdf5 Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:36 +0100 Subject: dt-bindings: phy: cadence-sierra: Add clock ID for derived reference clock Add clock ID for Sierra derived reference clock. Signed-off-by: Swapnil Jakhade Acked-by: Rob Herring Link: https://lore.kernel.org/r/20211223060137.9252-15-sjakhade@cadence.com Signed-off-by: Vinod Koul --- include/dt-bindings/phy/phy-cadence.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/phy/phy-cadence.h b/include/dt-bindings/phy/phy-cadence.h index d55fe6e6b936..0671991208fc 100644 --- a/include/dt-bindings/phy/phy-cadence.h +++ b/include/dt-bindings/phy/phy-cadence.h @@ -18,5 +18,6 @@ /* Sierra */ #define CDNS_SIERRA_PLL_CMNLC 0 #define CDNS_SIERRA_PLL_CMNLC1 1 +#define CDNS_SIERRA_DERIVED_REFCLK 2 #endif /* _DT_BINDINGS_CADENCE_SERDES_H */ -- cgit v1.2.3 From 09d976b3e8e257ff44405b6506bbaae6be1a6b3c Mon Sep 17 00:00:00 2001 From: Swapnil Jakhade Date: Thu, 23 Dec 2021 07:01:37 +0100 Subject: phy: cadence: Sierra: Add support for derived reference clock output Sierra has derived differential reference clock output which is sourced after the spread spectrum generation has been added. Add support to drive derived reference clock out of serdes. Model this derived clock as a "clock" so that platforms using this can enable it. Sierra Main LC VCO PLL divider 1 clock is programmed to output 100MHz clock output. Signed-off-by: Swapnil Jakhade Reviewed-by: Aswath Govindraju Link: https://lore.kernel.org/r/20211223060137.9252-16-sjakhade@cadence.com Signed-off-by: Vinod Koul --- drivers/phy/cadence/phy-cadence-sierra.c | 109 ++++++++++++++++++++++++++++++- 1 file changed, 108 insertions(+), 1 deletion(-) diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index e2b0530ca04c..da24acd26666 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -34,6 +34,7 @@ #define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG 0x49 #define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG 0x4A #define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG 0x4B +#define SIERRA_CMN_PLLLC_CLK1_PREG 0x4D #define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG 0x4F #define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG 0x50 #define SIERRA_CMN_PLLLC_DSMCORR_PREG 0x51 @@ -203,7 +204,7 @@ #define SIERRA_MAX_LANES 16 #define PLL_LOCK_TIME 100000 -#define CDNS_SIERRA_OUTPUT_CLOCKS 2 +#define CDNS_SIERRA_OUTPUT_CLOCKS 3 #define CDNS_SIERRA_INPUT_CLOCKS 5 enum cdns_sierra_clock_input { PHY_CLK, @@ -226,10 +227,15 @@ static const struct reg_field pllctrl_lock = REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0); static const struct reg_field phy_iso_link_ctrl_1 = REG_FIELD(SIERRA_PHY_ISO_LINK_CTRL, 1, 1); +static const struct reg_field cmn_plllc_clk1outdiv_preg = + REG_FIELD(SIERRA_CMN_PLLLC_CLK1_PREG, 0, 6); +static const struct reg_field cmn_plllc_clk1_en_preg = + REG_FIELD(SIERRA_CMN_PLLLC_CLK1_PREG, 12, 12); static const char * const clk_names[] = { [CDNS_SIERRA_PLL_CMNLC] = "pll_cmnlc", [CDNS_SIERRA_PLL_CMNLC1] = "pll_cmnlc1", + [CDNS_SIERRA_DERIVED_REFCLK] = "refclk_der", }; enum cdns_sierra_cmn_plllc { @@ -277,6 +283,16 @@ static u32 cdns_sierra_pll_mux_table[][SIERRA_NUM_CMN_PLLC_PARENTS] = { [CMN_PLLLC1] = { 1, 0 }, }; +struct cdns_sierra_derived_refclk { + struct clk_hw hw; + struct regmap_field *cmn_plllc_clk1outdiv_preg; + struct regmap_field *cmn_plllc_clk1_en_preg; + struct clk_init_data clk_data; +}; + +#define to_cdns_sierra_derived_refclk(_hw) \ + container_of(_hw, struct cdns_sierra_derived_refclk, hw) + enum cdns_sierra_phy_type { TYPE_NONE, TYPE_PCIE, @@ -766,6 +782,91 @@ static int cdns_sierra_phy_register_pll_mux(struct cdns_sierra_phy *sp) return 0; } +static int cdns_sierra_derived_refclk_enable(struct clk_hw *hw) +{ + struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw); + + regmap_field_write(derived_refclk->cmn_plllc_clk1_en_preg, 0x1); + + /* Programming to get 100Mhz clock output in ref_der_clk_out 5GHz VCO/50 = 100MHz */ + regmap_field_write(derived_refclk->cmn_plllc_clk1outdiv_preg, 0x2E); + + return 0; +} + +static void cdns_sierra_derived_refclk_disable(struct clk_hw *hw) +{ + struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw); + + regmap_field_write(derived_refclk->cmn_plllc_clk1_en_preg, 0); +} + +static int cdns_sierra_derived_refclk_is_enabled(struct clk_hw *hw) +{ + struct cdns_sierra_derived_refclk *derived_refclk = to_cdns_sierra_derived_refclk(hw); + int val; + + regmap_field_read(derived_refclk->cmn_plllc_clk1_en_preg, &val); + + return !!val; +} + +static const struct clk_ops cdns_sierra_derived_refclk_ops = { + .enable = cdns_sierra_derived_refclk_enable, + .disable = cdns_sierra_derived_refclk_disable, + .is_enabled = cdns_sierra_derived_refclk_is_enabled, +}; + +static int cdns_sierra_derived_refclk_register(struct cdns_sierra_phy *sp) +{ + struct cdns_sierra_derived_refclk *derived_refclk; + struct device *dev = sp->dev; + struct regmap_field *field; + struct clk_init_data *init; + struct regmap *regmap; + char clk_name[100]; + struct clk *clk; + + derived_refclk = devm_kzalloc(dev, sizeof(*derived_refclk), GFP_KERNEL); + if (!derived_refclk) + return -ENOMEM; + + snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev), + clk_names[CDNS_SIERRA_DERIVED_REFCLK]); + + init = &derived_refclk->clk_data; + + init->ops = &cdns_sierra_derived_refclk_ops; + init->flags = 0; + init->name = clk_name; + + regmap = sp->regmap_common_cdb; + + field = devm_regmap_field_alloc(dev, regmap, cmn_plllc_clk1outdiv_preg); + if (IS_ERR(field)) { + dev_err(dev, "cmn_plllc_clk1outdiv_preg reg field init failed\n"); + return PTR_ERR(field); + } + derived_refclk->cmn_plllc_clk1outdiv_preg = field; + + field = devm_regmap_field_alloc(dev, regmap, cmn_plllc_clk1_en_preg); + if (IS_ERR(field)) { + dev_err(dev, "cmn_plllc_clk1_en_preg reg field init failed\n"); + return PTR_ERR(field); + } + derived_refclk->cmn_plllc_clk1_en_preg = field; + + derived_refclk->hw.init = init; + + clk = devm_clk_register(dev, &derived_refclk->hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + sp->output_clks[CDNS_SIERRA_DERIVED_REFCLK] = clk; + + return 0; +} + static void cdns_sierra_clk_unregister(struct cdns_sierra_phy *sp) { struct device *dev = sp->dev; @@ -786,6 +887,12 @@ static int cdns_sierra_clk_register(struct cdns_sierra_phy *sp) return ret; } + ret = cdns_sierra_derived_refclk_register(sp); + if (ret) { + dev_err(dev, "Failed to register derived refclk\n"); + return ret; + } + sp->clk_data.clks = sp->output_clks; sp->clk_data.clk_num = CDNS_SIERRA_OUTPUT_CLOCKS; ret = of_clk_add_provider(node, of_clk_src_onecell_get, &sp->clk_data); -- cgit v1.2.3 From 38ac2f038666521f94d4fa37b5a9441cef832ccf Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Fri, 24 Dec 2021 07:08:33 -0800 Subject: iio: chemical: sunrise_co2: set val parameter only on success Clang static analysis reports this representative warning sunrise_co2.c:410:9: warning: Assigned value is garbage or undefined *val = value; ^ ~~~~~ The ealier call to sunrise_read_word can fail without setting value. So defer setting val until we know the read was successful. Fixes: c397894e24f1 ("iio: chemical: Add Senseair Sunrise 006-0-007 driver") Signed-off-by: Tom Rix Link: https://lore.kernel.org/r/20211224150833.3278236-1-trix@redhat.com Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/sunrise_co2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/chemical/sunrise_co2.c b/drivers/iio/chemical/sunrise_co2.c index 233bd0f379c9..8440dc0c77cf 100644 --- a/drivers/iio/chemical/sunrise_co2.c +++ b/drivers/iio/chemical/sunrise_co2.c @@ -407,24 +407,24 @@ static int sunrise_read_raw(struct iio_dev *iio_dev, mutex_lock(&sunrise->lock); ret = sunrise_read_word(sunrise, SUNRISE_CO2_FILTERED_COMP_REG, &value); - *val = value; mutex_unlock(&sunrise->lock); if (ret) return ret; + *val = value; return IIO_VAL_INT; case IIO_TEMP: mutex_lock(&sunrise->lock); ret = sunrise_read_word(sunrise, SUNRISE_CHIP_TEMPERATURE_REG, &value); - *val = value; mutex_unlock(&sunrise->lock); if (ret) return ret; + *val = value; return IIO_VAL_INT; default: -- cgit v1.2.3 From 0ac467447dde20d3b186b13cb315644483416c52 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Dec 2021 14:13:19 +0100 Subject: UIO: use default_groups in kobj_type There are currently 2 ways to create a set of sysfs files for a kobj_type, through the default_attrs field, and the default_groups field. Move the UIO code to use default_groups field which has been the preferred way since aa30f47cf666 ("kobject: Add support for default attribute groups to kobj_type") so that we can soon get rid of the obsolete default_attrs field. Link: https://lore.kernel.org/r/20211228131319.249324-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/uio/uio.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index ea96e319c8a0..43afbb7c5ab9 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -83,13 +83,14 @@ static struct map_sysfs_entry size_attribute = static struct map_sysfs_entry offset_attribute = __ATTR(offset, S_IRUGO, map_offset_show, NULL); -static struct attribute *attrs[] = { +static struct attribute *map_attrs[] = { &name_attribute.attr, &addr_attribute.attr, &size_attribute.attr, &offset_attribute.attr, NULL, /* need to NULL terminate the list of attributes */ }; +ATTRIBUTE_GROUPS(map); static void map_release(struct kobject *kobj) { @@ -119,7 +120,7 @@ static const struct sysfs_ops map_sysfs_ops = { static struct kobj_type map_attr_type = { .release = map_release, .sysfs_ops = &map_sysfs_ops, - .default_attrs = attrs, + .default_groups = map_groups, }; struct uio_portio { @@ -178,6 +179,7 @@ static struct attribute *portio_attrs[] = { &portio_porttype_attribute.attr, NULL, }; +ATTRIBUTE_GROUPS(portio); static void portio_release(struct kobject *kobj) { @@ -207,7 +209,7 @@ static const struct sysfs_ops portio_sysfs_ops = { static struct kobj_type portio_attr_type = { .release = portio_release, .sysfs_ops = &portio_sysfs_ops, - .default_attrs = portio_attrs, + .default_groups = portio_groups, }; static ssize_t name_show(struct device *dev, -- cgit v1.2.3 From 63064451d0b8359999e7e8c4fd92951d96f5a057 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 28 Dec 2021 14:13:50 +0100 Subject: cxl: use default_groups in kobj_type There are currently 2 ways to create a set of sysfs files for a kobj_type, through the default_attrs field, and the default_groups field. Move the cxl code to use default_groups field which has been the preferred way since aa30f47cf666 ("kobject: Add support for default attribute groups to kobj_type") so that we can soon get rid of the obsolete default_attrs field. Cc: Frederic Barrat Cc: Andrew Donnellan Cc: Arnd Bergmann Link: https://lore.kernel.org/r/20211228131350.249532-1-gregkh@linuxfoundation.org Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cxl/sysfs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index c173a5e88c91..315c43f17dd3 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -570,6 +570,7 @@ static struct attribute *afu_cr_attrs[] = { &class_attribute.attr, NULL, }; +ATTRIBUTE_GROUPS(afu_cr); static void release_afu_config_record(struct kobject *kobj) { @@ -581,7 +582,7 @@ static void release_afu_config_record(struct kobject *kobj) static struct kobj_type afu_config_record_type = { .sysfs_ops = &kobj_sysfs_ops, .release = release_afu_config_record, - .default_attrs = afu_cr_attrs, + .default_groups = afu_cr_groups, }; static struct afu_config_record *cxl_sysfs_afu_new_cr(struct cxl_afu *afu, int cr_idx) -- cgit v1.2.3 From fcee5ce50bdb21116711e38635e3865594af907e Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Tue, 28 Dec 2021 12:55:22 +0000 Subject: misc: lattice-ecp3-config: Fix task hung when firmware load failed When firmware load failed, kernel report task hung as follows: INFO: task xrun:5191 blocked for more than 147 seconds. Tainted: G W 5.16.0-rc5-next-20211220+ #11 "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message. task:xrun state:D stack: 0 pid: 5191 ppid: 270 flags:0x00000004 Call Trace: __schedule+0xc12/0x4b50 kernel/sched/core.c:4986 schedule+0xd7/0x260 kernel/sched/core.c:6369 (discriminator 1) schedule_timeout+0x7aa/0xa80 kernel/time/timer.c:1857 wait_for_completion+0x181/0x290 kernel/sched/completion.c:85 lattice_ecp3_remove+0x32/0x40 drivers/misc/lattice-ecp3-config.c:221 spi_remove+0x72/0xb0 drivers/spi/spi.c:409 lattice_ecp3_remove() wait for signals from firmware loading, but when load failed, firmware_load() does not send this signal. This cause device remove hung. Fix it by sending signal even if load failed. Fixes: 781551df57c7 ("misc: Add Lattice ECP3 FPGA configuration via SPI") Reported-by: Hulk Robot Signed-off-by: Wei Yongjun Link: https://lore.kernel.org/r/20211228125522.3122284-1-weiyongjun1@huawei.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/lattice-ecp3-config.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/misc/lattice-ecp3-config.c b/drivers/misc/lattice-ecp3-config.c index 0f54730c7ed5..98828030b5a4 100644 --- a/drivers/misc/lattice-ecp3-config.c +++ b/drivers/misc/lattice-ecp3-config.c @@ -76,12 +76,12 @@ static void firmware_load(const struct firmware *fw, void *context) if (fw == NULL) { dev_err(&spi->dev, "Cannot load firmware, aborting\n"); - return; + goto out; } if (fw->size == 0) { dev_err(&spi->dev, "Error: Firmware size is 0!\n"); - return; + goto out; } /* Fill dummy data (24 stuffing bits for commands) */ @@ -103,7 +103,7 @@ static void firmware_load(const struct firmware *fw, void *context) dev_err(&spi->dev, "Error: No supported FPGA detected (JEDEC_ID=%08x)!\n", jedec_id); - return; + goto out; } dev_info(&spi->dev, "FPGA %s detected\n", ecp3_dev[i].name); @@ -116,7 +116,7 @@ static void firmware_load(const struct firmware *fw, void *context) buffer = kzalloc(fw->size + 8, GFP_KERNEL); if (!buffer) { dev_err(&spi->dev, "Error: Can't allocate memory!\n"); - return; + goto out; } /* @@ -155,7 +155,7 @@ static void firmware_load(const struct firmware *fw, void *context) "Error: Timeout waiting for FPGA to clear (status=%08x)!\n", status); kfree(buffer); - return; + goto out; } dev_info(&spi->dev, "Configuring the FPGA...\n"); @@ -181,7 +181,7 @@ static void firmware_load(const struct firmware *fw, void *context) release_firmware(fw); kfree(buffer); - +out: complete(&data->fw_loaded); } -- cgit v1.2.3 From 6b0b80ac103b2a40c72a47c301745fd1f4ef4697 Mon Sep 17 00:00:00 2001 From: Alexander Usyskin Date: Tue, 28 Dec 2021 10:20:47 +0200 Subject: mei: hbm: fix client dma reply status Don't blindly copy status value received from the firmware into internal client status field, It may be positive and ERR_PTR(ret) will translate it into an invalid address and the caller will crash. Put the error code into the client status on failure. Fixes: 369aea845951 ("mei: implement client dma setup.") Cc: # v5.11+ Reported-by: Emmanuel Grumbach Tested-by: : Emmanuel Grumbach Acked-by: Tomas Winkler Signed-off-by: Alexander Usyskin Signed-off-by: Tomas Winkler Link: https://lore.kernel.org/r/20211228082047.378115-1-tomas.winkler@intel.com Signed-off-by: Greg Kroah-Hartman --- drivers/misc/mei/hbm.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index be41843df75b..cebcca6d6d3e 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -672,10 +672,14 @@ static void mei_hbm_cl_dma_map_res(struct mei_device *dev, if (!cl) return; - dev_dbg(dev->dev, "cl dma map result = %d\n", res->status); - cl->status = res->status; - if (!cl->status) + if (res->status) { + dev_err(dev->dev, "cl dma map failed %d\n", res->status); + cl->status = -EFAULT; + } else { + dev_dbg(dev->dev, "cl dma map succeeded\n"); cl->dma_mapped = 1; + cl->status = 0; + } wake_up(&cl->wait); } @@ -698,10 +702,14 @@ static void mei_hbm_cl_dma_unmap_res(struct mei_device *dev, if (!cl) return; - dev_dbg(dev->dev, "cl dma unmap result = %d\n", res->status); - cl->status = res->status; - if (!cl->status) + if (res->status) { + dev_err(dev->dev, "cl dma unmap failed %d\n", res->status); + cl->status = -EFAULT; + } else { + dev_dbg(dev->dev, "cl dma unmap succeeded\n"); cl->dma_mapped = 0; + cl->status = 0; + } wake_up(&cl->wait); } -- cgit v1.2.3 From b56346ddbd82f674db02453ecce60dd91f97b78c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:38 +0100 Subject: counter: Use container_of instead of drvdata to track counter_device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The counter core uses drvdata to find a struct counter_device from a struct device. However as the device is a member of struct counter_device, the lookup can be done faster (and a bit type safe) using container_of. There are no other users of drvdata, so the call to dev_set_drvdata can go away, too. Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-2-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 5acc54539623..f053a43c6c04 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -26,7 +26,8 @@ static DEFINE_IDA(counter_ida); static void counter_device_release(struct device *dev) { - struct counter_device *const counter = dev_get_drvdata(dev); + struct counter_device *const counter = + container_of(dev, struct counter_device, dev); counter_chrdev_remove(counter); ida_free(&counter_ida, dev->id); @@ -78,7 +79,6 @@ int counter_register(struct counter_device *const counter) dev->of_node = counter->parent->of_node; } device_initialize(dev); - dev_set_drvdata(dev, counter); err = counter_sysfs_add(counter); if (err < 0) -- cgit v1.2.3 From 8b2bc10ca2aa8eb6119d943141e2805c593a54e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:39 +0100 Subject: counter: ftm-quaddec: Drop unused platform_set_drvdata() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't ever use platform_get_drvdata, so drop this unused call. Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-3-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/ftm-quaddec.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index 5ef0478709cd..9272f7b58beb 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -269,8 +269,6 @@ static int ftm_quaddec_probe(struct platform_device *pdev) if (!ftm) return -ENOMEM; - platform_set_drvdata(pdev, ftm); - io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) { dev_err(&pdev->dev, "Failed to get memory region\n"); -- cgit v1.2.3 From 0880603c8401505d237c8bfd29538a064e3aaf0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:40 +0100 Subject: counter: microchip-tcb-capture: Drop unused platform_set_drvdata() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The driver doesn't ever use platform_get_drvdata, so drop this unused call. Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-4-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/microchip-tcb-capture.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index 0ab1b2716784..bb69f2e0ba93 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -307,8 +307,6 @@ static int mchp_tc_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); - match = of_match_node(atmel_tc_of_match, np->parent); tcb_config = match->data; if (!tcb_config) { -- cgit v1.2.3 From 5207fb2f311b0c45a9abfa1c84b7a7b657ffa550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:41 +0100 Subject: counter: Provide a wrapper to access device private data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For now this just wraps accessing struct counter_device::priv. However this is about to change and converting drivers to this helper individually makes fixing device lifetime issues result in easier to review patches. Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-5-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-core.c | 12 ++++++++++++ include/linux/counter.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index f053a43c6c04..00c41f28c101 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -45,6 +45,18 @@ static struct bus_type counter_bus_type = { static dev_t counter_devt; +/** + * counter_priv - access counter device private data + * @counter: counter device + * + * Get the counter device private data + */ +void *counter_priv(const struct counter_device *const counter) +{ + return counter->priv; +} +EXPORT_SYMBOL_GPL(counter_priv); + /** * counter_register - register Counter to the system * @counter: pointer to Counter to register diff --git a/include/linux/counter.h b/include/linux/counter.h index dfbde2808998..627f1757f6bb 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -329,6 +329,8 @@ struct counter_device { struct mutex ops_exist_lock; }; +void *counter_priv(const struct counter_device *const counter); + int counter_register(struct counter_device *const counter); void counter_unregister(struct counter_device *const counter); int devm_counter_register(struct device *dev, -- cgit v1.2.3 From aea8334b24feb0e43870e04823611f9a78bd21e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:42 +0100 Subject: counter: 104-quad-8: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-6-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/104-quad-8.c | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index a97027db0446..41b4d6f4583c 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -112,7 +112,7 @@ static int quad8_signal_read(struct counter_device *counter, struct counter_signal *signal, enum counter_signal_level *level) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); unsigned int state; /* Only Index signal levels can be read */ @@ -130,7 +130,7 @@ static int quad8_signal_read(struct counter_device *counter, static int quad8_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const int base_offset = priv->base + 2 * count->id; unsigned int flags; unsigned int borrow; @@ -162,7 +162,7 @@ static int quad8_count_read(struct counter_device *counter, static int quad8_count_write(struct counter_device *counter, struct counter_count *count, u64 val) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const int base_offset = priv->base + 2 * count->id; unsigned long irqflags; int i; @@ -212,7 +212,7 @@ static int quad8_function_read(struct counter_device *counter, struct counter_count *count, enum counter_function *function) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const int id = count->id; unsigned long irqflags; @@ -242,7 +242,7 @@ static int quad8_function_write(struct counter_device *counter, struct counter_count *count, enum counter_function function) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const int id = count->id; unsigned int *const quadrature_mode = priv->quadrature_mode + id; unsigned int *const scale = priv->quadrature_scale + id; @@ -304,7 +304,7 @@ static int quad8_direction_read(struct counter_device *counter, struct counter_count *count, enum counter_count_direction *direction) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); unsigned int ud_flag; const unsigned int flag_addr = priv->base + 2 * count->id + 1; @@ -334,7 +334,7 @@ static int quad8_action_read(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action *action) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); int err; enum counter_function function; const size_t signal_a_id = count->synapses[0].signal->id; @@ -397,7 +397,7 @@ enum { static int quad8_events_configure(struct counter_device *counter) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); unsigned long irq_enabled = 0; unsigned long irqflags; struct counter_event_node *event_node; @@ -495,7 +495,7 @@ static int quad8_index_polarity_get(struct counter_device *counter, struct counter_signal *signal, u32 *index_polarity) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id - 16; *index_polarity = priv->index_polarity[channel_id]; @@ -507,7 +507,7 @@ static int quad8_index_polarity_set(struct counter_device *counter, struct counter_signal *signal, u32 index_polarity) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; unsigned long irqflags; @@ -536,7 +536,7 @@ static int quad8_synchronous_mode_get(struct counter_device *counter, struct counter_signal *signal, u32 *synchronous_mode) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id - 16; *synchronous_mode = priv->synchronous_mode[channel_id]; @@ -548,7 +548,7 @@ static int quad8_synchronous_mode_set(struct counter_device *counter, struct counter_signal *signal, u32 synchronous_mode) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id - 16; const int base_offset = priv->base + 2 * channel_id + 1; unsigned long irqflags; @@ -587,7 +587,7 @@ static int quad8_count_mode_read(struct counter_device *counter, struct counter_count *count, enum counter_count_mode *cnt_mode) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); /* Map 104-QUAD-8 count mode to Generic Counter count mode */ switch (priv->count_mode[count->id]) { @@ -612,7 +612,7 @@ static int quad8_count_mode_write(struct counter_device *counter, struct counter_count *count, enum counter_count_mode cnt_mode) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); unsigned int count_mode; unsigned int mode_cfg; const int base_offset = priv->base + 2 * count->id + 1; @@ -659,7 +659,7 @@ static int quad8_count_mode_write(struct counter_device *counter, static int quad8_count_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); *enable = priv->ab_enable[count->id]; @@ -669,7 +669,7 @@ static int quad8_count_enable_read(struct counter_device *counter, static int quad8_count_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const int base_offset = priv->base + 2 * count->id; unsigned long irqflags; unsigned int ior_cfg; @@ -697,7 +697,7 @@ static const char *const quad8_noise_error_states[] = { static int quad8_error_noise_get(struct counter_device *counter, struct counter_count *count, u32 *noise_error) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); const int base_offset = priv->base + 2 * count->id + 1; *noise_error = !!(inb(base_offset) & QUAD8_FLAG_E); @@ -708,7 +708,7 @@ static int quad8_error_noise_get(struct counter_device *counter, static int quad8_count_preset_read(struct counter_device *counter, struct counter_count *count, u64 *preset) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); *preset = priv->preset[count->id]; @@ -734,7 +734,7 @@ static void quad8_preset_register_set(struct quad8 *const priv, const int id, static int quad8_count_preset_write(struct counter_device *counter, struct counter_count *count, u64 preset) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); unsigned long irqflags; /* Only 24-bit values are supported */ @@ -753,7 +753,7 @@ static int quad8_count_preset_write(struct counter_device *counter, static int quad8_count_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); unsigned long irqflags; spin_lock_irqsave(&priv->lock, irqflags); @@ -778,7 +778,7 @@ static int quad8_count_ceiling_read(struct counter_device *counter, static int quad8_count_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); unsigned long irqflags; /* Only 24-bit values are supported */ @@ -805,7 +805,7 @@ static int quad8_count_preset_enable_read(struct counter_device *counter, struct counter_count *count, u8 *preset_enable) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); *preset_enable = !priv->preset_enable[count->id]; @@ -816,7 +816,7 @@ static int quad8_count_preset_enable_write(struct counter_device *counter, struct counter_count *count, u8 preset_enable) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const int base_offset = priv->base + 2 * count->id + 1; unsigned long irqflags; unsigned int ior_cfg; @@ -843,7 +843,7 @@ static int quad8_signal_cable_fault_read(struct counter_device *counter, struct counter_signal *signal, u8 *cable_fault) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id / 2; unsigned long irqflags; bool disabled; @@ -873,7 +873,7 @@ static int quad8_signal_cable_fault_enable_read(struct counter_device *counter, struct counter_signal *signal, u8 *enable) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id / 2; *enable = !!(priv->cable_fault_enable & BIT(channel_id)); @@ -885,7 +885,7 @@ static int quad8_signal_cable_fault_enable_write(struct counter_device *counter, struct counter_signal *signal, u8 enable) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id / 2; unsigned long irqflags; unsigned int cable_fault_enable; @@ -911,7 +911,7 @@ static int quad8_signal_fck_prescaler_read(struct counter_device *counter, struct counter_signal *signal, u8 *prescaler) { - const struct quad8 *const priv = counter->priv; + const struct quad8 *const priv = counter_priv(counter); *prescaler = priv->fck_prescaler[signal->id / 2]; @@ -922,7 +922,7 @@ static int quad8_signal_fck_prescaler_write(struct counter_device *counter, struct counter_signal *signal, u8 prescaler) { - struct quad8 *const priv = counter->priv; + struct quad8 *const priv = counter_priv(counter); const size_t channel_id = signal->id / 2; const int base_offset = priv->base + 2 * channel_id; unsigned long irqflags; -- cgit v1.2.3 From 63f0e2b6c0334a35d00e81b4347e34f745bca274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:43 +0100 Subject: counter: interrupt-cnt: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-7-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/interrupt-cnt.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 8514a87fcbee..4bf706ef46e2 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -37,7 +37,7 @@ static irqreturn_t interrupt_cnt_isr(int irq, void *dev_id) static int interrupt_cnt_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - struct interrupt_cnt_priv *priv = counter->priv; + struct interrupt_cnt_priv *priv = counter_priv(counter); *enable = priv->enabled; @@ -47,7 +47,7 @@ static int interrupt_cnt_enable_read(struct counter_device *counter, static int interrupt_cnt_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct interrupt_cnt_priv *priv = counter->priv; + struct interrupt_cnt_priv *priv = counter_priv(counter); if (priv->enabled == enable) return 0; @@ -85,7 +85,7 @@ static int interrupt_cnt_action_read(struct counter_device *counter, static int interrupt_cnt_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct interrupt_cnt_priv *priv = counter->priv; + struct interrupt_cnt_priv *priv = counter_priv(counter); *val = atomic_read(&priv->count); @@ -95,7 +95,7 @@ static int interrupt_cnt_read(struct counter_device *counter, static int interrupt_cnt_write(struct counter_device *counter, struct counter_count *count, const u64 val) { - struct interrupt_cnt_priv *priv = counter->priv; + struct interrupt_cnt_priv *priv = counter_priv(counter); if (val != (typeof(priv->count.counter))val) return -ERANGE; @@ -122,7 +122,7 @@ static int interrupt_cnt_signal_read(struct counter_device *counter, struct counter_signal *signal, enum counter_signal_level *level) { - struct interrupt_cnt_priv *priv = counter->priv; + struct interrupt_cnt_priv *priv = counter_priv(counter); int ret; if (!priv->gpio) -- cgit v1.2.3 From a49ede8208117f48d3a9bc3b514c68d45019ee44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:44 +0100 Subject: counter: microchip-tcb-capture: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-8-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/microchip-tcb-capture.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index bb69f2e0ba93..1b56b7444668 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -72,7 +72,7 @@ static int mchp_tc_count_function_read(struct counter_device *counter, struct counter_count *count, enum counter_function *function) { - struct mchp_tc_data *const priv = counter->priv; + struct mchp_tc_data *const priv = counter_priv(counter); if (priv->qdec_mode) *function = COUNTER_FUNCTION_QUADRATURE_X4; @@ -86,7 +86,7 @@ static int mchp_tc_count_function_write(struct counter_device *counter, struct counter_count *count, enum counter_function function) { - struct mchp_tc_data *const priv = counter->priv; + struct mchp_tc_data *const priv = counter_priv(counter); u32 bmr, cmr; regmap_read(priv->regmap, ATMEL_TC_BMR, &bmr); @@ -148,7 +148,7 @@ static int mchp_tc_count_signal_read(struct counter_device *counter, struct counter_signal *signal, enum counter_signal_level *lvl) { - struct mchp_tc_data *const priv = counter->priv; + struct mchp_tc_data *const priv = counter_priv(counter); bool sigstatus; u32 sr; @@ -169,7 +169,7 @@ static int mchp_tc_count_action_read(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action *action) { - struct mchp_tc_data *const priv = counter->priv; + struct mchp_tc_data *const priv = counter_priv(counter); u32 cmr; regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CMR), &cmr); @@ -197,7 +197,7 @@ static int mchp_tc_count_action_write(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action action) { - struct mchp_tc_data *const priv = counter->priv; + struct mchp_tc_data *const priv = counter_priv(counter); u32 edge = ATMEL_TC_ETRGEDG_NONE; /* QDEC mode is rising edge only */ @@ -230,7 +230,7 @@ static int mchp_tc_count_action_write(struct counter_device *counter, static int mchp_tc_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct mchp_tc_data *const priv = counter->priv; + struct mchp_tc_data *const priv = counter_priv(counter); u32 cnt; regmap_read(priv->regmap, ATMEL_TC_REG(priv->channel[0], CV), &cnt); -- cgit v1.2.3 From 53ada0955270007bbc84c64b3646ac7c70f96442 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:45 +0100 Subject: counter: intel-qep: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Acked-by: Jarkko Nikula Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-9-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/intel-qep.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index 0924d16de6e2..8f84a48508ac 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -109,7 +109,7 @@ static void intel_qep_init(struct intel_qep *qep) static int intel_qep_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct intel_qep *const qep = counter->priv; + struct intel_qep *const qep = counter_priv(counter); pm_runtime_get_sync(qep->dev); *val = intel_qep_readl(qep, INTEL_QEPCOUNT); @@ -176,7 +176,7 @@ static struct counter_synapse intel_qep_count_synapses[] = { static int intel_qep_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); pm_runtime_get_sync(qep->dev); *ceiling = intel_qep_readl(qep, INTEL_QEPMAX); @@ -188,7 +188,7 @@ static int intel_qep_ceiling_read(struct counter_device *counter, static int intel_qep_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 max) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); int ret = 0; /* Intel QEP ceiling configuration only supports 32-bit values */ @@ -213,7 +213,7 @@ out: static int intel_qep_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); *enable = qep->enabled; @@ -223,7 +223,7 @@ static int intel_qep_enable_read(struct counter_device *counter, static int intel_qep_enable_write(struct counter_device *counter, struct counter_count *count, u8 val) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); u32 reg; bool changed; @@ -256,7 +256,7 @@ static int intel_qep_spike_filter_ns_read(struct counter_device *counter, struct counter_count *count, u64 *length) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); u32 reg; pm_runtime_get_sync(qep->dev); @@ -277,7 +277,7 @@ static int intel_qep_spike_filter_ns_write(struct counter_device *counter, struct counter_count *count, u64 length) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); u32 reg; bool enable; int ret = 0; @@ -326,7 +326,7 @@ static int intel_qep_preset_enable_read(struct counter_device *counter, struct counter_count *count, u8 *preset_enable) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); u32 reg; pm_runtime_get_sync(qep->dev); @@ -341,7 +341,7 @@ static int intel_qep_preset_enable_read(struct counter_device *counter, static int intel_qep_preset_enable_write(struct counter_device *counter, struct counter_count *count, u8 val) { - struct intel_qep *qep = counter->priv; + struct intel_qep *qep = counter_priv(counter); u32 reg; int ret = 0; -- cgit v1.2.3 From 1f1b40c0571ade98fcb6b4241d59b6d3a2fab5e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:46 +0100 Subject: counter: ftm-quaddec: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-10-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/ftm-quaddec.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index 9272f7b58beb..f5d92df6a611 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -118,7 +118,7 @@ static void ftm_quaddec_disable(void *ftm) static int ftm_quaddec_get_prescaler(struct counter_device *counter, struct counter_count *count, u32 *cnt_mode) { - struct ftm_quaddec *ftm = counter->priv; + struct ftm_quaddec *ftm = counter_priv(counter); uint32_t scflags; ftm_read(ftm, FTM_SC, &scflags); @@ -131,7 +131,7 @@ static int ftm_quaddec_get_prescaler(struct counter_device *counter, static int ftm_quaddec_set_prescaler(struct counter_device *counter, struct counter_count *count, u32 cnt_mode) { - struct ftm_quaddec *ftm = counter->priv; + struct ftm_quaddec *ftm = counter_priv(counter); mutex_lock(&ftm->ftm_quaddec_mutex); @@ -162,7 +162,7 @@ static int ftm_quaddec_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct ftm_quaddec *const ftm = counter->priv; + struct ftm_quaddec *const ftm = counter_priv(counter); uint32_t cntval; ftm_read(ftm, FTM_CNT, &cntval); @@ -176,7 +176,7 @@ static int ftm_quaddec_count_write(struct counter_device *counter, struct counter_count *count, const u64 val) { - struct ftm_quaddec *const ftm = counter->priv; + struct ftm_quaddec *const ftm = counter_priv(counter); if (val != 0) { dev_warn(&ftm->pdev->dev, "Can only accept '0' as new counter value\n"); -- cgit v1.2.3 From 8817c2d03a85a1311a4be30125e9299f795c7ae9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:47 +0100 Subject: counter: ti-eqep: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: William Breathitt Gray Acked-by: David Lechner Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-11-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/ti-eqep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index 9e0e46bca4c2..2c70b900a6cf 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -89,7 +89,7 @@ struct ti_eqep_cnt { static struct ti_eqep_cnt *ti_eqep_count_from_counter(struct counter_device *counter) { - return container_of(counter, struct ti_eqep_cnt, counter); + return counter_priv(counter); } static int ti_eqep_count_read(struct counter_device *counter, -- cgit v1.2.3 From e98ea385f8543b11d42edff35b4ae771697d3ab9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:48 +0100 Subject: counter: stm32-lptimer-cnt: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-12-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/stm32-lptimer-cnt.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 5168833b1fdf..9cf00e929cc0 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -141,7 +141,7 @@ static const enum counter_synapse_action stm32_lptim_cnt_synapse_actions[] = { static int stm32_lptim_cnt_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); u32 cnt; int ret; @@ -158,7 +158,7 @@ static int stm32_lptim_cnt_function_read(struct counter_device *counter, struct counter_count *count, enum counter_function *function) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); if (!priv->quadrature_mode) { *function = COUNTER_FUNCTION_INCREASE; @@ -177,7 +177,7 @@ static int stm32_lptim_cnt_function_write(struct counter_device *counter, struct counter_count *count, enum counter_function function) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); if (stm32_lptim_is_enabled(priv)) return -EBUSY; @@ -200,7 +200,7 @@ static int stm32_lptim_cnt_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); int ret; ret = stm32_lptim_is_enabled(priv); @@ -216,7 +216,7 @@ static int stm32_lptim_cnt_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); int ret; /* Check nobody uses the timer, or already disabled/enabled */ @@ -241,7 +241,7 @@ static int stm32_lptim_cnt_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); *ceiling = priv->ceiling; @@ -252,7 +252,7 @@ static int stm32_lptim_cnt_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); if (stm32_lptim_is_enabled(priv)) return -EBUSY; @@ -277,7 +277,7 @@ static int stm32_lptim_cnt_action_read(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action *action) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); enum counter_function function; int err; @@ -321,7 +321,7 @@ static int stm32_lptim_cnt_action_write(struct counter_device *counter, struct counter_synapse *synapse, enum counter_synapse_action action) { - struct stm32_lptim_cnt *const priv = counter->priv; + struct stm32_lptim_cnt *const priv = counter_priv(counter); enum counter_function function; int err; -- cgit v1.2.3 From e152833b2c97b043bdc5f650eda2f432cf1a21a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:49 +0100 Subject: counter: stm32-timer-cnt: Convert to counter_priv() wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a straight forward conversion to the new counter_priv() wrapper. Acked-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-13-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/stm32-timer-cnt.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 0546e932db0c..4b05b198a8d8 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -47,7 +47,7 @@ static const enum counter_function stm32_count_functions[] = { static int stm32_count_read(struct counter_device *counter, struct counter_count *count, u64 *val) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cnt; regmap_read(priv->regmap, TIM_CNT, &cnt); @@ -59,7 +59,7 @@ static int stm32_count_read(struct counter_device *counter, static int stm32_count_write(struct counter_device *counter, struct counter_count *count, const u64 val) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 ceiling; regmap_read(priv->regmap, TIM_ARR, &ceiling); @@ -73,7 +73,7 @@ static int stm32_count_function_read(struct counter_device *counter, struct counter_count *count, enum counter_function *function) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 smcr; regmap_read(priv->regmap, TIM_SMCR, &smcr); @@ -100,7 +100,7 @@ static int stm32_count_function_write(struct counter_device *counter, struct counter_count *count, enum counter_function function) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1, sms; switch (function) { @@ -140,7 +140,7 @@ static int stm32_count_direction_read(struct counter_device *counter, struct counter_count *count, enum counter_count_direction *direction) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; regmap_read(priv->regmap, TIM_CR1, &cr1); @@ -153,7 +153,7 @@ static int stm32_count_direction_read(struct counter_device *counter, static int stm32_count_ceiling_read(struct counter_device *counter, struct counter_count *count, u64 *ceiling) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 arr; regmap_read(priv->regmap, TIM_ARR, &arr); @@ -166,7 +166,7 @@ static int stm32_count_ceiling_read(struct counter_device *counter, static int stm32_count_ceiling_write(struct counter_device *counter, struct counter_count *count, u64 ceiling) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); if (ceiling > priv->max_arr) return -ERANGE; @@ -181,7 +181,7 @@ static int stm32_count_ceiling_write(struct counter_device *counter, static int stm32_count_enable_read(struct counter_device *counter, struct counter_count *count, u8 *enable) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; regmap_read(priv->regmap, TIM_CR1, &cr1); @@ -194,7 +194,7 @@ static int stm32_count_enable_read(struct counter_device *counter, static int stm32_count_enable_write(struct counter_device *counter, struct counter_count *count, u8 enable) { - struct stm32_timer_cnt *const priv = counter->priv; + struct stm32_timer_cnt *const priv = counter_priv(counter); u32 cr1; if (enable) { -- cgit v1.2.3 From c18e2760308e30f007fa24b558b87c39d7e86ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:50 +0100 Subject: counter: Provide alternative counter registration functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current implementation gets device lifetime tracking wrong. The problem is that allocation of struct counter_device is controlled by the individual drivers but this structure contains a struct device that might have to live longer than a driver is bound. As a result a command sequence like: { sleep 5; echo bang; } > /dev/counter0 & sleep 1; echo 40000000.timer:counter > /sys/bus/platform/drivers/stm32-timer-counter/unbind can keep a reference to the struct device and unbinding results in freeing the memory occupied by this device resulting in an oops. This commit provides two new functions (plus some helpers): - counter_alloc() to allocate a struct counter_device that is automatically freed once the embedded struct device is released - counter_add() to register such a device. Note that this commit doesn't fix any issues, all drivers have to be converted to these new functions to correct the lifetime problems. Reviewed-by: Jonathan Cameron Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-14-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-core.c | 168 ++++++++++++++++++++++++++++++++++++++++- include/linux/counter.h | 15 ++++ 2 files changed, 181 insertions(+), 2 deletions(-) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index 00c41f28c101..b3fa15bbcbdb 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -24,6 +25,16 @@ /* Provides a unique ID for each counter device */ static DEFINE_IDA(counter_ida); +struct counter_device_allochelper { + struct counter_device counter; + + /* + * This is cache line aligned to ensure private data behaves like if it + * were kmalloced separately. + */ + unsigned long privdata[] ____cacheline_aligned; +}; + static void counter_device_release(struct device *dev) { struct counter_device *const counter = @@ -31,6 +42,9 @@ static void counter_device_release(struct device *dev) counter_chrdev_remove(counter); ida_free(&counter_ida, dev->id); + + if (!counter->legacy_device) + kfree(container_of(counter, struct counter_device_allochelper, counter)); } static struct device_type counter_device_type = { @@ -53,7 +67,14 @@ static dev_t counter_devt; */ void *counter_priv(const struct counter_device *const counter) { - return counter->priv; + if (counter->legacy_device) { + return counter->priv; + } else { + struct counter_device_allochelper *ch = + container_of(counter, struct counter_device_allochelper, counter); + + return &ch->privdata; + } } EXPORT_SYMBOL_GPL(counter_priv); @@ -74,6 +95,8 @@ int counter_register(struct counter_device *const counter) int id; int err; + counter->legacy_device = true; + /* Acquire unique ID */ id = ida_alloc(&counter_ida, GFP_KERNEL); if (id < 0) @@ -114,6 +137,95 @@ err_free_id: } EXPORT_SYMBOL_GPL(counter_register); +/** + * counter_alloc - allocate a counter_device + * @sizeof_priv: size of the driver private data + * + * This is part one of counter registration. The structure is allocated + * dynamically to ensure the right lifetime for the embedded struct device. + * + * If this succeeds, call counter_put() to get rid of the counter_device again. + */ +struct counter_device *counter_alloc(size_t sizeof_priv) +{ + struct counter_device_allochelper *ch; + struct counter_device *counter; + struct device *dev; + int err; + + ch = kzalloc(sizeof(*ch) + sizeof_priv, GFP_KERNEL); + if (!ch) { + err = -ENOMEM; + goto err_alloc_ch; + } + + counter = &ch->counter; + dev = &counter->dev; + + /* Acquire unique ID */ + err = ida_alloc(&counter_ida, GFP_KERNEL); + if (err < 0) + goto err_ida_alloc; + dev->id = err; + + mutex_init(&counter->ops_exist_lock); + dev->type = &counter_device_type; + dev->bus = &counter_bus_type; + dev->devt = MKDEV(MAJOR(counter_devt), dev->id); + + err = counter_chrdev_add(counter); + if (err < 0) + goto err_chrdev_add; + + device_initialize(dev); + + return counter; + +err_chrdev_add: + + ida_free(&counter_ida, dev->id); +err_ida_alloc: + + kfree(ch); +err_alloc_ch: + + return ERR_PTR(err); +} +EXPORT_SYMBOL_GPL(counter_alloc); + +void counter_put(struct counter_device *counter) +{ + put_device(&counter->dev); +} +EXPORT_SYMBOL_GPL(counter_put); + +/** + * counter_add - complete registration of a counter + * @counter: the counter to add + * + * This is part two of counter registration. + * + * If this succeeds, call counter_unregister() to get rid of the counter_device again. + */ +int counter_add(struct counter_device *counter) +{ + int err; + struct device *dev = &counter->dev; + + if (counter->parent) { + dev->parent = counter->parent; + dev->of_node = counter->parent->of_node; + } + + err = counter_sysfs_add(counter); + if (err < 0) + return err; + + /* implies device_add(dev) */ + return cdev_device_add(&counter->chrdev, dev); +} +EXPORT_SYMBOL_GPL(counter_add); + /** * counter_unregister - unregister Counter from the system * @counter: pointer to Counter to unregister @@ -134,7 +246,8 @@ void counter_unregister(struct counter_device *const counter) mutex_unlock(&counter->ops_exist_lock); - put_device(&counter->dev); + if (counter->legacy_device) + put_device(&counter->dev); } EXPORT_SYMBOL_GPL(counter_unregister); @@ -168,6 +281,57 @@ int devm_counter_register(struct device *dev, } EXPORT_SYMBOL_GPL(devm_counter_register); +static void devm_counter_put(void *counter) +{ + counter_put(counter); +} + +/** + * devm_counter_alloc - allocate a counter_device + * @dev: the device to register the release callback for + * @sizeof_priv: size of the driver private data + * + * This is the device managed version of counter_add(). It registers a cleanup + * callback to care for calling counter_put(). + */ +struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv) +{ + struct counter_device *counter; + int err; + + counter = counter_alloc(sizeof_priv); + if (IS_ERR(counter)) + return counter; + + err = devm_add_action_or_reset(dev, devm_counter_put, counter); + if (err < 0) + return ERR_PTR(err); + + return counter; +} +EXPORT_SYMBOL_GPL(devm_counter_alloc); + +/** + * devm_counter_add - complete registration of a counter + * @dev: the device to register the release callback for + * @counter: the counter to add + * + * This is the device managed version of counter_add(). It registers a cleanup + * callback to care for calling counter_unregister(). + */ +int devm_counter_add(struct device *dev, + struct counter_device *const counter) +{ + int err; + + err = counter_add(counter); + if (err < 0) + return err; + + return devm_add_action_or_reset(dev, devm_counter_release, counter); +} +EXPORT_SYMBOL_GPL(devm_counter_add); + #define COUNTER_DEV_MAX 256 static int __init counter_init(void) diff --git a/include/linux/counter.h b/include/linux/counter.h index 627f1757f6bb..ed8d5820f0d1 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -327,14 +327,29 @@ struct counter_device { spinlock_t events_in_lock; struct mutex events_out_lock; struct mutex ops_exist_lock; + + /* + * This can go away once all drivers are converted to + * counter_alloc()/counter_add(). + */ + bool legacy_device; }; void *counter_priv(const struct counter_device *const counter); int counter_register(struct counter_device *const counter); + +struct counter_device *counter_alloc(size_t sizeof_priv); +void counter_put(struct counter_device *const counter); +int counter_add(struct counter_device *const counter); + void counter_unregister(struct counter_device *const counter); int devm_counter_register(struct device *dev, struct counter_device *const counter); +struct counter_device *devm_counter_alloc(struct device *dev, + size_t sizeof_priv); +int devm_counter_add(struct device *dev, + struct counter_device *const counter); void counter_push_event(struct counter_device *const counter, const u8 event, const u8 channel); -- cgit v1.2.3 From 98644726044e7f08322ccfb683773514810744e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:51 +0100 Subject: counter: Update documentation for new counter registration functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to replace the counter registration API also update the documentation to the new way. Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-15-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-api/generic-counter.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Documentation/driver-api/generic-counter.rst b/Documentation/driver-api/generic-counter.rst index 1b487a331467..71ccc30e586b 100644 --- a/Documentation/driver-api/generic-counter.rst +++ b/Documentation/driver-api/generic-counter.rst @@ -262,11 +262,11 @@ order to communicate with the device: to read and write various Signals and Counts, and to set and get the "action mode" and "function mode" for various Synapses and Counts respectively. -A defined counter_device structure may be registered to the system by -passing it to the counter_register function, and unregistered by passing -it to the counter_unregister function. Similarly, the -devm_counter_register function may be used if device memory-managed -registration is desired. +A counter_device structure is allocated using counter_alloc() and then +registered to the system by passing it to the counter_add() function, and +unregistered by passing it to the counter_unregister function. There are +device managed variants of these functions: devm_counter_alloc() and +devm_counter_add(). The struct counter_comp structure is used to define counter extensions for Signals, Synapses, and Counts. -- cgit v1.2.3 From 9e884bb19ca84bbba179b8ccccb2694badd4150f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:52 +0100 Subject: counter: 104-quad-8: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: f1d8a071d45b ("counter: 104-quad-8: Add Generic Counter interface support") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-16-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/104-quad-8.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 41b4d6f4583c..1fbb3923797c 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -52,7 +52,6 @@ MODULE_PARM_DESC(irq, "ACCES 104-QUAD-8 interrupt line numbers"); */ struct quad8 { spinlock_t lock; - struct counter_device counter; unsigned int fck_prescaler[QUAD8_NUM_COUNTERS]; unsigned int preset[QUAD8_NUM_COUNTERS]; unsigned int count_mode[QUAD8_NUM_COUNTERS]; @@ -1083,7 +1082,8 @@ static struct counter_count quad8_counts[] = { static irqreturn_t quad8_irq_handler(int irq, void *private) { - struct quad8 *const priv = private; + struct counter_device *counter = private; + struct quad8 *const priv = counter_priv(counter); const unsigned long base = priv->base; unsigned long irq_status; unsigned long channel; @@ -1114,7 +1114,7 @@ static irqreturn_t quad8_irq_handler(int irq, void *private) continue; } - counter_push_event(&priv->counter, event, channel); + counter_push_event(counter, event, channel); } /* Clear pending interrupts on device */ @@ -1125,6 +1125,7 @@ static irqreturn_t quad8_irq_handler(int irq, void *private) static int quad8_probe(struct device *dev, unsigned int id) { + struct counter_device *counter; struct quad8 *priv; int i, j; unsigned int base_offset; @@ -1136,19 +1137,19 @@ static int quad8_probe(struct device *dev, unsigned int id) return -EBUSY; } - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); /* Initialize Counter device and driver data */ - priv->counter.name = dev_name(dev); - priv->counter.parent = dev; - priv->counter.ops = &quad8_ops; - priv->counter.counts = quad8_counts; - priv->counter.num_counts = ARRAY_SIZE(quad8_counts); - priv->counter.signals = quad8_signals; - priv->counter.num_signals = ARRAY_SIZE(quad8_signals); - priv->counter.priv = priv; + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &quad8_ops; + counter->counts = quad8_counts; + counter->num_counts = ARRAY_SIZE(quad8_counts); + counter->signals = quad8_signals; + counter->num_signals = ARRAY_SIZE(quad8_signals); priv->base = base[id]; spin_lock_init(&priv->lock); @@ -1188,11 +1189,15 @@ static int quad8_probe(struct device *dev, unsigned int id) outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP); err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED, - priv->counter.name, priv); + counter->name, counter); if (err) return err; - return devm_counter_register(dev, &priv->counter); + err = devm_counter_add(dev, counter); + if (err < 0) + return dev_err_probe(dev, err, "Failed to add counter\n"); + + return 0; } static struct isa_driver quad8_driver = { -- cgit v1.2.3 From aefc7e1797243eaa7a5e03629819d4aaa3c3feae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:53 +0100 Subject: counter: interrupt-cnt: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: a55ebd47f21f ("counter: add IRQ or GPIO based counter") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-17-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/interrupt-cnt.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/counter/interrupt-cnt.c b/drivers/counter/interrupt-cnt.c index 4bf706ef46e2..9e99702470c2 100644 --- a/drivers/counter/interrupt-cnt.c +++ b/drivers/counter/interrupt-cnt.c @@ -16,7 +16,6 @@ struct interrupt_cnt_priv { atomic_t count; - struct counter_device counter; struct gpio_desc *gpio; int irq; bool enabled; @@ -148,12 +147,14 @@ static const struct counter_ops interrupt_cnt_ops = { static int interrupt_cnt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct counter_device *counter; struct interrupt_cnt_priv *priv; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); priv->irq = platform_get_irq_optional(pdev, 0); if (priv->irq == -ENXIO) @@ -184,8 +185,8 @@ static int interrupt_cnt_probe(struct platform_device *pdev) if (!priv->signals.name) return -ENOMEM; - priv->counter.signals = &priv->signals; - priv->counter.num_signals = 1; + counter->signals = &priv->signals; + counter->num_signals = 1; priv->synapses.actions_list = interrupt_cnt_synapse_actions; priv->synapses.num_actions = ARRAY_SIZE(interrupt_cnt_synapse_actions); @@ -199,12 +200,11 @@ static int interrupt_cnt_probe(struct platform_device *pdev) priv->cnts.ext = interrupt_cnt_ext; priv->cnts.num_ext = ARRAY_SIZE(interrupt_cnt_ext); - priv->counter.priv = priv; - priv->counter.name = dev_name(dev); - priv->counter.parent = dev; - priv->counter.ops = &interrupt_cnt_ops; - priv->counter.counts = &priv->cnts; - priv->counter.num_counts = 1; + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &interrupt_cnt_ops; + counter->counts = &priv->cnts; + counter->num_counts = 1; irq_set_status_flags(priv->irq, IRQ_NOAUTOEN); ret = devm_request_irq(dev, priv->irq, interrupt_cnt_isr, @@ -213,7 +213,11 @@ static int interrupt_cnt_probe(struct platform_device *pdev) if (ret) return ret; - return devm_counter_register(dev, &priv->counter); + ret = devm_counter_add(dev, counter); + if (ret < 0) + return dev_err_probe(dev, ret, "Failed to add counter\n"); + + return 0; } static const struct of_device_id interrupt_cnt_of_match[] = { -- cgit v1.2.3 From e99dec87a9d6890e9cfb806be07e692a6decb92f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:54 +0100 Subject: counter: intel-qep: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: b711f687a1c1 ("counter: Add support for Intel Quadrature Encoder Peripheral") Tested-by: Jarkko Nikula Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Acked-by: Jarkko Nikula Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-18-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/intel-qep.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/drivers/counter/intel-qep.c b/drivers/counter/intel-qep.c index 8f84a48508ac..47a6a9dfc9e8 100644 --- a/drivers/counter/intel-qep.c +++ b/drivers/counter/intel-qep.c @@ -63,7 +63,6 @@ #define INTEL_QEP_CLK_PERIOD_NS 10 struct intel_qep { - struct counter_device counter; struct mutex lock; struct device *dev; void __iomem *regs; @@ -392,14 +391,16 @@ static struct counter_count intel_qep_counter_count[] = { static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id) { + struct counter_device *counter; struct intel_qep *qep; struct device *dev = &pci->dev; void __iomem *regs; int ret; - qep = devm_kzalloc(dev, sizeof(*qep), GFP_KERNEL); - if (!qep) + counter = devm_counter_alloc(dev, sizeof(*qep)); + if (!counter) return -ENOMEM; + qep = counter_priv(counter); ret = pcim_enable_device(pci); if (ret) @@ -422,20 +423,23 @@ static int intel_qep_probe(struct pci_dev *pci, const struct pci_device_id *id) intel_qep_init(qep); pci_set_drvdata(pci, qep); - qep->counter.name = pci_name(pci); - qep->counter.parent = dev; - qep->counter.ops = &intel_qep_counter_ops; - qep->counter.counts = intel_qep_counter_count; - qep->counter.num_counts = ARRAY_SIZE(intel_qep_counter_count); - qep->counter.signals = intel_qep_signals; - qep->counter.num_signals = ARRAY_SIZE(intel_qep_signals); - qep->counter.priv = qep; + counter->name = pci_name(pci); + counter->parent = dev; + counter->ops = &intel_qep_counter_ops; + counter->counts = intel_qep_counter_count; + counter->num_counts = ARRAY_SIZE(intel_qep_counter_count); + counter->signals = intel_qep_signals; + counter->num_signals = ARRAY_SIZE(intel_qep_signals); qep->enabled = false; pm_runtime_put(dev); pm_runtime_allow(dev); - return devm_counter_register(&pci->dev, &qep->counter); + ret = devm_counter_add(&pci->dev, counter); + if (ret < 0) + return dev_err_probe(&pci->dev, ret, "Failed to add counter\n"); + + return 0; } static void intel_qep_remove(struct pci_dev *pci) -- cgit v1.2.3 From b5d6547c8e545269caca7eadc9c490e1201673cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:55 +0100 Subject: counter: ftm-quaddec: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: a3b9a99980d9 ("counter: add FlexTimer Module Quadrature decoder counter driver") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-19-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/ftm-quaddec.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/counter/ftm-quaddec.c b/drivers/counter/ftm-quaddec.c index f5d92df6a611..2a58582a9df4 100644 --- a/drivers/counter/ftm-quaddec.c +++ b/drivers/counter/ftm-quaddec.c @@ -26,7 +26,6 @@ }) struct ftm_quaddec { - struct counter_device counter; struct platform_device *pdev; void __iomem *ftm_base; bool big_endian; @@ -259,15 +258,17 @@ static struct counter_count ftm_quaddec_counts = { static int ftm_quaddec_probe(struct platform_device *pdev) { + struct counter_device *counter; struct ftm_quaddec *ftm; struct device_node *node = pdev->dev.of_node; struct resource *io; int ret; - ftm = devm_kzalloc(&pdev->dev, sizeof(*ftm), GFP_KERNEL); - if (!ftm) + counter = devm_counter_alloc(&pdev->dev, sizeof(*ftm)); + if (!counter) return -ENOMEM; + ftm = counter_priv(counter); io = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!io) { @@ -283,14 +284,13 @@ static int ftm_quaddec_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to map memory region\n"); return -EINVAL; } - ftm->counter.name = dev_name(&pdev->dev); - ftm->counter.parent = &pdev->dev; - ftm->counter.ops = &ftm_quaddec_cnt_ops; - ftm->counter.counts = &ftm_quaddec_counts; - ftm->counter.num_counts = 1; - ftm->counter.signals = ftm_quaddec_signals; - ftm->counter.num_signals = ARRAY_SIZE(ftm_quaddec_signals); - ftm->counter.priv = ftm; + counter->name = dev_name(&pdev->dev); + counter->parent = &pdev->dev; + counter->ops = &ftm_quaddec_cnt_ops; + counter->counts = &ftm_quaddec_counts; + counter->num_counts = 1; + counter->signals = ftm_quaddec_signals; + counter->num_signals = ARRAY_SIZE(ftm_quaddec_signals); mutex_init(&ftm->ftm_quaddec_mutex); @@ -300,9 +300,9 @@ static int ftm_quaddec_probe(struct platform_device *pdev) if (ret) return ret; - ret = devm_counter_register(&pdev->dev, &ftm->counter); + ret = devm_counter_add(&pdev->dev, counter); if (ret) - return ret; + return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n"); return 0; } -- cgit v1.2.3 From 5998ea6214249e2fe4d23ad43319f0bf4c7d5903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:56 +0100 Subject: counter: microchip-tcb-capture: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: 106b104137fd ("counter: Add microchip TCB capture counter") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-20-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/microchip-tcb-capture.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/counter/microchip-tcb-capture.c b/drivers/counter/microchip-tcb-capture.c index 1b56b7444668..00844445143b 100644 --- a/drivers/counter/microchip-tcb-capture.c +++ b/drivers/counter/microchip-tcb-capture.c @@ -24,7 +24,6 @@ struct mchp_tc_data { const struct atmel_tcb_config *tc_cfg; - struct counter_device counter; struct regmap *regmap; int qdec_mode; int num_channels; @@ -296,6 +295,7 @@ static int mchp_tc_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; const struct atmel_tcb_config *tcb_config; const struct of_device_id *match; + struct counter_device *counter; struct mchp_tc_data *priv; char clk_name[7]; struct regmap *regmap; @@ -303,9 +303,10 @@ static int mchp_tc_probe(struct platform_device *pdev) int channel; int ret, i; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(&pdev->dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); match = of_match_node(atmel_tc_of_match, np->parent); tcb_config = match->data; @@ -360,16 +361,19 @@ static int mchp_tc_probe(struct platform_device *pdev) priv->tc_cfg = tcb_config; priv->regmap = regmap; - priv->counter.name = dev_name(&pdev->dev); - priv->counter.parent = &pdev->dev; - priv->counter.ops = &mchp_tc_ops; - priv->counter.num_counts = ARRAY_SIZE(mchp_tc_counts); - priv->counter.counts = mchp_tc_counts; - priv->counter.num_signals = ARRAY_SIZE(mchp_tc_count_signals); - priv->counter.signals = mchp_tc_count_signals; - priv->counter.priv = priv; - - return devm_counter_register(&pdev->dev, &priv->counter); + counter->name = dev_name(&pdev->dev); + counter->parent = &pdev->dev; + counter->ops = &mchp_tc_ops; + counter->num_counts = ARRAY_SIZE(mchp_tc_counts); + counter->counts = mchp_tc_counts; + counter->num_signals = ARRAY_SIZE(mchp_tc_count_signals); + counter->signals = mchp_tc_count_signals; + + ret = devm_counter_add(&pdev->dev, counter); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n"); + + return 0; } static const struct of_device_id mchp_tc_dt_ids[] = { -- cgit v1.2.3 From e1717d2ea09fb83f3211bcc9ceeaf42fce58fae5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:57 +0100 Subject: counter: stm32-timer-cnt: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: ad29937e206f ("counter: Add STM32 Timer quadrature encoder") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-21-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/stm32-timer-cnt.c | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/drivers/counter/stm32-timer-cnt.c b/drivers/counter/stm32-timer-cnt.c index 4b05b198a8d8..5779ae7c73cf 100644 --- a/drivers/counter/stm32-timer-cnt.c +++ b/drivers/counter/stm32-timer-cnt.c @@ -29,7 +29,6 @@ struct stm32_timer_regs { }; struct stm32_timer_cnt { - struct counter_device counter; struct regmap *regmap; struct clk *clk; u32 max_arr; @@ -317,31 +316,38 @@ static int stm32_timer_cnt_probe(struct platform_device *pdev) struct stm32_timers *ddata = dev_get_drvdata(pdev->dev.parent); struct device *dev = &pdev->dev; struct stm32_timer_cnt *priv; + struct counter_device *counter; + int ret; if (IS_ERR_OR_NULL(ddata)) return -EINVAL; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); + priv->regmap = ddata->regmap; priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; - priv->counter.name = dev_name(dev); - priv->counter.parent = dev; - priv->counter.ops = &stm32_timer_cnt_ops; - priv->counter.counts = &stm32_counts; - priv->counter.num_counts = 1; - priv->counter.signals = stm32_signals; - priv->counter.num_signals = ARRAY_SIZE(stm32_signals); - priv->counter.priv = priv; + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &stm32_timer_cnt_ops; + counter->counts = &stm32_counts; + counter->num_counts = 1; + counter->signals = stm32_signals; + counter->num_signals = ARRAY_SIZE(stm32_signals); platform_set_drvdata(pdev, priv); /* Register Counter device */ - return devm_counter_register(dev, &priv->counter); + ret = devm_counter_add(dev, counter); + if (ret < 0) + dev_err_probe(dev, ret, "Failed to add counter\n"); + + return ret; } static int __maybe_unused stm32_timer_cnt_suspend(struct device *dev) -- cgit v1.2.3 From e75d678d041fe55c7289e2a3d36de9c4f0abbf97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:58 +0100 Subject: counter: stm32-lptimer-cnt: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: 597f55e3f36c ("counter: stm32-lptimer: add counter device") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-22-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/stm32-lptimer-cnt.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/drivers/counter/stm32-lptimer-cnt.c b/drivers/counter/stm32-lptimer-cnt.c index 9cf00e929cc0..68031d93ce89 100644 --- a/drivers/counter/stm32-lptimer-cnt.c +++ b/drivers/counter/stm32-lptimer-cnt.c @@ -20,7 +20,6 @@ #include struct stm32_lptim_cnt { - struct counter_device counter; struct device *dev; struct regmap *regmap; struct clk *clk; @@ -411,14 +410,17 @@ static struct counter_count stm32_lptim_in1_counts = { static int stm32_lptim_cnt_probe(struct platform_device *pdev) { struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent); + struct counter_device *counter; struct stm32_lptim_cnt *priv; + int ret; if (IS_ERR_OR_NULL(ddata)) return -EINVAL; - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(&pdev->dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); priv->dev = &pdev->dev; priv->regmap = ddata->regmap; @@ -426,23 +428,26 @@ static int stm32_lptim_cnt_probe(struct platform_device *pdev) priv->ceiling = STM32_LPTIM_MAX_ARR; /* Initialize Counter device */ - priv->counter.name = dev_name(&pdev->dev); - priv->counter.parent = &pdev->dev; - priv->counter.ops = &stm32_lptim_cnt_ops; + counter->name = dev_name(&pdev->dev); + counter->parent = &pdev->dev; + counter->ops = &stm32_lptim_cnt_ops; if (ddata->has_encoder) { - priv->counter.counts = &stm32_lptim_enc_counts; - priv->counter.num_signals = ARRAY_SIZE(stm32_lptim_cnt_signals); + counter->counts = &stm32_lptim_enc_counts; + counter->num_signals = ARRAY_SIZE(stm32_lptim_cnt_signals); } else { - priv->counter.counts = &stm32_lptim_in1_counts; - priv->counter.num_signals = 1; + counter->counts = &stm32_lptim_in1_counts; + counter->num_signals = 1; } - priv->counter.num_counts = 1; - priv->counter.signals = stm32_lptim_cnt_signals; - priv->counter.priv = priv; + counter->num_counts = 1; + counter->signals = stm32_lptim_cnt_signals; platform_set_drvdata(pdev, priv); - return devm_counter_register(&pdev->dev, &priv->counter); + ret = devm_counter_add(&pdev->dev, counter); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "Failed to add counter\n"); + + return 0; } #ifdef CONFIG_PM_SLEEP -- cgit v1.2.3 From 02758cb20dff4852e5dceed6e828f9a2eb34d6e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:02:59 +0100 Subject: counter: ti-eqep: Convert to new counter registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes device lifetime issues where it was possible to free a live struct device. Fixes: f213729f6796 ("counter: new TI eQEP driver") Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Acked-by: David Lechner Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-23-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/ti-eqep.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/drivers/counter/ti-eqep.c b/drivers/counter/ti-eqep.c index 2c70b900a6cf..0489d26eb47c 100644 --- a/drivers/counter/ti-eqep.c +++ b/drivers/counter/ti-eqep.c @@ -373,13 +373,15 @@ static const struct regmap_config ti_eqep_regmap16_config = { static int ti_eqep_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; + struct counter_device *counter; struct ti_eqep_cnt *priv; void __iomem *base; int err; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) + counter = devm_counter_alloc(dev, sizeof(*priv)); + if (!counter) return -ENOMEM; + priv = counter_priv(counter); base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) @@ -395,16 +397,15 @@ static int ti_eqep_probe(struct platform_device *pdev) if (IS_ERR(priv->regmap16)) return PTR_ERR(priv->regmap16); - priv->counter.name = dev_name(dev); - priv->counter.parent = dev; - priv->counter.ops = &ti_eqep_counter_ops; - priv->counter.counts = ti_eqep_counts; - priv->counter.num_counts = ARRAY_SIZE(ti_eqep_counts); - priv->counter.signals = ti_eqep_signals; - priv->counter.num_signals = ARRAY_SIZE(ti_eqep_signals); - priv->counter.priv = priv; + counter->name = dev_name(dev); + counter->parent = dev; + counter->ops = &ti_eqep_counter_ops; + counter->counts = ti_eqep_counts; + counter->num_counts = ARRAY_SIZE(ti_eqep_counts); + counter->signals = ti_eqep_signals; + counter->num_signals = ARRAY_SIZE(ti_eqep_signals); - platform_set_drvdata(pdev, priv); + platform_set_drvdata(pdev, counter); /* * Need to make sure power is turned on. On AM33xx, this comes from the @@ -414,7 +415,7 @@ static int ti_eqep_probe(struct platform_device *pdev) pm_runtime_enable(dev); pm_runtime_get_sync(dev); - err = counter_register(&priv->counter); + err = counter_add(counter); if (err < 0) { pm_runtime_put_sync(dev); pm_runtime_disable(dev); @@ -426,10 +427,10 @@ static int ti_eqep_probe(struct platform_device *pdev) static int ti_eqep_remove(struct platform_device *pdev) { - struct ti_eqep_cnt *priv = platform_get_drvdata(pdev); + struct counter_device *counter = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - counter_unregister(&priv->counter); + counter_unregister(counter); pm_runtime_put_sync(dev); pm_runtime_disable(dev); -- cgit v1.2.3 From f2ee4759fb700b32a1bd830960fe86bf6bdfd0ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= Date: Thu, 30 Dec 2021 16:03:00 +0100 Subject: counter: remove old and now unused registration API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Usage of counter_register() yields issues in device lifetime tracking. All drivers were converted to the new API, so the old one can go away. Reviewed-by: Jonathan Cameron Acked-by: William Breathitt Gray Signed-off-by: Uwe Kleine-König Link: https://lore.kernel.org/r/20211230150300.72196-24-u.kleine-koenig@pengutronix.de Signed-off-by: Greg Kroah-Hartman --- drivers/counter/counter-core.c | 100 ++--------------------------------------- include/linux/counter.h | 12 ----- 2 files changed, 4 insertions(+), 108 deletions(-) diff --git a/drivers/counter/counter-core.c b/drivers/counter/counter-core.c index b3fa15bbcbdb..7e0957eea094 100644 --- a/drivers/counter/counter-core.c +++ b/drivers/counter/counter-core.c @@ -43,8 +43,7 @@ static void counter_device_release(struct device *dev) counter_chrdev_remove(counter); ida_free(&counter_ida, dev->id); - if (!counter->legacy_device) - kfree(container_of(counter, struct counter_device_allochelper, counter)); + kfree(container_of(counter, struct counter_device_allochelper, counter)); } static struct device_type counter_device_type = { @@ -67,76 +66,13 @@ static dev_t counter_devt; */ void *counter_priv(const struct counter_device *const counter) { - if (counter->legacy_device) { - return counter->priv; - } else { - struct counter_device_allochelper *ch = - container_of(counter, struct counter_device_allochelper, counter); + struct counter_device_allochelper *ch = + container_of(counter, struct counter_device_allochelper, counter); - return &ch->privdata; - } + return &ch->privdata; } EXPORT_SYMBOL_GPL(counter_priv); -/** - * counter_register - register Counter to the system - * @counter: pointer to Counter to register - * - * This function registers a Counter to the system. A sysfs "counter" directory - * will be created and populated with sysfs attributes correlating with the - * Counter Signals, Synapses, and Counts respectively. - * - * RETURNS: - * 0 on success, negative error number on failure. - */ -int counter_register(struct counter_device *const counter) -{ - struct device *const dev = &counter->dev; - int id; - int err; - - counter->legacy_device = true; - - /* Acquire unique ID */ - id = ida_alloc(&counter_ida, GFP_KERNEL); - if (id < 0) - return id; - - mutex_init(&counter->ops_exist_lock); - - /* Configure device structure for Counter */ - dev->id = id; - dev->type = &counter_device_type; - dev->bus = &counter_bus_type; - dev->devt = MKDEV(MAJOR(counter_devt), id); - if (counter->parent) { - dev->parent = counter->parent; - dev->of_node = counter->parent->of_node; - } - device_initialize(dev); - - err = counter_sysfs_add(counter); - if (err < 0) - goto err_free_id; - - err = counter_chrdev_add(counter); - if (err < 0) - goto err_free_id; - - err = cdev_device_add(&counter->chrdev, dev); - if (err < 0) - goto err_remove_chrdev; - - return 0; - -err_remove_chrdev: - counter_chrdev_remove(counter); -err_free_id: - put_device(dev); - return err; -} -EXPORT_SYMBOL_GPL(counter_register); - /** * counter_alloc - allocate a counter_device * @sizeof_priv: size of the driver private data @@ -245,9 +181,6 @@ void counter_unregister(struct counter_device *const counter) wake_up(&counter->events_wait); mutex_unlock(&counter->ops_exist_lock); - - if (counter->legacy_device) - put_device(&counter->dev); } EXPORT_SYMBOL_GPL(counter_unregister); @@ -256,31 +189,6 @@ static void devm_counter_release(void *counter) counter_unregister(counter); } -/** - * devm_counter_register - Resource-managed counter_register - * @dev: device to allocate counter_device for - * @counter: pointer to Counter to register - * - * Managed counter_register. The Counter registered with this function is - * automatically unregistered on driver detach. This function calls - * counter_register internally. Refer to that function for more information. - * - * RETURNS: - * 0 on success, negative error number on failure. - */ -int devm_counter_register(struct device *dev, - struct counter_device *const counter) -{ - int err; - - err = counter_register(counter); - if (err < 0) - return err; - - return devm_add_action_or_reset(dev, devm_counter_release, counter); -} -EXPORT_SYMBOL_GPL(devm_counter_register); - static void devm_counter_put(void *counter) { counter_put(counter); diff --git a/include/linux/counter.h b/include/linux/counter.h index ed8d5820f0d1..1fe17f5adb09 100644 --- a/include/linux/counter.h +++ b/include/linux/counter.h @@ -314,8 +314,6 @@ struct counter_device { struct counter_comp *ext; size_t num_ext; - void *priv; - struct device dev; struct cdev chrdev; struct list_head events_list; @@ -327,25 +325,15 @@ struct counter_device { spinlock_t events_in_lock; struct mutex events_out_lock; struct mutex ops_exist_lock; - - /* - * This can go away once all drivers are converted to - * counter_alloc()/counter_add(). - */ - bool legacy_device; }; void *counter_priv(const struct counter_device *const counter); -int counter_register(struct counter_device *const counter); - struct counter_device *counter_alloc(size_t sizeof_priv); void counter_put(struct counter_device *const counter); int counter_add(struct counter_device *const counter); void counter_unregister(struct counter_device *const counter); -int devm_counter_register(struct device *dev, - struct counter_device *const counter); struct counter_device *devm_counter_alloc(struct device *dev, size_t sizeof_priv); int devm_counter_add(struct device *dev, -- cgit v1.2.3 From 04ce4a6b9b7b84eb6be7b544d3d0e748b6837764 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Sun, 2 Jan 2022 23:38:12 +0100 Subject: dt-bindings: ti-serdes-mux: Add defines for J721S2 SoC There are 4 lanes in the single instance of J721S2 SERDES. Each SERDES lane mux can select upto 4 different IPs. Define all the possible functions. Signed-off-by: Aswath Govindraju Acked-by: Rob Herring Signed-off-by: Peter Rosin Link: https://lore.kernel.org/r/0571fd6b-ec4d-71b3-5cf7-6fa48ed5592c@axentia.se Signed-off-by: Greg Kroah-Hartman --- include/dt-bindings/mux/ti-serdes.h | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/include/dt-bindings/mux/ti-serdes.h b/include/dt-bindings/mux/ti-serdes.h index d417b9268b16..d3116c52ab72 100644 --- a/include/dt-bindings/mux/ti-serdes.h +++ b/include/dt-bindings/mux/ti-serdes.h @@ -95,4 +95,26 @@ #define AM64_SERDES0_LANE0_PCIE0 0x0 #define AM64_SERDES0_LANE0_USB 0x1 +/* J721S2 */ + +#define J721S2_SERDES0_LANE0_EDP_LANE0 0x0 +#define J721S2_SERDES0_LANE0_PCIE1_LANE0 0x1 +#define J721S2_SERDES0_LANE0_IP3_UNUSED 0x2 +#define J721S2_SERDES0_LANE0_IP4_UNUSED 0x3 + +#define J721S2_SERDES0_LANE1_EDP_LANE1 0x0 +#define J721S2_SERDES0_LANE1_PCIE1_LANE1 0x1 +#define J721S2_SERDES0_LANE1_USB 0x2 +#define J721S2_SERDES0_LANE1_IP4_UNUSED 0x3 + +#define J721S2_SERDES0_LANE2_EDP_LANE2 0x0 +#define J721S2_SERDES0_LANE2_PCIE1_LANE2 0x1 +#define J721S2_SERDES0_LANE2_IP3_UNUSED 0x2 +#define J721S2_SERDES0_LANE2_IP4_UNUSED 0x3 + +#define J721S2_SERDES0_LANE3_EDP_LANE3 0x0 +#define J721S2_SERDES0_LANE3_PCIE1_LANE3 0x1 +#define J721S2_SERDES0_LANE3_USB 0x2 +#define J721S2_SERDES0_LANE3_IP4_UNUSED 0x3 + #endif /* _DT_BINDINGS_MUX_TI_SERDES */ -- cgit v1.2.3 From 8f2cade5da97713e77ca5be576438f194b8873d7 Mon Sep 17 00:00:00 2001 From: Aswath Govindraju Date: Sun, 2 Jan 2022 23:38:18 +0100 Subject: dt-bindings: mux: Document mux-states property In some cases, it is required to provide the state to which the mux controller has to be set to, from the consumer device tree node. Document the property mux-states that can be used for adding this support. Signed-off-by: Aswath Govindraju Reviewed-by: Rob Herring Signed-off-by: Peter Rosin Link: https://lore.kernel.org/r/f4d02ac8-23ee-d891-4056-75c672cc59c9@axentia.se Signed-off-by: Greg Kroah-Hartman --- .../devicetree/bindings/mux/gpio-mux.yaml | 11 +++++++-- .../devicetree/bindings/mux/mux-consumer.yaml | 21 +++++++++++++++++ .../devicetree/bindings/mux/mux-controller.yaml | 26 +++++++++++++++++++++- 3 files changed, 55 insertions(+), 3 deletions(-) diff --git a/Documentation/devicetree/bindings/mux/gpio-mux.yaml b/Documentation/devicetree/bindings/mux/gpio-mux.yaml index 0a7c8d64981a..ee4de9fbaf4d 100644 --- a/Documentation/devicetree/bindings/mux/gpio-mux.yaml +++ b/Documentation/devicetree/bindings/mux/gpio-mux.yaml @@ -26,7 +26,10 @@ properties: List of gpios used to control the multiplexer, least significant bit first. '#mux-control-cells': - const: 0 + enum: [ 0, 1 ] + + '#mux-state-cells': + enum: [ 1, 2 ] idle-state: default: -1 @@ -34,7 +37,11 @@ properties: required: - compatible - mux-gpios - - "#mux-control-cells" +anyOf: + - required: + - "#mux-control-cells" + - required: + - "#mux-state-cells" additionalProperties: false diff --git a/Documentation/devicetree/bindings/mux/mux-consumer.yaml b/Documentation/devicetree/bindings/mux/mux-consumer.yaml index 7af93298ab5c..d3d854967359 100644 --- a/Documentation/devicetree/bindings/mux/mux-consumer.yaml +++ b/Documentation/devicetree/bindings/mux/mux-consumer.yaml @@ -25,6 +25,17 @@ description: | strings to label each of the mux controllers listed in the "mux-controls" property. + If it is required to provide the state that the mux controller needs to + be set to, the property "mux-states" must be used. An optional property + "mux-state-names" can be used to provide a list of strings, to label + each of the multiplixer states listed in the "mux-states" property. + + Properties "mux-controls" and "mux-states" can be used depending on how + the consumers want to control the mux controller. If the consumer needs + needs to set multiple states in a mux controller, then property + "mux-controls" can be used. If the consumer needs to set the mux + controller to a given state then property "mux-states" can be used. + mux-ctrl-specifier typically encodes the chip-relative mux controller number. If the mux controller chip only provides a single mux controller, the mux-ctrl-specifier can typically be left out. @@ -35,12 +46,22 @@ properties: mux-controls: $ref: /schemas/types.yaml#/definitions/phandle-array + mux-states: + $ref: /schemas/types.yaml#/definitions/phandle-array + mux-control-names: description: Devices that use more than a single mux controller can use the "mux-control-names" property to map the name of the requested mux controller to an index into the list given by the "mux-controls" property. + mux-state-names: + description: + Devices that use more than a single multiplexer state can use the + "mux-state-names" property to map the name of the requested mux + controller to an index into the list given by the "mux-states" + property. + additionalProperties: true ... diff --git a/Documentation/devicetree/bindings/mux/mux-controller.yaml b/Documentation/devicetree/bindings/mux/mux-controller.yaml index 736a84c3b6a5..c855fbad3884 100644 --- a/Documentation/devicetree/bindings/mux/mux-controller.yaml +++ b/Documentation/devicetree/bindings/mux/mux-controller.yaml @@ -25,7 +25,9 @@ description: | -------------------- Mux controller nodes must specify the number of cells used for the - specifier using the '#mux-control-cells' property. + specifier using the '#mux-control-cells' or '#mux-state-cells' property. + The value of '#mux-state-cells' will always be one greater than the value + of '#mux-control-cells'. Optionally, mux controller nodes can also specify the state the mux should have when it is idle. The idle-state property is used for this. If the @@ -67,6 +69,8 @@ select: pattern: '^mux-controller' - required: - '#mux-control-cells' + - required: + - '#mux-state-cells' properties: $nodename: @@ -75,6 +79,9 @@ properties: '#mux-control-cells': enum: [ 0, 1 ] + '#mux-state-cells': + enum: [ 1, 2 ] + idle-state: $ref: /schemas/types.yaml#/definitions/int32 minimum: -2 @@ -179,4 +186,21 @@ examples: }; }; }; + + - | + #include + + mux1: mux-controller { + compatible = "gpio-mux"; + #mux-state-cells = <1>; + mux-gpios = <&exp_som 2 GPIO_ACTIVE_HIGH>; + }; + + transceiver4: can-phy4 { + compatible = "ti,tcan1042"; + #phy-cells = <0>; + max-bitrate = <5000000>; + standby-gpios = <&exp_som 7 GPIO_ACTIVE_HIGH>; + mux-states = <&mux1 1>; + }; ... -- cgit v1.2.3 From 663d8fb0f84c0f3aade8974fbf3a2ddb255f54a5 Mon Sep 17 00:00:00 2001 From: William Breathitt Gray Date: Wed, 5 Jan 2022 18:30:52 +0900 Subject: counter: 104-quad-8: Fix use-after-free by quad8_irq_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On unbind an irq might be pending which results in quad8_irq_handler() calling counter_push_event() for a counter that is already unregistered. This patch fixes that situation by passing the struct counter_device dev to devm_request_irq() rather than the parent's so that the irq handler is cleaned before the counter is unregistered. Fixes: 7aa2ba0df651 ("counter: 104-quad-8: Add IRQ support for the ACCES 104-QUAD-8") Cc: Syed Nayyar Waris Reported-by: Uwe Kleine-König Signed-off-by: William Breathitt Gray Link: https://lore.kernel.org/r/20220105093052.258791-1-vilhelm.gray@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/counter/104-quad-8.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/counter/104-quad-8.c b/drivers/counter/104-quad-8.c index 1fbb3923797c..a17e51d65aca 100644 --- a/drivers/counter/104-quad-8.c +++ b/drivers/counter/104-quad-8.c @@ -1188,8 +1188,8 @@ static int quad8_probe(struct device *dev, unsigned int id) /* Enable all counters and enable interrupt function */ outb(QUAD8_CHAN_OP_ENABLE_INTERRUPT_FUNC, base[id] + QUAD8_REG_CHAN_OP); - err = devm_request_irq(dev, irq[id], quad8_irq_handler, IRQF_SHARED, - counter->name, counter); + err = devm_request_irq(&counter->dev, irq[id], quad8_irq_handler, + IRQF_SHARED, counter->name, counter); if (err) return err; -- cgit v1.2.3