diff mbox series

[RFC,03/10] i2c: mmc: add nexell driver (gpio, i2c, mmc, pwm)

Message ID 1580762412-6260-1-git-send-email-stefan_b@posteo.net
State RFC
Delegated to: Tom Rini
Headers show
Series arm: add support for SoC S5P4418 | expand

Commit Message

Stefan Bosch Feb. 3, 2020, 8:40 p.m. UTC
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 <stefan_b@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

Comments

Heiko Schocher Feb. 4, 2020, 6:58 a.m. UTC | #1
Hello Stefan,

Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
> 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 <stefan_b@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 +++++

Could you please split this patch into 4 parts (i2c, gpio, mmc and
pwm) ?

Thanks!

>   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/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

Please keep lists sorted.

> 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 <truevirtue@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

Is this really configurable? If so, I do not find the Kconfig
description.

> +
> +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;
> +	}
> +}

Hmm... may this should be moved into a seperate pincontrol driver?

> +
> +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);

Where is DEV_NAME_I2C defined ?

> +	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;

return -ENODEV;

Hmm... is there no chance to use seq from struct udevice

https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152

?

For example like:
https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895

> +}
> +
> +/* 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);

Do you want to allow all values of speeds or may you want to use
standard speeds, see:

https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33

> +
> +	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);

You introdue here new properties, please document them in
u-boot:/doc/device-tree-bindings/i2c

Please without "nexell,"

Do you plan to post also a linux i2c driver?

If so, the devicetree bindings should be discussed there to avoid
different properties between U-Boot and linux!

> +}
> +
> +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;

please return -ENODEV

> +	}
> +
> +	/* i2c optional node parsing */
> +	i2c_process_node(dev);
> +	if (!bus->target_speed)
> +		return -1;

please return here also an errorcode from include/linux/errno.h

Hmm.. if you return here if target_speed is not set, it is not optional!

