Skip to content
Snippets Groups Projects
Select Git revision
  • f1f0f330b1d0ac1bcc38d7c84d439f4fde341a9c
  • master default
  • b4/phy-realtek-clock-fix
  • b4/rk3576-rock4d-phy-timings
  • b4/dw-wdt-fix-initial-timeout
  • radxa-v6.1-vendor-kernel
  • b4/fusb302-race-condition-fix
  • b4/rk3576-rock4d-phy-handling-fixes
  • b4/rk3588-evb1-hdmi-rx
  • b4/rk3576-fix-fspi-pmdomain
  • b4/usbc-for-rock5bp
  • b4/rock5bp-for-upstream
  • rockchip-devel
  • rk3588-test
  • rk3588-test-vendor-cam
  • lf-6.6.y_6.6.23-2.0.0_var01-panfrost
  • rk3588-linked-clk-gate-for-upstream
  • rk3588-gpu-pwr-domain-for-upstream
  • rk3588-rock5b-usbc-for-upstream
  • rk3588-evb1-for-upstream
  • imx95-upstream-with-vendor-display-stack
  • v5.17
  • v5.17-rc8
  • v5.17-rc7
  • v5.17-rc6
  • v5.17-rc5
  • v5.17-rc4
  • v5.17-rc3
  • v5.17-rc2
  • v5.17-rc1
  • v5.16
  • v5.16-rc8
  • v5.16-rc7
  • v5.16-rc6
  • v5.16-rc5
  • v5.16-rc4
  • v5.16-rc3
  • v5.16-rc2
  • v5.16-rc1
  • v5.15
  • v5.15-rc7
41 results

patch-kernel

Blame
  • nvs.c 4.56 KiB
    /*
     * nvs.c - Routines for saving and restoring ACPI NVS memory region
     *
     * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
     *
     * This file is released under the GPLv2.
     */
    
    #include <linux/io.h>
    #include <linux/kernel.h>
    #include <linux/list.h>
    #include <linux/mm.h>
    #include <linux/slab.h>
    #include <linux/acpi.h>
    #include <linux/acpi_io.h>
    #include <acpi/acpiosxf.h>
    
    /* ACPI NVS regions, APEI may use it */
    
    struct nvs_region {
    	__u64 phys_start;
    	__u64 size;
    	struct list_head node;
    };
    
    static LIST_HEAD(nvs_region_list);
    
    #ifdef CONFIG_ACPI_SLEEP
    static int suspend_nvs_register(unsigned long start, unsigned long size);
    #else
    static inline int suspend_nvs_register(unsigned long a, unsigned long b)
    {
    	return 0;
    }
    #endif
    
    int acpi_nvs_register(__u64 start, __u64 size)
    {
    	struct nvs_region *region;
    
    	region = kmalloc(sizeof(*region), GFP_KERNEL);
    	if (!region)
    		return -ENOMEM;
    	region->phys_start = start;
    	region->size = size;
    	list_add_tail(&region->node, &nvs_region_list);
    
    	return suspend_nvs_register(start, size);
    }
    
    int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data),
    			     void *data)
    {
    	int rc;
    	struct nvs_region *region;
    
    	list_for_each_entry(region, &nvs_region_list, node) {
    		rc = func(region->phys_start, region->size, data);
    		if (rc)
    			return rc;
    	}
    
    	return 0;
    }
    
    
    #ifdef CONFIG_ACPI_SLEEP
    /*
     * Platforms, like ACPI, may want us to save some memory used by them during
     * suspend and to restore the contents of this memory during the subsequent
     * resume.  The code below implements a mechanism allowing us to do that.
     */
    
    struct nvs_page {
    	unsigned long phys_start;
    	unsigned int size;
    	void *kaddr;
    	void *data;
    	bool unmap;
    	struct list_head node;
    };
    
    static LIST_HEAD(nvs_list);
    
    /**
     *	suspend_nvs_register - register platform NVS memory region to save
     *	@start - physical address of the region
     *	@size - size of the region
     *
     *	The NVS region need not be page-aligned (both ends) and we arrange
     *	things so that the data from page-aligned addresses in this region will
     *	be copied into separate RAM pages.
     */
    static int suspend_nvs_register(unsigned long start, unsigned long size)
    {
    	struct nvs_page *entry, *next;
    
    	pr_info("PM: Registering ACPI NVS region at %lx (%ld bytes)\n",
    		start, size);
    
    	while (size > 0) {
    		unsigned int nr_bytes;
    
    		entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL);
    		if (!entry)
    			goto Error;
    
    		list_add_tail(&entry->node, &nvs_list);
    		entry->phys_start = start;
    		nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK);
    		entry->size = (size < nr_bytes) ? size : nr_bytes;
    
    		start += entry->size;
    		size -= entry->size;
    	}
    	return 0;
    
     Error:
    	list_for_each_entry_safe(entry, next, &nvs_list, node) {
    		list_del(&entry->node);
    		kfree(entry);
    	}
    	return -ENOMEM;
    }
    
    /**
     *	suspend_nvs_free - free data pages allocated for saving NVS regions
     */
    void suspend_nvs_free(void)
    {
    	struct nvs_page *entry;
    
    	list_for_each_entry(entry, &nvs_list, node)
    		if (entry->data) {
    			free_page((unsigned long)entry->data);
    			entry->data = NULL;
    			if (entry->kaddr) {
    				if (entry->unmap) {
    					iounmap(entry->kaddr);
    					entry->unmap = false;
    				} else {
    					acpi_os_unmap_memory(entry->kaddr,
    							     entry->size);
    				}
    				entry->kaddr = NULL;
    			}
    		}
    }
    
    /**
     *	suspend_nvs_alloc - allocate memory necessary for saving NVS regions
     */
    int suspend_nvs_alloc(void)
    {
    	struct nvs_page *entry;
    
    	list_for_each_entry(entry, &nvs_list, node) {
    		entry->data = (void *)__get_free_page(GFP_KERNEL);
    		if (!entry->data) {
    			suspend_nvs_free();
    			return -ENOMEM;
    		}
    	}
    	return 0;
    }
    
    /**
     *	suspend_nvs_save - save NVS memory regions
     */
    int suspend_nvs_save(void)
    {
    	struct nvs_page *entry;
    
    	printk(KERN_INFO "PM: Saving platform NVS memory\n");
    
    	list_for_each_entry(entry, &nvs_list, node)
    		if (entry->data) {
    			unsigned long phys = entry->phys_start;
    			unsigned int size = entry->size;
    
    			entry->kaddr = acpi_os_get_iomem(phys, size);
    			if (!entry->kaddr) {
    				entry->kaddr = acpi_os_ioremap(phys, size);
    				entry->unmap = !!entry->kaddr;
    			}
    			if (!entry->kaddr) {
    				suspend_nvs_free();
    				return -ENOMEM;
    			}
    			memcpy(entry->data, entry->kaddr, entry->size);
    		}
    
    	return 0;
    }
    
    /**
     *	suspend_nvs_restore - restore NVS memory regions
     *
     *	This function is going to be called with interrupts disabled, so it
     *	cannot iounmap the virtual addresses used to access the NVS region.
     */
    void suspend_nvs_restore(void)
    {
    	struct nvs_page *entry;
    
    	printk(KERN_INFO "PM: Restoring platform NVS memory\n");
    
    	list_for_each_entry(entry, &nvs_list, node)
    		if (entry->data)
    			memcpy(entry->kaddr, entry->data, entry->size);
    }
    #endif