18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Intel(R) Trace Hub Software Trace Hub support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2014-2015 Intel Corporation.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	KBUILD_MODNAME ": " fmt
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/types.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/device.h>
138c2ecf20Sopenharmony_ci#include <linux/io.h>
148c2ecf20Sopenharmony_ci#include <linux/mm.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <linux/stm.h>
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include "intel_th.h"
198c2ecf20Sopenharmony_ci#include "sth.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct sth_device {
228c2ecf20Sopenharmony_ci	void __iomem	*base;
238c2ecf20Sopenharmony_ci	void __iomem	*channels;
248c2ecf20Sopenharmony_ci	phys_addr_t	channels_phys;
258c2ecf20Sopenharmony_ci	struct device	*dev;
268c2ecf20Sopenharmony_ci	struct stm_data	stm;
278c2ecf20Sopenharmony_ci	unsigned int	sw_nmasters;
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct intel_th_channel __iomem *
318c2ecf20Sopenharmony_cisth_channel(struct sth_device *sth, unsigned int master, unsigned int channel)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	struct intel_th_channel __iomem *sw_map = sth->channels;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	return &sw_map[(master - sth->stm.sw_start) * sth->stm.sw_nchannels +
368c2ecf20Sopenharmony_ci		       channel];
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void sth_iowrite(void __iomem *dest, const unsigned char *payload,
408c2ecf20Sopenharmony_ci			unsigned int size)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	switch (size) {
438c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT
448c2ecf20Sopenharmony_ci	case 8:
458c2ecf20Sopenharmony_ci		writeq_relaxed(*(u64 *)payload, dest);
468c2ecf20Sopenharmony_ci		break;
478c2ecf20Sopenharmony_ci#endif
488c2ecf20Sopenharmony_ci	case 4:
498c2ecf20Sopenharmony_ci		writel_relaxed(*(u32 *)payload, dest);
508c2ecf20Sopenharmony_ci		break;
518c2ecf20Sopenharmony_ci	case 2:
528c2ecf20Sopenharmony_ci		writew_relaxed(*(u16 *)payload, dest);
538c2ecf20Sopenharmony_ci		break;
548c2ecf20Sopenharmony_ci	case 1:
558c2ecf20Sopenharmony_ci		writeb_relaxed(*(u8 *)payload, dest);
568c2ecf20Sopenharmony_ci		break;
578c2ecf20Sopenharmony_ci	default:
588c2ecf20Sopenharmony_ci		break;
598c2ecf20Sopenharmony_ci	}
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic ssize_t notrace sth_stm_packet(struct stm_data *stm_data,
638c2ecf20Sopenharmony_ci				      unsigned int master,
648c2ecf20Sopenharmony_ci				      unsigned int channel,
658c2ecf20Sopenharmony_ci				      unsigned int packet,
668c2ecf20Sopenharmony_ci				      unsigned int flags,
678c2ecf20Sopenharmony_ci				      unsigned int size,
688c2ecf20Sopenharmony_ci				      const unsigned char *payload)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
718c2ecf20Sopenharmony_ci	struct intel_th_channel __iomem *out =
728c2ecf20Sopenharmony_ci		sth_channel(sth, master, channel);
738c2ecf20Sopenharmony_ci	u64 __iomem *outp = &out->Dn;
748c2ecf20Sopenharmony_ci	unsigned long reg = REG_STH_TRIG;
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci#ifndef CONFIG_64BIT
778c2ecf20Sopenharmony_ci	if (size > 4)
788c2ecf20Sopenharmony_ci		size = 4;
798c2ecf20Sopenharmony_ci#endif
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	size = rounddown_pow_of_two(size);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	switch (packet) {
848c2ecf20Sopenharmony_ci	/* Global packets (GERR, XSYNC, TRIG) are sent with register writes */
858c2ecf20Sopenharmony_ci	case STP_PACKET_GERR:
868c2ecf20Sopenharmony_ci		reg += 4;
878c2ecf20Sopenharmony_ci		fallthrough;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	case STP_PACKET_XSYNC:
908c2ecf20Sopenharmony_ci		reg += 8;
918c2ecf20Sopenharmony_ci		fallthrough;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	case STP_PACKET_TRIG:
948c2ecf20Sopenharmony_ci		if (flags & STP_PACKET_TIMESTAMPED)
958c2ecf20Sopenharmony_ci			reg += 4;
968c2ecf20Sopenharmony_ci		writeb_relaxed(*payload, sth->base + reg);
978c2ecf20Sopenharmony_ci		break;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	case STP_PACKET_MERR:
1008c2ecf20Sopenharmony_ci		if (size > 4)
1018c2ecf20Sopenharmony_ci			size = 4;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci		sth_iowrite(&out->MERR, payload, size);
1048c2ecf20Sopenharmony_ci		break;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	case STP_PACKET_FLAG:
1078c2ecf20Sopenharmony_ci		if (flags & STP_PACKET_TIMESTAMPED)
1088c2ecf20Sopenharmony_ci			outp = (u64 __iomem *)&out->FLAG_TS;
1098c2ecf20Sopenharmony_ci		else
1108c2ecf20Sopenharmony_ci			outp = (u64 __iomem *)&out->FLAG;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci		size = 0;
1138c2ecf20Sopenharmony_ci		writeb_relaxed(0, outp);
1148c2ecf20Sopenharmony_ci		break;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	case STP_PACKET_USER:
1178c2ecf20Sopenharmony_ci		if (flags & STP_PACKET_TIMESTAMPED)
1188c2ecf20Sopenharmony_ci			outp = &out->USER_TS;
1198c2ecf20Sopenharmony_ci		else
1208c2ecf20Sopenharmony_ci			outp = &out->USER;
1218c2ecf20Sopenharmony_ci		sth_iowrite(outp, payload, size);
1228c2ecf20Sopenharmony_ci		break;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	case STP_PACKET_DATA:
1258c2ecf20Sopenharmony_ci		outp = &out->Dn;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		if (flags & STP_PACKET_TIMESTAMPED)
1288c2ecf20Sopenharmony_ci			outp += 2;
1298c2ecf20Sopenharmony_ci		if (flags & STP_PACKET_MARKED)
1308c2ecf20Sopenharmony_ci			outp++;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		sth_iowrite(outp, payload, size);
1338c2ecf20Sopenharmony_ci		break;
1348c2ecf20Sopenharmony_ci	default:
1358c2ecf20Sopenharmony_ci		return -ENOTSUPP;
1368c2ecf20Sopenharmony_ci	}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	return size;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic phys_addr_t
1428c2ecf20Sopenharmony_cisth_stm_mmio_addr(struct stm_data *stm_data, unsigned int master,
1438c2ecf20Sopenharmony_ci		  unsigned int channel, unsigned int nr_chans)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
1468c2ecf20Sopenharmony_ci	phys_addr_t addr;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	master -= sth->stm.sw_start;
1498c2ecf20Sopenharmony_ci	addr = sth->channels_phys + (master * sth->stm.sw_nchannels + channel) *
1508c2ecf20Sopenharmony_ci		sizeof(struct intel_th_channel);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (offset_in_page(addr) ||
1538c2ecf20Sopenharmony_ci	    offset_in_page(nr_chans * sizeof(struct intel_th_channel)))
1548c2ecf20Sopenharmony_ci		return 0;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return addr;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic int sth_stm_link(struct stm_data *stm_data, unsigned int master,
1608c2ecf20Sopenharmony_ci			 unsigned int channel)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	return intel_th_set_output(to_intel_th_device(sth->dev), master);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_cistatic int intel_th_sw_init(struct sth_device *sth)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	u32 reg;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	reg = ioread32(sth->base + REG_STH_STHCAP1);
1728c2ecf20Sopenharmony_ci	sth->stm.sw_nchannels = reg & 0xff;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	reg = ioread32(sth->base + REG_STH_STHCAP0);
1758c2ecf20Sopenharmony_ci	sth->stm.sw_start = reg & 0xffff;
1768c2ecf20Sopenharmony_ci	sth->stm.sw_end = reg >> 16;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	sth->sw_nmasters = sth->stm.sw_end - sth->stm.sw_start;
1798c2ecf20Sopenharmony_ci	dev_dbg(sth->dev, "sw_start: %x sw_end: %x masters: %x nchannels: %x\n",
1808c2ecf20Sopenharmony_ci		sth->stm.sw_start, sth->stm.sw_end, sth->sw_nmasters,
1818c2ecf20Sopenharmony_ci		sth->stm.sw_nchannels);
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci	return 0;
1848c2ecf20Sopenharmony_ci}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_cistatic int intel_th_sth_probe(struct intel_th_device *thdev)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct device *dev = &thdev->dev;
1898c2ecf20Sopenharmony_ci	struct sth_device *sth;
1908c2ecf20Sopenharmony_ci	struct resource *res;
1918c2ecf20Sopenharmony_ci	void __iomem *base, *channels;
1928c2ecf20Sopenharmony_ci	int err;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
1958c2ecf20Sopenharmony_ci	if (!res)
1968c2ecf20Sopenharmony_ci		return -ENODEV;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	base = devm_ioremap(dev, res->start, resource_size(res));
1998c2ecf20Sopenharmony_ci	if (!base)
2008c2ecf20Sopenharmony_ci		return -ENOMEM;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 1);
2038c2ecf20Sopenharmony_ci	if (!res)
2048c2ecf20Sopenharmony_ci		return -ENODEV;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	channels = devm_ioremap(dev, res->start, resource_size(res));
2078c2ecf20Sopenharmony_ci	if (!channels)
2088c2ecf20Sopenharmony_ci		return -ENOMEM;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	sth = devm_kzalloc(dev, sizeof(*sth), GFP_KERNEL);
2118c2ecf20Sopenharmony_ci	if (!sth)
2128c2ecf20Sopenharmony_ci		return -ENOMEM;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	sth->dev = dev;
2158c2ecf20Sopenharmony_ci	sth->base = base;
2168c2ecf20Sopenharmony_ci	sth->channels = channels;
2178c2ecf20Sopenharmony_ci	sth->channels_phys = res->start;
2188c2ecf20Sopenharmony_ci	sth->stm.name = dev_name(dev);
2198c2ecf20Sopenharmony_ci	sth->stm.packet = sth_stm_packet;
2208c2ecf20Sopenharmony_ci	sth->stm.mmio_addr = sth_stm_mmio_addr;
2218c2ecf20Sopenharmony_ci	sth->stm.sw_mmiosz = sizeof(struct intel_th_channel);
2228c2ecf20Sopenharmony_ci	sth->stm.link = sth_stm_link;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	err = intel_th_sw_init(sth);
2258c2ecf20Sopenharmony_ci	if (err)
2268c2ecf20Sopenharmony_ci		return err;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	err = stm_register_device(dev, &sth->stm, THIS_MODULE);
2298c2ecf20Sopenharmony_ci	if (err) {
2308c2ecf20Sopenharmony_ci		dev_err(dev, "stm_register_device failed\n");
2318c2ecf20Sopenharmony_ci		return err;
2328c2ecf20Sopenharmony_ci	}
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	dev_set_drvdata(dev, sth);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return 0;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_cistatic void intel_th_sth_remove(struct intel_th_device *thdev)
2408c2ecf20Sopenharmony_ci{
2418c2ecf20Sopenharmony_ci	struct sth_device *sth = dev_get_drvdata(&thdev->dev);
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	stm_unregister_device(&sth->stm);
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic struct intel_th_driver intel_th_sth_driver = {
2478c2ecf20Sopenharmony_ci	.probe	= intel_th_sth_probe,
2488c2ecf20Sopenharmony_ci	.remove	= intel_th_sth_remove,
2498c2ecf20Sopenharmony_ci	.driver	= {
2508c2ecf20Sopenharmony_ci		.name	= "sth",
2518c2ecf20Sopenharmony_ci		.owner	= THIS_MODULE,
2528c2ecf20Sopenharmony_ci	},
2538c2ecf20Sopenharmony_ci};
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cimodule_driver(intel_th_sth_driver,
2568c2ecf20Sopenharmony_ci	      intel_th_driver_register,
2578c2ecf20Sopenharmony_ci	      intel_th_driver_unregister);
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
2608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Intel(R) Trace Hub Software Trace Hub driver");
2618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");
262