Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- i2c/nx_i2c.c: Some adaptions mainly because of changes in
  "struct udevice".
- mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).

Signed-off-by: Stefan Bosch <stefa...@posteo.net>
---

 drivers/gpio/Kconfig           |   9 +
 drivers/gpio/Makefile          |   1 +
 drivers/gpio/nx_gpio.c         | 252 +++++++++++++++++++
 drivers/i2c/Kconfig            |   9 +
 drivers/i2c/Makefile           |   1 +
 drivers/i2c/nx_i2c.c           | 537 +++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/Kconfig            |   6 +
 drivers/mmc/Makefile           |   1 +
 drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++
 drivers/pwm/Makefile           |   1 +
 drivers/pwm/pwm-nexell.c       | 252 +++++++++++++++++++
 drivers/pwm/pwm-nexell.h       |  54 +++++
 12 files changed, 1473 insertions(+)
 create mode 100644 drivers/gpio/nx_gpio.c
 create mode 100644 drivers/i2c/nx_i2c.c
 create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c
 create mode 100644 drivers/pwm/pwm-nexell.c
 create mode 100644 drivers/pwm/pwm-nexell.h

diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 1de6f52..febda89 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -421,4 +421,13 @@ config MT7621_GPIO
        help
          Say yes here to support MediaTek MT7621 compatible GPIOs.
 
+config NX_GPIO
+       bool "Nexell GPIO driver"
+       depends on DM_GPIO
+       help
+         Support GPIO access on Nexell SoCs. The GPIOs are arranged into
+         a number of banks (different for each SoC type) each with 32 GPIOs.
+         The GPIOs for a device are defined in the device tree with one node
+         for each bank.
+
 endmenu
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 449046b..e3340de 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO)     += pm8916_gpio.o
 obj-$(CONFIG_MT7621_GPIO)      += mt7621_gpio.o
 obj-$(CONFIG_MSCC_SGPIO)       += mscc_sgpio.o
 obj-$(CONFIG_SIFIVE_GPIO)      += sifive-gpio.o
+obj-$(CONFIG_NX_GPIO)          += nx_gpio.o
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c
new file mode 100644
index 0000000..86472f6
--- /dev/null
+++ b/drivers/gpio/nx_gpio.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016 Nexell
+ * DeokJin, Lee <truevir...@nexell.co.kr>
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <malloc.h>
+#include <fdtdec.h>
+#include <asm/io.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct nx_gpio_regs {
+       u32     data;           /* Data register */
+       u32     outputenb;      /* Output Enable register */
+       u32     detmode[2];     /* Detect Mode Register */
+       u32     intenb;         /* Interrupt Enable Register */
+       u32     det;            /* Event Detect Register */
+       u32     pad;            /* Pad Status Register */
+};
+
+struct nx_alive_gpio_regs {
+       u32     pwrgate;        /* Power Gating Register */
+       u32     reserved0[28];  /* Reserved0 */
+       u32     outputenb_reset;/* Alive GPIO Output Enable Reset Register */
+       u32     outputenb;      /* Alive GPIO Output Enable Register */
+       u32     outputenb_read; /* Alive GPIO Output Read Register */
+       u32     reserved1[3];   /* Reserved1 */
+       u32     pad_reset;      /* Alive GPIO Output Reset Register */
+       u32     data;           /* Alive GPIO Output Register */
+       u32     pad_read;       /* Alive GPIO Pad Read Register */
+       u32     reserved2[33];  /* Reserved2 */
+       u32     pad;            /* Alive GPIO Input Value Register */
+};
+
+struct nx_gpio_platdata {
+       void *regs;
+       int gpio_count;
+       const char *bank_name;
+};
+
+static int nx_alive_gpio_is_check(struct udevice *dev)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       const char *bank_name = plat->bank_name;
+
+       if (!strcmp(bank_name, "gpio_alv"))
+               return 1;
+
+       return 0;
+}
+
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_alive_gpio_regs *const regs = plat->regs;
+
+       setbits_le32(&regs->outputenb_reset, 1 << pin);
+
+       return 0;
+}
+
+static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned int 
pin,
+                                         int val)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_alive_gpio_regs *const regs = plat->regs;
+
+       if (val)
+               setbits_le32(&regs->data, 1 << pin);
+       else
+               setbits_le32(&regs->pad_reset, 1 << pin);
+
+       setbits_le32(&regs->outputenb, 1 << pin);
+
+       return 0;
+}
+
+static int nx_alive_gpio_get_value(struct udevice *dev, unsigned int pin)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_alive_gpio_regs *const regs = plat->regs;
+       unsigned int mask = 1UL << pin;
+       unsigned int value;
+
+       value = (readl(&regs->pad_read) & mask) >> pin;
+
+       return value;
+}
+
+static int nx_alive_gpio_set_value(struct udevice *dev, unsigned int pin,
+                                  int val)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_alive_gpio_regs *const regs = plat->regs;
+
+       if (val)
+               setbits_le32(&regs->data, 1 << pin);
+       else
+               clrbits_le32(&regs->pad_reset, 1 << pin);
+
+       return 0;
+}
+
+static int nx_alive_gpio_get_function(struct udevice *dev, unsigned int pin)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_alive_gpio_regs *const regs = plat->regs;
+       unsigned int mask = (1UL << pin);
+       unsigned int output;
+
+       output = readl(&regs->outputenb_read) & mask;
+
+       if (output)
+               return GPIOF_OUTPUT;
+       else
+               return GPIOF_INPUT;
+}
+
+static int nx_gpio_direction_input(struct udevice *dev, unsigned int pin)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_gpio_regs *const regs = plat->regs;
+
+       if (nx_alive_gpio_is_check(dev))
+               return nx_alive_gpio_direction_input(dev, pin);
+
+       clrbits_le32(&regs->outputenb, 1 << pin);
+
+       return 0;
+}
+
+static int nx_gpio_direction_output(struct udevice *dev, unsigned int pin,
+                                   int val)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_gpio_regs *const regs = plat->regs;
+
+       if (nx_alive_gpio_is_check(dev))
+               return nx_alive_gpio_direction_output(dev, pin, val);
+
+       if (val)
+               setbits_le32(&regs->data, 1 << pin);
+       else
+               clrbits_le32(&regs->data, 1 << pin);
+
+       setbits_le32(&regs->outputenb, 1 << pin);
+
+       return 0;
+}
+
+static int nx_gpio_get_value(struct udevice *dev, unsigned int pin)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_gpio_regs *const regs = plat->regs;
+       unsigned int mask = 1UL << pin;
+       unsigned int value;
+
+       if (nx_alive_gpio_is_check(dev))
+               return nx_alive_gpio_get_value(dev, pin);
+
+       value = (readl(&regs->pad) & mask) >> pin;
+
+       return value;
+}
+
+static int nx_gpio_set_value(struct udevice *dev, unsigned int pin, int val)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_gpio_regs *const regs = plat->regs;
+
+       if (nx_alive_gpio_is_check(dev))
+               return nx_alive_gpio_set_value(dev, pin, val);
+
+       if (val)
+               setbits_le32(&regs->data, 1 << pin);
+       else
+               clrbits_le32(&regs->data, 1 << pin);
+
+       return 0;
+}
+
+static int nx_gpio_get_function(struct udevice *dev, unsigned int pin)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+       struct nx_gpio_regs *const regs = plat->regs;
+       unsigned int mask = (1UL << pin);
+       unsigned int output;
+
+       if (nx_alive_gpio_is_check(dev))
+               return nx_alive_gpio_get_function(dev, pin);
+
+       output = readl(&regs->outputenb) & mask;
+
+       if (output)
+               return GPIOF_OUTPUT;
+       else
+               return GPIOF_INPUT;
+}
+
+static int nx_gpio_probe(struct udevice *dev)
+{
+       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+
+       uc_priv->gpio_count = plat->gpio_count;
+       uc_priv->bank_name = plat->bank_name;
+
+       return 0;
+}
+
+static int nx_gpio_ofdata_to_platdata(struct udevice *dev)
+{
+       struct nx_gpio_platdata *plat = dev_get_platdata(dev);
+
+       plat->regs = map_physmem(devfdt_get_addr(dev),
+                                sizeof(struct nx_gpio_regs),
+                                MAP_NOCACHE);
+       plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->node.of_offset,
+                                         "nexell,gpio-bank-width", 32);
+       plat->bank_name = fdt_getprop(gd->fdt_blob, dev->node.of_offset,
+                                     "gpio-bank-name", NULL);
+
+       return 0;
+}
+
+static const struct dm_gpio_ops nx_gpio_ops = {
+       .direction_input        = nx_gpio_direction_input,
+       .direction_output       = nx_gpio_direction_output,
+       .get_value              = nx_gpio_get_value,
+       .set_value              = nx_gpio_set_value,
+       .get_function           = nx_gpio_get_function,
+};
+
+static const struct udevice_id nx_gpio_ids[] = {
+       { .compatible = "nexell,nexell-gpio" },
+       { }
+};
+
+U_BOOT_DRIVER(nx_gpio) = {
+       .name           = "nx_gpio",
+       .id             = UCLASS_GPIO,
+       .of_match       = nx_gpio_ids,
+       .ops            = &nx_gpio_ops,
+       .ofdata_to_platdata = nx_gpio_ofdata_to_platdata,
+       .platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata),
+       .probe          = nx_gpio_probe,
+};
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 03d2fed..2cd0ed3 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -317,6 +317,15 @@ config SYS_MXC_I2C8_SLAVE
         MXC I2C8 Slave
 endif
 
