linux/drivers/memory/pl353-smc.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * ARM PL353 SMC driver
   4 *
   5 * Copyright (C) 2012 - 2018 Xilinx, Inc
   6 * Author: Punnaiah Choudary Kalluri <punnaiah@xilinx.com>
   7 * Author: Naga Sureshkumar Relli <nagasure@xilinx.com>
   8 */
   9
  10#include <linux/clk.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/of_platform.h>
  14#include <linux/platform_device.h>
  15#include <linux/amba/bus.h>
  16
  17/**
  18 * struct pl353_smc_data - Private smc driver structure
  19 * @memclk:             Pointer to the peripheral clock
  20 * @aclk:               Pointer to the AXI peripheral clock
  21 */
  22struct pl353_smc_data {
  23        struct clk              *memclk;
  24        struct clk              *aclk;
  25};
  26
  27static int __maybe_unused pl353_smc_suspend(struct device *dev)
  28{
  29        struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev);
  30
  31        clk_disable(pl353_smc->memclk);
  32        clk_disable(pl353_smc->aclk);
  33
  34        return 0;
  35}
  36
  37static int __maybe_unused pl353_smc_resume(struct device *dev)
  38{
  39        struct pl353_smc_data *pl353_smc = dev_get_drvdata(dev);
  40        int ret;
  41
  42        ret = clk_enable(pl353_smc->aclk);
  43        if (ret) {
  44                dev_err(dev, "Cannot enable axi domain clock.\n");
  45                return ret;
  46        }
  47
  48        ret = clk_enable(pl353_smc->memclk);
  49        if (ret) {
  50                dev_err(dev, "Cannot enable memory clock.\n");
  51                clk_disable(pl353_smc->aclk);
  52                return ret;
  53        }
  54
  55        return ret;
  56}
  57
  58static SIMPLE_DEV_PM_OPS(pl353_smc_dev_pm_ops, pl353_smc_suspend,
  59                         pl353_smc_resume);
  60
  61static const struct of_device_id pl353_smc_supported_children[] = {
  62        {
  63                .compatible = "cfi-flash"
  64        },
  65        {
  66                .compatible = "arm,pl353-nand-r2p1",
  67        },
  68        {}
  69};
  70
  71static int pl353_smc_probe(struct amba_device *adev, const struct amba_id *id)
  72{
  73        struct device_node *of_node = adev->dev.of_node;
  74        const struct of_device_id *match = NULL;
  75        struct pl353_smc_data *pl353_smc;
  76        struct device_node *child;
  77        int err;
  78
  79        pl353_smc = devm_kzalloc(&adev->dev, sizeof(*pl353_smc), GFP_KERNEL);
  80        if (!pl353_smc)
  81                return -ENOMEM;
  82
  83        pl353_smc->aclk = devm_clk_get(&adev->dev, "apb_pclk");
  84        if (IS_ERR(pl353_smc->aclk)) {
  85                dev_err(&adev->dev, "aclk clock not found.\n");
  86                return PTR_ERR(pl353_smc->aclk);
  87        }
  88
  89        pl353_smc->memclk = devm_clk_get(&adev->dev, "memclk");
  90        if (IS_ERR(pl353_smc->memclk)) {
  91                dev_err(&adev->dev, "memclk clock not found.\n");
  92                return PTR_ERR(pl353_smc->memclk);
  93        }
  94
  95        err = clk_prepare_enable(pl353_smc->aclk);
  96        if (err) {
  97                dev_err(&adev->dev, "Unable to enable AXI clock.\n");
  98                return err;
  99        }
 100
 101        err = clk_prepare_enable(pl353_smc->memclk);
 102        if (err) {
 103                dev_err(&adev->dev, "Unable to enable memory clock.\n");
 104                goto disable_axi_clk;
 105        }
 106
 107        amba_set_drvdata(adev, pl353_smc);
 108
 109        /* Find compatible children. Only a single child is supported */
 110        for_each_available_child_of_node(of_node, child) {
 111                match = of_match_node(pl353_smc_supported_children, child);
 112                if (!match) {
 113                        dev_warn(&adev->dev, "unsupported child node\n");
 114                        continue;
 115                }
 116                break;
 117        }
 118        if (!match) {
 119                err = -ENODEV;
 120                dev_err(&adev->dev, "no matching children\n");
 121                goto disable_mem_clk;
 122        }
 123
 124        of_platform_device_create(child, NULL, &adev->dev);
 125
 126        return 0;
 127
 128disable_mem_clk:
 129        clk_disable_unprepare(pl353_smc->memclk);
 130disable_axi_clk:
 131        clk_disable_unprepare(pl353_smc->aclk);
 132
 133        return err;
 134}
 135
 136static void pl353_smc_remove(struct amba_device *adev)
 137{
 138        struct pl353_smc_data *pl353_smc = amba_get_drvdata(adev);
 139
 140        clk_disable_unprepare(pl353_smc->memclk);
 141        clk_disable_unprepare(pl353_smc->aclk);
 142}
 143
 144static const struct amba_id pl353_ids[] = {
 145        {
 146                .id = 0x00041353,
 147                .mask = 0x000fffff,
 148        },
 149        { 0, 0 },
 150};
 151MODULE_DEVICE_TABLE(amba, pl353_ids);
 152
 153static struct amba_driver pl353_smc_driver = {
 154        .drv = {
 155                .owner = THIS_MODULE,
 156                .name = "pl353-smc",
 157                .pm = &pl353_smc_dev_pm_ops,
 158        },
 159        .id_table = pl353_ids,
 160        .probe = pl353_smc_probe,
 161        .remove = pl353_smc_remove,
 162};
 163
 164module_amba_driver(pl353_smc_driver);
 165
 166MODULE_AUTHOR("Xilinx, Inc.");
 167MODULE_DESCRIPTION("ARM PL353 SMC Driver");
 168MODULE_LICENSE("GPL");
 169