> +
> +	/* 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" },

Same here as for the new properties. Please discuss the names on

devicetree@vger.kernel.org <devicetree@vger.kernel.org>

first!

> +	{ }
> +};
> +
> +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,
> +};

bye,
Heiko
> 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 <park@nexell.co.kr>
> + *
> + * (C) Copyright 2019 Stefan Bosch <stefan_b@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.lee@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.park@samsung.com>
> + * Minkyu Kang <mk7.kang@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
>
Stefan Bosch Feb. 4, 2020, 6:29 p.m. UTC | #2
Hello Heiko,

thanks a lot for your annotations and suggestions. I will have a look at 
them and give you feedback ASAP.


Regards
Stefan


Am 04.02.20 um 07:58 schrieb Heiko Schocher:
> Hello Stefan,
>
> Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
>> 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 <stefan_b@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 +++++
>
> Could you please split this patch into 4 parts (i2c, gpio, mmc and
> pwm) ?
>
> Thanks!
>
>>   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/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
>
> Please keep lists sorted.
>
>> 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 <truevirtue@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
>
> Is this really configurable? If so, I do not find the Kconfig
> description.
>
>> +
>> +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;
>> +    }
>> +}
>
> Hmm... may this should be moved into a seperate pincontrol driver?
>
>> +
>> +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);
>
> Where is DEV_NAME_I2C defined ?
>
>> +    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;
>
> return -ENODEV;
>
> Hmm... is there no chance to use seq from struct udevice
>
> https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
>
> ?
>
> For example like:
> https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895 
>
>
>> +}
>> +
>> +/* 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);
>
> Do you want to allow all values of speeds or may you want to use
> standard speeds, see:
>
> https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
>
>> +
>> +    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);
>
> You introdue here new properties, please document them in
> u-boot:/doc/device-tree-bindings/i2c
>
> Please without "nexell,"
>
> Do you plan to post also a linux i2c driver?
>
> If so, the devicetree bindings should be discussed there to avoid
> different properties between U-Boot and linux!
>
>> +}
>> +
>> +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;
>
> please return -ENODEV
>
>> +    }
>> +
>> +    /* i2c optional node parsing */
>> +    i2c_process_node(dev);
>> +    if (!bus->target_speed)
>> +        return -1;
>
> please return here also an errorcode from include/linux/errno.h
>
> Hmm.. if you return here if target_speed is not set, it is not optional!
>
>> +
>> +    /* 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" },
>
> Same here as for the new properties. Please discuss the names on
>
> devicetree@vger.kernel.org <devicetree@vger.kernel.org>
>
> first!
>
>> +    { }
>> +};
>> +
>> +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,
>> +};
>
> bye,
> Heiko
>> 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 <park@nexell.co.kr>
>> + *
>> + * (C) Copyright 2019 Stefan Bosch <stefan_b@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.lee@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.park@samsung.com>
>> + * Minkyu Kang <mk7.kang@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
>>
>
Stefan Bosch Feb. 20, 2020, 5:49 p.m. UTC | #3
Hello Heiko,

see below my feedback, please give me further advise where indicated.

Unfortunately there have been some Bugs in the i2c-driver and I learned 
that this driver has not been used at all ("i2c-gpio" has been used 
instead). So I have done several Bugfixes and improvements appart from 
your proposals.


Regards
Stefan


Am 04.02.20 um 07:58 schrieb Heiko Schocher:
> Hello Stefan,
> 
> Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
>> 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 <stefan_b@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 +++++
> 
> Could you please split this patch into 4 parts (i2c, gpio, mmc and
> pwm) ?
> 
> Thanks!
> 
Ok, I will split this patch.

>>   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/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
> 
> Please keep lists sorted.

The list is not sorted (at least in no alphabetical order), but I can 
e.g. move "... += nx_gpio.o" one line up?

> 
>> 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 <truevirtue@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
> 
> Is this really configurable? If so, I do not find the Kconfig
> description.

No, it is not configurable. I have changed it to MAX_I2C_NUM.

> 
>> +
>> +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;
>> +    }
>> +}
> 
> Hmm... may this should be moved into a seperate pincontrol driver?

According to the above FIXME comment from Nexell it probably should. But 
there is no pincontrol driver implemented. But is the change to a driver 
necessary for now?

> 
>> +
>> +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);
> 
> Where is DEV_NAME_I2C defined ?

DEV_NAME_I2C is defined in arch/arm/mach-nexell/include/mach/nexell.h

> 
>> +    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;
> 
> return -ENODEV;
> 
> Hmm... is there no chance to use seq from struct udevice
> 
> https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
> 
> ?
> 
> For example like:
> https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895
> 

Ok, I have changed this as proposed.

>> +}
>> +
>> +/* 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);
> 
> Do you want to allow all values of speeds or may you want to use
> standard speeds, see:
> 
> https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
> 

I'd like to allow all values of speed. In my opinion allowing only 
standard speeds does complicate things (e.g. how to do error handling?). 
Furthermore I think sometimes it could be handy to be able to set speed 
to an arbitrary value (e.g. to a lower value than 100000) when trying a 
new i2c-device on the bus.

>> +
>> +    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);
> 
> You introdue here new properties, please document them in
> u-boot:/doc/device-tree-bindings/i2c
> 
> Please without "nexell,"
> 

I have changed "nexell,i2c-max-bus-freq" to the already defined 
"clock-frequency". Furthermore I have changed "nexell,i2c-sda-delay" to 
"i2c-sda-delay-ns".
Furthermore, I have added "nx_i2c.txt" in doc/device-tree-bindings/i2c.

> Do you plan to post also a linux i2c driver?
> 
> If so, the devicetree bindings should be discussed there to avoid
> different properties between U-Boot and linux!
> 

No, I do not plan to post a linux driver.

>> +}
>> +
>> +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;
> 
> please return -ENODEV
> 

Ok

>> +    }
>> +
>> +    /* i2c optional node parsing */
>> +    i2c_process_node(dev);
>> +    if (!bus->target_speed)
>> +        return -1;
> 
> please return here also an errorcode from include/linux/errno.h
> 

Ok

> Hmm.. if you return here if target_speed is not set, it is not optional!
> 

You are right, I have removed 'optional' in the comment.

