Message ID | 1580762412-6260-1-git-send-email-stefan_b@posteo.net |
---|---|
State | New |
Headers | show |
Series | arm: add support for SoC S5P4418 | expand |
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 >
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 >> >
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 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! > 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 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. 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 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 >> >
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 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! >> > 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 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. > > 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 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 >>> >> > bye, Heiko
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 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 + +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 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
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 +++++ 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