18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2011, Netlogic Microsystems.
38c2ecf20Sopenharmony_ci * Copyright 2004, Matt Porter <mporter@kernel.crashing.org>
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
68c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
78c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/device.h>
118c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
128c2ecf20Sopenharmony_ci#include <linux/kernel.h>
138c2ecf20Sopenharmony_ci#include <linux/init.h>
148c2ecf20Sopenharmony_ci#include <linux/io.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/ioport.h>
178c2ecf20Sopenharmony_ci#include <linux/resource.h>
188c2ecf20Sopenharmony_ci#include <linux/spi/flash.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/mtd/mtd.h>
218c2ecf20Sopenharmony_ci#include <linux/mtd/physmap.h>
228c2ecf20Sopenharmony_ci#include <linux/mtd/platnand.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <asm/netlogic/haldefs.h>
258c2ecf20Sopenharmony_ci#include <asm/netlogic/xlr/iomap.h>
268c2ecf20Sopenharmony_ci#include <asm/netlogic/xlr/flash.h>
278c2ecf20Sopenharmony_ci#include <asm/netlogic/xlr/bridge.h>
288c2ecf20Sopenharmony_ci#include <asm/netlogic/xlr/gpio.h>
298c2ecf20Sopenharmony_ci#include <asm/netlogic/xlr/xlr.h>
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci/*
328c2ecf20Sopenharmony_ci * Default NOR partition layout
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic struct mtd_partition xlr_nor_parts[] = {
358c2ecf20Sopenharmony_ci	{
368c2ecf20Sopenharmony_ci		.name = "User FS",
378c2ecf20Sopenharmony_ci		.offset = 0x800000,
388c2ecf20Sopenharmony_ci		.size	= MTDPART_SIZ_FULL,
398c2ecf20Sopenharmony_ci	}
408c2ecf20Sopenharmony_ci};
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/*
438c2ecf20Sopenharmony_ci * Default NAND partition layout
448c2ecf20Sopenharmony_ci */
458c2ecf20Sopenharmony_cistatic struct mtd_partition xlr_nand_parts[] = {
468c2ecf20Sopenharmony_ci	{
478c2ecf20Sopenharmony_ci		.name	= "Root Filesystem",
488c2ecf20Sopenharmony_ci		.offset = 64 * 64 * 2048,
498c2ecf20Sopenharmony_ci		.size	= 432 * 64 * 2048,
508c2ecf20Sopenharmony_ci	},
518c2ecf20Sopenharmony_ci	{
528c2ecf20Sopenharmony_ci		.name	= "Home Filesystem",
538c2ecf20Sopenharmony_ci		.offset = MTDPART_OFS_APPEND,
548c2ecf20Sopenharmony_ci		.size	= MTDPART_SIZ_FULL,
558c2ecf20Sopenharmony_ci	},
568c2ecf20Sopenharmony_ci};
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci/* Use PHYSMAP flash for NOR */
598c2ecf20Sopenharmony_cistruct physmap_flash_data xlr_nor_data = {
608c2ecf20Sopenharmony_ci	.width		= 2,
618c2ecf20Sopenharmony_ci	.parts		= xlr_nor_parts,
628c2ecf20Sopenharmony_ci	.nr_parts	= ARRAY_SIZE(xlr_nor_parts),
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic struct resource xlr_nor_res[] = {
668c2ecf20Sopenharmony_ci	{
678c2ecf20Sopenharmony_ci		.flags	= IORESOURCE_MEM,
688c2ecf20Sopenharmony_ci	},
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic struct platform_device xlr_nor_dev = {
728c2ecf20Sopenharmony_ci	.name	= "physmap-flash",
738c2ecf20Sopenharmony_ci	.dev	= {
748c2ecf20Sopenharmony_ci		.platform_data	= &xlr_nor_data,
758c2ecf20Sopenharmony_ci	},
768c2ecf20Sopenharmony_ci	.num_resources	= ARRAY_SIZE(xlr_nor_res),
778c2ecf20Sopenharmony_ci	.resource	= xlr_nor_res,
788c2ecf20Sopenharmony_ci};
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/*
818c2ecf20Sopenharmony_ci * Use "gen_nand" driver for NAND flash
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * There seems to be no way to store a private pointer containing
848c2ecf20Sopenharmony_ci * platform specific info in gen_nand drivier. We will use a global
858c2ecf20Sopenharmony_ci * struct for now, since we currently have only one NAND chip per board.
868c2ecf20Sopenharmony_ci */
878c2ecf20Sopenharmony_cistruct xlr_nand_flash_priv {
888c2ecf20Sopenharmony_ci	int cs;
898c2ecf20Sopenharmony_ci	uint64_t flash_mmio;
908c2ecf20Sopenharmony_ci};
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic struct xlr_nand_flash_priv nand_priv;
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void xlr_nand_ctrl(struct nand_chip *chip, int cmd,
958c2ecf20Sopenharmony_ci			  unsigned int ctrl)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci	if (ctrl & NAND_CLE)
988c2ecf20Sopenharmony_ci		nlm_write_reg(nand_priv.flash_mmio,
998c2ecf20Sopenharmony_ci			FLASH_NAND_CLE(nand_priv.cs), cmd);
1008c2ecf20Sopenharmony_ci	else if (ctrl & NAND_ALE)
1018c2ecf20Sopenharmony_ci		nlm_write_reg(nand_priv.flash_mmio,
1028c2ecf20Sopenharmony_ci			FLASH_NAND_ALE(nand_priv.cs), cmd);
1038c2ecf20Sopenharmony_ci}
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_cistruct platform_nand_data xlr_nand_data = {
1068c2ecf20Sopenharmony_ci	.chip = {
1078c2ecf20Sopenharmony_ci		.nr_chips	= 1,
1088c2ecf20Sopenharmony_ci		.nr_partitions	= ARRAY_SIZE(xlr_nand_parts),
1098c2ecf20Sopenharmony_ci		.chip_delay	= 50,
1108c2ecf20Sopenharmony_ci		.partitions	= xlr_nand_parts,
1118c2ecf20Sopenharmony_ci	},
1128c2ecf20Sopenharmony_ci	.ctrl = {
1138c2ecf20Sopenharmony_ci		.cmd_ctrl	= xlr_nand_ctrl,
1148c2ecf20Sopenharmony_ci	},
1158c2ecf20Sopenharmony_ci};
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_cistatic struct resource xlr_nand_res[] = {
1188c2ecf20Sopenharmony_ci	{
1198c2ecf20Sopenharmony_ci		.flags		= IORESOURCE_MEM,
1208c2ecf20Sopenharmony_ci	},
1218c2ecf20Sopenharmony_ci};
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_cistatic struct platform_device xlr_nand_dev = {
1248c2ecf20Sopenharmony_ci	.name		= "gen_nand",
1258c2ecf20Sopenharmony_ci	.id		= -1,
1268c2ecf20Sopenharmony_ci	.num_resources	= ARRAY_SIZE(xlr_nand_res),
1278c2ecf20Sopenharmony_ci	.resource	= xlr_nand_res,
1288c2ecf20Sopenharmony_ci	.dev		= {
1298c2ecf20Sopenharmony_ci		.platform_data	= &xlr_nand_data,
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci/*
1348c2ecf20Sopenharmony_ci * XLR/XLS supports upto 8 devices on its FLASH interface. The value in
1358c2ecf20Sopenharmony_ci * FLASH_BAR (on the MEM/IO bridge) gives the base for mapping all the
1368c2ecf20Sopenharmony_ci * flash devices.
1378c2ecf20Sopenharmony_ci * Under this, each flash device has an offset and size given by the
1388c2ecf20Sopenharmony_ci * CSBASE_ADDR and CSBASE_MASK registers for the device.
1398c2ecf20Sopenharmony_ci *
1408c2ecf20Sopenharmony_ci * The CSBASE_ registers are expected to be setup by the bootloader.
1418c2ecf20Sopenharmony_ci */
1428c2ecf20Sopenharmony_cistatic void setup_flash_resource(uint64_t flash_mmio,
1438c2ecf20Sopenharmony_ci	uint64_t flash_map_base, int cs, struct resource *res)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	u32 base, mask;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	base = nlm_read_reg(flash_mmio, FLASH_CSBASE_ADDR(cs));
1488c2ecf20Sopenharmony_ci	mask = nlm_read_reg(flash_mmio, FLASH_CSADDR_MASK(cs));
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	res->start = flash_map_base + ((unsigned long)base << 16);
1518c2ecf20Sopenharmony_ci	res->end = res->start + (mask + 1) * 64 * 1024;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_cistatic int __init xlr_flash_init(void)
1558c2ecf20Sopenharmony_ci{
1568c2ecf20Sopenharmony_ci	uint64_t gpio_mmio, flash_mmio, flash_map_base;
1578c2ecf20Sopenharmony_ci	u32 gpio_resetcfg, flash_bar;
1588c2ecf20Sopenharmony_ci	int cs, boot_nand, boot_nor;
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	/* Flash address bits 39:24 is in bridge flash BAR */
1618c2ecf20Sopenharmony_ci	flash_bar = nlm_read_reg(nlm_io_base, BRIDGE_FLASH_BAR);
1628c2ecf20Sopenharmony_ci	flash_map_base = (flash_bar & 0xffff0000) << 8;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	gpio_mmio = nlm_mmio_base(NETLOGIC_IO_GPIO_OFFSET);
1658c2ecf20Sopenharmony_ci	flash_mmio = nlm_mmio_base(NETLOGIC_IO_FLASH_OFFSET);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	/* Get the chip reset config */
1688c2ecf20Sopenharmony_ci	gpio_resetcfg = nlm_read_reg(gpio_mmio, GPIO_PWRON_RESET_CFG_REG);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	/* Check for boot flash type */
1718c2ecf20Sopenharmony_ci	boot_nor = boot_nand = 0;
1728c2ecf20Sopenharmony_ci	if (nlm_chip_is_xls()) {
1738c2ecf20Sopenharmony_ci		/* On XLS, check boot from NAND bit (GPIO reset reg bit 16) */
1748c2ecf20Sopenharmony_ci		if (gpio_resetcfg & (1 << 16))
1758c2ecf20Sopenharmony_ci			boot_nand = 1;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		/* check boot from PCMCIA, (GPIO reset reg bit 15 */
1788c2ecf20Sopenharmony_ci		if ((gpio_resetcfg & (1 << 15)) == 0)
1798c2ecf20Sopenharmony_ci			boot_nor = 1;	/* not set, booted from NOR */
1808c2ecf20Sopenharmony_ci	} else { /* XLR */
1818c2ecf20Sopenharmony_ci		/* check boot from PCMCIA (bit 16 in GPIO reset on XLR) */
1828c2ecf20Sopenharmony_ci		if ((gpio_resetcfg & (1 << 16)) == 0)
1838c2ecf20Sopenharmony_ci			boot_nor = 1;	/* not set, booted from NOR */
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	/* boot flash at chip select 0 */
1878c2ecf20Sopenharmony_ci	cs = 0;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (boot_nand) {
1908c2ecf20Sopenharmony_ci		nand_priv.cs = cs;
1918c2ecf20Sopenharmony_ci		nand_priv.flash_mmio = flash_mmio;
1928c2ecf20Sopenharmony_ci		setup_flash_resource(flash_mmio, flash_map_base, cs,
1938c2ecf20Sopenharmony_ci			 xlr_nand_res);
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci		/* Initialize NAND flash at CS 0 */
1968c2ecf20Sopenharmony_ci		nlm_write_reg(flash_mmio, FLASH_CSDEV_PARM(cs),
1978c2ecf20Sopenharmony_ci				FLASH_NAND_CSDEV_PARAM);
1988c2ecf20Sopenharmony_ci		nlm_write_reg(flash_mmio, FLASH_CSTIME_PARMA(cs),
1998c2ecf20Sopenharmony_ci				FLASH_NAND_CSTIME_PARAMA);
2008c2ecf20Sopenharmony_ci		nlm_write_reg(flash_mmio, FLASH_CSTIME_PARMB(cs),
2018c2ecf20Sopenharmony_ci				FLASH_NAND_CSTIME_PARAMB);
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci		pr_info("ChipSelect %d: NAND Flash %pR\n", cs, xlr_nand_res);
2048c2ecf20Sopenharmony_ci		return platform_device_register(&xlr_nand_dev);
2058c2ecf20Sopenharmony_ci	}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	if (boot_nor) {
2088c2ecf20Sopenharmony_ci		setup_flash_resource(flash_mmio, flash_map_base, cs,
2098c2ecf20Sopenharmony_ci			xlr_nor_res);
2108c2ecf20Sopenharmony_ci		pr_info("ChipSelect %d: NOR Flash %pR\n", cs, xlr_nor_res);
2118c2ecf20Sopenharmony_ci		return platform_device_register(&xlr_nor_dev);
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	return 0;
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ciarch_initcall(xlr_flash_init);
217