1 /* 2 * Tegra host1x Interrupt Management 3 * 4 * Copyright (C) 2010 Google, Inc. 5 * Copyright (c) 2010-2013, NVIDIA Corporation. 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms and conditions of the GNU General Public License, 9 * version 2, as published by the Free Software Foundation. 10 * 11 * This program is distributed in the hope it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 14 * more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 #include <linux/interrupt.h> 21 #include <linux/irq.h> 22 #include <linux/io.h> 23 #include <asm/mach/irq.h> 24 25 #include "intr.h" 26 #include "dev.h" 27 28 /* 29 * Sync point threshold interrupt service function 30 * Handles sync point threshold triggers, in interrupt context 31 */ 32 static void host1x_intr_syncpt_handle(struct host1x_syncpt *syncpt) 33 { 34 unsigned int id = syncpt->id; 35 struct host1x *host = syncpt->host; 36 37 host1x_sync_writel(host, BIT_MASK(id), 38 HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(BIT_WORD(id))); 39 host1x_sync_writel(host, BIT_MASK(id), 40 HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(BIT_WORD(id))); 41 42 queue_work(host->intr_wq, &syncpt->intr.work); 43 } 44 45 static irqreturn_t syncpt_thresh_isr(int irq, void *dev_id) 46 { 47 struct host1x *host = dev_id; 48 unsigned long reg; 49 int i, id; 50 51 for (i = 0; i <= BIT_WORD(host->info->nb_pts); i++) { 52 reg = host1x_sync_readl(host, 53 HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i)); 54 for_each_set_bit(id, ®, BITS_PER_LONG) { 55 struct host1x_syncpt *syncpt = 56 host->syncpt + (i * BITS_PER_LONG + id); 57 host1x_intr_syncpt_handle(syncpt); 58 } 59 } 60 61 return IRQ_HANDLED; 62 } 63 64 static void _host1x_intr_disable_all_syncpt_intrs(struct host1x *host) 65 { 66 u32 i; 67 68 for (i = 0; i <= BIT_WORD(host->info->nb_pts); ++i) { 69 host1x_sync_writel(host, 0xffffffffu, 70 HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(i)); 71 host1x_sync_writel(host, 0xffffffffu, 72 HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(i)); 73 } 74 } 75 76 static int _host1x_intr_init_host_sync(struct host1x *host, u32 cpm, 77 void (*syncpt_thresh_work)(struct work_struct *)) 78 { 79 int i, err; 80 81 host1x_hw_intr_disable_all_syncpt_intrs(host); 82 83 for (i = 0; i < host->info->nb_pts; i++) 84 INIT_WORK(&host->syncpt[i].intr.work, syncpt_thresh_work); 85 86 err = devm_request_irq(host->dev, host->intr_syncpt_irq, 87 syncpt_thresh_isr, IRQF_SHARED, 88 "host1x_syncpt", host); 89 if (IS_ERR_VALUE(err)) { 90 WARN_ON(1); 91 return err; 92 } 93 94 /* disable the ip_busy_timeout. this prevents write drops */ 95 host1x_sync_writel(host, 0, HOST1X_SYNC_IP_BUSY_TIMEOUT); 96 97 /* 98 * increase the auto-ack timout to the maximum value. 2d will hang 99 * otherwise on Tegra2. 100 */ 101 host1x_sync_writel(host, 0xff, HOST1X_SYNC_CTXSW_TIMEOUT_CFG); 102 103 /* update host clocks per usec */ 104 host1x_sync_writel(host, cpm, HOST1X_SYNC_USEC_CLK); 105 106 return 0; 107 } 108 109 static void _host1x_intr_set_syncpt_threshold(struct host1x *host, 110 u32 id, u32 thresh) 111 { 112 host1x_sync_writel(host, thresh, HOST1X_SYNC_SYNCPT_INT_THRESH(id)); 113 } 114 115 static void _host1x_intr_enable_syncpt_intr(struct host1x *host, u32 id) 116 { 117 host1x_sync_writel(host, BIT_MASK(id), 118 HOST1X_SYNC_SYNCPT_THRESH_INT_ENABLE_CPU0(BIT_WORD(id))); 119 } 120 121 static void _host1x_intr_disable_syncpt_intr(struct host1x *host, u32 id) 122 { 123 host1x_sync_writel(host, BIT_MASK(id), 124 HOST1X_SYNC_SYNCPT_THRESH_INT_DISABLE(BIT_WORD(id))); 125 host1x_sync_writel(host, BIT_MASK(id), 126 HOST1X_SYNC_SYNCPT_THRESH_CPU0_INT_STATUS(BIT_WORD(id))); 127 } 128 129 static int _host1x_free_syncpt_irq(struct host1x *host) 130 { 131 devm_free_irq(host->dev, host->intr_syncpt_irq, host); 132 flush_workqueue(host->intr_wq); 133 return 0; 134 } 135 136 static const struct host1x_intr_ops host1x_intr_ops = { 137 .init_host_sync = _host1x_intr_init_host_sync, 138 .set_syncpt_threshold = _host1x_intr_set_syncpt_threshold, 139 .enable_syncpt_intr = _host1x_intr_enable_syncpt_intr, 140 .disable_syncpt_intr = _host1x_intr_disable_syncpt_intr, 141 .disable_all_syncpt_intrs = _host1x_intr_disable_all_syncpt_intrs, 142 .free_syncpt_irq = _host1x_free_syncpt_irq, 143 }; 144