Commit 84dd4373 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'char-misc-6.10-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver fixes from Greg KH:
 "Here are some small driver fixes for 6.10-rc6. Included in here are:

   - IIO driver fixes for reported issues

   - Counter driver fix for a reported problem.

  All of these have been in linux-next this week with no reported
  issues"

* tag 'char-misc-6.10-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  counter: ti-eqep: enable clock at probe
  iio: chemical: bme680: Fix sensor data read operation
  iio: chemical: bme680: Fix overflows in compensate() functions
  iio: chemical: bme680: Fix calibration data variable
  iio: chemical: bme680: Fix pressure value output
  iio: humidity: hdc3020: fix hysteresis representation
  iio: dac: fix ad9739a random config compile error
  iio: accel: fxls8962af: select IIO_BUFFER & IIO_KFIFO_BUF
  iio: adc: ad7266: Fix variable checking bug
  iio: xilinx-ams: Don't include ams_ctrl_channels in scan_mask
parents 12529aa1 06ebbce9
......@@ -6,6 +6,7 @@
*/
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/counter.h>
#include <linux/kernel.h>
#include <linux/mod_devicetable.h>
......@@ -376,6 +377,7 @@ static int ti_eqep_probe(struct platform_device *pdev)
struct counter_device *counter;
struct ti_eqep_cnt *priv;
void __iomem *base;
struct clk *clk;
int err;
counter = devm_counter_alloc(dev, sizeof(*priv));
......@@ -415,6 +417,10 @@ static int ti_eqep_probe(struct platform_device *pdev)
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return dev_err_probe(dev, PTR_ERR(clk), "failed to enable clock\n");
err = counter_add(counter);
if (err < 0) {
pm_runtime_put_sync(dev);
......
......@@ -330,6 +330,8 @@ config DMARD10
config FXLS8962AF
tristate
depends on I2C || !I2C # cannot be built-in for modular I2C
select IIO_BUFFER
select IIO_KFIFO_BUF
config FXLS8962AF_I2C
tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver"
......
......@@ -157,6 +157,8 @@ static int ad7266_read_raw(struct iio_dev *indio_dev,
ret = ad7266_read_single(st, val, chan->address);
iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
*val = (*val >> 2) & 0xfff;
if (chan->scan_type.sign == 's')
*val = sign_extend32(*val,
......
......@@ -414,8 +414,12 @@ static void ams_enable_channel_sequence(struct iio_dev *indio_dev)
/* Run calibration of PS & PL as part of the sequence */
scan_mask = BIT(0) | BIT(AMS_PS_SEQ_MAX);
for (i = 0; i < indio_dev->num_channels; i++)
scan_mask |= BIT_ULL(indio_dev->channels[i].scan_index);
for (i = 0; i < indio_dev->num_channels; i++) {
const struct iio_chan_spec *chan = &indio_dev->channels[i];
if (chan->scan_index < AMS_CTRL_SEQ_BASE)
scan_mask |= BIT_ULL(chan->scan_index);
}
if (ams->ps_base) {
/* put sysmon in a soft reset to change the sequence */
......
......@@ -54,7 +54,9 @@
#define BME680_NB_CONV_MASK GENMASK(3, 0)
#define BME680_REG_MEAS_STAT_0 0x1D
#define BME680_NEW_DATA_BIT BIT(7)
#define BME680_GAS_MEAS_BIT BIT(6)
#define BME680_MEAS_BIT BIT(5)
/* Calibration Parameters */
#define BME680_T2_LSB_REG 0x8A
......
......@@ -10,6 +10,7 @@
*/
#include <linux/acpi.h>
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/log2.h>
......@@ -38,7 +39,7 @@ struct bme680_calib {
s8 par_h3;
s8 par_h4;
s8 par_h5;
s8 par_h6;
u8 par_h6;
s8 par_h7;
s8 par_gh1;
s16 par_gh2;
......@@ -342,10 +343,10 @@ static s16 bme680_compensate_temp(struct bme680_data *data,
if (!calib->par_t2)
bme680_read_calib(data, calib);
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
var1 = (adc_temp >> 3) - ((s32)calib->par_t1 << 1);
var2 = (var1 * calib->par_t2) >> 11;
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
var3 = (var3 * (calib->par_t3 << 4)) >> 14;
var3 = (var3 * ((s32)calib->par_t3 << 4)) >> 14;
data->t_fine = var2 + var3;
calc_temp = (data->t_fine * 5 + 128) >> 8;
......@@ -368,9 +369,9 @@ static u32 bme680_compensate_press(struct bme680_data *data,
var1 = (data->t_fine >> 1) - 64000;
var2 = ((((var1 >> 2) * (var1 >> 2)) >> 11) * calib->par_p6) >> 2;
var2 = var2 + (var1 * calib->par_p5 << 1);
var2 = (var2 >> 2) + (calib->par_p4 << 16);
var2 = (var2 >> 2) + ((s32)calib->par_p4 << 16);
var1 = (((((var1 >> 2) * (var1 >> 2)) >> 13) *
(calib->par_p3 << 5)) >> 3) +
((s32)calib->par_p3 << 5)) >> 3) +
((calib->par_p2 * var1) >> 1);
var1 = var1 >> 18;
var1 = ((32768 + var1) * calib->par_p1) >> 15;
......@@ -388,7 +389,7 @@ static u32 bme680_compensate_press(struct bme680_data *data,
var3 = ((press_comp >> 8) * (press_comp >> 8) *
(press_comp >> 8) * calib->par_p10) >> 17;
press_comp += (var1 + var2 + var3 + (calib->par_p7 << 7)) >> 4;
press_comp += (var1 + var2 + var3 + ((s32)calib->par_p7 << 7)) >> 4;
return press_comp;
}
......@@ -414,7 +415,7 @@ static u32 bme680_compensate_humid(struct bme680_data *data,
(((temp_scaled * ((temp_scaled * calib->par_h5) / 100))
>> 6) / 100) + (1 << 14))) >> 10;
var3 = var1 * var2;
var4 = calib->par_h6 << 7;
var4 = (s32)calib->par_h6 << 7;
var4 = (var4 + ((temp_scaled * calib->par_h7) / 100)) >> 4;
var5 = ((var3 >> 14) * (var3 >> 14)) >> 10;
var6 = (var4 * var5) >> 1;
......@@ -532,6 +533,43 @@ static u8 bme680_oversampling_to_reg(u8 val)
return ilog2(val) + 1;
}
/*
* Taken from Bosch BME680 API:
* https://github.com/boschsensortec/BME68x_SensorAPI/blob/v4.4.8/bme68x.c#L490
*/
static int bme680_wait_for_eoc(struct bme680_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
unsigned int check;
int ret;
/*
* (Sum of oversampling ratios * time per oversampling) +
* TPH measurement + gas measurement + wait transition from forced mode
* + heater duration
*/
int wait_eoc_us = ((data->oversampling_temp + data->oversampling_press +
data->oversampling_humid) * 1936) + (477 * 4) +
(477 * 5) + 1000 + (data->heater_dur * 1000);
usleep_range(wait_eoc_us, wait_eoc_us + 100);
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
if (ret) {
dev_err(dev, "failed to read measurement status register.\n");
return ret;
}
if (check & BME680_MEAS_BIT) {
dev_err(dev, "Device measurement cycle incomplete.\n");
return -EBUSY;
}
if (!(check & BME680_NEW_DATA_BIT)) {
dev_err(dev, "No new data available from the device.\n");
return -ENODATA;
}
return 0;
}
static int bme680_chip_config(struct bme680_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
......@@ -622,6 +660,10 @@ static int bme680_read_temp(struct bme680_data *data, int *val)
if (ret < 0)
return ret;
ret = bme680_wait_for_eoc(data);
if (ret)
return ret;
ret = regmap_bulk_read(data->regmap, BME680_REG_TEMP_MSB,
&tmp, 3);
if (ret < 0) {
......@@ -678,7 +720,7 @@ static int bme680_read_press(struct bme680_data *data,
}
*val = bme680_compensate_press(data, adc_press);
*val2 = 100;
*val2 = 1000;
return IIO_VAL_FRACTIONAL;
}
......@@ -738,6 +780,10 @@ static int bme680_read_gas(struct bme680_data *data,
if (ret < 0)
return ret;
ret = bme680_wait_for_eoc(data);
if (ret)
return ret;
ret = regmap_read(data->regmap, BME680_REG_MEAS_STAT_0, &check);
if (check & BME680_GAS_MEAS_BIT) {
dev_err(dev, "gas measurement incomplete\n");
......
......@@ -133,7 +133,7 @@ config AD5624R_SPI
config AD9739A
tristate "Analog Devices AD9739A RF DAC spi driver"
depends on SPI || COMPILE_TEST
depends on SPI
select REGMAP_SPI
select IIO_BACKEND
help
......
......@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/math64.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pm.h>
......@@ -66,8 +67,10 @@
#define HDC3020_CRC8_POLYNOMIAL 0x31
#define HDC3020_MIN_TEMP -40
#define HDC3020_MAX_TEMP 125
#define HDC3020_MIN_TEMP_MICRO -39872968
#define HDC3020_MAX_TEMP_MICRO 124875639
#define HDC3020_MAX_TEMP_HYST_MICRO 164748607
#define HDC3020_MAX_HUM_MICRO 99220264
struct hdc3020_data {
struct i2c_client *client;
......@@ -368,6 +371,105 @@ static int hdc3020_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
static int hdc3020_thresh_get_temp(u16 thresh)
{
int temp;
/*
* Get the temperature threshold from 9 LSBs, shift them to get
* the truncated temperature threshold representation and
* calculate the threshold according to the formula in the
* datasheet. Result is degree celsius scaled by 65535.
*/
temp = FIELD_GET(HDC3020_THRESH_TEMP_MASK, thresh) <<
HDC3020_THRESH_TEMP_TRUNC_SHIFT;
return -2949075 + (175 * temp);
}
static int hdc3020_thresh_get_hum(u16 thresh)
{
int hum;
/*
* Get the humidity threshold from 7 MSBs, shift them to get the
* truncated humidity threshold representation and calculate the
* threshold according to the formula in the datasheet. Result is
* percent scaled by 65535.
*/
hum = FIELD_GET(HDC3020_THRESH_HUM_MASK, thresh) <<
HDC3020_THRESH_HUM_TRUNC_SHIFT;
return hum * 100;
}
static u16 hdc3020_thresh_set_temp(int s_temp, u16 curr_thresh)
{
u64 temp;
u16 thresh;
/*
* Calculate temperature threshold, shift it down to get the
* truncated threshold representation in the 9LSBs while keeping
* the current humidity threshold in the 7 MSBs.
*/
temp = (u64)(s_temp + 45000000) * 65535ULL;
temp = div_u64(temp, 1000000 * 175) >> HDC3020_THRESH_TEMP_TRUNC_SHIFT;
thresh = FIELD_PREP(HDC3020_THRESH_TEMP_MASK, temp);
thresh |= (FIELD_GET(HDC3020_THRESH_HUM_MASK, curr_thresh) <<
HDC3020_THRESH_HUM_TRUNC_SHIFT);
return thresh;
}
static u16 hdc3020_thresh_set_hum(int s_hum, u16 curr_thresh)
{
u64 hum;
u16 thresh;
/*
* Calculate humidity threshold, shift it down and up to get the
* truncated threshold representation in the 7MSBs while keeping
* the current temperature threshold in the 9 LSBs.
*/
hum = (u64)(s_hum) * 65535ULL;
hum = div_u64(hum, 1000000 * 100) >> HDC3020_THRESH_HUM_TRUNC_SHIFT;
thresh = FIELD_PREP(HDC3020_THRESH_HUM_MASK, hum);
thresh |= FIELD_GET(HDC3020_THRESH_TEMP_MASK, curr_thresh);
return thresh;
}
static
int hdc3020_thresh_clr(s64 s_thresh, s64 s_hyst, enum iio_event_direction dir)
{
s64 s_clr;
/*
* Include directions when calculation the clear value,
* since hysteresis is unsigned by definition and the
* clear value is an absolute value which is signed.
*/
if (dir == IIO_EV_DIR_RISING)
s_clr = s_thresh - s_hyst;
else
s_clr = s_thresh + s_hyst;
/* Divide by 65535 to get units of micro */
return div_s64(s_clr, 65535);
}
static int _hdc3020_write_thresh(struct hdc3020_data *data, u16 reg, u16 val)
{
u8 buf[5];
put_unaligned_be16(reg, buf);
put_unaligned_be16(val, buf + 2);
buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
return hdc3020_write_bytes(data, buf, 5);
}
static int hdc3020_write_thresh(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
......@@ -376,67 +478,126 @@ static int hdc3020_write_thresh(struct iio_dev *indio_dev,
int val, int val2)
{
struct hdc3020_data *data = iio_priv(indio_dev);
u8 buf[5];
u64 tmp;
u16 reg;
int ret;
/* Supported temperature range is from –40 to 125 degree celsius */
if (val < HDC3020_MIN_TEMP || val > HDC3020_MAX_TEMP)
return -EINVAL;
/* Select threshold register */
if (info == IIO_EV_INFO_VALUE) {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_S_T_RH_THRESH_HIGH;
else
reg = HDC3020_S_T_RH_THRESH_LOW;
u16 reg, reg_val, reg_thresh_rd, reg_clr_rd, reg_thresh_wr, reg_clr_wr;
s64 s_thresh, s_hyst, s_clr;
int s_val, thresh, clr, ret;
/* Select threshold registers */
if (dir == IIO_EV_DIR_RISING) {
reg_thresh_rd = HDC3020_R_T_RH_THRESH_HIGH;
reg_thresh_wr = HDC3020_S_T_RH_THRESH_HIGH;
reg_clr_rd = HDC3020_R_T_RH_THRESH_HIGH_CLR;
reg_clr_wr = HDC3020_S_T_RH_THRESH_HIGH_CLR;
} else {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_S_T_RH_THRESH_HIGH_CLR;
else
reg = HDC3020_S_T_RH_THRESH_LOW_CLR;
reg_thresh_rd = HDC3020_R_T_RH_THRESH_LOW;
reg_thresh_wr = HDC3020_S_T_RH_THRESH_LOW;
reg_clr_rd = HDC3020_R_T_RH_THRESH_LOW_CLR;
reg_clr_wr = HDC3020_S_T_RH_THRESH_LOW_CLR;
}
guard(mutex)(&data->lock);
ret = hdc3020_read_be16(data, reg);
ret = hdc3020_read_be16(data, reg_thresh_rd);
if (ret < 0)
return ret;
thresh = ret;
ret = hdc3020_read_be16(data, reg_clr_rd);
if (ret < 0)
return ret;
clr = ret;
/* Scale value to include decimal part into calculations */
s_val = (val < 0) ? (val * 1000000 - val2) : (val * 1000000 + val2);
switch (chan->type) {
case IIO_TEMP:
/*
* Calculate temperature threshold, shift it down to get the
* truncated threshold representation in the 9LSBs while keeping
* the current humidity threshold in the 7 MSBs.
*/
tmp = ((u64)(((val + 45) * MICRO) + val2)) * 65535ULL;
tmp = div_u64(tmp, MICRO * 175);
val = tmp >> HDC3020_THRESH_TEMP_TRUNC_SHIFT;
val = FIELD_PREP(HDC3020_THRESH_TEMP_MASK, val);
val |= (FIELD_GET(HDC3020_THRESH_HUM_MASK, ret) <<
HDC3020_THRESH_HUM_TRUNC_SHIFT);
switch (info) {
case IIO_EV_INFO_VALUE:
s_val = max(s_val, HDC3020_MIN_TEMP_MICRO);
s_val = min(s_val, HDC3020_MAX_TEMP_MICRO);
reg = reg_thresh_wr;
reg_val = hdc3020_thresh_set_temp(s_val, thresh);
ret = _hdc3020_write_thresh(data, reg, reg_val);
if (ret < 0)
return ret;
/* Calculate old hysteresis */
s_thresh = (s64)hdc3020_thresh_get_temp(thresh) * 1000000;
s_clr = (s64)hdc3020_thresh_get_temp(clr) * 1000000;
s_hyst = div_s64(abs(s_thresh - s_clr), 65535);
/* Set new threshold */
thresh = reg_val;
/* Set old hysteresis */
s_val = s_hyst;
fallthrough;
case IIO_EV_INFO_HYSTERESIS:
/*
* Function hdc3020_thresh_get_temp returns temperature
* in degree celsius scaled by 65535. Scale by 1000000
* to be able to subtract scaled hysteresis value.
*/
s_thresh = (s64)hdc3020_thresh_get_temp(thresh) * 1000000;
/*
* Units of s_val are in micro degree celsius, scale by
* 65535 to get same units as s_thresh.
*/
s_val = min(abs(s_val), HDC3020_MAX_TEMP_HYST_MICRO);
s_hyst = (s64)s_val * 65535;
s_clr = hdc3020_thresh_clr(s_thresh, s_hyst, dir);
s_clr = max(s_clr, HDC3020_MIN_TEMP_MICRO);
s_clr = min(s_clr, HDC3020_MAX_TEMP_MICRO);
reg = reg_clr_wr;
reg_val = hdc3020_thresh_set_temp(s_clr, clr);
break;
default:
return -EOPNOTSUPP;
}
break;
case IIO_HUMIDITYRELATIVE:
/*
* Calculate humidity threshold, shift it down and up to get the
* truncated threshold representation in the 7MSBs while keeping
* the current temperature threshold in the 9 LSBs.
*/
tmp = ((u64)((val * MICRO) + val2)) * 65535ULL;
tmp = div_u64(tmp, MICRO * 100);
val = tmp >> HDC3020_THRESH_HUM_TRUNC_SHIFT;
val = FIELD_PREP(HDC3020_THRESH_HUM_MASK, val);
val |= FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret);
s_val = (s_val < 0) ? 0 : min(s_val, HDC3020_MAX_HUM_MICRO);
switch (info) {
case IIO_EV_INFO_VALUE:
reg = reg_thresh_wr;
reg_val = hdc3020_thresh_set_hum(s_val, thresh);
ret = _hdc3020_write_thresh(data, reg, reg_val);
if (ret < 0)
return ret;
/* Calculate old hysteresis */
s_thresh = (s64)hdc3020_thresh_get_hum(thresh) * 1000000;
s_clr = (s64)hdc3020_thresh_get_hum(clr) * 1000000;
s_hyst = div_s64(abs(s_thresh - s_clr), 65535);
/* Set new threshold */
thresh = reg_val;
/* Try to set old hysteresis */
s_val = min(abs(s_hyst), HDC3020_MAX_HUM_MICRO);
fallthrough;
case IIO_EV_INFO_HYSTERESIS:
/*
* Function hdc3020_thresh_get_hum returns relative
* humidity in percent scaled by 65535. Scale by 1000000
* to be able to subtract scaled hysteresis value.
*/
s_thresh = (s64)hdc3020_thresh_get_hum(thresh) * 1000000;
/*
* Units of s_val are in micro percent, scale by 65535
* to get same units as s_thresh.
*/
s_hyst = (s64)s_val * 65535;
s_clr = hdc3020_thresh_clr(s_thresh, s_hyst, dir);
s_clr = max(s_clr, 0);
s_clr = min(s_clr, HDC3020_MAX_HUM_MICRO);
reg = reg_clr_wr;
reg_val = hdc3020_thresh_set_hum(s_clr, clr);
break;
default:
return -EOPNOTSUPP;
}
break;
default:
return -EOPNOTSUPP;
}
put_unaligned_be16(reg, buf);
put_unaligned_be16(val, buf + 2);
buf[4] = crc8(hdc3020_crc8_table, buf + 2, 2, CRC8_INIT_VALUE);
return hdc3020_write_bytes(data, buf, 5);
return _hdc3020_write_thresh(data, reg, reg_val);
}
static int hdc3020_read_thresh(struct iio_dev *indio_dev,
......@@ -447,48 +608,60 @@ static int hdc3020_read_thresh(struct iio_dev *indio_dev,
int *val, int *val2)
{
struct hdc3020_data *data = iio_priv(indio_dev);
u16 reg;
int ret;
u16 reg_thresh, reg_clr;
int thresh, clr, ret;
/* Select threshold register */
if (info == IIO_EV_INFO_VALUE) {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_R_T_RH_THRESH_HIGH;
else
reg = HDC3020_R_T_RH_THRESH_LOW;
/* Select threshold registers */
if (dir == IIO_EV_DIR_RISING) {
reg_thresh = HDC3020_R_T_RH_THRESH_HIGH;
reg_clr = HDC3020_R_T_RH_THRESH_HIGH_CLR;
} else {
if (dir == IIO_EV_DIR_RISING)
reg = HDC3020_R_T_RH_THRESH_HIGH_CLR;
else
reg = HDC3020_R_T_RH_THRESH_LOW_CLR;
reg_thresh = HDC3020_R_T_RH_THRESH_LOW;
reg_clr = HDC3020_R_T_RH_THRESH_LOW_CLR;
}
guard(mutex)(&data->lock);
ret = hdc3020_read_be16(data, reg);
ret = hdc3020_read_be16(data, reg_thresh);
if (ret < 0)
return ret;
switch (chan->type) {
case IIO_TEMP:
/*
* Get the temperature threshold from 9 LSBs, shift them to get
* the truncated temperature threshold representation and
* calculate the threshold according to the formula in the
* datasheet.
*/
*val = FIELD_GET(HDC3020_THRESH_TEMP_MASK, ret);
*val = *val << HDC3020_THRESH_TEMP_TRUNC_SHIFT;
*val = -2949075 + (175 * (*val));
thresh = hdc3020_thresh_get_temp(ret);
switch (info) {
case IIO_EV_INFO_VALUE:
*val = thresh;
break;
case IIO_EV_INFO_HYSTERESIS:
ret = hdc3020_read_be16(data, reg_clr);
if (ret < 0)
return ret;
clr = hdc3020_thresh_get_temp(ret);
*val = abs(thresh - clr);
break;
default:
return -EOPNOTSUPP;
}
*val2 = 65535;
return IIO_VAL_FRACTIONAL;
case IIO_HUMIDITYRELATIVE:
/*
* Get the humidity threshold from 7 MSBs, shift them to get the
* truncated humidity threshold representation and calculate the
* threshold according to the formula in the datasheet.
*/
*val = FIELD_GET(HDC3020_THRESH_HUM_MASK, ret);
*val = (*val << HDC3020_THRESH_HUM_TRUNC_SHIFT) * 100;
thresh = hdc3020_thresh_get_hum(ret);
switch (info) {
case IIO_EV_INFO_VALUE:
*val = thresh;
break;
case IIO_EV_INFO_HYSTERESIS:
ret = hdc3020_read_be16(data, reg_clr);
if (ret < 0)
return ret;
clr = hdc3020_thresh_get_hum(ret);
*val = abs(thresh - clr);
break;
default:
return -EOPNOTSUPP;
}
*val2 = 65535;
return IIO_VAL_FRACTIONAL;
default:
......
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