[PATCH v2 0/8] Cortina Access Drivers Package 2

This release adds the following drivers and integrates support into the Cortina Access Presidio Engineering Board:
CAxxxx SoC eMMC/SD controller CAxxxx SoC I2C controller CAxxxx Soc LED controller CAxxxx SPI NAND and NOR controller
Changes in v3: - Fixup syntax issues related to checkpatch.pl cleanup
Changes in v2: - Add I2C controller - Add LED controller - Add SPI NAND and NOR controller
Alex Nemirovsky (3): board: presidio-asic: Add eMMC board support board: presidio-asic: Add I2C support board: presidio-asic: Add SPI NAND and NOR support
Arthur Li (2): mmc: ca_dw_mmc: add DesignWare based DM support for CAxxxx SoCs i2c: i2c-cortina: added CAxxxx I2C support
Jway Lin (2): led: led_cortina: Add CAxxx LED support board: presidio: add LED support
Pengpeng Chen (1): spi: ca_sflash: Add CAxxxx SPI Flash Controller
MAINTAINERS | 10 + arch/arm/dts/ca-presidio-engboard.dts | 39 +- board/cortina/presidio-asic/presidio.c | 16 +- configs/cortina_presidio-asic-emmc_defconfig | 38 ++ configs/cortina_presidio-asic-spi-nand_defconfig | 48 ++ configs/cortina_presidio-asic-spi-nor_defconfig | 59 +++ drivers/i2c/Kconfig | 7 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-cortina.c | 346 ++++++++++++++ drivers/i2c/i2c-cortina.h | 92 ++++ drivers/led/Kconfig | 8 + drivers/led/Makefile | 1 + drivers/led/led_cortina.c | 308 ++++++++++++ drivers/mmc/Kconfig | 11 + drivers/mmc/Makefile | 1 + drivers/mmc/ca_dw_mmc.c | 181 +++++++ drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/ca_sflash.c | 576 +++++++++++++++++++++++ 19 files changed, 1745 insertions(+), 6 deletions(-) create mode 100644 configs/cortina_presidio-asic-emmc_defconfig create mode 100644 configs/cortina_presidio-asic-spi-nand_defconfig create mode 100644 configs/cortina_presidio-asic-spi-nor_defconfig create mode 100644 drivers/i2c/i2c-cortina.c create mode 100644 drivers/i2c/i2c-cortina.h create mode 100644 drivers/led/led_cortina.c create mode 100644 drivers/mmc/ca_dw_mmc.c create mode 100644 drivers/spi/ca_sflash.c

From: Arthur Li arthur.li@cortina-access.com
Initial DesignWare based DM support for Cortina Access CAxxxx SoCs.
Signed-off-by: Arthur Li arthur.li@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Peng Fan peng.fan@nxp.com
---
Changes in v3: None Changes in v2: - Add I2C controller - Add LED controller - Add SPI NAND and NOR controller
MAINTAINERS | 2 + drivers/mmc/Kconfig | 11 +++ drivers/mmc/Makefile | 1 + drivers/mmc/ca_dw_mmc.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 drivers/mmc/ca_dw_mmc.c
diff --git a/MAINTAINERS b/MAINTAINERS index 82e4159..bb45d3c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -180,6 +180,7 @@ F: board/cortina/common/ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c +F: drivers/mmc/ca_dw_mmc.c
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -670,6 +671,7 @@ F: board/cortina/common/ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c +F: drivers/mmc/ca_dw_mmc.c
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb38787 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -205,6 +205,17 @@ config MMC_DW block, this provides host support for SD and MMC interfaces, in both PIO, internal DMA mode and external DMA mode.
+config MMC_DW_CORTINA + bool "Cortina specific extensions for Synopsys DW Memory Card Interface" + depends on DM_MMC + depends on MMC_DW + depends on BLK + default n + help + This selects support for Cortina SoC specific extensions to the + Synopsys DesignWare Memory Card Interface driver. Select this option + for platforms based on Cortina CAxxxx Soc's. + config MMC_DW_EXYNOS bool "Exynos specific extensions for Synopsys DW Memory Card Interface" depends on ARCH_EXYNOS diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..615b724 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -20,6 +20,7 @@ endif obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o +obj-$(CONFIG_MMC_DW_CORTINA) += ca_dw_mmc.o obj-$(CONFIG_MMC_DW_EXYNOS) += exynos_dw_mmc.o obj-$(CONFIG_MMC_DW_K3) += hi6220_dw_mmc.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += rockchip_dw_mmc.o diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c new file mode 100644 index 0000000..acbc850 --- /dev/null +++ b/drivers/mmc/ca_dw_mmc.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2019 Cortina Access + * Arthur Li arthur.li@cortina-access.com + */ + +#include <common.h> +#include <dwmmc.h> +#include <fdtdec.h> +#include <linux/libfdt.h> +#include <malloc.h> +#include <errno.h> +#include <dm.h> +#include <mapmem.h> + +#define SD_CLK_SEL_MASK (0x3) +#define SD_DLL_DEFAULT (0x143000) +#define SD_SCLK_MAX (200000000) + +#define SD_CLK_SEL_200MHZ (0x2) +#define SD_CLK_SEL_100MHZ (0x1) + +#define IO_DRV_SD_DS_OFFSET (16) +#define IO_DRV_SD_DS_MASK (0xff << IO_DRV_SD_DS_OFFSET) + +#define MIN_FREQ (400000) + +DECLARE_GLOBAL_DATA_PTR; + +struct ca_mmc_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +struct ca_dwmmc_priv_data { + struct dwmci_host host; + void __iomem *sd_dll_reg; + void __iomem *io_drv_reg; + u8 ds; +}; + +static void ca_dwmci_clksel(struct dwmci_host *host) +{ + struct ca_dwmmc_priv_data *priv = host->priv; + u32 val = readl(priv->sd_dll_reg); + + if (host->bus_hz >= 200000000) { + val &= ~SD_CLK_SEL_MASK; + val |= SD_CLK_SEL_200MHZ; + } else if (host->bus_hz >= 100000000) { + val &= ~SD_CLK_SEL_MASK; + val |= SD_CLK_SEL_100MHZ; + } else { + val &= ~SD_CLK_SEL_MASK; + } + + writel(val, priv->sd_dll_reg); +} + +static void ca_dwmci_board_init(struct dwmci_host *host) +{ + struct ca_dwmmc_priv_data *priv = host->priv; + u32 val = readl(priv->io_drv_reg); + + writel(SD_DLL_DEFAULT, priv->sd_dll_reg); + + val &= ~IO_DRV_SD_DS_MASK; + if (priv && priv->ds) + val |= priv->ds << IO_DRV_SD_DS_OFFSET; + writel(val, priv->io_drv_reg); +} + +unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, uint freq) +{ + struct ca_dwmmc_priv_data *priv = host->priv; + u8 sd_clk_sel = readl(priv->sd_dll_reg) & SD_CLK_SEL_MASK; + u8 clk_div; + + switch (sd_clk_sel) { + case 2: + clk_div = 1; + break; + case 1: + clk_div = 2; + break; + default: + clk_div = 4; + } + + return SD_SCLK_MAX / clk_div / (host->div + 1); +} + +static int ca_dwmmc_ofdata_to_platdata(struct udevice *dev) +{ + struct ca_dwmmc_priv_data *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + u32 tmp; + + host->name = dev->name; + host->dev_index = 0; + + host->buswidth = dev_read_u32_default(dev, "bus-width", 1); + if (host->buswidth != 1 && host->buswidth != 4) + return -EINVAL; + + host->bus_hz = dev_read_u32_default(dev, "max-frequency", 50000000); + priv->ds = dev_read_u32_default(dev, "io_ds", 0x33); + host->fifo_mode = dev_read_bool(dev, "fifo-mode"); + + dev_read_u32(dev, "sd_dll_ctrl", &tmp); + priv->sd_dll_reg = map_sysmem((uintptr_t)tmp, sizeof(uintptr_t)); + if (!priv->sd_dll_reg) + return -EINVAL; + + dev_read_u32(dev, "io_drv_ctrl", &tmp); + priv->io_drv_reg = map_sysmem((uintptr_t)tmp, sizeof(uintptr_t)); + if (!priv->io_drv_reg) + return -EINVAL; + + host->ioaddr = dev_read_addr_ptr(dev); + if (host->ioaddr == (void *)FDT_ADDR_T_NONE) { + printf("DWMMC: base address is invalid\n"); + return -EINVAL; + } + + host->priv = priv; + + return 0; +} + +struct dm_mmc_ops ca_dwmci_dm_ops; + +static int ca_dwmmc_probe(struct udevice *dev) +{ + struct ca_mmc_plat *plat = dev_get_platdata(dev); + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct ca_dwmmc_priv_data *priv = dev_get_priv(dev); + struct dwmci_host *host = &priv->host; + + memcpy(&ca_dwmci_dm_ops, &dm_dwmci_ops, sizeof(struct dm_mmc_ops)); + + dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, MIN_FREQ); + if (host->buswidth == 1) { + (&plat->cfg)->host_caps &= ~MMC_MODE_8BIT; + (&plat->cfg)->host_caps &= ~MMC_MODE_4BIT; + } + + host->mmc = &plat->mmc; + host->mmc->priv = &priv->host; + upriv->mmc = host->mmc; + host->mmc->dev = dev; + host->clksel = ca_dwmci_clksel; + host->board_init = ca_dwmci_board_init; + host->get_mmc_clk = ca_dwmci_get_mmc_clock; + + return dwmci_probe(dev); +} + +static int ca_dwmmc_bind(struct udevice *dev) +{ + struct ca_mmc_plat *plat = dev_get_platdata(dev); + + return dwmci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id ca_dwmmc_ids[] = { + { .compatible = "snps,dw-cortina" }, + { } +}; + +U_BOOT_DRIVER(ca_dwmmc_drv) = { + .name = "cortina_dwmmc", + .id = UCLASS_MMC, + .of_match = ca_dwmmc_ids, + .ofdata_to_platdata = ca_dwmmc_ofdata_to_platdata, + .bind = ca_dwmmc_bind, + .ops = &ca_dwmci_dm_ops, + .probe = ca_dwmmc_probe, + .priv_auto_alloc_size = sizeof(struct ca_dwmmc_priv_data), + .platdata_auto_alloc_size = sizeof(struct ca_mmc_plat), +};

Hi,
On 3/20/20 9:57 AM, Alex Nemirovsky wrote:
From: Arthur Li arthur.li@cortina-access.com
Initial DesignWare based DM support for Cortina Access CAxxxx SoCs.
Signed-off-by: Arthur Li arthur.li@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Peng Fan peng.fan@nxp.com
I didn't receive any reply about my previous comments. And Is there patch v3 or v2? Subject is v2..but change log is v3. What is correct version?
Best Regards, Jaehoon Chung
Changes in v3: None Changes in v2:
- Add I2C controller
- Add LED controller
- Add SPI NAND and NOR controller
MAINTAINERS | 2 + drivers/mmc/Kconfig | 11 +++ drivers/mmc/Makefile | 1 + drivers/mmc/ca_dw_mmc.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 drivers/mmc/ca_dw_mmc.c
diff --git a/MAINTAINERS b/MAINTAINERS index 82e4159..bb45d3c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -180,6 +180,7 @@ F: board/cortina/common/ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c +F: drivers/mmc/ca_dw_mmc.c
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -670,6 +671,7 @@ F: board/cortina/common/ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c +F: drivers/mmc/ca_dw_mmc.c
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb38787 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -205,6 +205,17 @@ config MMC_DW block, this provides host support for SD and MMC interfaces, in both PIO, internal DMA mode and external DMA mode.
+config MMC_DW_CORTINA
- bool "Cortina specific extensions for Synopsys DW Memory Card Interface"
- depends on DM_MMC
- depends on MMC_DW
- depends on BLK
- default n
- help
This selects support for Cortina SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Cortina CAxxxx Soc's.
config MMC_DW_EXYNOS bool "Exynos specific extensions for Synopsys DW Memory Card Interface" depends on ARCH_EXYNOS diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..615b724 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -20,6 +20,7 @@ endif obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o +obj-$(CONFIG_MMC_DW_CORTINA) += ca_dw_mmc.o obj-$(CONFIG_MMC_DW_EXYNOS) += exynos_dw_mmc.o obj-$(CONFIG_MMC_DW_K3) += hi6220_dw_mmc.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += rockchip_dw_mmc.o diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c new file mode 100644 index 0000000..acbc850 --- /dev/null +++ b/drivers/mmc/ca_dw_mmc.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2019 Cortina Access
- Arthur Li arthur.li@cortina-access.com
- */
+#include <common.h> +#include <dwmmc.h> +#include <fdtdec.h> +#include <linux/libfdt.h> +#include <malloc.h> +#include <errno.h> +#include <dm.h> +#include <mapmem.h>
+#define SD_CLK_SEL_MASK (0x3) +#define SD_DLL_DEFAULT (0x143000) +#define SD_SCLK_MAX (200000000)
+#define SD_CLK_SEL_200MHZ (0x2) +#define SD_CLK_SEL_100MHZ (0x1)
+#define IO_DRV_SD_DS_OFFSET (16) +#define IO_DRV_SD_DS_MASK (0xff << IO_DRV_SD_DS_OFFSET)
+#define MIN_FREQ (400000)
+DECLARE_GLOBAL_DATA_PTR;
+struct ca_mmc_plat {
- struct mmc_config cfg;
- struct mmc mmc;
+};
+struct ca_dwmmc_priv_data {
- struct dwmci_host host;
- void __iomem *sd_dll_reg;
- void __iomem *io_drv_reg;
- u8 ds;
+};
+static void ca_dwmci_clksel(struct dwmci_host *host) +{
- struct ca_dwmmc_priv_data *priv = host->priv;
- u32 val = readl(priv->sd_dll_reg);
- if (host->bus_hz >= 200000000) {
val &= ~SD_CLK_SEL_MASK;
val |= SD_CLK_SEL_200MHZ;
- } else if (host->bus_hz >= 100000000) {
val &= ~SD_CLK_SEL_MASK;
val |= SD_CLK_SEL_100MHZ;
- } else {
val &= ~SD_CLK_SEL_MASK;
- }
- writel(val, priv->sd_dll_reg);
+}
+static void ca_dwmci_board_init(struct dwmci_host *host) +{
- struct ca_dwmmc_priv_data *priv = host->priv;
- u32 val = readl(priv->io_drv_reg);
- writel(SD_DLL_DEFAULT, priv->sd_dll_reg);
- val &= ~IO_DRV_SD_DS_MASK;
- if (priv && priv->ds)
val |= priv->ds << IO_DRV_SD_DS_OFFSET;
- writel(val, priv->io_drv_reg);
+}
+unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, uint freq) +{
- struct ca_dwmmc_priv_data *priv = host->priv;
- u8 sd_clk_sel = readl(priv->sd_dll_reg) & SD_CLK_SEL_MASK;
- u8 clk_div;
- switch (sd_clk_sel) {
- case 2:
clk_div = 1;
break;
- case 1:
clk_div = 2;
break;
- default:
clk_div = 4;
- }
- return SD_SCLK_MAX / clk_div / (host->div + 1);
+}
+static int ca_dwmmc_ofdata_to_platdata(struct udevice *dev) +{
- struct ca_dwmmc_priv_data *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- u32 tmp;
- host->name = dev->name;
- host->dev_index = 0;
- host->buswidth = dev_read_u32_default(dev, "bus-width", 1);
- if (host->buswidth != 1 && host->buswidth != 4)
return -EINVAL;
- host->bus_hz = dev_read_u32_default(dev, "max-frequency", 50000000);
- priv->ds = dev_read_u32_default(dev, "io_ds", 0x33);
- host->fifo_mode = dev_read_bool(dev, "fifo-mode");
- dev_read_u32(dev, "sd_dll_ctrl", &tmp);
- priv->sd_dll_reg = map_sysmem((uintptr_t)tmp, sizeof(uintptr_t));
- if (!priv->sd_dll_reg)
return -EINVAL;
- dev_read_u32(dev, "io_drv_ctrl", &tmp);
- priv->io_drv_reg = map_sysmem((uintptr_t)tmp, sizeof(uintptr_t));
- if (!priv->io_drv_reg)
return -EINVAL;
- host->ioaddr = dev_read_addr_ptr(dev);
- if (host->ioaddr == (void *)FDT_ADDR_T_NONE) {
printf("DWMMC: base address is invalid\n");
return -EINVAL;
- }
- host->priv = priv;
- return 0;
+}
+struct dm_mmc_ops ca_dwmci_dm_ops;
+static int ca_dwmmc_probe(struct udevice *dev) +{
- struct ca_mmc_plat *plat = dev_get_platdata(dev);
- struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
- struct ca_dwmmc_priv_data *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- memcpy(&ca_dwmci_dm_ops, &dm_dwmci_ops, sizeof(struct dm_mmc_ops));
- dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, MIN_FREQ);
- if (host->buswidth == 1) {
(&plat->cfg)->host_caps &= ~MMC_MODE_8BIT;
(&plat->cfg)->host_caps &= ~MMC_MODE_4BIT;
- }
- host->mmc = &plat->mmc;
- host->mmc->priv = &priv->host;
- upriv->mmc = host->mmc;
- host->mmc->dev = dev;
- host->clksel = ca_dwmci_clksel;
- host->board_init = ca_dwmci_board_init;
- host->get_mmc_clk = ca_dwmci_get_mmc_clock;
- return dwmci_probe(dev);
+}
+static int ca_dwmmc_bind(struct udevice *dev) +{
- struct ca_mmc_plat *plat = dev_get_platdata(dev);
- return dwmci_bind(dev, &plat->mmc, &plat->cfg);
+}
+static const struct udevice_id ca_dwmmc_ids[] = {
- { .compatible = "snps,dw-cortina" },
- { }
+};
+U_BOOT_DRIVER(ca_dwmmc_drv) = {
- .name = "cortina_dwmmc",
- .id = UCLASS_MMC,
- .of_match = ca_dwmmc_ids,
- .ofdata_to_platdata = ca_dwmmc_ofdata_to_platdata,
- .bind = ca_dwmmc_bind,
- .ops = &ca_dwmci_dm_ops,
- .probe = ca_dwmmc_probe,
- .priv_auto_alloc_size = sizeof(struct ca_dwmmc_priv_data),
- .platdata_auto_alloc_size = sizeof(struct ca_mmc_plat),
+};

