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/module.h>
228b3d6663SArnd Bergmann #include <linux/errno.h>
238b3d6663SArnd Bergmann #include <linux/sched.h>
248b3d6663SArnd Bergmann #include <linux/kernel.h>
258b3d6663SArnd Bergmann #include <linux/mm.h>
263a843d7cSArnd Bergmann #include <linux/poll.h>
278b3d6663SArnd Bergmann #include <linux/smp.h>
288b3d6663SArnd Bergmann #include <linux/smp_lock.h>
298b3d6663SArnd Bergmann #include <linux/stddef.h>
308b3d6663SArnd Bergmann #include <linux/unistd.h>
318b3d6663SArnd Bergmann 
328b3d6663SArnd Bergmann #include <asm/io.h>
338b3d6663SArnd Bergmann #include <asm/spu.h>
34540270d8SGeoff Levand #include <asm/spu_priv1.h>
358b3d6663SArnd Bergmann #include <asm/spu_csa.h>
368b3d6663SArnd Bergmann #include <asm/mmu_context.h>
378b3d6663SArnd Bergmann #include "spufs.h"
388b3d6663SArnd Bergmann 
398b3d6663SArnd Bergmann static int spu_hw_mbox_read(struct spu_context *ctx, u32 * data)
408b3d6663SArnd Bergmann {
418b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
428b3d6663SArnd Bergmann 	struct spu_problem __iomem *prob = spu->problem;
438b3d6663SArnd Bergmann 	u32 mbox_stat;
448b3d6663SArnd Bergmann 	int ret = 0;
458b3d6663SArnd Bergmann 
468b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
478b3d6663SArnd Bergmann 	mbox_stat = in_be32(&prob->mb_stat_R);
488b3d6663SArnd Bergmann 	if (mbox_stat & 0x0000ff) {
498b3d6663SArnd Bergmann 		*data = in_be32(&prob->pu_mb_R);
508b3d6663SArnd Bergmann 		ret = 4;
518b3d6663SArnd Bergmann 	}
528b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
538b3d6663SArnd Bergmann 	return ret;
548b3d6663SArnd Bergmann }
558b3d6663SArnd Bergmann 
568b3d6663SArnd Bergmann static u32 spu_hw_mbox_stat_read(struct spu_context *ctx)
578b3d6663SArnd Bergmann {
588b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->mb_stat_R);
598b3d6663SArnd Bergmann }
608b3d6663SArnd Bergmann 
613a843d7cSArnd Bergmann static unsigned int spu_hw_mbox_stat_poll(struct spu_context *ctx,
623a843d7cSArnd Bergmann 					  unsigned int events)
633a843d7cSArnd Bergmann {
643a843d7cSArnd Bergmann 	struct spu *spu = ctx->spu;
653a843d7cSArnd Bergmann 	int ret = 0;
663a843d7cSArnd Bergmann 	u32 stat;
673a843d7cSArnd Bergmann 
683a843d7cSArnd Bergmann 	spin_lock_irq(&spu->register_lock);
693a843d7cSArnd Bergmann 	stat = in_be32(&spu->problem->mb_stat_R);
703a843d7cSArnd Bergmann 
713a843d7cSArnd Bergmann 	/* if the requested event is there, return the poll
723a843d7cSArnd Bergmann 	   mask, otherwise enable the interrupt to get notified,
733a843d7cSArnd Bergmann 	   but first mark any pending interrupts as done so
743a843d7cSArnd Bergmann 	   we don't get woken up unnecessarily */
753a843d7cSArnd Bergmann 
763a843d7cSArnd Bergmann 	if (events & (POLLIN | POLLRDNORM)) {
773a843d7cSArnd Bergmann 		if (stat & 0xff0000)
783a843d7cSArnd Bergmann 			ret |= POLLIN | POLLRDNORM;
793a843d7cSArnd Bergmann 		else {
80f0831accSArnd Bergmann 			spu_int_stat_clear(spu, 2, 0x1);
81f0831accSArnd Bergmann 			spu_int_mask_or(spu, 2, 0x1);
823a843d7cSArnd Bergmann 		}
833a843d7cSArnd Bergmann 	}
843a843d7cSArnd Bergmann 	if (events & (POLLOUT | POLLWRNORM)) {
853a843d7cSArnd Bergmann 		if (stat & 0x00ff00)
863a843d7cSArnd Bergmann 			ret = POLLOUT | POLLWRNORM;
873a843d7cSArnd Bergmann 		else {
88f0831accSArnd Bergmann 			spu_int_stat_clear(spu, 2, 0x10);
89f0831accSArnd Bergmann 			spu_int_mask_or(spu, 2, 0x10);
903a843d7cSArnd Bergmann 		}
913a843d7cSArnd Bergmann 	}
923a843d7cSArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
933a843d7cSArnd Bergmann 	return ret;
943a843d7cSArnd Bergmann }
953a843d7cSArnd Bergmann 
968b3d6663SArnd Bergmann static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data)
978b3d6663SArnd Bergmann {
988b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
998b3d6663SArnd Bergmann 	struct spu_problem __iomem *prob = spu->problem;
1008b3d6663SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = spu->priv2;
1018b3d6663SArnd Bergmann 	int ret;
1028b3d6663SArnd Bergmann 
1038b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1048b3d6663SArnd Bergmann 	if (in_be32(&prob->mb_stat_R) & 0xff0000) {
1058b3d6663SArnd Bergmann 		/* read the first available word */
1068b3d6663SArnd Bergmann 		*data = in_be64(&priv2->puint_mb_R);
1078b3d6663SArnd Bergmann 		ret = 4;
1088b3d6663SArnd Bergmann 	} else {
1098b3d6663SArnd Bergmann 		/* make sure we get woken up by the interrupt */
110f0831accSArnd Bergmann 		spu_int_mask_or(spu, 2, 0x1);
1118b3d6663SArnd Bergmann 		ret = 0;
1128b3d6663SArnd Bergmann 	}
1138b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1148b3d6663SArnd Bergmann 	return ret;
1158b3d6663SArnd Bergmann }
1168b3d6663SArnd Bergmann 
1178b3d6663SArnd Bergmann static int spu_hw_wbox_write(struct spu_context *ctx, u32 data)
1188b3d6663SArnd Bergmann {
1198b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1208b3d6663SArnd Bergmann 	struct spu_problem __iomem *prob = spu->problem;
1218b3d6663SArnd Bergmann 	int ret;
1228b3d6663SArnd Bergmann 
1238b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1248b3d6663SArnd Bergmann 	if (in_be32(&prob->mb_stat_R) & 0x00ff00) {
1258b3d6663SArnd Bergmann 		/* we have space to write wbox_data to */
1268b3d6663SArnd Bergmann 		out_be32(&prob->spu_mb_W, data);
1278b3d6663SArnd Bergmann 		ret = 4;
1288b3d6663SArnd Bergmann 	} else {
1298b3d6663SArnd Bergmann 		/* make sure we get woken up by the interrupt when space
1308b3d6663SArnd Bergmann 		   becomes available */
131f0831accSArnd Bergmann 		spu_int_mask_or(spu, 2, 0x10);
1328b3d6663SArnd Bergmann 		ret = 0;
1338b3d6663SArnd Bergmann 	}
1348b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1358b3d6663SArnd Bergmann 	return ret;
1368b3d6663SArnd Bergmann }
1378b3d6663SArnd Bergmann 
1388b3d6663SArnd Bergmann static void spu_hw_signal1_write(struct spu_context *ctx, u32 data)
1398b3d6663SArnd Bergmann {
1408b3d6663SArnd Bergmann 	out_be32(&ctx->spu->problem->signal_notify1, data);
1418b3d6663SArnd Bergmann }
1428b3d6663SArnd Bergmann 
1438b3d6663SArnd Bergmann static void spu_hw_signal2_write(struct spu_context *ctx, u32 data)
1448b3d6663SArnd Bergmann {
1458b3d6663SArnd Bergmann 	out_be32(&ctx->spu->problem->signal_notify2, data);
1468b3d6663SArnd Bergmann }
1478b3d6663SArnd Bergmann 
1488b3d6663SArnd Bergmann static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val)
1498b3d6663SArnd Bergmann {
1508b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1518b3d6663SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = spu->priv2;
1528b3d6663SArnd Bergmann 	u64 tmp;
1538b3d6663SArnd Bergmann 
1548b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1558b3d6663SArnd Bergmann 	tmp = in_be64(&priv2->spu_cfg_RW);
1568b3d6663SArnd Bergmann 	if (val)
1578b3d6663SArnd Bergmann 		tmp |= 1;
1588b3d6663SArnd Bergmann 	else
1598b3d6663SArnd Bergmann 		tmp &= ~1;
1608b3d6663SArnd Bergmann 	out_be64(&priv2->spu_cfg_RW, tmp);
1618b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1628b3d6663SArnd Bergmann }
1638b3d6663SArnd Bergmann 
1648b3d6663SArnd Bergmann static u64 spu_hw_signal1_type_get(struct spu_context *ctx)
1658b3d6663SArnd Bergmann {
1668b3d6663SArnd Bergmann 	return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0);
1678b3d6663SArnd Bergmann }
1688b3d6663SArnd Bergmann 
1698b3d6663SArnd Bergmann static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val)
1708b3d6663SArnd Bergmann {
1718b3d6663SArnd Bergmann 	struct spu *spu = ctx->spu;
1728b3d6663SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = spu->priv2;
1738b3d6663SArnd Bergmann 	u64 tmp;
1748b3d6663SArnd Bergmann 
1758b3d6663SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
1768b3d6663SArnd Bergmann 	tmp = in_be64(&priv2->spu_cfg_RW);
1778b3d6663SArnd Bergmann 	if (val)
1788b3d6663SArnd Bergmann 		tmp |= 2;
1798b3d6663SArnd Bergmann 	else
1808b3d6663SArnd Bergmann 		tmp &= ~2;
1818b3d6663SArnd Bergmann 	out_be64(&priv2->spu_cfg_RW, tmp);
1828b3d6663SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
1838b3d6663SArnd Bergmann }
1848b3d6663SArnd Bergmann 
1858b3d6663SArnd Bergmann static u64 spu_hw_signal2_type_get(struct spu_context *ctx)
1868b3d6663SArnd Bergmann {
1878b3d6663SArnd Bergmann 	return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0);
1888b3d6663SArnd Bergmann }
1898b3d6663SArnd Bergmann 
1908b3d6663SArnd Bergmann static u32 spu_hw_npc_read(struct spu_context *ctx)
1918b3d6663SArnd Bergmann {
1928b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->spu_npc_RW);
1938b3d6663SArnd Bergmann }
1948b3d6663SArnd Bergmann 
1958b3d6663SArnd Bergmann static void spu_hw_npc_write(struct spu_context *ctx, u32 val)
1968b3d6663SArnd Bergmann {
1978b3d6663SArnd Bergmann 	out_be32(&ctx->spu->problem->spu_npc_RW, val);
1988b3d6663SArnd Bergmann }
1998b3d6663SArnd Bergmann 
2008b3d6663SArnd Bergmann static u32 spu_hw_status_read(struct spu_context *ctx)
2018b3d6663SArnd Bergmann {
2028b3d6663SArnd Bergmann 	return in_be32(&ctx->spu->problem->spu_status_R);
2038b3d6663SArnd Bergmann }
2048b3d6663SArnd Bergmann 
2058b3d6663SArnd Bergmann static char *spu_hw_get_ls(struct spu_context *ctx)
2068b3d6663SArnd Bergmann {
2078b3d6663SArnd Bergmann 	return ctx->spu->local_store;
2088b3d6663SArnd Bergmann }
2098b3d6663SArnd Bergmann 
2103960c260SJeremy Kerr static u32 spu_hw_runcntl_read(struct spu_context *ctx)
2113960c260SJeremy Kerr {
2123960c260SJeremy Kerr 	return in_be32(&ctx->spu->problem->spu_runcntl_RW);
2133960c260SJeremy Kerr }
2143960c260SJeremy Kerr 
2155110459fSArnd Bergmann static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val)
2165110459fSArnd Bergmann {
2175737edd1SMark Nutter 	spin_lock_irq(&ctx->spu->register_lock);
2185737edd1SMark Nutter 	if (val & SPU_RUNCNTL_ISOLATE)
2195737edd1SMark Nutter 		out_be64(&ctx->spu->priv2->spu_privcntl_RW, 4LL);
2205110459fSArnd Bergmann 	out_be32(&ctx->spu->problem->spu_runcntl_RW, val);
2215737edd1SMark Nutter 	spin_unlock_irq(&ctx->spu->register_lock);
2225110459fSArnd Bergmann }
2235110459fSArnd Bergmann 
224ee2d7340SArnd Bergmann static void spu_hw_master_start(struct spu_context *ctx)
2255110459fSArnd Bergmann {
226ee2d7340SArnd Bergmann 	struct spu *spu = ctx->spu;
227ee2d7340SArnd Bergmann 	u64 sr1;
228ee2d7340SArnd Bergmann 
229ee2d7340SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
230ee2d7340SArnd Bergmann 	sr1 = spu_mfc_sr1_get(spu) | MFC_STATE1_MASTER_RUN_CONTROL_MASK;
231ee2d7340SArnd Bergmann 	spu_mfc_sr1_set(spu, sr1);
232ee2d7340SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
233ee2d7340SArnd Bergmann }
234ee2d7340SArnd Bergmann 
235ee2d7340SArnd Bergmann static void spu_hw_master_stop(struct spu_context *ctx)
236ee2d7340SArnd Bergmann {
237ee2d7340SArnd Bergmann 	struct spu *spu = ctx->spu;
238ee2d7340SArnd Bergmann 	u64 sr1;
239ee2d7340SArnd Bergmann 
240ee2d7340SArnd Bergmann 	spin_lock_irq(&spu->register_lock);
241ee2d7340SArnd Bergmann 	sr1 = spu_mfc_sr1_get(spu) & ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
242ee2d7340SArnd Bergmann 	spu_mfc_sr1_set(spu, sr1);
243ee2d7340SArnd Bergmann 	spin_unlock_irq(&spu->register_lock);
2445110459fSArnd Bergmann }
2455110459fSArnd Bergmann 
246a33a7d73SArnd Bergmann static int spu_hw_set_mfc_query(struct spu_context * ctx, u32 mask, u32 mode)
247a33a7d73SArnd Bergmann {
248ed2bfcd2SAl Viro 	struct spu_problem __iomem *prob = ctx->spu->problem;
249a33a7d73SArnd Bergmann 	int ret;
250a33a7d73SArnd Bergmann 
251a33a7d73SArnd Bergmann 	spin_lock_irq(&ctx->spu->register_lock);
252a33a7d73SArnd Bergmann 	ret = -EAGAIN;
253a33a7d73SArnd Bergmann 	if (in_be32(&prob->dma_querytype_RW))
254a33a7d73SArnd Bergmann 		goto out;
255a33a7d73SArnd Bergmann 	ret = 0;
256a33a7d73SArnd Bergmann 	out_be32(&prob->dma_querymask_RW, mask);
257a33a7d73SArnd Bergmann 	out_be32(&prob->dma_querytype_RW, mode);
258a33a7d73SArnd Bergmann out:
259a33a7d73SArnd Bergmann 	spin_unlock_irq(&ctx->spu->register_lock);
260a33a7d73SArnd Bergmann 	return ret;
261a33a7d73SArnd Bergmann }
262a33a7d73SArnd Bergmann 
263a33a7d73SArnd Bergmann static u32 spu_hw_read_mfc_tagstatus(struct spu_context * ctx)
264a33a7d73SArnd Bergmann {
265a33a7d73SArnd Bergmann 	return in_be32(&ctx->spu->problem->dma_tagstatus_R);
266a33a7d73SArnd Bergmann }
267a33a7d73SArnd Bergmann 
268a33a7d73SArnd Bergmann static u32 spu_hw_get_mfc_free_elements(struct spu_context *ctx)
269a33a7d73SArnd Bergmann {
270a33a7d73SArnd Bergmann 	return in_be32(&ctx->spu->problem->dma_qstatus_R);
271a33a7d73SArnd Bergmann }
272a33a7d73SArnd Bergmann 
273a33a7d73SArnd Bergmann static int spu_hw_send_mfc_command(struct spu_context *ctx,
274a33a7d73SArnd Bergmann 					struct mfc_dma_command *cmd)
275a33a7d73SArnd Bergmann {
276a33a7d73SArnd Bergmann 	u32 status;
277ed2bfcd2SAl Viro 	struct spu_problem __iomem *prob = ctx->spu->problem;
278a33a7d73SArnd Bergmann 
279a33a7d73SArnd Bergmann 	spin_lock_irq(&ctx->spu->register_lock);
280a33a7d73SArnd Bergmann 	out_be32(&prob->mfc_lsa_W, cmd->lsa);
281a33a7d73SArnd Bergmann 	out_be64(&prob->mfc_ea_W, cmd->ea);
282a33a7d73SArnd Bergmann 	out_be32(&prob->mfc_union_W.by32.mfc_size_tag32,
283a33a7d73SArnd Bergmann 				cmd->size << 16 | cmd->tag);
284a33a7d73SArnd Bergmann 	out_be32(&prob->mfc_union_W.by32.mfc_class_cmd32,
285a33a7d73SArnd Bergmann 				cmd->class << 16 | cmd->cmd);
286a33a7d73SArnd Bergmann 	status = in_be32(&prob->mfc_union_W.by32.mfc_class_cmd32);
287a33a7d73SArnd Bergmann 	spin_unlock_irq(&ctx->spu->register_lock);
288a33a7d73SArnd Bergmann 
289a33a7d73SArnd Bergmann 	switch (status & 0xffff) {
290a33a7d73SArnd Bergmann 	case 0:
291a33a7d73SArnd Bergmann 		return 0;
292a33a7d73SArnd Bergmann 	case 2:
293a33a7d73SArnd Bergmann 		return -EAGAIN;
294a33a7d73SArnd Bergmann 	default:
295a33a7d73SArnd Bergmann 		return -EINVAL;
296a33a7d73SArnd Bergmann 	}
297a33a7d73SArnd Bergmann }
298a33a7d73SArnd Bergmann 
29957dace23SArnd Bergmann static void spu_hw_restart_dma(struct spu_context *ctx)
30057dace23SArnd Bergmann {
30157dace23SArnd Bergmann 	struct spu_priv2 __iomem *priv2 = ctx->spu->priv2;
30257dace23SArnd Bergmann 
30357dace23SArnd Bergmann 	if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &ctx->spu->flags))
30457dace23SArnd Bergmann 		out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
30557dace23SArnd Bergmann }
30657dace23SArnd Bergmann 
3078b3d6663SArnd Bergmann struct spu_context_ops spu_hw_ops = {
3088b3d6663SArnd Bergmann 	.mbox_read = spu_hw_mbox_read,
3098b3d6663SArnd Bergmann 	.mbox_stat_read = spu_hw_mbox_stat_read,
3103a843d7cSArnd Bergmann 	.mbox_stat_poll = spu_hw_mbox_stat_poll,
3118b3d6663SArnd Bergmann 	.ibox_read = spu_hw_ibox_read,
3128b3d6663SArnd Bergmann 	.wbox_write = spu_hw_wbox_write,
3138b3d6663SArnd Bergmann 	.signal1_write = spu_hw_signal1_write,
3148b3d6663SArnd Bergmann 	.signal2_write = spu_hw_signal2_write,
3158b3d6663SArnd Bergmann 	.signal1_type_set = spu_hw_signal1_type_set,
3168b3d6663SArnd Bergmann 	.signal1_type_get = spu_hw_signal1_type_get,
3178b3d6663SArnd Bergmann 	.signal2_type_set = spu_hw_signal2_type_set,
3188b3d6663SArnd Bergmann 	.signal2_type_get = spu_hw_signal2_type_get,
3198b3d6663SArnd Bergmann 	.npc_read = spu_hw_npc_read,
3208b3d6663SArnd Bergmann 	.npc_write = spu_hw_npc_write,
3218b3d6663SArnd Bergmann 	.status_read = spu_hw_status_read,
3228b3d6663SArnd Bergmann 	.get_ls = spu_hw_get_ls,
3233960c260SJeremy Kerr 	.runcntl_read = spu_hw_runcntl_read,
3245110459fSArnd Bergmann 	.runcntl_write = spu_hw_runcntl_write,
325ee2d7340SArnd Bergmann 	.master_start = spu_hw_master_start,
326ee2d7340SArnd Bergmann 	.master_stop = spu_hw_master_stop,
327a33a7d73SArnd Bergmann 	.set_mfc_query = spu_hw_set_mfc_query,
328a33a7d73SArnd Bergmann 	.read_mfc_tagstatus = spu_hw_read_mfc_tagstatus,
329a33a7d73SArnd Bergmann 	.get_mfc_free_elements = spu_hw_get_mfc_free_elements,
330a33a7d73SArnd Bergmann 	.send_mfc_command = spu_hw_send_mfc_command,
33157dace23SArnd Bergmann 	.restart_dma = spu_hw_restart_dma,
3328b3d6663SArnd Bergmann };
333