xref: /openbmc/linux/drivers/mtd/nand/raw/denali_dt.c (revision ba61bb17)
1 /*
2  * NAND Flash Controller Device Driver for DT
3  *
4  * Copyright © 2011, Picochip.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms and conditions of the GNU General Public License,
8  * version 2, as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13  * more details.
14  */
15 
16 #include <linux/clk.h>
17 #include <linux/err.h>
18 #include <linux/io.h>
19 #include <linux/ioport.h>
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/of.h>
23 #include <linux/of_device.h>
24 #include <linux/platform_device.h>
25 
26 #include "denali.h"
27 
28 struct denali_dt {
29 	struct denali_nand_info	denali;
30 	struct clk		*clk;
31 };
32 
33 struct denali_dt_data {
34 	unsigned int revision;
35 	unsigned int caps;
36 	const struct nand_ecc_caps *ecc_caps;
37 };
38 
39 NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
40 		     512, 8, 15);
41 static const struct denali_dt_data denali_socfpga_data = {
42 	.caps = DENALI_CAP_HW_ECC_FIXUP,
43 	.ecc_caps = &denali_socfpga_ecc_caps,
44 };
45 
46 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
47 		     1024, 8, 16, 24);
48 static const struct denali_dt_data denali_uniphier_v5a_data = {
49 	.caps = DENALI_CAP_HW_ECC_FIXUP |
50 		DENALI_CAP_DMA_64BIT,
51 	.ecc_caps = &denali_uniphier_v5a_ecc_caps,
52 };
53 
54 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
55 		     1024, 8, 16);
56 static const struct denali_dt_data denali_uniphier_v5b_data = {
57 	.revision = 0x0501,
58 	.caps = DENALI_CAP_HW_ECC_FIXUP |
59 		DENALI_CAP_DMA_64BIT,
60 	.ecc_caps = &denali_uniphier_v5b_ecc_caps,
61 };
62 
63 static const struct of_device_id denali_nand_dt_ids[] = {
64 	{
65 		.compatible = "altr,socfpga-denali-nand",
66 		.data = &denali_socfpga_data,
67 	},
68 	{
69 		.compatible = "socionext,uniphier-denali-nand-v5a",
70 		.data = &denali_uniphier_v5a_data,
71 	},
72 	{
73 		.compatible = "socionext,uniphier-denali-nand-v5b",
74 		.data = &denali_uniphier_v5b_data,
75 	},
76 	{ /* sentinel */ }
77 };
78 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
79 
80 static int denali_dt_probe(struct platform_device *pdev)
81 {
82 	struct resource *res;
83 	struct denali_dt *dt;
84 	const struct denali_dt_data *data;
85 	struct denali_nand_info *denali;
86 	int ret;
87 
88 	dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
89 	if (!dt)
90 		return -ENOMEM;
91 	denali = &dt->denali;
92 
93 	data = of_device_get_match_data(&pdev->dev);
94 	if (data) {
95 		denali->revision = data->revision;
96 		denali->caps = data->caps;
97 		denali->ecc_caps = data->ecc_caps;
98 	}
99 
100 	denali->dev = &pdev->dev;
101 	denali->irq = platform_get_irq(pdev, 0);
102 	if (denali->irq < 0) {
103 		dev_err(&pdev->dev, "no irq defined\n");
104 		return denali->irq;
105 	}
106 
107 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
108 	denali->reg = devm_ioremap_resource(&pdev->dev, res);
109 	if (IS_ERR(denali->reg))
110 		return PTR_ERR(denali->reg);
111 
112 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
113 	denali->host = devm_ioremap_resource(&pdev->dev, res);
114 	if (IS_ERR(denali->host))
115 		return PTR_ERR(denali->host);
116 
117 	dt->clk = devm_clk_get(&pdev->dev, NULL);
118 	if (IS_ERR(dt->clk)) {
119 		dev_err(&pdev->dev, "no clk available\n");
120 		return PTR_ERR(dt->clk);
121 	}
122 	ret = clk_prepare_enable(dt->clk);
123 	if (ret)
124 		return ret;
125 
126 	/*
127 	 * Hardcode the clock rate for the backward compatibility.
128 	 * This works for both SOCFPGA and UniPhier.
129 	 */
130 	denali->clk_x_rate = 200000000;
131 
132 	ret = denali_init(denali);
133 	if (ret)
134 		goto out_disable_clk;
135 
136 	platform_set_drvdata(pdev, dt);
137 	return 0;
138 
139 out_disable_clk:
140 	clk_disable_unprepare(dt->clk);
141 
142 	return ret;
143 }
144 
145 static int denali_dt_remove(struct platform_device *pdev)
146 {
147 	struct denali_dt *dt = platform_get_drvdata(pdev);
148 
149 	denali_remove(&dt->denali);
150 	clk_disable_unprepare(dt->clk);
151 
152 	return 0;
153 }
154 
155 static struct platform_driver denali_dt_driver = {
156 	.probe		= denali_dt_probe,
157 	.remove		= denali_dt_remove,
158 	.driver		= {
159 		.name	= "denali-nand-dt",
160 		.of_match_table	= denali_nand_dt_ids,
161 	},
162 };
163 module_platform_driver(denali_dt_driver);
164 
165 MODULE_LICENSE("GPL");
166 MODULE_AUTHOR("Jamie Iles");
167 MODULE_DESCRIPTION("DT driver for Denali NAND controller");
168