162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * memory mapped NVRAM
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * (C) Copyright IBM Corp. 2005
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Authors : Utz Bacher <utz.bacher@de.ibm.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/fs.h>
1162306a36Sopenharmony_ci#include <linux/init.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/of_address.h>
1462306a36Sopenharmony_ci#include <linux/spinlock.h>
1562306a36Sopenharmony_ci#include <linux/types.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/machdep.h>
1862306a36Sopenharmony_ci#include <asm/nvram.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic void __iomem *mmio_nvram_start;
2162306a36Sopenharmony_cistatic long mmio_nvram_len;
2262306a36Sopenharmony_cistatic DEFINE_SPINLOCK(mmio_nvram_lock);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic ssize_t mmio_nvram_read(char *buf, size_t count, loff_t *index)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	unsigned long flags;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	if (*index >= mmio_nvram_len)
2962306a36Sopenharmony_ci		return 0;
3062306a36Sopenharmony_ci	if (*index + count > mmio_nvram_len)
3162306a36Sopenharmony_ci		count = mmio_nvram_len - *index;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_nvram_lock, flags);
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	memcpy_fromio(buf, mmio_nvram_start + *index, count);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_nvram_lock, flags);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	*index += count;
4062306a36Sopenharmony_ci	return count;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic unsigned char mmio_nvram_read_val(int addr)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	unsigned long flags;
4662306a36Sopenharmony_ci	unsigned char val;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	if (addr >= mmio_nvram_len)
4962306a36Sopenharmony_ci		return 0xff;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_nvram_lock, flags);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	val = ioread8(mmio_nvram_start + addr);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_nvram_lock, flags);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	return val;
5862306a36Sopenharmony_ci}
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_cistatic ssize_t mmio_nvram_write(char *buf, size_t count, loff_t *index)
6162306a36Sopenharmony_ci{
6262306a36Sopenharmony_ci	unsigned long flags;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	if (*index >= mmio_nvram_len)
6562306a36Sopenharmony_ci		return 0;
6662306a36Sopenharmony_ci	if (*index + count > mmio_nvram_len)
6762306a36Sopenharmony_ci		count = mmio_nvram_len - *index;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	spin_lock_irqsave(&mmio_nvram_lock, flags);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	memcpy_toio(mmio_nvram_start + *index, buf, count);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	spin_unlock_irqrestore(&mmio_nvram_lock, flags);
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	*index += count;
7662306a36Sopenharmony_ci	return count;
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void mmio_nvram_write_val(int addr, unsigned char val)
8062306a36Sopenharmony_ci{
8162306a36Sopenharmony_ci	unsigned long flags;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (addr < mmio_nvram_len) {
8462306a36Sopenharmony_ci		spin_lock_irqsave(&mmio_nvram_lock, flags);
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci		iowrite8(val, mmio_nvram_start + addr);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		spin_unlock_irqrestore(&mmio_nvram_lock, flags);
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci}
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic ssize_t mmio_nvram_get_size(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return mmio_nvram_len;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ciint __init mmio_nvram_init(void)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct device_node *nvram_node;
10062306a36Sopenharmony_ci	unsigned long nvram_addr;
10162306a36Sopenharmony_ci	struct resource r;
10262306a36Sopenharmony_ci	int ret;
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	nvram_node = of_find_node_by_type(NULL, "nvram");
10562306a36Sopenharmony_ci	if (!nvram_node)
10662306a36Sopenharmony_ci		nvram_node = of_find_compatible_node(NULL, NULL, "nvram");
10762306a36Sopenharmony_ci	if (!nvram_node) {
10862306a36Sopenharmony_ci		printk(KERN_WARNING "nvram: no node found in device-tree\n");
10962306a36Sopenharmony_ci		return -ENODEV;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	ret = of_address_to_resource(nvram_node, 0, &r);
11362306a36Sopenharmony_ci	if (ret) {
11462306a36Sopenharmony_ci		printk(KERN_WARNING "nvram: failed to get address (err %d)\n",
11562306a36Sopenharmony_ci		       ret);
11662306a36Sopenharmony_ci		goto out;
11762306a36Sopenharmony_ci	}
11862306a36Sopenharmony_ci	nvram_addr = r.start;
11962306a36Sopenharmony_ci	mmio_nvram_len = resource_size(&r);
12062306a36Sopenharmony_ci	if ( (!mmio_nvram_len) || (!nvram_addr) ) {
12162306a36Sopenharmony_ci		printk(KERN_WARNING "nvram: address or length is 0\n");
12262306a36Sopenharmony_ci		ret = -EIO;
12362306a36Sopenharmony_ci		goto out;
12462306a36Sopenharmony_ci	}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	mmio_nvram_start = ioremap(nvram_addr, mmio_nvram_len);
12762306a36Sopenharmony_ci	if (!mmio_nvram_start) {
12862306a36Sopenharmony_ci		printk(KERN_WARNING "nvram: failed to ioremap\n");
12962306a36Sopenharmony_ci		ret = -ENOMEM;
13062306a36Sopenharmony_ci		goto out;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	printk(KERN_INFO "mmio NVRAM, %luk at 0x%lx mapped to %p\n",
13462306a36Sopenharmony_ci	       mmio_nvram_len >> 10, nvram_addr, mmio_nvram_start);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci	ppc_md.nvram_read_val	= mmio_nvram_read_val;
13762306a36Sopenharmony_ci	ppc_md.nvram_write_val	= mmio_nvram_write_val;
13862306a36Sopenharmony_ci	ppc_md.nvram_read	= mmio_nvram_read;
13962306a36Sopenharmony_ci	ppc_md.nvram_write	= mmio_nvram_write;
14062306a36Sopenharmony_ci	ppc_md.nvram_size	= mmio_nvram_get_size;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciout:
14362306a36Sopenharmony_ci	of_node_put(nvram_node);
14462306a36Sopenharmony_ci	return ret;
14562306a36Sopenharmony_ci}
146