• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include <linux/clk.h>
16 #include <linux/err.h>
17 #include <linux/io.h>
18 #include <linux/ioport.h>
19 #include <linux/kernel.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/of.h>
23 #include <linux/of_device.h>
24 
25 #include "denali.h"
26 
27 struct denali_dt {
28 	struct denali_nand_info	denali;
29 	struct clk		*clk;
30 };
31 
32 struct denali_dt_data {
33 	unsigned int revision;
34 	unsigned int caps;
35 	const struct nand_ecc_caps *ecc_caps;
36 };
37 
38 NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
39 		     512, 8, 15);
40 static const struct denali_dt_data denali_socfpga_data = {
41 	.caps = DENALI_CAP_HW_ECC_FIXUP,
42 	.ecc_caps = &denali_socfpga_ecc_caps,
43 };
44 
45 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
46 		     1024, 8, 16, 24);
47 static const struct denali_dt_data denali_uniphier_v5a_data = {
48 	.caps = DENALI_CAP_HW_ECC_FIXUP |
49 		DENALI_CAP_DMA_64BIT,
50 	.ecc_caps = &denali_uniphier_v5a_ecc_caps,
51 };
52 
53 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
54 		     1024, 8, 16);
55 static const struct denali_dt_data denali_uniphier_v5b_data = {
56 	.revision = 0x0501,
57 	.caps = DENALI_CAP_HW_ECC_FIXUP |
58 		DENALI_CAP_DMA_64BIT,
59 	.ecc_caps = &denali_uniphier_v5b_ecc_caps,
60 };
61 
62 static const struct of_device_id denali_nand_dt_ids[] = {
63 	{
64 		.compatible = "altr,socfpga-denali-nand",
65 		.data = &denali_socfpga_data,
66 	},
67 	{
68 		.compatible = "socionext,uniphier-denali-nand-v5a",
69 		.data = &denali_uniphier_v5a_data,
70 	},
71 	{
72 		.compatible = "socionext,uniphier-denali-nand-v5b",
73 		.data = &denali_uniphier_v5b_data,
74 	},
75 	{ /* sentinel */ }
76 };
77 MODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
78 
denali_dt_probe(struct platform_device * pdev)79 static int denali_dt_probe(struct platform_device *pdev)
80 {
81 	struct resource *res;
82 	struct denali_dt *dt;
83 	const struct denali_dt_data *data;
84 	struct denali_nand_info *denali;
85 	int ret;
86 
87 	dt = devm_kzalloc(&pdev->dev, sizeof(*dt), GFP_KERNEL);
88 	if (!dt)
89 		return -ENOMEM;
90 	denali = &dt->denali;
91 
92 	data = of_device_get_match_data(&pdev->dev);
93 	if (data) {
94 		denali->revision = data->revision;
95 		denali->caps = data->caps;
96 		denali->ecc_caps = data->ecc_caps;
97 	}
98 
99 	denali->dev = &pdev->dev;
100 	denali->irq = platform_get_irq(pdev, 0);
101 	if (denali->irq < 0) {
102 		dev_err(&pdev->dev, "no irq defined\n");
103 		return denali->irq;
104 	}
105 
106 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
107 	denali->reg = devm_ioremap_resource(&pdev->dev, res);
108 	if (IS_ERR(denali->reg))
109 		return PTR_ERR(denali->reg);
110 
111 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
112 	denali->host = devm_ioremap_resource(&pdev->dev, res);
113 	if (IS_ERR(denali->host))
114 		return PTR_ERR(denali->host);
115 
116 	dt->clk = devm_clk_get(&pdev->dev, NULL);
117 	if (IS_ERR(dt->clk)) {
118 		dev_err(&pdev->dev, "no clk available\n");
119 		return PTR_ERR(dt->clk);
120 	}
121 	ret = clk_prepare_enable(dt->clk);
122 	if (ret)
123 		return ret;
124 
125 	/*
126 	 * Hardcode the clock rate for the backward compatibility.
127 	 * This works for both SOCFPGA and UniPhier.
128 	 */
129 	denali->clk_x_rate = 200000000;
130 
131 	ret = denali_init(denali);
132 	if (ret)
133 		goto out_disable_clk;
134 
135 	platform_set_drvdata(pdev, dt);
136 	return 0;
137 
138 out_disable_clk:
139 	clk_disable_unprepare(dt->clk);
140 
141 	return ret;
142 }
143 
denali_dt_remove(struct platform_device * pdev)144 static int denali_dt_remove(struct platform_device *pdev)
145 {
146 	struct denali_dt *dt = platform_get_drvdata(pdev);
147 
148 	denali_remove(&dt->denali);
149 	clk_disable_unprepare(dt->clk);
150 
151 	return 0;
152 }
153 
154 static struct platform_driver denali_dt_driver = {
155 	.probe		= denali_dt_probe,
156 	.remove		= denali_dt_remove,
157 	.driver		= {
158 		.name	= "denali-nand-dt",
159 		.of_match_table	= denali_nand_dt_ids,
160 	},
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