[RFC PATCH 03/10] i2c: mmc: add nexell driver (gpio, i2c, mmc, pwm)
Stefan B.
stefan_b at posteo.net
Tue Feb 4 19:29:43 CET 2020
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 at 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 at 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(®s->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(®s->data, 1 << pin);
>> +Â Â Â else
>> +Â Â Â Â Â Â Â setbits_le32(®s->pad_reset, 1 << pin);
>> +
>> +Â Â Â setbits_le32(®s->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(®s->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(®s->data, 1 << pin);
>> +Â Â Â else
>> +Â Â Â Â Â Â Â clrbits_le32(®s->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(®s->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(®s->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(®s->data, 1 << pin);
>> +Â Â Â else
>> +Â Â Â Â Â Â Â clrbits_le32(®s->data, 1 << pin);
>> +
>> +Â Â Â setbits_le32(®s->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(®s->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(®s->data, 1 << pin);
>> +Â Â Â else
>> +Â Â Â Â Â Â Â clrbits_le32(®s->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(®s->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 at vger.kernel.org <devicetree at 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 at nexell.co.kr>
>> + *
>> + * (C) Copyright 2019 Stefan Bosch <stefan_b at 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 at 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 at samsung.com>
>> + * Minkyu Kang <mk7.kang at 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
>>
>
More information about the U-Boot
mailing list