150352fa7SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0
214cdbf04SAlexander Shishkin /*
314cdbf04SAlexander Shishkin * Intel(R) Trace Hub PTI output driver
414cdbf04SAlexander Shishkin *
5f77d22bcSAlexander Shishkin * Copyright (C) 2014-2016 Intel Corporation.
614cdbf04SAlexander Shishkin */
714cdbf04SAlexander Shishkin
814cdbf04SAlexander Shishkin #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
914cdbf04SAlexander Shishkin
1014cdbf04SAlexander Shishkin #include <linux/types.h>
1114cdbf04SAlexander Shishkin #include <linux/module.h>
1214cdbf04SAlexander Shishkin #include <linux/device.h>
1314cdbf04SAlexander Shishkin #include <linux/sizes.h>
1414cdbf04SAlexander Shishkin #include <linux/printk.h>
1514cdbf04SAlexander Shishkin #include <linux/slab.h>
1614cdbf04SAlexander Shishkin #include <linux/mm.h>
1714cdbf04SAlexander Shishkin #include <linux/io.h>
1814cdbf04SAlexander Shishkin
1914cdbf04SAlexander Shishkin #include "intel_th.h"
2014cdbf04SAlexander Shishkin #include "pti.h"
2114cdbf04SAlexander Shishkin
2214cdbf04SAlexander Shishkin struct pti_device {
2314cdbf04SAlexander Shishkin void __iomem *base;
2414cdbf04SAlexander Shishkin struct intel_th_device *thdev;
2514cdbf04SAlexander Shishkin unsigned int mode;
2614cdbf04SAlexander Shishkin unsigned int freeclk;
2714cdbf04SAlexander Shishkin unsigned int clkdiv;
2814cdbf04SAlexander Shishkin unsigned int patgen;
29f77d22bcSAlexander Shishkin unsigned int lpp_dest_mask;
30f77d22bcSAlexander Shishkin unsigned int lpp_dest;
3114cdbf04SAlexander Shishkin };
3214cdbf04SAlexander Shishkin
3314cdbf04SAlexander Shishkin /* map PTI widths to MODE settings of PTI_CTL register */
3414cdbf04SAlexander Shishkin static const unsigned int pti_mode[] = {
3514cdbf04SAlexander Shishkin 0, 4, 8, 0, 12, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0,
3614cdbf04SAlexander Shishkin };
3714cdbf04SAlexander Shishkin
pti_width_mode(unsigned int width)3814cdbf04SAlexander Shishkin static int pti_width_mode(unsigned int width)
3914cdbf04SAlexander Shishkin {
4014cdbf04SAlexander Shishkin int i;
4114cdbf04SAlexander Shishkin
4214cdbf04SAlexander Shishkin for (i = 0; i < ARRAY_SIZE(pti_mode); i++)
4314cdbf04SAlexander Shishkin if (pti_mode[i] == width)
4414cdbf04SAlexander Shishkin return i;
4514cdbf04SAlexander Shishkin
4614cdbf04SAlexander Shishkin return -EINVAL;
4714cdbf04SAlexander Shishkin }
4814cdbf04SAlexander Shishkin
mode_show(struct device * dev,struct device_attribute * attr,char * buf)4914cdbf04SAlexander Shishkin static ssize_t mode_show(struct device *dev, struct device_attribute *attr,
5014cdbf04SAlexander Shishkin char *buf)
5114cdbf04SAlexander Shishkin {
5214cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
5314cdbf04SAlexander Shishkin
5414cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti_mode[pti->mode]);
5514cdbf04SAlexander Shishkin }
5614cdbf04SAlexander Shishkin
mode_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)5714cdbf04SAlexander Shishkin static ssize_t mode_store(struct device *dev, struct device_attribute *attr,
5814cdbf04SAlexander Shishkin const char *buf, size_t size)
5914cdbf04SAlexander Shishkin {
6014cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
6114cdbf04SAlexander Shishkin unsigned long val;
6214cdbf04SAlexander Shishkin int ret;
6314cdbf04SAlexander Shishkin
6414cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val);
6514cdbf04SAlexander Shishkin if (ret)
6614cdbf04SAlexander Shishkin return ret;
6714cdbf04SAlexander Shishkin
6814cdbf04SAlexander Shishkin ret = pti_width_mode(val);
6914cdbf04SAlexander Shishkin if (ret < 0)
7014cdbf04SAlexander Shishkin return ret;
7114cdbf04SAlexander Shishkin
7214cdbf04SAlexander Shishkin pti->mode = ret;
7314cdbf04SAlexander Shishkin
7414cdbf04SAlexander Shishkin return size;
7514cdbf04SAlexander Shishkin }
7614cdbf04SAlexander Shishkin
7714cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(mode);
7814cdbf04SAlexander Shishkin
7914cdbf04SAlexander Shishkin static ssize_t
freerunning_clock_show(struct device * dev,struct device_attribute * attr,char * buf)8014cdbf04SAlexander Shishkin freerunning_clock_show(struct device *dev, struct device_attribute *attr,
8114cdbf04SAlexander Shishkin char *buf)
8214cdbf04SAlexander Shishkin {
8314cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
8414cdbf04SAlexander Shishkin
8514cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", pti->freeclk);
8614cdbf04SAlexander Shishkin }
8714cdbf04SAlexander Shishkin
8814cdbf04SAlexander Shishkin static ssize_t
freerunning_clock_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)8914cdbf04SAlexander Shishkin freerunning_clock_store(struct device *dev, struct device_attribute *attr,
9014cdbf04SAlexander Shishkin const char *buf, size_t size)
9114cdbf04SAlexander Shishkin {
9214cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
9314cdbf04SAlexander Shishkin unsigned long val;
9414cdbf04SAlexander Shishkin int ret;
9514cdbf04SAlexander Shishkin
9614cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val);
9714cdbf04SAlexander Shishkin if (ret)
9814cdbf04SAlexander Shishkin return ret;
9914cdbf04SAlexander Shishkin
10014cdbf04SAlexander Shishkin pti->freeclk = !!val;
10114cdbf04SAlexander Shishkin
10214cdbf04SAlexander Shishkin return size;
10314cdbf04SAlexander Shishkin }
10414cdbf04SAlexander Shishkin
10514cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(freerunning_clock);
10614cdbf04SAlexander Shishkin
10714cdbf04SAlexander Shishkin static ssize_t
clock_divider_show(struct device * dev,struct device_attribute * attr,char * buf)10814cdbf04SAlexander Shishkin clock_divider_show(struct device *dev, struct device_attribute *attr,
10914cdbf04SAlexander Shishkin char *buf)
11014cdbf04SAlexander Shishkin {
11114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
11214cdbf04SAlexander Shishkin
11314cdbf04SAlexander Shishkin return scnprintf(buf, PAGE_SIZE, "%d\n", 1u << pti->clkdiv);
11414cdbf04SAlexander Shishkin }
11514cdbf04SAlexander Shishkin
11614cdbf04SAlexander Shishkin static ssize_t
clock_divider_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)11714cdbf04SAlexander Shishkin clock_divider_store(struct device *dev, struct device_attribute *attr,
11814cdbf04SAlexander Shishkin const char *buf, size_t size)
11914cdbf04SAlexander Shishkin {
12014cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
12114cdbf04SAlexander Shishkin unsigned long val;
12214cdbf04SAlexander Shishkin int ret;
12314cdbf04SAlexander Shishkin
12414cdbf04SAlexander Shishkin ret = kstrtoul(buf, 10, &val);
12514cdbf04SAlexander Shishkin if (ret)
12614cdbf04SAlexander Shishkin return ret;
12714cdbf04SAlexander Shishkin
12814cdbf04SAlexander Shishkin if (!is_power_of_2(val) || val > 8 || !val)
12914cdbf04SAlexander Shishkin return -EINVAL;
13014cdbf04SAlexander Shishkin
13114cdbf04SAlexander Shishkin pti->clkdiv = val;
13214cdbf04SAlexander Shishkin
13314cdbf04SAlexander Shishkin return size;
13414cdbf04SAlexander Shishkin }
13514cdbf04SAlexander Shishkin
13614cdbf04SAlexander Shishkin static DEVICE_ATTR_RW(clock_divider);
13714cdbf04SAlexander Shishkin
13814cdbf04SAlexander Shishkin static struct attribute *pti_output_attrs[] = {
13914cdbf04SAlexander Shishkin &dev_attr_mode.attr,
14014cdbf04SAlexander Shishkin &dev_attr_freerunning_clock.attr,
14114cdbf04SAlexander Shishkin &dev_attr_clock_divider.attr,
14214cdbf04SAlexander Shishkin NULL,
14314cdbf04SAlexander Shishkin };
14414cdbf04SAlexander Shishkin
145*d9962f6fSRikard Falkeborn static const struct attribute_group pti_output_group = {
14614cdbf04SAlexander Shishkin .attrs = pti_output_attrs,
14714cdbf04SAlexander Shishkin };
14814cdbf04SAlexander Shishkin
intel_th_pti_activate(struct intel_th_device * thdev)14914cdbf04SAlexander Shishkin static int intel_th_pti_activate(struct intel_th_device *thdev)
15014cdbf04SAlexander Shishkin {
15114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev);
15214cdbf04SAlexander Shishkin u32 ctl = PTI_EN;
15314cdbf04SAlexander Shishkin
15414cdbf04SAlexander Shishkin if (pti->patgen)
15514cdbf04SAlexander Shishkin ctl |= pti->patgen << __ffs(PTI_PATGENMODE);
15614cdbf04SAlexander Shishkin if (pti->freeclk)
15714cdbf04SAlexander Shishkin ctl |= PTI_FCEN;
15814cdbf04SAlexander Shishkin ctl |= pti->mode << __ffs(PTI_MODE);
15914cdbf04SAlexander Shishkin ctl |= pti->clkdiv << __ffs(PTI_CLKDIV);
160f77d22bcSAlexander Shishkin ctl |= pti->lpp_dest << __ffs(LPP_DEST);
16114cdbf04SAlexander Shishkin
16214cdbf04SAlexander Shishkin iowrite32(ctl, pti->base + REG_PTI_CTL);
16314cdbf04SAlexander Shishkin
16414cdbf04SAlexander Shishkin intel_th_trace_enable(thdev);
16514cdbf04SAlexander Shishkin
16614cdbf04SAlexander Shishkin return 0;
16714cdbf04SAlexander Shishkin }
16814cdbf04SAlexander Shishkin
intel_th_pti_deactivate(struct intel_th_device * thdev)16914cdbf04SAlexander Shishkin static void intel_th_pti_deactivate(struct intel_th_device *thdev)
17014cdbf04SAlexander Shishkin {
17114cdbf04SAlexander Shishkin struct pti_device *pti = dev_get_drvdata(&thdev->dev);
17214cdbf04SAlexander Shishkin
17314cdbf04SAlexander Shishkin intel_th_trace_disable(thdev);
17414cdbf04SAlexander Shishkin
17514cdbf04SAlexander Shishkin iowrite32(0, pti->base + REG_PTI_CTL);
17614cdbf04SAlexander Shishkin }
17714cdbf04SAlexander Shishkin
read_hw_config(struct pti_device * pti)17814cdbf04SAlexander Shishkin static void read_hw_config(struct pti_device *pti)
17914cdbf04SAlexander Shishkin {
18014cdbf04SAlexander Shishkin u32 ctl = ioread32(pti->base + REG_PTI_CTL);
18114cdbf04SAlexander Shishkin
18214cdbf04SAlexander Shishkin pti->mode = (ctl & PTI_MODE) >> __ffs(PTI_MODE);
18314cdbf04SAlexander Shishkin pti->clkdiv = (ctl & PTI_CLKDIV) >> __ffs(PTI_CLKDIV);
18414cdbf04SAlexander Shishkin pti->freeclk = !!(ctl & PTI_FCEN);
18514cdbf04SAlexander Shishkin
18614cdbf04SAlexander Shishkin if (!pti_mode[pti->mode])
18714cdbf04SAlexander Shishkin pti->mode = pti_width_mode(4);
18814cdbf04SAlexander Shishkin if (!pti->clkdiv)
18914cdbf04SAlexander Shishkin pti->clkdiv = 1;
190f77d22bcSAlexander Shishkin
191f77d22bcSAlexander Shishkin if (pti->thdev->output.type == GTH_LPP) {
192f77d22bcSAlexander Shishkin if (ctl & LPP_PTIPRESENT)
193f77d22bcSAlexander Shishkin pti->lpp_dest_mask |= LPP_DEST_PTI;
194f77d22bcSAlexander Shishkin if (ctl & LPP_BSSBPRESENT)
195f77d22bcSAlexander Shishkin pti->lpp_dest_mask |= LPP_DEST_EXI;
196f77d22bcSAlexander Shishkin if (ctl & LPP_DEST)
197f77d22bcSAlexander Shishkin pti->lpp_dest = 1;
198f77d22bcSAlexander Shishkin }
19914cdbf04SAlexander Shishkin }
20014cdbf04SAlexander Shishkin
intel_th_pti_probe(struct intel_th_device * thdev)20114cdbf04SAlexander Shishkin static int intel_th_pti_probe(struct intel_th_device *thdev)
20214cdbf04SAlexander Shishkin {
20314cdbf04SAlexander Shishkin struct device *dev = &thdev->dev;
20414cdbf04SAlexander Shishkin struct resource *res;
20514cdbf04SAlexander Shishkin struct pti_device *pti;
20614cdbf04SAlexander Shishkin void __iomem *base;
20714cdbf04SAlexander Shishkin
20814cdbf04SAlexander Shishkin res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
20914cdbf04SAlexander Shishkin if (!res)
21014cdbf04SAlexander Shishkin return -ENODEV;
21114cdbf04SAlexander Shishkin
21214cdbf04SAlexander Shishkin base = devm_ioremap(dev, res->start, resource_size(res));
21373061da0SDan Carpenter if (!base)
21473061da0SDan Carpenter return -ENOMEM;
21514cdbf04SAlexander Shishkin
21614cdbf04SAlexander Shishkin pti = devm_kzalloc(dev, sizeof(*pti), GFP_KERNEL);
21714cdbf04SAlexander Shishkin if (!pti)
21814cdbf04SAlexander Shishkin return -ENOMEM;
21914cdbf04SAlexander Shishkin
22014cdbf04SAlexander Shishkin pti->thdev = thdev;
22114cdbf04SAlexander Shishkin pti->base = base;
22214cdbf04SAlexander Shishkin
22314cdbf04SAlexander Shishkin read_hw_config(pti);
22414cdbf04SAlexander Shishkin
22514cdbf04SAlexander Shishkin dev_set_drvdata(dev, pti);
22614cdbf04SAlexander Shishkin
22714cdbf04SAlexander Shishkin return 0;
22814cdbf04SAlexander Shishkin }
22914cdbf04SAlexander Shishkin
intel_th_pti_remove(struct intel_th_device * thdev)23014cdbf04SAlexander Shishkin static void intel_th_pti_remove(struct intel_th_device *thdev)
23114cdbf04SAlexander Shishkin {
23214cdbf04SAlexander Shishkin }
23314cdbf04SAlexander Shishkin
23414cdbf04SAlexander Shishkin static struct intel_th_driver intel_th_pti_driver = {
23514cdbf04SAlexander Shishkin .probe = intel_th_pti_probe,
23614cdbf04SAlexander Shishkin .remove = intel_th_pti_remove,
23714cdbf04SAlexander Shishkin .activate = intel_th_pti_activate,
23814cdbf04SAlexander Shishkin .deactivate = intel_th_pti_deactivate,
239e8644e4cSAlexander Shishkin .attr_group = &pti_output_group,
24014cdbf04SAlexander Shishkin .driver = {
24114cdbf04SAlexander Shishkin .name = "pti",
24214cdbf04SAlexander Shishkin .owner = THIS_MODULE,
24314cdbf04SAlexander Shishkin },
24414cdbf04SAlexander Shishkin };
24514cdbf04SAlexander Shishkin
246f77d22bcSAlexander Shishkin static const char * const lpp_dest_str[] = { "pti", "exi" };
247f77d22bcSAlexander Shishkin
lpp_dest_show(struct device * dev,struct device_attribute * attr,char * buf)248f77d22bcSAlexander Shishkin static ssize_t lpp_dest_show(struct device *dev, struct device_attribute *attr,
249f77d22bcSAlexander Shishkin char *buf)
250f77d22bcSAlexander Shishkin {
251f77d22bcSAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
252f77d22bcSAlexander Shishkin ssize_t ret = 0;
253f77d22bcSAlexander Shishkin int i;
254f77d22bcSAlexander Shishkin
255f77d22bcSAlexander Shishkin for (i = ARRAY_SIZE(lpp_dest_str) - 1; i >= 0; i--) {
256f77d22bcSAlexander Shishkin const char *fmt = pti->lpp_dest == i ? "[%s] " : "%s ";
257f77d22bcSAlexander Shishkin
258f77d22bcSAlexander Shishkin if (!(pti->lpp_dest_mask & BIT(i)))
259f77d22bcSAlexander Shishkin continue;
260f77d22bcSAlexander Shishkin
261f77d22bcSAlexander Shishkin ret += scnprintf(buf + ret, PAGE_SIZE - ret,
262f77d22bcSAlexander Shishkin fmt, lpp_dest_str[i]);
263f77d22bcSAlexander Shishkin }
264f77d22bcSAlexander Shishkin
265f77d22bcSAlexander Shishkin if (ret)
266f77d22bcSAlexander Shishkin buf[ret - 1] = '\n';
267f77d22bcSAlexander Shishkin
268f77d22bcSAlexander Shishkin return ret;
269f77d22bcSAlexander Shishkin }
270f77d22bcSAlexander Shishkin
lpp_dest_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t size)271f77d22bcSAlexander Shishkin static ssize_t lpp_dest_store(struct device *dev, struct device_attribute *attr,
272f77d22bcSAlexander Shishkin const char *buf, size_t size)
273f77d22bcSAlexander Shishkin {
274f77d22bcSAlexander Shishkin struct pti_device *pti = dev_get_drvdata(dev);
275f77d22bcSAlexander Shishkin int i;
276f77d22bcSAlexander Shishkin
2771d2ef028SAndy Shevchenko i = sysfs_match_string(lpp_dest_str, buf);
2781d2ef028SAndy Shevchenko if (i < 0)
2791d2ef028SAndy Shevchenko return i;
280f77d22bcSAlexander Shishkin
2811d2ef028SAndy Shevchenko if (!(pti->lpp_dest_mask & BIT(i)))
2821d2ef028SAndy Shevchenko return -EINVAL;
2831d2ef028SAndy Shevchenko
284f77d22bcSAlexander Shishkin pti->lpp_dest = i;
2851d2ef028SAndy Shevchenko return size;
286f77d22bcSAlexander Shishkin }
287f77d22bcSAlexander Shishkin
288f77d22bcSAlexander Shishkin static DEVICE_ATTR_RW(lpp_dest);
289f77d22bcSAlexander Shishkin
290f77d22bcSAlexander Shishkin static struct attribute *lpp_output_attrs[] = {
291f77d22bcSAlexander Shishkin &dev_attr_mode.attr,
292f77d22bcSAlexander Shishkin &dev_attr_freerunning_clock.attr,
293f77d22bcSAlexander Shishkin &dev_attr_clock_divider.attr,
294f77d22bcSAlexander Shishkin &dev_attr_lpp_dest.attr,
295f77d22bcSAlexander Shishkin NULL,
296f77d22bcSAlexander Shishkin };
297f77d22bcSAlexander Shishkin
298*d9962f6fSRikard Falkeborn static const struct attribute_group lpp_output_group = {
299f77d22bcSAlexander Shishkin .attrs = lpp_output_attrs,
300f77d22bcSAlexander Shishkin };
301f77d22bcSAlexander Shishkin
302f77d22bcSAlexander Shishkin static struct intel_th_driver intel_th_lpp_driver = {
303f77d22bcSAlexander Shishkin .probe = intel_th_pti_probe,
304f77d22bcSAlexander Shishkin .remove = intel_th_pti_remove,
305f77d22bcSAlexander Shishkin .activate = intel_th_pti_activate,
306f77d22bcSAlexander Shishkin .deactivate = intel_th_pti_deactivate,
307f77d22bcSAlexander Shishkin .attr_group = &lpp_output_group,
308f77d22bcSAlexander Shishkin .driver = {
309f77d22bcSAlexander Shishkin .name = "lpp",
310f77d22bcSAlexander Shishkin .owner = THIS_MODULE,
311f77d22bcSAlexander Shishkin },
312f77d22bcSAlexander Shishkin };
313f77d22bcSAlexander Shishkin
intel_th_pti_lpp_init(void)314f77d22bcSAlexander Shishkin static int __init intel_th_pti_lpp_init(void)
315f77d22bcSAlexander Shishkin {
316f77d22bcSAlexander Shishkin int err;
317f77d22bcSAlexander Shishkin
318f77d22bcSAlexander Shishkin err = intel_th_driver_register(&intel_th_pti_driver);
319f77d22bcSAlexander Shishkin if (err)
320f77d22bcSAlexander Shishkin return err;
321f77d22bcSAlexander Shishkin
322f77d22bcSAlexander Shishkin err = intel_th_driver_register(&intel_th_lpp_driver);
323f77d22bcSAlexander Shishkin if (err) {
324f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_pti_driver);
325f77d22bcSAlexander Shishkin return err;
326f77d22bcSAlexander Shishkin }
327f77d22bcSAlexander Shishkin
328f77d22bcSAlexander Shishkin return 0;
329f77d22bcSAlexander Shishkin }
330f77d22bcSAlexander Shishkin
331f77d22bcSAlexander Shishkin module_init(intel_th_pti_lpp_init);
332f77d22bcSAlexander Shishkin
intel_th_pti_lpp_exit(void)333f77d22bcSAlexander Shishkin static void __exit intel_th_pti_lpp_exit(void)
334f77d22bcSAlexander Shishkin {
335f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_pti_driver);
336f77d22bcSAlexander Shishkin intel_th_driver_unregister(&intel_th_lpp_driver);
337f77d22bcSAlexander Shishkin }
338f77d22bcSAlexander Shishkin
339f77d22bcSAlexander Shishkin module_exit(intel_th_pti_lpp_exit);
34014cdbf04SAlexander Shishkin
34114cdbf04SAlexander Shishkin MODULE_LICENSE("GPL v2");
342f77d22bcSAlexander Shishkin MODULE_DESCRIPTION("Intel(R) Trace Hub PTI/LPP output driver");
34314cdbf04SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
344