pti.c (7224B)
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 const 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 const 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>");