linux/drivers/hwtracing/intel_th/pti.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Intel(R) Trace Hub PTI output driver
   4 *
   5 * Copyright (C) 2014-2016 Intel Corporation.
   6 */
   7
   8#define pr_fmt(fmt)     KBUILD_MODNAME ": " fmt
   9
  10#include <linux/types.h>
  11#include <linux/module.h>
  12#include <linux/device.h>
  13#include <linux/sizes.h>
  14#include <linux/printk.h>
  15#include <linux/slab.h>
  16#include <linux/mm.h>
  17#include <linux/io.h>
  18
  19#include "intel_th.h"
  20#include "pti.h"
  21
  22struct pti_device {
  23        void __iomem            *base;
  24        struct intel_th_device  *thdev;
  25        unsigned int            mode;
  26        unsigned int            freeclk;
  27        unsigned int            clkdiv;
  28        unsigned int            patgen;
  29        unsigned int            lpp_dest_mask;
  30        unsigned int            lpp_dest;
  31};
  32
  33/* map PTI widths to MODE settings of PTI_CTL register */
  34static const unsigned int pti_mode[] = {
  35        0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
  36};
  37
  38static int pti_width_mode(unsigned int width)
  39{
  40        int i;
  41
  42        for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
  43                if (pti_mode[i] == width)
  44                        return i;
  45
  46        return -EINVAL;
  47}
  48
  49static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
  50                         char *buf)
  51{
  52        struct pti_device *pti = dev_get_drvdata(dev);
  53
  54        return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
  55}
  56
  57static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
  58                          const char *buf, size_t size)
  59{
  60        struct pti_device *pti = dev_get_drvdata(dev);
  61        unsigned long val;
  62        int ret;
  63
  64        ret = kstrtoul(buf, 10, &val);
  65        if (ret)
  66                return ret;
  67
  68        ret = pti_width_mode(val);
  69        if (ret < 0)
  70                return ret;
  71
  72        pti->mode = ret;
  73
  74        return size;
  75}
  76
  77static DEVICE_ATTR_RW(mode);
  78
  79static ssize_t
  80freerunning_clock_show(struct device *dev, struct device_attribute *attr,
  81                       char *buf)
  82{
  83        struct pti_device *pti = dev_get_drvdata(dev);
  84
  85        return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
  86}
  87
  88static ssize_t
  89freerunning_clock_store(struct device *dev, struct device_attribute *attr,
  90                        const char *buf, size_t size)
  91{
  92        struct pti_device *pti = dev_get_drvdata(dev);
  93        unsigned long val;
  94        int ret;
  95
  96        ret = kstrtoul(buf, 10, &val);
  97        if (ret)
  98                return ret;
  99
 100        pti->freeclk = !!val;
 101
 102        return size;
 103}
 104
 105static DEVICE_ATTR_RW(freerunning_clock);
 106
 107static ssize_t
 108clock_divider_show(struct device *dev, struct device_attribute *attr,
 109                   char *buf)
 110{
 111        struct pti_device *pti = dev_get_drvdata(dev);
 112
 113        return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
 114}
 115
 116static ssize_t
 117clock_divider_store(struct device *dev, struct device_attribute *attr,
 118                    const char *buf, size_t size)
 119{
 120        struct pti_device *pti = dev_get_drvdata(dev);
 121        unsigned long val;
 122        int ret;
 123
 124        ret = kstrtoul(buf, 10, &val);
 125        if (ret)
 126                return ret;
 127
 128        if (!is_power_of_2(val) || val > 8 || !val)
 129                return -EINVAL;
 130
 131        pti->clkdiv = val;
 132
 133        return size;
 134}
 135
 136static DEVICE_ATTR_RW(clock_divider);
 137
 138static struct attribute *pti_output_attrs[] = {
 139        &dev_attr_mode.attr,
 140        &dev_attr_freerunning_clock.attr,
 141        &dev_attr_clock_divider.attr,
 142        NULL,
 143};
 144
 145static struct attribute_group pti_output_group = {
 146        .attrs  = pti_output_attrs,
 147};
 148
 149static int intel_th_pti_activate(struct intel_th_device *thdev)
 150{
 151        struct pti_device *pti = dev_get_drvdata(&thdev->dev);
 152        u32 ctl = PTI_EN;
 153
 154        if (pti->patgen)
 155                ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
 156        if (pti->freeclk)
 157                ctl |= PTI_FCEN;
 158        ctl |= pti->mode << __ffs(PTI_MODE);
 159        ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
 160        ctl |= pti->lpp_dest << __ffs(LPP_DEST);
 161
 162        iowrite32(ctl, pti->base + REG_PTI_CTL);
 163
 164        intel_th_trace_enable(thdev);
 165
 166        return 0;
 167}
 168
 169static void intel_th_pti_deactivate(struct intel_th_device *thdev)
 170{
 171        struct pti_device *pti = dev_get_drvdata(&thdev->dev);
 172
 173        intel_th_trace_disable(thdev);
 174
 175        iowrite32(0, pti->base + REG_PTI_CTL);
 176}
 177
 178static void read_hw_config(struct pti_device *pti)
 179{
 180        u32 ctl = ioread32(pti->base + REG_PTI_CTL);
 181
 182        pti->mode       = (ctl & PTI_MODE) >> __ffs(PTI_MODE);
 183        pti->clkdiv     = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
 184        pti->freeclk    = !!(ctl & PTI_FCEN);
 185
 186        if (!pti_mode[pti->mode])
 187                pti->mode = pti_width_mode(4);
 188        if (!pti->clkdiv)
 189                pti->clkdiv = 1;
 190
 191        if (pti->thdev->output.type == GTH_LPP) {
 192                if (ctl & LPP_PTIPRESENT)
 193                        pti->lpp_dest_mask |= LPP_DEST_PTI;
 194                if (ctl & LPP_BSSBPRESENT)
 195                        pti->lpp_dest_mask |= LPP_DEST_EXI;
 196                if (ctl & LPP_DEST)
 197                        pti->lpp_dest = 1;
 198        }
 199}
 200
 201static int intel_th_pti_probe(struct intel_th_device *thdev)
 202{
 203        struct device *dev = &thdev->dev;
 204        struct resource *res;
 205        struct pti_device *pti;
 206        void __iomem *base;
 207
 208        res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
 209        if (!res)
 210                return -ENODEV;
 211
 212        base = devm_ioremap(dev, res->start, resource_size(res));
 213        if (!base)
 214                return -ENOMEM;
 215
 216        pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
 217        if (!pti)
 218                return -ENOMEM;
 219
 220        pti->thdev = thdev;
 221        pti->base = base;
 222
 223        read_hw_config(pti);
 224
 225        dev_set_drvdata(dev, pti);
 226
 227        return 0;
 228}
 229
 230static void intel_th_pti_remove(struct intel_th_device *thdev)
 231{
 232}
 233
 234static struct intel_th_driver intel_th_pti_driver = {
 235        .probe  = intel_th_pti_probe,
 236        .remove = intel_th_pti_remove,
 237        .activate       = intel_th_pti_activate,
 238        .deactivate     = intel_th_pti_deactivate,
 239        .attr_group     = &pti_output_group,
 240        .driver = {
 241                .name   = "pti",
 242                .owner  = THIS_MODULE,
 243        },
 244};
 245
 246static const char * const lpp_dest_str[] = { "pti", "exi" };
 247
 248static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
 249                             char *buf)
 250{
 251        struct pti_device *pti = dev_get_drvdata(dev);
 252        ssize_t ret = 0;
 253        int i;
 254
 255        for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
 256                const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
 257
 258                if (!(pti->lpp_dest_mask & BIT(i)))
 259                        continue;
 260
 261                ret += scnprintf(buf + ret, PAGE_SIZE - ret,
 262                                 fmt, lpp_dest_str[i]);
 263        }
 264
 265        if (ret)
 266                buf[ret - 1] = '\n';
 267
 268        return ret;
 269}
 270
 271static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
 272                              const char *buf, size_t size)
 273{
 274        struct pti_device *pti = dev_get_drvdata(dev);
 275        int i;
 276
 277        i = sysfs_match_string(lpp_dest_str, buf);
 278        if (i < 0)
 279                return i;
 280
 281        if (!(pti->lpp_dest_mask & BIT(i)))
 282                return -EINVAL;
 283
 284        pti->lpp_dest = i;
 285        return size;
 286}
 287
 288static DEVICE_ATTR_RW(lpp_dest);
 289
 290static struct attribute *lpp_output_attrs[] = {
 291        &dev_attr_mode.attr,
 292        &dev_attr_freerunning_clock.attr,
 293        &dev_attr_clock_divider.attr,
 294        &dev_attr_lpp_dest.attr,
 295        NULL,
 296};
 297
 298static struct attribute_group lpp_output_group = {
 299        .attrs  = lpp_output_attrs,
 300};
 301
 302static struct intel_th_driver intel_th_lpp_driver = {
 303        .probe          = intel_th_pti_probe,
 304        .remove         = intel_th_pti_remove,
 305        .activate       = intel_th_pti_activate,
 306        .deactivate     = intel_th_pti_deactivate,
 307        .attr_group     = &lpp_output_group,
 308        .driver = {
 309                .name   = "lpp",
 310                .owner  = THIS_MODULE,
 311        },
 312};
 313
 314static int __init intel_th_pti_lpp_init(void)
 315{
 316        int err;
 317
 318        err = intel_th_driver_register(&intel_th_pti_driver);
 319        if (err)
 320                return err;
 321
 322        err = intel_th_driver_register(&intel_th_lpp_driver);
 323        if (err) {
 324                intel_th_driver_unregister(&intel_th_pti_driver);
 325                return err;
 326        }
 327
 328        return 0;
 329}
 330
 331module_init(intel_th_pti_lpp_init);
 332
 333static void __exit intel_th_pti_lpp_exit(void)
 334{
 335        intel_th_driver_unregister(&intel_th_pti_driver);
 336        intel_th_driver_unregister(&intel_th_lpp_driver);
 337}
 338
 339module_exit(intel_th_pti_lpp_exit);
 340
 341MODULE_LICENSE("GPL v2");
 342MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");
 343MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
 344