
Hello Stefan,
Am 03.02.2020 um 21:40 schrieb Stefan Bosch:
Changes in relation to FriendlyARM's U-Boot nanopi2-v2016.01:
- i2c/nx_i2c.c: Some adaptions mainly because of changes in "struct udevice".
- mmc: nexell_dw_mmc.c changed to nexell_dw_mmc_dm.c (switched to DM).
Signed-off-by: Stefan Bosch stefan_b@posteo.net
drivers/gpio/Kconfig | 9 + drivers/gpio/Makefile | 1 + drivers/gpio/nx_gpio.c | 252 +++++++++++++++++++ drivers/i2c/Kconfig | 9 + drivers/i2c/Makefile | 1 + drivers/i2c/nx_i2c.c | 537 +++++++++++++++++++++++++++++++++++++++++ drivers/mmc/Kconfig | 6 + drivers/mmc/Makefile | 1 + drivers/mmc/nexell_dw_mmc_dm.c | 350 +++++++++++++++++++++++++++ drivers/pwm/Makefile | 1 + drivers/pwm/pwm-nexell.c | 252 +++++++++++++++++++ drivers/pwm/pwm-nexell.h | 54 +++++
Could you please split this patch into 4 parts (i2c, gpio, mmc and pwm) ?
Thanks!
12 files changed, 1473 insertions(+) create mode 100644 drivers/gpio/nx_gpio.c create mode 100644 drivers/i2c/nx_i2c.c create mode 100644 drivers/mmc/nexell_dw_mmc_dm.c create mode 100644 drivers/pwm/pwm-nexell.c create mode 100644 drivers/pwm/pwm-nexell.h
[...]
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 449046b..e3340de 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -65,3 +65,4 @@ obj-$(CONFIG_PM8916_GPIO) += pm8916_gpio.o obj-$(CONFIG_MT7621_GPIO) += mt7621_gpio.o obj-$(CONFIG_MSCC_SGPIO) += mscc_sgpio.o obj-$(CONFIG_SIFIVE_GPIO) += sifive-gpio.o +obj-$(CONFIG_NX_GPIO) += nx_gpio.o
Please keep lists sorted.
diff --git a/drivers/gpio/nx_gpio.c b/drivers/gpio/nx_gpio.c new file mode 100644 index 0000000..86472f6 --- /dev/null +++ b/drivers/gpio/nx_gpio.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- DeokJin, Lee truevirtue@nexell.co.kr
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <malloc.h> +#include <fdtdec.h> +#include <asm/io.h> +#include <asm/gpio.h>
+DECLARE_GLOBAL_DATA_PTR;
+struct nx_gpio_regs {
- u32 data; /* Data register */
- u32 outputenb; /* Output Enable register */
- u32 detmode[2]; /* Detect Mode Register */
- u32 intenb; /* Interrupt Enable Register */
- u32 det; /* Event Detect Register */
- u32 pad; /* Pad Status Register */
+};
+struct nx_alive_gpio_regs {
- u32 pwrgate; /* Power Gating Register */
- u32 reserved0[28]; /* Reserved0 */
- u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */
- u32 outputenb; /* Alive GPIO Output Enable Register */
- u32 outputenb_read; /* Alive GPIO Output Read Register */
- u32 reserved1[3]; /* Reserved1 */
- u32 pad_reset; /* Alive GPIO Output Reset Register */
- u32 data; /* Alive GPIO Output Register */
- u32 pad_read; /* Alive GPIO Pad Read Register */
- u32 reserved2[33]; /* Reserved2 */
- u32 pad; /* Alive GPIO Input Value Register */
+};
+struct nx_gpio_platdata {
- void *regs;
- int gpio_count;
- const char *bank_name;
+};
+static int nx_alive_gpio_is_check(struct udevice *dev) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- const char *bank_name = plat->bank_name;
- if (!strcmp(bank_name, "gpio_alv"))
return 1;
- return 0;
+}
+static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned int pin) +{
- struct nx_gpio_platdata *plat = dev_get_platdata(dev);
- struct nx_alive_gpio_regs *const regs = plat->regs;
- setbits_le32(®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@vger.kernel.org devicetree@vger.kernel.org
first!
- { }
+};
+U_BOOT_DRIVER(i2c_nexell) = {
- .name = "i2c_nexell",
- .id = UCLASS_I2C,
- .of_match = nx_i2c_ids,
- .probe = nx_i2c_probe,
- .priv_auto_alloc_size = sizeof(struct nx_i2c_bus),
- .ops = &nx_i2c_ops,
+};
bye, Heiko
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb8e7c0 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -253,6 +253,12 @@ config MMC_DW_SNPS This selects support for Synopsys DesignWare Memory Card Interface driver extensions used in various Synopsys ARC devboards.
+config NEXELL_DWMMC
- bool "Nexell SD/MMC controller support"
- depends on ARCH_NEXELL
- depends on MMC_DW
- default y
- config MMC_MESON_GX bool "Meson GX EMMC controller support" depends on DM_MMC && BLK && ARCH_MESON
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..a7b5a7b 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o obj-$(CONFIG_SH_SDHI) += sh_sdhi.o obj-$(CONFIG_STM32_SDMMC2) += stm32_sdmmc2.o obj-$(CONFIG_JZ47XX_MMC) += jz_mmc.o +obj-$(CONFIG_NEXELL_DWMMC) += nexell_dw_mmc_dm.o
# SDHCI obj-$(CONFIG_MMC_SDHCI) += sdhci.o diff --git a/drivers/mmc/nexell_dw_mmc_dm.c b/drivers/mmc/nexell_dw_mmc_dm.c new file mode 100644 index 0000000..b06b60d --- /dev/null +++ b/drivers/mmc/nexell_dw_mmc_dm.c @@ -0,0 +1,350 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2016 Nexell
- Youngbok, Park park@nexell.co.kr
- (C) Copyright 2019 Stefan Bosch stefan_b@posteo.net
- */
+#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <dwmmc.h> +#include <syscon.h> +#include <asm/gpio.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/reset.h>
+#define DWMCI_CLKSEL 0x09C +#define DWMCI_SHIFT_0 0x0 +#define DWMCI_SHIFT_1 0x1 +#define DWMCI_SHIFT_2 0x2 +#define DWMCI_SHIFT_3 0x3 +#define DWMCI_SET_SAMPLE_CLK(x) (x) +#define DWMCI_SET_DRV_CLK(x) ((x) << 16) +#define DWMCI_SET_DIV_RATIO(x) ((x) << 24) +#define DWMCI_CLKCTRL 0x114 +#define NX_MMC_CLK_DELAY(x, y, a, b) ((((x) & 0xFF) << 0) |\
(((y) & 0x03) << 16) |\
(((a) & 0xFF) << 8) |\
(((b) & 0x03) << 24))
+struct nexell_mmc_plat {
- struct mmc_config cfg;
- struct mmc mmc;
+};
+struct nexell_dwmmc_priv {
- struct clk *clk;
- struct dwmci_host host;
- int fifo_size;
- bool fifo_mode;
- int frequency;
- u32 min_freq;
- u32 max_freq;
- int d_delay;
- int d_shift;
- int s_delay;
- int s_shift;
+};
+struct clk *clk_get(const char *id);
+static void set_pin_stat(int index, int bit, int value) +{ +#if !defined(CONFIG_SPL_BUILD)
- nx_gpio_set_pad_function(index, bit, value);
+#else +#if defined(CONFIG_ARCH_S5P4418) || \
- defined(CONFIG_ARCH_S5P6818)
- unsigned long base[5] = {
PHY_BASEADDR_GPIOA, PHY_BASEADDR_GPIOB,
PHY_BASEADDR_GPIOC, PHY_BASEADDR_GPIOD,
PHY_BASEADDR_GPIOE,
- };
- dw_mmc_set_pin(base[index], bit, value);
+#endif +#endif +}
+static void nx_dw_mmc_set_pin(struct dwmci_host *host) +{
- debug(" %s(): dev_index == %d", __func__, host->dev_index);
- switch (host->dev_index) {
- case 0:
set_pin_stat(0, 29, 1);
set_pin_stat(0, 31, 1);
set_pin_stat(1, 1, 1);
set_pin_stat(1, 3, 1);
set_pin_stat(1, 5, 1);
set_pin_stat(1, 7, 1);
break;
- case 1:
set_pin_stat(3, 22, 1);
set_pin_stat(3, 23, 1);
set_pin_stat(3, 24, 1);
set_pin_stat(3, 25, 1);
set_pin_stat(3, 26, 1);
set_pin_stat(3, 27, 1);
break;
- case 2:
set_pin_stat(2, 18, 2);
set_pin_stat(2, 19, 2);
set_pin_stat(2, 20, 2);
set_pin_stat(2, 21, 2);
set_pin_stat(2, 22, 2);
set_pin_stat(2, 23, 2);
if (host->buswidth == 8) {
set_pin_stat(4, 21, 2);
set_pin_stat(4, 22, 2);
set_pin_stat(4, 23, 2);
set_pin_stat(4, 24, 2);
}
break;
- default:
debug(" is invalid!");
- }
- debug("\n");
+}
+static void nx_dw_mmc_clksel(struct dwmci_host *host) +{
- u32 val;
+#ifdef CONFIG_BOOST_MMC
- val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(1);
+#else
- val = DWMCI_SET_SAMPLE_CLK(DWMCI_SHIFT_0) |
DWMCI_SET_DRV_CLK(DWMCI_SHIFT_0) | DWMCI_SET_DIV_RATIO(3);
+#endif
- dwmci_writel(host, DWMCI_CLKSEL, val);
+}
+static void nx_dw_mmc_reset(int ch) +{
- int rst_id = RESET_ID_SDMMC0 + ch;
- nx_rstcon_setrst(rst_id, 0);
- nx_rstcon_setrst(rst_id, 1);
+}
+static void nx_dw_mmc_clk_delay(struct udevice *dev) +{
- unsigned int delay;
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- delay = NX_MMC_CLK_DELAY(priv->d_delay,
priv->d_shift, priv->s_delay, priv->s_shift);
- writel(delay, (host->ioaddr + DWMCI_CLKCTRL));
- debug("%s(): Values set: d_delay==%d, d_shift==%d, s_delay==%d, "
"s_shift==%d\n", __func__, priv->d_delay, priv->d_shift,
priv->s_delay, priv->s_shift);
+}
+static unsigned int nx_dw_mmc_get_clk(struct dwmci_host *host, uint freq) +{
- struct clk *clk;
- struct udevice *dev = host->priv;
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- int index = host->dev_index;
- char name[50] = { 0, };
- clk = priv->clk;
- if (!clk) {
sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
clk = clk_get((const char *)name);
if (!clk)
return 0;
priv->clk = clk;
- }
- return clk_get_rate(clk) / 2;
+}
+static unsigned long nx_dw_mmc_set_clk(struct dwmci_host *host,
unsigned int rate)
+{
- struct clk *clk;
- char name[50] = { 0, };
- struct udevice *dev = host->priv;
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- int index = host->dev_index;
- clk = priv->clk;
- if (!clk) {
sprintf(name, "%s.%d", DEV_NAME_SDHC, index);
clk = clk_get((const char *)name);
if (!clk)
return 0;
priv->clk = clk;
- }
- clk_disable(clk);
- rate = clk_set_rate(clk, rate);
- clk_enable(clk);
- return rate;
+}
+static int nexell_dwmmc_ofdata_to_platdata(struct udevice *dev) +{
- /* if (dev): *priv = dev->priv, else: *priv = NULL */
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- int val = -1;
- debug("%s()\n", __func__);
- host->name = dev->name;
- host->ioaddr = dev_read_addr_ptr(dev);
- val = dev_read_u32_default(dev, "nexell,bus-width", -1);
- if (val < 0) {
debug(" 'nexell,bus-width' missing/invalid!\n");
return -EINVAL;
- }
- host->buswidth = val;
- host->get_mmc_clk = nx_dw_mmc_get_clk;
- host->clksel = nx_dw_mmc_clksel;
- host->priv = dev;
- val = dev_read_u32_default(dev, "index", -1);
- if (val < 0) {
debug(" 'index' missing/invalid!\n");
return -EINVAL;
- }
- host->dev_index = val;
- val = dev_read_u32_default(dev, "fifo-size", 0x20);
- if (val <= 0) {
debug(" 'fifo-size' missing/invalid!\n");
return -EINVAL;
- }
- priv->fifo_size = val;
- priv->fifo_mode = dev_read_bool(dev, "fifo-mode");
- val = dev_read_u32_default(dev, "frequency", -1);
- if (val < 0) {
debug(" 'frequency' missing/invalid!\n");
return -EINVAL;
- }
- priv->frequency = val;
- val = dev_read_u32_default(dev, "max-frequency", -1);
- if (val < 0) {
debug(" 'max-frequency' missing/invalid!\n");
return -EINVAL;
- }
- priv->max_freq = val;
- priv->min_freq = 400000; /* 400 kHz */
- val = dev_read_u32_default(dev, "nexell,drive_dly", -1);
- if (val < 0) {
debug(" 'nexell,drive_dly' missing/invalid!\n");
return -EINVAL;
- }
- priv->d_delay = val;
- val = dev_read_u32_default(dev, "nexell,drive_shift", -1);
- if (val < 0) {
debug(" 'nexell,drive_shift' missing/invalid!\n");
return -EINVAL;
- }
- priv->d_shift = val;
- val = dev_read_u32_default(dev, "nexell,sample_dly", -1);
- if (val < 0) {
debug(" 'nexell,sample_dly' missing/invalid!\n");
return -EINVAL;
- }
- priv->s_delay = val;
- val = dev_read_u32_default(dev, "nexell,sample_shift", -1);
- if (val < 0) {
debug(" 'nexell,sample_shift' missing/invalid!\n");
return -EINVAL;
- }
- priv->s_shift = val;
- debug(" index==%d, name==%s, ioaddr==0x%08x, buswidth==%d, "
"fifo_size==%d, fifo_mode==%d, frequency==%d\n",
host->dev_index, host->name, (u32)host->ioaddr,
host->buswidth, priv->fifo_size, priv->fifo_mode,
priv->frequency);
- debug(" min_freq==%d, max_freq==%d, delay: "
"0x%02x:0x%02x:0x%02x:0x%02x\n",
priv->min_freq, priv->max_freq, priv->d_delay,
priv->d_shift, priv->s_delay, priv->s_shift);
- return 0;
+}
+static int nexell_dwmmc_probe(struct udevice *dev) +{
- struct nexell_mmc_plat *plat = dev_get_platdata(dev);
- struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
- struct nexell_dwmmc_priv *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- struct udevice *pwr_dev __maybe_unused;
- debug("%s():\n", __func__);
- host->fifoth_val = MSIZE(0x2) |
RX_WMARK(priv->fifo_size / 2 - 1) |
TX_WMARK(priv->fifo_size / 2);
- host->fifo_mode = priv->fifo_mode;
- dwmci_setup_cfg(&plat->cfg, host, priv->max_freq, priv->min_freq);
- host->mmc = &plat->mmc;
- host->mmc->priv = &priv->host;
- host->mmc->dev = dev;
- upriv->mmc = host->mmc;
- nx_dw_mmc_set_pin(host);
- debug(" nx_dw_mmc_set_clk(host, frequency * 4 == %d)\n",
priv->frequency * 4);
- nx_dw_mmc_set_clk(host, priv->frequency * 4);
- nx_dw_mmc_reset(host->dev_index);
- nx_dw_mmc_clk_delay(dev);
- return dwmci_probe(dev);
+}
+static int nexell_dwmmc_bind(struct udevice *dev) +{
- struct nexell_mmc_plat *plat = dev_get_platdata(dev);
- return dwmci_bind(dev, &plat->mmc, &plat->cfg);
+}
+static const struct udevice_id nexell_dwmmc_ids[] = {
- { .compatible = "nexell,nexell-dwmmc" },
- { }
+};
+U_BOOT_DRIVER(nexell_dwmmc_drv) = {
- .name = "nexell_dwmmc",
- .id = UCLASS_MMC,
- .of_match = nexell_dwmmc_ids,
- .ofdata_to_platdata = nexell_dwmmc_ofdata_to_platdata,
- .ops = &dm_dwmci_ops,
- .bind = nexell_dwmmc_bind,
- .probe = nexell_dwmmc_probe,
- .priv_auto_alloc_size = sizeof(struct nexell_dwmmc_priv),
- .platdata_auto_alloc_size = sizeof(struct nexell_mmc_plat),
+}; diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index a837c35..b45aada 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -16,3 +16,4 @@ obj-$(CONFIG_PWM_ROCKCHIP) += rk_pwm.o obj-$(CONFIG_PWM_SANDBOX) += sandbox_pwm.o obj-$(CONFIG_PWM_TEGRA) += tegra_pwm.o obj-$(CONFIG_PWM_SUNXI) += sunxi_pwm.o +obj-$(CONFIG_PWM_NX) += pwm-nexell.o diff --git a/drivers/pwm/pwm-nexell.c b/drivers/pwm/pwm-nexell.c new file mode 100644 index 0000000..6c0f8f4 --- /dev/null +++ b/drivers/pwm/pwm-nexell.c @@ -0,0 +1,252 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- Copyright (C) 2011 Samsung Electronics
- Donghwa Lee dh09.lee@samsung.com
- */
+/* This codes are copied from arch/arm/cpu/armv7/s5p-common/pwm.c */
+#include <common.h> +#include <errno.h> +#include <pwm.h> +#include <asm/io.h> +#include <asm/arch/clk.h> +#include "pwm-nexell.h"
+#if defined(CONFIG_ARCH_NEXELL) +#include <asm/arch/nexell.h> +#include <asm/arch/reset.h> +#include <asm/arch/nx_gpio.h> +#include <asm/arch/tieoff.h>
+struct pwm_device {
- int ch;
- int grp;
- int bit;
- int pwm_fn;
+};
+static struct pwm_device pwm_dev[] = {
- [0] = { .ch = 0, .grp = 3, .bit = 1, .pwm_fn = 1 },
- [1] = { .ch = 1, .grp = 2, .bit = 13, .pwm_fn = 2 },
- [2] = { .ch = 2, .grp = 2, .bit = 14, .pwm_fn = 2 },
- [3] = { .ch = 3, .grp = 3, .bit = 0, .pwm_fn = 2 },
+}; +#endif
+int pwm_enable(int pwm_id) +{
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned long tcon;
- tcon = readl(&pwm->tcon);
- tcon |= TCON_START(pwm_id);
- writel(tcon, &pwm->tcon);
- return 0;
+}
+void pwm_disable(int pwm_id) +{
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned long tcon;
- tcon = readl(&pwm->tcon);
- tcon &= ~TCON_START(pwm_id);
- writel(tcon, &pwm->tcon);
+}
+static unsigned long pwm_calc_tin(int pwm_id, unsigned long freq) +{
- unsigned long tin_parent_rate;
- unsigned int div;
+#if defined(CONFIG_ARCH_NEXELL)
- unsigned int pre_div;
- const struct s5p_timer *pwm =
(struct s5p_timer *)PHY_BASEADDR_PWM;
- unsigned int val;
+#endif
+#if defined(CONFIG_ARCH_NEXELL)
- struct clk *clk = clk_get(CORECLK_NAME_PCLK);
- tin_parent_rate = clk_get_rate(clk);
+#else
- tin_parent_rate = get_pwm_clk();
+#endif
+#if defined(CONFIG_ARCH_NEXELL)
- writel(0, &pwm->tcfg0);
- val = readl(&pwm->tcfg0);
- if (pwm_id < 2)
div = ((val >> 0) & 0xff) + 1;
- else
div = ((val >> 8) & 0xff) + 1;
- writel(0, &pwm->tcfg1);
- val = readl(&pwm->tcfg1);
- val = (val >> MUX_DIV_SHIFT(pwm_id)) & 0xF;
- pre_div = (1UL << val);
- freq = tin_parent_rate / div / pre_div;
- return freq;
+#else
- for (div = 2; div <= 16; div *= 2) {
if ((tin_parent_rate / (div << 16)) < freq)
return tin_parent_rate / div;
- }
- return tin_parent_rate / 16;
+#endif +}
+#define NS_IN_SEC 1000000000UL
+int pwm_config(int pwm_id, int duty_ns, int period_ns) +{
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned int offset;
- unsigned long tin_rate;
- unsigned long tin_ns;
- unsigned long frequency;
- unsigned long tcon;
- unsigned long tcnt;
- unsigned long tcmp;
- /*
* We currently avoid using 64bit arithmetic by using the
* fact that anything faster than 1GHz is easily representable
* by 32bits.
*/
- if (period_ns > NS_IN_SEC || duty_ns > NS_IN_SEC || period_ns == 0)
return -ERANGE;
- if (duty_ns > period_ns)
return -EINVAL;
- frequency = NS_IN_SEC / period_ns;
- /* Check to see if we are changing the clock rate of the PWM */
- tin_rate = pwm_calc_tin(pwm_id, frequency);
- tin_ns = NS_IN_SEC / tin_rate;
+#if defined(CONFIG_ARCH_NEXELL)
- /* The counter starts at zero. */
- tcnt = (period_ns / tin_ns) - 1;
+#else
- tcnt = period_ns / tin_ns;
+#endif
- /* Note, counters count down */
- tcmp = duty_ns / tin_ns;
- tcmp = tcnt - tcmp;
- /* Update the PWM register block. */
- offset = pwm_id * 3;
- if (pwm_id < 4) {
writel(tcnt, &pwm->tcntb0 + offset);
writel(tcmp, &pwm->tcmpb0 + offset);
- }
- tcon = readl(&pwm->tcon);
- tcon |= TCON_UPDATE(pwm_id);
- if (pwm_id < 4)
tcon |= TCON_AUTO_RELOAD(pwm_id);
- else
tcon |= TCON4_AUTO_RELOAD;
- writel(tcon, &pwm->tcon);
- tcon &= ~TCON_UPDATE(pwm_id);
- writel(tcon, &pwm->tcon);
- return 0;
+}
+int pwm_init(int pwm_id, int div, int invert) +{
- u32 val;
- const struct s5p_timer *pwm =
+#if defined(CONFIG_ARCH_NEXELL)
(struct s5p_timer *)PHY_BASEADDR_PWM;
+#else
(struct s5p_timer *)samsung_get_base_timer();
+#endif
- unsigned long ticks_per_period;
- unsigned int offset, prescaler;
- /*
* Timer Freq(HZ) =
* PWM_CLK / { (prescaler_value + 1) * (divider_value) }
*/
- val = readl(&pwm->tcfg0);
- if (pwm_id < 2) {
prescaler = PRESCALER_0;
val &= ~0xff;
val |= (prescaler & 0xff);
- } else {
prescaler = PRESCALER_1;
val &= ~(0xff << 8);
val |= (prescaler & 0xff) << 8;
- }
- writel(val, &pwm->tcfg0);
- val = readl(&pwm->tcfg1);
- val &= ~(0xf << MUX_DIV_SHIFT(pwm_id));
- val |= (div & 0xf) << MUX_DIV_SHIFT(pwm_id);
- writel(val, &pwm->tcfg1);
- if (pwm_id == 4) {
/*
* TODO(sjg): Use this as a countdown timer for now. We count
* down from the maximum value to 0, then reset.
*/
ticks_per_period = -1UL;
- } else {
const unsigned long pwm_hz = 1000;
+#if defined(CONFIG_ARCH_NEXELL)
struct clk *clk = clk_get(CORECLK_NAME_PCLK);
unsigned long timer_rate_hz = clk_get_rate(clk) /
+#else
unsigned long timer_rate_hz = get_pwm_clk() /
+#endif
((prescaler + 1) * (1 << div));
ticks_per_period = timer_rate_hz / pwm_hz;
- }
- /* set count value */
- offset = pwm_id * 3;
- writel(ticks_per_period, &pwm->tcntb0 + offset);
- val = readl(&pwm->tcon) & ~(0xf << TCON_OFFSET(pwm_id));
- if (invert && pwm_id < 4)
val |= TCON_INVERTER(pwm_id);
- writel(val, &pwm->tcon);
- nx_gpio_set_pad_function(pwm_dev[pwm_id].grp, pwm_dev[pwm_id].bit,
pwm_dev[pwm_id].pwm_fn);
- pwm_enable(pwm_id);
- return 0;
+} diff --git a/drivers/pwm/pwm-nexell.h b/drivers/pwm/pwm-nexell.h new file mode 100644 index 0000000..92dc707 --- /dev/null +++ b/drivers/pwm/pwm-nexell.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0+
- Copyright (C) 2009 Samsung Electronics
- Kyungmin Park kyungmin.park@samsung.com
- Minkyu Kang mk7.kang@samsung.com
- */
+#ifndef __ASM_ARM_ARCH_PWM_H_ +#define __ASM_ARM_ARCH_PWM_H_
+#define PRESCALER_0 (8 - 1) /* prescaler of timer 0, 1 */ +#define PRESCALER_1 (16 - 1) /* prescaler of timer 2, 3, 4 */
+/* Divider MUX */ +#define MUX_DIV_1 0 /* 1/1 period */ +#define MUX_DIV_2 1 /* 1/2 period */ +#define MUX_DIV_4 2 /* 1/4 period */ +#define MUX_DIV_8 3 /* 1/8 period */ +#define MUX_DIV_16 4 /* 1/16 period */
+#define MUX_DIV_SHIFT(x) ((x) * 4)
+#define TCON_OFFSET(x) (((x) + 1) * (!!x) << 2)
+#define TCON_START(x) (1 << TCON_OFFSET(x)) +#define TCON_UPDATE(x) (1 << (TCON_OFFSET(x) + 1)) +#define TCON_INVERTER(x) (1 << (TCON_OFFSET(x) + 2)) +#define TCON_AUTO_RELOAD(x) (1 << (TCON_OFFSET(x) + 3)) +#define TCON4_AUTO_RELOAD (1 << 22)
+#ifndef __ASSEMBLY__ +struct s5p_timer {
- unsigned int tcfg0;
- unsigned int tcfg1;
- unsigned int tcon;
- unsigned int tcntb0;
- unsigned int tcmpb0;
- unsigned int tcnto0;
- unsigned int tcntb1;
- unsigned int tcmpb1;
- unsigned int tcnto1;
- unsigned int tcntb2;
- unsigned int tcmpb2;
- unsigned int tcnto2;
- unsigned int tcntb3;
- unsigned int res1;
- unsigned int tcnto3;
- unsigned int tcntb4;
- unsigned int tcnto4;
- unsigned int tintcstat;
+}; +#endif /* __ASSEMBLY__ */
+#endif