]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/fpga/dfl-afu-main.c
Merge branch 'work.mount3' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[linux.git] / drivers / fpga / dfl-afu-main.c
index 02baa6a227c0c3317039d7d5ad3203188c7f7e13..e4a34dc7947f92fda2d77d04a859b801198b14e6 100644 (file)
 #include "dfl-afu.h"
 
 /**
- * port_enable - enable a port
+ * __afu_port_enable - enable a port by clear reset
  * @pdev: port platform device.
  *
  * Enable Port by clear the port soft reset bit, which is set by default.
  * The AFU is unable to respond to any MMIO access while in reset.
- * port_enable function should only be used after port_disable function.
+ * __afu_port_enable function should only be used after __afu_port_disable
+ * function.
+ *
+ * The caller needs to hold lock for protection.
  */
-static void port_enable(struct platform_device *pdev)
+void __afu_port_enable(struct platform_device *pdev)
 {
        struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
        void __iomem *base;
@@ -52,13 +55,14 @@ static void port_enable(struct platform_device *pdev)
 #define RST_POLL_TIMEOUT 1000 /* us */
 
 /**
- * port_disable - disable a port
+ * __afu_port_disable - disable a port by hold reset
  * @pdev: port platform device.
  *
- * Disable Port by setting the port soft reset bit, it puts the port into
- * reset.
+ * Disable Port by setting the port soft reset bit, it puts the port into reset.
+ *
+ * The caller needs to hold lock for protection.
  */
-static int port_disable(struct platform_device *pdev)
+int __afu_port_disable(struct platform_device *pdev)
 {
        struct dfl_feature_platform_data *pdata = dev_get_platdata(&pdev->dev);
        void __iomem *base;
@@ -104,9 +108,9 @@ static int __port_reset(struct platform_device *pdev)
 {
        int ret;
 
-       ret = port_disable(pdev);
+       ret = __afu_port_disable(pdev);
        if (!ret)
-               port_enable(pdev);
+               __afu_port_enable(pdev);
 
        return ret;
 }
@@ -141,27 +145,267 @@ id_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR_RO(id);
 
-static const struct attribute *port_hdr_attrs[] = {
+static ssize_t
+ltr_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       u64 v;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       v = readq(base + PORT_HDR_CTRL);
+       mutex_unlock(&pdata->lock);
+
+       return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_CTRL_LATENCY, v));
+}
+
+static ssize_t
+ltr_store(struct device *dev, struct device_attribute *attr,
+         const char *buf, size_t count)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       bool ltr;
+       u64 v;
+
+       if (kstrtobool(buf, &ltr))
+               return -EINVAL;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       v = readq(base + PORT_HDR_CTRL);
+       v &= ~PORT_CTRL_LATENCY;
+       v |= FIELD_PREP(PORT_CTRL_LATENCY, ltr ? 1 : 0);
+       writeq(v, base + PORT_HDR_CTRL);
+       mutex_unlock(&pdata->lock);
+
+       return count;
+}
+static DEVICE_ATTR_RW(ltr);
+
+static ssize_t
+ap1_event_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       u64 v;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       v = readq(base + PORT_HDR_STS);
+       mutex_unlock(&pdata->lock);
+
+       return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP1_EVT, v));
+}
+
+static ssize_t
+ap1_event_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       bool clear;
+
+       if (kstrtobool(buf, &clear) || !clear)
+               return -EINVAL;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       writeq(PORT_STS_AP1_EVT, base + PORT_HDR_STS);
+       mutex_unlock(&pdata->lock);
+
+       return count;
+}
+static DEVICE_ATTR_RW(ap1_event);
+
+static ssize_t
+ap2_event_show(struct device *dev, struct device_attribute *attr,
+              char *buf)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       u64 v;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       v = readq(base + PORT_HDR_STS);
+       mutex_unlock(&pdata->lock);
+
+       return sprintf(buf, "%x\n", (u8)FIELD_GET(PORT_STS_AP2_EVT, v));
+}
+
+static ssize_t
+ap2_event_store(struct device *dev, struct device_attribute *attr,
+               const char *buf, size_t count)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       bool clear;
+
+       if (kstrtobool(buf, &clear) || !clear)
+               return -EINVAL;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       writeq(PORT_STS_AP2_EVT, base + PORT_HDR_STS);
+       mutex_unlock(&pdata->lock);
+
+       return count;
+}
+static DEVICE_ATTR_RW(ap2_event);
+
+static ssize_t
+power_state_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       void __iomem *base;
+       u64 v;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       v = readq(base + PORT_HDR_STS);
+       mutex_unlock(&pdata->lock);
+
+       return sprintf(buf, "0x%x\n", (u8)FIELD_GET(PORT_STS_PWR_STATE, v));
+}
+static DEVICE_ATTR_RO(power_state);
+
+static ssize_t
+userclk_freqcmd_store(struct device *dev, struct device_attribute *attr,
+                     const char *buf, size_t count)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       u64 userclk_freq_cmd;
+       void __iomem *base;
+
+       if (kstrtou64(buf, 0, &userclk_freq_cmd))
+               return -EINVAL;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       writeq(userclk_freq_cmd, base + PORT_HDR_USRCLK_CMD0);
+       mutex_unlock(&pdata->lock);
+
+       return count;
+}
+static DEVICE_ATTR_WO(userclk_freqcmd);
+
+static ssize_t
+userclk_freqcntrcmd_store(struct device *dev, struct device_attribute *attr,
+                         const char *buf, size_t count)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       u64 userclk_freqcntr_cmd;
+       void __iomem *base;
+
+       if (kstrtou64(buf, 0, &userclk_freqcntr_cmd))
+               return -EINVAL;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       writeq(userclk_freqcntr_cmd, base + PORT_HDR_USRCLK_CMD1);
+       mutex_unlock(&pdata->lock);
+
+       return count;
+}
+static DEVICE_ATTR_WO(userclk_freqcntrcmd);
+
+static ssize_t
+userclk_freqsts_show(struct device *dev, struct device_attribute *attr,
+                    char *buf)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       u64 userclk_freqsts;
+       void __iomem *base;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       userclk_freqsts = readq(base + PORT_HDR_USRCLK_STS0);
+       mutex_unlock(&pdata->lock);
+
+       return sprintf(buf, "0x%llx\n", (unsigned long long)userclk_freqsts);
+}
+static DEVICE_ATTR_RO(userclk_freqsts);
+
+static ssize_t
+userclk_freqcntrsts_show(struct device *dev, struct device_attribute *attr,
+                        char *buf)
+{
+       struct dfl_feature_platform_data *pdata = dev_get_platdata(dev);
+       u64 userclk_freqcntrsts;
+       void __iomem *base;
+
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       mutex_lock(&pdata->lock);
+       userclk_freqcntrsts = readq(base + PORT_HDR_USRCLK_STS1);
+       mutex_unlock(&pdata->lock);
+
+       return sprintf(buf, "0x%llx\n",
+                      (unsigned long long)userclk_freqcntrsts);
+}
+static DEVICE_ATTR_RO(userclk_freqcntrsts);
+
+static struct attribute *port_hdr_attrs[] = {
        &dev_attr_id.attr,
+       &dev_attr_ltr.attr,
+       &dev_attr_ap1_event.attr,
+       &dev_attr_ap2_event.attr,
+       &dev_attr_power_state.attr,
+       &dev_attr_userclk_freqcmd.attr,
+       &dev_attr_userclk_freqcntrcmd.attr,
+       &dev_attr_userclk_freqsts.attr,
+       &dev_attr_userclk_freqcntrsts.attr,
        NULL,
 };
 
