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 204 res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0); 205 if (!res) 206 return -ENODEV; 207 208 base = devm_ioremap(dev, res->start, resource_size(res)); 209 if (!base) 210 return -ENOMEM; 211 212 pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL); 213 if (!pti) 214 return -ENOMEM; 215 216 pti->thdev = thdev; 217 pti->base = base; 218 219 read_hw_config(pti); 220 221 dev_set_drvdata(dev, pti); 222 223 return 0; 224 } 225 226 static void intel_th_pti_remove(struct intel_th_device *thdev) 227 { 228 } 229 230 static struct intel_th_driver intel_th_pti_driver = { 231 .probe = intel_th_pti_probe, 232 .remove = intel_th_pti_remove, 233 .activate = intel_th_pti_activate, 234 .deactivate = intel_th_pti_deactivate, 235 .attr_group = &pti_output_group, 236 .driver = { 237 .name = "pti", 238 .owner = THIS_MODULE, 239 }, 240 }; 241 242 module_driver(intel_th_pti_driver, 243 intel_th_driver_register, 244 intel_th_driver_unregister); 245 246 MODULE_LICENSE("GPL v2"); 247 MODULE_DESCRIPTION("Intel(R) Trace Hub PTI output driver"); 248 MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>"); 249