18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * NAND Flash Controller Device Driver for DT
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright © 2011, Picochip.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci#include <linux/io.h>
128c2ecf20Sopenharmony_ci#include <linux/ioport.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/module.h>
158c2ecf20Sopenharmony_ci#include <linux/of.h>
168c2ecf20Sopenharmony_ci#include <linux/of_device.h>
178c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
188c2ecf20Sopenharmony_ci#include <linux/reset.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "denali.h"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistruct denali_dt {
238c2ecf20Sopenharmony_ci	struct denali_controller controller;
248c2ecf20Sopenharmony_ci	struct clk *clk;	/* core clock */
258c2ecf20Sopenharmony_ci	struct clk *clk_x;	/* bus interface clock */
268c2ecf20Sopenharmony_ci	struct clk *clk_ecc;	/* ECC circuit clock */
278c2ecf20Sopenharmony_ci	struct reset_control *rst;	/* core reset */
288c2ecf20Sopenharmony_ci	struct reset_control *rst_reg;	/* register reset */
298c2ecf20Sopenharmony_ci};
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_cistruct denali_dt_data {
328c2ecf20Sopenharmony_ci	unsigned int revision;
338c2ecf20Sopenharmony_ci	unsigned int caps;
348c2ecf20Sopenharmony_ci	unsigned int oob_skip_bytes;
358c2ecf20Sopenharmony_ci	const struct nand_ecc_caps *ecc_caps;
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(denali_socfpga_ecc_caps, denali_calc_ecc_bytes,
398c2ecf20Sopenharmony_ci		     512, 8, 15);
408c2ecf20Sopenharmony_cistatic const struct denali_dt_data denali_socfpga_data = {
418c2ecf20Sopenharmony_ci	.caps = DENALI_CAP_HW_ECC_FIXUP,
428c2ecf20Sopenharmony_ci	.oob_skip_bytes = 2,
438c2ecf20Sopenharmony_ci	.ecc_caps = &denali_socfpga_ecc_caps,
448c2ecf20Sopenharmony_ci};
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(denali_uniphier_v5a_ecc_caps, denali_calc_ecc_bytes,
478c2ecf20Sopenharmony_ci		     1024, 8, 16, 24);
488c2ecf20Sopenharmony_cistatic const struct denali_dt_data denali_uniphier_v5a_data = {
498c2ecf20Sopenharmony_ci	.caps = DENALI_CAP_HW_ECC_FIXUP |
508c2ecf20Sopenharmony_ci		DENALI_CAP_DMA_64BIT,
518c2ecf20Sopenharmony_ci	.oob_skip_bytes = 8,
528c2ecf20Sopenharmony_ci	.ecc_caps = &denali_uniphier_v5a_ecc_caps,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ciNAND_ECC_CAPS_SINGLE(denali_uniphier_v5b_ecc_caps, denali_calc_ecc_bytes,
568c2ecf20Sopenharmony_ci		     1024, 8, 16);
578c2ecf20Sopenharmony_cistatic const struct denali_dt_data denali_uniphier_v5b_data = {
588c2ecf20Sopenharmony_ci	.revision = 0x0501,
598c2ecf20Sopenharmony_ci	.caps = DENALI_CAP_HW_ECC_FIXUP |
608c2ecf20Sopenharmony_ci		DENALI_CAP_DMA_64BIT,
618c2ecf20Sopenharmony_ci	.oob_skip_bytes = 8,
628c2ecf20Sopenharmony_ci	.ecc_caps = &denali_uniphier_v5b_ecc_caps,
638c2ecf20Sopenharmony_ci};
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic const struct of_device_id denali_nand_dt_ids[] = {
668c2ecf20Sopenharmony_ci	{
678c2ecf20Sopenharmony_ci		.compatible = "altr,socfpga-denali-nand",
688c2ecf20Sopenharmony_ci		.data = &denali_socfpga_data,
698c2ecf20Sopenharmony_ci	},
708c2ecf20Sopenharmony_ci	{
718c2ecf20Sopenharmony_ci		.compatible = "socionext,uniphier-denali-nand-v5a",
728c2ecf20Sopenharmony_ci		.data = &denali_uniphier_v5a_data,
738c2ecf20Sopenharmony_ci	},
748c2ecf20Sopenharmony_ci	{
758c2ecf20Sopenharmony_ci		.compatible = "socionext,uniphier-denali-nand-v5b",
768c2ecf20Sopenharmony_ci		.data = &denali_uniphier_v5b_data,
778c2ecf20Sopenharmony_ci	},
788c2ecf20Sopenharmony_ci	{ /* sentinel */ }
798c2ecf20Sopenharmony_ci};
808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, denali_nand_dt_ids);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic int denali_dt_chip_init(struct denali_controller *denali,
838c2ecf20Sopenharmony_ci			       struct device_node *chip_np)
848c2ecf20Sopenharmony_ci{
858c2ecf20Sopenharmony_ci	struct denali_chip *dchip;
868c2ecf20Sopenharmony_ci	u32 bank;
878c2ecf20Sopenharmony_ci	int nsels, i, ret;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	nsels = of_property_count_u32_elems(chip_np, "reg");
908c2ecf20Sopenharmony_ci	if (nsels < 0)
918c2ecf20Sopenharmony_ci		return nsels;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	dchip = devm_kzalloc(denali->dev, struct_size(dchip, sels, nsels),
948c2ecf20Sopenharmony_ci			     GFP_KERNEL);
958c2ecf20Sopenharmony_ci	if (!dchip)
968c2ecf20Sopenharmony_ci		return -ENOMEM;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	dchip->nsels = nsels;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	for (i = 0; i < nsels; i++) {
1018c2ecf20Sopenharmony_ci		ret = of_property_read_u32_index(chip_np, "reg", i, &bank);
1028c2ecf20Sopenharmony_ci		if (ret)
1038c2ecf20Sopenharmony_ci			return ret;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci		dchip->sels[i].bank = bank;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci		nand_set_flash_node(&dchip->chip, chip_np);
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	return denali_chip_init(denali, dchip);
1118c2ecf20Sopenharmony_ci}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic int denali_dt_probe(struct platform_device *pdev)
1148c2ecf20Sopenharmony_ci{
1158c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
1168c2ecf20Sopenharmony_ci	struct resource *res;
1178c2ecf20Sopenharmony_ci	struct denali_dt *dt;
1188c2ecf20Sopenharmony_ci	const struct denali_dt_data *data;
1198c2ecf20Sopenharmony_ci	struct denali_controller *denali;
1208c2ecf20Sopenharmony_ci	struct device_node *np;
1218c2ecf20Sopenharmony_ci	int ret;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	dt = devm_kzalloc(dev, sizeof(*dt), GFP_KERNEL);
1248c2ecf20Sopenharmony_ci	if (!dt)
1258c2ecf20Sopenharmony_ci		return -ENOMEM;
1268c2ecf20Sopenharmony_ci	denali = &dt->controller;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	data = of_device_get_match_data(dev);
1298c2ecf20Sopenharmony_ci	if (WARN_ON(!data))
1308c2ecf20Sopenharmony_ci		return -EINVAL;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	denali->revision = data->revision;
1338c2ecf20Sopenharmony_ci	denali->caps = data->caps;
1348c2ecf20Sopenharmony_ci	denali->oob_skip_bytes = data->oob_skip_bytes;
1358c2ecf20Sopenharmony_ci	denali->ecc_caps = data->ecc_caps;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	denali->dev = dev;
1388c2ecf20Sopenharmony_ci	denali->irq = platform_get_irq(pdev, 0);
1398c2ecf20Sopenharmony_ci	if (denali->irq < 0)
1408c2ecf20Sopenharmony_ci		return denali->irq;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "denali_reg");
1438c2ecf20Sopenharmony_ci	denali->reg = devm_ioremap_resource(dev, res);
1448c2ecf20Sopenharmony_ci	if (IS_ERR(denali->reg))
1458c2ecf20Sopenharmony_ci		return PTR_ERR(denali->reg);
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data");
1488c2ecf20Sopenharmony_ci	denali->host = devm_ioremap_resource(dev, res);
1498c2ecf20Sopenharmony_ci	if (IS_ERR(denali->host))
1508c2ecf20Sopenharmony_ci		return PTR_ERR(denali->host);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	dt->clk = devm_clk_get(dev, "nand");
1538c2ecf20Sopenharmony_ci	if (IS_ERR(dt->clk))
1548c2ecf20Sopenharmony_ci		return PTR_ERR(dt->clk);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	dt->clk_x = devm_clk_get(dev, "nand_x");
1578c2ecf20Sopenharmony_ci	if (IS_ERR(dt->clk_x))
1588c2ecf20Sopenharmony_ci		return PTR_ERR(dt->clk_x);
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	dt->clk_ecc = devm_clk_get(dev, "ecc");
1618c2ecf20Sopenharmony_ci	if (IS_ERR(dt->clk_ecc))
1628c2ecf20Sopenharmony_ci		return PTR_ERR(dt->clk_ecc);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	dt->rst = devm_reset_control_get_optional_shared(dev, "nand");
1658c2ecf20Sopenharmony_ci	if (IS_ERR(dt->rst))
1668c2ecf20Sopenharmony_ci		return PTR_ERR(dt->rst);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	dt->rst_reg = devm_reset_control_get_optional_shared(dev, "reg");
1698c2ecf20Sopenharmony_ci	if (IS_ERR(dt->rst_reg))
1708c2ecf20Sopenharmony_ci		return PTR_ERR(dt->rst_reg);
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(dt->clk);
1738c2ecf20Sopenharmony_ci	if (ret)
1748c2ecf20Sopenharmony_ci		return ret;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(dt->clk_x);
1778c2ecf20Sopenharmony_ci	if (ret)
1788c2ecf20Sopenharmony_ci		goto out_disable_clk;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ret = clk_prepare_enable(dt->clk_ecc);
1818c2ecf20Sopenharmony_ci	if (ret)
1828c2ecf20Sopenharmony_ci		goto out_disable_clk_x;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	denali->clk_rate = clk_get_rate(dt->clk);
1858c2ecf20Sopenharmony_ci	denali->clk_x_rate = clk_get_rate(dt->clk_x);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	/*
1888c2ecf20Sopenharmony_ci	 * Deassert the register reset, and the core reset in this order.
1898c2ecf20Sopenharmony_ci	 * Deasserting the core reset while the register reset is asserted
1908c2ecf20Sopenharmony_ci	 * will cause unpredictable behavior in the controller.
1918c2ecf20Sopenharmony_ci	 */
1928c2ecf20Sopenharmony_ci	ret = reset_control_deassert(dt->rst_reg);
1938c2ecf20Sopenharmony_ci	if (ret)
1948c2ecf20Sopenharmony_ci		goto out_disable_clk_ecc;
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	ret = reset_control_deassert(dt->rst);
1978c2ecf20Sopenharmony_ci	if (ret)
1988c2ecf20Sopenharmony_ci		goto out_assert_rst_reg;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/*
2018c2ecf20Sopenharmony_ci	 * When the reset is deasserted, the initialization sequence is kicked
2028c2ecf20Sopenharmony_ci	 * (bootstrap process). The driver must wait until it finished.
2038c2ecf20Sopenharmony_ci	 * Otherwise, it will result in unpredictable behavior.
2048c2ecf20Sopenharmony_ci	 */
2058c2ecf20Sopenharmony_ci	usleep_range(200, 1000);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	ret = denali_init(denali);
2088c2ecf20Sopenharmony_ci	if (ret)
2098c2ecf20Sopenharmony_ci		goto out_assert_rst;
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	for_each_child_of_node(dev->of_node, np) {
2128c2ecf20Sopenharmony_ci		ret = denali_dt_chip_init(denali, np);
2138c2ecf20Sopenharmony_ci		if (ret) {
2148c2ecf20Sopenharmony_ci			of_node_put(np);
2158c2ecf20Sopenharmony_ci			goto out_remove_denali;
2168c2ecf20Sopenharmony_ci		}
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dt);
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	return 0;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ciout_remove_denali:
2248c2ecf20Sopenharmony_ci	denali_remove(denali);
2258c2ecf20Sopenharmony_ciout_assert_rst:
2268c2ecf20Sopenharmony_ci	reset_control_assert(dt->rst);
2278c2ecf20Sopenharmony_ciout_assert_rst_reg:
2288c2ecf20Sopenharmony_ci	reset_control_assert(dt->rst_reg);
2298c2ecf20Sopenharmony_ciout_disable_clk_ecc:
2308c2ecf20Sopenharmony_ci	clk_disable_unprepare(dt->clk_ecc);
2318c2ecf20Sopenharmony_ciout_disable_clk_x:
2328c2ecf20Sopenharmony_ci	clk_disable_unprepare(dt->clk_x);
2338c2ecf20Sopenharmony_ciout_disable_clk:
2348c2ecf20Sopenharmony_ci	clk_disable_unprepare(dt->clk);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic int denali_dt_remove(struct platform_device *pdev)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct denali_dt *dt = platform_get_drvdata(pdev);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	denali_remove(&dt->controller);
2448c2ecf20Sopenharmony_ci	reset_control_assert(dt->rst);
2458c2ecf20Sopenharmony_ci	reset_control_assert(dt->rst_reg);
2468c2ecf20Sopenharmony_ci	clk_disable_unprepare(dt->clk_ecc);
2478c2ecf20Sopenharmony_ci	clk_disable_unprepare(dt->clk_x);
2488c2ecf20Sopenharmony_ci	clk_disable_unprepare(dt->clk);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	return 0;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_cistatic struct platform_driver denali_dt_driver = {
2548c2ecf20Sopenharmony_ci	.probe		= denali_dt_probe,
2558c2ecf20Sopenharmony_ci	.remove		= denali_dt_remove,
2568c2ecf20Sopenharmony_ci	.driver		= {
2578c2ecf20Sopenharmony_ci		.name	= "denali-nand-dt",
2588c2ecf20Sopenharmony_ci		.of_match_table	= denali_nand_dt_ids,
2598c2ecf20Sopenharmony_ci	},
2608c2ecf20Sopenharmony_ci};
2618c2ecf20Sopenharmony_cimodule_platform_driver(denali_dt_driver);
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2648c2ecf20Sopenharmony_ciMODULE_AUTHOR("Jamie Iles");
2658c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("DT driver for Denali NAND controller");
266