Commit 00f9dd0a authored by Tony Lindgren's avatar Tony Lindgren

i2c: Merge omap i2c drivers from omap-historic

Merge omap i2c drivers from omap-historic
Signed-off-by: default avatarTony Lindgren <tony@atomide.com>
parent 9972fb78
......@@ -4,6 +4,7 @@
menuconfig I2C
tristate "I2C support"
default y if MACH_OMAP_H3 || MACH_OMAP_OSK
---help---
I2C (pronounce: I-square-C) is a slow serial bus protocol used in
many micro controller applications and developed by Philips. SMBus,
......
......@@ -52,6 +52,7 @@ obj-$(CONFIG_I2C_VIAPRO) += i2c-viapro.o
obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o
obj-$(CONFIG_SCx200_ACB) += scx200_acb.o
obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o
obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
ifeq ($(CONFIG_I2C_DEBUG_BUS),y)
EXTRA_CFLAGS += -DDEBUG
......
......@@ -36,6 +36,10 @@
#include <asm/io.h>
/* Hack to enable zero length transfers and smbus quick until clean fix
is available */
#define OMAP_HACK
/* timeout waiting for the controller to respond */
#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
......@@ -285,12 +289,16 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
struct i2c_msg *msg, int stop)
{
struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
#ifdef OMAP_HACK
u8 zero_byte = 0;
#endif
int r;
u16 w;
dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
msg->addr, msg->len, msg->flags, stop);
#ifndef OMAP_HACK
if (msg->len == 0)
return -EINVAL;
......@@ -300,6 +308,27 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
dev->buf = msg->buf;
dev->buf_len = msg->len;
#else
omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
/* REVISIT: Remove this hack when we can get I2C chips from board-*.c
* files
* Sigh, seems we can't do zero length transactions. Thus, we
* can't probe for devices w/o actually sending/receiving at least
* a single byte. So we'll set count to 1 for the zero length
* transaction case and hope we don't cause grief for some
* arbitrary device due to random byte write/read during
* probes.
*/
if (msg->len == 0) {
dev->buf = &zero_byte;
dev->buf_len = 1;
} else {
dev->buf = msg->buf;
dev->buf_len = msg->len;
}
#endif
omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
init_completion(&dev->cmd_complete);
......@@ -314,8 +343,8 @@ static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
w |= OMAP_I2C_CON_STP;
omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
OMAP_I2C_TIMEOUT);
r = wait_for_completion_timeout(&dev->cmd_complete,
OMAP_I2C_TIMEOUT);
dev->buf_len = 0;
if (r < 0)
return r;
......@@ -383,7 +412,11 @@ out:
static u32
omap_i2c_func(struct i2c_adapter *adap)
{
#ifndef OMAP_HACK
return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
#else
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
#endif
}
static inline void
......@@ -478,9 +511,14 @@ omap_i2c_isr(int this_irq, void *dev_id)
if (dev->buf_len) {
*dev->buf++ = w;
dev->buf_len--;
if (dev->buf_len) {
*dev->buf++ = w >> 8;
dev->buf_len--;
/*
* Data reg in 2430 is 8 bit wide,
*/
if (!cpu_is_omap2430()) {
if (dev->buf_len) {
*dev->buf++ = w >> 8;
dev->buf_len--;
}
}
} else
dev_err(dev->dev, "RRDY IRQ while no data"
......@@ -493,9 +531,14 @@ omap_i2c_isr(int this_irq, void *dev_id)
if (dev->buf_len) {
w = *dev->buf++;
dev->buf_len--;
if (dev->buf_len) {
w |= *dev->buf++ << 8;
dev->buf_len--;
/*
* Data reg in 2430 is 8 bit wide,
*/
if (!cpu_is_omap2430()) {
if (dev->buf_len) {
w |= *dev->buf++ << 8;
dev->buf_len--;
}
}
} else
dev_err(dev->dev, "XRDY IRQ while no"
......
......@@ -100,6 +100,32 @@ config TPS65010
This driver can also be built as a module. If so, the module
will be called tps65010.
config SENSORS_TLV320AIC23
tristate "Texas Instruments TLV320AIC23 Codec"
depends on I2C && I2C_OMAP
help
If you say yes here you get support for the I2C control
interface for Texas Instruments TLV320AIC23 audio codec.
config GPIOEXPANDER_OMAP
bool "GPIO Expander PCF8574PWR for OMAP"
depends on I2C && (ARCH_OMAP16XX || ARCH_OMAP24XX)
help
If you say yes here you get support for I/O expander calls
to configure IrDA, Camera and audio devices.
config MENELAUS
bool "Menelaus PM chip"
depends on I2C=y && ARCH_OMAP24XX
help
Say yes here if you have Menelaus chip on your board
config TWL4030_CORE
bool "TI's TWL4030 companion chip Core Driver Support"
depends on I2C=y && ARCH_OMAP24XX
help
Say yes here if you have TWL4030 chip on your board
config SENSORS_M41T00
tristate "ST M41T00 RTC chip"
depends on PPC32
......
......@@ -12,6 +12,11 @@ obj-$(CONFIG_SENSORS_PCF8574) += pcf8574.o
obj-$(CONFIG_SENSORS_PCF8591) += pcf8591.o
obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o
obj-$(CONFIG_TPS65010) += tps65010.o
obj-$(CONFIG_SENSORS_TLV320AIC23) += tlv320aic23.o
obj-$(CONFIG_GPIOEXPANDER_OMAP) += gpio_expander_omap.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl4030_core.o
obj-$(CONFIG_RTC_X1205_I2C) += x1205.o
ifeq ($(CONFIG_I2C_DEBUG_CHIP),y)
EXTRA_CFLAGS += -DDEBUG
......
/*
* drivers/i2c/chips/gpio_expander_omap.c
*
* Copyright (C) 2004 Texas Instruments Inc
* Author:
*
* gpio expander is used to configure IrDA, camera and audio devices on omap 1710 processor.
*
* 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/types.h>
#include <linux/i2c.h>
#include <linux/errno.h>
int read_gpio_expa(u8 * val, int addr);
int write_gpio_expa(u8 val, int addr);
int write_gpio_expa(u8 val, int addr)
{
struct i2c_adapter *adap;
int err;
struct i2c_msg msg[1];
unsigned char data[1];
adap = i2c_get_adapter(0);
if (!adap)
return -ENODEV;
msg->addr = addr; /* I2C address of GPIO EXPA */
msg->flags = 0;
msg->len = 1;
msg->buf = data;
data[0] = val;
err = i2c_transfer(adap, msg, 1);
if (err >= 0)
return 0;
return err;
}
/* Read from I/O EXPANDER on the H3 board.
* The IO expanders need an independent I2C client driver.
*/
int read_gpio_expa(u8 * val, int addr)
{
struct i2c_adapter *adap;
int err;
struct i2c_msg msg[1];
unsigned char data[1];
adap = i2c_get_adapter(0);
if (!adap)
return -ENODEV;
msg->addr = addr; /* I2C address of GPIO EXPA */
msg->flags = I2C_M_RD;
msg->len = 2;
msg->buf = data;
err = i2c_transfer(adap, msg, 1);
*val = data[0];
if (err >= 0)
return 0;
return err;
}
EXPORT_SYMBOL(read_gpio_expa);
EXPORT_SYMBOL(write_gpio_expa);
......@@ -35,7 +35,11 @@
#include <linux/workqueue.h>
#include <asm/irq.h>
#include <asm/mach-types.h>
#include <asm/arch/gpio.h>
#include <asm/arch/usb.h>
#include <asm/arch/mux.h>
#ifndef DEBUG
......@@ -44,7 +48,7 @@
#define DRIVER_VERSION "24 August 2004"
#define DRIVER_NAME (isp1301_driver.name)
#define DRIVER_NAME (isp1301_driver.driver.name)
MODULE_DESCRIPTION("ISP1301 USB OTG Transceiver Driver");
MODULE_LICENSE("GPL");
......@@ -55,6 +59,7 @@ struct isp1301 {
void (*i2c_release)(struct device *dev);
int irq;
int irq_type;
u32 last_otg_ctrl;
unsigned working:1;
......@@ -63,7 +68,7 @@ struct isp1301 {
/* use keventd context to change the state for us */
struct work_struct work;
unsigned long todo;
# define WORK_UPDATE_ISP 0 /* update ISP from OTG */
# define WORK_UPDATE_OTG 1 /* update OTG from ISP */
......@@ -90,14 +95,11 @@ struct isp1301 {
/*-------------------------------------------------------------------------*/
#ifdef CONFIG_MACH_OMAP_H2
#if defined(CONFIG_MACH_OMAP_H2) || \
defined(CONFIG_MACH_OMAP_H3)
/* board-specific PM hooks */
#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include <asm/mach-types.h>
#if defined(CONFIG_TPS65010) || defined(CONFIG_TPS65010_MODULE)
......@@ -128,17 +130,30 @@ static void enable_vbus_source(struct isp1301 *isp)
}
/* products will deliver OTG messages with LEDs, GUI, etc */
static inline void notresponding(struct isp1301 *isp)
#else
static void enable_vbus_draw(struct isp1301 *isp, unsigned mA)
{
printk(KERN_NOTICE "OTG device not responding.\n");
pr_debug("%s UNIMPL\n", __FUNCTION__);
}
static void enable_vbus_source(struct isp1301 *isp)
{
pr_debug("%s UNIMPL\n", __FUNCTION__);
}
#endif
/*-------------------------------------------------------------------------*/
/* products will deliver OTG messages with LEDs, GUI, etc */
static inline void notresponding(struct isp1301 *isp)
{
printk(KERN_NOTICE "OTG device not responding.\n");
}
/*-------------------------------------------------------------------------*/
/* only two addresses possible */
#define ISP_BASE 0x2c
static unsigned short normal_i2c[] = {
......@@ -291,7 +306,7 @@ static void power_up(struct isp1301 *isp)
{
// isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND_REG);
/* do this only when cpu is driving transceiver,
* so host won't see a low speed device...
*/
......@@ -514,6 +529,7 @@ static inline void check_state(struct isp1301 *isp, const char *tag) { }
static void update_otg1(struct isp1301 *isp, u8 int_src)
{
u32 otg_ctrl;
u8 int_id;
otg_ctrl = OTG_CTRL_REG
& OTG_CTRL_MASK
......@@ -527,7 +543,10 @@ static void update_otg1(struct isp1301 *isp, u8 int_src)
}
if (int_src & INTR_VBUS_VLD)
otg_ctrl |= OTG_VBUSVLD;
if (int_src & INTR_ID_GND) { /* default-A */
int_id = isp1301_get_u8(isp, ISP1301_INTERRUPT_SOURCE);
if (int_id & INTR_ID_GND) { /* default-A */
if (isp->otg.state == OTG_STATE_B_IDLE
|| isp->otg.state == OTG_STATE_UNDEFINED) {
a_idle(isp, "init");
......@@ -799,7 +818,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
/* role is host */
} else {
if (!(otg_ctrl & OTG_ID)) {
otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
otg_ctrl &= OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
OTG_CTRL_REG = otg_ctrl | OTG_A_BUSREQ;
}
......@@ -1082,7 +1101,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
/* update the OTG controller state to match the isp1301; may
* trigger OPRT_CHG irqs for changes going to the isp1301.
*/
update_otg1(isp, isp_stat);
update_otg1(isp, stat); // pass the actual interrupt latch status
update_otg2(isp, isp_bstat);
check_state(isp, __FUNCTION__);
#endif
......@@ -1100,9 +1119,9 @@ static u8 isp1301_clear_latch(struct isp1301 *isp)
}
static void
isp1301_work(void *data)
isp1301_work(struct work_struct *work)
{
struct isp1301 *isp = data;
struct isp1301 *isp = container_of(work, struct isp1301, work);
int stop;
/* implicit lock: we're the only task using this device */
......@@ -1223,6 +1242,12 @@ static int isp1301_detach_client(struct i2c_client *i2c)
if (machine_is_omap_h2())
omap_free_gpio(2);
if (machine_is_omap_h3())
omap_free_gpio(14);
if (machine_is_omap_h4())
omap_free_gpio(125);
isp->timer.data = 0;
set_bit(WORK_STOP, &isp->todo);
del_timer_sync(&isp->timer);
......@@ -1244,7 +1269,7 @@ static int isp1301_detach_client(struct i2c_client *i2c)
* - DEVICE mode, for when there's a B/Mini-B (device) connector
*
* As a rule, you won't have an isp1301 chip unless it's there to
* support the OTG mode. Other modes help testing USB controllers
* support the OTG mode. Other modes help testing USB controllers
* in isolation from (full) OTG support, or maybe so later board
* revisions can help to support those feature.
*/
......@@ -1260,9 +1285,9 @@ static int isp1301_otg_enable(struct isp1301 *isp)
* a few more interrupts than are strictly needed.
*/
isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
INTR_VBUS_VLD | INTR_SESS_VLD | INTR_ID_GND);
dev_info(&isp->client.dev, "ready for dual-role USB ...\n");
......@@ -1301,14 +1326,15 @@ isp1301_set_host(struct otg_transceiver *otg, struct usb_bus *host)
power_up(isp);
if (machine_is_omap_h2())
// XXX h4 too?
if (machine_is_omap_h2() || machine_is_omap_h3())
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
dev_info(&isp->client.dev, "A-Host sessions ok\n");
isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
INTR_ID_GND);
INTR_ID_GND);
isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
INTR_ID_GND);
INTR_ID_GND);
/* If this has a Mini-AB connector, this mode is highly
* nonstandard ... but can be handy for testing, especially with
......@@ -1364,13 +1390,14 @@ isp1301_set_peripheral(struct otg_transceiver *otg, struct usb_gadget *gadget)
power_up(isp);
isp->otg.state = OTG_STATE_B_IDLE;
if (machine_is_omap_h2())
// XXX h4 too?
if (machine_is_omap_h2() || machine_is_omap_h3())
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
isp1301_set_bits(isp, ISP1301_INTERRUPT_RISING,
INTR_SESS_VLD);
INTR_SESS_VLD | INTR_VBUS_VLD);
isp1301_set_bits(isp, ISP1301_INTERRUPT_FALLING,
INTR_VBUS_VLD);
INTR_VBUS_VLD | INTR_SESS_VLD);
dev_info(&isp->client.dev, "B-Peripheral sessions ok\n");
dump_regs(isp, __FUNCTION__);
......@@ -1447,6 +1474,10 @@ isp1301_start_hnp(struct otg_transceiver *dev)
* So do this part as early as possible...
*/
switch (isp->otg.state) {
case OTG_STATE_B_PERIPHERAL:
isp->otg.state = OTG_STATE_B_WAIT_ACON;
isp1301_defer_work(isp, WORK_UPDATE_ISP);
break;
case OTG_STATE_B_HOST:
isp->otg.state = OTG_STATE_B_PERIPHERAL;
/* caller will suspend next */
......@@ -1494,12 +1525,13 @@ static int isp1301_probe(struct i2c_adapter *bus, int address, int kind)
if (!isp)
return 0;
INIT_WORK(&isp->work, isp1301_work, isp);
INIT_WORK(&isp->work, isp1301_work);
init_timer(&isp->timer);
isp->timer.function = isp1301_timer;
isp->timer.data = (unsigned long) isp;
isp->irq = -1;
isp->irq_type = 0;
isp->client.addr = address;
i2c_set_clientdata(&isp->client, isp);
isp->client.adapter = bus;
......@@ -1562,23 +1594,44 @@ fail1:
}
#endif
if (machine_is_omap_h2()) {
// XXX h4 too?
if (machine_is_omap_h2() || machine_is_omap_h3()) {
/* full speed signaling by default */
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1,
MC1_SPEED_REG);
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2,
MC2_SPD_SUSP_CTRL);
}
if (machine_is_omap_h2()) {
/* IRQ wired at M14 */
omap_cfg_reg(M14_1510_GPIO2);
isp->irq = OMAP_GPIO_IRQ(2);
omap_request_gpio(2);
omap_set_gpio_direction(2, 1);
omap_set_gpio_edge_ctrl(2, OMAP_GPIO_FALLING_EDGE);
isp->irq_type = IRQF_TRIGGER_FALLING;
}
if (machine_is_omap_h3()) {
/* IRQ wired at N21 */
omap_cfg_reg(N21_1710_GPIO14);
isp->irq = OMAP_GPIO_IRQ(14);
omap_request_gpio(14);
omap_set_gpio_direction(14, 1);
isp->irq_type = IRQF_TRIGGER_FALLING;
}
if (machine_is_omap_h4()) {
/* IRQ wired at P14 */
omap_cfg_reg(P14_24XX_GPIO125);
isp->irq = OMAP_GPIO_IRQ(125);
omap_request_gpio(125);
omap_set_gpio_direction(125, 1);
isp->irq_type = IRQF_TRIGGER_LOW;
}
status = request_irq(isp->irq, isp1301_irq,
IRQF_SAMPLE_RANDOM, DRIVER_NAME, isp);
isp->irq_type, DRIVER_NAME, isp);
if (status < 0) {
dev_dbg(&i2c->dev, "can't get IRQ %d, err %d\n",
isp->irq, status);
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
* twl4030.h - header for TWL4030 PM and audio CODEC device
*
* Copyright (C) 2005-2006 Texas Instruments, Inc.
*
* Based on tlv320aic23.c:
* Copyright (c) by Kai Svahn <kai.svahn@nokia.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef __TWL4030_H_
#define __TWL4030_H_
/* USB ID */
#define TWL4030_MODULE_USB 0x00
/* AUD ID */
#define TWL4030_MODULE_AUDIO_VOICE 0x01
#define TWL4030_MODULE_GPIO 0x02
#define TWL4030_MODULE_INTBR 0x03
#define TWL4030_MODULE_PIH 0x04
#define TWL4030_MODULE_TEST 0x05
/* AUX ID */
#define TWL4030_MODULE_KEYPAD 0x06
#define TWL4030_MODULE_MADC 0x07
#define TWL4030_MODULE_INTERRUPTS 0x08
#define TWL4030_MODULE_LED 0x09
#define TWL4030_MODULE_MAIN_CHARGE 0x0A
#define TWL4030_MODULE_PRECHARGE 0x0B
#define TWL4030_MODULE_PWM0 0x0C
#define TWL4030_MODULE_PWM1 0x0D
#define TWL4030_MODULE_PWMA 0x0E
#define TWL4030_MODULE_PWMB 0x0F
/* POWER ID */
#define TWL4030_MODULE_BACKUP 0x10
#define TWL4030_MODULE_INT 0x11
#define TWL4030_MODULE_PM_MASTER 0x12
#define TWL4030_MODULE_PM_RECIEVER 0x13
#define TWL4030_MODULE_RTC 0x14
#define TWL4030_MODULE_SECURED_REG 0x15
/* IRQ information-need base */
#include <asm/arch/irqs.h>
/* TWL4030 interrupts */
#define TWL4030_MODIRQ_GPIO (IH_TWL4030_BASE + 0)
#define TWL4030_MODIRQ_KEYPAD (IH_TWL4030_BASE + 1)
#define TWL4030_MODIRQ_BCI (IH_TWL4030_BASE + 2)
#define TWL4030_MODIRQ_MADC (IH_TWL4030_BASE + 3)
#define TWL4030_MODIRQ_USB (IH_TWL4030_BASE + 4)
#define TWL4030_MODIRQ_PWR (IH_TWL4030_BASE + 5)
/* Rest are unsued currently*/
/* Offsets to Power Registers */
#define TWL4030_VDAC_DEV_GRP 0x3B
#define TWL4030_VDAC_DEDICATED 0x3E
#define TWL4030_VAUX2_DEV_GRP 0x1B
#define TWL4030_VAUX2_DEDICATED 0x1E
#define TWL4030_VAUX3_DEV_GRP 0x1F
#define TWL4030_VAUX3_DEDICATED 0x22
/* Functions to read and write from TWL4030 */
/*
* IMP NOTE:
* The base address of the module will be added by the triton driver
* It is the caller's responsibility to ensure sane values
*/
int twl4030_i2c_write_u8(u8 mod_no, u8 val, u8 reg);
int twl4030_i2c_read_u8(u8 mod_no, u8* val, u8 reg);
/*
* i2c_write: IMPORTANT - Allocate value num_bytes+1 and valid data starts at
* Offset 1.
*/
int twl4030_i2c_write(u8 mod_no, u8 * value, u8 reg, u8 num_bytes);
int twl4030_i2c_read(u8 mod_no, u8 * value, u8 reg, u8 num_bytes);
#endif /* End of __TWL4030_H */
This diff is collapsed.
......@@ -118,6 +118,8 @@
#define I2C_DRIVERID_WM8731 89 /* Wolfson WM8731 audio codec */
#define I2C_DRIVERID_WM8750 90 /* Wolfson WM8750 audio codec */
#define I2C_DRIVERID_MISC 99 /* Whatever until sorted out */
#define I2C_DRIVERID_I2CDEV 900
#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
#define I2C_DRIVERID_ALERT 903 /* SMBus Alert Responder Client */
......
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