Hi Jaehoon, We incorporated your comments locally. We will release the next series as v4 once all feedback is received for the rest of the patches in the series. Thank you for your feedback.
-BR AN
On Mar 24, 2020, at 12:16 AM, Jaehoon Chung jh80.chung@samsung.com wrote:
Hi,
On 3/20/20 9:57 AM, Alex Nemirovsky wrote:
From: Arthur Li arthur.li@cortina-access.com
Initial DesignWare based DM support for Cortina Access CAxxxx SoCs.
Signed-off-by: Arthur Li arthur.li@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Peng Fan peng.fan@nxp.com
I didn't receive any reply about my previous comments. And Is there patch v3 or v2? Subject is v2..but change log is v3. What is correct version?
Best Regards, Jaehoon Chung
Changes in v3: None Changes in v2:
- Add I2C controller
- Add LED controller
- Add SPI NAND and NOR controller
MAINTAINERS | 2 + drivers/mmc/Kconfig | 11 +++ drivers/mmc/Makefile | 1 + drivers/mmc/ca_dw_mmc.c | 181 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+) create mode 100644 drivers/mmc/ca_dw_mmc.c
diff --git a/MAINTAINERS b/MAINTAINERS index 82e4159..bb45d3c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -180,6 +180,7 @@ F: board/cortina/common/ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c +F: drivers/mmc/ca_dw_mmc.c
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -670,6 +671,7 @@ F: board/cortina/common/ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c +F: drivers/mmc/ca_dw_mmc.c
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 2f0eedc..bb38787 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -205,6 +205,17 @@ config MMC_DW block, this provides host support for SD and MMC interfaces, in both PIO, internal DMA mode and external DMA mode.
+config MMC_DW_CORTINA
- bool "Cortina specific extensions for Synopsys DW Memory Card Interface"
- depends on DM_MMC
- depends on MMC_DW
- depends on BLK
- default n
- help
This selects support for Cortina SoC specific extensions to the
Synopsys DesignWare Memory Card Interface driver. Select this option
for platforms based on Cortina CAxxxx Soc's.
config MMC_DW_EXYNOS bool "Exynos specific extensions for Synopsys DW Memory Card Interface" depends on ARCH_EXYNOS diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 9c1f8e5..615b724 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -20,6 +20,7 @@ endif obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o obj-$(CONFIG_MMC_DAVINCI) += davinci_mmc.o obj-$(CONFIG_MMC_DW) += dw_mmc.o +obj-$(CONFIG_MMC_DW_CORTINA) += ca_dw_mmc.o obj-$(CONFIG_MMC_DW_EXYNOS) += exynos_dw_mmc.o obj-$(CONFIG_MMC_DW_K3) += hi6220_dw_mmc.o obj-$(CONFIG_MMC_DW_ROCKCHIP) += rockchip_dw_mmc.o diff --git a/drivers/mmc/ca_dw_mmc.c b/drivers/mmc/ca_dw_mmc.c new file mode 100644 index 0000000..acbc850 --- /dev/null +++ b/drivers/mmc/ca_dw_mmc.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2019 Cortina Access
- Arthur Li arthur.li@cortina-access.com
- */
+#include <common.h> +#include <dwmmc.h> +#include <fdtdec.h> +#include <linux/libfdt.h> +#include <malloc.h> +#include <errno.h> +#include <dm.h> +#include <mapmem.h>
+#define SD_CLK_SEL_MASK (0x3) +#define SD_DLL_DEFAULT (0x143000) +#define SD_SCLK_MAX (200000000)
+#define SD_CLK_SEL_200MHZ (0x2) +#define SD_CLK_SEL_100MHZ (0x1)
+#define IO_DRV_SD_DS_OFFSET (16) +#define IO_DRV_SD_DS_MASK (0xff << IO_DRV_SD_DS_OFFSET)
+#define MIN_FREQ (400000)
+DECLARE_GLOBAL_DATA_PTR;
+struct ca_mmc_plat {
- struct mmc_config cfg;
- struct mmc mmc;
+};
+struct ca_dwmmc_priv_data {
- struct dwmci_host host;
- void __iomem *sd_dll_reg;
- void __iomem *io_drv_reg;
- u8 ds;
+};
+static void ca_dwmci_clksel(struct dwmci_host *host) +{
- struct ca_dwmmc_priv_data *priv = host->priv;
- u32 val = readl(priv->sd_dll_reg);
- if (host->bus_hz >= 200000000) {
val &= ~SD_CLK_SEL_MASK;
val |= SD_CLK_SEL_200MHZ;
- } else if (host->bus_hz >= 100000000) {
val &= ~SD_CLK_SEL_MASK;
val |= SD_CLK_SEL_100MHZ;
- } else {
val &= ~SD_CLK_SEL_MASK;
- }
- writel(val, priv->sd_dll_reg);
+}
+static void ca_dwmci_board_init(struct dwmci_host *host) +{
- struct ca_dwmmc_priv_data *priv = host->priv;
- u32 val = readl(priv->io_drv_reg);
- writel(SD_DLL_DEFAULT, priv->sd_dll_reg);
- val &= ~IO_DRV_SD_DS_MASK;
- if (priv && priv->ds)
val |= priv->ds << IO_DRV_SD_DS_OFFSET;
- writel(val, priv->io_drv_reg);
+}
+unsigned int ca_dwmci_get_mmc_clock(struct dwmci_host *host, uint freq) +{
- struct ca_dwmmc_priv_data *priv = host->priv;
- u8 sd_clk_sel = readl(priv->sd_dll_reg) & SD_CLK_SEL_MASK;
- u8 clk_div;
- switch (sd_clk_sel) {
- case 2:
clk_div = 1;
break;
- case 1:
clk_div = 2;
break;
- default:
clk_div = 4;
- }
- return SD_SCLK_MAX / clk_div / (host->div + 1);
+}
+static int ca_dwmmc_ofdata_to_platdata(struct udevice *dev) +{
- struct ca_dwmmc_priv_data *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- u32 tmp;
- host->name = dev->name;
- host->dev_index = 0;
- host->buswidth = dev_read_u32_default(dev, "bus-width", 1);
- if (host->buswidth != 1 && host->buswidth != 4)
return -EINVAL;
- host->bus_hz = dev_read_u32_default(dev, "max-frequency", 50000000);
- priv->ds = dev_read_u32_default(dev, "io_ds", 0x33);
- host->fifo_mode = dev_read_bool(dev, "fifo-mode");
- dev_read_u32(dev, "sd_dll_ctrl", &tmp);
- priv->sd_dll_reg = map_sysmem((uintptr_t)tmp, sizeof(uintptr_t));
- if (!priv->sd_dll_reg)
return -EINVAL;
- dev_read_u32(dev, "io_drv_ctrl", &tmp);
- priv->io_drv_reg = map_sysmem((uintptr_t)tmp, sizeof(uintptr_t));
- if (!priv->io_drv_reg)
return -EINVAL;
- host->ioaddr = dev_read_addr_ptr(dev);
- if (host->ioaddr == (void *)FDT_ADDR_T_NONE) {
printf("DWMMC: base address is invalid\n");
return -EINVAL;
- }
- host->priv = priv;
- return 0;
+}
+struct dm_mmc_ops ca_dwmci_dm_ops;
+static int ca_dwmmc_probe(struct udevice *dev) +{
- struct ca_mmc_plat *plat = dev_get_platdata(dev);
- struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
- struct ca_dwmmc_priv_data *priv = dev_get_priv(dev);
- struct dwmci_host *host = &priv->host;
- memcpy(&ca_dwmci_dm_ops, &dm_dwmci_ops, sizeof(struct dm_mmc_ops));
- dwmci_setup_cfg(&plat->cfg, host, host->bus_hz, MIN_FREQ);
- if (host->buswidth == 1) {
(&plat->cfg)->host_caps &= ~MMC_MODE_8BIT;
(&plat->cfg)->host_caps &= ~MMC_MODE_4BIT;
- }
- host->mmc = &plat->mmc;
- host->mmc->priv = &priv->host;
- upriv->mmc = host->mmc;
- host->mmc->dev = dev;
- host->clksel = ca_dwmci_clksel;
- host->board_init = ca_dwmci_board_init;
- host->get_mmc_clk = ca_dwmci_get_mmc_clock;
- return dwmci_probe(dev);
+}
+static int ca_dwmmc_bind(struct udevice *dev) +{
- struct ca_mmc_plat *plat = dev_get_platdata(dev);
- return dwmci_bind(dev, &plat->mmc, &plat->cfg);
+}
+static const struct udevice_id ca_dwmmc_ids[] = {
- { .compatible = "snps,dw-cortina" },
- { }
+};
+U_BOOT_DRIVER(ca_dwmmc_drv) = {
- .name = "cortina_dwmmc",
- .id = UCLASS_MMC,
- .of_match = ca_dwmmc_ids,
- .ofdata_to_platdata = ca_dwmmc_ofdata_to_platdata,
- .bind = ca_dwmmc_bind,
- .ops = &ca_dwmci_dm_ops,
- .probe = ca_dwmmc_probe,
- .priv_auto_alloc_size = sizeof(struct ca_dwmmc_priv_data),
- .platdata_auto_alloc_size = sizeof(struct ca_mmc_plat),
+};

Add initial eMMC support for Cortina Access Presidio Engineering Board
Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Peng Fan peng.fan@nxp.com ---
Changes in v3: None Changes in v2: None
configs/cortina_presidio-asic-emmc_defconfig | 33 ++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 configs/cortina_presidio-asic-emmc_defconfig
diff --git a/configs/cortina_presidio-asic-emmc_defconfig b/configs/cortina_presidio-asic-emmc_defconfig new file mode 100644 index 0000000..e10008a --- /dev/null +++ b/configs/cortina_presidio-asic-emmc_defconfig @@ -0,0 +1,33 @@ +CONFIG_ARM=y +# CONFIG_SYS_ARCH_TIMER is not set +CONFIG_TARGET_PRESIDIO_ASIC=y +CONFIG_SYS_TEXT_BASE=0x04000000 +CONFIG_ENV_SIZE=0x20000 +CONFIG_DM_GPIO=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_IDENT_STRING="Presidio-SoC" +CONFIG_SHOW_BOOT_PROGRESS=y +CONFIG_BOOTDELAY=3 +CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_SYS_PROMPT="G3#" +CONFIG_CMD_MMC=y +CONFIG_CMD_PART=y +CONFIG_CMD_WDT=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_TIMER=y +CONFIG_CMD_SMC=y +CONFIG_CMD_EXT2=y +CONFIG_CMD_EXT4=y +CONFIG_OF_CONTROL=y +CONFIG_OF_LIVE=y +CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard" +# CONFIG_NET is not set +CONFIG_DM=y +CONFIG_CORTINA_GPIO=y +CONFIG_DM_MMC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_CORTINA=y +CONFIG_DM_SERIAL=y +CONFIG_CORTINA_UART=y +CONFIG_WDT=y +CONFIG_WDT_CORTINA=y

