18b3d6663SArnd Bergmann /* hw_ops.c - query/set operations on active SPU context.
28b3d6663SArnd Bergmann  *
38b3d6663SArnd Bergmann  * Copyright (C) IBM 2005
48b3d6663SArnd Bergmann  * Author: Mark Nutter <mnutter@us.ibm.com>
58b3d6663SArnd Bergmann  *
68b3d6663SArnd Bergmann  * This program is free software; you can redistribute it and/or modify
78b3d6663SArnd Bergmann  * it under the terms of the GNU General Public License as published by
88b3d6663SArnd Bergmann  * the Free Software Foundation; either version 2, or (at your option)
98b3d6663SArnd Bergmann  * any later version.
108b3d6663SArnd Bergmann  *
118b3d6663SArnd Bergmann  * This program is distributed in the hope that it will be useful,
128b3d6663SArnd Bergmann  * but WITHOUT ANY WARRANTY; without even the implied warranty of
138b3d6663SArnd Bergmann  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
148b3d6663SArnd Bergmann  * GNU General Public License for more details.
158b3d6663SArnd Bergmann  *
168b3d6663SArnd Bergmann  * You should have received a copy of the GNU General Public License
178b3d6663SArnd Bergmann  * along with this program; if not, write to the Free Software
188b3d6663SArnd Bergmann  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
198b3d6663SArnd Bergmann  */
208b3d6663SArnd Bergmann 
218b3d6663SArnd Bergmann #include <linux/config.h>
228b3d6663SArnd Bergmann #include <linux/module.h>
238b3d6663SArnd Bergmann #include <linux/errno.h>
248b3d6663SArnd Bergmann #include <linux/sched.h>
258b3d6663SArnd Bergmann #include <linux/kernel.h>
268b3d6663SArnd Bergmann #include <linux/mm.h>
273a843d7cSArnd Bergmann #include <linux/poll.h>
288b3d6663SArnd Bergmann #include <linux/smp.h>
298b3d6663SArnd Bergmann #include <linux/smp_lock.h>
308b3d6663SArnd Bergmann #include <linux/stddef.h>
318b3d6663SArnd Bergmann #include <linux/unistd.h>
328b3d6663SArnd Bergmann 
338b3d6663SArnd Bergmann #include <asm/io.h>
348b3d6663SArnd Bergmann #include <asm/spu.h>
35540270d8SGeoff Levand #include <asm/spu_priv1.h>
368b3d6663SArnd Bergmann #include <asm/spu_csa.h>
378b3d6663SArnd Bergmann #include <asm/mmu_context.h>
388b3d6663SArnd Bergmann #include "spufs.h"
398b3d6663SArnd Bergmann 
408b3d6663SArnd Bergmann static int spu_hw_mbox_read(struct spu_context *ctx, u32 * data)
418b3d6663SArnd Bergmann {
428b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
438b3d6663SArnd Bergmann 	struct spu_problem __iomem *prob = spu->problem;
448b3d6663SArnd Bergmann 	u32 mbox_stat;
458b3d6663SArnd Bergmann 	int ret = 0;
468b3d6663SArnd Bergmann 
478b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
488b3d6663SArnd Bergmann 	mbox_stat = in_be32(&prob->mb_stat_R);
498b3d6663SArnd Bergmann 	if (mbox_stat & 0x0000ff) {
508b3d6663SArnd Bergmann 		*data = in_be32(&prob->pu_mb_R);
518b3d6663SArnd Bergmann 		ret = 4;
528b3d6663SArnd Bergmann 	}
538b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
548b3d6663SArnd Bergmann 	return ret;
558b3d6663SArnd Bergmann }
568b3d6663SArnd Bergmann 
578b3d6663SArnd Bergmann static u32 spu_hw_mbox_stat_read(struct spu_context *ctx)
588b3d6663SArnd Bergmann {
598b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->mb_stat_R);
608b3d6663SArnd Bergmann }
618b3d6663SArnd Bergmann 
623a843d7cSArnd Bergmann static unsigned int spu_hw_mbox_stat_poll(struct spu_context *ctx,
633a843d7cSArnd Bergmann 					  unsigned int events)
643a843d7cSArnd Bergmann {
653a843d7cSArnd Bergmann 	struct spu *spu = ctx->spu;
663a843d7cSArnd Bergmann 	int ret = 0;
673a843d7cSArnd Bergmann 	u32 stat;
683a843d7cSArnd Bergmann 
693a843d7cSArnd Bergmann 	spin_lock_irq(&spu->register_lock);
703a843d7cSArnd Bergmann 	stat = in_be32(&spu->problem->mb_stat_R);
713a843d7cSArnd Bergmann 
723a843d7cSArnd Bergmann 	/* if the requested event is there, return the poll
733a843d7cSArnd Bergmann 	   mask, otherwise enable the interrupt to get notified,
743a843d7cSArnd Bergmann 	   but first mark any pending interrupts as done so
753a843d7cSArnd Bergmann 	   we don't get woken up unnecessarily */
763a843d7cSArnd Bergmann 
773a843d7cSArnd Bergmann 	if (events & (POLLIN | POLLRDNORM)) {
783a843d7cSArnd Bergmann 		if (stat & 0xff0000)
793a843d7cSArnd Bergmann 			ret |= POLLIN | POLLRDNORM;
803a843d7cSArnd Bergmann 		else {
81f0831accSArnd Bergmann 			spu_int_stat_clear(spu, 2, 0x1);
82f0831accSArnd Bergmann 			spu_int_mask_or(spu, 2, 0x1);
833a843d7cSArnd Bergmann 		}
843a843d7cSArnd Bergmann 	}
853a843d7cSArnd Bergmann 	if (events & (POLLOUT | POLLWRNORM)) {
863a843d7cSArnd Bergmann 		if (stat & 0x00ff00)
873a843d7cSArnd Bergmann 			ret = POLLOUT | POLLWRNORM;
883a843d7cSArnd Bergmann 		else {
89f0831accSArnd Bergmann 			spu_int_stat_clear(spu, 2, 0x10);
90f0831accSArnd Bergmann 			spu_int_mask_or(spu, 2, 0x10);
913a843d7cSArnd Bergmann 		}
923a843d7cSArnd Bergmann 	}
933a843d7cSArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
943a843d7cSArnd Bergmann 	return ret;
953a843d7cSArnd Bergmann }
963a843d7cSArnd Bergmann 
978b3d6663SArnd Bergmann static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data)
988b3d6663SArnd Bergmann {
998b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1008b3d6663SArnd Bergmann 	struct spu_problem __iomem *prob = spu->problem;
1018b3d6663SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = spu->priv2;
1028b3d6663SArnd Bergmann 	int ret;
1038b3d6663SArnd Bergmann 
1048b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1058b3d6663SArnd Bergmann 	if (in_be32(&prob->mb_stat_R) & 0xff0000) {
1068b3d6663SArnd Bergmann 		/* read the first available word */
1078b3d6663SArnd Bergmann 		*data = in_be64(&priv2->puint_mb_R);
1088b3d6663SArnd Bergmann 		ret = 4;
1098b3d6663SArnd Bergmann 	} else {
1108b3d6663SArnd Bergmann 		/* make sure we get woken up by the interrupt */
111f0831accSArnd Bergmann 		spu_int_mask_or(spu, 2, 0x1);
1128b3d6663SArnd Bergmann 		ret = 0;
1138b3d6663SArnd Bergmann 	}
1148b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1158b3d6663SArnd Bergmann 	return ret;
1168b3d6663SArnd Bergmann }
1178b3d6663SArnd Bergmann 
1188b3d6663SArnd Bergmann static int spu_hw_wbox_write(struct spu_context *ctx, u32 data)
1198b3d6663SArnd Bergmann {
1208b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1218b3d6663SArnd Bergmann 	struct spu_problem __iomem *prob = spu->problem;
1228b3d6663SArnd Bergmann 	int ret;
1238b3d6663SArnd Bergmann 
1248b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1258b3d6663SArnd Bergmann 	if (in_be32(&prob->mb_stat_R) & 0x00ff00) {
1268b3d6663SArnd Bergmann 		/* we have space to write wbox_data to */
1278b3d6663SArnd Bergmann 		out_be32(&prob->spu_mb_W, data);
1288b3d6663SArnd Bergmann 		ret = 4;
1298b3d6663SArnd Bergmann 	} else {
1308b3d6663SArnd Bergmann 		/* make sure we get woken up by the interrupt when space
1318b3d6663SArnd Bergmann 		   becomes available */
132f0831accSArnd Bergmann 		spu_int_mask_or(spu, 2, 0x10);
1338b3d6663SArnd Bergmann 		ret = 0;
1348b3d6663SArnd Bergmann 	}
1358b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1368b3d6663SArnd Bergmann 	return ret;
1378b3d6663SArnd Bergmann }
1388b3d6663SArnd Bergmann 
1398b3d6663SArnd Bergmann static u32 spu_hw_signal1_read(struct spu_context *ctx)
1408b3d6663SArnd Bergmann {
1418b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->signal_notify1);
1428b3d6663SArnd Bergmann }
1438b3d6663SArnd Bergmann 
1448b3d6663SArnd Bergmann static void spu_hw_signal1_write(struct spu_context *ctx, u32 data)
1458b3d6663SArnd Bergmann {
1468b3d6663SArnd Bergmann 	out_be32(&ctx->spu->problem->signal_notify1, data);
1478b3d6663SArnd Bergmann }
1488b3d6663SArnd Bergmann 
1498b3d6663SArnd Bergmann static u32 spu_hw_signal2_read(struct spu_context *ctx)
1508b3d6663SArnd Bergmann {
1518b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->signal_notify1);
1528b3d6663SArnd Bergmann }
1538b3d6663SArnd Bergmann 
1548b3d6663SArnd Bergmann static void spu_hw_signal2_write(struct spu_context *ctx, u32 data)
1558b3d6663SArnd Bergmann {
1568b3d6663SArnd Bergmann 	out_be32(&ctx->spu->problem->signal_notify2, data);
1578b3d6663SArnd Bergmann }
1588b3d6663SArnd Bergmann 
1598b3d6663SArnd Bergmann static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val)
1608b3d6663SArnd Bergmann {
1618b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1628b3d6663SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = spu->priv2;
1638b3d6663SArnd Bergmann 	u64 tmp;
1648b3d6663SArnd Bergmann 
1658b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1668b3d6663SArnd Bergmann 	tmp = in_be64(&priv2->spu_cfg_RW);
1678b3d6663SArnd Bergmann 	if (val)
1688b3d6663SArnd Bergmann 		tmp |= 1;
1698b3d6663SArnd Bergmann 	else
1708b3d6663SArnd Bergmann 		tmp &= ~1;
1718b3d6663SArnd Bergmann 	out_be64(&priv2->spu_cfg_RW, tmp);
1728b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1738b3d6663SArnd Bergmann }
1748b3d6663SArnd Bergmann 
1758b3d6663SArnd Bergmann static u64 spu_hw_signal1_type_get(struct spu_context *ctx)
1768b3d6663SArnd Bergmann {
1778b3d6663SArnd Bergmann 	return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0);
1788b3d6663SArnd Bergmann }
1798b3d6663SArnd Bergmann 
1808b3d6663SArnd Bergmann static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val)
1818b3d6663SArnd Bergmann {
1828b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1838b3d6663SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = spu->priv2;
1848b3d6663SArnd Bergmann 	u64 tmp;
1858b3d6663SArnd Bergmann 
1868b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1878b3d6663SArnd Bergmann 	tmp = in_be64(&priv2->spu_cfg_RW);
1888b3d6663SArnd Bergmann 	if (val)
1898b3d6663SArnd Bergmann 		tmp |= 2;
1908b3d6663SArnd Bergmann 	else
1918b3d6663SArnd Bergmann 		tmp &= ~2;
1928b3d6663SArnd Bergmann 	out_be64(&priv2->spu_cfg_RW, tmp);
1938b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1948b3d6663SArnd Bergmann }
1958b3d6663SArnd Bergmann 
1968b3d6663SArnd Bergmann static u64 spu_hw_signal2_type_get(struct spu_context *ctx)
1978b3d6663SArnd Bergmann {
1988b3d6663SArnd Bergmann 	return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0);
1998b3d6663SArnd Bergmann }
2008b3d6663SArnd Bergmann 
2018b3d6663SArnd Bergmann static u32 spu_hw_npc_read(struct spu_context *ctx)
2028b3d6663SArnd Bergmann {
2038b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->spu_npc_RW);
2048b3d6663SArnd Bergmann }
2058b3d6663SArnd Bergmann 
2068b3d6663SArnd Bergmann static void spu_hw_npc_write(struct spu_context *ctx, u32 val)
2078b3d6663SArnd Bergmann {
2088b3d6663SArnd Bergmann 	out_be32(&ctx->spu->problem->spu_npc_RW, val);
2098b3d6663SArnd Bergmann }
2108b3d6663SArnd Bergmann 
2118b3d6663SArnd Bergmann static u32 spu_hw_status_read(struct spu_context *ctx)
2128b3d6663SArnd Bergmann {
2138b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->spu_status_R);
2148b3d6663SArnd Bergmann }
2158b3d6663SArnd Bergmann 
2168b3d6663SArnd Bergmann static char *spu_hw_get_ls(struct spu_context *ctx)
2178b3d6663SArnd Bergmann {
2188b3d6663SArnd Bergmann 	return ctx->spu->local_store;
2198b3d6663SArnd Bergmann }
2208b3d6663SArnd Bergmann 
2215110459fSArnd Bergmann static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val)
2225110459fSArnd Bergmann {
2235110459fSArnd Bergmann 	eieio();
2245110459fSArnd Bergmann 	out_be32(&ctx->spu->problem->spu_runcntl_RW, val);
2255110459fSArnd Bergmann }
2265110459fSArnd Bergmann 
2275110459fSArnd Bergmann static void spu_hw_runcntl_stop(struct spu_context *ctx)
2285110459fSArnd Bergmann {
2295110459fSArnd Bergmann 	spin_lock_irq(&ctx->spu->register_lock);
2305110459fSArnd Bergmann 	out_be32(&ctx->spu->problem->spu_runcntl_RW, SPU_RUNCNTL_STOP);
2315110459fSArnd Bergmann 	while (in_be32(&ctx->spu->problem->spu_status_R) & SPU_STATUS_RUNNING)
2325110459fSArnd Bergmann 		cpu_relax();
2335110459fSArnd Bergmann 	spin_unlock_irq(&ctx->spu->register_lock);
2345110459fSArnd Bergmann }
2355110459fSArnd Bergmann 
236a33a7d73SArnd Bergmann static int spu_hw_set_mfc_query(struct spu_context * ctx, u32 mask, u32 mode)
237a33a7d73SArnd Bergmann {
238a33a7d73SArnd Bergmann 	struct spu_problem *prob = ctx->spu->problem;
239a33a7d73SArnd Bergmann 	int ret;
240a33a7d73SArnd Bergmann 
241a33a7d73SArnd Bergmann 	spin_lock_irq(&ctx->spu->register_lock);
242a33a7d73SArnd Bergmann 	ret = -EAGAIN;
243a33a7d73SArnd Bergmann 	if (in_be32(&prob->dma_querytype_RW))
244a33a7d73SArnd Bergmann 		goto out;
245a33a7d73SArnd Bergmann 	ret = 0;
246a33a7d73SArnd Bergmann 	out_be32(&prob->dma_querymask_RW, mask);
247a33a7d73SArnd Bergmann 	out_be32(&prob->dma_querytype_RW, mode);
248a33a7d73SArnd Bergmann out:
249a33a7d73SArnd Bergmann 	spin_unlock_irq(&ctx->spu->register_lock);
250a33a7d73SArnd Bergmann 	return ret;
251a33a7d73SArnd Bergmann }
252a33a7d73SArnd Bergmann 
253a33a7d73SArnd Bergmann static u32 spu_hw_read_mfc_tagstatus(struct spu_context * ctx)
254a33a7d73SArnd Bergmann {
255a33a7d73SArnd Bergmann 	return in_be32(&ctx->spu->problem->dma_tagstatus_R);
256a33a7d73SArnd Bergmann }
257a33a7d73SArnd Bergmann 
258a33a7d73SArnd Bergmann static u32 spu_hw_get_mfc_free_elements(struct spu_context *ctx)
259a33a7d73SArnd Bergmann {
260a33a7d73SArnd Bergmann 	return in_be32(&ctx->spu->problem->dma_qstatus_R);
261a33a7d73SArnd Bergmann }
262a33a7d73SArnd Bergmann 
263a33a7d73SArnd Bergmann static int spu_hw_send_mfc_command(struct spu_context *ctx,
264a33a7d73SArnd Bergmann 					struct mfc_dma_command *cmd)
265a33a7d73SArnd Bergmann {
266a33a7d73SArnd Bergmann 	u32 status;
267a33a7d73SArnd Bergmann 	struct spu_problem *prob = ctx->spu->problem;
268a33a7d73SArnd Bergmann 
269a33a7d73SArnd Bergmann 	spin_lock_irq(&ctx->spu->register_lock);
270a33a7d73SArnd Bergmann 	out_be32(&prob->mfc_lsa_W, cmd->lsa);
271a33a7d73SArnd Bergmann 	out_be64(&prob->mfc_ea_W, cmd->ea);
272a33a7d73SArnd Bergmann 	out_be32(&prob->mfc_union_W.by32.mfc_size_tag32,
273a33a7d73SArnd Bergmann 				cmd->size << 16 | cmd->tag);
274a33a7d73SArnd Bergmann 	out_be32(&prob->mfc_union_W.by32.mfc_class_cmd32,
275a33a7d73SArnd Bergmann 				cmd->class << 16 | cmd->cmd);
276a33a7d73SArnd Bergmann 	status = in_be32(&prob->mfc_union_W.by32.mfc_class_cmd32);
277a33a7d73SArnd Bergmann 	spin_unlock_irq(&ctx->spu->register_lock);
278a33a7d73SArnd Bergmann 
279a33a7d73SArnd Bergmann 	switch (status & 0xffff) {
280a33a7d73SArnd Bergmann 	case 0:
281a33a7d73SArnd Bergmann 		return 0;
282a33a7d73SArnd Bergmann 	case 2:
283a33a7d73SArnd Bergmann 		return -EAGAIN;
284a33a7d73SArnd Bergmann 	default:
285a33a7d73SArnd Bergmann 		return -EINVAL;
286a33a7d73SArnd Bergmann 	}
287a33a7d73SArnd Bergmann }
288a33a7d73SArnd Bergmann 
2898b3d6663SArnd Bergmann struct spu_context_ops spu_hw_ops = {
2908b3d6663SArnd Bergmann 	.mbox_read = spu_hw_mbox_read,
2918b3d6663SArnd Bergmann 	.mbox_stat_read = spu_hw_mbox_stat_read,
2923a843d7cSArnd Bergmann 	.mbox_stat_poll = spu_hw_mbox_stat_poll,
2938b3d6663SArnd Bergmann 	.ibox_read = spu_hw_ibox_read,
2948b3d6663SArnd Bergmann 	.wbox_write = spu_hw_wbox_write,
2958b3d6663SArnd Bergmann 	.signal1_read = spu_hw_signal1_read,
2968b3d6663SArnd Bergmann 	.signal1_write = spu_hw_signal1_write,
2978b3d6663SArnd Bergmann 	.signal2_read = spu_hw_signal2_read,
2988b3d6663SArnd Bergmann 	.signal2_write = spu_hw_signal2_write,
2998b3d6663SArnd Bergmann 	.signal1_type_set = spu_hw_signal1_type_set,
3008b3d6663SArnd Bergmann 	.signal1_type_get = spu_hw_signal1_type_get,
3018b3d6663SArnd Bergmann 	.signal2_type_set = spu_hw_signal2_type_set,
3028b3d6663SArnd Bergmann 	.signal2_type_get = spu_hw_signal2_type_get,
3038b3d6663SArnd Bergmann 	.npc_read = spu_hw_npc_read,
3048b3d6663SArnd Bergmann 	.npc_write = spu_hw_npc_write,
3058b3d6663SArnd Bergmann 	.status_read = spu_hw_status_read,
3068b3d6663SArnd Bergmann 	.get_ls = spu_hw_get_ls,
3075110459fSArnd Bergmann 	.runcntl_write = spu_hw_runcntl_write,
3085110459fSArnd Bergmann 	.runcntl_stop = spu_hw_runcntl_stop,
309a33a7d73SArnd Bergmann 	.set_mfc_query = spu_hw_set_mfc_query,
310a33a7d73SArnd Bergmann 	.read_mfc_tagstatus = spu_hw_read_mfc_tagstatus,
311a33a7d73SArnd Bergmann 	.get_mfc_free_elements = spu_hw_get_mfc_free_elements,
312a33a7d73SArnd Bergmann 	.send_mfc_command = spu_hw_send_mfc_command,
3138b3d6663SArnd Bergmann };
314