162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci *   Octeon Bootbus flash setup
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
562306a36Sopenharmony_ci * License.  See the file "COPYING" in the main directory of this archive
662306a36Sopenharmony_ci * for more details.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (C) 2007, 2008 Cavium Networks
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci#include <linux/kernel.h>
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/semaphore.h>
1362306a36Sopenharmony_ci#include <linux/mtd/mtd.h>
1462306a36Sopenharmony_ci#include <linux/mtd/map.h>
1562306a36Sopenharmony_ci#include <linux/of.h>
1662306a36Sopenharmony_ci#include <linux/platform_device.h>
1762306a36Sopenharmony_ci#include <linux/mtd/partitions.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <asm/octeon/octeon.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic struct map_info flash_map;
2262306a36Sopenharmony_cistatic struct mtd_info *mymtd;
2362306a36Sopenharmony_cistatic const char *part_probe_types[] = {
2462306a36Sopenharmony_ci	"cmdlinepart",
2562306a36Sopenharmony_ci#ifdef CONFIG_MTD_REDBOOT_PARTS
2662306a36Sopenharmony_ci	"RedBoot",
2762306a36Sopenharmony_ci#endif
2862306a36Sopenharmony_ci	NULL
2962306a36Sopenharmony_ci};
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_cistatic map_word octeon_flash_map_read(struct map_info *map, unsigned long ofs)
3262306a36Sopenharmony_ci{
3362306a36Sopenharmony_ci	map_word r;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	down(&octeon_bootbus_sem);
3662306a36Sopenharmony_ci	r = inline_map_read(map, ofs);
3762306a36Sopenharmony_ci	up(&octeon_bootbus_sem);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	return r;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic void octeon_flash_map_write(struct map_info *map, const map_word datum,
4362306a36Sopenharmony_ci				   unsigned long ofs)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	down(&octeon_bootbus_sem);
4662306a36Sopenharmony_ci	inline_map_write(map, datum, ofs);
4762306a36Sopenharmony_ci	up(&octeon_bootbus_sem);
4862306a36Sopenharmony_ci}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_cistatic void octeon_flash_map_copy_from(struct map_info *map, void *to,
5162306a36Sopenharmony_ci				       unsigned long from, ssize_t len)
5262306a36Sopenharmony_ci{
5362306a36Sopenharmony_ci	down(&octeon_bootbus_sem);
5462306a36Sopenharmony_ci	inline_map_copy_from(map, to, from, len);
5562306a36Sopenharmony_ci	up(&octeon_bootbus_sem);
5662306a36Sopenharmony_ci}
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void octeon_flash_map_copy_to(struct map_info *map, unsigned long to,
5962306a36Sopenharmony_ci				     const void *from, ssize_t len)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	down(&octeon_bootbus_sem);
6262306a36Sopenharmony_ci	inline_map_copy_to(map, to, from, len);
6362306a36Sopenharmony_ci	up(&octeon_bootbus_sem);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/*
6762306a36Sopenharmony_ci * Module/ driver initialization.
6862306a36Sopenharmony_ci *
6962306a36Sopenharmony_ci * Returns Zero on success
7062306a36Sopenharmony_ci */
7162306a36Sopenharmony_cistatic int octeon_flash_probe(struct platform_device *pdev)
7262306a36Sopenharmony_ci{
7362306a36Sopenharmony_ci	union cvmx_mio_boot_reg_cfgx region_cfg;
7462306a36Sopenharmony_ci	u32 cs;
7562306a36Sopenharmony_ci	int r;
7662306a36Sopenharmony_ci	struct device_node *np = pdev->dev.of_node;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	r = of_property_read_u32(np, "reg", &cs);
7962306a36Sopenharmony_ci	if (r)
8062306a36Sopenharmony_ci		return r;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/*
8362306a36Sopenharmony_ci	 * Read the bootbus region 0 setup to determine the base
8462306a36Sopenharmony_ci	 * address of the flash.
8562306a36Sopenharmony_ci	 */
8662306a36Sopenharmony_ci	region_cfg.u64 = cvmx_read_csr(CVMX_MIO_BOOT_REG_CFGX(cs));
8762306a36Sopenharmony_ci	if (region_cfg.s.en) {
8862306a36Sopenharmony_ci		/*
8962306a36Sopenharmony_ci		 * The bootloader always takes the flash and sets its
9062306a36Sopenharmony_ci		 * address so the entire flash fits below
9162306a36Sopenharmony_ci		 * 0x1fc00000. This way the flash aliases to
9262306a36Sopenharmony_ci		 * 0x1fc00000 for booting. Software can access the
9362306a36Sopenharmony_ci		 * full flash at the true address, while core boot can
9462306a36Sopenharmony_ci		 * access 4MB.
9562306a36Sopenharmony_ci		 */
9662306a36Sopenharmony_ci		/* Use this name so old part lines work */
9762306a36Sopenharmony_ci		flash_map.name = "phys_mapped_flash";
9862306a36Sopenharmony_ci		flash_map.phys = region_cfg.s.base << 16;
9962306a36Sopenharmony_ci		flash_map.size = 0x1fc00000 - flash_map.phys;
10062306a36Sopenharmony_ci		/* 8-bit bus (0 + 1) or 16-bit bus (1 + 1) */
10162306a36Sopenharmony_ci		flash_map.bankwidth = region_cfg.s.width + 1;
10262306a36Sopenharmony_ci		flash_map.virt = ioremap(flash_map.phys, flash_map.size);
10362306a36Sopenharmony_ci		pr_notice("Bootbus flash: Setting flash for %luMB flash at "
10462306a36Sopenharmony_ci			  "0x%08llx\n", flash_map.size >> 20, flash_map.phys);
10562306a36Sopenharmony_ci		WARN_ON(!map_bankwidth_supported(flash_map.bankwidth));
10662306a36Sopenharmony_ci		flash_map.read = octeon_flash_map_read;
10762306a36Sopenharmony_ci		flash_map.write = octeon_flash_map_write;
10862306a36Sopenharmony_ci		flash_map.copy_from = octeon_flash_map_copy_from;
10962306a36Sopenharmony_ci		flash_map.copy_to = octeon_flash_map_copy_to;
11062306a36Sopenharmony_ci		mymtd = do_map_probe("cfi_probe", &flash_map);
11162306a36Sopenharmony_ci		if (mymtd) {
11262306a36Sopenharmony_ci			mymtd->owner = THIS_MODULE;
11362306a36Sopenharmony_ci			mtd_device_parse_register(mymtd, part_probe_types,
11462306a36Sopenharmony_ci						  NULL, NULL, 0);
11562306a36Sopenharmony_ci		} else {
11662306a36Sopenharmony_ci			pr_err("Failed to register MTD device for flash\n");
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci	}
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic const struct of_device_id of_flash_match[] = {
12362306a36Sopenharmony_ci	{
12462306a36Sopenharmony_ci		.compatible	= "cfi-flash",
12562306a36Sopenharmony_ci	},
12662306a36Sopenharmony_ci	{ },
12762306a36Sopenharmony_ci};
12862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, of_flash_match);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic struct platform_driver of_flash_driver = {
13162306a36Sopenharmony_ci	.driver = {
13262306a36Sopenharmony_ci		.name = "octeon-of-flash",
13362306a36Sopenharmony_ci		.of_match_table = of_flash_match,
13462306a36Sopenharmony_ci	},
13562306a36Sopenharmony_ci	.probe		= octeon_flash_probe,
13662306a36Sopenharmony_ci};
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_cistatic int octeon_flash_init(void)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	return platform_driver_register(&of_flash_driver);
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_cilate_initcall(octeon_flash_init);
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
145