1/*
2 *  This program is free software; you can redistribute it and/or modify it
3 *  under the terms of the GNU General Public License version 2 as published
4 *  by the Free Software Foundation.
5 *
6 *  based on xway_nand.c
7 *  Copyright �� 2012 John Crispin <blogic@openwrt.org>
8 *  and oxnas_nand.c "NAND glue for Oxnas platforms"
9 *  written by Ma Haijun <mahaijuns@gmail.com>
10 */
11
12#include <linux/mtd/nand.h>
13#include <linux/of_gpio.h>
14#include <linux/of_platform.h>
15#include <linux/clk.h>
16#include <linux/reset.h>
17
18/* nand commands */
19#define NAND_CMD_ALE		BIT(18)
20#define NAND_CMD_CLE		BIT(19)
21#define NAND_CMD_CS		0
22#define NAND_CMD_RESET		0xff
23#define NAND_CMD		(NAND_CMD_CS | NAND_CMD_CLE)
24#define NAND_ADDR		(NAND_CMD_CS | NAND_CMD_ALE)
25#define NAND_DATA		(NAND_CMD_CS)
26
27static void oxnas_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
28{
29	struct nand_chip *this = mtd->priv;
30	unsigned long nandaddr = (unsigned long) this->IO_ADDR_W;
31
32	if (ctrl & NAND_CTRL_CHANGE) {
33		nandaddr &= ~(NAND_CMD | NAND_ADDR);
34		if (ctrl & NAND_CLE)
35			nandaddr |= NAND_CMD;
36		else if (ctrl & NAND_ALE)
37			nandaddr |= NAND_ADDR;
38		this->IO_ADDR_W = (void __iomem *) nandaddr;
39	}
40
41	if (cmd != NAND_CMD_NONE)
42		writeb(cmd, (void __iomem *) nandaddr);
43}
44
45static int oxnas_nand_probe(struct platform_device *pdev)
46{
47	/* enable clock and release static block reset */
48	struct clk *clk = of_clk_get(pdev->dev.of_node, 0);
49
50	if (IS_ERR(clk))
51		return PTR_ERR(clk);
52
53	clk_prepare_enable(clk);
54	device_reset(&pdev->dev);
55
56	return 0;
57}
58
59static struct platform_nand_data oxnas_nand_data = {
60	.chip = {
61		.nr_chips		= 1,
62		.chip_delay		= 30,
63	},
64	.ctrl = {
65		.probe		= oxnas_nand_probe,
66		.cmd_ctrl	= oxnas_cmd_ctrl,
67	}
68};
69
70/*
71 * Try to find the node inside the DT. If it is available attach out
72 * platform_nand_data
73 */
74static int __init oxnas_register_nand(void)
75{
76	struct device_node *node;
77	struct platform_device *pdev;
78
79	node = of_find_compatible_node(NULL, NULL, "plxtech,nand-nas782x");
80	if (!node)
81		return -ENOENT;
82	pdev = of_find_device_by_node(node);
83	if (!pdev)
84		return -EINVAL;
85	pdev->dev.platform_data = &oxnas_nand_data;
86	of_node_put(node);
87	return 0;
88}
89
90subsys_initcall(oxnas_register_nand);
91