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
nexedi
linux
Commits
b3c418bb
Commit
b3c418bb
authored
Aug 20, 2015
by
Ben Skeggs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
drm/nouveau/therm: switch to subdev printk macros
Signed-off-by:
Ben Skeggs
<
bskeggs@redhat.com
>
parent
c19e329d
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
72 additions
and
50 deletions
+72
-50
drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
+8
-5
drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
+12
-9
drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
+13
-9
drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
+3
-2
drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
+4
-4
drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
+10
-7
drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
+3
-1
drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
+19
-13
No files found.
drivers/gpu/drm/nouveau/nvkm/subdev/therm/base.c
View file @
b3c418bb
...
...
@@ -83,7 +83,8 @@ static void
nvkm_therm_update
(
struct
nvkm_therm
*
obj
,
int
mode
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_timer
*
tmr
=
nvkm_timer
(
therm
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
struct
nvkm_timer
*
tmr
=
subdev
->
device
->
timer
;
unsigned
long
flags
;
bool
immd
=
true
;
bool
poll
=
true
;
...
...
@@ -129,7 +130,7 @@ nvkm_therm_update(struct nvkm_therm *obj, int mode)
spin_unlock_irqrestore
(
&
therm
->
lock
,
flags
);
if
(
duty
>=
0
)
{
nv
_debug
(
therm
,
"FAN target request: %d%%
\n
"
,
duty
);
nv
km_debug
(
subdev
,
"FAN target request: %d%%
\n
"
,
duty
);
nvkm_therm_fan_set
(
&
therm
->
base
,
immd
,
duty
);
}
}
...
...
@@ -138,9 +139,10 @@ int
nvkm_therm_cstate
(
struct
nvkm_therm
*
obj
,
int
fan
,
int
dir
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
if
(
!
dir
||
(
dir
<
0
&&
fan
<
therm
->
cstate
)
||
(
dir
>
0
&&
fan
>
therm
->
cstate
))
{
nv
_debug
(
therm
,
"default fan speed -> %d%%
\n
"
,
fan
);
nv
km_debug
(
subdev
,
"default fan speed -> %d%%
\n
"
,
fan
);
therm
->
cstate
=
fan
;
nvkm_therm_update
(
&
therm
->
base
,
-
1
);
}
...
...
@@ -159,7 +161,8 @@ int
nvkm_therm_fan_mode
(
struct
nvkm_therm
*
obj
,
int
mode
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_device
*
device
=
nv_device
(
therm
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
static
const
char
*
name
[]
=
{
"disabled"
,
"manual"
,
...
...
@@ -181,7 +184,7 @@ nvkm_therm_fan_mode(struct nvkm_therm *obj, int mode)
if
(
therm
->
mode
==
mode
)
return
0
;
nv
_info
(
therm
,
"fan management: %s
\n
"
,
name
[
mode
]);
nv
km_debug
(
subdev
,
"fan management: %s
\n
"
,
name
[
mode
]);
nvkm_therm_update
(
&
therm
->
base
,
mode
);
return
0
;
}
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/fan.c
View file @
b3c418bb
...
...
@@ -32,7 +32,8 @@ static int
nvkm_fan_update
(
struct
nvkm_fan
*
fan
,
bool
immediate
,
int
target
)
{
struct
nvkm_therm_priv
*
therm
=
(
void
*
)
fan
->
parent
;
struct
nvkm_timer
*
tmr
=
nvkm_timer
(
therm
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
struct
nvkm_timer
*
tmr
=
subdev
->
device
->
timer
;
unsigned
long
flags
;
int
ret
=
0
;
int
duty
;
...
...
@@ -44,7 +45,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
target
=
max_t
(
u8
,
target
,
fan
->
bios
.
min_duty
);
target
=
min_t
(
u8
,
target
,
fan
->
bios
.
max_duty
);
if
(
fan
->
percent
!=
target
)
{
nv
_debug
(
therm
,
"FAN target: %d
\n
"
,
target
);
nv
km_debug
(
subdev
,
"FAN target: %d
\n
"
,
target
);
fan
->
percent
=
target
;
}
...
...
@@ -69,7 +70,7 @@ nvkm_fan_update(struct nvkm_fan *fan, bool immediate, int target)
duty
=
target
;
}
nv
_debug
(
therm
,
"FAN update: %d
\n
"
,
duty
);
nv
km_debug
(
subdev
,
"FAN update: %d
\n
"
,
duty
);
ret
=
fan
->
set
(
&
therm
->
base
,
duty
);
if
(
ret
)
{
spin_unlock_irqrestore
(
&
fan
->
lock
,
flags
);
...
...
@@ -228,8 +229,10 @@ int
nvkm_therm_fan_ctor
(
struct
nvkm_therm
*
obj
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_gpio
*
gpio
=
nvkm_gpio
(
therm
);
struct
nvkm_bios
*
bios
=
nvkm_bios
(
therm
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
struct
nvkm_gpio
*
gpio
=
device
->
gpio
;
struct
nvkm_bios
*
bios
=
device
->
bios
;
struct
dcb_gpio_func
func
;
int
ret
;
...
...
@@ -238,7 +241,7 @@ nvkm_therm_fan_ctor(struct nvkm_therm *obj)
if
(
ret
==
0
)
{
/* FIXME: is this really the place to perform such checks ? */
if
(
func
.
line
!=
16
&&
func
.
log
[
0
]
&
DCB_GPIO_LOG_DIR_IN
)
{
nv
_debug
(
therm
,
"GPIO_FAN is in input mode
\n
"
);
nv
km_debug
(
subdev
,
"GPIO_FAN is in input mode
\n
"
);
ret
=
-
EINVAL
;
}
else
{
ret
=
nvkm_fanpwm_create
(
&
therm
->
base
,
&
func
);
...
...
@@ -254,7 +257,7 @@ nvkm_therm_fan_ctor(struct nvkm_therm *obj)
return
ret
;
}
nv
_info
(
therm
,
"FAN control: %s
\n
"
,
therm
->
fan
->
type
);
nv
km_debug
(
subdev
,
"FAN control: %s
\n
"
,
therm
->
fan
->
type
);
/* read the current speed, it is useful when resuming */
therm
->
fan
->
percent
=
nvkm_therm_fan_get
(
&
therm
->
base
);
...
...
@@ -273,9 +276,9 @@ nvkm_therm_fan_ctor(struct nvkm_therm *obj)
nvkm_therm_fan_set_defaults
(
&
therm
->
base
);
nvbios_perf_fan_parse
(
bios
,
&
therm
->
fan
->
perf
);
if
(
!
nvbios_fan_parse
(
bios
,
&
therm
->
fan
->
bios
))
{
nv
_debug
(
therm
,
"parsing the fan table failed
\n
"
);
nv
km_debug
(
subdev
,
"parsing the fan table failed
\n
"
);
if
(
nvbios_therm_fan_parse
(
bios
,
&
therm
->
fan
->
bios
))
nv
_error
(
therm
,
"parsing both fan tables failed
\n
"
);
nv
km_error
(
subdev
,
"parsing both fan tables failed
\n
"
);
}
nvkm_therm_fan_safety_checks
(
&
therm
->
base
);
return
0
;
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/g84.c
View file @
b3c418bb
...
...
@@ -56,8 +56,9 @@ static void
g84_therm_program_alarms
(
struct
nvkm_therm
*
obj
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_device
*
device
=
therm
->
base
.
subdev
.
device
;
struct
nvbios_therm_sensor
*
sensor
=
&
therm
->
bios_sensor
;
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
therm
->
sensor
.
alarm_program_lock
,
flags
);
...
...
@@ -79,13 +80,16 @@ g84_therm_program_alarms(struct nvkm_therm *obj)
nvkm_wr32
(
device
,
0x20414
,
sensor
->
thrs_down_clock
.
temp
);
spin_unlock_irqrestore
(
&
therm
->
sensor
.
alarm_program_lock
,
flags
);
nv_debug
(
therm
,
"Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]
\n
"
,
sensor
->
thrs_fan_boost
.
temp
,
sensor
->
thrs_fan_boost
.
hysteresis
,
sensor
->
thrs_down_clock
.
temp
,
sensor
->
thrs_down_clock
.
hysteresis
,
sensor
->
thrs_critical
.
temp
,
sensor
->
thrs_critical
.
hysteresis
,
sensor
->
thrs_shutdown
.
temp
,
sensor
->
thrs_shutdown
.
hysteresis
);
nvkm_debug
(
subdev
,
"Programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]
\n
"
,
sensor
->
thrs_fan_boost
.
temp
,
sensor
->
thrs_fan_boost
.
hysteresis
,
sensor
->
thrs_down_clock
.
temp
,
sensor
->
thrs_down_clock
.
hysteresis
,
sensor
->
thrs_critical
.
temp
,
sensor
->
thrs_critical
.
hysteresis
,
sensor
->
thrs_shutdown
.
temp
,
sensor
->
thrs_shutdown
.
hysteresis
);
}
...
...
@@ -180,7 +184,7 @@ g84_therm_intr(struct nvkm_subdev *subdev)
}
if
(
intr
)
nv
_error
(
therm
,
"unhandled intr 0x
%08x
\n
"
,
intr
);
nv
km_error
(
subdev
,
"intr
%08x
\n
"
,
intr
);
/* ACK everything */
nvkm_wr32
(
device
,
0x20100
,
0xffffffff
);
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/gf110.c
View file @
b3c418bb
...
...
@@ -26,7 +26,8 @@
static
int
pwm_info
(
struct
nvkm_therm
*
therm
,
int
line
)
{
struct
nvkm_device
*
device
=
therm
->
subdev
.
device
;
struct
nvkm_subdev
*
subdev
=
&
therm
->
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
u32
gpio
=
nvkm_rd32
(
device
,
0x00d610
+
(
line
*
0x04
));
switch
(
gpio
&
0x000000c0
)
{
...
...
@@ -44,7 +45,7 @@ pwm_info(struct nvkm_therm *therm, int line)
break
;
}
nv
_error
(
therm
,
"GPIO %d unknown PWM: 0x
%08x
\n
"
,
line
,
gpio
);
nv
km_error
(
subdev
,
"GPIO %d unknown PWM:
%08x
\n
"
,
line
,
gpio
);
return
-
ENODEV
;
}
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/ic.c
View file @
b3c418bb
...
...
@@ -46,10 +46,10 @@ probe_monitoring_device(struct nvkm_i2c_port *i2c,
return
false
;
}
nv
_info
(
therm
,
"Found an %s at address 0x%x (controlled by lm_sensors, "
"temp offset %+i C)
\n
"
,
info
->
type
,
info
->
addr
,
sensor
->
offset_constant
);
nv
km_debug
(
&
therm
->
base
.
subdev
,
"Found an %s at address 0x%x (controlled by lm_sensors, "
"temp offset %+i C)
\n
"
,
info
->
type
,
info
->
addr
,
sensor
->
offset_constant
);
therm
->
ic
=
client
;
return
true
;
}
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv40.c
View file @
b3c418bb
...
...
@@ -109,12 +109,13 @@ nv40_temp_get(struct nvkm_therm *obj)
static
int
nv40_fan_pwm_ctrl
(
struct
nvkm_therm
*
therm
,
int
line
,
bool
enable
)
{
struct
nvkm_device
*
device
=
therm
->
subdev
.
device
;
struct
nvkm_subdev
*
subdev
=
&
therm
->
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
u32
mask
=
enable
?
0x80000000
:
0x0000000
;
if
(
line
==
2
)
nvkm_mask
(
device
,
0x0010f0
,
0x80000000
,
mask
);
else
if
(
line
==
9
)
nvkm_mask
(
device
,
0x0015f4
,
0x80000000
,
mask
);
else
{
nv
_error
(
therm
,
"unknown pwm ctrl for gpio %d
\n
"
,
line
);
nv
km_error
(
subdev
,
"unknown pwm ctrl for gpio %d
\n
"
,
line
);
return
-
ENODEV
;
}
return
0
;
...
...
@@ -123,7 +124,8 @@ nv40_fan_pwm_ctrl(struct nvkm_therm *therm, int line, bool enable)
static
int
nv40_fan_pwm_get
(
struct
nvkm_therm
*
therm
,
int
line
,
u32
*
divs
,
u32
*
duty
)
{
struct
nvkm_device
*
device
=
therm
->
subdev
.
device
;
struct
nvkm_subdev
*
subdev
=
&
therm
->
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
if
(
line
==
2
)
{
u32
reg
=
nvkm_rd32
(
device
,
0x0010f0
);
if
(
reg
&
0x80000000
)
{
...
...
@@ -140,7 +142,7 @@ nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
return
0
;
}
}
else
{
nv
_error
(
therm
,
"unknown pwm ctrl for gpio %d
\n
"
,
line
);
nv
km_error
(
subdev
,
"unknown pwm ctrl for gpio %d
\n
"
,
line
);
return
-
ENODEV
;
}
...
...
@@ -150,7 +152,8 @@ nv40_fan_pwm_get(struct nvkm_therm *therm, int line, u32 *divs, u32 *duty)
static
int
nv40_fan_pwm_set
(
struct
nvkm_therm
*
therm
,
int
line
,
u32
divs
,
u32
duty
)
{
struct
nvkm_device
*
device
=
therm
->
subdev
.
device
;
struct
nvkm_subdev
*
subdev
=
&
therm
->
subdev
;
struct
nvkm_device
*
device
=
subdev
->
device
;
if
(
line
==
2
)
{
nvkm_mask
(
device
,
0x0010f0
,
0x7fff7fff
,
(
duty
<<
16
)
|
divs
);
}
else
...
...
@@ -158,7 +161,7 @@ nv40_fan_pwm_set(struct nvkm_therm *therm, int line, u32 divs, u32 duty)
nvkm_wr32
(
device
,
0x0015f8
,
divs
);
nvkm_mask
(
device
,
0x0015f4
,
0x7fffffff
,
duty
);
}
else
{
nv
_error
(
therm
,
"unknown pwm ctrl for gpio %d
\n
"
,
line
);
nv
km_error
(
subdev
,
"unknown pwm ctrl for gpio %d
\n
"
,
line
);
return
-
ENODEV
;
}
...
...
@@ -177,7 +180,7 @@ nv40_therm_intr(struct nvkm_subdev *subdev)
/* ack all IRQs */
nvkm_wr32
(
device
,
0x1100
,
0x70000
);
nv
_error
(
therm
,
"THERM received an IRQ: stat = %x
\n
"
,
stat
);
nv
km_error
(
subdev
,
"THERM received an IRQ: stat = %x
\n
"
,
stat
);
}
static
int
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/nv50.c
View file @
b3c418bb
...
...
@@ -27,6 +27,8 @@
static
int
pwm_info
(
struct
nvkm_therm
*
therm
,
int
*
line
,
int
*
ctrl
,
int
*
indx
)
{
struct
nvkm_subdev
*
subdev
=
&
therm
->
subdev
;
if
(
*
line
==
0x04
)
{
*
ctrl
=
0x00e100
;
*
line
=
4
;
...
...
@@ -42,7 +44,7 @@ pwm_info(struct nvkm_therm *therm, int *line, int *ctrl, int *indx)
*
line
=
0
;
*
indx
=
0
;
}
else
{
nv
_error
(
therm
,
"unknown pwm ctrl for gpio %d
\n
"
,
*
line
);
nv
km_error
(
subdev
,
"unknown pwm ctrl for gpio %d
\n
"
,
*
line
);
return
-
ENODEV
;
}
...
...
drivers/gpu/drm/nouveau/nvkm/subdev/therm/temp.c
View file @
b3c418bb
...
...
@@ -88,6 +88,7 @@ nvkm_therm_sensor_event(struct nvkm_therm *obj, enum nvkm_therm_thrs thrs,
enum
nvkm_therm_thrs_direction
dir
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
bool
active
;
const
char
*
thresolds
[]
=
{
"fanboost"
,
"downclock"
,
"critical"
,
"shutdown"
...
...
@@ -98,11 +99,12 @@ nvkm_therm_sensor_event(struct nvkm_therm *obj, enum nvkm_therm_thrs thrs,
return
;
if
(
dir
==
NVKM_THERM_THRS_FALLING
)
nv_info
(
therm
,
"temperature (%i C) went below the '%s' threshold
\n
"
,
temperature
,
thresolds
[
thrs
]);
nvkm_info
(
subdev
,
"temperature (%i C) went below the '%s' threshold
\n
"
,
temperature
,
thresolds
[
thrs
]);
else
nv
_info
(
therm
,
"temperature (%i C) hit the '%s' threshold
\n
"
,
temperature
,
thresolds
[
thrs
]);
nv
km_info
(
subdev
,
"temperature (%i C) hit the '%s' threshold
\n
"
,
temperature
,
thresolds
[
thrs
]);
active
=
(
dir
==
NVKM_THERM_THRS_RISING
);
switch
(
thrs
)
{
...
...
@@ -200,13 +202,16 @@ nvkm_therm_program_alarms_polling(struct nvkm_therm *obj)
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvbios_therm_sensor
*
sensor
=
&
therm
->
bios_sensor
;
nv_debug
(
therm
,
"programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]
\n
"
,
sensor
->
thrs_fan_boost
.
temp
,
sensor
->
thrs_fan_boost
.
hysteresis
,
sensor
->
thrs_down_clock
.
temp
,
sensor
->
thrs_down_clock
.
hysteresis
,
sensor
->
thrs_critical
.
temp
,
sensor
->
thrs_critical
.
hysteresis
,
sensor
->
thrs_shutdown
.
temp
,
sensor
->
thrs_shutdown
.
hysteresis
);
nvkm_debug
(
&
therm
->
base
.
subdev
,
"programmed thresholds [ %d(%d), %d(%d), %d(%d), %d(%d) ]
\n
"
,
sensor
->
thrs_fan_boost
.
temp
,
sensor
->
thrs_fan_boost
.
hysteresis
,
sensor
->
thrs_down_clock
.
temp
,
sensor
->
thrs_down_clock
.
hysteresis
,
sensor
->
thrs_critical
.
temp
,
sensor
->
thrs_critical
.
hysteresis
,
sensor
->
thrs_shutdown
.
temp
,
sensor
->
thrs_shutdown
.
hysteresis
);
alarm_timer_callback
(
&
therm
->
sensor
.
therm_poll_alarm
);
}
...
...
@@ -238,13 +243,14 @@ nvkm_therm_sensor_preinit(struct nvkm_therm *therm)
if
(
therm
->
temp_get
(
therm
)
<
0
)
sensor_avail
=
"no"
;
nv
_info
(
therm
,
"internal sensor: %s
\n
"
,
sensor_avail
);
nv
km_debug
(
&
therm
->
subdev
,
"internal sensor: %s
\n
"
,
sensor_avail
);
}
int
nvkm_therm_sensor_ctor
(
struct
nvkm_therm
*
obj
)
{
struct
nvkm_therm_priv
*
therm
=
container_of
(
obj
,
typeof
(
*
therm
),
base
);
struct
nvkm_subdev
*
subdev
=
&
therm
->
base
.
subdev
;
struct
nvkm_bios
*
bios
=
nvkm_bios
(
therm
);
nvkm_alarm_init
(
&
therm
->
sensor
.
therm_poll_alarm
,
alarm_timer_callback
);
...
...
@@ -252,7 +258,7 @@ nvkm_therm_sensor_ctor(struct nvkm_therm *obj)
nvkm_therm_temp_set_defaults
(
&
therm
->
base
);
if
(
nvbios_therm_sensor_parse
(
bios
,
NVBIOS_THERM_DOMAIN_CORE
,
&
therm
->
bios_sensor
))
nv
_error
(
therm
,
"nvbios_therm_sensor_parse failed
\n
"
);
nv
km_error
(
subdev
,
"nvbios_therm_sensor_parse failed
\n
"
);
nvkm_therm_temp_safety_checks
(
&
therm
->
base
);
return
0
;
...
...
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