From: Arthur Li arthur.li@cortina-access.com
Add I2C controller support for Cortina Access CAxxxx SoCs
Signed-off-by: Arthur Li arthur.li@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Heiko Schocher hs@denx.de ---
Changes in v3: None Changes in v2: None
MAINTAINERS | 4 + drivers/i2c/Kconfig | 7 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-cortina.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/i2c-cortina.h | 92 ++++++++++++ 5 files changed, 450 insertions(+) create mode 100644 drivers/i2c/i2c-cortina.c create mode 100644 drivers/i2c/i2c-cortina.h
diff --git a/MAINTAINERS b/MAINTAINERS index bb45d3c..b147faa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -181,6 +181,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -672,6 +674,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..b98a4aa 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -85,6 +85,13 @@ config SYS_I2C_CADENCE Say yes here to select Cadence I2C Host Controller. This controller is e.g. used by Xilinx Zynq.
+config SYS_I2C_CA + tristate "Cortina-Access I2C Controller" + depends on DM_I2C && CORTINA_PLATFORM + default n + help + Say yes here to select Cortina-Access I2C Host Controller. + config SYS_I2C_DAVINCI bool "Davinci I2C Controller" depends on (ARCH_KEYSTONE || ARCH_DAVINCI) diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..5d18cf7 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o +obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o ifdef CONFIG_DM_PCI diff --git a/drivers/i2c/i2c-cortina.c b/drivers/i2c/i2c-cortina.c new file mode 100644 index 0000000..99c63f3 --- /dev/null +++ b/drivers/i2c/i2c-cortina.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2020 + * Arthur Li, Cortina Access, arthur.li@cortina-access.com. + */ + +#include <common.h> +#include <i2c.h> +#include <asm/io.h> +#include <dm.h> +#include <mapmem.h> +#include "i2c-cortina.h" + +static void set_speed(struct i2c_regs *regs, int i2c_spd) +{ + union ca_biw_cfg i2c_cfg; + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 0; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + + switch (i2c_spd) { + case IC_SPEED_MODE_MAX: + i2c_cfg.bf.prer = + CORTINA_PER_IO_FREQ / (5 * I2C_MAX_SPEED) - 1; + break; + + case IC_SPEED_MODE_STANDARD: + i2c_cfg.bf.prer = + CORTINA_PER_IO_FREQ / (5 * I2C_STANDARD_SPEED) - 1; + break; + + case IC_SPEED_MODE_FAST: + default: + i2c_cfg.bf.prer = + CORTINA_PER_IO_FREQ / (5 * I2C_FAST_SPEED) - 1; + break; + } + + i2c_cfg.bf.core_en = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); +} + +static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int i2c_spd; + + if (speed >= I2C_MAX_SPEED) { + i2c_spd = IC_SPEED_MODE_MAX; + priv->speed = I2C_MAX_SPEED; + } else if (speed >= I2C_FAST_SPEED) { + i2c_spd = IC_SPEED_MODE_FAST; + priv->speed = I2C_FAST_SPEED; + } else { + i2c_spd = IC_SPEED_MODE_STANDARD; + priv->speed = I2C_STANDARD_SPEED; + } + + set_speed(priv->regs, i2c_spd); + + return 0; +} + +static int ca_i2c_get_bus_speed(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + return priv->speed; +} + +static void ca_i2c_init(struct i2c_regs *regs) +{ + union ca_biw_cfg i2c_cfg; + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 0; + i2c_cfg.bf.biw_soft_reset = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + mdelay(10); + i2c_cfg.bf.biw_soft_reset = 0; + writel(i2c_cfg.wrd, ®s->i2c_cfg); + + set_speed(regs, IC_SPEED_MODE_STANDARD); + + i2c_cfg.wrd = readl(®s->i2c_cfg); + i2c_cfg.bf.core_en = 1; + writel(i2c_cfg.wrd, ®s->i2c_cfg); +} + +static int i2c_wait_complete(struct i2c_regs *regs) +{ + union ca_biw_ctrl i2c_ctrl; + unsigned long start_time_bb = get_timer(0); + + i2c_ctrl.wrd = readl(®s->i2c_ctrl); + + while (i2c_ctrl.bf.biwdone == 0) { + i2c_ctrl.wrd = readl(®s->i2c_ctrl); + + if (get_timer(start_time_bb) > + (unsigned long)(I2C_BYTE_TO_BB)) { + printf("%s not done!!!\n", __func__); + return 1; + } + } + + /* Clear done bit */ + writel(i2c_ctrl.wrd, ®s->i2c_ctrl); + + return 0; +} + +static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr, + int write_read) +{ + writel(i2c_addr | write_read, ®s->i2c_txr); + + writel(BIW_CTRL_START | BIW_CTRL_WRITE, + ®s->i2c_ctrl); + + i2c_wait_complete(regs); +} + +static int i2c_wait_for_bus_busy(struct i2c_regs *regs) +{ + union ca_biw_ack i2c_ack; + unsigned long start_time_bb = get_timer(0); + + i2c_ack.wrd = readl(®s->i2c_ack); + + while (i2c_ack.bf.biw_busy) { + i2c_ack.wrd = readl(®s->i2c_ack); + + if (get_timer(start_time_bb) > + (unsigned long)(I2C_BYTE_TO_BB)) { + printf("%s: timeout!\n", __func__); + return 1; + } + } + + return 0; +} + +static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, int write_read) +{ + int addr_len = alen; + + if (i2c_wait_for_bus_busy(regs)) + return 1; + /* First cycle must write addr + offset */ + chip = ((chip & 0x7F) << 1); + if (alen == 0 && write_read == I2C_CMD_RD) + i2c_setaddress(regs, chip, I2C_CMD_RD); + else + i2c_setaddress(regs, chip, I2C_CMD_WT); + + while (alen) { + alen--; + writel(addr, ®s->i2c_txr); + if (write_read == I2C_CMD_RD) + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, + ®s->i2c_ctrl); + else + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); + i2c_wait_complete(regs); + } + + /* Send address again with Read flag if it's read command */ + if (write_read == I2C_CMD_RD && addr_len > 0) + i2c_setaddress(regs, chip, I2C_CMD_RD); + + return 0; +} + +static int i2c_xfer_finish(struct i2c_regs *regs) +{ + /* Dummy read makes bus free */ + writel(BIW_CTRL_READ | BIW_CTRL_STOP, ®s->i2c_ctrl); + i2c_wait_complete(regs); + + if (i2c_wait_for_bus_busy(regs)) { + printf("Timed out waiting for bus\n"); + return 1; + } + + return 0; +} + +static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len) +{ + unsigned long start_time_rx; + int rc = 0; + + if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD)) + return 1; + + start_time_rx = get_timer(0); + while (len) { + /* ACK_IN is ack value to send during read. + * ack high only on the very last byte! + */ + if (len == 1) { + writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP, + ®s->i2c_ctrl); + } else { + writel(BIW_CTRL_READ, ®s->i2c_ctrl); + } + + rc = i2c_wait_complete(regs); + udelay(1); + + if (rc == 0) { + *buffer++ = + (uchar) readl(®s->i2c_rxr); + len--; + start_time_rx = get_timer(0); + + } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { + return 1; + } + } + i2c_xfer_finish(regs); + return rc; +} + +static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr, + int alen, uint8_t *buffer, int len) +{ + int rc, nb = len; + unsigned long start_time_tx; + + if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT)) + return 1; + + start_time_tx = get_timer(0); + while (len) { + writel(*buffer, ®s->i2c_txr); + if (len == 1) { + writel(BIW_CTRL_WRITE | BIW_CTRL_STOP, + ®s->i2c_ctrl); + } else { + writel(BIW_CTRL_WRITE, ®s->i2c_ctrl); + } + + rc = i2c_wait_complete(regs); + + if (rc == 0) { + len--; + buffer++; + start_time_tx = get_timer(0); + } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) { + printf("Timed out. i2c write Failed\n"); + return 1; + } + } + + return 0; +} + +static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr, + uint chip_flags) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int ret; + u32 tmp; + + /* Try to read the first location of the chip */ + ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1); + if (ret) + ca_i2c_init(priv->regs); + + return ret; +} + +static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{ + struct ca_i2c *priv = dev_get_priv(bus); + int ret; + + debug("i2c_xfer: %d messages\n", nmsgs); + for (; nmsgs > 0; nmsgs--, msg++) { + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len); + if (msg->flags & I2C_M_RD) { + ret = ca_i2c_read(priv->regs, msg->addr, 0, 0, + msg->buf, msg->len); + } else { + ret = ca_i2c_write(priv->regs, msg->addr, 0, 0, + msg->buf, msg->len); + } + + if (ret) { + debug("i2c_write: error sending\n"); + return -EREMOTEIO; + } + } + + return 0; +} + +static const struct dm_i2c_ops ca_i2c_ops = { + .xfer = ca_i2c_xfer, + .probe_chip = ca_i2c_probe_chip, + .set_bus_speed = ca_i2c_set_bus_speed, + .get_bus_speed = ca_i2c_get_bus_speed, +}; + +static const struct udevice_id ca_i2c_ids[] = { + { .compatible = "cortina,ca-i2c", }, + { } +}; + +static int ca_i2c_probe(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + ca_i2c_init(priv->regs); + + return 0; +} + +static int ca_i2c_ofdata_to_platdata(struct udevice *bus) +{ + struct ca_i2c *priv = dev_get_priv(bus); + + priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs)); + if (!priv->regs) { + printf("I2C: base address is invalid\n"); + return -EINVAL; + } + + return 0; +} + +U_BOOT_DRIVER(i2c_cortina) = { + .name = "i2c_cortina", + .id = UCLASS_I2C, + .of_match = ca_i2c_ids, + .ofdata_to_platdata = ca_i2c_ofdata_to_platdata, + .probe = ca_i2c_probe, + .priv_auto_alloc_size = sizeof(struct ca_i2c), + .ops = &ca_i2c_ops, + .flags = DM_FLAG_PRE_RELOC, +}; diff --git a/drivers/i2c/i2c-cortina.h b/drivers/i2c/i2c-cortina.h new file mode 100644 index 0000000..f1c3dc7 --- /dev/null +++ b/drivers/i2c/i2c-cortina.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * (C) Copyright 2019 + * Cortina Access, <www.cortina-access.com> + */ + +#ifndef __CA_I2C_H_ +#define __CA_I2C_H_ + +#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__) +struct i2c_regs { + u32 i2c_cfg; + u32 i2c_ctrl; + u32 i2c_txr; + u32 i2c_rxr; + u32 i2c_ack; + u32 i2c_ie0; + u32 i2c_int0; + u32 i2c_ie1; + u32 i2c_int1; + u32 i2c_stat; +}; + +union ca_biw_cfg { + struct biw_cfg { + u32 core_en : 1; + u32 biw_soft_reset : 1; + u32 busywait_en : 1; + u32 stretch_en : 1; + u32 arb_en : 1; + u32 clksync_en : 1; + u32 rsrvd1 : 2; + u32 spike_cnt : 4; + u32 rsrvd2 : 4; + u32 prer : 16; + } bf; + unsigned int wrd; +}; + +union ca_biw_ctrl { + struct biw_ctrl { + u32 biwdone : 1; + u32 rsrvd1 : 2; + u32 ack_in : 1; + u32 write : 1; + u32 read : 1; + u32 stop : 1; + u32 start : 1; + u32 rsrvd2 : 24; + } bf; + unsigned int wrd; +}; + +union ca_biw_ack { + struct biw_ack { + u32 al :1; + u32 biw_busy :1; + u32 ack_out :1; + u32 rsrvd1 :29; + } bf; + unsigned int wrd; +}; +#endif /* !__ASSEMBLER__*/ + +struct ca_i2c { + struct i2c_regs *regs; + unsigned int speed; +}; + +#define I2C_CMD_WT 0 +#define I2C_CMD_RD 1 + +#define BIW_CTRL_DONE BIT(0) +#define BIW_CTRL_ACK_IN BIT(3) +#define BIW_CTRL_WRITE BIT(4) +#define BIW_CTRL_READ BIT(5) +#define BIW_CTRL_STOP BIT(6) +#define BIW_CTRL_START BIT(7) + +#define I2C_BYTE_TO (CONFIG_SYS_HZ / 500) +#define I2C_STOPDET_TO (CONFIG_SYS_HZ / 500) +#define I2C_BYTE_TO_BB (10) + +#define IC_SPEED_MODE_STANDARD 1 +#define IC_SPEED_MODE_FAST 2 +#define IC_SPEED_MODE_MAX 3 + +#define I2C_MAX_SPEED 1000000 +#define I2C_FAST_SPEED 400000 +#define I2C_STANDARD_SPEED 100000 + +#endif /* __CA_I2C_H_ */

