Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
0a2cdd88
Commit
0a2cdd88
authored
Oct 11, 2007
by
Josh Boyer
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'virtex-for-2.6.24' of
git://git.secretlab.ca/git/linux-2.6-virtex
into for-2.6.24-4xx
parents
cdec12ae
17c5c209
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
203 additions
and
84 deletions
+203
-84
MAINTAINERS
MAINTAINERS
+1
-1
arch/powerpc/boot/uartlite.c
arch/powerpc/boot/uartlite.c
+2
-2
arch/powerpc/sysdev/Makefile
arch/powerpc/sysdev/Makefile
+1
-1
drivers/video/xilinxfb.c
drivers/video/xilinxfb.c
+199
-80
No files found.
MAINTAINERS
View file @
0a2cdd88
...
@@ -2306,7 +2306,7 @@ S: Maintained
...
@@ -2306,7 +2306,7 @@ S: Maintained
LINUX FOR POWERPC EMBEDDED XILINX VIRTEX
LINUX FOR POWERPC EMBEDDED XILINX VIRTEX
P: Grant Likely
P: Grant Likely
M: grant.likely@secretlab.ca
M: grant.likely@secretlab.ca
W: http://w
ww.secretlab.ca/
W: http://w
iki.secretlab.ca/index.php/Linux_on_Xilinx_Virtex
L: linuxppc-dev@ozlabs.org
L: linuxppc-dev@ozlabs.org
S: Maintained
S: Maintained
...
...
arch/powerpc/boot/uartlite.c
View file @
0a2cdd88
...
@@ -45,8 +45,8 @@ static void uartlite_putc(unsigned char c)
...
@@ -45,8 +45,8 @@ static void uartlite_putc(unsigned char c)
static
unsigned
char
uartlite_getc
(
void
)
static
unsigned
char
uartlite_getc
(
void
)
{
{
u32
reg
=
ULITE_STATUS_RXVALID
;
u32
reg
=
0
;
while
(
reg
&
ULITE_STATUS_RXVALID
)
/* spin on
RXVALID bit */
while
(
!
(
reg
&
ULITE_STATUS_RXVALID
))
/* spin waiting for
RXVALID bit */
reg
=
in_be32
(
reg_base
+
ULITE_STATUS
);
reg
=
in_be32
(
reg_base
+
ULITE_STATUS
);
return
in_be32
(
reg_base
+
ULITE_RX
);
return
in_be32
(
reg_base
+
ULITE_RX
);
}
}
...
...
arch/powerpc/sysdev/Makefile
View file @
0a2cdd88
...
@@ -6,7 +6,6 @@ mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o
...
@@ -6,7 +6,6 @@ mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o
obj-$(CONFIG_MPIC)
+=
mpic.o
$
(
mpic-msi-obj-y
)
obj-$(CONFIG_MPIC)
+=
mpic.o
$
(
mpic-msi-obj-y
)
obj-$(CONFIG_PPC_MPC106)
+=
grackle.o
obj-$(CONFIG_PPC_MPC106)
+=
grackle.o
obj-$(CONFIG_PPC_DCR)
+=
dcr.o
obj-$(CONFIG_PPC_DCR_NATIVE)
+=
dcr-low.o
obj-$(CONFIG_PPC_DCR_NATIVE)
+=
dcr-low.o
obj-$(CONFIG_PPC_PMI)
+=
pmi.o
obj-$(CONFIG_PPC_PMI)
+=
pmi.o
obj-$(CONFIG_U3_DART)
+=
dart_iommu.o
obj-$(CONFIG_U3_DART)
+=
dart_iommu.o
...
@@ -33,6 +32,7 @@ endif
...
@@ -33,6 +32,7 @@ endif
ifeq
($(ARCH),powerpc)
ifeq
($(ARCH),powerpc)
obj-$(CONFIG_CPM)
+=
cpm_common.o
obj-$(CONFIG_CPM)
+=
cpm_common.o
obj-$(CONFIG_CPM2)
+=
cpm2_common.o cpm2_pic.o
obj-$(CONFIG_CPM2)
+=
cpm2_common.o cpm2_pic.o
obj-$(CONFIG_PPC_DCR)
+=
dcr.o
obj-$(CONFIG_8xx)
+=
mpc8xx_pic.o commproc.o
obj-$(CONFIG_8xx)
+=
mpc8xx_pic.o commproc.o
obj-$(CONFIG_UCODE_PATCH)
+=
micropatch.o
obj-$(CONFIG_UCODE_PATCH)
+=
micropatch.o
endif
endif
drivers/video/xilinxfb.c
View file @
0a2cdd88
...
@@ -6,9 +6,12 @@
...
@@ -6,9 +6,12 @@
* Author: MontaVista Software, Inc.
* Author: MontaVista Software, Inc.
* source@mvista.com
* source@mvista.com
*
*
* 2002-2007 (c) MontaVista Software, Inc. This file is licensed under the
* 2002-2007 (c) MontaVista Software, Inc.
* terms of the GNU General Public License version 2. This program is licensed
* 2007 (c) Secret Lab Technologies, Ltd.
* "as is" without any warranty of any kind, whether express or implied.
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
*/
/*
/*
...
@@ -18,6 +21,7 @@
...
@@ -18,6 +21,7 @@
* Geert Uytterhoeven.
* Geert Uytterhoeven.
*/
*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kernel.h>
#include <linux/version.h>
#include <linux/version.h>
...
@@ -28,7 +32,10 @@
...
@@ -28,7 +32,10 @@
#include <linux/init.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/dma-mapping.h>
#include <linux/platform_device.h>
#include <linux/platform_device.h>
#if defined(CONFIG_OF)
#include <linux/of_device.h>
#include <linux/of_platform.h>
#endif
#include <asm/io.h>
#include <asm/io.h>
#include <linux/xilinxfb.h>
#include <linux/xilinxfb.h>
...
@@ -111,7 +118,7 @@ struct xilinxfb_drvdata {
...
@@ -111,7 +118,7 @@ struct xilinxfb_drvdata {
u32
regs_phys
;
/* phys. address of the control registers */
u32
regs_phys
;
/* phys. address of the control registers */
u32
__iomem
*
regs
;
/* virt. address of the control registers */
u32
__iomem
*
regs
;
/* virt. address of the control registers */
unsigned
char
__iomem
*
fb_virt
;
/* virt. address of the frame buffer */
void
*
fb_virt
;
/* virt. address of the frame buffer */
dma_addr_t
fb_phys
;
/* phys. address of the frame buffer */
dma_addr_t
fb_phys
;
/* phys. address of the frame buffer */
u32
reg_ctrl_default
;
u32
reg_ctrl_default
;
...
@@ -195,130 +202,120 @@ static struct fb_ops xilinxfb_ops =
...
@@ -195,130 +202,120 @@ static struct fb_ops xilinxfb_ops =
.
fb_imageblit
=
cfb_imageblit
,
.
fb_imageblit
=
cfb_imageblit
,
};
};
/* === The device driver === */
/* ---------------------------------------------------------------------
* Bus independent setup/teardown
*/
static
int
static
int
xilinxfb_assign
(
struct
device
*
dev
,
unsigned
long
physaddr
,
xilinxfb_drv_probe
(
struct
device
*
dev
)
int
width_mm
,
int
height_mm
,
int
rotate
)
{
{
struct
platform_device
*
pdev
;
struct
xilinxfb_platform_data
*
pdata
;
struct
xilinxfb_drvdata
*
drvdata
;
struct
xilinxfb_drvdata
*
drvdata
;
struct
resource
*
regs_res
;
int
rc
;
int
retval
;
if
(
!
dev
)
return
-
EINVAL
;
pdev
=
to_platform_device
(
dev
);
pdata
=
pdev
->
dev
.
platform_data
;
/* Allocate the driver data region */
drvdata
=
kzalloc
(
sizeof
(
*
drvdata
),
GFP_KERNEL
);
drvdata
=
kzalloc
(
sizeof
(
*
drvdata
),
GFP_KERNEL
);
if
(
!
drvdata
)
{
if
(
!
drvdata
)
{
printk
(
KERN_ERR
"Couldn't allocate device private record
\n
"
);
dev_err
(
dev
,
"Couldn't allocate device private record
\n
"
);
return
-
ENOMEM
;
return
-
ENOMEM
;
}
}
dev_set_drvdata
(
dev
,
drvdata
);
dev_set_drvdata
(
dev
,
drvdata
);
/* Map the control registers in */
/* Map the control registers in */
regs_res
=
platform_get_resource
(
pdev
,
IORESOURCE_IO
,
0
);
if
(
!
request_mem_region
(
physaddr
,
8
,
DRIVER_NAME
))
{
if
(
!
regs_res
||
(
regs_res
->
end
-
regs_res
->
start
+
1
<
8
))
{
dev_err
(
dev
,
"Couldn't lock memory region at 0x%08lX
\n
"
,
printk
(
KERN_ERR
"Couldn't get registers resource
\n
"
);
physaddr
);
r
etval
=
-
EFAULT
;
r
c
=
-
ENODEV
;
goto
failed1
;
goto
err_region
;
}
}
drvdata
->
regs_phys
=
physaddr
;
if
(
!
request_mem_region
(
regs_res
->
start
,
8
,
DRIVER_NAME
))
{
drvdata
->
regs
=
ioremap
(
physaddr
,
8
);
printk
(
KERN_ERR
if
(
!
drvdata
->
regs
)
{
"Couldn't lock memory region at 0x%08
X
\n
"
,
dev_err
(
dev
,
"Couldn't lock memory region at 0x%08l
X
\n
"
,
regs_res
->
start
);
physaddr
);
r
etval
=
-
EBUSY
;
r
c
=
-
ENODEV
;
goto
failed1
;
goto
err_map
;
}
}
drvdata
->
regs
=
(
u32
__iomem
*
)
ioremap
(
regs_res
->
start
,
8
);
drvdata
->
regs_phys
=
regs_res
->
start
;
/* Allocate the framebuffer memory */
/* Allocate the framebuffer memory */
drvdata
->
fb_virt
=
dma_alloc_coherent
(
dev
,
PAGE_ALIGN
(
FB_SIZE
),
drvdata
->
fb_virt
=
dma_alloc_coherent
(
dev
,
PAGE_ALIGN
(
FB_SIZE
),
&
drvdata
->
fb_phys
,
GFP_KERNEL
);
&
drvdata
->
fb_phys
,
GFP_KERNEL
);
if
(
!
drvdata
->
fb_virt
)
{
if
(
!
drvdata
->
fb_virt
)
{
printk
(
KERN_ERR
"Could not allocate frame buffer memory
\n
"
);
dev_err
(
dev
,
"Could not allocate frame buffer memory
\n
"
);
r
etval
=
-
ENOMEM
;
r
c
=
-
ENOMEM
;
goto
failed2
;
goto
err_fbmem
;
}
}
/* Clear (turn to black) the framebuffer */
/* Clear (turn to black) the framebuffer */
memset_io
((
void
*
)
drvdata
->
fb_virt
,
0
,
FB_SIZE
);
memset_io
((
void
__iomem
*
)
drvdata
->
fb_virt
,
0
,
FB_SIZE
);
/* Tell the hardware where the frame buffer is */
/* Tell the hardware where the frame buffer is */
xilinx_fb_out_be32
(
drvdata
,
REG_FB_ADDR
,
drvdata
->
fb_phys
);
xilinx_fb_out_be32
(
drvdata
,
REG_FB_ADDR
,
drvdata
->
fb_phys
);
/* Turn on the display */
/* Turn on the display */
drvdata
->
reg_ctrl_default
=
REG_CTRL_ENABLE
;
drvdata
->
reg_ctrl_default
=
REG_CTRL_ENABLE
;
if
(
pdata
&&
pdata
->
rotate_screen
)
if
(
rotate
)
drvdata
->
reg_ctrl_default
|=
REG_CTRL_ROTATE
;
drvdata
->
reg_ctrl_default
|=
REG_CTRL_ROTATE
;
xilinx_fb_out_be32
(
drvdata
,
REG_CTRL
,
drvdata
->
reg_ctrl_default
);
xilinx_fb_out_be32
(
drvdata
,
REG_CTRL
,
drvdata
->
reg_ctrl_default
);
/* Fill struct fb_info */
/* Fill struct fb_info */
drvdata
->
info
.
device
=
dev
;
drvdata
->
info
.
device
=
dev
;
drvdata
->
info
.
screen_base
=
drvdata
->
fb_virt
;
drvdata
->
info
.
screen_base
=
(
void
__iomem
*
)
drvdata
->
fb_virt
;
drvdata
->
info
.
fbops
=
&
xilinxfb_ops
;
drvdata
->
info
.
fbops
=
&
xilinxfb_ops
;
drvdata
->
info
.
fix
=
xilinx_fb_fix
;
drvdata
->
info
.
fix
=
xilinx_fb_fix
;
drvdata
->
info
.
fix
.
smem_start
=
drvdata
->
fb_phys
;
drvdata
->
info
.
fix
.
smem_start
=
drvdata
->
fb_phys
;
drvdata
->
info
.
pseudo_palette
=
drvdata
->
pseudo_palette
;
drvdata
->
info
.
pseudo_palette
=
drvdata
->
pseudo_palette
;
drvdata
->
info
.
flags
=
FBINFO_DEFAULT
;
drvdata
->
info
.
var
=
xilinx_fb_var
;
xilinx_fb_var
.
height
=
height_mm
;
xilinx_fb_var
.
width
=
width_mm
;
if
(
fb_alloc_cmap
(
&
drvdata
->
info
.
cmap
,
PALETTE_ENTRIES_NO
,
0
)
<
0
)
{
/* Allocate a colour map */
printk
(
KERN_ERR
"Fail to allocate colormap (%d entries)
\n
"
,
rc
=
fb_alloc_cmap
(
&
drvdata
->
info
.
cmap
,
PALETTE_ENTRIES_NO
,
0
);
if
(
rc
)
{
dev_err
(
dev
,
"Fail to allocate colormap (%d entries)
\n
"
,
PALETTE_ENTRIES_NO
);
PALETTE_ENTRIES_NO
);
retval
=
-
EFAULT
;
goto
err_cmap
;
goto
failed3
;
}
}
drvdata
->
info
.
flags
=
FBINFO_DEFAULT
;
if
(
pdata
)
{
xilinx_fb_var
.
height
=
pdata
->
screen_height_mm
;
xilinx_fb_var
.
width
=
pdata
->
screen_width_mm
;
}
drvdata
->
info
.
var
=
xilinx_fb_var
;
/* Register new frame buffer */
/* Register new frame buffer */
if
(
register_framebuffer
(
&
drvdata
->
info
)
<
0
)
{
rc
=
register_framebuffer
(
&
drvdata
->
info
);
printk
(
KERN_ERR
"Could not register frame buffer
\n
"
);
if
(
rc
)
{
retval
=
-
EINVAL
;
dev_err
(
dev
,
"Could not register frame buffer
\n
"
)
;
goto
failed4
;
goto
err_regfb
;
}
}
/* Put a banner in the log (for DEBUG) */
dev_dbg
(
dev
,
"regs: phys=%lx, virt=%p
\n
"
,
physaddr
,
drvdata
->
regs
);
dev_dbg
(
dev
,
"fb: phys=%p, virt=%p, size=%x
\n
"
,
(
void
*
)
drvdata
->
fb_phys
,
drvdata
->
fb_virt
,
FB_SIZE
);
return
0
;
/* success */
return
0
;
/* success */
failed4
:
err_regfb
:
fb_dealloc_cmap
(
&
drvdata
->
info
.
cmap
);
fb_dealloc_cmap
(
&
drvdata
->
info
.
cmap
);
failed3
:
err_cmap
:
dma_free_coherent
(
dev
,
PAGE_ALIGN
(
FB_SIZE
),
drvdata
->
fb_virt
,
dma_free_coherent
(
dev
,
PAGE_ALIGN
(
FB_SIZE
),
drvdata
->
fb_virt
,
drvdata
->
fb_phys
);
drvdata
->
fb_phys
);
/* Turn off the display */
/* Turn off the display */
xilinx_fb_out_be32
(
drvdata
,
REG_CTRL
,
0
);
xilinx_fb_out_be32
(
drvdata
,
REG_CTRL
,
0
);
err_fbmem:
iounmap
(
drvdata
->
regs
);
iounmap
(
drvdata
->
regs
);
failed2
:
err_map
:
release_mem_region
(
regs_res
->
start
,
8
);
release_mem_region
(
physaddr
,
8
);
failed1
:
err_region
:
kfree
(
drvdata
);
kfree
(
drvdata
);
dev_set_drvdata
(
dev
,
NULL
);
dev_set_drvdata
(
dev
,
NULL
);
return
r
etval
;
return
r
c
;
}
}
static
int
static
int
xilinxfb_release
(
struct
device
*
dev
)
xilinxfb_drv_remove
(
struct
device
*
dev
)
{
{
struct
xilinxfb_drvdata
*
drvdata
;
struct
xilinxfb_drvdata
*
drvdata
=
dev_get_drvdata
(
dev
);
if
(
!
dev
)
return
-
ENODEV
;
drvdata
=
(
struct
xilinxfb_drvdata
*
)
dev_get_drvdata
(
dev
);
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
#if !defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_LOGO)
xilinx_fb_blank
(
VESA_POWERDOWN
,
&
drvdata
->
info
);
xilinx_fb_blank
(
VESA_POWERDOWN
,
&
drvdata
->
info
);
...
@@ -343,29 +340,151 @@ xilinxfb_drv_remove(struct device *dev)
...
@@ -343,29 +340,151 @@ xilinxfb_drv_remove(struct device *dev)
return
0
;
return
0
;
}
}
/* ---------------------------------------------------------------------
* Platform bus binding
*/
static
int
xilinxfb_platform_probe
(
struct
platform_device
*
pdev
)
{
struct
xilinxfb_platform_data
*
pdata
;
struct
resource
*
res
;
int
width_mm
=
0
;
int
height_mm
=
0
;
int
rotate
=
0
;
/* Find the registers address */
res
=
platform_get_resource
(
pdev
,
IORESOURCE_IO
,
0
);
if
(
!
res
)
{
dev_err
(
&
pdev
->
dev
,
"Couldn't get registers resource
\n
"
);
return
-
ENODEV
;
}
/* If a pdata structure is provided, then extract the parameters */
pdata
=
pdev
->
dev
.
platform_data
;
if
(
pdata
)
{
height_mm
=
pdata
->
screen_height_mm
;
width_mm
=
pdata
->
screen_width_mm
;
rotate
=
pdata
->
rotate_screen
?
1
:
0
;
}
return
xilinxfb_assign
(
&
pdev
->
dev
,
res
->
start
,
width_mm
,
height_mm
,
rotate
);
}
static
int
xilinxfb_platform_remove
(
struct
platform_device
*
pdev
)
{
return
xilinxfb_release
(
&
pdev
->
dev
);
}
static
struct
device_driver
xilinxfb_driver
=
{
.
name
=
DRIVER_NAME
,
.
bus
=
&
platform_bus_type
,
.
probe
=
xilinxfb_drv_probe
,
static
struct
platform_driver
xilinxfb_platform_driver
=
{
.
remove
=
xilinxfb_drv_remove
.
probe
=
xilinxfb_platform_probe
,
.
remove
=
xilinxfb_platform_remove
,
.
driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
DRIVER_NAME
,
},
};
};
/* ---------------------------------------------------------------------
* OF bus binding
*/
#if defined(CONFIG_OF)
static
int
__devinit
xilinxfb_of_probe
(
struct
of_device
*
op
,
const
struct
of_device_id
*
match
)
{
struct
resource
res
;
const
u32
*
prop
;
int
width
=
0
,
height
=
0
,
rotate
=
0
;
int
size
,
rc
;
dev_dbg
(
&
op
->
dev
,
"xilinxfb_of_probe(%p, %p)
\n
"
,
op
,
match
);
rc
=
of_address_to_resource
(
op
->
node
,
0
,
&
res
);
if
(
rc
)
{
dev_err
(
&
op
->
dev
,
"invalid address
\n
"
);
return
rc
;
}
prop
=
of_get_property
(
op
->
node
,
"display-number"
,
&
size
);
if
((
prop
)
&&
(
size
>=
sizeof
(
u32
)
*
2
))
{
width
=
prop
[
0
];
height
=
prop
[
1
];
}
if
(
of_find_property
(
op
->
node
,
"rotate-display"
,
NULL
))
rotate
=
1
;
return
xilinxfb_assign
(
&
op
->
dev
,
res
.
start
,
width
,
height
,
rotate
);
}
static
int
__devexit
xilinxfb_of_remove
(
struct
of_device
*
op
)
{
return
xilinxfb_release
(
&
op
->
dev
);
}
/* Match table for of_platform binding */
static
struct
of_device_id
__devinit
xilinxfb_of_match
[]
=
{
{
.
compatible
=
"xilinx,ml300-fb"
,
},
{},
};
MODULE_DEVICE_TABLE
(
of
,
xilinxfb_of_match
);
static
struct
of_platform_driver
xilinxfb_of_driver
=
{
.
owner
=
THIS_MODULE
,
.
name
=
DRIVER_NAME
,
.
match_table
=
xilinxfb_of_match
,
.
probe
=
xilinxfb_of_probe
,
.
remove
=
__devexit_p
(
xilinxfb_of_remove
),
.
driver
=
{
.
name
=
DRIVER_NAME
,
},
};
/* Registration helpers to keep the number of #ifdefs to a minimum */
static
inline
int
__init
xilinxfb_of_register
(
void
)
{
pr_debug
(
"xilinxfb: calling of_register_platform_driver()
\n
"
);
return
of_register_platform_driver
(
&
xilinxfb_of_driver
);
}
static
inline
void
__exit
xilinxfb_of_unregister
(
void
)
{
of_unregister_platform_driver
(
&
xilinxfb_of_driver
);
}
#else
/* CONFIG_OF */
/* CONFIG_OF not enabled; do nothing helpers */
static
inline
int
__init
xilinxfb_of_register
(
void
)
{
return
0
;
}
static
inline
void
__exit
xilinxfb_of_unregister
(
void
)
{
}
#endif
/* CONFIG_OF */
/* ---------------------------------------------------------------------
* Module setup and teardown
*/
static
int
__init
static
int
__init
xilinxfb_init
(
void
)
xilinxfb_init
(
void
)
{
{
/*
int
rc
;
* No kernel boot options used,
rc
=
xilinxfb_of_register
();
* so we just need to register the driver
if
(
rc
)
*/
return
rc
;
return
driver_register
(
&
xilinxfb_driver
);
rc
=
platform_driver_register
(
&
xilinxfb_platform_driver
);
if
(
rc
)
xilinxfb_of_unregister
();
return
rc
;
}
}
static
void
__exit
static
void
__exit
xilinxfb_cleanup
(
void
)
xilinxfb_cleanup
(
void
)
{
{
driver_unregister
(
&
xilinxfb_driver
);
platform_driver_unregister
(
&
xilinxfb_platform_driver
);
xilinxfb_of_unregister
();
}
}
module_init
(
xilinxfb_init
);
module_init
(
xilinxfb_init
);
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment