162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/* linux/drivers/mtd/maps/scx200_docflash.c
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci   Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci   National Semiconductor SCx200 flash mapped with DOCCS
762306a36Sopenharmony_ci*/
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci#include <linux/module.h>
1062306a36Sopenharmony_ci#include <linux/types.h>
1162306a36Sopenharmony_ci#include <linux/kernel.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <asm/io.h>
1462306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1562306a36Sopenharmony_ci#include <linux/mtd/map.h>
1662306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/pci.h>
1962306a36Sopenharmony_ci#include <linux/scx200.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#define NAME "scx200_docflash"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciMODULE_AUTHOR("Christer Weinigel <wingel@hack.org>");
2462306a36Sopenharmony_ciMODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver");
2562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_cistatic int probe = 0;		/* Don't autoprobe */
2862306a36Sopenharmony_cistatic unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */
2962306a36Sopenharmony_cistatic unsigned width = 8;	/* Default to 8 bits wide */
3062306a36Sopenharmony_cistatic char *flashtype = "cfi_probe";
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cimodule_param(probe, int, 0);
3362306a36Sopenharmony_ciMODULE_PARM_DESC(probe, "Probe for a BIOS mapping");
3462306a36Sopenharmony_cimodule_param(size, int, 0);
3562306a36Sopenharmony_ciMODULE_PARM_DESC(size, "Size of the flash mapping");
3662306a36Sopenharmony_cimodule_param(width, int, 0);
3762306a36Sopenharmony_ciMODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)");
3862306a36Sopenharmony_cimodule_param(flashtype, charp, 0);
3962306a36Sopenharmony_ciMODULE_PARM_DESC(flashtype, "Type of MTD probe to do");
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic struct resource docmem = {
4262306a36Sopenharmony_ci	.flags = IORESOURCE_MEM,
4362306a36Sopenharmony_ci	.name  = "NatSemi SCx200 DOCCS Flash",
4462306a36Sopenharmony_ci};
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_cistatic struct mtd_info *mymtd;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_cistatic struct mtd_partition partition_info[] = {
4962306a36Sopenharmony_ci	{
5062306a36Sopenharmony_ci		.name   = "DOCCS Boot kernel",
5162306a36Sopenharmony_ci		.offset = 0,
5262306a36Sopenharmony_ci		.size   = 0xc0000
5362306a36Sopenharmony_ci	},
5462306a36Sopenharmony_ci	{
5562306a36Sopenharmony_ci		.name   = "DOCCS Low BIOS",
5662306a36Sopenharmony_ci		.offset = 0xc0000,
5762306a36Sopenharmony_ci		.size   = 0x40000
5862306a36Sopenharmony_ci	},
5962306a36Sopenharmony_ci	{
6062306a36Sopenharmony_ci		.name   = "DOCCS File system",
6162306a36Sopenharmony_ci		.offset = 0x100000,
6262306a36Sopenharmony_ci		.size   = ~0	/* calculate from flash size */
6362306a36Sopenharmony_ci	},
6462306a36Sopenharmony_ci	{
6562306a36Sopenharmony_ci		.name   = "DOCCS High BIOS",
6662306a36Sopenharmony_ci		.offset = ~0, 	/* calculate from flash size */
6762306a36Sopenharmony_ci		.size   = 0x80000
6862306a36Sopenharmony_ci	},
6962306a36Sopenharmony_ci};
7062306a36Sopenharmony_ci#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_cistatic struct map_info scx200_docflash_map = {
7362306a36Sopenharmony_ci	.name      = "NatSemi SCx200 DOCCS Flash",
7462306a36Sopenharmony_ci};
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistatic int __init init_scx200_docflash(void)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	unsigned u;
7962306a36Sopenharmony_ci	unsigned base;
8062306a36Sopenharmony_ci	unsigned ctrl;
8162306a36Sopenharmony_ci	unsigned pmr;
8262306a36Sopenharmony_ci	struct pci_dev *bridge;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n");
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	if ((bridge = pci_get_device(PCI_VENDOR_ID_NS,
8762306a36Sopenharmony_ci				      PCI_DEVICE_ID_NS_SCx200_BRIDGE,
8862306a36Sopenharmony_ci				      NULL)) == NULL)
8962306a36Sopenharmony_ci		return -ENODEV;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	/* check that we have found the configuration block */
9262306a36Sopenharmony_ci	if (!scx200_cb_present()) {
9362306a36Sopenharmony_ci		pci_dev_put(bridge);
9462306a36Sopenharmony_ci		return -ENODEV;
9562306a36Sopenharmony_ci	}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (probe) {
9862306a36Sopenharmony_ci		/* Try to use the present flash mapping if any */
9962306a36Sopenharmony_ci		pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base);
10062306a36Sopenharmony_ci		pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl);
10162306a36Sopenharmony_ci		pci_dev_put(bridge);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci		pmr = inl(scx200_cb_base + SCx200_PMR);
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci		if (base == 0
10662306a36Sopenharmony_ci		    || (ctrl & 0x07000000) != 0x07000000
10762306a36Sopenharmony_ci		    || (ctrl & 0x0007ffff) == 0)
10862306a36Sopenharmony_ci			return -ENODEV;
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci		size = ((ctrl&0x1fff)<<13) + (1<<13);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci		for (u = size; u > 1; u >>= 1)
11362306a36Sopenharmony_ci			;
11462306a36Sopenharmony_ci		if (u != 1)
11562306a36Sopenharmony_ci			return -ENODEV;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci		if (pmr & (1<<6))
11862306a36Sopenharmony_ci			width = 16;
11962306a36Sopenharmony_ci		else
12062306a36Sopenharmony_ci			width = 8;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci		docmem.start = base;
12362306a36Sopenharmony_ci		docmem.end = base + size;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci		if (request_resource(&iomem_resource, &docmem)) {
12662306a36Sopenharmony_ci			printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
12762306a36Sopenharmony_ci			return -ENOMEM;
12862306a36Sopenharmony_ci		}
12962306a36Sopenharmony_ci	} else {
13062306a36Sopenharmony_ci		pci_dev_put(bridge);
13162306a36Sopenharmony_ci		for (u = size; u > 1; u >>= 1)
13262306a36Sopenharmony_ci			;
13362306a36Sopenharmony_ci		if (u != 1) {
13462306a36Sopenharmony_ci			printk(KERN_ERR NAME ": invalid size for flash mapping\n");
13562306a36Sopenharmony_ci			return -EINVAL;
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci		if (width != 8 && width != 16) {
13962306a36Sopenharmony_ci			printk(KERN_ERR NAME ": invalid bus width for flash mapping\n");
14062306a36Sopenharmony_ci			return -EINVAL;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (allocate_resource(&iomem_resource, &docmem,
14462306a36Sopenharmony_ci				      size,
14562306a36Sopenharmony_ci				      0xc0000000, 0xffffffff,
14662306a36Sopenharmony_ci				      size, NULL, NULL)) {
14762306a36Sopenharmony_ci			printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
14862306a36Sopenharmony_ci			return -ENOMEM;
14962306a36Sopenharmony_ci		}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci		ctrl = 0x07000000 | ((size-1) >> 13);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci		printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl);
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci		pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start);
15662306a36Sopenharmony_ci		pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl);
15762306a36Sopenharmony_ci		pmr = inl(scx200_cb_base + SCx200_PMR);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci		if (width == 8) {
16062306a36Sopenharmony_ci			pmr &= ~(1<<6);
16162306a36Sopenharmony_ci		} else {
16262306a36Sopenharmony_ci			pmr |= (1<<6);
16362306a36Sopenharmony_ci		}
16462306a36Sopenharmony_ci		outl(pmr, scx200_cb_base + SCx200_PMR);
16562306a36Sopenharmony_ci	}
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	printk(KERN_INFO NAME ": DOCCS mapped at %pR, width %d\n",
16862306a36Sopenharmony_ci	       &docmem, width);
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	scx200_docflash_map.size = size;
17162306a36Sopenharmony_ci	if (width == 8)
17262306a36Sopenharmony_ci		scx200_docflash_map.bankwidth = 1;
17362306a36Sopenharmony_ci	else
17462306a36Sopenharmony_ci		scx200_docflash_map.bankwidth = 2;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	simple_map_init(&scx200_docflash_map);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	scx200_docflash_map.phys = docmem.start;
17962306a36Sopenharmony_ci	scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size);
18062306a36Sopenharmony_ci	if (!scx200_docflash_map.virt) {
18162306a36Sopenharmony_ci		printk(KERN_ERR NAME ": failed to ioremap the flash\n");
18262306a36Sopenharmony_ci		release_resource(&docmem);
18362306a36Sopenharmony_ci		return -EIO;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	mymtd = do_map_probe(flashtype, &scx200_docflash_map);
18762306a36Sopenharmony_ci	if (!mymtd) {
18862306a36Sopenharmony_ci		printk(KERN_ERR NAME ": unable to detect flash\n");
18962306a36Sopenharmony_ci		iounmap(scx200_docflash_map.virt);
19062306a36Sopenharmony_ci		release_resource(&docmem);
19162306a36Sopenharmony_ci		return -ENXIO;
19262306a36Sopenharmony_ci	}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	if (size < mymtd->size)
19562306a36Sopenharmony_ci		printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	mymtd->owner = THIS_MODULE;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	partition_info[3].offset = mymtd->size-partition_info[3].size;
20062306a36Sopenharmony_ci	partition_info[2].size = partition_info[3].offset-partition_info[2].offset;
20162306a36Sopenharmony_ci	mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
20262306a36Sopenharmony_ci
20362306a36Sopenharmony_ci	return 0;
20462306a36Sopenharmony_ci}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_cistatic void __exit cleanup_scx200_docflash(void)
20762306a36Sopenharmony_ci{
20862306a36Sopenharmony_ci	if (mymtd) {
20962306a36Sopenharmony_ci		mtd_device_unregister(mymtd);
21062306a36Sopenharmony_ci		map_destroy(mymtd);
21162306a36Sopenharmony_ci	}
21262306a36Sopenharmony_ci	if (scx200_docflash_map.virt) {
21362306a36Sopenharmony_ci		iounmap(scx200_docflash_map.virt);
21462306a36Sopenharmony_ci		release_resource(&docmem);
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cimodule_init(init_scx200_docflash);
21962306a36Sopenharmony_cimodule_exit(cleanup_scx200_docflash);
220