>> +
>> +    /* 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" },
> 
> Same here as for the new properties. Please discuss the names on
> 
> devicetree@vger.kernel.org <devicetree@vger.kernel.org>
> 
> first!
> 
>> +    { }
>> +};
>> +
>> +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,
>> +};
> 
> bye,
> Heiko
>> 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 <park@nexell.co.kr>
>> + *
>> + * (C) Copyright 2019 Stefan Bosch <stefan_b@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.lee@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.park@samsung.com>
>> + * Minkyu Kang <mk7.kang@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
>>
>
Heiko Schocher Feb. 22, 2020, 12:34 p.m. UTC | #4
Hello Stefan,

Am 20.02.2020 um 18:49 schrieb Stefan B.:
> Hello Heiko,
> 
> see below my feedback, please give me further advise where indicated.
> 
> Unfortunately there have been some Bugs in the i2c-driver and I learned that this driver has not 
> been used at all ("i2c-gpio" has been used instead). So I have done several Bugfixes and 
> improvements appart from your proposals.
> 
> 
> Regards
> Stefan
> 
> 
> Am 04.02.20 um 07:58 schrieb Heiko Schocher:
>> Hello Stefan,
>>
>> Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
>>> 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 <stefan_b@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 +++++
>>
>> Could you please split this patch into 4 parts (i2c, gpio, mmc and
>> pwm) ?
>>
>> Thanks!
>>
> Ok, I will split this patch.

Thanks!

>>>   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/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
>>
>> Please keep lists sorted.
> 
> The list is not sorted (at least in no alphabetical order), but I can e.g. move "... += nx_gpio.o" 
> one line up?

Find for me.

>>> 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 <truevirtue@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
>>
>> Is this really configurable? If so, I do not find the Kconfig
>> description.
> 
> No, it is not configurable. I have changed it to MAX_I2C_NUM.

Ok.

>>> +
>>> +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;
>>> +    }
>>> +}
>>
>> Hmm... may this should be moved into a seperate pincontrol driver?
> 
> According to the above FIXME comment from Nexell it probably should. But there is no pincontrol 
> driver implemented. But is the change to a driver necessary for now?

Hmm.. it would be better to have a pinctrl driver, but I can accept it
for now.

>>> +
>>> +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);
>>
>> Where is DEV_NAME_I2C defined ?
> 
> DEV_NAME_I2C is defined in arch/arm/mach-nexell/include/mach/nexell.h

Ah, ok.

> 
>>
>>> +    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;
>>
>> return -ENODEV;
>>
>> Hmm... is there no chance to use seq from struct udevice
>>
>> https://gitlab.denx.de/u-boot/u-boot/blob/master/include/dm/device.h#L152
>>
>> ?
>>
>> For example like:
>> https://gitlab.denx.de/u-boot/u-boot/blob/master/drivers/i2c/mxc_i2c.c#L895
>>
> 
> Ok, I have changed this as proposed.

Thanks!

>>> +}
>>> +
>>> +/* 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);
>>
>> Do you want to allow all values of speeds or may you want to use
>> standard speeds, see:
>>
>> https://gitlab.denx.de/u-boot/u-boot/blob/master/include/i2c.h#L33
>>
> 
> I'd like to allow all values of speed. In my opinion allowing only standard speeds does complicate 
> things (e.g. how to do error handling?). Furthermore I think sometimes it could be handy to be able 
> to set speed to an arbitrary value (e.g. to a lower value than 100000) when trying a new i2c-device 
> on the bus.

ok.

> 
>>> +
>>> +    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);
>>
>> You introdue here new properties, please document them in
>> u-boot:/doc/device-tree-bindings/i2c
>>
>> Please without "nexell,"
>>
> 
> I have changed "nexell,i2c-max-bus-freq" to the already defined "clock-frequency". Furthermore I 
> have changed "nexell,i2c-sda-delay" to "i2c-sda-delay-ns".
> Furthermore, I have added "nx_i2c.txt" in doc/device-tree-bindings/i2c.

Thanks!

> 
>> Do you plan to post also a linux i2c driver?
>>
>> If so, the devicetree bindings should be discussed there to avoid
>> different properties between U-Boot and linux!
>>
> 
> No, I do not plan to post a linux driver.

Ok.

> 
>>> +}
>>> +
>>> +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;
>>
>> please return -ENODEV
>>
> 
> Ok
> 
>>> +    }
>>> +
>>> +    /* i2c optional node parsing */
>>> +    i2c_process_node(dev);
>>> +    if (!bus->target_speed)
>>> +        return -1;
>>
>> please return here also an errorcode from include/linux/errno.h
>>
> 
> Ok
> 
>> Hmm.. if you return here if target_speed is not set, it is not optional!
>>
> 
> You are right, I have removed 'optional' in the comment.

Thanks.

> 
>>> +
>>> +    /* 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" },
>>
>> Same here as for the new properties. Please discuss the names on
>>
>> devicetree@vger.kernel.org <devicetree@vger.kernel.org>
>>
>> first!
>>
>>> +    { }
>>> +};
>>> +
>>> +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,
>>> +};
>>
>> bye,
>> Heiko
>>> 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 <park@nexell.co.kr>
>>> + *
>>> + * (C) Copyright 2019 Stefan Bosch <stefan_b@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.lee@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.park@samsung.com>
>>> + * Minkyu Kang <mk7.kang@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
>>>
>>
> 

bye,
Heiko
diff mbox series

Patch

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 <truevirtue@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 <park@nexell.co.kr>
+ *
+ * (C) Copyright 2019 Stefan Bosch <stefan_b@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.lee@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.park@samsung.com>
+ * Minkyu Kang <mk7.kang@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