aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorZhenyu Wang <zhenyuw@linux.intel.com>2009-11-10 12:25:25 -0500
committerEric Anholt <eric@anholt.net>2010-02-26 16:23:19 -0500
commit14bc490bbdf1b194ad1f5f3d2a0a27edfdf78986 (patch)
treeec3aa6b118d7f9750bdb12e45af122748978a6d6 /drivers
parent21099537dbacc5c8999d833e6bfd1b72edd89189 (diff)
drm/i915, agp/intel: Fix stolen memory size on Sandybridge
New memory control config reg at 0x50 should be used for stolen memory size detection on Sandybridge. Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com> Signed-off-by: Eric Anholt <eric@anholt.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/char/agp/intel-agp.c78
-rw-r--r--drivers/gpu/drm/i915/i915_dma.c156
-rw-r--r--drivers/gpu/drm/i915/i915_reg.h19
3 files changed, 200 insertions, 53 deletions
diff --git a/drivers/char/agp/intel-agp.c b/drivers/char/agp/intel-agp.c
index c3c870bf5678..9a551bc34c39 100644
--- a/drivers/char/agp/intel-agp.c
+++ b/drivers/char/agp/intel-agp.c
@@ -150,6 +150,25 @@ extern int agp_memory_reserved;
150#define INTEL_I7505_AGPCTRL 0x70 150#define INTEL_I7505_AGPCTRL 0x70
151#define INTEL_I7505_MCHCFG 0x50 151#define INTEL_I7505_MCHCFG 0x50
152 152
153#define SNB_GMCH_CTRL 0x50
154#define SNB_GMCH_GMS_STOLEN_MASK 0xF8
155#define SNB_GMCH_GMS_STOLEN_32M (1 << 3)
156#define SNB_GMCH_GMS_STOLEN_64M (2 << 3)
157#define SNB_GMCH_GMS_STOLEN_96M (3 << 3)
158#define SNB_GMCH_GMS_STOLEN_128M (4 << 3)
159#define SNB_GMCH_GMS_STOLEN_160M (5 << 3)
160#define SNB_GMCH_GMS_STOLEN_192M (6 << 3)
161#define SNB_GMCH_GMS_STOLEN_224M (7 << 3)
162#define SNB_GMCH_GMS_STOLEN_256M (8 << 3)
163#define SNB_GMCH_GMS_STOLEN_288M (9 << 3)
164#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3)
165#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3)
166#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3)
167#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3)
168#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3)
169#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3)
170#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3)
171
153static const struct aper_size_info_fixed intel_i810_sizes[] = 172static const struct aper_size_info_fixed intel_i810_sizes[] =
154{ 173{
155 {64, 16384, 4}, 174 {64, 16384, 4},
@@ -621,7 +640,7 @@ static struct aper_size_info_fixed intel_i830_sizes[] =
621static void intel_i830_init_gtt_entries(void) 640static void intel_i830_init_gtt_entries(void)
622{ 641{
623 u16 gmch_ctrl; 642 u16 gmch_ctrl;
624 int gtt_entries; 643 int gtt_entries = 0;
625 u8 rdct; 644 u8 rdct;
626 int local = 0; 645 int local = 0;
627 static const int ddt[4] = { 0, 16, 32, 64 }; 646 static const int ddt[4] = { 0, 16, 32, 64 };
@@ -715,10 +734,61 @@ static void intel_i830_init_gtt_entries(void)
715 } 734 }
716 } else if (agp_bridge->dev->device == 735 } else if (agp_bridge->dev->device ==
717 PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) { 736 PCI_DEVICE_ID_INTEL_SANDYBRIDGE_HB) {
718 /* XXX: This is what my A1 silicon has. What's the right 737 /*
719 * answer? 738 * SandyBridge has new memory control reg at 0x50.w
720 */ 739 */
721 gtt_entries = MB(64) - KB(size); 740 u16 snb_gmch_ctl;
741 pci_read_config_word(intel_private.pcidev, SNB_GMCH_CTRL, &snb_gmch_ctl);
742 switch (snb_gmch_ctl & SNB_GMCH_GMS_STOLEN_MASK) {
743 case SNB_GMCH_GMS_STOLEN_32M:
744 gtt_entries = MB(32) - KB(size);
745 break;
746 case SNB_GMCH_GMS_STOLEN_64M:
747 gtt_entries = MB(64) - KB(size);
748 break;
749 case SNB_GMCH_GMS_STOLEN_96M:
750 gtt_entries = MB(96) - KB(size);
751 break;
752 case SNB_GMCH_GMS_STOLEN_128M:
753 gtt_entries = MB(128) - KB(size);
754 break;
755 case SNB_GMCH_GMS_STOLEN_160M:
756 gtt_entries = MB(160) - KB(size);
757 break;
758 case SNB_GMCH_GMS_STOLEN_192M:
759 gtt_entries = MB(192) - KB(size);
760 break;
761 case SNB_GMCH_GMS_STOLEN_224M:
762 gtt_entries = MB(224) - KB(size);
763 break;
764 case SNB_GMCH_GMS_STOLEN_256M:
765 gtt_entries = MB(256) - KB(size);
766 break;
767 case SNB_GMCH_GMS_STOLEN_288M:
768 gtt_entries = MB(288) - KB(size);
769 break;
770 case SNB_GMCH_GMS_STOLEN_320M:
771 gtt_entries = MB(320) - KB(size);
772 break;
773 case SNB_GMCH_GMS_STOLEN_352M:
774 gtt_entries = MB(352) - KB(size);
775 break;
776 case SNB_GMCH_GMS_STOLEN_384M:
777 gtt_entries = MB(384) - KB(size);
778 break;
779 case SNB_GMCH_GMS_STOLEN_416M:
780 gtt_entries = MB(416) - KB(size);
781 break;
782 case SNB_GMCH_GMS_STOLEN_448M:
783 gtt_entries = MB(448) - KB(size);
784 break;
785 case SNB_GMCH_GMS_STOLEN_480M:
786 gtt_entries = MB(480) - KB(size);
787 break;
788 case SNB_GMCH_GMS_STOLEN_512M:
789 gtt_entries = MB(512) - KB(size);
790 break;
791 }
722 } else { 792 } else {
723 switch (gmch_ctrl & I855_GMCH_GMS_MASK) { 793 switch (gmch_ctrl & I855_GMCH_GMS_MASK) {
724 case I855_GMCH_GMS_STOLEN_1M: 794 case I855_GMCH_GMS_STOLEN_1M:
diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
index 281faca3e37c..3e658d6a6b7d 100644
--- a/drivers/gpu/drm/i915/i915_dma.c
+++ b/drivers/gpu/drm/i915/i915_dma.c
@@ -1099,60 +1099,118 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
1099 else 1099 else
1100 overhead = (*aperture_size / 1024) + 4096; 1100 overhead = (*aperture_size / 1024) + 4096;
1101 1101
1102 switch (tmp & INTEL_GMCH_GMS_MASK) { 1102 if (IS_GEN6(dev)) {
1103 case INTEL_855_GMCH_GMS_DISABLED: 1103 /* SNB has memory control reg at 0x50.w */
1104 /* XXX: This is what my A1 silicon has. */ 1104 pci_read_config_word(dev->pdev, SNB_GMCH_CTRL, &tmp);
1105 if (IS_GEN6(dev)) { 1105
1106 switch (tmp & SNB_GMCH_GMS_STOLEN_MASK) {
1107 case INTEL_855_GMCH_GMS_DISABLED:
1108 DRM_ERROR("video memory is disabled\n");
1109 return -1;
1110 case SNB_GMCH_GMS_STOLEN_32M:
1111 stolen = 32 * 1024 * 1024;
1112 break;
1113 case SNB_GMCH_GMS_STOLEN_64M:
1106 stolen = 64 * 1024 * 1024; 1114 stolen = 64 * 1024 * 1024;
1107 } else { 1115 break;
1116 case SNB_GMCH_GMS_STOLEN_96M:
1117 stolen = 96 * 1024 * 1024;
1118 break;
1119 case SNB_GMCH_GMS_STOLEN_128M:
1120 stolen = 128 * 1024 * 1024;
1121 break;
1122 case SNB_GMCH_GMS_STOLEN_160M:
1123 stolen = 160 * 1024 * 1024;
1124 break;
1125 case SNB_GMCH_GMS_STOLEN_192M:
1126 stolen = 192 * 1024 * 1024;
1127 break;
1128 case SNB_GMCH_GMS_STOLEN_224M:
1129 stolen = 224 * 1024 * 1024;
1130 break;
1131 case SNB_GMCH_GMS_STOLEN_256M:
1132 stolen = 256 * 1024 * 1024;
1133 break;
1134 case SNB_GMCH_GMS_STOLEN_288M:
1135 stolen = 288 * 1024 * 1024;
1136 break;
1137 case SNB_GMCH_GMS_STOLEN_320M:
1138 stolen = 320 * 1024 * 1024;
1139 break;
1140 case SNB_GMCH_GMS_STOLEN_352M:
1141 stolen = 352 * 1024 * 1024;
1142 break;
1143 case SNB_GMCH_GMS_STOLEN_384M:
1144 stolen = 384 * 1024 * 1024;
1145 break;
1146 case SNB_GMCH_GMS_STOLEN_416M:
1147 stolen = 416 * 1024 * 1024;
1148 break;
1149 case SNB_GMCH_GMS_STOLEN_448M:
1150 stolen = 448 * 1024 * 1024;
1151 break;
1152 case SNB_GMCH_GMS_STOLEN_480M:
1153 stolen = 480 * 1024 * 1024;
1154 break;
1155 case SNB_GMCH_GMS_STOLEN_512M:
1156 stolen = 512 * 1024 * 1024;
1157 break;
1158 default:
1159 DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
1160 tmp & SNB_GMCH_GMS_STOLEN_MASK);
1161 return -1;
1162 }
1163 } else {
1164 switch (tmp & INTEL_GMCH_GMS_MASK) {
1165 case INTEL_855_GMCH_GMS_DISABLED:
1108 DRM_ERROR("video memory is disabled\n"); 1166 DRM_ERROR("video memory is disabled\n");
1109 return -1; 1167 return -1;
1168 case INTEL_855_GMCH_GMS_STOLEN_1M:
1169 stolen = 1 * 1024 * 1024;
1170 break;
1171 case INTEL_855_GMCH_GMS_STOLEN_4M:
1172 stolen = 4 * 1024 * 1024;
1173 break;
1174 case INTEL_855_GMCH_GMS_STOLEN_8M:
1175 stolen = 8 * 1024 * 1024;
1176 break;
1177 case INTEL_855_GMCH_GMS_STOLEN_16M:
1178 stolen = 16 * 1024 * 1024;
1179 break;
1180 case INTEL_855_GMCH_GMS_STOLEN_32M:
1181 stolen = 32 * 1024 * 1024;
1182 break;
1183 case INTEL_915G_GMCH_GMS_STOLEN_48M:
1184 stolen = 48 * 1024 * 1024;
1185 break;
1186 case INTEL_915G_GMCH_GMS_STOLEN_64M:
1187 stolen = 64 * 1024 * 1024;
1188 break;
1189 case INTEL_GMCH_GMS_STOLEN_128M:
1190 stolen = 128 * 1024 * 1024;
1191 break;
1192 case INTEL_GMCH_GMS_STOLEN_256M:
1193 stolen = 256 * 1024 * 1024;
1194 break;
1195 case INTEL_GMCH_GMS_STOLEN_96M:
1196 stolen = 96 * 1024 * 1024;
1197 break;
1198 case INTEL_GMCH_GMS_STOLEN_160M:
1199 stolen = 160 * 1024 * 1024;
1200 break;
1201 case INTEL_GMCH_GMS_STOLEN_224M:
1202 stolen = 224 * 1024 * 1024;
1203 break;
1204 case INTEL_GMCH_GMS_STOLEN_352M:
1205 stolen = 352 * 1024 * 1024;
1206 break;
1207 default:
1208 DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
1209 tmp & INTEL_GMCH_GMS_MASK);
1210 return -1;
1110 } 1211 }
1111 break;
1112 case INTEL_855_GMCH_GMS_STOLEN_1M:
1113 stolen = 1 * 1024 * 1024;
1114 break;
1115 case INTEL_855_GMCH_GMS_STOLEN_4M:
1116 stolen = 4 * 1024 * 1024;
1117 break;
1118 case INTEL_855_GMCH_GMS_STOLEN_8M:
1119 stolen = 8 * 1024 * 1024;
1120 break;
1121 case INTEL_855_GMCH_GMS_STOLEN_16M:
1122 stolen = 16 * 1024 * 1024;
1123 break;
1124 case INTEL_855_GMCH_GMS_STOLEN_32M:
1125 stolen = 32 * 1024 * 1024;
1126 break;
1127 case INTEL_915G_GMCH_GMS_STOLEN_48M:
1128 stolen = 48 * 1024 * 1024;
1129 break;
1130 case INTEL_915G_GMCH_GMS_STOLEN_64M:
1131 stolen = 64 * 1024 * 1024;
1132 break;
1133 case INTEL_GMCH_GMS_STOLEN_128M:
1134 stolen = 128 * 1024 * 1024;
1135 break;
1136 case INTEL_GMCH_GMS_STOLEN_256M:
1137 stolen = 256 * 1024 * 1024;
1138 break;
1139 case INTEL_GMCH_GMS_STOLEN_96M:
1140 stolen = 96 * 1024 * 1024;
1141 break;
1142 case INTEL_GMCH_GMS_STOLEN_160M:
1143 stolen = 160 * 1024 * 1024;
1144 break;
1145 case INTEL_GMCH_GMS_STOLEN_224M:
1146 stolen = 224 * 1024 * 1024;
1147 break;
1148 case INTEL_GMCH_GMS_STOLEN_352M:
1149 stolen = 352 * 1024 * 1024;
1150 break;
1151 default:
1152 DRM_ERROR("unexpected GMCH_GMS value: 0x%02x\n",
1153 tmp & INTEL_GMCH_GMS_MASK);
1154 return -1;
1155 } 1212 }
1213
1156 *preallocated_size = stolen - overhead; 1214 *preallocated_size = stolen - overhead;
1157 *start = overhead; 1215 *start = overhead;
1158 1216
diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
index 2a312b674a72..3d59862c7ccd 100644
--- a/drivers/gpu/drm/i915/i915_reg.h
+++ b/drivers/gpu/drm/i915/i915_reg.h
@@ -53,6 +53,25 @@
53#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4) 53#define INTEL_GMCH_GMS_STOLEN_224M (0xc << 4)
54#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4) 54#define INTEL_GMCH_GMS_STOLEN_352M (0xd << 4)
55 55
56#define SNB_GMCH_CTRL 0x50
57#define SNB_GMCH_GMS_STOLEN_MASK 0xF8
58#define SNB_GMCH_GMS_STOLEN_32M (1 << 3)
59#define SNB_GMCH_GMS_STOLEN_64M (2 << 3)
60#define SNB_GMCH_GMS_STOLEN_96M (3 << 3)
61#define SNB_GMCH_GMS_STOLEN_128M (4 << 3)
62#define SNB_GMCH_GMS_STOLEN_160M (5 << 3)
63#define SNB_GMCH_GMS_STOLEN_192M (6 << 3)
64#define SNB_GMCH_GMS_STOLEN_224M (7 << 3)
65#define SNB_GMCH_GMS_STOLEN_256M (8 << 3)
66#define SNB_GMCH_GMS_STOLEN_288M (9 << 3)
67#define SNB_GMCH_GMS_STOLEN_320M (0xa << 3)
68#define SNB_GMCH_GMS_STOLEN_352M (0xb << 3)
69#define SNB_GMCH_GMS_STOLEN_384M (0xc << 3)
70#define SNB_GMCH_GMS_STOLEN_416M (0xd << 3)
71#define SNB_GMCH_GMS_STOLEN_448M (0xe << 3)
72#define SNB_GMCH_GMS_STOLEN_480M (0xf << 3)
73#define SNB_GMCH_GMS_STOLEN_512M (0x10 << 3)
74
56/* PCI config space */ 75/* PCI config space */
57 76
58#define HPLLCC 0xc0 /* 855 only */ 77#define HPLLCC 0xc0 /* 855 only */
Because we are not running where we were linked, any calls to functions external to this file must be indirect. To be safe, we apply the opposite rule to functions within this file, with local labels given to them to ensure correctness. */ .Lsyscall_nosys: syscall_nosys: ldil L%syscall_exit,%r1 be R%syscall_exit(%sr7,%r1) ldo -ENOSYS(%r0),%r28 /* set errno */ /* Warning! This trace code is a virtual duplicate of the code above so be * sure to maintain both! */ .Ltracesys: tracesys: /* Need to save more registers so the debugger can see where we * are. This saves only the lower 8 bits of PSW, so that the C * bit is still clear on syscalls, and the D bit is set if this * full register save path has been executed. We check the D * bit on syscall_return_rfi to determine which registers to * restore. An interrupt results in a full PSW saved with the * C bit set, a non-straced syscall entry results in C and D clear * in the saved PSW. */ ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ LDREG TI_TASK(%r1), %r1 ssm 0,%r2 STREG %r2,TASK_PT_PSW(%r1) /* Lower 8 bits only!! */ mfsp %sr0,%r2 STREG %r2,TASK_PT_SR0(%r1) mfsp %sr1,%r2 STREG %r2,TASK_PT_SR1(%r1) mfsp %sr2,%r2 STREG %r2,TASK_PT_SR2(%r1) mfsp %sr3,%r2 STREG %r2,TASK_PT_SR3(%r1) STREG %r2,TASK_PT_SR4(%r1) STREG %r2,TASK_PT_SR5(%r1) STREG %r2,TASK_PT_SR6(%r1) STREG %r2,TASK_PT_SR7(%r1) STREG %r2,TASK_PT_IASQ0(%r1) STREG %r2,TASK_PT_IASQ1(%r1) LDREG TASK_PT_GR31(%r1),%r2 STREG %r2,TASK_PT_IAOQ0(%r1) ldo 4(%r2),%r2 STREG %r2,TASK_PT_IAOQ1(%r1) ldo TASK_REGS(%r1),%r2 /* reg_save %r2 */ STREG %r3,PT_GR3(%r2) STREG %r4,PT_GR4(%r2) STREG %r5,PT_GR5(%r2) STREG %r6,PT_GR6(%r2) STREG %r7,PT_GR7(%r2) STREG %r8,PT_GR8(%r2) STREG %r9,PT_GR9(%r2) STREG %r10,PT_GR10(%r2) STREG %r11,PT_GR11(%r2) STREG %r12,PT_GR12(%r2) STREG %r13,PT_GR13(%r2) STREG %r14,PT_GR14(%r2) STREG %r15,PT_GR15(%r2) STREG %r16,PT_GR16(%r2) STREG %r17,PT_GR17(%r2) STREG %r18,PT_GR18(%r2) /* Finished saving things for the debugger */ ldil L%syscall_trace,%r1 ldil L%tracesys_next,%r2 be R%syscall_trace(%sr7,%r1) ldo R%tracesys_next(%r2),%r2 tracesys_next: ldil L%sys_call_table,%r1 ldo R%sys_call_table(%r1), %r19 ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ LDREG TI_TASK(%r1), %r1 LDREG TASK_PT_GR20(%r1), %r20 LDREG TASK_PT_GR26(%r1), %r26 /* Restore the users args */ LDREG TASK_PT_GR25(%r1), %r25 LDREG TASK_PT_GR24(%r1), %r24 LDREG TASK_PT_GR23(%r1), %r23 #ifdef CONFIG_64BIT LDREG TASK_PT_GR22(%r1), %r22 LDREG TASK_PT_GR21(%r1), %r21 ldo -16(%r30),%r29 /* Reference param save area */ #endif comiclr,>>= __NR_Linux_syscalls, %r20, %r0 b,n .Lsyscall_nosys LDREGX %r20(%r19), %r19 /* If this is a sys_rt_sigreturn call, and the signal was received * when not in_syscall, then we want to return via syscall_exit_rfi, * not syscall_exit. Signal no. in r20, in_syscall in r25 (see * trampoline code in signal.c). */ ldi __NR_rt_sigreturn,%r2 comb,= %r2,%r20,.Ltrace_rt_sigreturn .Ltrace_in_syscall: ldil L%tracesys_exit,%r2 be 0(%sr7,%r19) ldo R%tracesys_exit(%r2),%r2 /* Do *not* call this function on the gateway page, because it makes a direct call to syscall_trace. */ tracesys_exit: ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ LDREG TI_TASK(%r1), %r1 #ifdef CONFIG_64BIT ldo -16(%r30),%r29 /* Reference param save area */ #endif bl syscall_trace, %r2 STREG %r28,TASK_PT_GR28(%r1) /* save return value now */ ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ LDREG TI_TASK(%r1), %r1 LDREG TASK_PT_GR28(%r1), %r28 /* Restore return val. */ ldil L%syscall_exit,%r1 be,n R%syscall_exit(%sr7,%r1) .Ltrace_rt_sigreturn: comib,<> 0,%r25,.Ltrace_in_syscall ldil L%tracesys_sigexit,%r2 be 0(%sr7,%r19) ldo R%tracesys_sigexit(%r2),%r2 tracesys_sigexit: ldo -THREAD_SZ_ALGN-FRAME_SIZE(%r30),%r1 /* get task ptr */ LDREG 0(%r1), %r1 #ifdef CONFIG_64BIT ldo -16(%r30),%r29 /* Reference param save area */ #endif bl syscall_trace, %r2 nop ldil L%syscall_exit_rfi,%r1 be,n R%syscall_exit_rfi(%sr7,%r1) /********************************************************* Light-weight-syscall code r20 - lws number r26,r25,r24,r23,r22 - Input registers r28 - Function return register r21 - Error code. Scracth: Any of the above that aren't being currently used, including r1. Return pointer: r31 (Not usable) Error codes returned by entry path: ENOSYS - r20 was an invalid LWS number. *********************************************************/ lws_start: /* Gate and ensure we return to userspace */ gate .+8, %r0 depi 3, 31, 2, %r31 /* Ensure we return to userspace */ #ifdef CONFIG_64BIT /* FIXME: If we are a 64-bit kernel just * turn this on unconditionally. */ ssm PSW_SM_W, %r1 extrd,u %r1,PSW_W_BIT,1,%r1 /* sp must be aligned on 4, so deposit the W bit setting into * the bottom of sp temporarily */ or,ev %r1,%r30,%r30 /* Clip LWS number to a 32-bit value always */ depdi 0, 31, 32, %r20 #endif /* Is the lws entry number valid? */ comiclr,>>= __NR_lws_entries, %r20, %r0 b,n lws_exit_nosys /* WARNING: Trashing sr2 and sr3 */ mfsp %sr7,%r1 /* get userspace into sr3 */ mtsp %r1,%sr3 mtsp %r0,%sr2 /* get kernel space into sr2 */ /* Load table start */ ldil L%lws_table, %r1 ldo R%lws_table(%r1), %r28 /* Scratch use of r28 */ LDREGX %r20(%sr2,r28), %r21 /* Scratch use of r21 */ /* Jump to lws, lws table pointers already relocated */ be,n 0(%sr2,%r21) lws_exit_nosys: ldo -ENOSYS(%r0),%r21 /* set errno */ /* Fall through: Return to userspace */ lws_exit: #ifdef CONFIG_64BIT /* decide whether to reset the wide mode bit * * For a syscall, the W bit is stored in the lowest bit * of sp. Extract it and reset W if it is zero */ extrd,u,*<> %r30,63,1,%r1 rsm PSW_SM_W, %r0 /* now reset the lowest bit of sp if it was set */ xor %r30,%r1,%r30 #endif be,n 0(%sr3, %r31) /*************************************************** Implementing CAS as an atomic operation: %r26 - Address to examine %r25 - Old value to check (old) %r24 - New value to set (new) %r28 - Return prev through this register. %r21 - Kernel error code If debugging is DISabled: %r21 has the following meanings: EAGAIN - CAS is busy, ldcw failed, try again. EFAULT - Read or write failed. If debugging is enabled: EDEADLOCK - CAS called recursively. EAGAIN && r28 == 1 - CAS is busy. Lock contended. EAGAIN && r28 == 2 - CAS is busy. ldcw failed. EFAULT - Read or write failed. Scratch: r20, r28, r1 ****************************************************/ /* Do not enable LWS debugging */ #define ENABLE_LWS_DEBUG 0 /* ELF64 Process entry path */ lws_compare_and_swap64: #ifdef CONFIG_64BIT b,n lws_compare_and_swap #else /* If we are not a 64-bit kernel, then we don't * implement having 64-bit input registers */ b,n lws_exit_nosys #endif /* ELF32 Process entry path */ lws_compare_and_swap32: #ifdef CONFIG_64BIT /* Clip all the input registers */ depdi 0, 31, 32, %r26 depdi 0, 31, 32, %r25 depdi 0, 31, 32, %r24 #endif lws_compare_and_swap: #ifdef CONFIG_SMP /* Load start of lock table */ ldil L%lws_lock_start, %r20 ldo R%lws_lock_start(%r20), %r28 /* Extract four bits from r26 and hash lock (Bits 4-7) */ extru %r26, 27, 4, %r20 /* Find lock to use, the hash is either one of 0 to 15, multiplied by 16 (keep it 16-byte aligned) and add to the lock table offset. */ shlw %r20, 4, %r20 add %r20, %r28, %r20 # if ENABLE_LWS_DEBUG /* DEBUG, check for deadlock! If the thread register values are the same then we were the one that locked it last and this is a recurisve call that will deadlock. We *must* giveup this call and fail. */ ldw 4(%sr2,%r20), %r28 /* Load thread register */ /* WARNING: If cr27 cycles to the same value we have problems */ mfctl %cr27, %r21 /* Get current thread register */ cmpb,<>,n %r21, %r28, cas_lock /* Called recursive? */ b lws_exit /* Return error! */ ldo -EDEADLOCK(%r0), %r21 cas_lock: cmpb,=,n %r0, %r28, cas_nocontend /* Is nobody using it? */ ldo 1(%r0), %r28 /* 1st case */ b lws_exit /* Contended... */ ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ cas_nocontend: # endif /* ENABLE_LWS_DEBUG */ LDCW 0(%sr2,%r20), %r28 /* Try to acquire the lock */ cmpb,<>,n %r0, %r28, cas_action /* Did we get it? */ cas_wouldblock: ldo 2(%r0), %r28 /* 2nd case */ b lws_exit /* Contended... */ ldo -EAGAIN(%r0), %r21 /* Spin in userspace */ #endif /* CONFIG_SMP */ /* prev = *addr; if ( prev == old ) *addr = new; return prev; */ /* NOTES: This all works becuse intr_do_signal and schedule both check the return iasq and see that we are on the kernel page so this process is never scheduled off or is ever sent any signal of any sort, thus it is wholly atomic from usrspaces perspective */ cas_action: #if defined CONFIG_SMP && ENABLE_LWS_DEBUG /* DEBUG */ mfctl %cr27, %r1 stw %r1, 4(%sr2,%r20) #endif /* The load and store could fail */ 1: ldw 0(%sr3,%r26), %r28 sub,<> %r28, %r25, %r0 2: stw %r24, 0(%sr3,%r26) #ifdef CONFIG_SMP /* Free lock */ stw %r20, 0(%sr2,%r20) # if ENABLE_LWS_DEBUG /* Clear thread register indicator */ stw %r0, 4(%sr2,%r20) # endif #endif /* Return to userspace, set no error */ b lws_exit copy %r0, %r21 3: /* Error occured on load or store */ #ifdef CONFIG_SMP /* Free lock */ stw %r20, 0(%sr2,%r20) # if ENABLE_LWS_DEBUG stw %r0, 4(%sr2,%r20) # endif #endif b lws_exit ldo -EFAULT(%r0),%r21 /* set errno */ nop nop nop nop /* Two exception table entries, one for the load, the other for the store. Either return -EFAULT. Each of the entries must be relocated. */ .section __ex_table,"aw" ASM_ULONG_INSN (1b - linux_gateway_page), (3b - linux_gateway_page) ASM_ULONG_INSN (2b - linux_gateway_page), (3b - linux_gateway_page) .previous /* Make sure nothing else is placed on this page */ .align PAGE_SIZE END(linux_gateway_page) ENTRY(end_linux_gateway_page) /* Relocate symbols assuming linux_gateway_page is mapped to virtual address 0x0 */ #define LWS_ENTRY(_name_) ASM_ULONG_INSN (lws_##_name_ - linux_gateway_page) .section .rodata,"a" .align PAGE_SIZE /* Light-weight-syscall table */ /* Start of lws table. */ ENTRY(lws_table) LWS_ENTRY(compare_and_swap32) /* 0 - ELF32 Atomic compare and swap */ LWS_ENTRY(compare_and_swap64) /* 1 - ELF64 Atomic compare and swap */ END(lws_table) /* End of lws table */ .align PAGE_SIZE ENTRY(sys_call_table) #include "syscall_table.S" END(sys_call_table) #ifdef CONFIG_64BIT .align PAGE_SIZE ENTRY(sys_call_table64) #define SYSCALL_TABLE_64BIT #include "syscall_table.S" END(sys_call_table64) #endif #ifdef CONFIG_SMP /* All light-weight-syscall atomic operations will use this set of locks */ .section .data .align PAGE_SIZE ENTRY(lws_lock_start) /* lws locks */ .align 16 .rept 16 /* Keep locks aligned at 16-bytes */ .word 1 .word 0 .word 0 .word 0 .endr END(lws_lock_start) .previous #endif /* CONFIG_SMP for lws_lock_start */ .end