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