linux/drivers/fpga/altera-freeze-bridge.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * FPGA Freeze Bridge Controller
   4 *
   5 *  Copyright (C) 2016 Altera Corporation. All rights reserved.
   6 */
   7#include <linux/delay.h>
   8#include <linux/io.h>
   9#include <linux/kernel.h>
  10#include <linux/of_device.h>
  11#include <linux/module.h>
  12#include <linux/fpga/fpga-bridge.h>
  13
  14#define FREEZE_CSR_STATUS_OFFSET                0
  15#define FREEZE_CSR_CTRL_OFFSET                  4
  16#define FREEZE_CSR_ILLEGAL_REQ_OFFSET           8
  17#define FREEZE_CSR_REG_VERSION                  12
  18
  19#define FREEZE_CSR_SUPPORTED_VERSION            2
  20#define FREEZE_CSR_OFFICIAL_VERSION             0xad000003
  21
  22#define FREEZE_CSR_STATUS_FREEZE_REQ_DONE       BIT(0)
  23#define FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE     BIT(1)
  24
  25#define FREEZE_CSR_CTRL_FREEZE_REQ              BIT(0)
  26#define FREEZE_CSR_CTRL_RESET_REQ               BIT(1)
  27#define FREEZE_CSR_CTRL_UNFREEZE_REQ            BIT(2)
  28
  29#define FREEZE_BRIDGE_NAME                      "freeze"
  30
  31struct altera_freeze_br_data {
  32        struct device *dev;
  33        void __iomem *base_addr;
  34        bool enable;
  35};
  36
  37/*
  38 * Poll status until status bit is set or we have a timeout.
  39 */
  40static int altera_freeze_br_req_ack(struct altera_freeze_br_data *priv,
  41                                    u32 timeout, u32 req_ack)
  42{
  43        struct device *dev = priv->dev;
  44        void __iomem *csr_illegal_req_addr = priv->base_addr +
  45                                             FREEZE_CSR_ILLEGAL_REQ_OFFSET;
  46        u32 status, illegal, ctrl;
  47        int ret = -ETIMEDOUT;
  48
  49        do {
  50                illegal = readl(csr_illegal_req_addr);
  51                if (illegal) {
  52                        dev_err(dev, "illegal request detected 0x%x", illegal);
  53
  54                        writel(1, csr_illegal_req_addr);
  55
  56                        illegal = readl(csr_illegal_req_addr);
  57                        if (illegal)
  58                                dev_err(dev, "illegal request not cleared 0x%x",
  59                                        illegal);
  60
  61                        ret = -EINVAL;
  62                        break;
  63                }
  64
  65                status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
  66                dev_dbg(dev, "%s %x %x\n", __func__, status, req_ack);
  67                status &= req_ack;
  68                if (status) {
  69                        ctrl = readl(priv->base_addr + FREEZE_CSR_CTRL_OFFSET);
  70                        dev_dbg(dev, "%s request %x acknowledged %x %x\n",
  71                                __func__, req_ack, status, ctrl);
  72                        ret = 0;
  73                        break;
  74                }
  75
  76                udelay(1);
  77        } while (timeout--);
  78
  79        if (ret == -ETIMEDOUT)
  80                dev_err(dev, "%s timeout waiting for 0x%x\n",
  81                        __func__, req_ack);
  82
  83        return ret;
  84}
  85
  86static int altera_freeze_br_do_freeze(struct altera_freeze_br_data *priv,
  87                                      u32 timeout)
  88{
  89        struct device *dev = priv->dev;
  90        void __iomem *csr_ctrl_addr = priv->base_addr +
  91                                      FREEZE_CSR_CTRL_OFFSET;
  92        u32 status;
  93        int ret;
  94
  95        status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
  96
  97        dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
  98
  99        if (status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE) {
 100                dev_dbg(dev, "%s bridge already disabled %d\n",
 101                        __func__, status);
 102                return 0;
 103        } else if (!(status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)) {
 104                dev_err(dev, "%s bridge not enabled %d\n", __func__, status);
 105                return -EINVAL;
 106        }
 107
 108        writel(FREEZE_CSR_CTRL_FREEZE_REQ, csr_ctrl_addr);
 109
 110        ret = altera_freeze_br_req_ack(priv, timeout,
 111                                       FREEZE_CSR_STATUS_FREEZE_REQ_DONE);
 112
 113        if (ret)
 114                writel(0, csr_ctrl_addr);
 115        else
 116                writel(FREEZE_CSR_CTRL_RESET_REQ, csr_ctrl_addr);
 117
 118        return ret;
 119}
 120
 121static int altera_freeze_br_do_unfreeze(struct altera_freeze_br_data *priv,
 122                                        u32 timeout)
 123{
 124        struct device *dev = priv->dev;
 125        void __iomem *csr_ctrl_addr = priv->base_addr +
 126                                      FREEZE_CSR_CTRL_OFFSET;
 127        u32 status;
 128        int ret;
 129
 130        writel(0, csr_ctrl_addr);
 131
 132        status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
 133
 134        dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
 135
 136        if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE) {
 137                dev_dbg(dev, "%s bridge already enabled %d\n",
 138                        __func__, status);
 139                return 0;
 140        } else if (!(status & FREEZE_CSR_STATUS_FREEZE_REQ_DONE)) {
 141                dev_err(dev, "%s bridge not frozen %d\n", __func__, status);
 142                return -EINVAL;
 143        }
 144
 145        writel(FREEZE_CSR_CTRL_UNFREEZE_REQ, csr_ctrl_addr);
 146
 147        ret = altera_freeze_br_req_ack(priv, timeout,
 148                                       FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE);
 149
 150        status = readl(priv->base_addr + FREEZE_CSR_STATUS_OFFSET);
 151
 152        dev_dbg(dev, "%s %d %d\n", __func__, status, readl(csr_ctrl_addr));
 153
 154        writel(0, csr_ctrl_addr);
 155
 156        return ret;
 157}
 158
 159/*
 160 * enable = 1 : allow traffic through the bridge
 161 * enable = 0 : disable traffic through the bridge
 162 */
 163static int altera_freeze_br_enable_set(struct fpga_bridge *bridge,
 164                                       bool enable)
 165{
 166        struct altera_freeze_br_data *priv = bridge->priv;
 167        struct fpga_image_info *info = bridge->info;
 168        u32 timeout = 0;
 169        int ret;
 170
 171        if (enable) {
 172                if (info)
 173                        timeout = info->enable_timeout_us;
 174
 175                ret = altera_freeze_br_do_unfreeze(bridge->priv, timeout);
 176        } else {
 177                if (info)
 178                        timeout = info->disable_timeout_us;
 179
 180                ret = altera_freeze_br_do_freeze(bridge->priv, timeout);
 181        }
 182
 183        if (!ret)
 184                priv->enable = enable;
 185
 186        return ret;
 187}
 188
 189static int altera_freeze_br_enable_show(struct fpga_bridge *bridge)
 190{
 191        struct altera_freeze_br_data *priv = bridge->priv;
 192
 193        return priv->enable;
 194}
 195
 196static const struct fpga_bridge_ops altera_freeze_br_br_ops = {
 197        .enable_set = altera_freeze_br_enable_set,
 198        .enable_show = altera_freeze_br_enable_show,
 199};
 200
 201#ifdef CONFIG_OF
 202static const struct of_device_id altera_freeze_br_of_match[] = {
 203        { .compatible = "altr,freeze-bridge-controller", },
 204        {},
 205};
 206MODULE_DEVICE_TABLE(of, altera_freeze_br_of_match);
 207#endif
 208
 209static int altera_freeze_br_probe(struct platform_device *pdev)
 210{
 211        struct device *dev = &pdev->dev;
 212        struct device_node *np = pdev->dev.of_node;
 213        void __iomem *base_addr;
 214        struct altera_freeze_br_data *priv;
 215        struct fpga_bridge *br;
 216        struct resource *res;
 217        u32 status, revision;
 218
 219        if (!np)
 220                return -ENODEV;
 221
 222        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 223        base_addr = devm_ioremap_resource(dev, res);
 224        if (IS_ERR(base_addr))
 225                return PTR_ERR(base_addr);
 226
 227        revision = readl(base_addr + FREEZE_CSR_REG_VERSION);
 228        if ((revision != FREEZE_CSR_SUPPORTED_VERSION) &&
 229            (revision != FREEZE_CSR_OFFICIAL_VERSION)) {
 230                dev_err(dev,
 231                        "%s unexpected revision 0x%x != 0x%x != 0x%x\n",
 232                        __func__, revision, FREEZE_CSR_SUPPORTED_VERSION,
 233                        FREEZE_CSR_OFFICIAL_VERSION);
 234                return -EINVAL;
 235        }
 236
 237        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 238        if (!priv)
 239                return -ENOMEM;
 240
 241        priv->dev = dev;
 242
 243        status = readl(base_addr + FREEZE_CSR_STATUS_OFFSET);
 244        if (status & FREEZE_CSR_STATUS_UNFREEZE_REQ_DONE)
 245                priv->enable = 1;
 246
 247        priv->base_addr = base_addr;
 248
 249        br = fpga_bridge_register(dev, FREEZE_BRIDGE_NAME,
 250                                  &altera_freeze_br_br_ops, priv);
 251        if (IS_ERR(br))
 252                return PTR_ERR(br);
 253
 254        platform_set_drvdata(pdev, br);
 255
 256        return 0;
 257}
 258
 259static int altera_freeze_br_remove(struct platform_device *pdev)
 260{
 261        struct fpga_bridge *br = platform_get_drvdata(pdev);
 262
 263        fpga_bridge_unregister(br);
 264
 265        return 0;
 266}
 267
 268static struct platform_driver altera_freeze_br_driver = {
 269        .probe = altera_freeze_br_probe,
 270        .remove = altera_freeze_br_remove,
 271        .driver = {
 272                .name   = "altera_freeze_br",
 273                .of_match_table = of_match_ptr(altera_freeze_br_of_match),
 274        },
 275};
 276
 277module_platform_driver(altera_freeze_br_driver);
 278
 279MODULE_DESCRIPTION("Altera Freeze Bridge");
 280MODULE_AUTHOR("Alan Tull <atull@opensource.altera.com>");
 281MODULE_LICENSE("GPL v2");
 282