Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

intel_th: Add PTI output driver

Parallel Trace Interface (PTI) unit is a trace output device that sends
data over a PTI port.

The driver provides interfaces to configure bus width, bus clock divider
and mode. Tracing is enabled via output device's "active" attribute.

Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Alexander Shishkin and committed by
Greg Kroah-Hartman
14cdbf04 ba82664c

+317
+24
Documentation/ABI/testing/sysfs-bus-intel_th-devices-pti
··· 1 + What: /sys/bus/intel_th/devices/<intel_th_id>-pti/mode 2 + Date: June 2015 3 + KernelVersion: 4.3 4 + Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com> 5 + Description: (RW) Configure PTI output width. Currently supported values 6 + are 4, 8, 12, 16. 7 + 8 + What: /sys/bus/intel_th/devices/<intel_th_id>-pti/freerunning_clock 9 + Date: June 2015 10 + KernelVersion: 4.3 11 + Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com> 12 + Description: (RW) 0: PTI trace clock acts as a strobe which only toggles 13 + when there is trace data to send. 1: PTI trace clock is a 14 + free-running clock. 15 + 16 + What: /sys/bus/intel_th/devices/<intel_th_id>-pti/clock_divider 17 + Date: June 2015 18 + KernelVersion: 4.3 19 + Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com> 20 + Description: (RW) Configure PTI port clock divider: 21 + - 0: Intel TH clock rate, 22 + - 1: 1/2 Intel TH clock rate, 23 + - 2: 1/4 Intel TH clock rate, 24 + - 3: 1/8 Intel TH clock rate.
+9
drivers/hwtracing/intel_th/Kconfig
··· 54 54 55 55 Say Y here to enable MSU output device for Intel TH. 56 56 57 + config INTEL_TH_PTI 58 + tristate "Intel(R) Trace Hub PTI output" 59 + help 60 + Parallel Trace Interface unit (PTI) is a trace output device 61 + of Intel TH architecture that facilitates STP trace output via 62 + a PTI port. 63 + 64 + Say Y to enable PTI output of Intel TH data. 65 + 57 66 config INTEL_TH_DEBUG 58 67 bool "Intel(R) Trace Hub debugging" 59 68 depends on DEBUG_FS
+3
drivers/hwtracing/intel_th/Makefile
··· 13 13 14 14 obj-$(CONFIG_INTEL_TH_MSU) += intel_th_msu.o 15 15 intel_th_msu-y := msu.o 16 + 17 + obj-$(CONFIG_INTEL_TH_PTI) += intel_th_pti.o 18 + intel_th_pti-y := pti.o
+252
drivers/hwtracing/intel_th/pti.c
··· 1 + /* 2 + * Intel(R) Trace Hub PTI output driver 3 + * 4 + * Copyright (C) 2014-2015 Intel Corporation. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope it will be useful, but WITHOUT 11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 + * more details. 14 + */ 15 + 16 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 17 + 18 + #include <linux/types.h> 19 + #include <linux/module.h> 20 + #include <linux/device.h> 21 + #include <linux/sizes.h> 22 + #include <linux/printk.h> 23 + #include <linux/slab.h> 24 + #include <linux/mm.h> 25 + #include <linux/io.h> 26 + 27 + #include "intel_th.h" 28 + #include "pti.h" 29 + 30 + struct pti_device { 31 + void __iomem *base; 32 + struct intel_th_device *thdev; 33 + unsigned int mode; 34 + unsigned int freeclk; 35 + unsigned int clkdiv; 36 + unsigned int patgen; 37 + }; 38 + 39 + /* map PTI widths to MODE settings of PTI_CTL register */ 40 + static const unsigned int pti_mode[] = { 41 + 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 42 + }; 43 + 44 + static int pti_width_mode(unsigned int width) 45 + { 46 + int i; 47 + 48 + for (i = 0; i < ARRAY_SIZE(pti_mode); i++) 49 + if (pti_mode[i] == width) 50 + return i; 51 + 52 + return -EINVAL; 53 + } 54 + 55 + static ssize_t mode_show(struct device *dev, struct device_attribute *attr, 56 + char *buf) 57 + { 58 + struct pti_device *pti = dev_get_drvdata(dev); 59 + 60 + return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]); 61 + } 62 + 63 + static ssize_t mode_store(struct device *dev, struct device_attribute *attr, 64 + const char *buf, size_t size) 65 + { 66 + struct pti_device *pti = dev_get_drvdata(dev); 67 + unsigned long val; 68 + int ret; 69 + 70 + ret = kstrtoul(buf, 10, &val); 71 + if (ret) 72 + return ret; 73 + 74 + ret = pti_width_mode(val); 75 + if (ret < 0) 76 + return ret; 77 + 78 + pti->mode = ret; 79 + 80 + return size; 81 + } 82 + 83 + static DEVICE_ATTR_RW(mode); 84 + 85 + static ssize_t 86 + freerunning_clock_show(struct device *dev, struct device_attribute *attr, 87 + char *buf) 88 + { 89 + struct pti_device *pti = dev_get_drvdata(dev); 90 + 91 + return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk); 92 + } 93 + 94 + static ssize_t 95 + freerunning_clock_store(struct device *dev, struct device_attribute *attr, 96 + const char *buf, size_t size) 97 + { 98 + struct pti_device *pti = dev_get_drvdata(dev); 99 + unsigned long val; 100 + int ret; 101 + 102 + ret = kstrtoul(buf, 10, &val); 103 + if (ret) 104 + return ret; 105 + 106 + pti->freeclk = !!val; 107 + 108 + return size; 109 + } 110 + 111 + static DEVICE_ATTR_RW(freerunning_clock); 112 + 113 + static ssize_t 114 + clock_divider_show(struct device *dev, struct device_attribute *attr, 115 + char *buf) 116 + { 117 + struct pti_device *pti = dev_get_drvdata(dev); 118 + 119 + return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv); 120 + } 121 + 122 + static ssize_t 123 + clock_divider_store(struct device *dev, struct device_attribute *attr, 124 + const char *buf, size_t size) 125 + { 126 + struct pti_device *pti = dev_get_drvdata(dev); 127 + unsigned long val; 128 + int ret; 129 + 130 + ret = kstrtoul(buf, 10, &val); 131 + if (ret) 132 + return ret; 133 + 134 + if (!is_power_of_2(val) || val > 8 || !val) 135 + return -EINVAL; 136 + 137 + pti->clkdiv = val; 138 + 139 + return size; 140 + } 141 + 142 + static DEVICE_ATTR_RW(clock_divider); 143 + 144 + static struct attribute *pti_output_attrs[] = { 145 + &dev_attr_mode.attr, 146 + &dev_attr_freerunning_clock.attr, 147 + &dev_attr_clock_divider.attr, 148 + NULL, 149 + }; 150 + 151 + static struct attribute_group pti_output_group = { 152 + .attrs = pti_output_attrs, 153 + }; 154 + 155 + static int intel_th_pti_activate(struct intel_th_device *thdev) 156 + { 157 + struct pti_device *pti = dev_get_drvdata(&thdev->dev); 158 + u32 ctl = PTI_EN; 159 + 160 + if (pti->patgen) 161 + ctl |= pti->patgen << __ffs(PTI_PATGENMODE); 162 + if (pti->freeclk) 163 + ctl |= PTI_FCEN; 164 + ctl |= pti->mode << __ffs(PTI_MODE); 165 + ctl |= pti->clkdiv << __ffs(PTI_CLKDIV); 166 + 167 + iowrite32(ctl, pti->base + REG_PTI_CTL); 168 + 169 + intel_th_trace_enable(thdev); 170 + 171 + return 0; 172 + } 173 + 174 + static void intel_th_pti_deactivate(struct intel_th_device *thdev) 175 + { 176 + struct pti_device *pti = dev_get_drvdata(&thdev->dev); 177 + 178 + intel_th_trace_disable(thdev); 179 + 180 + iowrite32(0, pti->base + REG_PTI_CTL); 181 + } 182 + 183 + static void read_hw_config(struct pti_device *pti) 184 + { 185 + u32 ctl = ioread32(pti->base + REG_PTI_CTL); 186 + 187 + pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE); 188 + pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV); 189 + pti->freeclk = !!(ctl & PTI_FCEN); 190 + 191 + if (!pti_mode[pti->mode]) 192 + pti->mode = pti_width_mode(4); 193 + if (!pti->clkdiv) 194 + pti->clkdiv = 1; 195 + } 196 + 197 + static int intel_th_pti_probe(struct intel_th_device *thdev) 198 + { 199 + struct device *dev = &thdev->dev; 200 + struct resource *res; 201 + struct pti_device *pti; 202 + void __iomem *base; 203 + int ret; 204 + 205 + res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 206 + if (!res) 207 + return -ENODEV; 208 + 209 + base = devm_ioremap(dev, res->start, resource_size(res)); 210 + if (IS_ERR(base)) 211 + return PTR_ERR(base); 212 + 213 + pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); 214 + if (!pti) 215 + return -ENOMEM; 216 + 217 + pti->thdev = thdev; 218 + pti->base = base; 219 + 220 + read_hw_config(pti); 221 + 222 + ret = sysfs_create_group(&dev->kobj, &pti_output_group); 223 + if (ret) 224 + return ret; 225 + 226 + dev_set_drvdata(dev, pti); 227 + 228 + return 0; 229 + } 230 + 231 + static void intel_th_pti_remove(struct intel_th_device *thdev) 232 + { 233 + } 234 + 235 + static struct intel_th_driver intel_th_pti_driver = { 236 + .probe = intel_th_pti_probe, 237 + .remove = intel_th_pti_remove, 238 + .activate = intel_th_pti_activate, 239 + .deactivate = intel_th_pti_deactivate, 240 + .driver = { 241 + .name = "pti", 242 + .owner = THIS_MODULE, 243 + }, 244 + }; 245 + 246 + module_driver(intel_th_pti_driver, 247 + intel_th_driver_register, 248 + intel_th_driver_unregister); 249 + 250 + MODULE_LICENSE("GPL v2"); 251 + MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); 252 + MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
+29
drivers/hwtracing/intel_th/pti.h
··· 1 + /* 2 + * Intel(R) Trace Hub PTI output data structures 3 + * 4 + * Copyright (C) 2014-2015 Intel Corporation. 5 + * 6 + * This program is free software; you can redistribute it and/or modify it 7 + * under the terms and conditions of the GNU General Public License, 8 + * version 2, as published by the Free Software Foundation. 9 + * 10 + * This program is distributed in the hope it will be useful, but WITHOUT 11 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13 + * more details. 14 + */ 15 + 16 + #ifndef __INTEL_TH_STH_H__ 17 + #define __INTEL_TH_STH_H__ 18 + 19 + enum { 20 + REG_PTI_CTL = 0x1c00, 21 + }; 22 + 23 + #define PTI_EN BIT(0) 24 + #define PTI_FCEN BIT(1) 25 + #define PTI_MODE 0xf0 26 + #define PTI_CLKDIV 0x000f0000 27 + #define PTI_PATGENMODE 0x00f00000 28 + 29 + #endif /* __INTEL_TH_STH_H__ */