-static int port_hdr_init(struct platform_device *pdev,
-                        struct dfl_feature *feature)
+static umode_t port_hdr_attrs_visible(struct kobject *kobj,
+                                     struct attribute *attr, int n)
 {
-       dev_dbg(&pdev->dev, "PORT HDR Init.\n");
+       struct device *dev = kobj_to_dev(kobj);
+       umode_t mode = attr->mode;
+       void __iomem *base;
 
-       port_reset(pdev);
+       base = dfl_get_feature_ioaddr_by_id(dev, PORT_FEATURE_ID_HEADER);
+
+       if (dfl_feature_revision(base) > 0) {
+               /*
+                * userclk sysfs interfaces are only visible in case port
+                * revision is 0, as hardware with revision >0 doesn't
+                * support this.
+                */
+               if (attr == &dev_attr_userclk_freqcmd.attr ||
+                   attr == &dev_attr_userclk_freqcntrcmd.attr ||
+                   attr == &dev_attr_userclk_freqsts.attr ||
+                   attr == &dev_attr_userclk_freqcntrsts.attr)
+                       mode = 0;
+       }
 
-       return sysfs_create_files(&pdev->dev.kobj, port_hdr_attrs);
+       return mode;
 }
 
-static void port_hdr_uinit(struct platform_device *pdev,
-                          struct dfl_feature *feature)
+static const struct attribute_group port_hdr_group = {
+       .attrs      = port_hdr_attrs,
+       .is_visible = port_hdr_attrs_visible,
+};
+
+static int port_hdr_init(struct platform_device *pdev,
+                        struct dfl_feature *feature)
 {
-       dev_dbg(&pdev->dev, "PORT HDR UInit.\n");
+       port_reset(pdev);
 
-       sysfs_remove_files(&pdev->dev.kobj, port_hdr_attrs);
+       return 0;
 }
 
 static long
@@ -185,9 +429,13 @@ port_hdr_ioctl(struct platform_device *pdev, struct dfl_feature *feature,
        return ret;
 }
 
+static const struct dfl_feature_id port_hdr_id_table[] = {
+       {.id = PORT_FEATURE_ID_HEADER,},
+       {0,}
+};
+
 static const struct dfl_feature_ops port_hdr_ops = {
        .init = port_hdr_init,
-       .uinit = port_hdr_uinit,
        .ioctl = port_hdr_ioctl,
 };
 