+config SYS_I2C_NEXELL
+       bool "Nexell I2C driver"
+       depends on DM_I2C
+       help
+         Add support for the Nexell I2C driver. This is used with various
+         Nexell parts such as S5Pxx18 series SoCs. All chips
+         have several I2C ports and all are provided, controlled by the
+         device tree.
+
 config SYS_I2C_OMAP24XX
        bool "TI OMAP2+ I2C driver"
        depends on ARCH_OMAP2PLUS || ARCH_K3
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile
index f5a471f..64b8ead 100644
--- a/drivers/i2c/Makefile
+++ b/drivers/i2c/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_SYS_I2C_LPC32XX) += lpc32xx_i2c.o
 obj-$(CONFIG_SYS_I2C_MESON) += meson_i2c.o
 obj-$(CONFIG_SYS_I2C_MVTWSI) += mvtwsi.o
 obj-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o
+obj-$(CONFIG_SYS_I2C_NEXELL) += nx_i2c.o
 obj-$(CONFIG_SYS_I2C_OMAP24XX) += omap24xx_i2c.o
 obj-$(CONFIG_SYS_I2C_RCAR_I2C) += rcar_i2c.o
 obj-$(CONFIG_SYS_I2C_RCAR_IIC) += rcar_iic.o
diff --git a/drivers/i2c/nx_i2c.c b/drivers/i2c/nx_i2c.c
new file mode 100644
index 0000000..a3eec6c
--- /dev/null
+++ b/drivers/i2c/nx_i2c.c
@@ -0,0 +1,537 @@
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <i2c.h>
+#include <asm/arch/nexell.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/clk.h>
+#include <asm/arch/nx_gpio.h>
+
+#define I2C_WRITE       0
+#define I2C_READ        1
+
+#define I2C_OK          0
+#define I2C_NOK         1
+#define I2C_NACK        2
+#define I2C_NOK_LA      3       /* Lost arbitration */
+#define I2C_NOK_TOUT    4       /* time out */
+
+#define I2CLC_FILTER   0x04    /* SDA filter on*/
+#define I2CSTAT_BSY     0x20    /* Busy bit */
+#define I2CSTAT_NACK    0x01    /* Nack bit */
+#define I2CSTAT_ABT    0x08    /* Arbitration bit */
+#define I2CCON_ACKGEN   0x80    /* Acknowledge generation */
+#define I2CCON_IRENB   0x20    /* Interrupt Enable bit  */
+#define I2CCON_IRPND    0x10    /* Interrupt pending bit */
+#define I2C_MODE_MT     0xC0    /* Master Transmit Mode */
+#define I2C_MODE_MR     0x80    /* Master Receive Mode */
+#define I2C_START_STOP  0x20    /* START / STOP */
+#define I2C_TXRX_ENA    0x10    /* I2C Tx/Rx enable */
+
+#define I2C_TIMEOUT_MS 10      /* 10 ms */
+
+#define I2C_M_NOSTOP   0x100
+
+#ifndef CONFIG_MAX_I2C_NUM
+#define CONFIG_MAX_I2C_NUM 3
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct nx_i2c_regs {
+       uint     iiccon;
+       uint     iicstat;
+       uint     iicadd;
+       uint     iicds;
+       uint     iiclc;
+};
+
+struct nx_i2c_bus {
+       uint bus_num;
+       struct nx_i2c_regs *regs;
+       uint speed;
+       uint target_speed;
+       uint sda_delay;
+};
+
+/* s5pxx18 i2c must be reset before enabled */
+static void i2c_reset(int ch)
+{
+       int rst_id = RESET_ID_I2C0 + ch;
+
+       nx_rstcon_setrst(rst_id, 0);
+       nx_rstcon_setrst(rst_id, 1);
+}
+
+/* FIXME : this func will be removed after reset dm driver ported.
+ * set mmc pad alternative func.
+ */
+static void set_i2c_pad_func(struct nx_i2c_bus *i2c)
+{
+       switch (i2c->bus_num) {
+       case 0:
+               nx_gpio_set_pad_function(3, 2, 1);
+               nx_gpio_set_pad_function(3, 3, 1);
+               break;
+       case 1:
+               nx_gpio_set_pad_function(3, 4, 1);
+               nx_gpio_set_pad_function(3, 5, 1);
+               break;
+       case 2:
+               nx_gpio_set_pad_function(3, 6, 1);
+               nx_gpio_set_pad_function(3, 7, 1);
+               break;
+       }
+}
+
+static uint i2c_get_clkrate(struct nx_i2c_bus *bus)
+{
+       struct clk *clk;
+       int index = bus->bus_num;
+       char name[50] = {0, };
+
+       sprintf(name, "%s.%d", DEV_NAME_I2C, index);
+       clk = clk_get((const char *)name);
+       if (!clk)
+               return -1;
+
+       return clk_get_rate(clk);
+}
+
+static uint i2c_set_clk(struct nx_i2c_bus *bus, uint enb)
+{
+       struct clk *clk;
+       char name[50];
+
+       sprintf(name, "%s.%d", DEV_NAME_I2C, bus->bus_num);
+       clk = clk_get((const char *)name);
+       if (!clk)
+               return -1;
+
+       if (enb) {
+               clk_disable(clk);
+               clk_enable(clk);
+       } else {
+               clk_disable(clk);
+       }
+
+       return 0;
+}
+
+/* get i2c module number from base address */
+static uint i2c_get_busnum(struct nx_i2c_bus *bus)
+{
+       void *base_addr = (void *)PHY_BASEADDR_I2C0;
+       int i;
+
+       for (i = 0; i < CONFIG_MAX_I2C_NUM; i++) {
+               if (base_addr == ((void *)bus->regs)) {
+                       bus->bus_num = i;
+                       return i;
+               }
+               base_addr += 0x1000;
+       }
+
+       return -1;
+}
+
+/* Set SDA line delay */
+static int nx_i2c_set_sda_delay(struct nx_i2c_bus *bus, ulong clkin)
+{
+       struct nx_i2c_regs *i2c = bus->regs;
+       uint sda_delay = 0;
+
+       if (bus->sda_delay) {
+               sda_delay = clkin * bus->sda_delay;
+               sda_delay = DIV_ROUND_UP(sda_delay, 1000000);
+               sda_delay = DIV_ROUND_UP(sda_delay, 5);
+               if (sda_delay > 3)
+                       sda_delay = 3;
+               sda_delay |= I2CLC_FILTER;
+       } else {
+               sda_delay = 0;
+       }
+
+       sda_delay &= 0x7;
+       writel(sda_delay, &i2c->iiclc);
+
+       return 0;
+}
+
+/* Calculate the value of the divider and prescaler, set the bus speed. */
+static int nx_i2c_set_bus_speed(struct udevice *dev, uint speed)
+{
+       struct nx_i2c_bus *bus = dev_get_priv(dev);
+       struct nx_i2c_regs *i2c = bus->regs;
+       unsigned long freq, pres = 16, div;
+
+       freq = i2c_get_clkrate(bus);
+       /* calculate prescaler and divisor values */
+       if ((freq / pres / (16 + 1)) > speed)
+               /* set prescaler to 512 */
+               pres = 512;
+
+       div = 0;
+       while ((freq / pres / (div + 1)) > speed)
+               div++;
+
+       /* set prescaler, divisor according to freq, also set ACKGEN, IRQ */
+       writel((div & 0x0F) | ((pres == 512) ? 0x40 : 0), &i2c->iiccon);
+
+       /* init to SLAVE REVEIVE and set slaveaddr */
+       writel(0, &i2c->iicstat);
+       writel(0x00, &i2c->iicadd);
+       /* program Master Transmit (and implicit STOP) */
+       writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
+
+       bus->speed = bus->target_speed / (div * pres);
+
+       return 0;
+}
+
+static void nx_i2c_set_clockrate(struct udevice *dev, uint speed)
+{
+       struct nx_i2c_bus *bus = dev_get_priv(dev);
+       ulong clkin;
+
+       nx_i2c_set_bus_speed(dev, speed);
+       clkin = bus->speed;                     /* the actual i2c speed */
+       clkin /= 1000;                          /* clkin now in Khz */
+       nx_i2c_set_sda_delay(bus, clkin);
+}
+
+static void i2c_process_node(struct udevice *dev)
+{
+       struct nx_i2c_bus *bus = dev_get_priv(dev);
+       const void *blob = gd->fdt_blob;
+
+       int node;
+
+       node = dev->node.of_offset;
+
+       bus->target_speed = fdtdec_get_int(blob, node,
+                                          "nexell,i2c-max-bus-freq", 0);
+       bus->sda_delay = fdtdec_get_int(blob, node,
+                                       "nexell,i2c-sda-delay", 0);
+}
+
+static int nx_i2c_probe(struct udevice *dev)
+{
+       struct nx_i2c_bus *bus = dev_get_priv(dev);
+
+       /* get regs */
+       bus->regs = (struct nx_i2c_regs *)devfdt_get_addr(dev);
+       /* calc index */
+       if (!i2c_get_busnum(bus)) {
+               debug("not found i2c number!\n");
+               return -1;
+       }
+
+       /* i2c optional node parsing */
+       i2c_process_node(dev);
+       if (!bus->target_speed)
+               return -1;
+
+       /* reset */
+       i2c_reset(bus->bus_num);
+       /* gpio pad */
+       set_i2c_pad_func(bus);
+
+       /* clock rate */
+       i2c_set_clk(bus, 1);
+       nx_i2c_set_clockrate(dev, bus->target_speed);
+       i2c_set_clk(bus, 0);
+
+       return 0;
+}
+
+/* i2c bus busy check */
+static int i2c_is_busy(struct nx_i2c_regs *i2c)
+{
+       ulong start_time;
+
+       start_time = get_timer(0);
+       while (readl(&i2c->iicstat) & I2CSTAT_BSY) {
+               if (get_timer(start_time) > I2C_TIMEOUT_MS) {
+                       debug("Timeout\n");
+                       return -I2C_NOK_TOUT;
+               }
+       }
+       return 0;
+}
+
+/* irq enable/disable functions */
+static void i2c_enable_irq(struct nx_i2c_regs *i2c)
+{
+       unsigned int reg;
+
+       reg = readl(&i2c->iiccon);
+       reg |= I2CCON_IRENB;
+       writel(reg, &i2c->iiccon);
+}
+
+/* irq clear function */
+static void i2c_clear_irq(struct nx_i2c_regs *i2c)
+{
+       clrbits_le32(&i2c->iiccon, I2CCON_IRPND);
+}
+
+/* ack enable functions */
+static void i2c_enable_ack(struct nx_i2c_regs *i2c)
+{
+       unsigned int reg;
+
+       reg = readl(&i2c->iiccon);
+       reg |= I2CCON_ACKGEN;
+       writel(reg, &i2c->iiccon);
+}
+
+static void i2c_send_stop(struct nx_i2c_regs *i2c)
+{
+       unsigned int reg;
+
+       /* Send STOP. */
+       reg = readl(&i2c->iicstat);
+       reg |= I2C_MODE_MR | I2C_TXRX_ENA;
+       reg &= (~I2C_START_STOP);
+       writel(reg, &i2c->iicstat);
+       i2c_clear_irq(i2c);
+}
+
+static int wait_for_xfer(struct nx_i2c_regs *i2c)
+{
+       unsigned long start_time = get_timer(0);
+
+       do {
+               if (readl(&i2c->iiccon) & I2CCON_IRPND)
+                       return (readl(&i2c->iicstat) & I2CSTAT_NACK) ?
+                               I2C_NACK : I2C_OK;
+       } while (get_timer(start_time) < I2C_TIMEOUT_MS);
+
+       return I2C_NOK_TOUT;
+}
+
+static int i2c_transfer(struct nx_i2c_regs *i2c,
+                       uchar cmd_type,
+                       uchar chip,
+                       uchar addr[],
+                       uchar addr_len,
+                       uchar data[],
+                       unsigned short data_len,
+                       uint seq)
+{
+       uint status;
+       int i = 0, result;
+
+       if (data == 0 || data_len == 0) {
+               /*Don't support data transfer of no length or to address 0 */
+               debug("%s: bad call\n", __func__);
+               return I2C_NOK;
+       }
+
+       i2c_enable_irq(i2c);
+       i2c_enable_ack(i2c);
+
+       /* Get the slave chip address going */
+       writel(chip, &i2c->iicds);
+       status = I2C_TXRX_ENA | I2C_START_STOP;
+       if (cmd_type == I2C_WRITE || (addr && addr_len))
+               status |= I2C_MODE_MT;
+       else
+               status |= I2C_MODE_MR;
+       writel(status, &i2c->iicstat);
+       if (seq)
+               i2c_clear_irq(i2c);
+
+       /* Wait for chip address to transmit. */
+       result = wait_for_xfer(i2c);
+       if (result != I2C_OK)
+               goto bailout;
+
+       /* If register address needs to be transmitted - do it now. */
+       if (addr && addr_len) {  /* register addr */
+               while ((i < addr_len) && (result == I2C_OK)) {
+                       writel(addr[i++], &i2c->iicds);
+                       i2c_clear_irq(i2c);
+                       result = wait_for_xfer(i2c);
+               }
+
+               i = 0;
+               if (result != I2C_OK)
+                       goto bailout;
+       }
+
+       switch (cmd_type) {
+       case I2C_WRITE:
+               while ((i < data_len) && (result == I2C_OK)) {
+                       writel(data[i++], &i2c->iicds);
+                       i2c_clear_irq(i2c);
+                       result = wait_for_xfer(i2c);
+               }
+               break;
+       case I2C_READ:
+               if (addr && addr_len) {
+                       /*
+                        * Register address has been sent, now send slave chip
+                        * address again to start the actual read transaction.
+                        */
+                       writel(chip, &i2c->iicds);
+
+                       /* Generate a re-START. */
+                       writel(I2C_MODE_MR | I2C_TXRX_ENA
+                                       | I2C_START_STOP, &i2c->iicstat);
+                       i2c_clear_irq(i2c);
+                       result = wait_for_xfer(i2c);
+                       if (result != I2C_OK)
+                               goto bailout;
+               }
+
+               while ((i < data_len) && (result == I2C_OK)) {
+                       /* disable ACK for final READ */
+                       if (i == data_len - 1)
+                               clrbits_le32(&i2c->iiccon
+                                               , I2CCON_ACKGEN);
+
+                       i2c_clear_irq(i2c);
+                       result = wait_for_xfer(i2c);
+                       data[i++] = readb(&i2c->iicds);
+               }
+
+               if (result == I2C_NACK)
+                       result = I2C_OK; /* Normal terminated read. */
+               break;
+
+       default:
+               debug("%s: bad call\n", __func__);
+               result = I2C_NOK;
+               break;
+       }
+
+bailout:
+       return result;
+}
+
+static int nx_i2c_read(struct udevice *dev, uchar chip, uint addr,
+                      uint alen, uchar *buffer, uint len, uint seq)
+{
+       struct nx_i2c_bus *i2c;
+       uchar xaddr[4];
+       int ret;
+
+       i2c = dev_get_priv(dev);
+       if (!i2c)
+               return -EFAULT;
+
+       if (alen > 4) {
+               debug("I2C read: addr len %d not supported\n", alen);
+               return -EADDRNOTAVAIL;
+       }
+
+       if (alen > 0)
+               xaddr[0] = (addr >> 24) & 0xFF;
+
+       if (alen > 0) {
+               xaddr[0] = (addr >> 24) & 0xFF;
+               xaddr[1] = (addr >> 16) & 0xFF;
+               xaddr[2] = (addr >> 8) & 0xFF;
+               xaddr[3] = addr & 0xFF;
+       }
+
+       ret = i2c_transfer(i2c->regs, I2C_READ, chip << 1,
+                          &xaddr[4 - alen], alen, buffer, len, seq);
+
+       if (ret) {
+               debug("I2C read failed %d\n", ret);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int nx_i2c_write(struct udevice *dev, uchar chip, uint addr,
+                       uint alen, uchar *buffer, uint len, uint seq)
+{
+       struct nx_i2c_bus *i2c;
+       uchar xaddr[4];
+       int ret;
+
+       i2c = dev_get_priv(dev);
+       if (!i2c)
+               return -EFAULT;
+
+       if (alen > 4) {
+               debug("I2C write: addr len %d not supported\n", alen);
+               return -EINVAL;
+       }
+
+       if (alen > 0) {
+               xaddr[0] = (addr >> 24) & 0xFF;
+               xaddr[1] = (addr >> 16) & 0xFF;
+               xaddr[2] = (addr >> 8) & 0xFF;
+               xaddr[3] = addr & 0xFF;
+       }
+
+       ret = i2c_transfer(i2c->regs, I2C_WRITE, chip << 1,
+                          &xaddr[4 - alen], alen, buffer, len, seq);
+       if (ret) {
+               debug("I2C write failed %d\n", ret);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+static int nx_i2c_xfer(struct udevice *dev, struct i2c_msg *msg, int nmsgs)
+{
+       struct nx_i2c_bus *bus = dev_get_priv(dev);
+       struct nx_i2c_regs *i2c = bus->regs;
+       int ret;
+       int i;
+
+       /* The power loss by the clock, only during on/off. */
+       i2c_set_clk(bus, 1);
+
+       /* Bus State(Busy) check  */
+       ret = i2c_is_busy(i2c);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < nmsgs; msg++, i++) {
+               if (msg->flags & I2C_M_RD) {
+                       ret = nx_i2c_read(dev, msg->addr, 0, 0, msg->buf,
+                                         msg->len, i);
+               } else {
+                       ret = nx_i2c_write(dev, msg->addr, 0, 0, msg->buf,
+                                          msg->len, i);
+               }
+
+               if (ret) {
+                       debug("i2c_xfer: error sending\n");
+                       return -EREMOTEIO;
+               }
+       }
+       /* Send Stop */
+       i2c_send_stop(i2c);
+       i2c_set_clk(bus, 0);
+
+       return ret ? -EREMOTEIO : 0;
+};
+
+static const struct dm_i2c_ops nx_i2c_ops = {
+       .xfer           = nx_i2c_xfer,
+       .set_bus_speed  = nx_i2c_set_bus_speed,
+};
+
+static const struct udevice_id nx_i2c_ids[] = {
+       { .compatible = "nexell,s5pxx18-i2c" },
+       { }
+};
+
+U_BOOT_DRIVER(i2c_nexell) = {
+       .name           = "i2c_nexell",
+       .id             = UCLASS_I2C,
+       .of_match       = nx_i2c_ids,
+       .probe          = nx_i2c_probe,
+       .priv_auto_alloc_size   = sizeof(struct nx_i2c_bus),
+       .ops            = &nx_i2c_ops,
+};
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 2f0eedc..bb8e7c0 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -253,6 +253,12 @@ config MMC_DW_SNPS
          This selects support for Synopsys DesignWare Memory Card Interface 
driver
          extensions used in various Synopsys ARC devboards.
 
+config NEXELL_DWMMC
+       bool "Nexell SD/MMC controller support"
+       depends on ARCH_NEXELL
+       depends on MMC_DW
+       default y
+
 config MMC_MESON_GX
        bool "Meson GX EMMC controller support"
        depends on DM_MMC && BLK && ARCH_MESON
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 9c1f8e5..a7b5a7b 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
 obj-$(CONFIG_SH_SDHI) += sh_sdhi.o
 obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o
 obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o
+obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o
 
 # SDHCI
 obj-$(CONFIG_MMC_SDHCI)                        += sdhci.o
diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c
new file mode 100644
index 0000000..b06b60d
--- /dev/null
+++ b/drivers/mmc/nexell_dw_mmc_dm.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * (C) Copyright 2016 Nexell
+ * Youngbok, Park <p...@nexell.co.kr>
+ *
+ * (C) Copyright 2019 Stefan Bosch <stefa...@posteo.net>
+ */
+
+#include <common.h>
+#include <clk.h>
+#include <dm.h>
+#include <dt-structs.h>
+#include <dwmmc.h>
+#include <syscon.h>
+#include <asm/gpio.h>
+#include <asm/arch/nx_gpio.h>
+#include <asm/arch/reset.h>
+
+#define DWMCI_CLKSEL                   0x09C
+#define DWMCI_SHIFT_0                  0x0
+#define DWMCI_SHIFT_1                  0x1
+#define DWMCI_SHIFT_2                  0x2
+#define DWMCI_SHIFT_3                  0x3
+#define DWMCI_SET_SAMPLE_CLK(x)        (x)
+#define DWMCI_SET_DRV_CLK(x)   ((x) << 16)
+#define DWMCI_SET_DIV_RATIO(x) ((x) << 24)
+#define DWMCI_CLKCTRL                  0x114
+#define NX_MMC_CLK_DELAY(x, y, a, b)   ((((x) & 0xFF) << 0) |\
+                                       (((y) & 0x03) << 16) |\
+                                       (((a) & 0xFF) << 8)  |\
+                                       (((b) & 0x03) << 24))
+
+struct nexell_mmc_plat {
+       struct mmc_config cfg;
+       struct mmc mmc;
+};
+
+struct nexell_dwmmc_priv {
+       struct clk *clk;
+       struct dwmci_host host;
+       int fifo_size;
+       bool fifo_mode;
+       int frequency;
+       u32 min_freq;
+       u32 max_freq;
+       int d_delay;
+       int d_shift;
+       int s_delay;
+       int s_shift;
+
+};
+
+struct clk *clk_get(const char *id);
+
+static void set_pin_stat(int index, int bit, int value)
+{
+#if !defined(CONFIG_SPL_BUILD)
+       nx_gpio_set_pad_function(index, bit, value);
+#else
+#if defined(CONFIG_ARCH_S5P4418) ||    \
+       defined(CONFIG_ARCH_S5P6818)
+
+       unsigned long base[5] = {
+               PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB,
+               PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD,
+               PHY_BASEADDR_GPIOE,
+       };
+
+       dw_mmc_set_pin(base[index], bit, value);
+#endif
+#endif
+}
+
+static void nx_dw_mmc_set_pin(struct dwmci_host *host)
+{
+       debug("  %s(): dev_index == %d", __func__, host->dev_index);
+
+       switch (host->dev_index) {
+       case 0:
+               set_pin_stat(0, 29, 1);
+               set_pin_stat(0, 31, 1);
+               set_pin_stat(1, 1, 1);
+               set_pin_stat(1, 3, 1);
+               set_pin_stat(1, 5, 1);
+               set_pin_stat(1, 7, 1);
+               break;
+       case 1:
+               set_pin_stat(3, 22, 1);
+               set_pin_stat(3, 23, 1);
+               set_pin_stat(3, 24, 1);
+               set_pin_stat(3, 25, 1);
+               set_pin_stat(3, 26, 1);
+               set_pin_stat(3, 27, 1);
+               break;
+       case 2:
+               set_pin_stat(2, 18, 2);
+               set_pin_stat(2, 19, 2);
+               set_pin_stat(2, 20, 2);
+               set_pin_stat(2, 21, 2);
+               set_pin_stat(2, 22, 2);
+               set_pin_stat(2, 23, 2);
+               if (host->buswidth == 8) {
+                       set_pin_stat(4, 21, 2);
+                       set_pin_stat(4, 22, 2);
+                       set_pin_stat(4, 23, 2);
+                       set_pin_stat(4, 24, 2);
+               }
+               break;
+       default:
+               debug(" is invalid!");
+       }
+       debug("\n");
+}
+
+static void nx_dw_mmc_clksel(struct dwmci_host *host)
+{
+       u32 val;
+
+#ifdef CONFIG_BOOST_MMC
+       val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
+           DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1);
+#else
+       val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
+           DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3);
+#endif
+
+       dwmci_writel(host, DWMCI_CLKSEL, val);
+}
+
+static void nx_dw_mmc_reset(int ch)
+{
+       int rst_id = RESET_ID_SDMMC0 + ch;
+
+       nx_rstcon_setrst(rst_id, 0);
+       nx_rstcon_setrst(rst_id, 1);
+}
+
+static void nx_dw_mmc_clk_delay(struct udevice *dev)
+{
+       unsigned int delay;
+       struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+       struct dwmci_host *host = &priv->host;
+
+       delay = NX_MMC_CLK_DELAY(priv->d_delay,
+                                priv->d_shift, priv->s_delay, priv->s_shift);
+
+       writel(delay, (host->ioaddr + DWMCI_CLKCTRL));
+       debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, "
+             "s_shift==%d\n", __func__, priv->d_delay, priv->d_shift,
+             priv->s_delay, priv->s_shift);
+}
+
+static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq)
+{
+       struct clk *clk;
+       struct udevice *dev = host->priv;
+       struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+
+       int index = host->dev_index;
+       char name[50] = { 0, };
+
+       clk = priv->clk;
+       if (!clk) {
+               sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
+               clk = clk_get((const char *)name);
+               if (!clk)
+                       return 0;
+               priv->clk = clk;
+       }
+
+       return clk_get_rate(clk) / 2;
+}
+
+static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host,
+                                      unsigned int rate)
+{
+       struct clk *clk;
+       char name[50] = { 0, };
+       struct udevice *dev = host->priv;
+       struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+
+       int index = host->dev_index;
+
+       clk = priv->clk;
+       if (!clk) {
+               sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
+               clk = clk_get((const char *)name);
+               if (!clk)
+                       return 0;
+               priv->clk = clk;
+       }
+
+       clk_disable(clk);
+       rate = clk_set_rate(clk, rate);
+       clk_enable(clk);
+
+       return rate;
+}
+
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev)
+{
+       /* if (dev): *priv = dev->priv, else: *priv = NULL */
+       struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+       struct dwmci_host *host = &priv->host;
+       int val = -1;
+
+       debug("%s()\n", __func__);
+
+       host->name = dev->name;
+       host->ioaddr = dev_read_addr_ptr(dev);
+
+       val = dev_read_u32_default(dev, "nexell,bus-width", -1);
+       if (val < 0) {
+               debug("  'nexell,bus-width' missing/invalid!\n");
+               return -EINVAL;
+       }
+       host->buswidth = val;
+       host->get_mmc_clk = nx_dw_mmc_get_clk;
+       host->clksel = nx_dw_mmc_clksel;
+       host->priv = dev;
+
+       val = dev_read_u32_default(dev, "index", -1);
+       if (val < 0) {
+               debug("  'index' missing/invalid!\n");
+               return -EINVAL;
+       }
+       host->dev_index = val;
+
+       val = dev_read_u32_default(dev, "fifo-size", 0x20);
+       if (val <= 0) {
+               debug("  'fifo-size' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->fifo_size = val;
+
+       priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
+
+       val = dev_read_u32_default(dev, "frequency", -1);
+       if (val < 0) {
+               debug("  'frequency' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->frequency = val;
+
+       val = dev_read_u32_default(dev, "max-frequency", -1);
+       if (val < 0) {
+               debug("  'max-frequency' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->max_freq = val;
+       priv->min_freq = 400000;  /* 400 kHz */
+
+       val = dev_read_u32_default(dev, "nexell,drive_dly", -1);
+       if (val < 0) {
+               debug("  'nexell,drive_dly' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->d_delay = val;
+
+       val = dev_read_u32_default(dev, "nexell,drive_shift", -1);
+       if (val < 0) {
+               debug("  'nexell,drive_shift' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->d_shift = val;
+
+       val = dev_read_u32_default(dev, "nexell,sample_dly", -1);
+       if (val < 0) {
+               debug("  'nexell,sample_dly' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->s_delay = val;
+
+       val = dev_read_u32_default(dev, "nexell,sample_shift", -1);
+       if (val < 0) {
+               debug("  'nexell,sample_shift' missing/invalid!\n");
+               return -EINVAL;
+       }
+       priv->s_shift = val;
+
+       debug("  index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, "
+                 "fifo_size==%d, fifo_mode==%d, frequency==%d\n",
+                 host->dev_index, host->name, (u32)host->ioaddr,
+                 host->buswidth, priv->fifo_size, priv->fifo_mode,
+                 priv->frequency);
+       debug("  min_freq==%d, max_freq==%d, delay: "
+                 "0x%02x:0x%02x:0x%02x:0x%02x\n",
+                 priv->min_freq, priv->max_freq, priv->d_delay,
+                 priv->d_shift, priv->s_delay, priv->s_shift);
+
+       return 0;
+}
+
+static int nexell_dwmmc_probe(struct udevice *dev)
+{
+       struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+       struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+       struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
+       struct dwmci_host *host = &priv->host;
+       struct udevice *pwr_dev __maybe_unused;
+
+       debug("%s():\n", __func__);
+
+       host->fifoth_val = MSIZE(0x2) |
+               RX_WMARK(priv->fifo_size / 2 - 1) |
+               TX_WMARK(priv->fifo_size / 2);
+
+       host->fifo_mode = priv->fifo_mode;
+
+       dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq);
+       host->mmc = &plat->mmc;
+       host->mmc->priv = &priv->host;
+       host->mmc->dev = dev;
+       upriv->mmc = host->mmc;
+
+       nx_dw_mmc_set_pin(host);
+
+       debug("  nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n",
+             priv->frequency * 4);
+       nx_dw_mmc_set_clk(host, priv->frequency * 4);
+
+       nx_dw_mmc_reset(host->dev_index);
+       nx_dw_mmc_clk_delay(dev);
+
+       return dwmci_probe(dev);
+}
+
+static int nexell_dwmmc_bind(struct udevice *dev)
+{
+       struct nexell_mmc_plat *plat = dev_get_platdata(dev);
+
+       return dwmci_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static const struct udevice_id nexell_dwmmc_ids[] = {
+       { .compatible = "nexell,nexell-dwmmc" },
+       { }
+};
+
+U_BOOT_DRIVER(nexell_dwmmc_drv) = {
+       .name           = "nexell_dwmmc",
+       .id             = UCLASS_MMC,
+       .of_match       = nexell_dwmmc_ids,
+       .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata,
+       .ops            = &dm_dwmci_ops,
+       .bind           = nexell_dwmmc_bind,
+       .probe          = nexell_dwmmc_probe,
+       .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv),
+       .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat),
+};
diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
index a837c35..b45aada 100644
--- a/drivers/pwm/Makefile
+++ b/drivers/pwm/Makefile
@@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP)    += rk_pwm.o
 obj-$(CONFIG_PWM_SANDBOX)      += sandbox_pwm.o
 obj-$(CONFIG_PWM_TEGRA)                += tegra_pwm.o
 obj-$(CONFIG_PWM_SUNXI)                += sunxi_pwm.o
+obj-$(CONFIG_PWM_NX)           += pwm-nexell.o
diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c
new file mode 100644
index 0000000..6c0f8f4
--- /dev/null
+++ b/drivers/pwm/pwm-nexell.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2011 Samsung Electronics
+ *
+ * Donghwa Lee <dh09....@samsung.com>
+ */
+
+/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */
+
+#include <common.h>
+#include <errno.h>
+#include <pwm.h>
+#include <asm/io.h>
+#include <asm/arch/clk.h>
+#include "pwm-nexell.h"
+
+#if defined(CONFIG_ARCH_NEXELL)
+#include <asm/arch/nexell.h>
+#include <asm/arch/reset.h>
+#include <asm/arch/nx_gpio.h>
+#include <asm/arch/tieoff.h>
+
+struct pwm_device {
+       int ch;
+       int grp;
+       int bit;
+       int pwm_fn;
+};
+
+static struct pwm_device pwm_dev[] = {
+       [0] = { .ch = 0, .grp = 3, .bit = 1,  .pwm_fn = 1 },
+       [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 },
+       [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 },
+       [3] = { .ch = 3, .grp = 3, .bit = 0,  .pwm_fn = 2 },
+};
+#endif
+
+int pwm_enable(int pwm_id)
+{
+       const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+                       (struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
+                       (struct s5p_timer *)samsung_get_base_timer();
+#endif
+       unsigned long tcon;
+
+       tcon = readl(&pwm->tcon);
+       tcon |= TCON_START(pwm_id);
+
+       writel(tcon, &pwm->tcon);
+
+       return 0;
+}
+
+void pwm_disable(int pwm_id)
+{
+       const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+                       (struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
+                       (struct s5p_timer *)samsung_get_base_timer();
+#endif
+       unsigned long tcon;
+
+       tcon = readl(&pwm->tcon);
+       tcon &= ~TCON_START(pwm_id);
+
+       writel(tcon, &pwm->tcon);
+}
+
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq)
+{
+       unsigned long tin_parent_rate;
+       unsigned int div;
+#if defined(CONFIG_ARCH_NEXELL)
+       unsigned int pre_div;
+       const struct s5p_timer *pwm =
+               (struct s5p_timer *)PHY_BASEADDR_PWM;
+       unsigned int val;
+#endif
+
+#if defined(CONFIG_ARCH_NEXELL)
+       struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+
+       tin_parent_rate = clk_get_rate(clk);
+#else
+       tin_parent_rate = get_pwm_clk();
+#endif
+
+#if defined(CONFIG_ARCH_NEXELL)
+       writel(0, &pwm->tcfg0);
+       val = readl(&pwm->tcfg0);
+
+       if (pwm_id < 2)
+               div = ((val >> 0) & 0xff) + 1;
+       else
+               div = ((val >> 8) & 0xff) + 1;
+
+       writel(0, &pwm->tcfg1);
+       val = readl(&pwm->tcfg1);
+       val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF;
+       pre_div = (1UL << val);
+
+       freq = tin_parent_rate / div / pre_div;
+
+       return freq;
+#else
+       for (div = 2; div <= 16; div *= 2) {
+               if ((tin_parent_rate / (div << 16)) < freq)
+                       return tin_parent_rate / div;
+       }
+
+       return tin_parent_rate / 16;
+#endif
+}
+
+#define NS_IN_SEC 1000000000UL
+
+int pwm_config(int pwm_id, int duty_ns, int period_ns)
+{
+       const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+               (struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
+               (struct s5p_timer *)samsung_get_base_timer();
+#endif
+       unsigned int offset;
+       unsigned long tin_rate;
+       unsigned long tin_ns;
+       unsigned long frequency;
+       unsigned long tcon;
+       unsigned long tcnt;
+       unsigned long tcmp;
+
+       /*
+        * We currently avoid using 64bit arithmetic by using the
+        * fact that anything faster than 1GHz is easily representable
+        * by 32bits.
+        */
+       if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0)
+               return -ERANGE;
+
+       if (duty_ns > period_ns)
+               return -EINVAL;
+
+       frequency = NS_IN_SEC / period_ns;
+
+       /* Check to see if we are changing the clock rate of the PWM */
+       tin_rate = pwm_calc_tin(pwm_id, frequency);
+
+       tin_ns = NS_IN_SEC / tin_rate;
+#if defined(CONFIG_ARCH_NEXELL)
+       /* The counter starts at zero. */
+       tcnt = (period_ns / tin_ns) - 1;
+#else
+       tcnt = period_ns / tin_ns;
+#endif
+
+       /* Note, counters count down */
+       tcmp = duty_ns / tin_ns;
+       tcmp = tcnt - tcmp;
+
+       /* Update the PWM register block. */
+       offset = pwm_id * 3;
+       if (pwm_id < 4) {
+               writel(tcnt, &pwm->tcntb0 + offset);
+               writel(tcmp, &pwm->tcmpb0 + offset);
+       }
+
+       tcon = readl(&pwm->tcon);
+       tcon |= TCON_UPDATE(pwm_id);
+       if (pwm_id < 4)
+               tcon |= TCON_AUTO_RELOAD(pwm_id);
+       else
+               tcon |= TCON4_AUTO_RELOAD;
+       writel(tcon, &pwm->tcon);
+
+       tcon &= ~TCON_UPDATE(pwm_id);
+       writel(tcon, &pwm->tcon);
+
+       return 0;
+}
+
+int pwm_init(int pwm_id, int div, int invert)
+{
+       u32 val;
+       const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
+                       (struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
+                       (struct s5p_timer *)samsung_get_base_timer();
+#endif
+       unsigned long ticks_per_period;
+       unsigned int offset, prescaler;
+
+       /*
+        * Timer Freq(HZ) =
+        *      PWM_CLK / { (prescaler_value + 1) * (divider_value) }
+        */
+
+       val = readl(&pwm->tcfg0);
+       if (pwm_id < 2) {
+               prescaler = PRESCALER_0;
+               val &= ~0xff;
+               val |= (prescaler & 0xff);
+       } else {
+               prescaler = PRESCALER_1;
+               val &= ~(0xff << 8);
+               val |= (prescaler & 0xff) << 8;
+       }
+       writel(val, &pwm->tcfg0);
+       val = readl(&pwm->tcfg1);
+       val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
+       val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
+       writel(val, &pwm->tcfg1);
+
+       if (pwm_id == 4) {
+               /*
+                * TODO(sjg): Use this as a countdown timer for now. We count
+                * down from the maximum value to 0, then reset.
+                */
+               ticks_per_period = -1UL;
+       } else {
+               const unsigned long pwm_hz = 1000;
+#if defined(CONFIG_ARCH_NEXELL)
+               struct clk *clk = clk_get(CORECLK_NAME_PCLK);
+               unsigned long timer_rate_hz = clk_get_rate(clk) /
+#else
+               unsigned long timer_rate_hz = get_pwm_clk() /
+#endif
+                       ((prescaler + 1) * (1 << div));
+
+               ticks_per_period = timer_rate_hz / pwm_hz;
+       }
+
+       /* set count value */
+       offset = pwm_id * 3;
+
+       writel(ticks_per_period, &pwm->tcntb0 + offset);
+
+       val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
+       if (invert && pwm_id < 4)
+               val |= TCON_INVERTER(pwm_id);
+       writel(val, &pwm->tcon);
+
+       nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit,
+                                pwm_dev[pwm_id].pwm_fn);
+       pwm_enable(pwm_id);
+
+       return 0;
+}
diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h
new file mode 100644
index 0000000..92dc707
--- /dev/null
+++ b/drivers/pwm/pwm-nexell.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0+
+ *
+ * Copyright (C) 2009 Samsung Electronics
+ * Kyungmin Park <kyungmin.p...@samsung.com>
+ * Minkyu Kang <mk7.k...@samsung.com>
+ */
+
+#ifndef __ASM_ARM_ARCH_PWM_H_
+#define __ASM_ARM_ARCH_PWM_H_
+
+#define PRESCALER_0            (8 - 1)         /* prescaler of timer 0, 1 */
+#define PRESCALER_1            (16 - 1)        /* prescaler of timer 2, 3, 4 */
+
+/* Divider MUX */
+#define MUX_DIV_1              0               /* 1/1 period */
+#define MUX_DIV_2              1               /* 1/2 period */
+#define MUX_DIV_4              2               /* 1/4 period */
+#define MUX_DIV_8              3               /* 1/8 period */
+#define MUX_DIV_16             4               /* 1/16 period */
+
+#define MUX_DIV_SHIFT(x)       ((x) * 4)
+
+#define TCON_OFFSET(x)         (((x) + 1) * (!!x) << 2)
+
+#define TCON_START(x)          (1 << TCON_OFFSET(x))
+#define TCON_UPDATE(x)         (1 << (TCON_OFFSET(x) + 1))
+#define TCON_INVERTER(x)       (1 << (TCON_OFFSET(x) + 2))
+#define TCON_AUTO_RELOAD(x)    (1 << (TCON_OFFSET(x) + 3))
+#define TCON4_AUTO_RELOAD      (1 << 22)
+
+#ifndef __ASSEMBLY__
+struct s5p_timer {
+       unsigned int    tcfg0;
+       unsigned int    tcfg1;
+       unsigned int    tcon;
+       unsigned int    tcntb0;
+       unsigned int    tcmpb0;
+       unsigned int    tcnto0;
+       unsigned int    tcntb1;
+       unsigned int    tcmpb1;
+       unsigned int    tcnto1;
+       unsigned int    tcntb2;
+       unsigned int    tcmpb2;
+       unsigned int    tcnto2;
+       unsigned int    tcntb3;
+       unsigned int    res1;
+       unsigned int    tcnto3;
+       unsigned int    tcntb4;
+       unsigned int    tcnto4;
+       unsigned int    tintcstat;
+};
+#endif /* __ASSEMBLY__ */
+
+#endif
-- 
1.9.1


Reply via email to