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> 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 struct spu_priv1 __iomem *priv1 = spu->priv1; 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 { 813a843d7cSArnd Bergmann out_be64(&priv1->int_stat_class2_RW, 0x1); 823a843d7cSArnd Bergmann out_be64(&priv1->int_mask_class2_RW, 833a843d7cSArnd Bergmann in_be64(&priv1->int_mask_class2_RW) | 0x1); 843a843d7cSArnd Bergmann } 853a843d7cSArnd Bergmann } 863a843d7cSArnd Bergmann if (events & (POLLOUT | POLLWRNORM)) { 873a843d7cSArnd Bergmann if (stat & 0x00ff00) 883a843d7cSArnd Bergmann ret = POLLOUT | POLLWRNORM; 893a843d7cSArnd Bergmann else { 903a843d7cSArnd Bergmann out_be64(&priv1->int_stat_class2_RW, 0x10); 913a843d7cSArnd Bergmann out_be64(&priv1->int_mask_class2_RW, 923a843d7cSArnd Bergmann in_be64(&priv1->int_mask_class2_RW) | 0x10); 933a843d7cSArnd Bergmann } 943a843d7cSArnd Bergmann } 953a843d7cSArnd Bergmann spin_unlock_irq(&spu->register_lock); 963a843d7cSArnd Bergmann return ret; 973a843d7cSArnd Bergmann } 983a843d7cSArnd Bergmann 998b3d6663SArnd Bergmann static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data) 1008b3d6663SArnd Bergmann { 1018b3d6663SArnd Bergmann struct spu *spu = ctx->spu; 1028b3d6663SArnd Bergmann struct spu_problem __iomem *prob = spu->problem; 1038b3d6663SArnd Bergmann struct spu_priv1 __iomem *priv1 = spu->priv1; 1048b3d6663SArnd Bergmann struct spu_priv2 __iomem *priv2 = spu->priv2; 1058b3d6663SArnd Bergmann int ret; 1068b3d6663SArnd Bergmann 1078b3d6663SArnd Bergmann spin_lock_irq(&spu->register_lock); 1088b3d6663SArnd Bergmann if (in_be32(&prob->mb_stat_R) & 0xff0000) { 1098b3d6663SArnd Bergmann /* read the first available word */ 1108b3d6663SArnd Bergmann *data = in_be64(&priv2->puint_mb_R); 1118b3d6663SArnd Bergmann ret = 4; 1128b3d6663SArnd Bergmann } else { 1138b3d6663SArnd Bergmann /* make sure we get woken up by the interrupt */ 1148b3d6663SArnd Bergmann out_be64(&priv1->int_mask_class2_RW, 1158b3d6663SArnd Bergmann in_be64(&priv1->int_mask_class2_RW) | 0x1); 1168b3d6663SArnd Bergmann ret = 0; 1178b3d6663SArnd Bergmann } 1188b3d6663SArnd Bergmann spin_unlock_irq(&spu->register_lock); 1198b3d6663SArnd Bergmann return ret; 1208b3d6663SArnd Bergmann } 1218b3d6663SArnd Bergmann 1228b3d6663SArnd Bergmann static int spu_hw_wbox_write(struct spu_context *ctx, u32 data) 1238b3d6663SArnd Bergmann { 1248b3d6663SArnd Bergmann struct spu *spu = ctx->spu; 1258b3d6663SArnd Bergmann struct spu_problem __iomem *prob = spu->problem; 1268b3d6663SArnd Bergmann struct spu_priv1 __iomem *priv1 = spu->priv1; 1278b3d6663SArnd Bergmann int ret; 1288b3d6663SArnd Bergmann 1298b3d6663SArnd Bergmann spin_lock_irq(&spu->register_lock); 1308b3d6663SArnd Bergmann if (in_be32(&prob->mb_stat_R) & 0x00ff00) { 1318b3d6663SArnd Bergmann /* we have space to write wbox_data to */ 1328b3d6663SArnd Bergmann out_be32(&prob->spu_mb_W, data); 1338b3d6663SArnd Bergmann ret = 4; 1348b3d6663SArnd Bergmann } else { 1358b3d6663SArnd Bergmann /* make sure we get woken up by the interrupt when space 1368b3d6663SArnd Bergmann becomes available */ 1378b3d6663SArnd Bergmann out_be64(&priv1->int_mask_class2_RW, 1388b3d6663SArnd Bergmann in_be64(&priv1->int_mask_class2_RW) | 0x10); 1398b3d6663SArnd Bergmann ret = 0; 1408b3d6663SArnd Bergmann } 1418b3d6663SArnd Bergmann spin_unlock_irq(&spu->register_lock); 1428b3d6663SArnd Bergmann return ret; 1438b3d6663SArnd Bergmann } 1448b3d6663SArnd Bergmann 1458b3d6663SArnd Bergmann static u32 spu_hw_signal1_read(struct spu_context *ctx) 1468b3d6663SArnd Bergmann { 1478b3d6663SArnd Bergmann return in_be32(&ctx->spu->problem->signal_notify1); 1488b3d6663SArnd Bergmann } 1498b3d6663SArnd Bergmann 1508b3d6663SArnd Bergmann static void spu_hw_signal1_write(struct spu_context *ctx, u32 data) 1518b3d6663SArnd Bergmann { 1528b3d6663SArnd Bergmann out_be32(&ctx->spu->problem->signal_notify1, data); 1538b3d6663SArnd Bergmann } 1548b3d6663SArnd Bergmann 1558b3d6663SArnd Bergmann static u32 spu_hw_signal2_read(struct spu_context *ctx) 1568b3d6663SArnd Bergmann { 1578b3d6663SArnd Bergmann return in_be32(&ctx->spu->problem->signal_notify1); 1588b3d6663SArnd Bergmann } 1598b3d6663SArnd Bergmann 1608b3d6663SArnd Bergmann static void spu_hw_signal2_write(struct spu_context *ctx, u32 data) 1618b3d6663SArnd Bergmann { 1628b3d6663SArnd Bergmann out_be32(&ctx->spu->problem->signal_notify2, data); 1638b3d6663SArnd Bergmann } 1648b3d6663SArnd Bergmann 1658b3d6663SArnd Bergmann static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val) 1668b3d6663SArnd Bergmann { 1678b3d6663SArnd Bergmann struct spu *spu = ctx->spu; 1688b3d6663SArnd Bergmann struct spu_priv2 __iomem *priv2 = spu->priv2; 1698b3d6663SArnd Bergmann u64 tmp; 1708b3d6663SArnd Bergmann 1718b3d6663SArnd Bergmann spin_lock_irq(&spu->register_lock); 1728b3d6663SArnd Bergmann tmp = in_be64(&priv2->spu_cfg_RW); 1738b3d6663SArnd Bergmann if (val) 1748b3d6663SArnd Bergmann tmp |= 1; 1758b3d6663SArnd Bergmann else 1768b3d6663SArnd Bergmann tmp &= ~1; 1778b3d6663SArnd Bergmann out_be64(&priv2->spu_cfg_RW, tmp); 1788b3d6663SArnd Bergmann spin_unlock_irq(&spu->register_lock); 1798b3d6663SArnd Bergmann } 1808b3d6663SArnd Bergmann 1818b3d6663SArnd Bergmann static u64 spu_hw_signal1_type_get(struct spu_context *ctx) 1828b3d6663SArnd Bergmann { 1838b3d6663SArnd Bergmann return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0); 1848b3d6663SArnd Bergmann } 1858b3d6663SArnd Bergmann 1868b3d6663SArnd Bergmann static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val) 1878b3d6663SArnd Bergmann { 1888b3d6663SArnd Bergmann struct spu *spu = ctx->spu; 1898b3d6663SArnd Bergmann struct spu_priv2 __iomem *priv2 = spu->priv2; 1908b3d6663SArnd Bergmann u64 tmp; 1918b3d6663SArnd Bergmann 1928b3d6663SArnd Bergmann spin_lock_irq(&spu->register_lock); 1938b3d6663SArnd Bergmann tmp = in_be64(&priv2->spu_cfg_RW); 1948b3d6663SArnd Bergmann if (val) 1958b3d6663SArnd Bergmann tmp |= 2; 1968b3d6663SArnd Bergmann else 1978b3d6663SArnd Bergmann tmp &= ~2; 1988b3d6663SArnd Bergmann out_be64(&priv2->spu_cfg_RW, tmp); 1998b3d6663SArnd Bergmann spin_unlock_irq(&spu->register_lock); 2008b3d6663SArnd Bergmann } 2018b3d6663SArnd Bergmann 2028b3d6663SArnd Bergmann static u64 spu_hw_signal2_type_get(struct spu_context *ctx) 2038b3d6663SArnd Bergmann { 2048b3d6663SArnd Bergmann return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0); 2058b3d6663SArnd Bergmann } 2068b3d6663SArnd Bergmann 2078b3d6663SArnd Bergmann static u32 spu_hw_npc_read(struct spu_context *ctx) 2088b3d6663SArnd Bergmann { 2098b3d6663SArnd Bergmann return in_be32(&ctx->spu->problem->spu_npc_RW); 2108b3d6663SArnd Bergmann } 2118b3d6663SArnd Bergmann 2128b3d6663SArnd Bergmann static void spu_hw_npc_write(struct spu_context *ctx, u32 val) 2138b3d6663SArnd Bergmann { 2148b3d6663SArnd Bergmann out_be32(&ctx->spu->problem->spu_npc_RW, val); 2158b3d6663SArnd Bergmann } 2168b3d6663SArnd Bergmann 2178b3d6663SArnd Bergmann static u32 spu_hw_status_read(struct spu_context *ctx) 2188b3d6663SArnd Bergmann { 2198b3d6663SArnd Bergmann return in_be32(&ctx->spu->problem->spu_status_R); 2208b3d6663SArnd Bergmann } 2218b3d6663SArnd Bergmann 2228b3d6663SArnd Bergmann static char *spu_hw_get_ls(struct spu_context *ctx) 2238b3d6663SArnd Bergmann { 2248b3d6663SArnd Bergmann return ctx->spu->local_store; 2258b3d6663SArnd Bergmann } 2268b3d6663SArnd Bergmann 2275110459fSArnd Bergmann static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val) 2285110459fSArnd Bergmann { 2295110459fSArnd Bergmann eieio(); 2305110459fSArnd Bergmann out_be32(&ctx->spu->problem->spu_runcntl_RW, val); 2315110459fSArnd Bergmann } 2325110459fSArnd Bergmann 2335110459fSArnd Bergmann static void spu_hw_runcntl_stop(struct spu_context *ctx) 2345110459fSArnd Bergmann { 2355110459fSArnd Bergmann spin_lock_irq(&ctx->spu->register_lock); 2365110459fSArnd Bergmann out_be32(&ctx->spu->problem->spu_runcntl_RW, SPU_RUNCNTL_STOP); 2375110459fSArnd Bergmann while (in_be32(&ctx->spu->problem->spu_status_R) & SPU_STATUS_RUNNING) 2385110459fSArnd Bergmann cpu_relax(); 2395110459fSArnd Bergmann spin_unlock_irq(&ctx->spu->register_lock); 2405110459fSArnd Bergmann } 2415110459fSArnd Bergmann 2428b3d6663SArnd Bergmann struct spu_context_ops spu_hw_ops = { 2438b3d6663SArnd Bergmann .mbox_read = spu_hw_mbox_read, 2448b3d6663SArnd Bergmann .mbox_stat_read = spu_hw_mbox_stat_read, 2453a843d7cSArnd Bergmann .mbox_stat_poll = spu_hw_mbox_stat_poll, 2468b3d6663SArnd Bergmann .ibox_read = spu_hw_ibox_read, 2478b3d6663SArnd Bergmann .wbox_write = spu_hw_wbox_write, 2488b3d6663SArnd Bergmann .signal1_read = spu_hw_signal1_read, 2498b3d6663SArnd Bergmann .signal1_write = spu_hw_signal1_write, 2508b3d6663SArnd Bergmann .signal2_read = spu_hw_signal2_read, 2518b3d6663SArnd Bergmann .signal2_write = spu_hw_signal2_write, 2528b3d6663SArnd Bergmann .signal1_type_set = spu_hw_signal1_type_set, 2538b3d6663SArnd Bergmann .signal1_type_get = spu_hw_signal1_type_get, 2548b3d6663SArnd Bergmann .signal2_type_set = spu_hw_signal2_type_set, 2558b3d6663SArnd Bergmann .signal2_type_get = spu_hw_signal2_type_get, 2568b3d6663SArnd Bergmann .npc_read = spu_hw_npc_read, 2578b3d6663SArnd Bergmann .npc_write = spu_hw_npc_write, 2588b3d6663SArnd Bergmann .status_read = spu_hw_status_read, 2598b3d6663SArnd Bergmann .get_ls = spu_hw_get_ls, 2605110459fSArnd Bergmann .runcntl_write = spu_hw_runcntl_write, 2615110459fSArnd Bergmann .runcntl_stop = spu_hw_runcntl_stop, 2628b3d6663SArnd Bergmann }; 263