1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/module.h>
3 #include <linux/kernel.h>
4 #include <linux/errno.h>
5 #include <linux/string.h>
6 #include <linux/mm.h>
7 #include <linux/slab.h>
8 #include <linux/delay.h>
9 #include <linux/fb.h>
10 #include <linux/ioport.h>
11 #include <linux/init.h>
12 #include <linux/pci.h>
13 #include <linux/vmalloc.h>
14 #include <linux/pagemap.h>
15 #include <linux/console.h>
16 #ifdef CONFIG_MTRR
17 #include <asm/mtrr.h>
18 #endif
19 #include <linux/platform_device.h>
20 #include <linux/screen_info.h>
21 #include <linux/sizes.h>
22 
23 #include "sm750.h"
24 #include "ddk750.h"
25 #include "sm750_accel.h"
26 
27 void __iomem *mmio750;
28 
hw_sm750_map(struct sm750_dev * sm750_dev,struct pci_dev * pdev)29 int hw_sm750_map(struct sm750_dev *sm750_dev, struct pci_dev *pdev)
30 {
31 	int ret;
32 
33 	ret = 0;
34 
35 	sm750_dev->vidreg_start = pci_resource_start(pdev, 1);
36 	sm750_dev->vidreg_size = SZ_2M;
37 
38 	pr_info("mmio phyAddr = %lx\n", sm750_dev->vidreg_start);
39 
40 	/*
41 	 * reserve the vidreg space of smi adaptor
42 	 * if you do this, you need to add release region code
43 	 * in lynxfb_remove, or memory will not be mapped again
44 	 * successfully
45 	 */
46 	ret = pci_request_region(pdev, 1, "sm750fb");
47 	if (ret) {
48 		pr_err("Can not request PCI regions.\n");
49 		goto exit;
50 	}
51 
52 	/* now map mmio and vidmem */
53 	sm750_dev->pvReg =
54 		ioremap(sm750_dev->vidreg_start, sm750_dev->vidreg_size);
55 	if (!sm750_dev->pvReg) {
56 		pr_err("mmio failed\n");
57 		ret = -EFAULT;
58 		goto exit;
59 	} else {
60 		pr_info("mmio virtual addr = %p\n", sm750_dev->pvReg);
61 	}
62 
63 	sm750_dev->accel.dprBase = sm750_dev->pvReg + DE_BASE_ADDR_TYPE1;
64 	sm750_dev->accel.dpPortBase = sm750_dev->pvReg + DE_PORT_ADDR_TYPE1;
65 
66 	mmio750 = sm750_dev->pvReg;
67 	sm750_set_chip_type(sm750_dev->devid, sm750_dev->revid);
68 
69 	sm750_dev->vidmem_start = pci_resource_start(pdev, 0);
70 	/*
71 	 * don't use pdev_resource[x].end - resource[x].start to
72 	 * calculate the resource size, it's only the maximum available
73 	 * size but not the actual size, using
74 	 * @ddk750_get_vm_size function can be safe.
75 	 */
76 	sm750_dev->vidmem_size = ddk750_get_vm_size();
77 	pr_info("video memory phyAddr = %lx, size = %u bytes\n",
78 		sm750_dev->vidmem_start, sm750_dev->vidmem_size);
79 
80 	/* reserve the vidmem space of smi adaptor */
81 	sm750_dev->pvMem =
82 		ioremap_wc(sm750_dev->vidmem_start, sm750_dev->vidmem_size);
83 	if (!sm750_dev->pvMem) {
84 		iounmap(sm750_dev->pvReg);
85 		pr_err("Map video memory failed\n");
86 		ret = -EFAULT;
87 		goto exit;
88 	} else {
89 		pr_info("video memory vaddr = %p\n", sm750_dev->pvMem);
90 	}
91 exit:
92 	return ret;
93 }
94 
hw_sm750_inithw(struct sm750_dev * sm750_dev,struct pci_dev * pdev)95 int hw_sm750_inithw(struct sm750_dev *sm750_dev, struct pci_dev *pdev)
96 {
97 	struct init_status *parm;
98 
99 	parm = &sm750_dev->initParm;
100 	if (parm->chip_clk == 0)
101 		parm->chip_clk = (sm750_get_chip_type() == SM750LE) ?
102 					       DEFAULT_SM750LE_CHIP_CLOCK :
103 					       DEFAULT_SM750_CHIP_CLOCK;
104 
105 	if (parm->mem_clk == 0)
106 		parm->mem_clk = parm->chip_clk;
107 	if (parm->master_clk == 0)
108 		parm->master_clk = parm->chip_clk / 3;
109 
110 	ddk750_init_hw((struct initchip_param *)&sm750_dev->initParm);
111 	/* for sm718, open pci burst */
112 	if (sm750_dev->devid == 0x718) {
113 		poke32(SYSTEM_CTRL,
114 		       peek32(SYSTEM_CTRL) | SYSTEM_CTRL_PCI_BURST);
115 	}
116 
117 	if (sm750_get_chip_type() != SM750LE) {
118 		unsigned int val;
119 		/* does user need CRT? */
120 		if (sm750_dev->nocrt) {
121 			poke32(MISC_CTRL,
122 			       peek32(MISC_CTRL) | MISC_CTRL_DAC_POWER_OFF);
123 			/* shut off dpms */
124 			val = peek32(SYSTEM_CTRL) & ~SYSTEM_CTRL_DPMS_MASK;
125 			val |= SYSTEM_CTRL_DPMS_VPHN;
126 			poke32(SYSTEM_CTRL, val);
127 		} else {
128 			poke32(MISC_CTRL,
129 			       peek32(MISC_CTRL) & ~MISC_CTRL_DAC_POWER_OFF);
130 			/* turn on dpms */
131 			val = peek32(SYSTEM_CTRL) & ~SYSTEM_CTRL_DPMS_MASK;
132 			val |= SYSTEM_CTRL_DPMS_VPHP;
133 			poke32(SYSTEM_CTRL, val);
134 		}
135 
136 		val = peek32(PANEL_DISPLAY_CTRL) &
137 		      ~(PANEL_DISPLAY_CTRL_DUAL_DISPLAY |
138 			PANEL_DISPLAY_CTRL_DOUBLE_PIXEL);
139 		switch (sm750_dev->pnltype) {
140 		case sm750_24TFT:
141 			break;
142 		case sm750_doubleTFT:
143 			val |= PANEL_DISPLAY_CTRL_DOUBLE_PIXEL;
144 			break;
145 		case sm750_dualTFT:
146 			val |= PANEL_DISPLAY_CTRL_DUAL_DISPLAY;
147 			break;
148 		}
149 		poke32(PANEL_DISPLAY_CTRL, val);
150 	} else {
151 		/*
152 		 * for 750LE, no DVI chip initialization
153 		 * makes Monitor no signal
154 		 *
155 		 * Set up GPIO for software I2C to program DVI chip in the
156 		 * Xilinx SP605 board, in order to have video signal.
157 		 */
158 		sm750_sw_i2c_init(0, 1);
159 
160 		/*
161 		 * Customer may NOT use CH7301 DVI chip, which has to be
162 		 * initialized differently.
163 		 */
164 		if (sm750_sw_i2c_read_reg(0xec, 0x4a) == 0x95) {
165 			/*
166 			 * The following register values for CH7301 are from
167 			 * Chrontel app note and our experiment.
168 			 */
169 			pr_info("yes,CH7301 DVI chip found\n");
170 			sm750_sw_i2c_write_reg(0xec, 0x1d, 0x16);
171 			sm750_sw_i2c_write_reg(0xec, 0x21, 0x9);
172 			sm750_sw_i2c_write_reg(0xec, 0x49, 0xC0);
173 			pr_info("okay,CH7301 DVI chip setup done\n");
174 		}
175 	}
176 
177 	/* init 2d engine */
178 	if (!sm750_dev->accel_off)
179 		hw_sm750_initAccel(sm750_dev);
180 
181 	return 0;
182 }
183 
hw_sm750_output_setMode(struct lynxfb_output * output,struct fb_var_screeninfo * var,struct fb_fix_screeninfo * fix)184 int hw_sm750_output_setMode(struct lynxfb_output *output,
185 			    struct fb_var_screeninfo *var,
186 			    struct fb_fix_screeninfo *fix)
187 {
188 	int ret;
189 	enum disp_output disp_set;
190 	int channel;
191 
192 	ret = 0;
193 	disp_set = 0;
194 	channel = *output->channel;
195 
196 	if (sm750_get_chip_type() != SM750LE) {
197 		if (channel == sm750_primary) {
198 			pr_info("primary channel\n");
199 			if (output->paths & sm750_panel)
200 				disp_set |= do_LCD1_PRI;
201 			if (output->paths & sm750_crt)
202 				disp_set |= do_CRT_PRI;
203 
204 		} else {
205 			pr_info("secondary channel\n");
206 			if (output->paths & sm750_panel)
207 				disp_set |= do_LCD1_SEC;
208 			if (output->paths & sm750_crt)
209 				disp_set |= do_CRT_SEC;
210 		}
211 		ddk750_set_logical_disp_out(disp_set);
212 	} else {
213 		/* just open DISPLAY_CONTROL_750LE register bit 3:0 */
214 		u32 reg;
215 
216 		reg = peek32(DISPLAY_CONTROL_750LE);
217 		reg |= 0xf;
218 		poke32(DISPLAY_CONTROL_750LE, reg);
219 	}
220 
221 	pr_info("ddk setlogicdispout done\n");
222 	return ret;
223 }
224 
hw_sm750_crtc_checkMode(struct lynxfb_crtc * crtc,struct fb_var_screeninfo * var)225 int hw_sm750_crtc_checkMode(struct lynxfb_crtc *crtc,
226 			    struct fb_var_screeninfo *var)
227 {
228 	struct sm750_dev *sm750_dev;
229 	struct lynxfb_par *par = container_of(crtc, struct lynxfb_par, crtc);
230 
231 	sm750_dev = par->dev;
232 
233 	switch (var->bits_per_pixel) {
234 	case 8:
235 	case 16:
236 		break;
237 	case 32:
238 		if (sm750_dev->revid == SM750LE_REVISION_ID) {
239 			pr_debug("750le do not support 32bpp\n");
240 			return -EINVAL;
241 		}
242 		break;
243 	default:
244 		return -EINVAL;
245 	}
246 
247 	return 0;
248 }
249 
250 /* set the controller's mode for @crtc charged with @var and @fix parameters */
hw_sm750_crtc_setMode(struct lynxfb_crtc * crtc,struct fb_var_screeninfo * var,struct fb_fix_screeninfo * fix)251 int hw_sm750_crtc_setMode(struct lynxfb_crtc *crtc,
252 			  struct fb_var_screeninfo *var,
253 			  struct fb_fix_screeninfo *fix)
254 {
255 	int ret, fmt;
256 	u32 reg;
257 	struct mode_parameter modparm;
258 	enum clock_type clock;
259 	struct sm750_dev *sm750_dev;
260 	struct lynxfb_par *par;
261 
262 	ret = 0;
263 	par = container_of(crtc, struct lynxfb_par, crtc);
264 	sm750_dev = par->dev;
265 
266 	if (!sm750_dev->accel_off) {
267 		/* set 2d engine pixel format according to mode bpp */
268 		switch (var->bits_per_pixel) {
269 		case 8:
270 			fmt = 0;
271 			break;
272 		case 16:
273 			fmt = 1;
274 			break;
275 		case 32:
276 		default:
277 			fmt = 2;
278 			break;
279 		}
280 		sm750_hw_set2dformat(&sm750_dev->accel, fmt);
281 	}
282 
283 	/* set timing */
284 	modparm.pixel_clock = ps_to_hz(var->pixclock);
285 	modparm.vertical_sync_polarity =
286 		(var->sync & FB_SYNC_HOR_HIGH_ACT) ? POS : NEG;
287 	modparm.horizontal_sync_polarity =
288 		(var->sync & FB_SYNC_VERT_HIGH_ACT) ? POS : NEG;
289 	modparm.clock_phase_polarity =
290 		(var->sync & FB_SYNC_COMP_HIGH_ACT) ? POS : NEG;
291 	modparm.horizontal_display_end = var->xres;
292 	modparm.horizontal_sync_width = var->hsync_len;
293 	modparm.horizontal_sync_start = var->xres + var->right_margin;
294 	modparm.horizontal_total = var->xres + var->left_margin +
295 				   var->right_margin + var->hsync_len;
296 	modparm.vertical_display_end = var->yres;
297 	modparm.vertical_sync_height = var->vsync_len;
298 	modparm.vertical_sync_start = var->yres + var->lower_margin;
299 	modparm.vertical_total = var->yres + var->upper_margin +
300 				 var->lower_margin + var->vsync_len;
301 
302 	/* choose pll */
303 	if (crtc->channel != sm750_secondary)
304 		clock = PRIMARY_PLL;
305 	else
306 		clock = SECONDARY_PLL;
307 
308 	pr_debug("Request pixel clock = %lu\n", modparm.pixel_clock);
309 	ret = ddk750_setModeTiming(&modparm, clock);
310 	if (ret) {
311 		pr_err("Set mode timing failed\n");
312 		goto exit;
313 	}
314 
315 	if (crtc->channel != sm750_secondary) {
316 		/* set pitch, offset, width, start address, etc... */
317 		poke32(PANEL_FB_ADDRESS,
318 		       crtc->o_screen & PANEL_FB_ADDRESS_ADDRESS_MASK);
319 
320 		reg = var->xres * (var->bits_per_pixel >> 3);
321 		/*
322 		 * crtc->channel is not equal to par->index on numeric,
323 		 * be aware of that
324 		 */
325 		reg = ALIGN(reg, crtc->line_pad);
326 		reg = (reg << PANEL_FB_WIDTH_WIDTH_SHIFT) &
327 		      PANEL_FB_WIDTH_WIDTH_MASK;
328 		reg |= (fix->line_length & PANEL_FB_WIDTH_OFFSET_MASK);
329 		poke32(PANEL_FB_WIDTH, reg);
330 
331 		reg = ((var->xres - 1) << PANEL_WINDOW_WIDTH_WIDTH_SHIFT) &
332 		      PANEL_WINDOW_WIDTH_WIDTH_MASK;
333 		reg |= (var->xoffset & PANEL_WINDOW_WIDTH_X_MASK);
334 		poke32(PANEL_WINDOW_WIDTH, reg);
335 
336 		reg = (var->yres_virtual - 1)
337 		      << PANEL_WINDOW_HEIGHT_HEIGHT_SHIFT;
338 		reg &= PANEL_WINDOW_HEIGHT_HEIGHT_MASK;
339 		reg |= (var->yoffset & PANEL_WINDOW_HEIGHT_Y_MASK);
340 		poke32(PANEL_WINDOW_HEIGHT, reg);
341 
342 		poke32(PANEL_PLANE_TL, 0);
343 
344 		reg = ((var->yres - 1) << PANEL_PLANE_BR_BOTTOM_SHIFT) &
345 		      PANEL_PLANE_BR_BOTTOM_MASK;
346 		reg |= ((var->xres - 1) & PANEL_PLANE_BR_RIGHT_MASK);
347 		poke32(PANEL_PLANE_BR, reg);
348 
349 		/* set pixel format */
350 		reg = peek32(PANEL_DISPLAY_CTRL);
351 		poke32(PANEL_DISPLAY_CTRL, reg | (var->bits_per_pixel >> 4));
352 	} else {
353 		/* not implemented now */
354 		poke32(CRT_FB_ADDRESS, crtc->o_screen);
355 		reg = var->xres * (var->bits_per_pixel >> 3);
356 		/*
357 		 * crtc->channel is not equal to par->index on numeric,
358 		 * be aware of that
359 		 */
360 		reg = ALIGN(reg, crtc->line_pad) << CRT_FB_WIDTH_WIDTH_SHIFT;
361 		reg &= CRT_FB_WIDTH_WIDTH_MASK;
362 		reg |= (fix->line_length & CRT_FB_WIDTH_OFFSET_MASK);
363 		poke32(CRT_FB_WIDTH, reg);
364 
365 		/* SET PIXEL FORMAT */
366 		reg = peek32(CRT_DISPLAY_CTRL);
367 		reg |= ((var->bits_per_pixel >> 4) &
368 			CRT_DISPLAY_CTRL_FORMAT_MASK);
369 		poke32(CRT_DISPLAY_CTRL, reg);
370 	}
371 
372 exit:
373 	return ret;
374 }
375 
hw_sm750_setColReg(struct lynxfb_crtc * crtc,ushort index,ushort red,ushort green,ushort blue)376 int hw_sm750_setColReg(struct lynxfb_crtc *crtc, ushort index, ushort red,
377 		       ushort green, ushort blue)
378 {
379 	static unsigned int add[] = { PANEL_PALETTE_RAM, CRT_PALETTE_RAM };
380 
381 	poke32(add[crtc->channel] + index * 4,
382 	       (red << 16) | (green << 8) | blue);
383 	return 0;
384 }
385 
hw_sm750le_setBLANK(struct lynxfb_output * output,int blank)386 int hw_sm750le_setBLANK(struct lynxfb_output *output, int blank)
387 {
388 	int dpms, crtdb;
389 
390 	switch (blank) {
391 	case FB_BLANK_UNBLANK:
392 		dpms = CRT_DISPLAY_CTRL_DPMS_0;
393 		crtdb = 0;
394 		break;
395 	case FB_BLANK_NORMAL:
396 		dpms = CRT_DISPLAY_CTRL_DPMS_0;
397 		crtdb = CRT_DISPLAY_CTRL_BLANK;
398 		break;
399 	case FB_BLANK_VSYNC_SUSPEND:
400 		dpms = CRT_DISPLAY_CTRL_DPMS_2;
401 		crtdb = CRT_DISPLAY_CTRL_BLANK;
402 		break;
403 	case FB_BLANK_HSYNC_SUSPEND:
404 		dpms = CRT_DISPLAY_CTRL_DPMS_1;
405 		crtdb = CRT_DISPLAY_CTRL_BLANK;
406 		break;
407 	case FB_BLANK_POWERDOWN:
408 		dpms = CRT_DISPLAY_CTRL_DPMS_3;
409 		crtdb = CRT_DISPLAY_CTRL_BLANK;
410 		break;
411 	default:
412 		return -EINVAL;
413 	}
414 
415 	if (output->paths & sm750_crt) {
416 		unsigned int val;
417 
418 		val = peek32(CRT_DISPLAY_CTRL) & ~CRT_DISPLAY_CTRL_DPMS_MASK;
419 		poke32(CRT_DISPLAY_CTRL, val | dpms);
420 
421 		val = peek32(CRT_DISPLAY_CTRL) & ~CRT_DISPLAY_CTRL_BLANK;
422 		poke32(CRT_DISPLAY_CTRL, val | crtdb);
423 	}
424 	return 0;
425 }
426 
hw_sm750_setBLANK(struct lynxfb_output * output,int blank)427 int hw_sm750_setBLANK(struct lynxfb_output *output, int blank)
428 {
429 	unsigned int dpms, pps, crtdb;
430 
431 	dpms = 0;
432 	pps = 0;
433 	crtdb = 0;
434 
435 	switch (blank) {
436 	case FB_BLANK_UNBLANK:
437 		pr_debug("flag = FB_BLANK_UNBLANK\n");
438 		dpms = SYSTEM_CTRL_DPMS_VPHP;
439 		pps = PANEL_DISPLAY_CTRL_DATA;
440 		break;
441 	case FB_BLANK_NORMAL:
442 		pr_debug("flag = FB_BLANK_NORMAL\n");
443 		dpms = SYSTEM_CTRL_DPMS_VPHP;
444 		crtdb = CRT_DISPLAY_CTRL_BLANK;
445 		break;
446 	case FB_BLANK_VSYNC_SUSPEND:
447 		dpms = SYSTEM_CTRL_DPMS_VNHP;
448 		crtdb = CRT_DISPLAY_CTRL_BLANK;
449 		break;
450 	case FB_BLANK_HSYNC_SUSPEND:
451 		dpms = SYSTEM_CTRL_DPMS_VPHN;
452 		crtdb = CRT_DISPLAY_CTRL_BLANK;
453 		break;
454 	case FB_BLANK_POWERDOWN:
455 		dpms = SYSTEM_CTRL_DPMS_VNHN;
456 		crtdb = CRT_DISPLAY_CTRL_BLANK;
457 		break;
458 	}
459 
460 	if (output->paths & sm750_crt) {
461 		unsigned int val = peek32(SYSTEM_CTRL) & ~SYSTEM_CTRL_DPMS_MASK;
462 
463 		poke32(SYSTEM_CTRL, val | dpms);
464 
465 		val = peek32(CRT_DISPLAY_CTRL) & ~CRT_DISPLAY_CTRL_BLANK;
466 		poke32(CRT_DISPLAY_CTRL, val | crtdb);
467 	}
468 
469 	if (output->paths & sm750_panel) {
470 		unsigned int val = peek32(PANEL_DISPLAY_CTRL);
471 
472 		val &= ~PANEL_DISPLAY_CTRL_DATA;
473 		val |= pps;
474 		poke32(PANEL_DISPLAY_CTRL, val);
475 	}
476 
477 	return 0;
478 }
479 
hw_sm750_initAccel(struct sm750_dev * sm750_dev)480 void hw_sm750_initAccel(struct sm750_dev *sm750_dev)
481 {
482 	u32 reg;
483 
484 	sm750_enable_2d_engine(1);
485 
486 	if (sm750_get_chip_type() == SM750LE) {
487 		reg = peek32(DE_STATE1);
488 		reg |= DE_STATE1_DE_ABORT;
489 		poke32(DE_STATE1, reg);
490 
491 		reg = peek32(DE_STATE1);
492 		reg &= ~DE_STATE1_DE_ABORT;
493 		poke32(DE_STATE1, reg);
494 
495 	} else {
496 		/* engine reset */
497 		reg = peek32(SYSTEM_CTRL);
498 		reg |= SYSTEM_CTRL_DE_ABORT;
499 		poke32(SYSTEM_CTRL, reg);
500 
501 		reg = peek32(SYSTEM_CTRL);
502 		reg &= ~SYSTEM_CTRL_DE_ABORT;
503 		poke32(SYSTEM_CTRL, reg);
504 	}
505 
506 	/* call 2d init */
507 	sm750_dev->accel.de_init(&sm750_dev->accel);
508 }
509 
hw_sm750le_deWait(void)510 int hw_sm750le_deWait(void)
511 {
512 	int i = 0x10000000;
513 	unsigned int mask = DE_STATE2_DE_STATUS_BUSY | DE_STATE2_DE_FIFO_EMPTY |
514 			    DE_STATE2_DE_MEM_FIFO_EMPTY;
515 
516 	while (i--) {
517 		unsigned int val = peek32(DE_STATE2);
518 
519 		if ((val & mask) ==
520 		    (DE_STATE2_DE_FIFO_EMPTY | DE_STATE2_DE_MEM_FIFO_EMPTY))
521 			return 0;
522 	}
523 	/* timeout error */
524 	return -1;
525 }
526 
hw_sm750_deWait(void)527 int hw_sm750_deWait(void)
528 {
529 	int i = 0x10000000;
530 	unsigned int mask = SYSTEM_CTRL_DE_STATUS_BUSY |
531 			    SYSTEM_CTRL_DE_FIFO_EMPTY |
532 			    SYSTEM_CTRL_DE_MEM_FIFO_EMPTY;
533 
534 	while (i--) {
535 		unsigned int val = peek32(SYSTEM_CTRL);
536 
537 		if ((val & mask) ==
538 		    (SYSTEM_CTRL_DE_FIFO_EMPTY | SYSTEM_CTRL_DE_MEM_FIFO_EMPTY))
539 			return 0;
540 	}
541 	/* timeout error */
542 	return -1;
543 }
544 
hw_sm750_pan_display(struct lynxfb_crtc * crtc,const struct fb_var_screeninfo * var,const struct fb_info * info)545 int hw_sm750_pan_display(struct lynxfb_crtc *crtc,
546 			 const struct fb_var_screeninfo *var,
547 			 const struct fb_info *info)
548 {
549 	u32 total;
550 	/* check params */
551 	if ((var->xoffset + var->xres > var->xres_virtual) ||
552 	    (var->yoffset + var->yres > var->yres_virtual)) {
553 		return -EINVAL;
554 	}
555 
556 	total = var->yoffset * info->fix.line_length +
557 		((var->xoffset * var->bits_per_pixel) >> 3);
558 	total += crtc->o_screen;
559 	if (crtc->channel == sm750_primary) {
560 		poke32(PANEL_FB_ADDRESS,
561 		       peek32(PANEL_FB_ADDRESS) |
562 			       (total & PANEL_FB_ADDRESS_ADDRESS_MASK));
563 	} else {
564 		poke32(CRT_FB_ADDRESS,
565 		       peek32(CRT_FB_ADDRESS) |
566 			       (total & CRT_FB_ADDRESS_ADDRESS_MASK));
567 	}
568 	return 0;
569 }
570