mtd: rawnand: denali: set SPARE_AREA_SKIP_BYTES based on DT compatible
[oweals/u-boot.git] / drivers / mtd / nand / raw / denali_dt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2017 Socionext Inc.
4  *   Author: Masahiro Yamada <yamada.masahiro@socionext.com>
5  */
6
7 #include <clk.h>
8 #include <dm.h>
9 #include <linux/io.h>
10 #include <linux/ioport.h>
11 #include <linux/printk.h>
12 #include <reset.h>
13
14 #include "denali.h"
15
16 struct denali_dt_data {
17         unsigned int revision;
18         unsigned int caps;
19         unsigned int oob_skip_bytes;
20         const struct nand_ecc_caps *ecc_caps;
21 };
22
23 NAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
24                      512, 8, 15);
25 static const struct denali_dt_data denali_socfpga_data = {
26         .caps = DENALI_CAP_HW_ECC_FIXUP,
27         .oob_skip_bytes = 2,
28         .ecc_caps = &denali_socfpga_ecc_caps,
29 };
30
31 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
32                      1024, 8, 16, 24);
33 static const struct denali_dt_data denali_uniphier_v5a_data = {
34         .caps = DENALI_CAP_HW_ECC_FIXUP |
35                 DENALI_CAP_DMA_64BIT,
36         .oob_skip_bytes = 8,
37         .ecc_caps = &denali_uniphier_v5a_ecc_caps,
38 };
39
40 NAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
41                      1024, 8, 16);
42 static const struct denali_dt_data denali_uniphier_v5b_data = {
43         .revision = 0x0501,
44         .caps = DENALI_CAP_HW_ECC_FIXUP |
45                 DENALI_CAP_DMA_64BIT,
46         .oob_skip_bytes = 8,
47         .ecc_caps = &denali_uniphier_v5b_ecc_caps,
48 };
49
50 static const struct udevice_id denali_nand_dt_ids[] = {
51         {
52                 .compatible = "altr,socfpga-denali-nand",
53                 .data = (unsigned long)&denali_socfpga_data,
54         },
55         {
56                 .compatible = "socionext,uniphier-denali-nand-v5a",
57                 .data = (unsigned long)&denali_uniphier_v5a_data,
58         },
59         {
60                 .compatible = "socionext,uniphier-denali-nand-v5b",
61                 .data = (unsigned long)&denali_uniphier_v5b_data,
62         },
63         { /* sentinel */ }
64 };
65
66 static int denali_dt_probe(struct udevice *dev)
67 {
68         struct denali_nand_info *denali = dev_get_priv(dev);
69         const struct denali_dt_data *data;
70         struct clk clk, clk_x, clk_ecc;
71         struct reset_ctl_bulk resets;
72         struct resource res;
73         int ret;
74
75         data = (void *)dev_get_driver_data(dev);
76         if (WARN_ON(!data))
77                 return -EINVAL;
78
79         denali->revision = data->revision;
80         denali->caps = data->caps;
81         denali->oob_skip_bytes = data->oob_skip_bytes;
82         denali->ecc_caps = data->ecc_caps;
83
84         denali->dev = dev;
85
86         ret = dev_read_resource_byname(dev, "denali_reg", &res);
87         if (ret)
88                 return ret;
89
90         denali->reg = devm_ioremap(dev, res.start, resource_size(&res));
91
92         ret = dev_read_resource_byname(dev, "nand_data", &res);
93         if (ret)
94                 return ret;
95
96         denali->host = devm_ioremap(dev, res.start, resource_size(&res));
97
98         ret = clk_get_by_name(dev, "nand", &clk);
99         if (ret)
100                 ret = clk_get_by_index(dev, 0, &clk);
101         if (ret)
102                 clk.dev = NULL;
103
104         ret = clk_get_by_name(dev, "nand_x", &clk_x);
105         if (ret)
106                 clk_x.dev = NULL;
107
108         ret = clk_get_by_name(dev, "ecc", &clk_ecc);
109         if (ret)
110                 clk_ecc.dev = NULL;
111
112         if (clk.dev) {
113                 ret = clk_enable(&clk);
114                 if (ret)
115                         return ret;
116         }
117
118         if (clk_x.dev) {
119                 ret = clk_enable(&clk_x);
120                 if (ret)
121                         return ret;
122         }
123
124         if (clk_ecc.dev) {
125                 ret = clk_enable(&clk_ecc);
126                 if (ret)
127                         return ret;
128         }
129
130         if (clk_x.dev) {
131                 denali->clk_rate = clk_get_rate(&clk);
132                 denali->clk_x_rate = clk_get_rate(&clk_x);
133         } else {
134                 /*
135                  * Hardcode the clock rates for the backward compatibility.
136                  * This works for both SOCFPGA and UniPhier.
137                  */
138                 dev_notice(dev,
139                            "necessary clock is missing. default clock rates are used.\n");
140                 denali->clk_rate = 50000000;
141                 denali->clk_x_rate = 200000000;
142         }
143
144         ret = reset_get_bulk(dev, &resets);
145         if (ret) {
146                 dev_warn(dev, "Can't get reset: %d\n", ret);
147         } else {
148                 reset_deassert_bulk(&resets);
149
150                 /*
151                  * When the reset is deasserted, the initialization sequence is
152                  * kicked (bootstrap process). The driver must wait until it is
153                  * finished. Otherwise, it will result in unpredictable behavior.
154                  */
155                 udelay(200);
156         }
157
158         return denali_init(denali);
159 }
160
161 U_BOOT_DRIVER(denali_nand_dt) = {
162         .name = "denali-nand-dt",
163         .id = UCLASS_MISC,
164         .of_match = denali_nand_dt_ids,
165         .probe = denali_dt_probe,
166         .priv_auto_alloc_size = sizeof(struct denali_nand_info),
167 };
168
169 void board_nand_init(void)
170 {
171         struct udevice *dev;
172         int ret;
173
174         ret = uclass_get_device_by_driver(UCLASS_MISC,
175                                           DM_GET_DRIVER(denali_nand_dt),
176                                           &dev);
177         if (ret && ret != -ENODEV)
178                 pr_err("Failed to initialize Denali NAND controller. (error %d)\n",
179                        ret);
180 }