2 * Intel(R) Trace Hub PTI output driver
4 * Copyright (C) 2014-2016 Intel Corporation.
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.
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
16 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
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>
32 struct intel_th_device *thdev;
37 unsigned int lpp_dest_mask;
38 unsigned int lpp_dest;
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,
46 static int pti_width_mode(unsigned int width)
50 for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
51 if (pti_mode[i] == width)
57 static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
60 struct pti_device *pti = dev_get_drvdata(dev);
62 return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
65 static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
66 const char *buf, size_t size)
68 struct pti_device *pti = dev_get_drvdata(dev);
72 ret = kstrtoul(buf, 10, &val);
76 ret = pti_width_mode(val);
85 static DEVICE_ATTR_RW(mode);
88 freerunning_clock_show(struct device *dev, struct device_attribute *attr,
91 struct pti_device *pti = dev_get_drvdata(dev);
93 return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
97 freerunning_clock_store(struct device *dev, struct device_attribute *attr,
98 const char *buf, size_t size)
100 struct pti_device *pti = dev_get_drvdata(dev);
104 ret = kstrtoul(buf, 10, &val);
108 pti->freeclk = !!val;
113 static DEVICE_ATTR_RW(freerunning_clock);
116 clock_divider_show(struct device *dev, struct device_attribute *attr,
119 struct pti_device *pti = dev_get_drvdata(dev);
121 return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
125 clock_divider_store(struct device *dev, struct device_attribute *attr,
126 const char *buf, size_t size)
128 struct pti_device *pti = dev_get_drvdata(dev);
132 ret = kstrtoul(buf, 10, &val);
136 if (!is_power_of_2(val) || val > 8 || !val)
144 static DEVICE_ATTR_RW(clock_divider);
146 static struct attribute *pti_output_attrs[] = {
148 &dev_attr_freerunning_clock.attr,
149 &dev_attr_clock_divider.attr,
153 static struct attribute_group pti_output_group = {
154 .attrs = pti_output_attrs,
157 static int intel_th_pti_activate(struct intel_th_device *thdev)
159 struct pti_device *pti = dev_get_drvdata(&thdev->dev);
163 ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
166 ctl |= pti->mode << __ffs(PTI_MODE);
167 ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
168 ctl |= pti->lpp_dest << __ffs(LPP_DEST);
170 iowrite32(ctl, pti->base + REG_PTI_CTL);
172 intel_th_trace_enable(thdev);
177 static void intel_th_pti_deactivate(struct intel_th_device *thdev)
179 struct pti_device *pti = dev_get_drvdata(&thdev->dev);
181 intel_th_trace_disable(thdev);
183 iowrite32(0, pti->base + REG_PTI_CTL);
186 static void read_hw_config(struct pti_device *pti)
188 u32 ctl = ioread32(pti->base + REG_PTI_CTL);
190 pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE);
191 pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
192 pti->freeclk = !!(ctl & PTI_FCEN);
194 if (!pti_mode[pti->mode])
195 pti->mode = pti_width_mode(4);
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;
209 static int intel_th_pti_probe(struct intel_th_device *thdev)
211 struct device *dev = &thdev->dev;
212 struct resource *res;
213 struct pti_device *pti;
216 res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
220 base = devm_ioremap(dev, res->start, resource_size(res));
224 pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
233 dev_set_drvdata(dev, pti);
238 static void intel_th_pti_remove(struct intel_th_device *thdev)
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,
250 .owner = THIS_MODULE,
254 static const char * const lpp_dest_str[] = { "pti", "exi" };
256 static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
259 struct pti_device *pti = dev_get_drvdata(dev);
263 for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
264 const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
266 if (!(pti->lpp_dest_mask & BIT(i)))
269 ret += scnprintf(buf + ret, PAGE_SIZE - ret,
270 fmt, lpp_dest_str[i]);
279 static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
280 const char *buf, size_t size)
282 struct pti_device *pti = dev_get_drvdata(dev);
283 ssize_t ret = -EINVAL;
286 for (i = 0; i < ARRAY_SIZE(lpp_dest_str); i++)
287 if (sysfs_streq(buf, lpp_dest_str[i]))
290 if (i < ARRAY_SIZE(lpp_dest_str) && pti->lpp_dest_mask & BIT(i)) {
298 static DEVICE_ATTR_RW(lpp_dest);
300 static struct attribute *lpp_output_attrs[] = {
302 &dev_attr_freerunning_clock.attr,
303 &dev_attr_clock_divider.attr,
304 &dev_attr_lpp_dest.attr,
308 static struct attribute_group lpp_output_group = {
309 .attrs = lpp_output_attrs,
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,
320 .owner = THIS_MODULE,
324 static int __init intel_th_pti_lpp_init(void)
328 err = intel_th_driver_register(&intel_th_pti_driver);
332 err = intel_th_driver_register(&intel_th_lpp_driver);
334 intel_th_driver_unregister(&intel_th_pti_driver);
341 module_init(intel_th_pti_lpp_init);
343 static void __exit intel_th_pti_lpp_exit(void)
345 intel_th_driver_unregister(&intel_th_pti_driver);
346 intel_th_driver_unregister(&intel_th_lpp_driver);
349 module_exit(intel_th_pti_lpp_exit);
351 MODULE_LICENSE("GPL v2");
352 MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");