Hello Alex,
Am 20.03.2020 um 01:57 schrieb Alex Nemirovsky:
From: Arthur Li arthur.li@cortina-access.com
Add I2C controller support for Cortina Access CAxxxx SoCs
Signed-off-by: Arthur Li arthur.li@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Heiko Schocher hs@denx.de
Changes in v3: None Changes in v2: None
MAINTAINERS | 4 + drivers/i2c/Kconfig | 7 + drivers/i2c/Makefile | 1 + drivers/i2c/i2c-cortina.c | 346 ++++++++++++++++++++++++++++++++++++++++++++++ drivers/i2c/i2c-cortina.h | 92 ++++++++++++ 5 files changed, 450 insertions(+) create mode 100644 drivers/i2c/i2c-cortina.c create mode 100644 drivers/i2c/i2c-cortina.h
diff --git a/MAINTAINERS b/MAINTAINERS index bb45d3c..b147faa 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -181,6 +181,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -672,6 +674,8 @@ F: drivers/gpio/cortina_gpio.c F: drivers/watchdog/cortina_wdt.c F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c +F: drivers/i2c/i2c-cortina.c +F: drivers/i2c/i2c-cortina.h
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig index 03d2fed..b98a4aa 100644 --- a/drivers/i2c/Kconfig +++ b/drivers/i2c/Kconfig @@ -85,6 +85,13 @@ config SYS_I2C_CADENCE Say yes here to select Cadence I2C Host Controller. This controller is e.g. used by Xilinx Zynq.
+config SYS_I2C_CA
- tristate "Cortina-Access I2C Controller"
- depends on DM_I2C && CORTINA_PLATFORM
- default n
- help
Say yes here to select Cortina-Access I2C Host Controller.
- config SYS_I2C_DAVINCI bool "Davinci I2C Controller" depends on (ARCH_KEYSTONE || ARCH_DAVINCI)
diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index f5a471f..5d18cf7 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -12,6 +12,7 @@ obj-$(CONFIG_SYS_I2C) += i2c_core.o obj-$(CONFIG_SYS_I2C_ASPEED) += ast_i2c.o obj-$(CONFIG_SYS_I2C_AT91) += at91_i2c.o obj-$(CONFIG_SYS_I2C_CADENCE) += i2c-cdns.o +obj-$(CONFIG_SYS_I2C_CA) += i2c-cortina.o obj-$(CONFIG_SYS_I2C_DAVINCI) += davinci_i2c.o obj-$(CONFIG_SYS_I2C_DW) += designware_i2c.o ifdef CONFIG_DM_PCI diff --git a/drivers/i2c/i2c-cortina.c b/drivers/i2c/i2c-cortina.c new file mode 100644 index 0000000..99c63f3 --- /dev/null +++ b/drivers/i2c/i2c-cortina.c @@ -0,0 +1,346 @@ +// SPDX-License-Identifier: GPL-2.0+ +/*
- (C) Copyright 2020
- Arthur Li, Cortina Access, arthur.li@cortina-access.com.
- */
+#include <common.h> +#include <i2c.h> +#include <asm/io.h> +#include <dm.h> +#include <mapmem.h> +#include "i2c-cortina.h"
+static void set_speed(struct i2c_regs *regs, int i2c_spd) +{
- union ca_biw_cfg i2c_cfg;
- i2c_cfg.wrd = readl(®s->i2c_cfg);
- i2c_cfg.bf.core_en = 0;
- writel(i2c_cfg.wrd, ®s->i2c_cfg);
- switch (i2c_spd) {
- case IC_SPEED_MODE_MAX:
i2c_cfg.bf.prer =
CORTINA_PER_IO_FREQ / (5 * I2C_MAX_SPEED) - 1;
break;
- case IC_SPEED_MODE_STANDARD:
i2c_cfg.bf.prer =
CORTINA_PER_IO_FREQ / (5 * I2C_STANDARD_SPEED) - 1;
break;
- case IC_SPEED_MODE_FAST:
- default:
i2c_cfg.bf.prer =
CORTINA_PER_IO_FREQ / (5 * I2C_FAST_SPEED) - 1;
break;
- }
- i2c_cfg.bf.core_en = 1;
- writel(i2c_cfg.wrd, ®s->i2c_cfg);
+}
+static int ca_i2c_set_bus_speed(struct udevice *bus, unsigned int speed) +{
- struct ca_i2c *priv = dev_get_priv(bus);
- int i2c_spd;
- if (speed >= I2C_MAX_SPEED) {
i2c_spd = IC_SPEED_MODE_MAX;
priv->speed = I2C_MAX_SPEED;
- } else if (speed >= I2C_FAST_SPEED) {
i2c_spd = IC_SPEED_MODE_FAST;
priv->speed = I2C_FAST_SPEED;
- } else {
i2c_spd = IC_SPEED_MODE_STANDARD;
priv->speed = I2C_STANDARD_SPEED;
- }
- set_speed(priv->regs, i2c_spd);
- return 0;
+}
+static int ca_i2c_get_bus_speed(struct udevice *bus) +{
- struct ca_i2c *priv = dev_get_priv(bus);
- return priv->speed;
+}
+static void ca_i2c_init(struct i2c_regs *regs) +{
- union ca_biw_cfg i2c_cfg;
- i2c_cfg.wrd = readl(®s->i2c_cfg);
- i2c_cfg.bf.core_en = 0;
- i2c_cfg.bf.biw_soft_reset = 1;
- writel(i2c_cfg.wrd, ®s->i2c_cfg);
- mdelay(10);
- i2c_cfg.bf.biw_soft_reset = 0;
- writel(i2c_cfg.wrd, ®s->i2c_cfg);
- set_speed(regs, IC_SPEED_MODE_STANDARD);
- i2c_cfg.wrd = readl(®s->i2c_cfg);
- i2c_cfg.bf.core_en = 1;
- writel(i2c_cfg.wrd, ®s->i2c_cfg);
+}
+static int i2c_wait_complete(struct i2c_regs *regs) +{
- union ca_biw_ctrl i2c_ctrl;
- unsigned long start_time_bb = get_timer(0);
- i2c_ctrl.wrd = readl(®s->i2c_ctrl);
- while (i2c_ctrl.bf.biwdone == 0) {
i2c_ctrl.wrd = readl(®s->i2c_ctrl);
if (get_timer(start_time_bb) >
(unsigned long)(I2C_BYTE_TO_BB)) {
printf("%s not done!!!\n", __func__);
return 1;
Please use -ETIMEDOUT
}
- }
- /* Clear done bit */
- writel(i2c_ctrl.wrd, ®s->i2c_ctrl);
- return 0;
+}
+static void i2c_setaddress(struct i2c_regs *regs, unsigned int i2c_addr,
int write_read)
+{
- writel(i2c_addr | write_read, ®s->i2c_txr);
- writel(BIW_CTRL_START | BIW_CTRL_WRITE,
®s->i2c_ctrl);
- i2c_wait_complete(regs);
+}
+static int i2c_wait_for_bus_busy(struct i2c_regs *regs) +{
- union ca_biw_ack i2c_ack;
- unsigned long start_time_bb = get_timer(0);
- i2c_ack.wrd = readl(®s->i2c_ack);
- while (i2c_ack.bf.biw_busy) {
i2c_ack.wrd = readl(®s->i2c_ack);
if (get_timer(start_time_bb) >
(unsigned long)(I2C_BYTE_TO_BB)) {
printf("%s: timeout!\n", __func__);
return 1;
Here too.
}
- }
- return 0;
+}
+static int i2c_xfer_init(struct i2c_regs *regs, uint8_t chip, uint addr,
int alen, int write_read)
+{
- int addr_len = alen;
- if (i2c_wait_for_bus_busy(regs))
return 1;
Add a blank line please
- /* First cycle must write addr + offset */
- chip = ((chip & 0x7F) << 1);
- if (alen == 0 && write_read == I2C_CMD_RD)
i2c_setaddress(regs, chip, I2C_CMD_RD);
- else
i2c_setaddress(regs, chip, I2C_CMD_WT);
- while (alen) {
alen--;
writel(addr, ®s->i2c_txr);
if (write_read == I2C_CMD_RD)
writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
®s->i2c_ctrl);
else
writel(BIW_CTRL_WRITE, ®s->i2c_ctrl);
i2c_wait_complete(regs);
- }
- /* Send address again with Read flag if it's read command */
- if (write_read == I2C_CMD_RD && addr_len > 0)
i2c_setaddress(regs, chip, I2C_CMD_RD);
- return 0;
+}
+static int i2c_xfer_finish(struct i2c_regs *regs) +{
- /* Dummy read makes bus free */
- writel(BIW_CTRL_READ | BIW_CTRL_STOP, ®s->i2c_ctrl);
- i2c_wait_complete(regs);
- if (i2c_wait_for_bus_busy(regs)) {
printf("Timed out waiting for bus\n");
return 1;
return -ETIMEDOUT
- }
- return 0;
+}
+static int ca_i2c_read(struct i2c_regs *regs, uint8_t chip, uint addr,
int alen, uint8_t *buffer, int len)
+{
- unsigned long start_time_rx;
- int rc = 0;
- if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_RD))
return 1;
please return the return value from i2c_xfer_init()
- start_time_rx = get_timer(0);
- while (len) {
/* ACK_IN is ack value to send during read.
* ack high only on the very last byte!
*/
if (len == 1) {
writel(BIW_CTRL_READ | BIW_CTRL_ACK_IN | BIW_CTRL_STOP,
®s->i2c_ctrl);
} else {
writel(BIW_CTRL_READ, ®s->i2c_ctrl);
}
remove the brackets, they are not needed.
rc = i2c_wait_complete(regs);
udelay(1);
if (rc == 0) {
*buffer++ =
(uchar) readl(®s->i2c_rxr);
len--;
start_time_rx = get_timer(0);
} else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
return 1;
return -ETIMEDOUT
}
- }
- i2c_xfer_finish(regs);
- return rc;
+}
+static int ca_i2c_write(struct i2c_regs *regs, uint8_t chip, uint addr,
int alen, uint8_t *buffer, int len)
+{
- int rc, nb = len;
- unsigned long start_time_tx;
- if (i2c_xfer_init(regs, chip, addr, alen, I2C_CMD_WT))
return 1;
please return the return value from i2c_xfer_init
- start_time_tx = get_timer(0);
- while (len) {
writel(*buffer, ®s->i2c_txr);
if (len == 1) {
writel(BIW_CTRL_WRITE | BIW_CTRL_STOP,
®s->i2c_ctrl);
} else {
writel(BIW_CTRL_WRITE, ®s->i2c_ctrl);
}
also here, the brackets are not needed.
rc = i2c_wait_complete(regs);
if (rc == 0) {
len--;
buffer++;
start_time_tx = get_timer(0);
} else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
printf("Timed out. i2c write Failed\n");
return 1;
return -ETIMEDOUT
}
- }
- return 0;
+}
+static int ca_i2c_probe_chip(struct udevice *bus, uint chip_addr,
uint chip_flags)
+{
- struct ca_i2c *priv = dev_get_priv(bus);
- int ret;
- u32 tmp;
- /* Try to read the first location of the chip */
- ret = ca_i2c_read(priv->regs, chip_addr, 0, 1, (uchar *)&tmp, 1);
- if (ret)
ca_i2c_init(priv->regs);
- return ret;
+}
+static int ca_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs) +{
- struct ca_i2c *priv = dev_get_priv(bus);
- int ret;
- debug("i2c_xfer: %d messages\n", nmsgs);
- for (; nmsgs > 0; nmsgs--, msg++) {
debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
if (msg->flags & I2C_M_RD) {
ret = ca_i2c_read(priv->regs, msg->addr, 0, 0,
msg->buf, msg->len);
} else {
ret = ca_i2c_write(priv->regs, msg->addr, 0, 0,
msg->buf, msg->len);
}
here too, brackets are not needed.
if (ret) {
debug("i2c_write: error sending\n");
wrong printf when you did a read.
return -EREMOTEIO;
}
- }
- return 0;
+}
+static const struct dm_i2c_ops ca_i2c_ops = {
- .xfer = ca_i2c_xfer,
- .probe_chip = ca_i2c_probe_chip,
- .set_bus_speed = ca_i2c_set_bus_speed,
- .get_bus_speed = ca_i2c_get_bus_speed,
+};
+static const struct udevice_id ca_i2c_ids[] = {
- { .compatible = "cortina,ca-i2c", },
- { }
+};
+static int ca_i2c_probe(struct udevice *bus) +{
- struct ca_i2c *priv = dev_get_priv(bus);
- ca_i2c_init(priv->regs);
- return 0;
+}
+static int ca_i2c_ofdata_to_platdata(struct udevice *bus) +{
- struct ca_i2c *priv = dev_get_priv(bus);
- priv->regs = map_sysmem(dev_read_addr(bus), sizeof(struct i2c_regs));
- if (!priv->regs) {
printf("I2C: base address is invalid\n");
return -EINVAL;
- }
- return 0;
+}
+U_BOOT_DRIVER(i2c_cortina) = {
- .name = "i2c_cortina",
- .id = UCLASS_I2C,
- .of_match = ca_i2c_ids,
- .ofdata_to_platdata = ca_i2c_ofdata_to_platdata,
- .probe = ca_i2c_probe,
- .priv_auto_alloc_size = sizeof(struct ca_i2c),
- .ops = &ca_i2c_ops,
- .flags = DM_FLAG_PRE_RELOC,
+}; diff --git a/drivers/i2c/i2c-cortina.h b/drivers/i2c/i2c-cortina.h new file mode 100644 index 0000000..f1c3dc7 --- /dev/null +++ b/drivers/i2c/i2c-cortina.h @@ -0,0 +1,92 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/*
- (C) Copyright 2019
- Cortina Access, <www.cortina-access.com>
- */
+#ifndef __CA_I2C_H_ +#define __CA_I2C_H_
+#if !defined(__ASSEMBLER__) && !defined(__ASSEMBLY__) +struct i2c_regs {
- u32 i2c_cfg;
- u32 i2c_ctrl;
- u32 i2c_txr;
- u32 i2c_rxr;
- u32 i2c_ack;
- u32 i2c_ie0;
- u32 i2c_int0;
- u32 i2c_ie1;
- u32 i2c_int1;
- u32 i2c_stat;
+};
+union ca_biw_cfg {
- struct biw_cfg {
u32 core_en : 1;
u32 biw_soft_reset : 1;
u32 busywait_en : 1;
u32 stretch_en : 1;
u32 arb_en : 1;
u32 clksync_en : 1;
u32 rsrvd1 : 2;
u32 spike_cnt : 4;
u32 rsrvd2 : 4;
u32 prer : 16;
- } bf;
- unsigned int wrd;
+};
+union ca_biw_ctrl {
- struct biw_ctrl {
u32 biwdone : 1;
u32 rsrvd1 : 2;
u32 ack_in : 1;
u32 write : 1;
u32 read : 1;
u32 stop : 1;
u32 start : 1;
u32 rsrvd2 : 24;
- } bf;
- unsigned int wrd;
+};
+union ca_biw_ack {
- struct biw_ack {
u32 al :1;
u32 biw_busy :1;
u32 ack_out :1;
u32 rsrvd1 :29;
- } bf;
- unsigned int wrd;
+}; +#endif /* !__ASSEMBLER__*/
+struct ca_i2c {
- struct i2c_regs *regs;
- unsigned int speed;
+};
+#define I2C_CMD_WT 0 +#define I2C_CMD_RD 1
+#define BIW_CTRL_DONE BIT(0) +#define BIW_CTRL_ACK_IN BIT(3) +#define BIW_CTRL_WRITE BIT(4) +#define BIW_CTRL_READ BIT(5) +#define BIW_CTRL_STOP BIT(6) +#define BIW_CTRL_START BIT(7)
+#define I2C_BYTE_TO (CONFIG_SYS_HZ / 500) +#define I2C_STOPDET_TO (CONFIG_SYS_HZ / 500) +#define I2C_BYTE_TO_BB (10)
+#define IC_SPEED_MODE_STANDARD 1 +#define IC_SPEED_MODE_FAST 2 +#define IC_SPEED_MODE_MAX 3
+#define I2C_MAX_SPEED 1000000 +#define I2C_FAST_SPEED 400000 +#define I2C_STANDARD_SPEED 100000
Not needed, could you please use:
https://gitlab.denx.de/u-boot/u-boot/-/blob/master/include/i2c.h#L44
+#endif /* __CA_I2C_H_ */
Thanks!
bye, Heiko

Add I2C board support for Cortina Access Presidio Engineering Board
Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Heiko Schocher hs@denx.de ---
Changes in v3: None Changes in v2: None
configs/cortina_presidio-asic-emmc_defconfig | 3 +++ 1 file changed, 3 insertions(+)
diff --git a/configs/cortina_presidio-asic-emmc_defconfig b/configs/cortina_presidio-asic-emmc_defconfig index e10008a..e45e23c 100644 --- a/configs/cortina_presidio-asic-emmc_defconfig +++ b/configs/cortina_presidio-asic-emmc_defconfig @@ -10,6 +10,7 @@ CONFIG_SHOW_BOOT_PROGRESS=y CONFIG_BOOTDELAY=3 CONFIG_BOARD_EARLY_INIT_R=y CONFIG_SYS_PROMPT="G3#" +CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_PART=y CONFIG_CMD_WDT=y @@ -24,6 +25,8 @@ CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard" # CONFIG_NET is not set CONFIG_DM=y CONFIG_CORTINA_GPIO=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_CA=y CONFIG_DM_MMC=y CONFIG_MMC_DW=y CONFIG_MMC_DW_CORTINA=y

