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