mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
168 lines
3.7 KiB
168 lines
3.7 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* ARM PL353 SMC driver |
|
* |
|
* Copyright (C) 2012 - 2018 Xilinx, Inc |
|
* Author: Punnaiah Choudary Kalluri <[email protected]> |
|
* Author: Naga Sureshkumar Relli <[email protected]> |
|
*/ |
|
|
|
#include <linux/clk.h> |
|
#include <linux/kernel.h> |
|
#include <linux/module.h> |
|
#include <linux/of_platform.h> |
|
#include <linux/platform_device.h> |
|
#include <linux/amba/bus.h> |
|
|
|
/** |
|
* struct pl353_smc_data - Private smc driver structure |
|
* @memclk: Pointer to the peripheral clock |
|
* @aclk: Pointer to the AXI peripheral clock |
|
*/ |
|
struct pl353_smc_data { |
|
struct clk *memclk; |
|
struct clk *aclk; |
|
}; |
|
|
|
static int __maybe_unused pl353_smc_suspend(struct device *dev) |
|
{ |
|
struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); |
|
|
|
clk_disable(pl353_smc->memclk); |
|
clk_disable(pl353_smc->aclk); |
|
|
|
return 0; |
|
} |
|
|
|
static int __maybe_unused pl353_smc_resume(struct device *dev) |
|
{ |
|
struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev); |
|
int ret; |
|
|
|
ret = clk_enable(pl353_smc->aclk); |
|
if (ret) { |
|
dev_err(dev, "Cannot enable axi domain clock.\n"); |
|
return ret; |
|
} |
|
|
|
ret = clk_enable(pl353_smc->memclk); |
|
if (ret) { |
|
dev_err(dev, "Cannot enable memory clock.\n"); |
|
clk_disable(pl353_smc->aclk); |
|
return ret; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
static SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend, |
|
pl353_smc_resume); |
|
|
|
static const struct of_device_id pl353_smc_supported_children[] = { |
|
{ |
|
.compatible = "cfi-flash" |
|
}, |
|
{ |
|
.compatible = "arm,pl353-nand-r2p1", |
|
}, |
|
{} |
|
}; |
|
|
|
static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id) |
|
{ |
|
struct device_node *of_node = adev->dev.of_node; |
|
const struct of_device_id *match = NULL; |
|
struct pl353_smc_data *pl353_smc; |
|
struct device_node *child; |
|
int err; |
|
|
|
pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL); |
|
if (!pl353_smc) |
|
return -ENOMEM; |
|
|
|
pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk"); |
|
if (IS_ERR(pl353_smc->aclk)) { |
|
dev_err(&adev->dev, "aclk clock not found.\n"); |
|
return PTR_ERR(pl353_smc->aclk); |
|
} |
|
|
|
pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk"); |
|
if (IS_ERR(pl353_smc->memclk)) { |
|
dev_err(&adev->dev, "memclk clock not found.\n"); |
|
return PTR_ERR(pl353_smc->memclk); |
|
} |
|
|
|
err = clk_prepare_enable(pl353_smc->aclk); |
|
if (err) { |
|
dev_err(&adev->dev, "Unable to enable AXI clock.\n"); |
|
return err; |
|
} |
|
|
|
err = clk_prepare_enable(pl353_smc->memclk); |
|
if (err) { |
|
dev_err(&adev->dev, "Unable to enable memory clock.\n"); |
|
goto disable_axi_clk; |
|
} |
|
|
|
amba_set_drvdata(adev, pl353_smc); |
|
|
|
/* Find compatible children. Only a single child is supported */ |
|
for_each_available_child_of_node(of_node, child) { |
|
match = of_match_node(pl353_smc_supported_children, child); |
|
if (!match) { |
|
dev_warn(&adev->dev, "unsupported child node\n"); |
|
continue; |
|
} |
|
break; |
|
} |
|
if (!match) { |
|
err = -ENODEV; |
|
dev_err(&adev->dev, "no matching children\n"); |
|
goto disable_mem_clk; |
|
} |
|
|
|
of_platform_device_create(child, NULL, &adev->dev); |
|
|
|
return 0; |
|
|
|
disable_mem_clk: |
|
clk_disable_unprepare(pl353_smc->memclk); |
|
disable_axi_clk: |
|
clk_disable_unprepare(pl353_smc->aclk); |
|
|
|
return err; |
|
} |
|
|
|
static void pl353_smc_remove(struct amba_device *adev) |
|
{ |
|
struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev); |
|
|
|
clk_disable_unprepare(pl353_smc->memclk); |
|
clk_disable_unprepare(pl353_smc->aclk); |
|
} |
|
|
|
static const struct amba_id pl353_ids[] = { |
|
{ |
|
.id = 0x00041353, |
|
.mask = 0x000fffff, |
|
}, |
|
{ 0, 0 }, |
|
}; |
|
MODULE_DEVICE_TABLE(amba, pl353_ids); |
|
|
|
static struct amba_driver pl353_smc_driver = { |
|
.drv = { |
|
.owner = THIS_MODULE, |
|
.name = "pl353-smc", |
|
.pm = &pl353_smc_dev_pm_ops, |
|
}, |
|
.id_table = pl353_ids, |
|
.probe = pl353_smc_probe, |
|
.remove = pl353_smc_remove, |
|
}; |
|
|
|
module_amba_driver(pl353_smc_driver); |
|
|
|
MODULE_AUTHOR("Xilinx, Inc."); |
|
MODULE_DESCRIPTION("ARM PL353 SMC Driver"); |
|
MODULE_LICENSE("GPL");
|
|
|