From: Jway Lin jway.lin@cortina-access.com
Add Cortina Access LED controller support for CAxxxx SOCs
Signed-off-by: Jway Lin jway.lin@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
MAINTAINERS | 2 + drivers/led/Kconfig | 8 ++ drivers/led/Makefile | 1 + drivers/led/led_cortina.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 319 insertions(+) create mode 100644 drivers/led/led_cortina.c
diff --git a/MAINTAINERS b/MAINTAINERS index b147faa..24a2655 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -183,6 +183,7 @@ F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h +F: drivers/led/led_cortina.c
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -676,6 +677,7 @@ F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h +F: drivers/led/led_cortina.c
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 6675934..cc87fbf 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -35,6 +35,14 @@ config LED_BCM6858 This option enables support for LEDs connected to the BCM6858 HW has blinking capabilities and up to 32 LEDs can be controlled.
+config LED_CORTINA + bool "LED Support for Cortina Access CAxxxx SoCs" + depends on LED && (CORTINA_PLATFORM) + help + This option enables support for LEDs connected to the Cortina + Access CAxxxx SOCs. + + config LED_BLINK bool "Support LED blinking" depends on LED diff --git a/drivers/led/Makefile b/drivers/led/Makefile index 3654dd3..8e3ae7f 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o +obj-$(CONFIG_LED_CORTINA) += led_cortina.o diff --git a/drivers/led/led_cortina.c b/drivers/led/led_cortina.c new file mode 100644 index 0000000..53435e8 --- /dev/null +++ b/drivers/led/led_cortina.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * Copyright (C) 2020 Cortina-Access + * Author: Jway Lin jway.lin@cortina-access.com + * + */ + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <linux/compat.h> + +#define CORTINA_LED_NUM 16 + +#define BIT(nr) (1UL << (nr)) + +#define cortina_LED_CONTROL 0x00 +#define cortina_LED_CONFIG_0 0x04 +#define cortina_LED_CONFIG_1 0x08 +#define cortina_LED_CONFIG_2 0x0c +#define cortina_LED_CONFIG_3 0x10 +#define cortina_LED_CONFIG_4 0x14 +#define cortina_LED_CONFIG_5 0x18 +#define cortina_LED_CONFIG_6 0x1c +#define cortina_LED_CONFIG_7 0x20 +#define cortina_LED_CONFIG_8 0x24 +#define cortina_LED_CONFIG_9 0x28 +#define cortina_LED_CONFIG_10 0x2c +#define cortina_LED_CONFIG_11 0x30 +#define cortina_LED_CONFIG_12 0x34 +#define cortina_LED_CONFIG_13 0x38 +#define cortina_LED_CONFIG_14 0x3c +#define cortina_LED_CONFIG_15 0x40 + +#define cortina_LED_MAX_HW_BLINK 127 +#define cortina_LED_MAX_COUNT CORTINA_LED_NUM +#define cortina_LED_MAX_PORT 8 + +/* LED_CONTROL fields */ +#define cortina_LED_BLINK_RATE1_OFFSET 0 +#define cortina_LED_BLINK_RATE1_MASK 0xFF +#define cortina_LED_BLINK_RATE2_OFFSET 8 +#define cortina_LED_BLINK_RATE2_MASK 0xFF +#define cortina_LED_CLK_TEST BIT(16) +#define cortina_LED_CLK_POLARITY BIT(17) +#define cortina_LED_CLK_TEST_MODE BIT(16) +#define cortina_LED_CLK_TEST_RX_TEST BIT(30) +#define cortina_LED_CLK_TEST_TX_TEST BIT(31) + +/* LED_CONFIG fields */ +#define cortina_LED_EVENT_ON_OFFSET 0 +#define cortina_LED_EVENT_ON_MASK 0x7 +#define cortina_LED_EVENT_BLINK_OFFSET 3 +#define cortina_LED_EVENT_BLINK_MASK 0x7 +#define cortina_LED_EVENT_OFF_OFFSET 6 +#define cortina_LED_EVENT_OFF_MASK 0x7 +#define cortina_LED_OFF_ON_OFFSET 9 +#define cortina_LED_OFF_ON_MASK 0x3 +#define cortina_LED_PORT_OFFSET 11 +#define cortina_LED_PORT_MASK 0x7 +#define cortina_LED_OFF_VAL BIT(14) +#define cortina_LED_SW_EVENT BIT(15) +#define cortina_LED_BLINK_SEL BIT(16) + +struct cortina_led_cfg { + void __iomem *regs; + spinlock_t *lock; /* protect LED resource access */ + int idx; + bool active_low; + + int off_event; + int blink_event; + int on_event; + int port; + int blink; + int enable; +}; + +struct cortina_led_top_cfg { + void __iomem *regs; + u16 blink_rate1; + u16 blink_rate2; +}; + +static struct cortina_led_top_cfg glb_led_ctrl; + +static void cortina_led_write(void __iomem *reg, unsigned long data) +{ + writel(data, reg); +} + +static unsigned long cortina_led_read(void __iomem *reg) +{ + return readl(reg); +} + +static enum led_state_t cortina_led_get_state(struct udevice *dev) +{ + struct cortina_led_cfg *priv = dev_get_priv(dev); + enum led_state_t state = LEDST_OFF; + u32 val; + + val = readl(priv->regs); + + if (val & cortina_LED_SW_EVENT) + state = LEDST_ON; + + return state; +} + +static int cortina_led_set_state(struct udevice *dev, enum led_state_t state) +{ + u32 val; + struct cortina_led_cfg *priv = dev_get_priv(dev); + + val = readl(priv->regs); + + switch (state) { + case LEDST_OFF: + val &= ~cortina_LED_SW_EVENT; + val &= ~(cortina_LED_OFF_ON_MASK << cortina_LED_OFF_ON_OFFSET); + val |= 0x3 << cortina_LED_OFF_ON_OFFSET; + cortina_led_write(priv->regs, val); + break; + case LEDST_ON: + val |= cortina_LED_SW_EVENT; + val &= ~(cortina_LED_OFF_ON_MASK << cortina_LED_OFF_ON_OFFSET); + val |= 0x1 << cortina_LED_OFF_ON_OFFSET; + cortina_led_write(priv->regs, val); + break; + case LEDST_TOGGLE: + if (cortina_led_get_state(dev) == LEDST_OFF) + return cortina_led_set_state(dev, LEDST_ON); + else + return cortina_led_set_state(dev, LEDST_OFF); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static const struct led_ops cortina_led_ops = { + .get_state = cortina_led_get_state, + .set_state = cortina_led_set_state, +}; + +static int cortina_led_probe(struct udevice *dev) +{ + struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev); + void __iomem *regs; + u32 reg_value, val; + u16 rate1, rate2; + struct cortina_led_cfg *priv = dev_get_priv(dev); + unsigned int pin; + u32 blink, port, off_event, blink_event, on_event; + + /* Top-level LED node */ + if (!uc_plat->label) { + regs = dev_remap_addr(dev); + + if (!regs) + return -EINVAL; + + glb_led_ctrl.regs = regs; + + reg_value = 0; + reg_value |= cortina_LED_CLK_POLARITY; + + rate1 = dev_read_u32_default(dev, "cortina, blink_rate1", 256); + rate2 = dev_read_u32_default(dev, "cortina, blink_rate2", 512); + + val = rate1 / 16 - 1; + glb_led_ctrl.blink_rate1 = val > cortina_LED_MAX_HW_BLINK ? + cortina_LED_MAX_HW_BLINK : val; + reg_value |= (glb_led_ctrl.blink_rate1 & cortina_LED_BLINK_RATE1_MASK) + << cortina_LED_BLINK_RATE1_OFFSET; + + val = rate2 / 16 - 1; + glb_led_ctrl.blink_rate2 = val > cortina_LED_MAX_HW_BLINK ? + cortina_LED_MAX_HW_BLINK : val; + reg_value |= (glb_led_ctrl.blink_rate2 & cortina_LED_BLINK_RATE2_MASK) + << cortina_LED_BLINK_RATE2_OFFSET; + + cortina_led_write(glb_led_ctrl.regs, reg_value); + } else { + regs = dev_remap_addr(dev_get_parent(dev)); + + if (!regs) + return -EINVAL; + + pin = dev_read_u32_default(dev, "pin", cortina_LED_MAX_COUNT); + + if (pin >= cortina_LED_MAX_COUNT) + return -EINVAL; + + priv->regs = regs + 4 + (pin * 4); + + val = cortina_led_read(priv->regs); + + if (dev_read_bool(dev, "active-low")) { + priv->active_low = true; + val |= cortina_LED_OFF_VAL; + } else { + priv->active_low = false; + val &= ~cortina_LED_OFF_VAL; + } + + blink = dev_read_u32_default(dev, "blink-sel", 0); + + if (blink == 0) { + priv->blink = 0; + val &= ~cortina_LED_BLINK_SEL; + } else if (blink == 1) { + priv->blink = 1; + val |= cortina_LED_BLINK_SEL; + } + + //Todo : cortina_led_config(); + off_event = dev_read_u32_default(dev, "off-event", 3); + val &= ~(cortina_LED_EVENT_OFF_MASK << cortina_LED_EVENT_OFF_OFFSET); + if (off_event != 3) { + priv->off_event = off_event; + val |= BIT(off_event) << cortina_LED_EVENT_OFF_OFFSET; + } + + blink_event = dev_read_u32_default(dev, "blink-event", 3); + val &= ~(cortina_LED_EVENT_BLINK_MASK << + cortina_LED_EVENT_BLINK_OFFSET); + + if (blink_event != 3) { + priv->blink_event = blink_event; + val |= BIT(blink_event) << cortina_LED_EVENT_BLINK_OFFSET; + } + + on_event = dev_read_u32_default(dev, "on-event", 3); + val &= ~(cortina_LED_EVENT_ON_MASK << cortina_LED_EVENT_ON_OFFSET); + if (on_event != 3) { + priv->on_event = on_event; + val |= BIT(on_event) << cortina_LED_EVENT_ON_OFFSET; + } + + port = dev_read_u32_default(dev, "port", 0); + priv->port = port; + val &= ~(cortina_LED_PORT_MASK << cortina_LED_PORT_OFFSET); + val |= port << cortina_LED_PORT_OFFSET; + + priv->enable = 0; + + /* force off */ + val &= ~(cortina_LED_OFF_ON_MASK << cortina_LED_OFF_ON_OFFSET); + val |= 0x3 << cortina_LED_OFF_ON_OFFSET; + + cortina_led_write(priv->regs, val); + } + + return 0; +} + +static int cortina_led_bind(struct udevice *parent) +{ + ofnode node; + + dev_for_each_subnode(node, parent) { + struct led_uc_plat *uc_plat; + struct udevice *dev; + const char *label; + int ret; + + label = ofnode_read_string(node, "label"); + if (!label) { + debug("%s: node %s has no label\n", __func__, + ofnode_get_name(node)); + return -EINVAL; + } + + ret = device_bind_driver_to_node(parent, "ca-leds", + ofnode_get_name(node), + node, &dev); + if (ret) + return ret; + uc_plat = dev_get_uclass_platdata(dev); + uc_plat->label = label; + } + + return 0; +} + +static const struct udevice_id ca_led_ids[] = { + { .compatible = "cortina,ca-leds" }, + { /* sentinel */ } +}; + +U_BOOT_DRIVER(cortina_led) = { + .name = "ca-leds", + .id = UCLASS_LED, + .of_match = ca_led_ids, + .bind = cortina_led_bind, + .probe = cortina_led_probe, + .priv_auto_alloc_size = sizeof(struct cortina_led_cfg), + .ops = &cortina_led_ops, +};

