Commit 36b80422 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'regmap-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap

Pull regmap updates from Mark Brown:
 "The usual small smattering of activity for regmap this time round:

   - Addition of support for the 1-Wire bus standard.

   - Options that allow support for more interrupt controllers with
     regmap-irq.

   - Only build LZO cache support if it's actually being used"

* tag 'regmap-v4.13' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap:
  regmap: irq: add chip option mask_writeonly
  regmap: irq: allow to register one cell interrupt controllers
  regmap: Fix typo in IS_ENABLED() check
  regmap: Add 1-Wire bus support
  regmap: make LZO cache optional
parents 24d734a2 2a40f942
...@@ -3,10 +3,13 @@ ...@@ -3,10 +3,13 @@
# subsystems should select the appropriate symbols. # subsystems should select the appropriate symbols.
config REGMAP config REGMAP
default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ) default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_W1 || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
select IRQ_DOMAIN if REGMAP_IRQ
bool
config REGCACHE_COMPRESSED
select LZO_COMPRESS select LZO_COMPRESS
select LZO_DECOMPRESS select LZO_DECOMPRESS
select IRQ_DOMAIN if REGMAP_IRQ
bool bool
config REGMAP_AC97 config REGMAP_AC97
...@@ -24,6 +27,10 @@ config REGMAP_SPMI ...@@ -24,6 +27,10 @@ config REGMAP_SPMI
tristate tristate
depends on SPMI depends on SPMI
config REGMAP_W1
tristate
depends on W1
config REGMAP_MMIO config REGMAP_MMIO
tristate tristate
......
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
CFLAGS_regmap.o := -I$(src) CFLAGS_regmap.o := -I$(src)
obj-$(CONFIG_REGMAP) += regmap.o regcache.o obj-$(CONFIG_REGMAP) += regmap.o regcache.o
obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-flat.o
obj-$(CONFIG_REGCACHE_COMPRESSED) += regcache-lzo.o
obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
...@@ -10,3 +11,4 @@ obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o ...@@ -10,3 +11,4 @@ obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o obj-$(CONFIG_REGMAP_MMIO) += regmap-mmio.o
obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o obj-$(CONFIG_REGMAP_IRQ) += regmap-irq.o
obj-$(CONFIG_REGMAP_W1) += regmap-w1.o
...@@ -21,7 +21,9 @@ ...@@ -21,7 +21,9 @@
static const struct regcache_ops *cache_types[] = { static const struct regcache_ops *cache_types[] = {
&regcache_rbtree_ops, &regcache_rbtree_ops,
#if IS_ENABLED(CONFIG_REGCACHE_COMPRESSED)
&regcache_lzo_ops, &regcache_lzo_ops,
#endif
&regcache_flat_ops, &regcache_flat_ops,
}; };
......
...@@ -60,6 +60,16 @@ static void regmap_irq_lock(struct irq_data *data) ...@@ -60,6 +60,16 @@ static void regmap_irq_lock(struct irq_data *data)
mutex_lock(&d->lock); mutex_lock(&d->lock);
} }
static int regmap_irq_update_bits(struct regmap_irq_chip_data *d,
unsigned int reg, unsigned int mask,
unsigned int val)
{
if (d->chip->mask_writeonly)
return regmap_write_bits(d->map, reg, mask, val);
else
return regmap_update_bits(d->map, reg, mask, val);
}
static void regmap_irq_sync_unlock(struct irq_data *data) static void regmap_irq_sync_unlock(struct irq_data *data)
{ {
struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data); struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
...@@ -84,11 +94,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -84,11 +94,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg = d->chip->mask_base + reg = d->chip->mask_base +
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
if (d->chip->mask_invert) { if (d->chip->mask_invert) {
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]); d->mask_buf_def[i], ~d->mask_buf[i]);
} else if (d->chip->unmask_base) { } else if (d->chip->unmask_base) {
/* set mask with mask_base register */ /* set mask with mask_base register */
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], ~d->mask_buf[i]); d->mask_buf_def[i], ~d->mask_buf[i]);
if (ret < 0) if (ret < 0)
dev_err(d->map->dev, dev_err(d->map->dev,
...@@ -97,12 +107,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -97,12 +107,12 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
unmask_offset = d->chip->unmask_base - unmask_offset = d->chip->unmask_base -
d->chip->mask_base; d->chip->mask_base;
/* clear mask with unmask_base register */ /* clear mask with unmask_base register */
ret = regmap_update_bits(d->map, ret = regmap_irq_update_bits(d,
reg + unmask_offset, reg + unmask_offset,
d->mask_buf_def[i], d->mask_buf_def[i],
d->mask_buf[i]); d->mask_buf[i]);
} else { } else {
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], d->mask_buf[i]); d->mask_buf_def[i], d->mask_buf[i]);
} }
if (ret != 0) if (ret != 0)
...@@ -113,11 +123,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -113,11 +123,11 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
if (d->wake_buf) { if (d->wake_buf) {
if (d->chip->wake_invert) if (d->chip->wake_invert)
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
~d->wake_buf[i]); ~d->wake_buf[i]);
else else
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
d->wake_buf[i]); d->wake_buf[i]);
if (ret != 0) if (ret != 0)
...@@ -153,10 +163,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data) ...@@ -153,10 +163,10 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
reg = d->chip->type_base + reg = d->chip->type_base +
(i * map->reg_stride * d->type_reg_stride); (i * map->reg_stride * d->type_reg_stride);
if (d->chip->type_invert) if (d->chip->type_invert)
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->type_buf_def[i], ~d->type_buf[i]); d->type_buf_def[i], ~d->type_buf[i]);
else else
ret = regmap_update_bits(d->map, reg, ret = regmap_irq_update_bits(d, reg,
d->type_buf_def[i], d->type_buf[i]); d->type_buf_def[i], d->type_buf[i]);
if (ret != 0) if (ret != 0)
dev_err(d->map->dev, "Failed to sync type in %x\n", dev_err(d->map->dev, "Failed to sync type in %x\n",
...@@ -394,7 +404,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq, ...@@ -394,7 +404,7 @@ static int regmap_irq_map(struct irq_domain *h, unsigned int virq,
static const struct irq_domain_ops regmap_domain_ops = { static const struct irq_domain_ops regmap_domain_ops = {
.map = regmap_irq_map, .map = regmap_irq_map,
.xlate = irq_domain_xlate_twocell, .xlate = irq_domain_xlate_onetwocell,
}; };
/** /**
...@@ -519,17 +529,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -519,17 +529,17 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
reg = chip->mask_base + reg = chip->mask_base +
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
if (chip->mask_invert) if (chip->mask_invert)
ret = regmap_update_bits(map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf[i], ~d->mask_buf[i]); d->mask_buf[i], ~d->mask_buf[i]);
else if (d->chip->unmask_base) { else if (d->chip->unmask_base) {
unmask_offset = d->chip->unmask_base - unmask_offset = d->chip->unmask_base -
d->chip->mask_base; d->chip->mask_base;
ret = regmap_update_bits(d->map, ret = regmap_irq_update_bits(d,
reg + unmask_offset, reg + unmask_offset,
d->mask_buf[i], d->mask_buf[i],
d->mask_buf[i]); d->mask_buf[i]);
} else } else
ret = regmap_update_bits(map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf[i], d->mask_buf[i]); d->mask_buf[i], d->mask_buf[i]);
if (ret != 0) { if (ret != 0) {
dev_err(map->dev, "Failed to set masks in 0x%x: %d\n", dev_err(map->dev, "Failed to set masks in 0x%x: %d\n",
...@@ -575,11 +585,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -575,11 +585,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
(i * map->reg_stride * d->irq_reg_stride); (i * map->reg_stride * d->irq_reg_stride);
if (chip->wake_invert) if (chip->wake_invert)
ret = regmap_update_bits(map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
0); 0);
else else
ret = regmap_update_bits(map, reg, ret = regmap_irq_update_bits(d, reg,
d->mask_buf_def[i], d->mask_buf_def[i],
d->wake_buf[i]); d->wake_buf[i]);
if (ret != 0) { if (ret != 0) {
...@@ -603,10 +613,10 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags, ...@@ -603,10 +613,10 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
reg = chip->type_base + reg = chip->type_base +
(i * map->reg_stride * d->type_reg_stride); (i * map->reg_stride * d->type_reg_stride);
if (chip->type_invert) if (chip->type_invert)
ret = regmap_update_bits(map, reg, ret = regmap_irq_update_bits(d, reg,
d->type_buf_def[i], 0xFF); d->type_buf_def[i], 0xFF);
else else
ret = regmap_update_bits(map, reg, ret = regmap_irq_update_bits(d, reg,
d->type_buf_def[i], 0x0); d->type_buf_def[i], 0x0);
if (ret != 0) { if (ret != 0) {
dev_err(map->dev, dev_err(map->dev,
......
/*
* Register map access API - W1 (1-Wire) support
*
* Copyright (C) 2017 OAO Radioavionica
* Author: Alex A. Mihaylov <minimumlaw@rambler.ru>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation
*/
#include <linux/regmap.h>
#include <linux/module.h>
#include "../../w1/w1.h"
#include "internal.h"
#define W1_CMD_READ_DATA 0x69
#define W1_CMD_WRITE_DATA 0x6C
/*
* 1-Wire slaves registers with addess 8 bit and data 8 bit
*/
static int w1_reg_a8_v8_read(void *context, unsigned int reg, unsigned int *val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_READ_DATA);
w1_write_8(sl->master, reg);
*val = w1_read_8(sl->master);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_reg_a8_v8_write(void *context, unsigned int reg, unsigned int val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_WRITE_DATA);
w1_write_8(sl->master, reg);
w1_write_8(sl->master, val);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
/*
* 1-Wire slaves registers with addess 8 bit and data 16 bit
*/
static int w1_reg_a8_v16_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_READ_DATA);
w1_write_8(sl->master, reg);
*val = w1_read_8(sl->master);
*val |= w1_read_8(sl->master)<<8;
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_reg_a8_v16_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 255)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_WRITE_DATA);
w1_write_8(sl->master, reg);
w1_write_8(sl->master, val & 0x00FF);
w1_write_8(sl->master, val>>8 & 0x00FF);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
/*
* 1-Wire slaves registers with addess 16 bit and data 16 bit
*/
static int w1_reg_a16_v16_read(void *context, unsigned int reg,
unsigned int *val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 65535)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_READ_DATA);
w1_write_8(sl->master, reg & 0x00FF);
w1_write_8(sl->master, reg>>8 & 0x00FF);
*val = w1_read_8(sl->master);
*val |= w1_read_8(sl->master)<<8;
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
static int w1_reg_a16_v16_write(void *context, unsigned int reg,
unsigned int val)
{
struct device *dev = context;
struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
int ret = 0;
if (reg > 65535)
return -EINVAL;
mutex_lock(&sl->master->bus_mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_8(sl->master, W1_CMD_WRITE_DATA);
w1_write_8(sl->master, reg & 0x00FF);
w1_write_8(sl->master, reg>>8 & 0x00FF);
w1_write_8(sl->master, val & 0x00FF);
w1_write_8(sl->master, val>>8 & 0x00FF);
} else {
ret = -ENODEV;
}
mutex_unlock(&sl->master->bus_mutex);
return ret;
}
/*
* Various types of supported bus addressing
*/
static struct regmap_bus regmap_w1_bus_a8_v8 = {
.reg_read = w1_reg_a8_v8_read,
.reg_write = w1_reg_a8_v8_write,
};
static struct regmap_bus regmap_w1_bus_a8_v16 = {
.reg_read = w1_reg_a8_v16_read,
.reg_write = w1_reg_a8_v16_write,
};
static struct regmap_bus regmap_w1_bus_a16_v16 = {
.reg_read = w1_reg_a16_v16_read,
.reg_write = w1_reg_a16_v16_write,
};
static const struct regmap_bus *regmap_get_w1_bus(struct device *w1_dev,
const struct regmap_config *config)
{
if (config->reg_bits == 8 && config->val_bits == 8)
return &regmap_w1_bus_a8_v8;
if (config->reg_bits == 8 && config->val_bits == 16)
return &regmap_w1_bus_a8_v16;
if (config->reg_bits == 16 && config->val_bits == 16)
return &regmap_w1_bus_a16_v16;
return ERR_PTR(-ENOTSUPP);
}
struct regmap *__regmap_init_w1(struct device *w1_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __regmap_init(w1_dev, bus, w1_dev, config,
lock_key, lock_name);
return NULL;
}
EXPORT_SYMBOL_GPL(__regmap_init_w1);
struct regmap *__devm_regmap_init_w1(struct device *w1_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name)
{
const struct regmap_bus *bus = regmap_get_w1_bus(w1_dev, config);
if (IS_ERR(bus))
return ERR_CAST(bus);
return __devm_regmap_init(w1_dev, bus, w1_dev, config,
lock_key, lock_name);
return NULL;
}
EXPORT_SYMBOL_GPL(__devm_regmap_init_w1);
MODULE_LICENSE("GPL");
...@@ -461,6 +461,10 @@ struct regmap *__regmap_init_spmi_ext(struct spmi_device *dev, ...@@ -461,6 +461,10 @@ struct regmap *__regmap_init_spmi_ext(struct spmi_device *dev,
const struct regmap_config *config, const struct regmap_config *config,
struct lock_class_key *lock_key, struct lock_class_key *lock_key,
const char *lock_name); const char *lock_name);
struct regmap *__regmap_init_w1(struct device *w1_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id, struct regmap *__regmap_init_mmio_clk(struct device *dev, const char *clk_id,
void __iomem *regs, void __iomem *regs,
const struct regmap_config *config, const struct regmap_config *config,
...@@ -493,6 +497,10 @@ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *dev, ...@@ -493,6 +497,10 @@ struct regmap *__devm_regmap_init_spmi_ext(struct spmi_device *dev,
const struct regmap_config *config, const struct regmap_config *config,
struct lock_class_key *lock_key, struct lock_class_key *lock_key,
const char *lock_name); const char *lock_name);
struct regmap *__devm_regmap_init_w1(struct device *w1_dev,
const struct regmap_config *config,
struct lock_class_key *lock_key,
const char *lock_name);
struct regmap *__devm_regmap_init_mmio_clk(struct device *dev, struct regmap *__devm_regmap_init_mmio_clk(struct device *dev,
const char *clk_id, const char *clk_id,
void __iomem *regs, void __iomem *regs,
...@@ -596,6 +604,19 @@ int regmap_attach_dev(struct device *dev, struct regmap *map, ...@@ -596,6 +604,19 @@ int regmap_attach_dev(struct device *dev, struct regmap *map,
__regmap_lockdep_wrapper(__regmap_init_spmi_ext, #config, \ __regmap_lockdep_wrapper(__regmap_init_spmi_ext, #config, \
dev, config) dev, config)
/**
* regmap_init_w1() - Initialise register map
*
* @w1_dev: Device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer to
* a struct regmap.
*/
#define regmap_init_w1(w1_dev, config) \
__regmap_lockdep_wrapper(__regmap_init_w1, #config, \
w1_dev, config)
/** /**
* regmap_init_mmio_clk() - Initialise register map with register clock * regmap_init_mmio_clk() - Initialise register map with register clock
* *
...@@ -711,6 +732,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg); ...@@ -711,6 +732,19 @@ bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
__regmap_lockdep_wrapper(__devm_regmap_init_spmi_ext, #config, \ __regmap_lockdep_wrapper(__devm_regmap_init_spmi_ext, #config, \
dev, config) dev, config)
/**
* devm_regmap_init_w1() - Initialise managed register map
*
* @w1_dev: Device that will be interacted with
* @config: Configuration for register map
*
* The return value will be an ERR_PTR() on error or a valid pointer
* to a struct regmap. The regmap will be automatically freed by the
* device management code.
*/
#define devm_regmap_init_w1(w1_dev, config) \
__regmap_lockdep_wrapper(__devm_regmap_init_w1, #config, \
w1_dev, config)
/** /**
* devm_regmap_init_mmio_clk() - Initialise managed register map with clock * devm_regmap_init_mmio_clk() - Initialise managed register map with clock
* *
...@@ -884,6 +918,7 @@ struct regmap_irq { ...@@ -884,6 +918,7 @@ struct regmap_irq {
* *
* @status_base: Base status register address. * @status_base: Base status register address.
* @mask_base: Base mask register address. * @mask_base: Base mask register address.
* @mask_writeonly: Base mask register is write only.
* @unmask_base: Base unmask register address. for chips who have * @unmask_base: Base unmask register address. for chips who have
* separate mask and unmask registers * separate mask and unmask registers
* @ack_base: Base ack address. If zero then the chip is clear on read. * @ack_base: Base ack address. If zero then the chip is clear on read.
...@@ -927,6 +962,7 @@ struct regmap_irq_chip { ...@@ -927,6 +962,7 @@ struct regmap_irq_chip {
unsigned int wake_base; unsigned int wake_base;
unsigned int type_base; unsigned int type_base;
unsigned int irq_reg_stride; unsigned int irq_reg_stride;
bool mask_writeonly:1;
bool init_ack_masked:1; bool init_ack_masked:1;
bool mask_invert:1; bool mask_invert:1;
bool use_ack:1; bool use_ack:1;
......
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