1/*
2 * Intel(R) Trace Hub Software Trace Hub support
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/io.h>
22#include <linux/mm.h>
23#include <linux/slab.h>
24#include <linux/stm.h>
25
26#include "intel_th.h"
27#include "sth.h"
28
29struct sth_device {
30	void __iomem	*base;
31	void __iomem	*channels;
32	phys_addr_t	channels_phys;
33	struct device	*dev;
34	struct stm_data	stm;
35	unsigned int	sw_nmasters;
36};
37
38static struct intel_th_channel __iomem *
39sth_channel(struct sth_device *sth, unsigned int master, unsigned int channel)
40{
41	struct intel_th_channel __iomem *sw_map = sth->channels;
42
43	return &sw_map[(master - sth->stm.sw_start) * sth->stm.sw_nchannels +
44		       channel];
45}
46
47static void sth_iowrite(void __iomem *dest, const unsigned char *payload,
48			unsigned int size)
49{
50	switch (size) {
51#ifdef CONFIG_64BIT
52	case 8:
53		writeq_relaxed(*(u64 *)payload, dest);
54		break;
55#endif
56	case 4:
57		writel_relaxed(*(u32 *)payload, dest);
58		break;
59	case 2:
60		writew_relaxed(*(u16 *)payload, dest);
61		break;
62	case 1:
63		writeb_relaxed(*(u8 *)payload, dest);
64		break;
65	default:
66		break;
67	}
68}
69
70static ssize_t sth_stm_packet(struct stm_data *stm_data, unsigned int master,
71			      unsigned int channel, unsigned int packet,
72			      unsigned int flags, unsigned int size,
73			      const unsigned char *payload)
74{
75	struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
76	struct intel_th_channel __iomem *out =
77		sth_channel(sth, master, channel);
78	u64 __iomem *outp = &out->Dn;
79	unsigned long reg = REG_STH_TRIG;
80
81#ifndef CONFIG_64BIT
82	if (size > 4)
83		size = 4;
84#endif
85
86	size = rounddown_pow_of_two(size);
87
88	switch (packet) {
89	/* Global packets (GERR, XSYNC, TRIG) are sent with register writes */
90	case STP_PACKET_GERR:
91		reg += 4;
92	case STP_PACKET_XSYNC:
93		reg += 8;
94	case STP_PACKET_TRIG:
95		if (flags & STP_PACKET_TIMESTAMPED)
96			reg += 4;
97		iowrite8(*payload, sth->base + reg);
98		break;
99
100	case STP_PACKET_MERR:
101		sth_iowrite(&out->MERR, payload, size);
102		break;
103
104	case STP_PACKET_FLAG:
105		if (flags & STP_PACKET_TIMESTAMPED)
106			outp = (u64 __iomem *)&out->FLAG_TS;
107		else
108			outp = (u64 __iomem *)&out->FLAG;
109
110		size = 1;
111		sth_iowrite(outp, payload, size);
112		break;
113
114	case STP_PACKET_USER:
115		if (flags & STP_PACKET_TIMESTAMPED)
116			outp = &out->USER_TS;
117		else
118			outp = &out->USER;
119		sth_iowrite(outp, payload, size);
120		break;
121
122	case STP_PACKET_DATA:
123		outp = &out->Dn;
124
125		if (flags & STP_PACKET_TIMESTAMPED)
126			outp += 2;
127		if (flags & STP_PACKET_MARKED)
128			outp++;
129
130		sth_iowrite(outp, payload, size);
131		break;
132	}
133
134	return size;
135}
136
137static phys_addr_t
138sth_stm_mmio_addr(struct stm_data *stm_data, unsigned int master,
139		  unsigned int channel, unsigned int nr_chans)
140{
141	struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
142	phys_addr_t addr;
143
144	master -= sth->stm.sw_start;
145	addr = sth->channels_phys + (master * sth->stm.sw_nchannels + channel) *
146		sizeof(struct intel_th_channel);
147
148	if (offset_in_page(addr) ||
149	    offset_in_page(nr_chans * sizeof(struct intel_th_channel)))
150		return 0;
151
152	return addr;
153}
154
155static int sth_stm_link(struct stm_data *stm_data, unsigned int master,
156			 unsigned int channel)
157{
158	struct sth_device *sth = container_of(stm_data, struct sth_device, stm);
159
160	intel_th_set_output(to_intel_th_device(sth->dev), master);
161
162	return 0;
163}
164
165static int intel_th_sw_init(struct sth_device *sth)
166{
167	u32 reg;
168
169	reg = ioread32(sth->base + REG_STH_STHCAP1);
170	sth->stm.sw_nchannels = reg & 0xff;
171
172	reg = ioread32(sth->base + REG_STH_STHCAP0);
173	sth->stm.sw_start = reg & 0xffff;
174	sth->stm.sw_end = reg >> 16;
175
176	sth->sw_nmasters = sth->stm.sw_end - sth->stm.sw_start;
177	dev_dbg(sth->dev, "sw_start: %x sw_end: %x masters: %x nchannels: %x\n",
178		sth->stm.sw_start, sth->stm.sw_end, sth->sw_nmasters,
179		sth->stm.sw_nchannels);
180
181	return 0;
182}
183
184static int intel_th_sth_probe(struct intel_th_device *thdev)
185{
186	struct device *dev = &thdev->dev;
187	struct sth_device *sth;
188	struct resource *res;
189	void __iomem *base, *channels;
190	int err;
191
192	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 0);
193	if (!res)
194		return -ENODEV;
195
196	base = devm_ioremap(dev, res->start, resource_size(res));
197	if (!base)
198		return -ENOMEM;
199
200	res = intel_th_device_get_resource(thdev, IORESOURCE_MEM, 1);
201	if (!res)
202		return -ENODEV;
203
204	channels = devm_ioremap(dev, res->start, resource_size(res));
205	if (!channels)
206		return -ENOMEM;
207
208	sth = devm_kzalloc(dev, sizeof(*sth), GFP_KERNEL);
209	if (!sth)
210		return -ENOMEM;
211
212	sth->dev = dev;
213	sth->base = base;
214	sth->channels = channels;
215	sth->channels_phys = res->start;
216	sth->stm.name = dev_name(dev);
217	sth->stm.packet = sth_stm_packet;
218	sth->stm.mmio_addr = sth_stm_mmio_addr;
219	sth->stm.sw_mmiosz = sizeof(struct intel_th_channel);
220	sth->stm.link = sth_stm_link;
221
222	err = intel_th_sw_init(sth);
223	if (err)
224		return err;
225
226	err = stm_register_device(dev, &sth->stm, THIS_MODULE);
227	if (err) {
228		dev_err(dev, "stm_register_device failed\n");
229		return err;
230	}
231
232	dev_set_drvdata(dev, sth);
233
234	return 0;
235}
236
237static void intel_th_sth_remove(struct intel_th_device *thdev)
238{
239	struct sth_device *sth = dev_get_drvdata(&thdev->dev);
240
241	stm_unregister_device(&sth->stm);
242}
243
244static struct intel_th_driver intel_th_sth_driver = {
245	.probe	= intel_th_sth_probe,
246	.remove	= intel_th_sth_remove,
247	.driver	= {
248		.name	= "sth",
249		.owner	= THIS_MODULE,
250	},
251};
252
253module_driver(intel_th_sth_driver,
254	      intel_th_driver_register,
255	      intel_th_driver_unregister);
256
257MODULE_LICENSE("GPL v2");
258MODULE_DESCRIPTION("Intel(R) Trace Hub Software Trace Hub driver");
259MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@intel.com>");
260