Hi Alex,
On Thu, 19 Mar 2020 at 18:57, Alex Nemirovsky alex.nemirovsky@cortina-access.com wrote:
From: Jway Lin jway.lin@cortina-access.com
Add Cortina Access LED controller support for CAxxxx SOCs
Signed-off-by: Jway Lin jway.lin@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
MAINTAINERS | 2 + drivers/led/Kconfig | 8 ++ drivers/led/Makefile | 1 + drivers/led/led_cortina.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 319 insertions(+) create mode 100644 drivers/led/led_cortina.c
diff --git a/MAINTAINERS b/MAINTAINERS index b147faa..24a2655 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -183,6 +183,7 @@ F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h +F: drivers/led/led_cortina.c
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -676,6 +677,7 @@ F: drivers/serial/serial_cortina.c F: drivers/mmc/ca_dw_mmc.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h +F: drivers/led/led_cortina.c
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/led/Kconfig b/drivers/led/Kconfig index 6675934..cc87fbf 100644 --- a/drivers/led/Kconfig +++ b/drivers/led/Kconfig @@ -35,6 +35,14 @@ config LED_BCM6858 This option enables support for LEDs connected to the BCM6858 HW has blinking capabilities and up to 32 LEDs can be controlled.
+config LED_CORTINA
bool "LED Support for Cortina Access CAxxxx SoCs"
depends on LED && (CORTINA_PLATFORM)
help
This option enables support for LEDs connected to the Cortina
Access CAxxxx SOCs.
config LED_BLINK bool "Support LED blinking" depends on LED diff --git a/drivers/led/Makefile b/drivers/led/Makefile index 3654dd3..8e3ae7f 100644 --- a/drivers/led/Makefile +++ b/drivers/led/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_LED_BCM6328) += led_bcm6328.o obj-$(CONFIG_LED_BCM6358) += led_bcm6358.o obj-$(CONFIG_LED_BCM6858) += led_bcm6858.o obj-$(CONFIG_$(SPL_)LED_GPIO) += led_gpio.o +obj-$(CONFIG_LED_CORTINA) += led_cortina.o diff --git a/drivers/led/led_cortina.c b/drivers/led/led_cortina.c new file mode 100644 index 0000000..53435e8 --- /dev/null +++ b/drivers/led/led_cortina.c @@ -0,0 +1,308 @@ +// SPDX-License-Identifier: GPL-2.0+
+/*
- Copyright (C) 2020 Cortina-Access
- Author: Jway Lin jway.lin@cortina-access.com
- */
+#include <common.h> +#include <dm.h> +#include <errno.h> +#include <led.h> +#include <asm/io.h> +#include <dm/lists.h> +#include <linux/compat.h>
+#define CORTINA_LED_NUM 16
+#define BIT(nr) (1UL << (nr))
That is already defined somewhere
+#define cortina_LED_CONTROL 0x00
Drop the cortina_ prefix if you can.. It should be upper case. Since this is local to the file it just adds code.
+#define cortina_LED_CONFIG_0 0x04 +#define cortina_LED_CONFIG_1 0x08 +#define cortina_LED_CONFIG_2 0x0c +#define cortina_LED_CONFIG_3 0x10 +#define cortina_LED_CONFIG_4 0x14 +#define cortina_LED_CONFIG_5 0x18 +#define cortina_LED_CONFIG_6 0x1c +#define cortina_LED_CONFIG_7 0x20 +#define cortina_LED_CONFIG_8 0x24 +#define cortina_LED_CONFIG_9 0x28 +#define cortina_LED_CONFIG_10 0x2c +#define cortina_LED_CONFIG_11 0x30 +#define cortina_LED_CONFIG_12 0x34 +#define cortina_LED_CONFIG_13 0x38 +#define cortina_LED_CONFIG_14 0x3c +#define cortina_LED_CONFIG_15 0x40
What are all those used for?
+#define cortina_LED_MAX_HW_BLINK 127 +#define cortina_LED_MAX_COUNT CORTINA_LED_NUM +#define cortina_LED_MAX_PORT 8
+/* LED_CONTROL fields */ +#define cortina_LED_BLINK_RATE1_OFFSET 0 +#define cortina_LED_BLINK_RATE1_MASK 0xFF
Lower-case hex
+#define cortina_LED_BLINK_RATE2_OFFSET 8 +#define cortina_LED_BLINK_RATE2_MASK 0xFF +#define cortina_LED_CLK_TEST BIT(16) +#define cortina_LED_CLK_POLARITY BIT(17) +#define cortina_LED_CLK_TEST_MODE BIT(16) +#define cortina_LED_CLK_TEST_RX_TEST BIT(30) +#define cortina_LED_CLK_TEST_TX_TEST BIT(31)
+/* LED_CONFIG fields */ +#define cortina_LED_EVENT_ON_OFFSET 0 +#define cortina_LED_EVENT_ON_MASK 0x7 +#define cortina_LED_EVENT_BLINK_OFFSET 3 +#define cortina_LED_EVENT_BLINK_MASK 0x7 +#define cortina_LED_EVENT_OFF_OFFSET 6 +#define cortina_LED_EVENT_OFF_MASK 0x7 +#define cortina_LED_OFF_ON_OFFSET 9 +#define cortina_LED_OFF_ON_MASK 0x3 +#define cortina_LED_PORT_OFFSET 11 +#define cortina_LED_PORT_MASK 0x7 +#define cortina_LED_OFF_VAL BIT(14) +#define cortina_LED_SW_EVENT BIT(15) +#define cortina_LED_BLINK_SEL BIT(16)
Need a struct comment
+struct cortina_led_cfg {
void __iomem *regs;
spinlock_t *lock; /* protect LED resource access */
int idx;
bool active_low;
int off_event;
int blink_event;
int on_event;
int port;
int blink;
int enable;
+};
+struct cortina_led_top_cfg {
here too
void __iomem *regs;
u16 blink_rate1;
u16 blink_rate2;
+};
+static struct cortina_led_top_cfg glb_led_ctrl;
You are not allowed to use BSS in driver model. It's not clear why you have two structures as there are no comments. Can you not combine them?
+static void cortina_led_write(void __iomem *reg, unsigned long data) +{
writel(data, reg);
+}
+static unsigned long cortina_led_read(void __iomem *reg) +{
return readl(reg);
+}
+static enum led_state_t cortina_led_get_state(struct udevice *dev) +{
struct cortina_led_cfg *priv = dev_get_priv(dev);
enum led_state_t state = LEDST_OFF;
u32 val;
val = readl(priv->regs);
if (val & cortina_LED_SW_EVENT)
state = LEDST_ON;
return state;
+}
+static int cortina_led_set_state(struct udevice *dev, enum led_state_t state) +{
u32 val;
struct cortina_led_cfg *priv = dev_get_priv(dev);
val = readl(priv->regs);
switch (state) {
case LEDST_OFF:
val &= ~cortina_LED_SW_EVENT;
val &= ~(cortina_LED_OFF_ON_MASK << cortina_LED_OFF_ON_OFFSET);
val |= 0x3 << cortina_LED_OFF_ON_OFFSET;
cortina_led_write(priv->regs, val);
break;
case LEDST_ON:
val |= cortina_LED_SW_EVENT;
val &= ~(cortina_LED_OFF_ON_MASK << cortina_LED_OFF_ON_OFFSET);
val |= 0x1 << cortina_LED_OFF_ON_OFFSET;
cortina_led_write(priv->regs, val);
break;
case LEDST_TOGGLE:
if (cortina_led_get_state(dev) == LEDST_OFF)
return cortina_led_set_state(dev, LEDST_ON);
else
return cortina_led_set_state(dev, LEDST_OFF);
break;
default:
return -EINVAL;
}
return 0;
+}
+static const struct led_ops cortina_led_ops = {
.get_state = cortina_led_get_state,
.set_state = cortina_led_set_state,
+};
+static int cortina_led_probe(struct udevice *dev) +{
struct led_uc_plat *uc_plat = dev_get_uclass_platdata(dev);
void __iomem *regs;
u32 reg_value, val;
u16 rate1, rate2;
struct cortina_led_cfg *priv = dev_get_priv(dev);
unsigned int pin;
u32 blink, port, off_event, blink_event, on_event;
/* Top-level LED node */
if (!uc_plat->label) {
regs = dev_remap_addr(dev);
if (!regs)
return -EINVAL;
glb_led_ctrl.regs = regs;
reg_value = 0;
reg_value |= cortina_LED_CLK_POLARITY;
rate1 = dev_read_u32_default(dev, "cortina, blink_rate1", 256);
rate2 = dev_read_u32_default(dev, "cortina, blink_rate2", 512);
Strictly speaking you should read your platdata into a plat struct in your ofdata_to_platdata method.
Also do you have a binding file for this? I think we sholld use hyphen for properties instead of underscore.
val = rate1 / 16 - 1;
glb_led_ctrl.blink_rate1 = val > cortina_LED_MAX_HW_BLINK ?
cortina_LED_MAX_HW_BLINK : val;
reg_value |= (glb_led_ctrl.blink_rate1 & cortina_LED_BLINK_RATE1_MASK)
<< cortina_LED_BLINK_RATE1_OFFSET;
val = rate2 / 16 - 1;
glb_led_ctrl.blink_rate2 = val > cortina_LED_MAX_HW_BLINK ?
cortina_LED_MAX_HW_BLINK : val;
reg_value |= (glb_led_ctrl.blink_rate2 & cortina_LED_BLINK_RATE2_MASK)
<< cortina_LED_BLINK_RATE2_OFFSET;
cortina_led_write(glb_led_ctrl.regs, reg_value);
} else {
regs = dev_remap_addr(dev_get_parent(dev));
if (!regs)
return -EINVAL;
pin = dev_read_u32_default(dev, "pin", cortina_LED_MAX_COUNT);
if (pin >= cortina_LED_MAX_COUNT)
return -EINVAL;
priv->regs = regs + 4 + (pin * 4);
val = cortina_led_read(priv->regs);
if (dev_read_bool(dev, "active-low")) {
priv->active_low = true;
val |= cortina_LED_OFF_VAL;
} else {
priv->active_low = false;
val &= ~cortina_LED_OFF_VAL;
}
blink = dev_read_u32_default(dev, "blink-sel", 0);
if (blink == 0) {
priv->blink = 0;
val &= ~cortina_LED_BLINK_SEL;
} else if (blink == 1) {
priv->blink = 1;
val |= cortina_LED_BLINK_SEL;
}
//Todo : cortina_led_config();
off_event = dev_read_u32_default(dev, "off-event", 3);
val &= ~(cortina_LED_EVENT_OFF_MASK << cortina_LED_EVENT_OFF_OFFSET);
if (off_event != 3) {
priv->off_event = off_event;
val |= BIT(off_event) << cortina_LED_EVENT_OFF_OFFSET;
}
blink_event = dev_read_u32_default(dev, "blink-event", 3);
val &= ~(cortina_LED_EVENT_BLINK_MASK <<
cortina_LED_EVENT_BLINK_OFFSET);
if (blink_event != 3) {
priv->blink_event = blink_event;
val |= BIT(blink_event) << cortina_LED_EVENT_BLINK_OFFSET;
}
on_event = dev_read_u32_default(dev, "on-event", 3);
val &= ~(cortina_LED_EVENT_ON_MASK << cortina_LED_EVENT_ON_OFFSET);
if (on_event != 3) {
priv->on_event = on_event;
val |= BIT(on_event) << cortina_LED_EVENT_ON_OFFSET;
}
port = dev_read_u32_default(dev, "port", 0);
priv->port = port;
val &= ~(cortina_LED_PORT_MASK << cortina_LED_PORT_OFFSET);
val |= port << cortina_LED_PORT_OFFSET;
priv->enable = 0;
/* force off */
val &= ~(cortina_LED_OFF_ON_MASK << cortina_LED_OFF_ON_OFFSET);
val |= 0x3 << cortina_LED_OFF_ON_OFFSET;
cortina_led_write(priv->regs, val);
}
funny indent?
return 0;
+}
+static int cortina_led_bind(struct udevice *parent) +{
ofnode node;
dev_for_each_subnode(node, parent) {
struct led_uc_plat *uc_plat;
struct udevice *dev;
const char *label;
int ret;
label = ofnode_read_string(node, "label");
if (!label) {
debug("%s: node %s has no label\n", __func__,
ofnode_get_name(node));
return -EINVAL;
}
ret = device_bind_driver_to_node(parent, "ca-leds",
ofnode_get_name(node),
node, &dev);
Does device_bind_ofnode() help here?
if (ret)
return ret;
uc_plat = dev_get_uclass_platdata(dev);
uc_plat->label = label;
}
return 0;
+}
+static const struct udevice_id ca_led_ids[] = {
{ .compatible = "cortina,ca-leds" },
{ /* sentinel */ }
+};
+U_BOOT_DRIVER(cortina_led) = {
.name = "ca-leds",
.id = UCLASS_LED,
.of_match = ca_led_ids,
.bind = cortina_led_bind,
.probe = cortina_led_probe,
.priv_auto_alloc_size = sizeof(struct cortina_led_cfg),
.ops = &cortina_led_ops,
+};
2.7.4
Regards, Simon

From: Jway Lin jway.lin@cortina-access.com
Add LED support for Cortina Access Presidio Engineering Board
Signed-off-by: Jway Lin jway.lin@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Simon Glass sjg@chromium.org ---
Changes in v3: None Changes in v2: None
arch/arm/dts/ca-presidio-engboard.dts | 31 ++++++++++++++++++++++++++++ configs/cortina_presidio-asic-emmc_defconfig | 2 ++ 2 files changed, 33 insertions(+)
diff --git a/arch/arm/dts/ca-presidio-engboard.dts b/arch/arm/dts/ca-presidio-engboard.dts index c03dacc..ae897e8 100644 --- a/arch/arm/dts/ca-presidio-engboard.dts +++ b/arch/arm/dts/ca-presidio-engboard.dts @@ -66,4 +66,35 @@ spi-max-frequency = <108000000>; }; }; + + leds: led-controller@f43200f0 { + compatible = "cortina,ca-leds"; + reg = <0x0 0xf43200f0 0x40>; + + cortina,blink_rate1 = <256>; + cortina,blink_rate2 = <512>; + + led@0 { + pin = <0>; + active-low; + blink-sel =<0>; + port = <0>; + off-event = <0>; + label = "led0"; + }; + + led@1 { + pin = <1>; + active-low; + blink-sel =<1>; + label = "led1"; + }; + + led@2 { + pin = <2>; + active-low; + label = "led2"; + }; + + }; }; diff --git a/configs/cortina_presidio-asic-emmc_defconfig b/configs/cortina_presidio-asic-emmc_defconfig index e45e23c..3c6bd6b 100644 --- a/configs/cortina_presidio-asic-emmc_defconfig +++ b/configs/cortina_presidio-asic-emmc_defconfig @@ -27,6 +27,8 @@ CONFIG_DM=y CONFIG_CORTINA_GPIO=y CONFIG_DM_I2C=y CONFIG_SYS_I2C_CA=y +CONFIG_LED=y +CONFIG_LED_CORTINA=y CONFIG_DM_MMC=y CONFIG_MMC_DW=y CONFIG_MMC_DW_CORTINA=y

On Thu, 19 Mar 2020 at 18:57, Alex Nemirovsky alex.nemirovsky@cortina-access.com wrote:
From: Jway Lin jway.lin@cortina-access.com
Add LED support for Cortina Access Presidio Engineering Board
Signed-off-by: Jway Lin jway.lin@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Simon Glass sjg@chromium.org
Changes in v3: None Changes in v2: None
arch/arm/dts/ca-presidio-engboard.dts | 31 ++++++++++++++++++++++++++++ configs/cortina_presidio-asic-emmc_defconfig | 2 ++ 2 files changed, 33 insertions(+)
Reviewed-by: Simon Glass sjg@chromium.org