@@ -214,51 +462,90 @@ afu_id_show(struct device *dev, struct device_attribute *attr, char *buf)
 }
 static DEVICE_ATTR_RO(afu_id);
 
-static const struct attribute *port_afu_attrs[] = {
+static struct attribute *port_afu_attrs[] = {
        &dev_attr_afu_id.attr,
        NULL
 };
 
+static umode_t port_afu_attrs_visible(struct kobject *kobj,
+                                     struct attribute *attr, int n)
+{
+       struct device *dev = kobj_to_dev(kobj);
+
+       /*
+        * sysfs entries are visible only if related private feature is
+        * enumerated.
+        */
+       if (!dfl_get_feature_by_id(dev, PORT_FEATURE_ID_AFU))
+               return 0;
+
+       return attr->mode;
+}
+
+static const struct attribute_group port_afu_group = {
+       .attrs      = port_afu_attrs,
+       .is_visible = port_afu_attrs_visible,
+};
+
 static int port_afu_init(struct platform_device *pdev,
                         struct dfl_feature *feature)
 {
        struct resource *res = &pdev->resource[feature->resource_index];
-       int ret;
 
-       dev_dbg(&pdev->dev, "PORT AFU Init.\n");
+       return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
+                                  DFL_PORT_REGION_INDEX_AFU,
+                                  resource_size(res), res->start,
+                                  DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
+                                  DFL_PORT_REGION_WRITE);
+}
 
-       ret = afu_mmio_region_add(dev_get_platdata(&pdev->dev),
-                                 DFL_PORT_REGION_INDEX_AFU, resource_size(res),
-                                 res->start, DFL_PORT_REGION_READ |
-                                 DFL_PORT_REGION_WRITE | DFL_PORT_REGION_MMAP);
-       if (ret)
-               return ret;
+static const struct dfl_feature_id port_afu_id_table[] = {
+       {.id = PORT_FEATURE_ID_AFU,},
+       {0,}
+};
 
-       return sysfs_create_files(&pdev->dev.kobj, port_afu_attrs);
-}
+static const struct dfl_feature_ops port_afu_ops = {
+       .init = port_afu_init,
+};
 
-static void port_afu_uinit(struct platform_device *pdev,
-                          struct dfl_feature *feature)
+static int port_stp_init(struct platform_device *pdev,
+                        struct dfl_feature *feature)
 {
-       dev_dbg(&pdev->dev, "PORT AFU UInit.\n");
+       struct resource *res = &pdev->resource[feature->resource_index];
 
-       sysfs_remove_files(&pdev->dev.kobj, port_afu_attrs);
+       return afu_mmio_region_add(dev_get_platdata(&pdev->dev),
+                                  DFL_PORT_REGION_INDEX_STP,
+                                  resource_size(res), res->start,
+                                  DFL_PORT_REGION_MMAP | DFL_PORT_REGION_READ |
+                                  DFL_PORT_REGION_WRITE);
 }
 
-static const struct dfl_feature_ops port_afu_ops = {
-       .init = port_afu_init,
-       .uinit = port_afu_uinit,
+static const struct dfl_feature_id port_stp_id_table[] = {
+       {.id = PORT_FEATURE_ID_STP,},
+       {0,}
+};
+
+static const struct dfl_feature_ops port_stp_ops = {
+       .init = port_stp_init,
 };
 
 static struct dfl_feature_driver port_feature_drvs[] = {
        {
-               .id = PORT_FEATURE_ID_HEADER,
+               .id_table = port_hdr_id_table,
                .ops = &port_hdr_ops,
        },
        {
-               .id = PORT_FEATURE_ID_AFU,
+               .id_table = port_afu_id_table,
                .ops = &port_afu_ops,
        },
+       {
+               .id_table = port_err_id_table,
+               .ops = &port_err_ops,
+       },
+       {
+               .id_table = port_stp_id_table,
+               .ops = &port_stp_ops,
+       },
        {
                .ops = NULL,
        }
@@ -545,9 +832,9 @@ static int port_enable_set(struct platform_device *pdev, bool enable)
 
        mutex_lock(&pdata->lock);
        if (enable)
-               port_enable(pdev);
+               __afu_port_enable(pdev);
        else
-               ret = port_disable(pdev);
+               ret = __afu_port_disable(pdev);
        mutex_unlock(&pdata->lock);
 
        return ret;
@@ -599,9 +886,17 @@ static int afu_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct attribute_group *afu_dev_groups[] = {
+       &port_hdr_group,
+       &port_afu_group,
+       &port_err_group,
+       NULL
+};
+
 static struct platform_driver afu_driver = {
        .driver = {
-               .name    = DFL_FPGA_FEATURE_DEV_PORT,
+               .name       = DFL_FPGA_FEATURE_DEV_PORT,
+               .dev_groups = afu_dev_groups,
        },
        .probe   = afu_probe,
        .remove  = afu_remove,