Hi,
second try. I've merged the new functions in patch 1 into asic3_set_register
and updated the following patches accordingly.
regards
Philipp
Used to configure single bits of the SDHWCTRL_SDCONF and EXTCF_RESET/SELECT
registers needed for DS1WM, MMC/SDIO and PCMCIA functionality.
Signed-off-by: Philipp Zabel <[email protected]>
---
drivers/mfd/asic3.c | 15 +++++++++++++++
include/linux/mfd/asic3.h | 10 +++++-----
2 files changed, 20 insertions(+), 5 deletions(-)
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 9e48545..ad3c591 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -52,6 +52,21 @@ static inline u32 asic3_read_register(struct asic3 *asic,
(reg >> asic->bus_shift));
}
+void asic3_set_register(struct asic3 *asic, u32 reg, u32 bits, bool set)
+{
+ unsigned long flags;
+ u32 val;
+
+ spin_lock_irqsave(&asic->lock, flags);
+ val = asic3_read_register(asic, reg);
+ if (set)
+ val |= bits;
+ else
+ val &= ~bits;
+ asic3_write_register(asic, reg, val);
+ spin_unlock_irqrestore(&asic->lock, flags);
+}
+
/* IRQs */
#define MAX_ASIC_ISR_LOOPS 20
#define ASIC3_GPIO_BASE_INCR \
diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h
index 322cd6d..6b427ec 100644
--- a/include/linux/mfd/asic3.h
+++ b/include/linux/mfd/asic3.h
@@ -227,8 +227,8 @@ struct asic3_platform_data {
/* Basic control of the SD ASIC */
-#define ASIC3_SDHWCTRL_Base 0x0E00
-#define ASIC3_SDHWCTRL_SDConf 0x00
+#define ASIC3_SDHWCTRL_BASE 0x0E00
+#define ASIC3_SDHWCTRL_SDCONF 0x00
#define ASIC3_SDHWCTRL_SUSPEND (1 << 0) /* 1=suspend all SD operations */
#define ASIC3_SDHWCTRL_CLKSEL (1 << 1) /* 1=SDICK, 0=HCLK */
@@ -242,10 +242,10 @@ struct asic3_platform_data {
/* SD card power supply ctrl 1=enable */
#define ASIC3_SDHWCTRL_SDPWR (1 << 6)
-#define ASIC3_EXTCF_Base 0x1100
+#define ASIC3_EXTCF_BASE 0x1100
-#define ASIC3_EXTCF_Select 0x00
-#define ASIC3_EXTCF_Reset 0x04
+#define ASIC3_EXTCF_SELECT 0x00
+#define ASIC3_EXTCF_RESET 0x04
#define ASIC3_EXTCF_SMOD0 (1 << 0) /* slot number of mode 0 */
#define ASIC3_EXTCF_SMOD1 (1 << 1) /* slot number of mode 1 */
--
1.6.3.1
Since ASIC3 has to work on both PXA and S3C and since their
struct clk implementations differ, we can't register out
clocks with the clkdev mechanism (yet?).
For now we have to keep clock handling internal to this
driver and enable/disable the clocks via the
mfd_cell->enable/disable functions.
Signed-off-by: Philipp Zabel <[email protected]>
---
drivers/mfd/asic3.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 80 insertions(+), 0 deletions(-)
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index ad3c591..ebe8893 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -25,6 +25,48 @@
#include <linux/mfd/asic3.h>
+enum {
+ ASIC3_CLOCK_SPI,
+ ASIC3_CLOCK_OWM,
+ ASIC3_CLOCK_PWM0,
+ ASIC3_CLOCK_PWM1,
+ ASIC3_CLOCK_LED0,
+ ASIC3_CLOCK_LED1,
+ ASIC3_CLOCK_LED2,
+ ASIC3_CLOCK_SD_HOST,
+ ASIC3_CLOCK_SD_BUS,
+ ASIC3_CLOCK_SMBUS,
+ ASIC3_CLOCK_EX0,
+ ASIC3_CLOCK_EX1,
+};
+
+struct asic3_clk {
+ int enabled;
+ unsigned int cdex;
+ unsigned long rate;
+};
+
+#define INIT_CDEX(_name, _rate) \
+ [ASIC3_CLOCK_##_name] = { \
+ .cdex = CLOCK_CDEX_##_name, \
+ .rate = _rate, \
+ }
+
+struct asic3_clk asic3_clk_init[] __initdata = {
+ INIT_CDEX(SPI, 0),
+ INIT_CDEX(OWM, 5000000),
+ INIT_CDEX(PWM0, 0),
+ INIT_CDEX(PWM1, 0),
+ INIT_CDEX(LED0, 0),
+ INIT_CDEX(LED1, 0),
+ INIT_CDEX(LED2, 0),
+ INIT_CDEX(SD_HOST, 24576000),
+ INIT_CDEX(SD_BUS, 12288000),
+ INIT_CDEX(SMBUS, 0),
+ INIT_CDEX(EX0, 32768),
+ INIT_CDEX(EX1, 24576000),
+};
+
struct asic3 {
void __iomem *mapping;
unsigned int bus_shift;
@@ -34,6 +76,8 @@ struct asic3 {
u16 irq_bothedge[4];
struct gpio_chip gpio;
struct device *dev;
+
+ struct asic3_clk clocks[ARRAY_SIZE(asic3_clk_init)];
};
static int asic3_gpio_get(struct gpio_chip *chip, unsigned offset);
@@ -540,6 +584,37 @@ static int asic3_gpio_remove(struct platform_device *pdev)
return gpiochip_remove(&asic->gpio);
}
+static int asic3_clk_enable(struct asic3 *asic, struct asic3_clk *clk)
+{
+ unsigned long flags;
+ u32 cdex;
+
+ spin_lock_irqsave(&asic->lock, flags);
+ if (clk->enabled++ == 0) {
+ cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
+ cdex |= clk->cdex;
+ asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
+ }
+ spin_unlock_irqrestore(&asic->lock, flags);
+
+ return 0;
+}
+
+static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
+{
+ unsigned long flags;
+ u32 cdex;
+
+ WARN_ON(clk->enabled == 0);
+
+ spin_lock_irqsave(&asic->lock, flags);
+ if (--clk->enabled == 0) {
+ cdex = asic3_read_register(asic, ASIC3_OFFSET(CLOCK, CDEX));
+ cdex &= ~clk->cdex;
+ asic3_write_register(asic, ASIC3_OFFSET(CLOCK, CDEX), cdex);
+ }
+ spin_unlock_irqrestore(&asic->lock, flags);
+}
/* Core */
static int __init asic3_probe(struct platform_device *pdev)
@@ -605,6 +680,11 @@ static int __init asic3_probe(struct platform_device *pdev)
goto out_irq;
}
+ /* Making a per-device copy is only needed for the
+ * theoretical case of multiple ASIC3s on one board:
+ */
+ memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init));
+
dev_info(asic->dev, "ASIC3 Core driver\n");
return 0;
--
1.6.3.1
IRQ number definitions for PWM, LED, SPI and OWM (ds1wm).
Signed-off-by: Philipp Zabel <[email protected]>
---
include/linux/mfd/asic3.h | 7 +++++++
1 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h
index 6b427ec..d2ecaaf 100644
--- a/include/linux/mfd/asic3.h
+++ b/include/linux/mfd/asic3.h
@@ -30,6 +30,13 @@ struct asic3_platform_data {
#define ASIC3_NUM_GPIOS 64
#define ASIC3_NR_IRQS ASIC3_NUM_GPIOS + 6
+#define ASIC3_IRQ_LED0 64
+#define ASIC3_IRQ_LED1 65
+#define ASIC3_IRQ_LED2 66
+#define ASIC3_IRQ_SPI 67
+#define ASIC3_IRQ_SMBUS 68
+#define ASIC3_IRQ_OWM 69
+
#define ASIC3_TO_GPIO(gpio) (NR_BUILTIN_GPIO + (gpio))
#define ASIC3_GPIO_BANK_A 0
--
1.6.3.1
This should make the code a little bit easier to read.
Signed-off-by: Philipp Zabel <[email protected]>
---
drivers/mfd/asic3.c | 6 ++----
1 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index ebe8893..d5dd0df 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -623,7 +623,6 @@ static int __init asic3_probe(struct platform_device *pdev)
struct asic3 *asic;
struct resource *mem;
unsigned long clksel;
- int map_size;
int ret = 0;
asic = kzalloc(sizeof(struct asic3), GFP_KERNEL);
@@ -643,8 +642,7 @@ static int __init asic3_probe(struct platform_device *pdev)
goto out_free;
}
- map_size = mem->end - mem->start + 1;
- asic->mapping = ioremap(mem->start, map_size);
+ asic->mapping = ioremap(mem->start, resource_size(mem));
if (!asic->mapping) {
ret = -ENOMEM;
dev_err(asic->dev, "Couldn't ioremap\n");
@@ -654,7 +652,7 @@ static int __init asic3_probe(struct platform_device *pdev)
asic->irq_base = pdata->irq_base;
/* calculate bus shift from mem resource */
- asic->bus_shift = 2 - (map_size >> 12);
+ asic->bus_shift = 2 - (resource_size(mem) >> 12);
clksel = 0;
asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL), clksel);
--
1.6.3.1
Only the base addresses remain, as they are needed to set up
the IOMEM resources.
Signed-off-by: Philipp Zabel <[email protected]>
---
include/linux/mfd/asic3.h | 219 +--------------------------------------------
1 files changed, 3 insertions(+), 216 deletions(-)
diff --git a/include/linux/mfd/asic3.h b/include/linux/mfd/asic3.h
index d2ecaaf..de3c4ad 100644
--- a/include/linux/mfd/asic3.h
+++ b/include/linux/mfd/asic3.h
@@ -286,222 +286,9 @@ struct asic3_platform_data {
* SDIO_CTRL Control registers for SDIO operations
*
*****************************************************************************/
-#define ASIC3_SD_CONFIG_Base 0x0400 /* Assumes 32 bit addressing */
-
-#define ASIC3_SD_CONFIG_Command 0x08 /* R/W: Command */
-
-/* [0:8] SD Control Register Base Address */
-#define ASIC3_SD_CONFIG_Addr0 0x20
-
-/* [9:31] SD Control Register Base Address */
-#define ASIC3_SD_CONFIG_Addr1 0x24
-
-/* R/O: interrupt assigned to pin */
-#define ASIC3_SD_CONFIG_IntPin 0x78
-
-/*
- * Set to 0x1f to clock SD controller, 0 otherwise.
- * At 0x82 - Gated Clock Ctrl
- */
-#define ASIC3_SD_CONFIG_ClkStop 0x80
-
-/* Control clock of SD controller */
-#define ASIC3_SD_CONFIG_ClockMode 0x84
-#define ASIC3_SD_CONFIG_SDHC_PinStatus 0x88 /* R/0: SD pins status */
-#define ASIC3_SD_CONFIG_SDHC_Power1 0x90 /* Power1 - manual pwr ctrl */
-
-/* auto power up after card inserted */
-#define ASIC3_SD_CONFIG_SDHC_Power2 0x92
-
-/* auto power down when card removed */
-#define ASIC3_SD_CONFIG_SDHC_Power3 0x94
-#define ASIC3_SD_CONFIG_SDHC_CardDetect 0x98
-#define ASIC3_SD_CONFIG_SDHC_Slot 0xA0 /* R/O: support slot number */
-#define ASIC3_SD_CONFIG_SDHC_ExtGateClk1 0x1E0 /* Not used */
-#define ASIC3_SD_CONFIG_SDHC_ExtGateClk2 0x1E2 /* Not used*/
-
-/* GPIO Output Reg. , at 0x1EA - GPIO Output Enable Reg. */
-#define ASIC3_SD_CONFIG_SDHC_GPIO_OutAndEnable 0x1E8
-#define ASIC3_SD_CONFIG_SDHC_GPIO_Status 0x1EC /* GPIO Status Reg. */
-
-/* Bit 1: double buffer/single buffer */
-#define ASIC3_SD_CONFIG_SDHC_ExtGateClk3 0x1F0
-
-/* Memory access enable (set to 1 to access SD Controller) */
-#define SD_CONFIG_COMMAND_MAE (1<<1)
-
-#define SD_CONFIG_CLK_ENABLE_ALL 0x1f
-
-#define SD_CONFIG_POWER1_PC_33V 0x0200 /* Set for 3.3 volts */
-#define SD_CONFIG_POWER1_PC_OFF 0x0000 /* Turn off power */
-
- /* two bits - number of cycles for card detection */
-#define SD_CONFIG_CARDDETECTMODE_CLK ((x) & 0x3)
-
-
-#define ASIC3_SD_CTRL_Base 0x1000
-
-#define ASIC3_SD_CTRL_Cmd 0x00
-#define ASIC3_SD_CTRL_Arg0 0x08
-#define ASIC3_SD_CTRL_Arg1 0x0C
-#define ASIC3_SD_CTRL_StopInternal 0x10
-#define ASIC3_SD_CTRL_TransferSectorCount 0x14
-#define ASIC3_SD_CTRL_Response0 0x18
-#define ASIC3_SD_CTRL_Response1 0x1C
-#define ASIC3_SD_CTRL_Response2 0x20
-#define ASIC3_SD_CTRL_Response3 0x24
-#define ASIC3_SD_CTRL_Response4 0x28
-#define ASIC3_SD_CTRL_Response5 0x2C
-#define ASIC3_SD_CTRL_Response6 0x30
-#define ASIC3_SD_CTRL_Response7 0x34
-#define ASIC3_SD_CTRL_CardStatus 0x38
-#define ASIC3_SD_CTRL_BufferCtrl 0x3C
-#define ASIC3_SD_CTRL_IntMaskCard 0x40
-#define ASIC3_SD_CTRL_IntMaskBuffer 0x44
-#define ASIC3_SD_CTRL_CardClockCtrl 0x48
-#define ASIC3_SD_CTRL_MemCardXferDataLen 0x4C
-#define ASIC3_SD_CTRL_MemCardOptionSetup 0x50
-#define ASIC3_SD_CTRL_ErrorStatus0 0x58
-#define ASIC3_SD_CTRL_ErrorStatus1 0x5C
-#define ASIC3_SD_CTRL_DataPort 0x60
-#define ASIC3_SD_CTRL_TransactionCtrl 0x68
-#define ASIC3_SD_CTRL_SoftwareReset 0x1C0
-
-#define SD_CTRL_SOFTWARE_RESET_CLEAR (1<<0)
-
-#define SD_CTRL_TRANSACTIONCONTROL_SET (1<<8)
-
-#define SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD (1<<15)
-#define SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK (1<<8)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_512 (1<<7)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_256 (1<<6)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_128 (1<<5)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_64 (1<<4)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_32 (1<<3)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_16 (1<<2)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_8 (1<<1)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_4 (1<<0)
-#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_2 (0<<0)
-
-#define MEM_CARD_OPTION_REQUIRED 0x000e
-#define MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(x) (((x) & 0x0f) << 4)
-#define MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT (1<<14)
-#define MEM_CARD_OPTION_DATA_XFR_WIDTH_1 (1<<15)
-#define MEM_CARD_OPTION_DATA_XFR_WIDTH_4 0
-
-#define SD_CTRL_COMMAND_INDEX(x) ((x) & 0x3f)
-#define SD_CTRL_COMMAND_TYPE_CMD (0 << 6)
-#define SD_CTRL_COMMAND_TYPE_ACMD (1 << 6)
-#define SD_CTRL_COMMAND_TYPE_AUTHENTICATION (2 << 6)
-#define SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL (0 << 8)
-#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1 (4 << 8)
-#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B (5 << 8)
-#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2 (6 << 8)
-#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 (7 << 8)
-#define SD_CTRL_COMMAND_DATA_PRESENT (1 << 11)
-#define SD_CTRL_COMMAND_TRANSFER_READ (1 << 12)
-#define SD_CTRL_COMMAND_TRANSFER_WRITE (0 << 12)
-#define SD_CTRL_COMMAND_MULTI_BLOCK (1 << 13)
-#define SD_CTRL_COMMAND_SECURITY_CMD (1 << 14)
-
-#define SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12 (1 << 0)
-#define SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12 (1 << 8)
-
-#define SD_CTRL_CARDSTATUS_RESPONSE_END (1 << 0)
-#define SD_CTRL_CARDSTATUS_RW_END (1 << 2)
-#define SD_CTRL_CARDSTATUS_CARD_REMOVED_0 (1 << 3)
-#define SD_CTRL_CARDSTATUS_CARD_INSERTED_0 (1 << 4)
-#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_0 (1 << 5)
-#define SD_CTRL_CARDSTATUS_WRITE_PROTECT (1 << 7)
-#define SD_CTRL_CARDSTATUS_CARD_REMOVED_3 (1 << 8)
-#define SD_CTRL_CARDSTATUS_CARD_INSERTED_3 (1 << 9)
-#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_3 (1 << 10)
-
-#define SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR (1 << 0)
-#define SD_CTRL_BUFFERSTATUS_CRC_ERROR (1 << 1)
-#define SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR (1 << 2)
-#define SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT (1 << 3)
-#define SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW (1 << 4)
-#define SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW (1 << 5)
-#define SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT (1 << 6)
-#define SD_CTRL_BUFFERSTATUS_UNK7 (1 << 7)
-#define SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE (1 << 8)
-#define SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE (1 << 9)
-#define SD_CTRL_BUFFERSTATUS_ILLEGAL_FUNCTION (1 << 13)
-#define SD_CTRL_BUFFERSTATUS_CMD_BUSY (1 << 14)
-#define SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS (1 << 15)
-
-#define SD_CTRL_INTMASKCARD_RESPONSE_END (1 << 0)
-#define SD_CTRL_INTMASKCARD_RW_END (1 << 2)
-#define SD_CTRL_INTMASKCARD_CARD_REMOVED_0 (1 << 3)
-#define SD_CTRL_INTMASKCARD_CARD_INSERTED_0 (1 << 4)
-#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 (1 << 5)
-#define SD_CTRL_INTMASKCARD_UNK6 (1 << 6)
-#define SD_CTRL_INTMASKCARD_WRITE_PROTECT (1 << 7)
-#define SD_CTRL_INTMASKCARD_CARD_REMOVED_3 (1 << 8)
-#define SD_CTRL_INTMASKCARD_CARD_INSERTED_3 (1 << 9)
-#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 (1 << 10)
-
-#define SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR (1 << 0)
-#define SD_CTRL_INTMASKBUFFER_CRC_ERROR (1 << 1)
-#define SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR (1 << 2)
-#define SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT (1 << 3)
-#define SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW (1 << 4)
-#define SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW (1 << 5)
-#define SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT (1 << 6)
-#define SD_CTRL_INTMASKBUFFER_UNK7 (1 << 7)
-#define SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE (1 << 8)
-#define SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE (1 << 9)
-#define SD_CTRL_INTMASKBUFFER_ILLEGAL_FUNCTION (1 << 13)
-#define SD_CTRL_INTMASKBUFFER_CMD_BUSY (1 << 14)
-#define SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS (1 << 15)
-
-#define SD_CTRL_DETAIL0_RESPONSE_CMD_ERROR (1 << 0)
-#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 2)
-#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_CMD12 (1 << 3)
-#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_READ_DATA (1 << 4)
-#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_WRITE_CRC_STATUS (1 << 5)
-#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 8)
-#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_CMD12 (1 << 9)
-#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_READ_DATA (1 << 10)
-#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_WRITE_CMD (1 << 11)
-
-#define SD_CTRL_DETAIL1_NO_CMD_RESPONSE (1 << 0)
-#define SD_CTRL_DETAIL1_TIMEOUT_READ_DATA (1 << 4)
-#define SD_CTRL_DETAIL1_TIMEOUT_CRS_STATUS (1 << 5)
-#define SD_CTRL_DETAIL1_TIMEOUT_CRC_BUSY (1 << 6)
-
-#define ASIC3_SDIO_CTRL_Base 0x1200
-
-#define ASIC3_SDIO_CTRL_Cmd 0x00
-#define ASIC3_SDIO_CTRL_CardPortSel 0x04
-#define ASIC3_SDIO_CTRL_Arg0 0x08
-#define ASIC3_SDIO_CTRL_Arg1 0x0C
-#define ASIC3_SDIO_CTRL_TransferBlockCount 0x14
-#define ASIC3_SDIO_CTRL_Response0 0x18
-#define ASIC3_SDIO_CTRL_Response1 0x1C
-#define ASIC3_SDIO_CTRL_Response2 0x20
-#define ASIC3_SDIO_CTRL_Response3 0x24
-#define ASIC3_SDIO_CTRL_Response4 0x28
-#define ASIC3_SDIO_CTRL_Response5 0x2C
-#define ASIC3_SDIO_CTRL_Response6 0x30
-#define ASIC3_SDIO_CTRL_Response7 0x34
-#define ASIC3_SDIO_CTRL_CardStatus 0x38
-#define ASIC3_SDIO_CTRL_BufferCtrl 0x3C
-#define ASIC3_SDIO_CTRL_IntMaskCard 0x40
-#define ASIC3_SDIO_CTRL_IntMaskBuffer 0x44
-#define ASIC3_SDIO_CTRL_CardXferDataLen 0x4C
-#define ASIC3_SDIO_CTRL_CardOptionSetup 0x50
-#define ASIC3_SDIO_CTRL_ErrorStatus0 0x54
-#define ASIC3_SDIO_CTRL_ErrorStatus1 0x58
-#define ASIC3_SDIO_CTRL_DataPort 0x60
-#define ASIC3_SDIO_CTRL_TransactionCtrl 0x68
-#define ASIC3_SDIO_CTRL_CardIntCtrl 0x6C
-#define ASIC3_SDIO_CTRL_ClocknWaitCtrl 0x70
-#define ASIC3_SDIO_CTRL_HostInformation 0x74
-#define ASIC3_SDIO_CTRL_ErrorCtrl 0x78
-#define ASIC3_SDIO_CTRL_LEDCtrl 0x7C
-#define ASIC3_SDIO_CTRL_SoftwareReset 0x1C0
+#define ASIC3_SD_CONFIG_BASE 0x0400 /* Assumes 32 bit addressing */
+#define ASIC3_SD_CTRL_BASE 0x1000
+#define ASIC3_SDIO_CTRL_BASE 0x1200
#define ASIC3_MAP_SIZE_32BIT 0x2000
#define ASIC3_MAP_SIZE_16BIT 0x1000
--
1.6.3.1
This enables the ASIC3's DS1WM MFD cell, supported by the ds1wm driver.
Signed-off-by: Philipp Zabel <[email protected]>
---
drivers/mfd/Kconfig | 1 +
drivers/mfd/asic3.c | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 101 insertions(+), 0 deletions(-)
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index ee3927a..9e6d5ee 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -30,6 +30,7 @@ config MFD_SM501_GPIO
config MFD_ASIC3
bool "Support for Compaq ASIC3"
depends on GENERIC_HARDIRQS && GPIOLIB && ARM
+ select MFD_CORE
---help---
This driver supports the ASIC3 multifunction chip found on many
PDAs (mainly iPAQ and HTC based ones)
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index d5dd0df..3fadd02 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -17,6 +17,7 @@
*/
#include <linux/kernel.h>
+#include <linux/delay.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <linux/io.h>
@@ -24,6 +25,8 @@
#include <linux/platform_device.h>
#include <linux/mfd/asic3.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/ds1wm.h>
enum {
ASIC3_CLOCK_SPI,
@@ -616,6 +619,99 @@ static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
spin_unlock_irqrestore(&asic->lock, flags);
}
+/* MFD cells (SPI, PWM, LED, DS1WM, MMC) */
+static struct ds1wm_driver_data ds1wm_pdata = {
+ .active_high = 1,
+};
+
+static struct resource ds1wm_resources[] = {
+ {
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = ASIC3_IRQ_OWM,
+ .start = ASIC3_IRQ_OWM,
+ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
+ },
+};
+
+static int ds1wm_enable(struct platform_device *pdev)
+{
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ /* Turn on external clocks and the OWM clock */
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
+ msleep(1);
+
+ /* Reset and enable DS1WM */
+ asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
+ ASIC3_EXTCF_OWM_RESET, 1);
+ msleep(1);
+ asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
+ ASIC3_EXTCF_OWM_RESET, 0);
+ msleep(1);
+ asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+ ASIC3_EXTCF_OWM_EN, 1);
+ msleep(1);
+
+ return 0;
+}
+
+static int ds1wm_disable(struct platform_device *pdev)
+{
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+ ASIC3_EXTCF_OWM_EN, 0);
+
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+
+ return 0;
+}
+
+static struct mfd_cell asic3_cell_ds1wm = {
+ .name = "ds1wm",
+ .enable = ds1wm_enable,
+ .disable = ds1wm_disable,
+ .driver_data = &ds1wm_pdata,
+ .num_resources = ARRAY_SIZE(ds1wm_resources),
+ .resources = ds1wm_resources,
+};
+
+static int __init asic3_mfd_probe(struct platform_device *pdev,
+ struct resource *mem)
+{
+ struct asic3 *asic = platform_get_drvdata(pdev);
+ int ret;
+
+ /* DS1WM */
+ asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+ ASIC3_EXTCF_OWM_SMB, 0);
+
+ ds1wm_resources[0].start = ASIC3_OWM_BASE >> asic->bus_shift;
+ ds1wm_resources[0].end = ds1wm_resources[0].start
+ + (5 << (2 - asic->bus_shift)) - 1;
+
+ asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm;
+ asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm);
+
+ ret = mfd_add_devices(&pdev->dev, pdev->id,
+ &asic3_cell_ds1wm, 1, mem, asic->irq_base);
+
+ return ret;
+}
+
+static void asic3_mfd_remove(struct platform_device *pdev)
+{
+ struct asic3 *asic = platform_get_drvdata(pdev);
+
+ mfd_remove_devices(&pdev->dev);
+}
+
/* Core */
static int __init asic3_probe(struct platform_device *pdev)
{
@@ -683,6 +779,8 @@ static int __init asic3_probe(struct platform_device *pdev)
*/
memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init));
+ asic3_mfd_probe(pdev, mem);
+
dev_info(asic->dev, "ASIC3 Core driver\n");
return 0;
@@ -704,6 +802,8 @@ static int asic3_remove(struct platform_device *pdev)
int ret;
struct asic3 *asic = platform_get_drvdata(pdev);
+ asic3_mfd_remove(pdev);
+
ret = asic3_gpio_remove(pdev);
if (ret < 0)
return ret;
--
1.6.3.1
This enables the ASIC3's SD/SDIO MFD cell, supported by the tmio_mmc driver.
Signed-off-by: Philipp Zabel <[email protected]>
---
drivers/mfd/asic3.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 113 insertions(+), 3 deletions(-)
diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
index 3fadd02..895d365 100644
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -27,6 +27,7 @@
#include <linux/mfd/asic3.h>
#include <linux/mfd/core.h>
#include <linux/mfd/ds1wm.h>
+#include <linux/mfd/tmio.h>
enum {
ASIC3_CLOCK_SPI,
@@ -682,11 +683,106 @@ static struct mfd_cell asic3_cell_ds1wm = {
.resources = ds1wm_resources,
};
+static struct tmio_mmc_data asic3_mmc_data = {
+ .hclk = 24576000,
+};
+
+static struct resource asic3_mmc_resources[] = {
+ {
+ .start = ASIC3_SD_CTRL_BASE,
+ .end = ASIC3_SD_CTRL_BASE + 0x3ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = ASIC3_SD_CONFIG_BASE,
+ .end = ASIC3_SD_CONFIG_BASE + 0x1ff,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .start = 0,
+ .end = 0,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static int asic3_mmc_enable(struct platform_device *pdev)
+{
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ /* Not sure if it must be done bit by bit, but leaving as-is */
+ asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+ ASIC3_SDHWCTRL_LEVCD, 1);
+ asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+ ASIC3_SDHWCTRL_LEVWP, 1);
+ asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+ ASIC3_SDHWCTRL_SUSPEND, 0);
+ asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+ ASIC3_SDHWCTRL_PCLR, 0);
+
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+ /* CLK32 used for card detection and for interruption detection
+ * when HCLK is stopped.
+ */
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+ msleep(1);
+
+ /* HCLK 24.576 MHz, BCLK 12.288 MHz: */
+ asic3_write_register(asic, ASIC3_OFFSET(CLOCK, SEL),
+ CLOCK_SEL_CX | CLOCK_SEL_SD_HCLK_SEL);
+
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
+ asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
+ msleep(1);
+
+ asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
+ ASIC3_EXTCF_SD_MEM_ENABLE, 1);
+
+ /* Enable SD card slot 3.3V power supply */
+ asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+ ASIC3_SDHWCTRL_SDPWR, 1);
+
+ return 0;
+}
+
+static int asic3_mmc_disable(struct platform_device *pdev)
+{
+ struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
+
+ /* Put in suspend mode */
+ asic3_set_register(asic, ASIC3_OFFSET(SDHWCTRL, SDCONF),
+ ASIC3_SDHWCTRL_SUSPEND, 1);
+
+ /* Disable clocks */
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_HOST]);
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_SD_BUS]);
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
+ asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
+ return 0;
+}
+
+static struct mfd_cell asic3_cell_mmc = {
+ .name = "tmio-mmc",
+ .enable = asic3_mmc_enable,
+ .disable = asic3_mmc_disable,
+ .driver_data = &asic3_mmc_data,
+ .num_resources = ARRAY_SIZE(asic3_mmc_resources),
+ .resources = asic3_mmc_resources,
+};
+
static int __init asic3_mfd_probe(struct platform_device *pdev,
struct resource *mem)
{
struct asic3 *asic = platform_get_drvdata(pdev);
- int ret;
+ struct resource *mem_sdio;
+ int irq, ret;
+
+ mem_sdio = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+ if (!mem_sdio)
+ dev_dbg(asic->dev, "no SDIO MEM resource\n");
+
+ irq = platform_get_irq(pdev, 1);
+ if (irq < 0)
+ dev_dbg(asic->dev, "no SDIO IRQ resource\n");
/* DS1WM */
asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
@@ -699,16 +795,30 @@ static int __init asic3_mfd_probe(struct platform_device *pdev,
asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm;
asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm);
+ /* MMC */
+ asic3_mmc_resources[0].start >>= (2 - asic->bus_shift);
+ asic3_mmc_resources[0].end >>= (2 - asic->bus_shift);
+ asic3_mmc_resources[1].start >>= (2 - asic->bus_shift);
+ asic3_mmc_resources[1].end >>= (2 - asic->bus_shift);
+
+ asic3_cell_mmc.platform_data = &asic3_cell_mmc;
+ asic3_cell_mmc.data_size = sizeof(asic3_cell_mmc);
+
ret = mfd_add_devices(&pdev->dev, pdev->id,
&asic3_cell_ds1wm, 1, mem, asic->irq_base);
+ if (ret < 0)
+ goto out;
+ if (mem_sdio && (irq >= 0))
+ ret = mfd_add_devices(&pdev->dev, pdev->id,
+ &asic3_cell_mmc, 1, mem_sdio, irq);
+
+ out:
return ret;
}
static void asic3_mfd_remove(struct platform_device *pdev)
{
- struct asic3 *asic = platform_get_drvdata(pdev);
-
mfd_remove_devices(&pdev->dev);
}
--
1.6.3.1
On Fri, Jun 5, 2009 at 6:31 PM, Philipp Zabel<[email protected]> wrote:
> This enables the ASIC3's DS1WM MFD cell, supported by the ds1wm driver.
>
> Signed-off-by: Philipp Zabel <[email protected]>
> ---
> ?drivers/mfd/Kconfig | ? ?1 +
> ?drivers/mfd/asic3.c | ?100 +++++++++++++++++++++++++++++++++++++++++++++++++++
> ?2 files changed, 101 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
> index ee3927a..9e6d5ee 100644
> --- a/drivers/mfd/Kconfig
> +++ b/drivers/mfd/Kconfig
> @@ -30,6 +30,7 @@ config MFD_SM501_GPIO
> ?config MFD_ASIC3
> ? ? ? ?bool "Support for Compaq ASIC3"
> ? ? ? ?depends on GENERIC_HARDIRQS && GPIOLIB && ARM
> + ? ? ? select MFD_CORE
> ? ? ? ? ---help---
> ? ? ? ? ?This driver supports the ASIC3 multifunction chip found on many
> ? ? ? ? ?PDAs (mainly iPAQ and HTC based ones)
> diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c
> index d5dd0df..3fadd02 100644
> --- a/drivers/mfd/asic3.c
> +++ b/drivers/mfd/asic3.c
> @@ -17,6 +17,7 @@
> ?*/
>
> ?#include <linux/kernel.h>
> +#include <linux/delay.h>
> ?#include <linux/irq.h>
> ?#include <linux/gpio.h>
> ?#include <linux/io.h>
> @@ -24,6 +25,8 @@
> ?#include <linux/platform_device.h>
>
> ?#include <linux/mfd/asic3.h>
> +#include <linux/mfd/core.h>
> +#include <linux/mfd/ds1wm.h>
>
> ?enum {
> ? ? ? ?ASIC3_CLOCK_SPI,
> @@ -616,6 +619,99 @@ static void asic3_clk_disable(struct asic3 *asic, struct asic3_clk *clk)
> ? ? ? ?spin_unlock_irqrestore(&asic->lock, flags);
> ?}
>
> +/* MFD cells (SPI, PWM, LED, DS1WM, MMC) */
> +static struct ds1wm_driver_data ds1wm_pdata = {
> + ? ? ? .active_high = 1,
> +};
> +
> +static struct resource ds1wm_resources[] = {
> + ? ? ? {
> + ? ? ? ? ? ? ? .flags = IORESOURCE_MEM,
> + ? ? ? },
> + ? ? ? {
> + ? ? ? ? ? ? ? .start = ASIC3_IRQ_OWM,
> + ? ? ? ? ? ? ? .start = ASIC3_IRQ_OWM,
> + ? ? ? ? ? ? ? .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE,
> + ? ? ? },
> +};
> +
> +static int ds1wm_enable(struct platform_device *pdev)
> +{
> + ? ? ? struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
> +
> + ? ? ? /* Turn on external clocks and the OWM clock */
> + ? ? ? asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
> + ? ? ? asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
> + ? ? ? asic3_clk_enable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
> + ? ? ? msleep(1);
> +
> + ? ? ? /* Reset and enable DS1WM */
> + ? ? ? asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
> + ? ? ? ? ? ? ? ? ? ? ? ? ?ASIC3_EXTCF_OWM_RESET, 1);
> + ? ? ? msleep(1);
> + ? ? ? asic3_set_register(asic, ASIC3_OFFSET(EXTCF, RESET),
> + ? ? ? ? ? ? ? ? ? ? ? ? ?ASIC3_EXTCF_OWM_RESET, 0);
> + ? ? ? msleep(1);
> + ? ? ? asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
> + ? ? ? ? ? ? ? ? ? ? ? ? ?ASIC3_EXTCF_OWM_EN, 1);
> + ? ? ? msleep(1);
> +
> + ? ? ? return 0;
> +}
> +
> +static int ds1wm_disable(struct platform_device *pdev)
> +{
> + ? ? ? struct asic3 *asic = dev_get_drvdata(pdev->dev.parent);
> +
> + ? ? ? asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
> + ? ? ? ? ? ? ? ? ? ? ? ? ?ASIC3_EXTCF_OWM_EN, 0);
> +
> + ? ? ? asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_OWM]);
> + ? ? ? asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX0]);
> + ? ? ? asic3_clk_disable(asic, &asic->clocks[ASIC3_CLOCK_EX1]);
> +
> + ? ? ? return 0;
> +}
> +
> +static struct mfd_cell asic3_cell_ds1wm = {
> + ? ? ? .name ? ? ? ? ?= "ds1wm",
> + ? ? ? .enable ? ? ? ?= ds1wm_enable,
> + ? ? ? .disable ? ? ? = ds1wm_disable,
> + ? ? ? .driver_data ? = &ds1wm_pdata,
> + ? ? ? .num_resources = ARRAY_SIZE(ds1wm_resources),
> + ? ? ? .resources ? ? = ds1wm_resources,
> +};
> +
> +static int __init asic3_mfd_probe(struct platform_device *pdev,
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? struct resource *mem)
> +{
> + ? ? ? struct asic3 *asic = platform_get_drvdata(pdev);
> + ? ? ? int ret;
> +
> + ? ? ? /* DS1WM */
> + ? ? ? asic3_set_register(asic, ASIC3_OFFSET(EXTCF, SELECT),
> + ? ? ? ? ? ? ? ? ? ? ? ? ?ASIC3_EXTCF_OWM_SMB, 0);
> +
> + ? ? ? ds1wm_resources[0].start = ASIC3_OWM_BASE >> asic->bus_shift;
> + ? ? ? ds1wm_resources[0].end = ds1wm_resources[0].start
> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?+ (5 << (2 - asic->bus_shift)) - 1;
> +
> + ? ? ? asic3_cell_ds1wm.platform_data = &asic3_cell_ds1wm;
> + ? ? ? asic3_cell_ds1wm.data_size = sizeof(asic3_cell_ds1wm);
> +
> + ? ? ? ret = mfd_add_devices(&pdev->dev, pdev->id,
> + ? ? ? ? ? ? ? ? ? ? ? &asic3_cell_ds1wm, 1, mem, asic->irq_base);
> +
> + ? ? ? return ret;
> +}
> +
> +static void asic3_mfd_remove(struct platform_device *pdev)
> +{
> + ? ? ? struct asic3 *asic = platform_get_drvdata(pdev);
> +
> + ? ? ? mfd_remove_devices(&pdev->dev);
> +}
I've just noticed that the following hunk got squashed into 7/7 instead of 6/7:
--- a/drivers/mfd/asic3.c
+++ b/drivers/mfd/asic3.c
@@ -737,8 +737,6 @@ static int __init asic3_mfd_probe(struct platform_device *pd
static void asic3_mfd_remove(struct platform_device *pdev)
{
- struct asic3 *asic = platform_get_drvdata(pdev);
-
mfd_remove_devices(&pdev->dev);
}
Please consider replacing those two with the fixed versions I'll send.
> +
> ?/* Core */
> ?static int __init asic3_probe(struct platform_device *pdev)
> ?{
> @@ -683,6 +779,8 @@ static int __init asic3_probe(struct platform_device *pdev)
> ? ? ? ? */
> ? ? ? ?memcpy(asic->clocks, asic3_clk_init, sizeof(asic3_clk_init));
>
> + ? ? ? asic3_mfd_probe(pdev, mem);
> +
> ? ? ? ?dev_info(asic->dev, "ASIC3 Core driver\n");
>
> ? ? ? ?return 0;
> @@ -704,6 +802,8 @@ static int asic3_remove(struct platform_device *pdev)
> ? ? ? ?int ret;
> ? ? ? ?struct asic3 *asic = platform_get_drvdata(pdev);
>
> + ? ? ? asic3_mfd_remove(pdev);
> +
> ? ? ? ?ret = asic3_gpio_remove(pdev);
> ? ? ? ?if (ret < 0)
> ? ? ? ? ? ? ? ?return ret;
> --
> 1.6.3.1
>
>
regards
Philipp