From: Pengpeng Chen pengpeng.chen@cortina-access.com
Add SPI Flash controller driver for Cortina Access CAxxxx SoCs
Signed-off-by: Pengpeng Chen pengpeng.chen@cortina-access.com Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Jagan Teki jagan@amarulasolutions.com
---
Changes in v3: - Fixup syntax issues related to checkpatch.pl cleanup
Changes in v2: None
MAINTAINERS | 2 + drivers/spi/Kconfig | 8 + drivers/spi/Makefile | 1 + drivers/spi/ca_sflash.c | 576 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 587 insertions(+) create mode 100644 drivers/spi/ca_sflash.c
diff --git a/MAINTAINERS b/MAINTAINERS index 24a2655..8509779 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -184,6 +184,7 @@ F: drivers/mmc/ca_dw_mmc.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h F: drivers/led/led_cortina.c +F: drivers/spi/ca_sflash.c
ARM/CZ.NIC TURRIS MOX SUPPORT M: Marek Behun marek.behun@nic.cz @@ -678,6 +679,7 @@ F: drivers/mmc/ca_dw_mmc.c F: drivers/i2c/i2c-cortina.c F: drivers/i2c/i2c-cortina.h F: drivers/led/led_cortina.c +F: drivers/spi/ca_sflash.c
MIPS MSCC M: Gregory CLEMENT gregory.clement@bootlin.com diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4166c61..8a244f1 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -106,6 +106,14 @@ config BCMSTB_SPI be used to access the SPI flash on platforms embedding this Broadcom SPI core.
+config CORTINA_SFLASH + bool "Cortina-Access Serial Flash controller driver" + depends on DM_SPI && SPI_MEM + help + Enable the Cortina-Access Serial Flash controller driver. This driver + can be used to access the SPI NOR/NAND flash on platforms embedding this + Cortina-Access IP core. + config CADENCE_QSPI bool "Cadence QSPI driver" help diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index 52462e1..32b98b4 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_BCM63XX_SPI) += bcm63xx_spi.o obj-$(CONFIG_BCMSTB_SPI) += bcmstb_spi.o obj-$(CONFIG_CADENCE_QSPI) += cadence_qspi.o cadence_qspi_apb.o obj-$(CONFIG_CF_SPI) += cf_spi.o +obj-$(CONFIG_CORTINA_SFLASH) += ca_sflash.o obj-$(CONFIG_DAVINCI_SPI) += davinci_spi.o obj-$(CONFIG_DESIGNWARE_SPI) += designware_spi.o obj-$(CONFIG_EXYNOS_SPI) += exynos_spi.o diff --git a/drivers/spi/ca_sflash.c b/drivers/spi/ca_sflash.c new file mode 100644 index 0000000..0709650 --- /dev/null +++ b/drivers/spi/ca_sflash.c @@ -0,0 +1,576 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Cortina SPI-FLASH Controller + * + * Copyright (C) 2020 Cortina Access Inc. All Rights Reserved. + * + * Author: PengPeng Chen pengpeng.chen@cortina-access.com + */ + +#include <common.h> +#include <malloc.h> +#include <clk.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <linux/compat.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/ioport.h> +#include <linux/sizes.h> +#include <spi.h> +#include <spi-mem.h> +#include <reset.h> + +DECLARE_GLOBAL_DATA_PTR; + +struct ca_sflash_regs { + u32 idr; /* 0x00:Flash word ID Register */ + u32 tc; /* 0x04:Flash Timeout Counter Register */ + u32 sr; /* 0x08:Flash Status Register */ + u32 tr; /* 0x0C:Flash Type Register */ + u32 asr; /* 0x10:Flash ACCESS START/BUSY Register */ + u32 isr; /* 0x14:Flash Interrupt Status Register */ + u32 imr; /* 0x18:Flash Interrupt Mask Register */ + u32 fcr; /* 0x1C:NAND Flash FIFO Control Register */ + u32 ffsr; /* 0x20:Flash FIFO Status Register */ + u32 ffar; /* 0x24:Flash FIFO ADDRESS Register */ + u32 ffmar; /* 0x28:Flash FIFO MATCHING ADDRESS Register */ + u32 ffdr; /* 0x2C:Flash FIFO Data Register */ + u32 ar; /* 0x30:Serial Flash Access Register */ + u32 ear; /* 0x34:Serial Flash Extend Access Register */ + u32 adr; /* 0x38:Serial Flash ADdress Register */ + u32 dr; /* 0x3C:Serial Flash Data Register */ + u32 tmr; /* 0x40:Serial Flash Timing Register */ +}; + +/* + * FLASH_TYPE + */ +#define CA_FLASH_TR_PIN BIT(15) +#define CA_FLASH_TR_TYPE_MASK GENMASK(14, 12) +#define CA_FLASH_TR_TYPE(tp) (((tp) << 12) & CA_FLASH_TR_TYPE_MASK) +#define CA_FLASH_TR_WIDTH BIT(11) +#define CA_FLASH_TR_SIZE_MASK GENMASK(10, 9) +#define CA_FLASH_TR_SIZE(sz) (((sz) << 9) & CA_FLASH_TR_SIZE_MASK) + +/* + * FLASH_FLASH_ACCESS_START + */ +#define CA_FLASH_ASR_IND_START_EN BIT(1) +#define CA_FLASH_ASR_DMA_START_EN BIT(3) +#define CA_FLASH_ASR_WR_ACCESS_EN BIT(9) + +/* + * FLASH_FLASH_INTERRUPT + */ +#define CA_FLASH_ISR_REG_IRQ BIT(1) +#define CA_FLASH_ISR_FIFO_IRQ BIT(2) + +/* + * FLASH_SF_ACCESS + */ +#define CA_SF_AR_OPCODE_MASK GENMASK(7, 0) +#define CA_SF_AR_OPCODE(op) ((op) << 0 & CA_SF_AR_OPCODE_MASK) +#define CA_SF_AR_ACCODE_MASK GENMASK(11, 8) +#define CA_SF_AR_ACCODE(ac) (((ac) << 8) & CA_SF_AR_ACCODE_MASK) +#define CA_SF_AR_FORCE_TERM BIT(12) +#define CA_SF_AR_FORCE_BURST BIT(13) +#define CA_SF_AR_AUTO_MODE_EN BIT(15) +#define CA_SF_AR_CHIP_EN_ALT BIT(16) +#define CA_SF_AR_HI_SPEED_RD BIT(17) +#define CA_SF_AR_MIO_INF_DC BIT(24) +#define CA_SF_AR_MIO_INF_AC BIT(25) +#define CA_SF_AR_MIO_INF_CC BIT(26) +#define CA_SF_AR_DDR_MASK GENMASK(29, 28) +#define CA_SF_AR_DDR(ddr) (((ddr) << 28) & CA_SF_AR_DDR_MASK) +#define CA_SF_AR_MIO_INF_MASK GENMASK(31, 30) +#define CA_SF_AR_MIO_INF(io) (((io) << 30) & CA_SF_AR_MIO_INF_MASK) + +/* + * FLASH_SF_EXT_ACCESS + */ +#define CA_SF_EAR_OPCODE_MASK GENMASK(7, 0) +#define CA_SF_EAR_OPCODE(op) (((op) << 0) & CA_SF_EAR_OPCODE_MASK) +#define CA_SF_EAR_DATA_CNT_MASK GENMASK(20, 8) +#define CA_SF_EAR_DATA_CNT(cnt) (((cnt) << 8) & CA_SF_EAR_DATA_CNT_MASK) +#define CA_SF_EAR_DATA_CNT_MAX (4096) +#define CA_SF_EAR_ADDR_CNT_MASK GENMASK(23, 21) +#define CA_SF_EAR_ADDR_CNT(cnt) (((cnt) << 21) & CA_SF_EAR_ADDR_CNT_MASK) +#define CA_SF_EAR_ADDR_CNT_MAX (5) +#define CA_SF_EAR_DUMY_CNT_MASK GENMASK(29, 24) +#define CA_SF_EAR_DUMY_CNT(cnt) (((cnt) << 24) & CA_SF_EAR_DUMY_CNT_MASK) +#define CA_SF_EAR_DUMY_CNT_MAX (32) +#define CA_SF_EAR_DRD_CMD_EN BIT(31) + +/* + * FLASH_SF_ADDRESS + */ +#define CA_SF_ADR_REG_MASK GENMASK(31, 0) +#define CA_SF_ADR_REG(addr) (((addr) << 0) & CA_SF_ADR_REG_MASK) + +/* + * FLASH_SF_DATA + */ +#define CA_SF_DR_REG_MASK GENMASK(31, 0) +#define CA_SF_DR_REG(addr) (((addr) << 0) & CA_SF_DR_REG_MASK) + +/* + * FLASH_SF_TIMING + */ +#define CA_SF_TMR_IDLE_MASK GENMASK(7, 0) +#define CA_SF_TMR_IDLE(idle) (((idle) << 0) & CA_SF_TMR_IDLE_MASK) +#define CA_SF_TMR_HOLD_MASK GENMASK(15, 8) +#define CA_SF_TMR_HOLD(hold) (((hold) << 8) & CA_SF_TMR_HOLD_MASK) +#define CA_SF_TMR_SETUP_MASK GENMASK(23, 16) +#define CA_SF_TMR_SETUP(setup) (((setup) << 16) & CA_SF_TMR_SETUP_MASK) +#define CA_SF_TMR_CLK_MASK GENMASK(26, 24) +#define CA_SF_TMR_CLK(clk) (((clk) << 24) & CA_SF_TMR_CLK_MASK) + +#define CA_SFLASH_IND_WRITE 0 +#define CA_SFLASH_IND_READ 1 +#define CA_SFLASH_MEM_MAP 3 +#define CA_SFLASH_FIFO_TIMEOUT_US 30000 +#define CA_SFLASH_BUSY_TIMEOUT_US 40000 + +#define CA_SF_AC_OPCODE 0x00 +#define CA_SF_AC_OPCODE_1_DATA 0x01 +#define CA_SF_AC_OPCODE_2_DATA 0x02 +#define CA_SF_AC_OPCODE_3_DATA 0x03 +#define CA_SF_AC_OPCODE_4_DATA 0x04 +#define CA_SF_AC_OPCODE_3_ADDR 0x05 +#define CA_SF_AC_OPCODE_4_ADDR (CA_SF_AC_OPCODE_3_ADDR) +#define CA_SF_AC_OPCODE_3_ADDR_1_DATA 0x06 +#define CA_SF_AC_OPCODE_4_ADDR_1_DATA (CA_SF_AC_OPCODE_3_ADDR_1_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_2_DATA 0x07 +#define CA_SF_AC_OPCODE_4_ADDR_2_DATA (CA_SF_AC_OPCODE_3_ADDR_2_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_3_DATA 0x08 +#define CA_SF_AC_OPCODE_4_ADDR_3_DATA (CA_SF_AC_OPCODE_3_ADDR_3_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_4_DATA 0x09 +#define CA_SF_AC_OPCODE_4_ADDR_4_DATA (CA_SF_AC_OPCODE_3_ADDR_4_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_X_1_DATA 0x0A +#define CA_SF_AC_OPCODE_4_ADDR_X_1_DATA (CA_SF_AC_OPCODE_3_ADDR_X_1_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_X_2_DATA 0x0B +#define CA_SF_AC_OPCODE_4_ADDR_X_2_DATA (CA_SF_AC_OPCODE_3_ADDR_X_2_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_X_3_DATA 0x0C +#define CA_SF_AC_OPCODE_4_ADDR_X_3_DATA (CA_SF_AC_OPCODE_3_ADDR_X_3_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_X_4_DATA 0x0D +#define CA_SF_AC_OPCODE_4_ADDR_X_4_DATA (CA_SF_AC_OPCODE_3_ADDR_X_4_DATA << 2) +#define CA_SF_AC_OPCODE_3_ADDR_4X_1_DATA 0x0E +#define CA_SF_AC_OPCODE_4_ADDR_4X_1_DATA (CA_SF_AC_OPCODE_3_ADDR_4X_1_DATA << 2) +#define CA_SF_AC_OPCODE_EXTEND 0x0F + +#define CA_SF_ACCESS_MIO_SINGLE 0 +#define CA_SF_ACCESS_MIO_DUAL 1 +#define CA_SF_ACCESS_MIO_QUARD 2 + +enum access_type { + RD_ACCESS, + WR_ACCESS, +}; + +struct ca_sflash_priv { + struct ca_sflash_regs *regs; + u8 rx_width; + u8 tx_width; +}; + +/* + * This function doesn't do anything except help with debugging + */ +static int ca_sflash_claim_bus(struct udevice *dev) +{ + debug("%s:\n", __func__); + return 0; +} + +static int ca_sflash_release_bus(struct udevice *dev) +{ + debug("%s:\n", __func__); + return 0; +} + +static int ca_sflash_set_speed(struct udevice *dev, uint speed) +{ + debug("%s:\n", __func__); + return 0; +} + +static int ca_sflash_set_mode(struct udevice *dev, uint mode) +{ + struct ca_sflash_priv *priv = dev_get_priv(dev); + + if (mode & SPI_RX_QUAD) + priv->rx_width = 4; + else if (mode & SPI_RX_DUAL) + priv->rx_width = 2; + else + priv->rx_width = 1; + + if (mode & SPI_TX_QUAD) + priv->tx_width = 4; + else if (mode & SPI_TX_DUAL) + priv->tx_width = 2; + else + priv->tx_width = 1; + + debug("%s: mode=%d, rx_width=%d, tx_width=%d\n", + __func__, mode, priv->rx_width, priv->tx_width); + + return 0; +} + +static int _ca_sflash_wait_for_not_busy(struct ca_sflash_priv *priv) +{ + u32 asr; + + if (readl_poll_timeout(&priv->regs->asr, asr, + !(asr & CA_FLASH_ASR_IND_START_EN), + CA_SFLASH_BUSY_TIMEOUT_US)) { + pr_err("busy timeout (stat:%#x)\n", asr); + return -1; + } + + return 0; +} + +static int _ca_sflash_wait_cmd(struct ca_sflash_priv *priv, + enum access_type type) +{ + if (type == WR_ACCESS) { + /* Enable write access and start the sflash indirect access */ + clrsetbits_le32(&priv->regs->asr, GENMASK(31, 0), + CA_FLASH_ASR_WR_ACCESS_EN + | CA_FLASH_ASR_IND_START_EN); + } else if (type == RD_ACCESS) { + /* Start the sflash indirect access */ + clrsetbits_le32(&priv->regs->asr, GENMASK(31, 0), + CA_FLASH_ASR_IND_START_EN); + } else { + printf("%s: !error access type.\n", __func__); + return -1; + } + + /* Wait til the action(rd/wr) completed */ + return _ca_sflash_wait_for_not_busy(priv); +} + +static int _ca_sflash_read(struct ca_sflash_priv *priv, + u8 *buf, unsigned int data_len) +{ + u32 reg_data; + int len; + + len = data_len; + while (len >= 4) { + if (_ca_sflash_wait_cmd(priv, RD_ACCESS)) + return -1; + reg_data = readl(&priv->regs->dr); + *buf++ = reg_data & 0xFF; + *buf++ = (reg_data >> 8) & 0xFF; + *buf++ = (reg_data >> 16) & 0xFF; + *buf++ = (reg_data >> 24) & 0xFF; + len -= 4; + debug("%s: reg_data=%#08x\n", + __func__, reg_data); + } + + if (len > 0) { + if (_ca_sflash_wait_cmd(priv, RD_ACCESS)) + return -1; + reg_data = readl(&priv->regs->dr); + debug("%s: reg_data=%#08x\n", + __func__, reg_data); + } + + switch (len) { + case 3: + *buf++ = reg_data & 0xFF; + *buf++ = (reg_data >> 8) & 0xFF; + *buf++ = (reg_data >> 16) & 0xFF; + break; + case 2: + *buf++ = reg_data & 0xFF; + *buf++ = (reg_data >> 8) & 0xFF; + break; + case 1: + *buf++ = reg_data & 0xFF; + break; + case 0: + break; + default: + printf("%s: error data_length %d!\n", __func__, len); + } + + return 0; +} + +static int _ca_sflash_mio_set(struct ca_sflash_priv *priv, + u8 width) +{ + if (width == 4) { + setbits_le32(&priv->regs->ar, + CA_SF_AR_MIO_INF_DC + | CA_SF_AR_MIO_INF(CA_SF_ACCESS_MIO_QUARD) + | CA_SF_AR_FORCE_BURST); + } else if (width == 2) { + setbits_le32(&priv->regs->ar, + CA_SF_AR_MIO_INF_DC + | CA_SF_AR_MIO_INF(CA_SF_ACCESS_MIO_DUAL) + | CA_SF_AR_FORCE_BURST); + } else if (width == 1) { + setbits_le32(&priv->regs->ar, + CA_SF_AR_MIO_INF(CA_SF_ACCESS_MIO_SINGLE) + | CA_SF_AR_FORCE_BURST); + } else { + printf("%s: error rx/tx width %d!\n", __func__, width); + return -1; + } + + return 0; +} + +static int _ca_sflash_write(struct ca_sflash_priv *priv, + u8 *buf, unsigned int data_len) +{ + u32 reg_data; + int len; + + len = data_len; + while (len > 0) { + reg_data = buf[0] + | (buf[1] << 8) + | (buf[2] << 16) + | (buf[3] << 24); + + debug("%s: reg_data=%#08x\n", + __func__, reg_data); + /* Fill data */ + clrsetbits_le32(&priv->regs->dr, GENMASK(31, 0), reg_data); + + if (_ca_sflash_wait_cmd(priv, WR_ACCESS)) + return -1; + + len -= 4; + buf += 4; + } + + return 0; +} + +static int _ca_sflash_access_data(struct ca_sflash_priv *priv, + struct spi_mem_op *op) +{ + int total_cnt; + unsigned int len; + unsigned int data_cnt = op->data.nbytes; + u64 addr_offset = op->addr.val; + u8 addr_cnt = op->addr.nbytes; + u8 *data_buf = NULL; + u8 *buf = NULL; + + if (op->data.dir == SPI_MEM_DATA_IN) + data_buf = (u8 *)op->data.buf.in; + else + data_buf = (u8 *)op->data.buf.out; + + if (data_cnt > CA_SF_EAR_DATA_CNT_MAX) + buf = malloc(CA_SF_EAR_DATA_CNT_MAX); + else + buf = malloc(data_cnt); + + total_cnt = data_cnt; + while (total_cnt > 0) { + /* Fill address */ + if (addr_cnt > 0) + clrsetbits_le32(&priv->regs->adr, + GENMASK(31, 0), (u32)addr_offset); + + if (total_cnt > CA_SF_EAR_DATA_CNT_MAX) { + len = CA_SF_EAR_DATA_CNT_MAX; + addr_offset += CA_SF_EAR_DATA_CNT_MAX; + /* Clear start bit before next bulk read */ + clrbits_le32(&priv->regs->asr, GENMASK(31, 0)); + } else { + len = total_cnt; + } + + memset(buf, 0, len); + if (op->data.dir == SPI_MEM_DATA_IN) { + if (_ca_sflash_read(priv, buf, len)) + break; + memcpy(data_buf, buf, len); + } else { + memcpy(buf, data_buf, len); + if (_ca_sflash_write(priv, buf, len)) + break; + } + + total_cnt -= len; + data_buf += len; + } + if (buf) + free(buf); + + return total_cnt > 0 ? -1 : 0; +} + +static int _ca_sflash_issue_cmd(struct ca_sflash_priv *priv, + struct spi_mem_op *op, u8 opcode) +{ + u8 dummy_cnt = op->dummy.nbytes; + u8 addr_cnt = op->addr.nbytes; + u8 mio_width; + unsigned int data_cnt = op->data.nbytes; + u64 addr_offset = op->addr.val; + + /* Set the access register */ + clrsetbits_le32(&priv->regs->ar, + GENMASK(31, 0), CA_SF_AR_ACCODE(opcode)); + + if (opcode == CA_SF_AC_OPCODE_EXTEND) { /* read_data, write_data */ + if (data_cnt > 6) { + if (op->data.dir == SPI_MEM_DATA_IN) + mio_width = priv->rx_width; + else + mio_width = priv->tx_width; + if (_ca_sflash_mio_set(priv, mio_width)) + return -1; + } + debug("%s: FLASH ACCESS reg=%#08x\n", + __func__, readl(&priv->regs->ar)); + + /* Use command in extend_access register */ + clrsetbits_le32(&priv->regs->ear, + GENMASK(31, 0), CA_SF_EAR_OPCODE(op->cmd.opcode) + | CA_SF_EAR_DUMY_CNT(dummy_cnt * 8 - 1) + | CA_SF_EAR_ADDR_CNT(addr_cnt - 1) + | CA_SF_EAR_DATA_CNT(4 - 1) + | CA_SF_EAR_DRD_CMD_EN); + debug("%s: FLASH EXT ACCESS reg=%#08x\n", + __func__, readl(&priv->regs->ear)); + + if (_ca_sflash_access_data(priv, op)) + return -1; + } else { /* reset_op, wr_enable, wr_disable */ + setbits_le32(&priv->regs->ar, + CA_SF_AR_OPCODE(op->cmd.opcode)); + debug("%s: FLASH ACCESS reg=%#08x\n", + __func__, readl(&priv->regs->ar)); + + if (opcode == CA_SF_AC_OPCODE_4_ADDR) { /* erase_op */ + /* Configure address length */ + if (addr_cnt > 3) /* 4 Bytes address */ + setbits_le32(&priv->regs->tr, + CA_FLASH_TR_SIZE(2)); + else /* 3 Bytes address */ + clrbits_le32(&priv->regs->tr, + CA_FLASH_TR_SIZE_MASK); + + /* Fill address */ + if (addr_cnt > 0) + clrsetbits_le32(&priv->regs->adr, + GENMASK(31, 0), + (u32)addr_offset); + } + + if (_ca_sflash_wait_cmd(priv, RD_ACCESS)) + return -1; + } + /* elapse 10us before issuing any other command */ + udelay(10); + + return 0; +} + +static int ca_sflash_exec_op(struct spi_slave *slave, + const struct spi_mem_op *op) +{ + struct ca_sflash_priv *priv = dev_get_priv(slave->dev->parent); + u8 opcode; + + debug("%s: cmd:%#02x addr.val:%#llx addr.len:%#x data.len:%#x data.dir:%#x\n", + __func__, op->cmd.opcode, op->addr.val, + op->addr.nbytes, op->data.nbytes, op->data.dir); + + if (op->data.nbytes == 0 && op->addr.nbytes == 0) { + opcode = CA_SF_AC_OPCODE; + } else if (op->data.nbytes == 0 && op->addr.nbytes > 0) { + opcode = CA_SF_AC_OPCODE_4_ADDR; + } else if (op->data.nbytes > 0) { + opcode = CA_SF_AC_OPCODE_EXTEND; + } else { + printf("%s: can't support cmd.opcode:(%#02x) type currently!\n", + __func__, op->cmd.opcode); + return -1; + } + + return _ca_sflash_issue_cmd(priv, (struct spi_mem_op *)op, opcode); +} + +static void ca_sflash_init(struct ca_sflash_priv *priv) +{ + /* Set FLASH_TYPE as serial flash, value: 0x0400*/ + clrsetbits_le32(&priv->regs->tr, + GENMASK(31, 0), CA_FLASH_TR_SIZE(2)); + debug("%s: FLASH_TYPE reg=%#x\n", + __func__, readl(&priv->regs->tr)); + + /* Minimize flash timing, value: 0x07010101 */ + clrsetbits_le32(&priv->regs->tmr, + GENMASK(31, 0), + CA_SF_TMR_CLK(0x07) + | CA_SF_TMR_SETUP(0x01) + | CA_SF_TMR_HOLD(0x01) + | CA_SF_TMR_IDLE(0x01)); + debug("%s: FLASH_TIMING reg=%#x\n", + __func__, readl(&priv->regs->tmr)); +} + +static int ca_sflash_probe(struct udevice *dev) +{ + struct ca_sflash_priv *priv = dev_get_priv(dev); + struct resource res; + int ret; + + /* Map the registers */ + ret = dev_read_resource_byname(dev, "sflash-regs", &res); + if (ret) { + dev_err(dev, "can't get regs base addresses(ret = %d)!\n", ret); + return ret; + } + priv->regs = devm_ioremap(dev, res.start, resource_size(&res)); + if (IS_ERR(priv->regs)) + return PTR_ERR(priv->regs); + + ca_sflash_init(priv); + + printf("SFLASH: Controller probed ready\n"); + return 0; +} + +static const struct spi_controller_mem_ops ca_sflash_mem_ops = { + .exec_op = ca_sflash_exec_op, +}; + +static const struct dm_spi_ops ca_sflash_ops = { + .claim_bus = ca_sflash_claim_bus, + .release_bus = ca_sflash_release_bus, + .set_speed = ca_sflash_set_speed, + .set_mode = ca_sflash_set_mode, + .mem_ops = &ca_sflash_mem_ops, +}; + +static const struct udevice_id ca_sflash_ids[] = { + {.compatible = "cortina,ca-sflash"}, + {} +}; + +U_BOOT_DRIVER(ca_sflash) = { + .name = "ca_sflash", + .id = UCLASS_SPI, + .of_match = ca_sflash_ids, + .ops = &ca_sflash_ops, + .priv_auto_alloc_size = sizeof(struct ca_sflash_priv), + .probe = ca_sflash_probe, +};

