Commit 567a6d5e authored by Kristina Martšenko's avatar Kristina Martšenko Committed by Sasha Levin

iio: mxs-lradc: separate touchscreen and buffer virtual channels

commit f81197b8 upstream.

The touchscreen was initially designed [1] to map all of its physical
channels to one virtual channel, leaving buffered capture to use the
remaining 7 virtual channels. When the touchscreen was reimplemented
[2], it was made to use four virtual channels, which overlap and
conflict with the channels the buffer uses.

As a result, when the buffer is enabled, the touchscreen's virtual
channels are remapped to whichever physical channels the buffer was
configured with, causing the touchscreen to read those instead of the
touch measurement channels. Effectively the touchscreen stops working.

So here we separate the channels again, giving the touchscreen 2 virtual
channels and the buffer 6. We can't give the touchscreen just 1 channel
as before, as the current pressure calculation requires 2 channels to be
read at the same time.

This makes the touchscreen continue to work during buffered capture. It
has been tested on i.MX28, but not on i.MX23.

[1] 06ddd353 ("iio: mxs: Implement support for touchscreen")
[2] dee05308 ("Staging/iio/adc/touchscreen/MXS: add interrupt driven
touch detection")
Signed-off-by: default avatarKristina Martšenko <kristina.martsenko@gmail.com>
Reviewed-by: default avatarMarek Vasut <marex@denx.de>
Signed-off-by: default avatarJonathan Cameron <jic23@kernel.org>
Signed-off-by: default avatarSasha Levin <sasha.levin@oracle.com>
parent c0d65db7
...@@ -214,11 +214,14 @@ struct mxs_lradc { ...@@ -214,11 +214,14 @@ struct mxs_lradc {
unsigned long is_divided; unsigned long is_divided;
/* /*
* Touchscreen LRADC channels receives a private slot in the CTRL4 * When the touchscreen is enabled, we give it two private virtual
* register, the slot #7. Therefore only 7 slots instead of 8 in the * channels: #6 and #7. This means that only 6 virtual channels (instead
* CTRL4 register can be mapped to LRADC channels when using the * of 8) will be available for buffered capture.
* touchscreen. */
* #define TOUCHSCREEN_VCHANNEL1 7
#define TOUCHSCREEN_VCHANNEL2 6
/*
* Furthermore, certain LRADC channels are shared between touchscreen * Furthermore, certain LRADC channels are shared between touchscreen
* and/or touch-buttons and generic LRADC block. Therefore when using * and/or touch-buttons and generic LRADC block. Therefore when using
* either of these, these channels are not available for the regular * either of these, these channels are not available for the regular
...@@ -342,6 +345,9 @@ struct mxs_lradc { ...@@ -342,6 +345,9 @@ struct mxs_lradc {
#define LRADC_CTRL4 0x140 #define LRADC_CTRL4 0x140
#define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4)) #define LRADC_CTRL4_LRADCSELECT_MASK(n) (0xf << ((n) * 4))
#define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4) #define LRADC_CTRL4_LRADCSELECT_OFFSET(n) ((n) * 4)
#define LRADC_CTRL4_LRADCSELECT(n, x) \
(((x) << LRADC_CTRL4_LRADCSELECT_OFFSET(n)) & \
LRADC_CTRL4_LRADCSELECT_MASK(n))
#define LRADC_RESOLUTION 12 #define LRADC_RESOLUTION 12
#define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1) #define LRADC_SINGLE_SAMPLE_MASK ((1 << LRADC_RESOLUTION) - 1)
...@@ -416,6 +422,14 @@ static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc) ...@@ -416,6 +422,14 @@ static bool mxs_lradc_check_touch_event(struct mxs_lradc *lradc)
LRADC_STATUS_TOUCH_DETECT_RAW); LRADC_STATUS_TOUCH_DETECT_RAW);
} }
static void mxs_lradc_map_channel(struct mxs_lradc *lradc, unsigned vch,
unsigned ch)
{
mxs_lradc_reg_clear(lradc, LRADC_CTRL4_LRADCSELECT_MASK(vch),
LRADC_CTRL4);
mxs_lradc_reg_set(lradc, LRADC_CTRL4_LRADCSELECT(vch, ch), LRADC_CTRL4);
}
static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
{ {
/* /*
...@@ -443,12 +457,8 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch) ...@@ -443,12 +457,8 @@ static void mxs_lradc_setup_ts_channel(struct mxs_lradc *lradc, unsigned ch)
LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
LRADC_DELAY(3)); LRADC_DELAY(3));
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch), LRADC_CTRL1);
LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
/* wake us again, when the complete conversion is done */
mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch), LRADC_CTRL1);
/* /*
* after changing the touchscreen plates setting * after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the * the signals need some initial time to settle. Start the
...@@ -501,12 +511,8 @@ static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1, ...@@ -501,12 +511,8 @@ static void mxs_lradc_setup_ts_pressure(struct mxs_lradc *lradc, unsigned ch1,
LRADC_DELAY_DELAY(lradc->over_sample_delay - 1), LRADC_DELAY_DELAY(lradc->over_sample_delay - 1),
LRADC_DELAY(3)); LRADC_DELAY(3));
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(2) | mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(ch2), LRADC_CTRL1);
LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(4) |
LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1);
/* wake us again, when the conversions are done */
mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(ch2), LRADC_CTRL1);
/* /*
* after changing the touchscreen plates setting * after changing the touchscreen plates setting
* the signals need some initial time to settle. Start the * the signals need some initial time to settle. Start the
...@@ -571,36 +577,6 @@ static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc, ...@@ -571,36 +577,6 @@ static unsigned mxs_lradc_read_ts_pressure(struct mxs_lradc *lradc,
#define TS_CH_XM 4 #define TS_CH_XM 4
#define TS_CH_YM 5 #define TS_CH_YM 5
static int mxs_lradc_read_ts_channel(struct mxs_lradc *lradc)
{
u32 reg;
int val;
reg = readl(lradc->base + LRADC_CTRL1);
/* only channels 3 to 5 are of interest here */
if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YP)) {
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YP) |
LRADC_CTRL1_LRADC_IRQ(TS_CH_YP), LRADC_CTRL1);
val = mxs_lradc_read_raw_channel(lradc, TS_CH_YP);
} else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_XM)) {
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_XM) |
LRADC_CTRL1_LRADC_IRQ(TS_CH_XM), LRADC_CTRL1);
val = mxs_lradc_read_raw_channel(lradc, TS_CH_XM);
} else if (reg & LRADC_CTRL1_LRADC_IRQ(TS_CH_YM)) {
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ_EN(TS_CH_YM) |
LRADC_CTRL1_LRADC_IRQ(TS_CH_YM), LRADC_CTRL1);
val = mxs_lradc_read_raw_channel(lradc, TS_CH_YM);
} else {
return -EIO;
}
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
return val;
}
/* /*
* YP(open)--+-------------+ * YP(open)--+-------------+
* | |--+ * | |--+
...@@ -644,7 +620,8 @@ static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc) ...@@ -644,7 +620,8 @@ static void mxs_lradc_prepare_x_pos(struct mxs_lradc *lradc)
mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0); mxs_lradc_reg_set(lradc, mxs_lradc_drive_x_plate(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_X; lradc->cur_plate = LRADC_SAMPLE_X;
mxs_lradc_setup_ts_channel(lradc, TS_CH_YP); mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YP);
mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
} }
/* /*
...@@ -665,7 +642,8 @@ static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc) ...@@ -665,7 +642,8 @@ static void mxs_lradc_prepare_y_pos(struct mxs_lradc *lradc)
mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0); mxs_lradc_reg_set(lradc, mxs_lradc_drive_y_plate(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_Y; lradc->cur_plate = LRADC_SAMPLE_Y;
mxs_lradc_setup_ts_channel(lradc, TS_CH_XM); mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_XM);
mxs_lradc_setup_ts_channel(lradc, TOUCHSCREEN_VCHANNEL1);
} }
/* /*
...@@ -686,7 +664,10 @@ static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc) ...@@ -686,7 +664,10 @@ static void mxs_lradc_prepare_pressure(struct mxs_lradc *lradc)
mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0); mxs_lradc_reg_set(lradc, mxs_lradc_drive_pressure(lradc), LRADC_CTRL0);
lradc->cur_plate = LRADC_SAMPLE_PRESSURE; lradc->cur_plate = LRADC_SAMPLE_PRESSURE;
mxs_lradc_setup_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL1, TS_CH_YM);
mxs_lradc_map_channel(lradc, TOUCHSCREEN_VCHANNEL2, TS_CH_XP);
mxs_lradc_setup_ts_pressure(lradc, TOUCHSCREEN_VCHANNEL2,
TOUCHSCREEN_VCHANNEL1);
} }
static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
...@@ -699,6 +680,19 @@ static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc) ...@@ -699,6 +680,19 @@ static void mxs_lradc_enable_touch_detection(struct mxs_lradc *lradc)
mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
} }
static void mxs_lradc_start_touch_event(struct mxs_lradc *lradc)
{
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
LRADC_CTRL1);
mxs_lradc_reg_set(lradc,
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1);
/*
* start with the Y-pos, because it uses nearly the same plate
* settings like the touch detection
*/
mxs_lradc_prepare_y_pos(lradc);
}
static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc) static void mxs_lradc_report_ts_event(struct mxs_lradc *lradc)
{ {
input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos); input_report_abs(lradc->ts_input, ABS_X, lradc->ts_x_pos);
...@@ -716,10 +710,12 @@ static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc) ...@@ -716,10 +710,12 @@ static void mxs_lradc_complete_touch_event(struct mxs_lradc *lradc)
* start a dummy conversion to burn time to settle the signals * start a dummy conversion to burn time to settle the signals
* note: we are not interested in the conversion's value * note: we are not interested in the conversion's value
*/ */
mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(5)); mxs_lradc_reg_wrt(lradc, 0, LRADC_CH(TOUCHSCREEN_VCHANNEL1));
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_LRADC_IRQ(5), LRADC_CTRL1); mxs_lradc_reg_clear(lradc,
mxs_lradc_reg_set(lradc, LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1); LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
mxs_lradc_reg_wrt(lradc, LRADC_DELAY_TRIGGER(1 << 5) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
mxs_lradc_reg_wrt(lradc,
LRADC_DELAY_TRIGGER(1 << TOUCHSCREEN_VCHANNEL1) |
LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */ LRADC_DELAY_KICK | LRADC_DELAY_DELAY(10), /* waste 5 ms */
LRADC_DELAY(2)); LRADC_DELAY(2));
} }
...@@ -751,59 +747,45 @@ static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid) ...@@ -751,59 +747,45 @@ static void mxs_lradc_finish_touch_event(struct mxs_lradc *lradc, bool valid)
/* if it is released, wait for the next touch via IRQ */ /* if it is released, wait for the next touch via IRQ */
lradc->cur_plate = LRADC_TOUCH; lradc->cur_plate = LRADC_TOUCH;
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, LRADC_CTRL1); mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(2));
mxs_lradc_reg_wrt(lradc, 0, LRADC_DELAY(3));
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1), LRADC_CTRL1);
mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1); mxs_lradc_reg_set(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN, LRADC_CTRL1);
} }
/* touchscreen's state machine */ /* touchscreen's state machine */
static void mxs_lradc_handle_touch(struct mxs_lradc *lradc) static void mxs_lradc_handle_touch(struct mxs_lradc *lradc)
{ {
int val;
switch (lradc->cur_plate) { switch (lradc->cur_plate) {
case LRADC_TOUCH: case LRADC_TOUCH:
/* if (mxs_lradc_check_touch_event(lradc))
* start with the Y-pos, because it uses nearly the same plate mxs_lradc_start_touch_event(lradc);
* settings like the touch detection
*/
if (mxs_lradc_check_touch_event(lradc)) {
mxs_lradc_reg_clear(lradc,
LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
LRADC_CTRL1);
mxs_lradc_prepare_y_pos(lradc);
}
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ, mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ,
LRADC_CTRL1); LRADC_CTRL1);
return; return;
case LRADC_SAMPLE_Y: case LRADC_SAMPLE_Y:
val = mxs_lradc_read_ts_channel(lradc); lradc->ts_y_pos = mxs_lradc_read_raw_channel(lradc,
if (val < 0) { TOUCHSCREEN_VCHANNEL1);
mxs_lradc_enable_touch_detection(lradc); /* re-start */
return;
}
lradc->ts_y_pos = val;
mxs_lradc_prepare_x_pos(lradc); mxs_lradc_prepare_x_pos(lradc);
return; return;
case LRADC_SAMPLE_X: case LRADC_SAMPLE_X:
val = mxs_lradc_read_ts_channel(lradc); lradc->ts_x_pos = mxs_lradc_read_raw_channel(lradc,
if (val < 0) { TOUCHSCREEN_VCHANNEL1);
mxs_lradc_enable_touch_detection(lradc); /* re-start */
return;
}
lradc->ts_x_pos = val;
mxs_lradc_prepare_pressure(lradc); mxs_lradc_prepare_pressure(lradc);
return; return;
case LRADC_SAMPLE_PRESSURE: case LRADC_SAMPLE_PRESSURE:
lradc->ts_pressure = lradc->ts_pressure = mxs_lradc_read_ts_pressure(lradc,
mxs_lradc_read_ts_pressure(lradc, TS_CH_XP, TS_CH_YM); TOUCHSCREEN_VCHANNEL2,
TOUCHSCREEN_VCHANNEL1);
mxs_lradc_complete_touch_event(lradc); mxs_lradc_complete_touch_event(lradc);
return; return;
case LRADC_SAMPLE_VALID: case LRADC_SAMPLE_VALID:
val = mxs_lradc_read_ts_channel(lradc); /* ignore the value */
mxs_lradc_finish_touch_event(lradc, 1); mxs_lradc_finish_touch_event(lradc, 1);
break; break;
} }
...@@ -1081,9 +1063,8 @@ static void mxs_lradc_disable_ts(struct mxs_lradc *lradc) ...@@ -1081,9 +1063,8 @@ static void mxs_lradc_disable_ts(struct mxs_lradc *lradc)
{ {
/* stop all interrupts from firing */ /* stop all interrupts from firing */
mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN | mxs_lradc_reg_clear(lradc, LRADC_CTRL1_TOUCH_DETECT_IRQ_EN |
LRADC_CTRL1_LRADC_IRQ_EN(2) | LRADC_CTRL1_LRADC_IRQ_EN(3) | LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ_EN(4) | LRADC_CTRL1_LRADC_IRQ_EN(5), LRADC_CTRL1_LRADC_IRQ_EN(TOUCHSCREEN_VCHANNEL2), LRADC_CTRL1);
LRADC_CTRL1);
/* Power-down touchscreen touch-detect circuitry. */ /* Power-down touchscreen touch-detect circuitry. */
mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0); mxs_lradc_reg_clear(lradc, mxs_lradc_plate_mask(lradc), LRADC_CTRL0);
...@@ -1149,26 +1130,29 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data) ...@@ -1149,26 +1130,29 @@ static irqreturn_t mxs_lradc_handle_irq(int irq, void *data)
struct iio_dev *iio = data; struct iio_dev *iio = data;
struct mxs_lradc *lradc = iio_priv(iio); struct mxs_lradc *lradc = iio_priv(iio);
unsigned long reg = readl(lradc->base + LRADC_CTRL1); unsigned long reg = readl(lradc->base + LRADC_CTRL1);
uint32_t clr_irq = mxs_lradc_irq_mask(lradc);
const uint32_t ts_irq_mask = const uint32_t ts_irq_mask =
LRADC_CTRL1_TOUCH_DETECT_IRQ | LRADC_CTRL1_TOUCH_DETECT_IRQ |
LRADC_CTRL1_LRADC_IRQ(2) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(3) | LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2);
LRADC_CTRL1_LRADC_IRQ(4) |
LRADC_CTRL1_LRADC_IRQ(5);
if (!(reg & mxs_lradc_irq_mask(lradc))) if (!(reg & mxs_lradc_irq_mask(lradc)))
return IRQ_NONE; return IRQ_NONE;
if (lradc->use_touchscreen && (reg & ts_irq_mask)) if (lradc->use_touchscreen && (reg & ts_irq_mask)) {
mxs_lradc_handle_touch(lradc); mxs_lradc_handle_touch(lradc);
/* Make sure we don't clear the next conversion's interrupt. */
clr_irq &= ~(LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL1) |
LRADC_CTRL1_LRADC_IRQ(TOUCHSCREEN_VCHANNEL2));
}
if (iio_buffer_enabled(iio)) if (iio_buffer_enabled(iio))
iio_trigger_poll(iio->trig); iio_trigger_poll(iio->trig);
else if (reg & LRADC_CTRL1_LRADC_IRQ(0)) else if (reg & LRADC_CTRL1_LRADC_IRQ(0))
complete(&lradc->completion); complete(&lradc->completion);
mxs_lradc_reg_clear(lradc, reg & mxs_lradc_irq_mask(lradc), mxs_lradc_reg_clear(lradc, reg & clr_irq, LRADC_CTRL1);
LRADC_CTRL1);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
...@@ -1344,7 +1328,7 @@ static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio, ...@@ -1344,7 +1328,7 @@ static bool mxs_lradc_validate_scan_mask(struct iio_dev *iio,
if (lradc->use_touchbutton) if (lradc->use_touchbutton)
rsvd_chans++; rsvd_chans++;
if (lradc->use_touchscreen) if (lradc->use_touchscreen)
rsvd_chans++; rsvd_chans += 2;
/* Test for attempts to map channels with special mode of operation. */ /* Test for attempts to map channels with special mode of operation. */
if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS)) if (bitmap_intersects(mask, &rsvd_mask, LRADC_MAX_TOTAL_CHANS))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment