18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * memory mapped NVRAM
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * (C) Copyright IBM Corp. 2005
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Authors : Utz Bacher <utz.bacher@de.ibm.com>
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/fs.h>
118c2ecf20Sopenharmony_ci#include <linux/init.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <asm/machdep.h>
178c2ecf20Sopenharmony_ci#include <asm/nvram.h>
188c2ecf20Sopenharmony_ci#include <asm/prom.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic void __iomem *mmio_nvram_start;
218c2ecf20Sopenharmony_cistatic long mmio_nvram_len;
228c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(mmio_nvram_lock);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic ssize_t mmio_nvram_read(char *buf, size_t count, loff_t *index)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	unsigned long flags;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci	if (*index >= mmio_nvram_len)
298c2ecf20Sopenharmony_ci		return 0;
308c2ecf20Sopenharmony_ci	if (*index + count > mmio_nvram_len)
318c2ecf20Sopenharmony_ci		count = mmio_nvram_len - *index;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mmio_nvram_lock, flags);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	memcpy_fromio(buf, mmio_nvram_start + *index, count);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mmio_nvram_lock, flags);
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	*index += count;
408c2ecf20Sopenharmony_ci	return count;
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic unsigned char mmio_nvram_read_val(int addr)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	unsigned long flags;
468c2ecf20Sopenharmony_ci	unsigned char val;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	if (addr >= mmio_nvram_len)
498c2ecf20Sopenharmony_ci		return 0xff;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mmio_nvram_lock, flags);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	val = ioread8(mmio_nvram_start + addr);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mmio_nvram_lock, flags);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	return val;
588c2ecf20Sopenharmony_ci}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_cistatic ssize_t mmio_nvram_write(char *buf, size_t count, loff_t *index)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	unsigned long flags;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (*index >= mmio_nvram_len)
658c2ecf20Sopenharmony_ci		return 0;
668c2ecf20Sopenharmony_ci	if (*index + count > mmio_nvram_len)
678c2ecf20Sopenharmony_ci		count = mmio_nvram_len - *index;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	spin_lock_irqsave(&mmio_nvram_lock, flags);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	memcpy_toio(mmio_nvram_start + *index, buf, count);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&mmio_nvram_lock, flags);
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	*index += count;
768c2ecf20Sopenharmony_ci	return count;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cistatic void mmio_nvram_write_val(int addr, unsigned char val)
808c2ecf20Sopenharmony_ci{
818c2ecf20Sopenharmony_ci	unsigned long flags;
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	if (addr < mmio_nvram_len) {
848c2ecf20Sopenharmony_ci		spin_lock_irqsave(&mmio_nvram_lock, flags);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci		iowrite8(val, mmio_nvram_start + addr);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&mmio_nvram_lock, flags);
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic ssize_t mmio_nvram_get_size(void)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	return mmio_nvram_len;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ciint __init mmio_nvram_init(void)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct device_node *nvram_node;
1008c2ecf20Sopenharmony_ci	unsigned long nvram_addr;
1018c2ecf20Sopenharmony_ci	struct resource r;
1028c2ecf20Sopenharmony_ci	int ret;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	nvram_node = of_find_node_by_type(NULL, "nvram");
1058c2ecf20Sopenharmony_ci	if (!nvram_node)
1068c2ecf20Sopenharmony_ci		nvram_node = of_find_compatible_node(NULL, NULL, "nvram");
1078c2ecf20Sopenharmony_ci	if (!nvram_node) {
1088c2ecf20Sopenharmony_ci		printk(KERN_WARNING "nvram: no node found in device-tree\n");
1098c2ecf20Sopenharmony_ci		return -ENODEV;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	ret = of_address_to_resource(nvram_node, 0, &r);
1138c2ecf20Sopenharmony_ci	if (ret) {
1148c2ecf20Sopenharmony_ci		printk(KERN_WARNING "nvram: failed to get address (err %d)\n",
1158c2ecf20Sopenharmony_ci		       ret);
1168c2ecf20Sopenharmony_ci		goto out;
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci	nvram_addr = r.start;
1198c2ecf20Sopenharmony_ci	mmio_nvram_len = resource_size(&r);
1208c2ecf20Sopenharmony_ci	if ( (!mmio_nvram_len) || (!nvram_addr) ) {
1218c2ecf20Sopenharmony_ci		printk(KERN_WARNING "nvram: address or length is 0\n");
1228c2ecf20Sopenharmony_ci		ret = -EIO;
1238c2ecf20Sopenharmony_ci		goto out;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	mmio_nvram_start = ioremap(nvram_addr, mmio_nvram_len);
1278c2ecf20Sopenharmony_ci	if (!mmio_nvram_start) {
1288c2ecf20Sopenharmony_ci		printk(KERN_WARNING "nvram: failed to ioremap\n");
1298c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1308c2ecf20Sopenharmony_ci		goto out;
1318c2ecf20Sopenharmony_ci	}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	printk(KERN_INFO "mmio NVRAM, %luk at 0x%lx mapped to %p\n",
1348c2ecf20Sopenharmony_ci	       mmio_nvram_len >> 10, nvram_addr, mmio_nvram_start);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	ppc_md.nvram_read_val	= mmio_nvram_read_val;
1378c2ecf20Sopenharmony_ci	ppc_md.nvram_write_val	= mmio_nvram_write_val;
1388c2ecf20Sopenharmony_ci	ppc_md.nvram_read	= mmio_nvram_read;
1398c2ecf20Sopenharmony_ci	ppc_md.nvram_write	= mmio_nvram_write;
1408c2ecf20Sopenharmony_ci	ppc_md.nvram_size	= mmio_nvram_get_size;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ciout:
1438c2ecf20Sopenharmony_ci	of_node_put(nvram_node);
1448c2ecf20Sopenharmony_ci	return ret;
1458c2ecf20Sopenharmony_ci}
146