Add SPI NAND and NOR support for Cortina Access Presidio Engineering Board
Signed-off-by: Alex Nemirovsky alex.nemirovsky@cortina-access.com CC: Jagan Teki jagan@amarulasolutions.com ---
Changes in v3: None Changes in v2: None
arch/arm/dts/ca-presidio-engboard.dts | 8 ++-- board/cortina/presidio-asic/presidio.c | 16 ++++++- configs/cortina_presidio-asic-spi-nand_defconfig | 48 +++++++++++++++++++ configs/cortina_presidio-asic-spi-nor_defconfig | 59 ++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 6 deletions(-) create mode 100644 configs/cortina_presidio-asic-spi-nand_defconfig create mode 100644 configs/cortina_presidio-asic-spi-nor_defconfig
diff --git a/arch/arm/dts/ca-presidio-engboard.dts b/arch/arm/dts/ca-presidio-engboard.dts index ae897e8..8c73eb6 100644 --- a/arch/arm/dts/ca-presidio-engboard.dts +++ b/arch/arm/dts/ca-presidio-engboard.dts @@ -55,15 +55,13 @@ };
sflash: sflash-controller@f4324000 { - #address-cells = <2>; - #size-cells = <1>; compatible = "cortina,ca-sflash"; reg = <0x0 0xf4324000 0x50>; reg-names = "sflash-regs"; flash@0 { - compatible = "jedec,spi-nor"; - spi-rx-bus-width = <1>; - spi-max-frequency = <108000000>; + compatible = "spi-nand", "jedec,spi-nor"; + spi-rx-bus-width = <4>; + spi-tx-bus-width = <4>; }; };
diff --git a/board/cortina/presidio-asic/presidio.c b/board/cortina/presidio-asic/presidio.c index b4fa01f..d547b60 100644 --- a/board/cortina/presidio-asic/presidio.c +++ b/board/cortina/presidio-asic/presidio.c @@ -14,7 +14,7 @@ #include <asm/psci.h> #include <cpu_func.h> #include <asm/armv8/mmu.h> - +#include <dm/uclass.h> DECLARE_GLOBAL_DATA_PTR;
#define CA_PERIPH_BASE 0xE0000000UL @@ -70,9 +70,23 @@ static noinline int invoke_psci_fn_smc(u64 function_id, u64 arg0, u64 arg1, return function_id; }
+#ifdef CONFIG_CORTINA_SFLASH +static int init_sflash(void) +{ + struct udevice *dev; + + uclass_first_device(UCLASS_SPI, &dev); + + return 0; +} +#endif + int board_early_init_r(void) { dcache_disable(); +#ifdef CONFIG_CORTINA_SFLASH + init_sflash(); +#endif return 0; }
diff --git a/configs/cortina_presidio-asic-spi-nand_defconfig b/configs/cortina_presidio-asic-spi-nand_defconfig new file mode 100644 index 0000000..515ad22 --- /dev/null +++ b/configs/cortina_presidio-asic-spi-nand_defconfig @@ -0,0 +1,48 @@ +CONFIG_ARM=y +# CONFIG_SYS_ARCH_TIMER is not set +CONFIG_TARGET_PRESIDIO_ASIC=y +CONFIG_SYS_TEXT_BASE=0x04000000 +CONFIG_ENV_SIZE=0x20000 +CONFIG_DM_GPIO=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_IDENT_STRING="Presidio-SoC" +CONFIG_SHOW_BOOT_PROGRESS=y +CONFIG_BOOTDELAY=3 +CONFIG_LOGLEVEL=7 +CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_SYS_PROMPT="G3#" +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_MTD=y +CONFIG_CMD_PART=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_SPI=y +CONFIG_CMD_WDT=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_TIMER=y +CONFIG_CMD_SMC=y +CONFIG_CMD_EXT2=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_MTDPARTS=y +CONFIG_OF_CONTROL=y +CONFIG_OF_LIVE=y +CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard" +# CONFIG_NET is not set +CONFIG_DM=y +CONFIG_CORTINA_GPIO=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_CA=y +CONFIG_DM_MMC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_CORTINA=y +CONFIG_MTD=y +CONFIG_DM_MTD=y +CONFIG_MTD_SPI_NAND=y +CONFIG_DM_SPI_FLASH=y +CONFIG_DM_SERIAL=y +CONFIG_CORTINA_UART=y +CONFIG_SPI=y +CONFIG_DM_SPI=y +CONFIG_CORTINA_SFLASH=y +CONFIG_WDT=y +CONFIG_WDT_CORTINA=y diff --git a/configs/cortina_presidio-asic-spi-nor_defconfig b/configs/cortina_presidio-asic-spi-nor_defconfig new file mode 100644 index 0000000..d7ecec3 --- /dev/null +++ b/configs/cortina_presidio-asic-spi-nor_defconfig @@ -0,0 +1,59 @@ +CONFIG_ARM=y +# CONFIG_SYS_ARCH_TIMER is not set +CONFIG_TARGET_PRESIDIO_ASIC=y +CONFIG_SYS_TEXT_BASE=0x04000000 +CONFIG_ENV_SIZE=0x20000 +CONFIG_DM_GPIO=y +CONFIG_NR_DRAM_BANKS=1 +CONFIG_IDENT_STRING="Presidio-SoC" +CONFIG_SHOW_BOOT_PROGRESS=y +CONFIG_BOOTDELAY=3 +CONFIG_BOARD_EARLY_INIT_R=y +CONFIG_SYS_PROMPT="G3#" +CONFIG_CMD_I2C=y +CONFIG_CMD_MMC=y +CONFIG_CMD_MTD=y +CONFIG_CMD_PART=y +CONFIG_CMD_SF_TEST=y +CONFIG_CMD_SPI=y +CONFIG_CMD_WDT=y +CONFIG_CMD_CACHE=y +CONFIG_CMD_TIMER=y +CONFIG_CMD_SMC=y +CONFIG_CMD_EXT2=y +CONFIG_CMD_EXT4=y +CONFIG_CMD_MTDPARTS=y +CONFIG_OF_CONTROL=y +CONFIG_OF_LIVE=y +CONFIG_DEFAULT_DEVICE_TREE="ca-presidio-engboard" +# CONFIG_NET is not set +CONFIG_DM=y +CONFIG_CORTINA_GPIO=y +CONFIG_DM_I2C=y +CONFIG_SYS_I2C_CA=y +CONFIG_LED=y +CONFIG_LED_CORTINA=y +CONFIG_DM_MMC=y +CONFIG_MMC_DW=y +CONFIG_MMC_DW_CORTINA=y +CONFIG_MTD=y +CONFIG_DM_MTD=y +CONFIG_DM_SPI_FLASH=y +CONFIG_SPI_FLASH_ATMEL=y +CONFIG_SPI_FLASH_EON=y +CONFIG_SPI_FLASH_GIGADEVICE=y +CONFIG_SPI_FLASH_ISSI=y +CONFIG_SPI_FLASH_MACRONIX=y +CONFIG_SPI_FLASH_SPANSION=y +CONFIG_SPI_FLASH_STMICRO=y +CONFIG_SPI_FLASH_SST=y +CONFIG_SPI_FLASH_WINBOND=y +CONFIG_SPI_FLASH_XMC=y +CONFIG_SPI_FLASH_MTD=y +CONFIG_DM_SERIAL=y +CONFIG_CORTINA_UART=y +CONFIG_SPI=y +CONFIG_DM_SPI=y +CONFIG_CORTINA_SFLASH=y +CONFIG_WDT=y +CONFIG_WDT_CORTINA=y
participants (5)
-
Alex Nemirovsky
-
Alex Nemirovsky
-
Heiko Schocher
-
Jaehoon Chung
-
Simon Glass