summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile2
-rw-r--r--drivers/block/Kconfig5
-rw-r--r--drivers/block/Makefile8
-rw-r--r--drivers/block/ahci-uclass.c (renamed from drivers/block/disk-uclass.c)6
-rw-r--r--drivers/block/blk-uclass.c361
-rw-r--r--drivers/block/blk_legacy.c261
-rw-r--r--drivers/block/sandbox.c103
-rw-r--r--drivers/block/sandbox_scsi.c29
-rw-r--r--drivers/block/sata_sandbox.c33
-rw-r--r--drivers/block/sym53c8xx.c2
-rw-r--r--drivers/block/systemace.c110
-rw-r--r--drivers/core/device-remove.c2
-rw-r--r--drivers/core/device.c10
-rw-r--r--drivers/core/lists.c4
-rw-r--r--drivers/ddr/marvell/a38x/ddr3_init.c29
-rw-r--r--drivers/dfu/dfu_mmc.c13
-rw-r--r--drivers/gpio/74x164_gpio.c193
-rw-r--r--drivers/gpio/Kconfig30
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/gpio-uclass.c30
-rw-r--r--drivers/gpio/intel_broadwell_gpio.c10
-rw-r--r--drivers/gpio/omap_gpio.c11
-rw-r--r--drivers/gpio/pca953x_gpio.c351
-rw-r--r--drivers/gpio/pic32_gpio.c10
-rw-r--r--drivers/gpio/rk_gpio.c11
-rw-r--r--drivers/gpio/s5p_gpio.c11
-rw-r--r--drivers/i2c/Kconfig25
-rw-r--r--drivers/i2c/designware_i2c.c9
-rw-r--r--drivers/i2c/fsl_i2c.c379
-rw-r--r--drivers/i2c/i2c-cdns.c132
-rw-r--r--drivers/i2c/muxes/Kconfig10
-rw-r--r--drivers/i2c/muxes/Makefile1
-rw-r--r--drivers/i2c/muxes/pca954x.c79
-rw-r--r--drivers/i2c/mvtwsi.c62
-rw-r--r--drivers/mmc/Kconfig11
-rw-r--r--drivers/mmc/Makefile12
-rw-r--r--drivers/mmc/mmc-uclass.c106
-rw-r--r--drivers/mmc/mmc.c311
-rw-r--r--drivers/mmc/mmc_legacy.c108
-rw-r--r--drivers/mmc/mmc_private.h33
-rw-r--r--drivers/mmc/mmc_write.c18
-rw-r--r--drivers/mmc/omap_hsmmc.c1
-rw-r--r--drivers/mmc/pic32_sdhci.c7
-rw-r--r--drivers/mmc/rockchip_dw_mmc.c1
-rw-r--r--drivers/mmc/sandbox_mmc.c134
-rw-r--r--drivers/mmc/socfpga_dw_mmc.c1
-rw-r--r--drivers/mmc/uniphier-sd.c1
-rw-r--r--drivers/mmc/zynq_sdhci.c1
-rw-r--r--drivers/mtd/Kconfig7
-rw-r--r--drivers/mtd/Makefile1
-rw-r--r--drivers/mtd/pic32_flash.c444
-rw-r--r--drivers/pci/pci.c4
-rw-r--r--drivers/pinctrl/Kconfig18
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/ath79/Makefile6
-rw-r--r--drivers/pinctrl/ath79/pinctrl_ar933x.c136
-rw-r--r--drivers/pinctrl/ath79/pinctrl_qca953x.c156
-rw-r--r--drivers/serial/Kconfig18
-rw-r--r--drivers/serial/Makefile1
-rw-r--r--drivers/serial/mcfuart.c188
-rw-r--r--drivers/serial/serial_ar933x.c243
-rw-r--r--drivers/spi/Kconfig9
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/ath79_spi.c228
-rw-r--r--drivers/spi/omap3_spi.c23
-rw-r--r--drivers/spi/soft_spi.c55
-rw-r--r--drivers/spi/spi-uclass.c28
-rw-r--r--drivers/usb/common/Makefile1
-rw-r--r--drivers/usb/common/common.c40
-rw-r--r--drivers/video/tegra.c7
70 files changed, 4126 insertions, 569 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 6900097e79..99dd07fc76 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -36,6 +36,8 @@ obj-$(CONFIG_SPL_WATCHDOG_SUPPORT) += watchdog/
obj-$(CONFIG_SPL_USB_HOST_SUPPORT) += usb/host/
obj-$(CONFIG_OMAP_USB_PHY) += usb/phy/
obj-$(CONFIG_SPL_SATA_SUPPORT) += block/
+obj-$(CONFIG_SPL_USB_HOST_SUPPORT) += block/
+obj-$(CONFIG_SPL_MMC_SUPPORT) += block/
else
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index fcc9ccdd7f..80eea84dc2 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -9,10 +9,9 @@ config BLK
be partitioned into several areas, called 'partitions' in U-Boot.
A filesystem can be placed in each partition.
-config DISK
- bool "Support disk controllers with driver model"
+config AHCI
+ bool "Support SATA controllers with driver model"
depends on DM
- default y if DM
help
This enables a uclass for disk controllers in U-Boot. Various driver
types can use this, such as AHCI/SATA. It does not provide any standard
diff --git a/drivers/block/Makefile b/drivers/block/Makefile
index a43492f208..436b79f981 100644
--- a/drivers/block/Makefile
+++ b/drivers/block/Makefile
@@ -7,7 +7,11 @@
obj-$(CONFIG_BLK) += blk-uclass.o
-obj-$(CONFIG_DISK) += disk-uclass.o
+ifndef CONFIG_BLK
+obj-y += blk_legacy.o
+endif
+
+obj-$(CONFIG_AHCI) += ahci-uclass.o
obj-$(CONFIG_SCSI_AHCI) += ahci.o
obj-$(CONFIG_DWC_AHSATA) += dwc_ahsata.o
obj-$(CONFIG_FSL_SATA) += fsl_sata.o
@@ -22,7 +26,7 @@ obj-$(CONFIG_SATA_MV) += sata_mv.o
obj-$(CONFIG_SATA_SIL3114) += sata_sil3114.o
obj-$(CONFIG_SATA_SIL) += sata_sil.o
obj-$(CONFIG_IDE_SIL680) += sil680.o
-obj-$(CONFIG_SANDBOX) += sandbox.o
+obj-$(CONFIG_SANDBOX) += sandbox.o sandbox_scsi.o sata_sandbox.o
obj-$(CONFIG_SCSI_SYM53C8XX) += sym53c8xx.o
obj-$(CONFIG_SYSTEMACE) += systemace.o
obj-$(CONFIG_BLOCK_CACHE) += blkcache.o
diff --git a/drivers/block/disk-uclass.c b/drivers/block/ahci-uclass.c
index d665b3505a..7b8c32699f 100644
--- a/drivers/block/disk-uclass.c
+++ b/drivers/block/ahci-uclass.c
@@ -8,7 +8,7 @@
#include <common.h>
#include <dm.h>
-UCLASS_DRIVER(disk) = {
- .id = UCLASS_DISK,
- .name = "disk",
+UCLASS_DRIVER(ahci) = {
+ .id = UCLASS_AHCI,
+ .name = "ahci",
};
diff --git a/drivers/block/blk-uclass.c b/drivers/block/blk-uclass.c
index 617db226a2..6ba1026f58 100644
--- a/drivers/block/blk-uclass.c
+++ b/drivers/block/blk-uclass.c
@@ -11,6 +11,315 @@
#include <dm/device-internal.h>
#include <dm/lists.h>
+static const char *if_typename_str[IF_TYPE_COUNT] = {
+ [IF_TYPE_IDE] = "ide",
+ [IF_TYPE_SCSI] = "scsi",
+ [IF_TYPE_ATAPI] = "atapi",
+ [IF_TYPE_USB] = "usb",
+ [IF_TYPE_DOC] = "doc",
+ [IF_TYPE_MMC] = "mmc",
+ [IF_TYPE_SD] = "sd",
+ [IF_TYPE_SATA] = "sata",
+ [IF_TYPE_HOST] = "host",
+ [IF_TYPE_SYSTEMACE] = "ace",
+};
+
+static enum uclass_id if_type_uclass_id[IF_TYPE_COUNT] = {
+ [IF_TYPE_IDE] = UCLASS_INVALID,
+ [IF_TYPE_SCSI] = UCLASS_INVALID,
+ [IF_TYPE_ATAPI] = UCLASS_INVALID,
+ [IF_TYPE_USB] = UCLASS_MASS_STORAGE,
+ [IF_TYPE_DOC] = UCLASS_INVALID,
+ [IF_TYPE_MMC] = UCLASS_MMC,
+ [IF_TYPE_SD] = UCLASS_INVALID,
+ [IF_TYPE_SATA] = UCLASS_AHCI,
+ [IF_TYPE_HOST] = UCLASS_ROOT,
+ [IF_TYPE_SYSTEMACE] = UCLASS_INVALID,
+};
+
+static enum if_type if_typename_to_iftype(const char *if_typename)
+{
+ int i;
+
+ for (i = 0; i < IF_TYPE_COUNT; i++) {
+ if (if_typename_str[i] &&
+ !strcmp(if_typename, if_typename_str[i]))
+ return i;
+ }
+
+ return IF_TYPE_UNKNOWN;
+}
+
+static enum uclass_id if_type_to_uclass_id(enum if_type if_type)
+{
+ return if_type_uclass_id[if_type];
+}
+
+struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum)
+{
+ struct blk_desc *desc;
+ struct udevice *dev;
+ int ret;
+
+ ret = blk_get_device(if_type, devnum, &dev);
+ if (ret)
+ return NULL;
+ desc = dev_get_uclass_platdata(dev);
+
+ return desc;
+}
+
+/*
+ * This function is complicated with driver model. We look up the interface
+ * name in a local table. This gives us an interface type which we can match
+ * against the uclass of the block device's parent.
+ */
+struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
+{
+ enum uclass_id uclass_id;
+ enum if_type if_type;
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ if_type = if_typename_to_iftype(if_typename);
+ if (if_type == IF_TYPE_UNKNOWN) {
+ debug("%s: Unknown interface type '%s'\n", __func__,
+ if_typename);
+ return NULL;
+ }
+ uclass_id = if_type_to_uclass_id(if_type);
+ if (uclass_id == UCLASS_INVALID) {
+ debug("%s: Unknown uclass for interface type'\n",
+ if_typename_str[if_type]);
+ return NULL;
+ }
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return NULL;
+ uclass_foreach_dev(dev, uc) {
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
+ if_type, devnum, dev->name, desc->if_type, desc->devnum);
+ if (desc->devnum != devnum)
+ continue;
+
+ /* Find out the parent device uclass */
+ if (device_get_uclass_id(dev->parent) != uclass_id) {
+ debug("%s: parent uclass %d, this dev %d\n", __func__,
+ device_get_uclass_id(dev->parent), uclass_id);
+ continue;
+ }
+
+ if (device_probe(dev))
+ return NULL;
+
+ debug("%s: Device desc %p\n", __func__, desc);
+ return desc;
+ }
+ debug("%s: No device found\n", __func__);
+
+ return NULL;
+}
+
+/**
+ * get_desc() - Get the block device descriptor for the given device number
+ *
+ * @if_type: Interface type
+ * @devnum: Device number (0 = first)
+ * @descp: Returns block device descriptor on success
+ * @return 0 on success, -ENODEV if there is no such device and no device
+ * with a higher device number, -ENOENT if there is no such device but there
+ * is one with a higher number, or other -ve on other error.
+ */
+static int get_desc(enum if_type if_type, int devnum, struct blk_desc **descp)
+{
+ bool found_more = false;
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ *descp = NULL;
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev(dev, uc) {
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ debug("%s: if_type=%d, devnum=%d: %s, %d, %d\n", __func__,
+ if_type, devnum, dev->name, desc->if_type, desc->devnum);
+ if (desc->if_type == if_type) {
+ if (desc->devnum == devnum) {
+ ret = device_probe(dev);
+ if (ret)
+ return ret;
+
+ } else if (desc->devnum > devnum) {
+ found_more = true;
+ }
+ }
+ }
+
+ return found_more ? -ENOENT : -ENODEV;
+}
+
+int blk_select_hwpart_devnum(enum if_type if_type, int devnum, int hwpart)
+{
+ struct udevice *dev;
+ int ret;
+
+ ret = blk_get_device(if_type, devnum, &dev);
+ if (ret)
+ return ret;
+
+ return blk_select_hwpart(dev, hwpart);
+}
+
+int blk_list_part(enum if_type if_type)
+{
+ struct blk_desc *desc;
+ int devnum, ok;
+ int ret;
+
+ for (ok = 0, devnum = 0;; ++devnum) {
+ ret = get_desc(if_type, devnum, &desc);
+ if (ret == -ENODEV)
+ break;
+ else if (ret)
+ continue;
+ if (desc->part_type != PART_TYPE_UNKNOWN) {
+ ++ok;
+ if (devnum)
+ putc('\n');
+ part_print(desc);
+ }
+ }
+ if (!ok)
+ return -ENODEV;
+
+ return 0;
+}
+
+int blk_print_part_devnum(enum if_type if_type, int devnum)
+{
+ struct blk_desc *desc;
+ int ret;
+
+ ret = get_desc(if_type, devnum, &desc);
+ if (ret)
+ return ret;
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ return -ENOENT;
+ part_print(desc);
+
+ return 0;
+}
+
+void blk_list_devices(enum if_type if_type)
+{
+ struct blk_desc *desc;
+ int ret;
+ int i;
+
+ for (i = 0;; ++i) {
+ ret = get_desc(if_type, i, &desc);
+ if (ret == -ENODEV)
+ break;
+ else if (ret)
+ continue;
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ continue; /* list only known devices */
+ printf("Device %d: ", i);
+ dev_print(desc);
+ }
+}
+
+int blk_print_device_num(enum if_type if_type, int devnum)
+{
+ struct blk_desc *desc;
+ int ret;
+
+ ret = get_desc(if_type, devnum, &desc);
+ if (ret)
+ return ret;
+ printf("\nIDE device %d: ", devnum);
+ dev_print(desc);
+
+ return 0;
+}
+
+int blk_show_device(enum if_type if_type, int devnum)
+{
+ struct blk_desc *desc;
+ int ret;
+
+ printf("\nDevice %d: ", devnum);
+ ret = get_desc(if_type, devnum, &desc);
+ if (ret == -ENODEV || ret == -ENOENT) {
+ printf("unknown device\n");
+ return -ENODEV;
+ }
+ if (ret)
+ return ret;
+ dev_print(desc);
+
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ return -ENOENT;
+
+ return 0;
+}
+
+ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start,
+ lbaint_t blkcnt, void *buffer)
+{
+ struct blk_desc *desc;
+ ulong n;
+ int ret;
+
+ ret = get_desc(if_type, devnum, &desc);
+ if (ret)
+ return ret;
+ n = blk_dread(desc, start, blkcnt, buffer);
+ if (IS_ERR_VALUE(n))
+ return n;
+
+ /* flush cache after read */
+ flush_cache((ulong)buffer, blkcnt * desc->blksz);
+
+ return n;
+}
+
+ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start,
+ lbaint_t blkcnt, const void *buffer)
+{
+ struct blk_desc *desc;
+ int ret;
+
+ ret = get_desc(if_type, devnum, &desc);
+ if (ret)
+ return ret;
+ return blk_dwrite(desc, start, blkcnt, buffer);
+}
+
+int blk_select_hwpart(struct udevice *dev, int hwpart)
+{
+ const struct blk_ops *ops = blk_get_ops(dev);
+
+ if (!ops)
+ return -ENOSYS;
+ if (!ops->select_hwpart)
+ return 0;
+
+ return ops->select_hwpart(dev, hwpart);
+}
+
+int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
+{
+ return blk_select_hwpart(desc->bdev, hwpart);
+}
+
int blk_first_device(int if_type, struct udevice **devp)
{
struct blk_desc *desc;
@@ -131,6 +440,26 @@ int blk_prepare_device(struct udevice *dev)
return 0;
}
+int blk_find_max_devnum(enum if_type if_type)
+{
+ struct udevice *dev;
+ int max_devnum = -ENODEV;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_BLK, &uc);
+ if (ret)
+ return ret;
+ uclass_foreach_dev(dev, uc) {
+ struct blk_desc *desc = dev_get_uclass_platdata(dev);
+
+ if (desc->if_type == if_type && desc->devnum > max_devnum)
+ max_devnum = desc->devnum;
+ }
+
+ return max_devnum;
+}
+
int blk_create_device(struct udevice *parent, const char *drv_name,
const char *name, int if_type, int devnum, int blksz,
lbaint_t size, struct udevice **devp)
@@ -139,6 +468,15 @@ int blk_create_device(struct udevice *parent, const char *drv_name,
struct udevice *dev;
int ret;
+ if (devnum == -1) {
+ ret = blk_find_max_devnum(if_type);
+ if (ret == -ENODEV)
+ devnum = 0;
+ else if (ret < 0)
+ return ret;
+ else
+ devnum = ret + 1;
+ }
ret = device_bind_driver(parent, drv_name, name, &dev);
if (ret)
return ret;
@@ -154,6 +492,29 @@ int blk_create_device(struct udevice *parent, const char *drv_name,
return 0;
}
+int blk_create_devicef(struct udevice *parent, const char *drv_name,
+ const char *name, int if_type, int devnum, int blksz,
+ lbaint_t size, struct udevice **devp)
+{
+ char dev_name[30], *str;
+ int ret;
+
+ snprintf(dev_name, sizeof(dev_name), "%s.%s", parent->name, name);
+ str = strdup(dev_name);
+ if (!str)
+ return -ENOMEM;
+
+ ret = blk_create_device(parent, drv_name, str, if_type, devnum,
+ blksz, size, devp);
+ if (ret) {
+ free(str);
+ return ret;
+ }
+ device_set_name_alloced(*devp);
+
+ return ret;
+}
+
int blk_unbind_all(int if_type)
{
struct uclass *uc;
diff --git a/drivers/block/blk_legacy.c b/drivers/block/blk_legacy.c
new file mode 100644
index 0000000000..7b90a8a6e1
--- /dev/null
+++ b/drivers/block/blk_legacy.c
@@ -0,0 +1,261 @@
+/*
+ * Copyright (C) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <linux/err.h>
+
+struct blk_driver *blk_driver_lookup_type(int if_type)
+{
+ struct blk_driver *drv = ll_entry_start(struct blk_driver, blk_driver);
+ const int n_ents = ll_entry_count(struct blk_driver, blk_driver);
+ struct blk_driver *entry;
+
+ for (entry = drv; entry != drv + n_ents; entry++) {
+ if (if_type == entry->if_type)
+ return entry;
+ }
+
+ /* Not found */
+ return NULL;
+}
+
+static struct blk_driver *blk_driver_lookup_typename(const char *if_typename)
+{
+ struct blk_driver *drv = ll_entry_start(struct blk_driver, blk_driver);
+ const int n_ents = ll_entry_count(struct blk_driver, blk_driver);
+ struct blk_driver *entry;
+
+ for (entry = drv; entry != drv + n_ents; entry++) {
+ if (!strcmp(if_typename, entry->if_typename))
+ return entry;
+ }
+
+ /* Not found */
+ return NULL;
+}
+
+/**
+ * get_desc() - Get the block device descriptor for the given device number
+ *
+ * @drv: Legacy block driver
+ * @devnum: Device number (0 = first)
+ * @descp: Returns block device descriptor on success
+ * @return 0 on success, -ENODEV if there is no such device, -ENOSYS if the
+ * driver does not provide a way to find a device, or other -ve on other
+ * error.
+ */
+static int get_desc(struct blk_driver *drv, int devnum, struct blk_desc **descp)
+{
+ if (drv->desc) {
+ if (devnum < 0 || devnum >= drv->max_devs)
+ return -ENODEV;
+ *descp = &drv->desc[devnum];
+ return 0;
+ }
+ if (!drv->get_dev)
+ return -ENOSYS;
+
+ return drv->get_dev(devnum, descp);
+}
+
+#ifdef HAVE_BLOCK_DEVICE
+int blk_list_part(enum if_type if_type)
+{
+ struct blk_driver *drv;
+ struct blk_desc *desc;
+ int devnum, ok;
+ bool first = true;
+
+ drv = blk_driver_lookup_type(if_type);
+ if (!drv)
+ return -ENOSYS;
+ for (ok = 0, devnum = 0; devnum < drv->max_devs; ++devnum) {
+ if (get_desc(drv, devnum, &desc))
+ continue;
+ if (desc->part_type != PART_TYPE_UNKNOWN) {
+ ++ok;
+ if (!first)
+ putc('\n');
+ part_print(desc);
+ first = false;
+ }
+ }
+ if (!ok)
+ return -ENODEV;
+
+ return 0;
+}
+
+int blk_print_part_devnum(enum if_type if_type, int devnum)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ int ret;
+
+ if (!drv)
+ return -ENOSYS;
+ ret = get_desc(drv, devnum, &desc);
+ if (ret)
+ return ret;
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ return -ENOENT;
+ part_print(desc);
+
+ return 0;
+}
+
+void blk_list_devices(enum if_type if_type)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ int i;
+
+ if (!drv)
+ return;
+ for (i = 0; i < drv->max_devs; ++i) {
+ if (get_desc(drv, i, &desc))
+ continue;
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ continue; /* list only known devices */
+ printf("Device %d: ", i);
+ dev_print(desc);
+ }
+}
+
+int blk_print_device_num(enum if_type if_type, int devnum)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ int ret;
+
+ if (!drv)
+ return -ENOSYS;
+ ret = get_desc(drv, devnum, &desc);
+ if (ret)
+ return ret;
+ printf("\n%s device %d: ", drv->if_typename, devnum);
+ dev_print(desc);
+
+ return 0;
+}
+
+int blk_show_device(enum if_type if_type, int devnum)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ int ret;
+
+ if (!drv)
+ return -ENOSYS;
+ printf("\nDevice %d: ", devnum);
+ if (devnum >= drv->max_devs) {
+ puts("unknown device\n");
+ return -ENODEV;
+ }
+ ret = get_desc(drv, devnum, &desc);
+ if (ret)
+ return ret;
+ dev_print(desc);
+
+ if (desc->type == DEV_TYPE_UNKNOWN)
+ return -ENOENT;
+
+ return 0;
+}
+#endif /* HAVE_BLOCK_DEVICE */
+
+struct blk_desc *blk_get_devnum_by_type(enum if_type if_type, int devnum)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+
+ if (!drv)
+ return NULL;
+
+ if (get_desc(drv, devnum, &desc))
+ return NULL;
+
+ return desc;
+}
+
+int blk_dselect_hwpart(struct blk_desc *desc, int hwpart)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(desc->if_type);
+
+ if (!drv)
+ return -ENOSYS;
+ if (drv->select_hwpart)
+ return drv->select_hwpart(desc, hwpart);
+
+ return 0;
+}
+
+struct blk_desc *blk_get_devnum_by_typename(const char *if_typename, int devnum)
+{
+ struct blk_driver *drv = blk_driver_lookup_typename(if_typename);
+ struct blk_desc *desc;
+
+ if (!drv)
+ return NULL;
+
+ if (get_desc(drv, devnum, &desc))
+ return NULL;
+
+ return desc;
+}
+
+ulong blk_read_devnum(enum if_type if_type, int devnum, lbaint_t start,
+ lbaint_t blkcnt, void *buffer)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ ulong n;
+ int ret;
+
+ if (!drv)
+ return -ENOSYS;
+ ret = get_desc(drv, devnum, &desc);
+ if (ret)
+ return ret;
+ n = desc->block_read(desc, start, blkcnt, buffer);
+ if (IS_ERR_VALUE(n))
+ return n;
+
+ /* flush cache after read */
+ flush_cache((ulong)buffer, blkcnt * desc->blksz);
+
+ return n;
+}
+
+ulong blk_write_devnum(enum if_type if_type, int devnum, lbaint_t start,
+ lbaint_t blkcnt, const void *buffer)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ int ret;
+
+ if (!drv)
+ return -ENOSYS;
+ ret = get_desc(drv, devnum, &desc);
+ if (ret)
+ return ret;
+ return desc->block_write(desc, start, blkcnt, buffer);
+}
+
+int blk_select_hwpart_devnum(enum if_type if_type, int devnum, int hwpart)
+{
+ struct blk_driver *drv = blk_driver_lookup_type(if_type);
+ struct blk_desc *desc;
+ int ret;
+
+ if (!drv)
+ return -ENOSYS;
+ ret = get_desc(drv, devnum, &desc);
+ if (ret)
+ return ret;
+ return drv->select_hwpart(desc, hwpart);
+}
diff --git a/drivers/block/sandbox.c b/drivers/block/sandbox.c
index 2d340efd32..ac28f83472 100644
--- a/drivers/block/sandbox.c
+++ b/drivers/block/sandbox.c
@@ -17,6 +17,19 @@
DECLARE_GLOBAL_DATA_PTR;
+#ifndef CONFIG_BLK
+static struct host_block_dev host_devices[CONFIG_HOST_MAX_DEVICES];
+
+static struct host_block_dev *find_host_device(int dev)
+{
+ if (dev >= 0 && dev < CONFIG_HOST_MAX_DEVICES)
+ return &host_devices[dev];
+
+ return NULL;
+}
+#endif
+
+#ifdef CONFIG_BLK
static unsigned long host_block_read(struct udevice *dev,
unsigned long start, lbaint_t blkcnt,
void *buffer)
@@ -24,6 +37,18 @@ static unsigned long host_block_read(struct udevice *dev,
struct host_block_dev *host_dev = dev_get_priv(dev);
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
+#else
+static unsigned long host_block_read(struct blk_desc *block_dev,
+ unsigned long start, lbaint_t blkcnt,
+ void *buffer)
+{
+ int dev = block_dev->devnum;
+ struct host_block_dev *host_dev = find_host_device(dev);
+
+ if (!host_dev)
+ return -1;
+#endif
+
if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
-1) {
printf("ERROR: Invalid block %lx\n", start);
@@ -35,12 +60,21 @@ static unsigned long host_block_read(struct udevice *dev,
return -1;
}
+#ifdef CONFIG_BLK
static unsigned long host_block_write(struct udevice *dev,
unsigned long start, lbaint_t blkcnt,
const void *buffer)
{
struct host_block_dev *host_dev = dev_get_priv(dev);
struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
+#else
+static unsigned long host_block_write(struct blk_desc *block_dev,
+ unsigned long start, lbaint_t blkcnt,
+ const void *buffer)
+{
+ int dev = block_dev->devnum;
+ struct host_block_dev *host_dev = find_host_device(dev);
+#endif
if (os_lseek(host_dev->fd, start * block_dev->blksz, OS_SEEK_SET) ==
-1) {
@@ -53,6 +87,7 @@ static unsigned long host_block_write(struct udevice *dev,
return -1;
}
+#ifdef CONFIG_BLK
int host_dev_bind(int devnum, char *filename)
{
struct host_block_dev *host_dev;
@@ -115,9 +150,51 @@ err:
free(str);
return ret;
}
+#else
+int host_dev_bind(int dev, char *filename)
+{
+ struct host_block_dev *host_dev = find_host_device(dev);
+
+ if (!host_dev)
+ return -1;
+ if (host_dev->blk_dev.priv) {
+ os_close(host_dev->fd);
+ host_dev->blk_dev.priv = NULL;
+ }
+ if (host_dev->filename)
+ free(host_dev->filename);
+ if (filename && *filename) {
+ host_dev->filename = strdup(filename);
+ } else {
+ host_dev->filename = NULL;
+ return 0;
+ }
+
+ host_dev->fd = os_open(host_dev->filename, OS_O_RDWR);
+ if (host_dev->fd == -1) {
+ printf("Failed to access host backing file '%s'\n",
+ host_dev->filename);
+ return 1;
+ }
+
+ struct blk_desc *blk_dev = &host_dev->blk_dev;
+ blk_dev->if_type = IF_TYPE_HOST;
+ blk_dev->priv = host_dev;
+ blk_dev->blksz = 512;
+ blk_dev->lba = os_lseek(host_dev->fd, 0, OS_SEEK_END) / blk_dev->blksz;
+ blk_dev->block_read = host_block_read;
+ blk_dev->block_write = host_block_write;
+ blk_dev->devnum = dev;
+ blk_dev->part_type = PART_TYPE_UNKNOWN;
+ part_init(blk_dev);
+
+ return 0;
+}
+#endif
int host_get_dev_err(int devnum, struct blk_desc **blk_devp)
{
+#ifdef CONFIG_BLK
struct udevice *dev;
int ret;
@@ -125,20 +202,22 @@ int host_get_dev_err(int devnum, struct blk_desc **blk_devp)
if (ret)
return ret;
*blk_devp = dev_get_uclass_platdata(dev);
+#else
+ struct host_block_dev *host_dev = find_host_device(devnum);
- return 0;
-}
+ if (!host_dev)
+ return -ENODEV;
-struct blk_desc *host_get_dev(int dev)
-{
- struct blk_desc *blk_dev;
+ if (!host_dev->blk_dev.priv)
+ return -ENOENT;
- if (host_get_dev_err(dev, &blk_dev))
- return NULL;
+ *blk_devp = &host_dev->blk_dev;
+#endif
- return blk_dev;
+ return 0;
}
+#ifdef CONFIG_BLK
static const struct blk_ops sandbox_host_blk_ops = {
.read = host_block_read,
.write = host_block_write,
@@ -150,3 +229,11 @@ U_BOOT_DRIVER(sandbox_host_blk) = {
.ops = &sandbox_host_blk_ops,
.priv_auto_alloc_size = sizeof(struct host_block_dev),
};
+#else
+U_BOOT_LEGACY_BLK(sandbox_host) = {
+ .if_typename = "host",
+ .if_type = IF_TYPE_HOST,
+ .max_devs = CONFIG_HOST_MAX_DEVICES,
+ .get_dev = host_get_dev_err,
+};
+#endif
diff --git a/drivers/block/sandbox_scsi.c b/drivers/block/sandbox_scsi.c
new file mode 100644
index 0000000000..ad961bd225
--- /dev/null
+++ b/drivers/block/sandbox_scsi.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This file contains dummy implementations of SCSI functions requried so
+ * that CONFIG_SCSI can be enabled for sandbox.
+ */
+
+#include <common.h>
+#include <scsi.h>
+
+void scsi_bus_reset(void)
+{
+}
+
+void scsi_init(void)
+{
+}
+
+int scsi_exec(ccb *pccb)
+{
+ return 0;
+}
+
+void scsi_print_error(ccb *pccb)
+{
+}
diff --git a/drivers/block/sata_sandbox.c b/drivers/block/sata_sandbox.c
new file mode 100644
index 0000000000..bd967d290c
--- /dev/null
+++ b/drivers/block/sata_sandbox.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2015 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+
+int init_sata(int dev)
+{
+ return 0;
+}
+
+int reset_sata(int dev)
+{
+ return 0;
+}
+
+int scan_sata(int dev)
+{
+ return 0;
+}
+
+ulong sata_read(int dev, ulong blknr, lbaint_t blkcnt, void *buffer)
+{
+ return 0;
+}
+
+ulong sata_write(int dev, ulong blknr, lbaint_t blkcnt, const void *buffer)
+{
+ return 0;
+}
diff --git a/drivers/block/sym53c8xx.c b/drivers/block/sym53c8xx.c
index c7c40affae..5daede7279 100644
--- a/drivers/block/sym53c8xx.c
+++ b/drivers/block/sym53c8xx.c
@@ -33,7 +33,7 @@
#define PRINTF(fmt,args...)
#endif
-#if defined(CONFIG_CMD_SCSI) && defined(CONFIG_SCSI_SYM53C8XX)
+#if defined(CONFIG_SCSI) && defined(CONFIG_SCSI_SYM53C8XX)
#undef SCSI_SINGLE_STEP
/*
diff --git a/drivers/block/systemace.c b/drivers/block/systemace.c
index 09fe834e22..9392beaf05 100644
--- a/drivers/block/systemace.c
+++ b/drivers/block/systemace.c
@@ -27,7 +27,7 @@
#include <common.h>
#include <command.h>
-#include <systemace.h>
+#include <dm.h>
#include <part.h>
#include <asm/io.h>
@@ -69,11 +69,9 @@ static u16 ace_readw(unsigned off)
return in16(base + off);
}
-static unsigned long systemace_read(struct blk_desc *block_dev,
- unsigned long start, lbaint_t blkcnt,
- void *buffer);
-
+#ifndef CONFIG_BLK
static struct blk_desc systemace_dev = { 0 };
+#endif
static int get_cf_lock(void)
{
@@ -104,42 +102,19 @@ static void release_cf_lock(void)
ace_writew((val & 0xffff), 0x18);
}
-#ifdef CONFIG_PARTITIONS
-struct blk_desc *systemace_get_dev(int dev)
-{
- /* The first time through this, the systemace_dev object is
- not yet initialized. In that case, fill it in. */
- if (systemace_dev.blksz == 0) {
- systemace_dev.if_type = IF_TYPE_UNKNOWN;
- systemace_dev.devnum = 0;
- systemace_dev.part_type = PART_TYPE_UNKNOWN;
- systemace_dev.type = DEV_TYPE_HARDDISK;
- systemace_dev.blksz = 512;
- systemace_dev.log2blksz = LOG2(systemace_dev.blksz);
- systemace_dev.removable = 1;
- systemace_dev.block_read = systemace_read;
-
- /*
- * Ensure the correct bus mode (8/16 bits) gets enabled
- */
- ace_writew(width == 8 ? 0 : 0x0001, 0);
-
- part_init(&systemace_dev);
-
- }
-
- return &systemace_dev;
-}
-#endif
-
/*
* This function is called (by dereferencing the block_read pointer in
* the dev_desc) to read blocks of data. The return value is the
* number of blocks read. A zero return indicates an error.
*/
+#ifdef CONFIG_BLK
+static unsigned long systemace_read(struct udevice *dev, unsigned long start,
+ lbaint_t blkcnt, void *buffer)
+#else
static unsigned long systemace_read(struct blk_desc *block_dev,
unsigned long start, lbaint_t blkcnt,
void *buffer)
+#endif
{
int retry;
unsigned blk_countdown;
@@ -257,3 +232,72 @@ static unsigned long systemace_read(struct blk_desc *block_dev,
return blkcnt;
}
+
+#ifdef CONFIG_BLK
+static int systemace_bind(struct udevice *dev)
+{
+ struct blk_desc *bdesc;
+ struct udevice *bdev;
+ int ret;
+
+ ret = blk_create_devicef(dev, "systemace_blk", "blk", IF_TYPE_SYSTEMACE,
+ -1, 512, 0, &bdev);
+ if (ret) {
+ debug("Cannot create block device\n");
+ return ret;
+ }
+ bdesc = dev_get_uclass_platdata(bdev);
+ bdesc->removable = 1;
+ bdesc->part_type = PART_TYPE_UNKNOWN;
+ bdesc->log2blksz = LOG2(bdesc->blksz);
+
+ /* Ensure the correct bus mode (8/16 bits) gets enabled */
+ ace_writew(width == 8 ? 0 : 0x0001, 0);
+
+ return 0;
+}
+
+static const struct blk_ops systemace_blk_ops = {
+ .read = systemace_read,
+};
+
+U_BOOT_DRIVER(systemace_blk) = {
+ .name = "systemace_blk",
+ .id = UCLASS_BLK,
+ .ops = &systemace_blk_ops,
+ .bind = systemace_bind,
+};
+#else
+static int systemace_get_dev(int dev, struct blk_desc **descp)
+{
+ /* The first time through this, the systemace_dev object is
+ not yet initialized. In that case, fill it in. */
+ if (systemace_dev.blksz == 0) {
+ systemace_dev.if_type = IF_TYPE_UNKNOWN;
+ systemace_dev.devnum = 0;
+ systemace_dev.part_type = PART_TYPE_UNKNOWN;
+ systemace_dev.type = DEV_TYPE_HARDDISK;
+ systemace_dev.blksz = 512;
+ systemace_dev.log2blksz = LOG2(systemace_dev.blksz);
+ systemace_dev.removable = 1;
+ systemace_dev.block_read = systemace_read;
+
+ /*
+ * Ensure the correct bus mode (8/16 bits) gets enabled
+ */
+ ace_writew(width == 8 ? 0 : 0x0001, 0);
+
+ part_init(&systemace_dev);
+ }
+ *descp = &systemace_dev;
+
+ return 0;
+}
+
+U_BOOT_LEGACY_BLK(systemace) = {
+ .if_typename = "ace",
+ .if_type = IF_TYPE_SYSTEMACE,
+ .max_devs = 1,
+ .get_dev = systemace_get_dev,
+};
+#endif
diff --git a/drivers/core/device-remove.c b/drivers/core/device-remove.c
index e1714b2202..0e56b23fbb 100644
--- a/drivers/core/device-remove.c
+++ b/drivers/core/device-remove.c
@@ -112,6 +112,8 @@ int device_unbind(struct udevice *dev)
devres_release_all(dev);
+ if (dev->flags & DM_NAME_ALLOCED)
+ free((char *)dev->name);
free(dev);
return 0;
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 1322991d6c..5c2dc7021f 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -657,8 +657,8 @@ fdt_addr_t dev_get_addr_name(struct udevice *dev, const char *name)
#if CONFIG_IS_ENABLED(OF_CONTROL)
int index;
- index = fdt_find_string(gd->fdt_blob, dev->parent->of_offset,
- "reg-names", name);
+ index = fdt_find_string(gd->fdt_blob, dev->of_offset, "reg-names",
+ name);
if (index < 0)
return index;
@@ -706,12 +706,18 @@ bool device_is_last_sibling(struct udevice *dev)
return list_is_last(&dev->sibling_node, &parent->child_head);
}
+void device_set_name_alloced(struct udevice *dev)
+{
+ dev->flags |= DM_NAME_ALLOCED;
+}
+
int device_set_name(struct udevice *dev, const char *name)
{
name = strdup(name);
if (!name)
return -ENOMEM;
dev->name = name;
+ device_set_name_alloced(dev);
return 0;
}
diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index c4fc216340..a72db13a11 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -171,6 +171,10 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
dm_dbg(" - found match at '%s'\n", entry->name);
ret = device_bind(parent, entry, name, NULL, offset, &dev);
+ if (ret == -ENODEV) {
+ dm_dbg("Driver '%s' refuses to bind\n", entry->name);
+ continue;
+ }
if (ret) {
dm_warn("Error binding driver '%s': %d\n", entry->name,
ret);
diff --git a/drivers/ddr/marvell/a38x/ddr3_init.c b/drivers/ddr/marvell/a38x/ddr3_init.c
index ee05f57f43..55baad498a 100644
--- a/drivers/ddr/marvell/a38x/ddr3_init.c
+++ b/drivers/ddr/marvell/a38x/ddr3_init.c
@@ -678,7 +678,7 @@ u32 ddr3_get_device_width(u32 cs)
return (device_width == 0) ? 8 : 16;
}
-float ddr3_get_device_size(u32 cs)
+static int ddr3_get_device_size(u32 cs)
{
u32 device_size_low, device_size_high, device_size;
u32 data, cs_low_offset, cs_high_offset;
@@ -695,15 +695,15 @@ float ddr3_get_device_size(u32 cs)
switch (device_size) {
case 0:
- return 2;
+ return 2048;
case 2:
- return 0.5;
+ return 512;
case 3:
- return 1;
+ return 1024;
case 4:
- return 4;
+ return 4096;
case 5:
- return 8;
+ return 8192;
case 1:
default:
DEBUG_INIT_C("Error: Wrong device size of Cs: ", cs, 1);
@@ -711,13 +711,13 @@ float ddr3_get_device_size(u32 cs)
* Small value will give wrong emem size in
* ddr3_calc_mem_cs_size
*/
- return 0.01;
+ return 0;
}
}
int ddr3_calc_mem_cs_size(u32 cs, u32 *cs_size)
{
- float cs_mem_size;
+ int cs_mem_size;
/* Calculate in GiB */
cs_mem_size = ((ddr3_get_bus_width() / ddr3_get_device_width(cs)) *
@@ -731,21 +731,12 @@ int ddr3_calc_mem_cs_size(u32 cs, u32 *cs_size)
*/
cs_mem_size *= DDR_CONTROLLER_BUS_WIDTH_MULTIPLIER;
- if (cs_mem_size == 0.125) {
- *cs_size = 128 << 20;
- } else if (cs_mem_size == 0.25) {
- *cs_size = 256 << 20;
- } else if (cs_mem_size == 0.5) {
- *cs_size = 512 << 20;
- } else if (cs_mem_size == 1) {
- *cs_size = 1 << 30;
- } else if (cs_mem_size == 2) {
- *cs_size = 2 << 30;
- } else {
+ if (!cs_mem_size || (cs_mem_size == 64) || (cs_mem_size == 4096)) {
DEBUG_INIT_C("Error: Wrong Memory size of Cs: ", cs, 1);
return MV_BAD_VALUE;
}
+ *cs_size = cs_mem_size << 20;
return MV_OK;
}
diff --git a/drivers/dfu/dfu_mmc.c b/drivers/dfu/dfu_mmc.c
index faece8883a..78724e467b 100644
--- a/drivers/dfu/dfu_mmc.c
+++ b/drivers/dfu/dfu_mmc.c
@@ -50,8 +50,9 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
if (dfu->data.mmc.hw_partition >= 0) {
part_num_bkp = mmc->block_dev.hwpart;
- ret = mmc_select_hwpart(dfu->data.mmc.dev_num,
- dfu->data.mmc.hw_partition);
+ ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
+ dfu->data.mmc.dev_num,
+ dfu->data.mmc.hw_partition);
if (ret)
return ret;
}
@@ -75,12 +76,16 @@ static int mmc_block_op(enum dfu_op op, struct dfu_entity *dfu,
if (n != blk_count) {
error("MMC operation failed");
if (dfu->data.mmc.hw_partition >= 0)
- mmc_select_hwpart(dfu->data.mmc.dev_num, part_num_bkp);
+ blk_select_hwpart_devnum(IF_TYPE_MMC,
+ dfu->data.mmc.dev_num,
+ part_num_bkp);
return -EIO;
}
if (dfu->data.mmc.hw_partition >= 0) {
- ret = mmc_select_hwpart(dfu->data.mmc.dev_num, part_num_bkp);
+ ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
+ dfu->data.mmc.dev_num,
+ part_num_bkp);
if (ret)
return ret;
}
diff --git a/drivers/gpio/74x164_gpio.c b/drivers/gpio/74x164_gpio.c
new file mode 100644
index 0000000000..9ac10a745d
--- /dev/null
+++ b/drivers/gpio/74x164_gpio.c
@@ -0,0 +1,193 @@
+/*
+ * Take drivers/gpio/gpio-74x164.c as reference.
+ *
+ * 74Hx164 - Generic serial-in/parallel-out 8-bits shift register GPIO driver
+ *
+ * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <malloc.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <spi.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * struct gen_74x164_chip - Data for 74Hx164
+ *
+ * @oe: OE pin
+ * @nregs: number of registers
+ * @buffer: buffer for chained chips
+ */
+#define GEN_74X164_NUMBER_GPIOS 8
+
+struct gen_74x164_priv {
+ struct gpio_desc oe;
+ u32 nregs;
+ /*
+ * Since the nregs are chained, every byte sent will make
+ * the previous byte shift to the next register in the
+ * chain. Thus, the first byte sent will end up in the last
+ * register at the end of the transfer. So, to have a logical
+ * numbering, store the bytes in reverse order.
+ */
+ u8 *buffer;
+};
+
+static int gen_74x164_write_conf(struct udevice *dev)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_spi_claim_bus(dev);
+ if (ret)
+ return ret;
+
+ ret = dm_spi_xfer(dev, priv->nregs * 8, priv->buffer, NULL,
+ SPI_XFER_BEGIN | SPI_XFER_END);
+
+ dm_spi_release_bus(dev);
+
+ return ret;
+}
+
+static int gen_74x164_get_value(struct udevice *dev, unsigned offset)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ uint bank = priv->nregs - 1 - offset / 8;
+ uint pin = offset % 8;
+
+ return (priv->buffer[bank] >> pin) & 0x1;
+}
+
+static int gen_74x164_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ uint bank = priv->nregs - 1 - offset / 8;
+ uint pin = offset % 8;
+ int ret;
+
+ if (value)
+ priv->buffer[bank] |= 1 << pin;
+ else
+ priv->buffer[bank] &= ~(1 << pin);
+
+ ret = gen_74x164_write_conf(dev);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int gen_74x164_direction_input(struct udevice *dev, unsigned offset)
+{
+ return -ENOSYS;
+}
+
+static int gen_74x164_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ return gen_74x164_set_value(dev, offset, value);
+}
+
+static int gen_74x164_get_function(struct udevice *dev, unsigned offset)
+{
+ return GPIOF_OUTPUT;
+}
+
+static int gen_74x164_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct fdtdec_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops gen_74x164_ops = {
+ .direction_input = gen_74x164_direction_input,
+ .direction_output = gen_74x164_direction_output,
+ .get_value = gen_74x164_get_value,
+ .set_value = gen_74x164_set_value,
+ .get_function = gen_74x164_get_function,
+ .xlate = gen_74x164_xlate,
+};
+
+static int gen_74x164_probe(struct udevice *dev)
+{
+ struct gen_74x164_priv *priv = dev_get_priv(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ char *str, name[32];
+ int ret;
+ const void *fdt = gd->fdt_blob;
+ int node = dev->of_offset;
+
+ snprintf(name, sizeof(name), "%s_", dev->name);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+
+ /*
+ * See Linux kernel:
+ * Documentation/devicetree/bindings/gpio/gpio-74x164.txt
+ */
+ priv->nregs = fdtdec_get_int(fdt, node, "registers-number", 1);
+ priv->buffer = calloc(priv->nregs, sizeof(u8));
+ if (!priv->buffer) {
+ ret = -ENOMEM;
+ goto free_str;
+ }
+
+ ret = fdtdec_get_byte_array(fdt, node, "registers-default",
+ priv->buffer, priv->nregs);
+ if (ret)
+ dev_dbg(dev, "No registers-default property\n");
+
+ ret = gpio_request_by_name(dev, "oe-gpios", 0, &priv->oe,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret) {
+ dev_err(dev, "No oe-pins property\n");
+ goto free_buf;
+ }
+
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = priv->nregs * 8;
+
+ ret = gen_74x164_write_conf(dev);
+ if (ret)
+ goto free_buf;
+
+ dev_dbg(dev, "%s is ready\n", dev->name);
+
+ return 0;
+
+free_buf:
+ free(priv->buffer);
+free_str:
+ free(str);
+ return ret;
+}
+
+static const struct udevice_id gen_74x164_ids[] = {
+ { .compatible = "fairchild,74hc595" },
+ { }
+};
+
+U_BOOT_DRIVER(74x164) = {
+ .name = "74x164",
+ .id = UCLASS_GPIO,
+ .ops = &gen_74x164_ops,
+ .probe = gen_74x164_probe,
+ .priv_auto_alloc_size = sizeof(struct gen_74x164_priv),
+ .of_match = gen_74x164_ids,
+};
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 2b4624d7f8..93a7e8c6c2 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -143,4 +143,34 @@ config ZYNQ_GPIO
help
Supports GPIO access on Zynq SoC.
+config DM_74X164
+ bool "74x164 serial-in/parallel-out 8-bits shift register"
+ depends on DM_GPIO
+ help
+ Driver for 74x164 compatible serial-in/parallel-out 8-outputs
+ shift registers, such as 74lv165, 74hc595.
+ This driver can be used to provide access to more gpio outputs.
+
+config DM_PCA953X
+ bool "PCA95[357]x, PCA9698, TCA64xx, and MAX7310 I/O ports"
+ depends on DM_GPIO
+ help
+ Say yes here to provide access to several register-oriented
+ SMBus I/O expanders, made mostly by NXP or TI. Compatible
+ models include:
+
+ 4 bits: pca9536, pca9537
+
+ 8 bits: max7310, max7315, pca6107, pca9534, pca9538, pca9554,
+ pca9556, pca9557, pca9574, tca6408, xra1202
+
+ 16 bits: max7312, max7313, pca9535, pca9539, pca9555, pca9575,
+ tca6416
+
+ 24 bits: tca6424
+
+ 40 bits: pca9505, pca9698
+
+ Now, max 24 bits chips and PCA953X compatible chips are
+ supported
endmenu
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4f071c4517..ddec1ef8de 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -11,6 +11,9 @@ obj-$(CONFIG_AXP_GPIO) += axp_gpio.o
endif
obj-$(CONFIG_DM_GPIO) += gpio-uclass.o
+obj-$(CONFIG_DM_PCA953X) += pca953x_gpio.o
+obj-$(CONFIG_DM_74X164) += 74x164_gpio.o
+
obj-$(CONFIG_AT91_GPIO) += at91_gpio.o
obj-$(CONFIG_ATMEL_PIO4) += atmel_pio4.o
obj-$(CONFIG_INTEL_ICH6_GPIO) += intel_ich6_gpio.o
diff --git a/drivers/gpio/gpio-uclass.c b/drivers/gpio/gpio-uclass.c
index b58d4e64e8..732b6c2afa 100644
--- a/drivers/gpio/gpio-uclass.c
+++ b/drivers/gpio/gpio-uclass.c
@@ -6,6 +6,7 @@
#include <common.h>
#include <dm.h>
+#include <dt-bindings/gpio/gpio.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
@@ -113,19 +114,33 @@ int gpio_lookup_name(const char *name, struct udevice **devp,
return 0;
}
+int gpio_xlate_offs_flags(struct udevice *dev,
+ struct gpio_desc *desc,
+ struct fdtdec_phandle_args *args)
+{
+ if (args->args_count < 1)
+ return -EINVAL;
+
+ desc->offset = args->args[0];
+
+ if (args->args_count < 2)
+ return 0;
+
+ if (args->args[1] & GPIO_ACTIVE_LOW)
+ desc->flags = GPIOD_ACTIVE_LOW;
+
+ return 0;
+}
+
static int gpio_find_and_xlate(struct gpio_desc *desc,
struct fdtdec_phandle_args *args)
{
struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
- /* Use the first argument as the offset by default */
- if (args->args_count > 0)
- desc->offset = args->args[0];
+ if (ops->xlate)
+ return ops->xlate(desc->dev, desc, args);
else
- desc->offset = -1;
- desc->flags = 0;
-
- return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0;
+ return gpio_xlate_offs_flags(desc->dev, desc, args);
}
int dm_gpio_request(struct gpio_desc *desc, const char *label)
@@ -605,6 +620,7 @@ static int _gpio_request_by_name_nodev(const void *blob, int node,
desc->dev = NULL;
desc->offset = 0;
+ desc->flags = 0;
ret = fdtdec_parse_phandle_with_args(blob, node, list_name,
"#gpio-cells", 0, index, &args);
if (ret) {
diff --git a/drivers/gpio/intel_broadwell_gpio.c b/drivers/gpio/intel_broadwell_gpio.c
index 8cf76f96c2..81ce446e1a 100644
--- a/drivers/gpio/intel_broadwell_gpio.c
+++ b/drivers/gpio/intel_broadwell_gpio.c
@@ -162,15 +162,6 @@ static int broadwell_gpio_ofdata_to_platdata(struct udevice *dev)
return 0;
}
-static int broadwell_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
- struct fdtdec_phandle_args *args)
-{
- desc->offset = args->args[0];
- desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
-
- return 0;
-}
-
static const struct dm_gpio_ops gpio_broadwell_ops = {
.request = broadwell_gpio_request,
.direction_input = broadwell_gpio_direction_input,
@@ -178,7 +169,6 @@ static const struct dm_gpio_ops gpio_broadwell_ops = {
.get_value = broadwell_gpio_get_value,
.set_value = broadwell_gpio_set_value,
.get_function = broadwell_gpio_get_function,
- .xlate = broadwell_gpio_xlate,
};
static const struct udevice_id intel_broadwell_gpio_ids[] = {
diff --git a/drivers/gpio/omap_gpio.c b/drivers/gpio/omap_gpio.c
index 93d18e44a5..cd960dc013 100644
--- a/drivers/gpio/omap_gpio.c
+++ b/drivers/gpio/omap_gpio.c
@@ -25,7 +25,6 @@
#include <asm/io.h>
#include <asm/errno.h>
#include <malloc.h>
-#include <dt-bindings/gpio/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -277,22 +276,12 @@ static int omap_gpio_get_function(struct udevice *dev, unsigned offset)
return GPIOF_INPUT;
}
-static int omap_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
- struct fdtdec_phandle_args *args)
-{
- desc->offset = args->args[0];
- desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
-
- return 0;
-}
-
static const struct dm_gpio_ops gpio_omap_ops = {
.direction_input = omap_gpio_direction_input,
.direction_output = omap_gpio_direction_output,
.get_value = omap_gpio_get_value,
.set_value = omap_gpio_set_value,
.get_function = omap_gpio_get_function,
- .xlate = omap_gpio_xlate,
};
static int omap_gpio_probe(struct udevice *dev)
diff --git a/drivers/gpio/pca953x_gpio.c b/drivers/gpio/pca953x_gpio.c
new file mode 100644
index 0000000000..987d10e967
--- /dev/null
+++ b/drivers/gpio/pca953x_gpio.c
@@ -0,0 +1,351 @@
+/*
+ * Take linux kernel driver drivers/gpio/gpio-pca953x.c for reference.
+ *
+ * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ */
+
+/*
+ * Note:
+ * The driver's compatible table is borrowed from Linux Kernel,
+ * but now max supported gpio pins is 24 and only PCA953X_TYPE
+ * is supported. PCA957X_TYPE is not supported now.
+ * Also the Polarity Inversion feature is not supported now.
+ *
+ * TODO:
+ * 1. Support PCA957X_TYPE
+ * 2. Support max 40 gpio pins
+ * 3. Support Plolarity Inversion
+ */
+
+#include <common.h>
+#include <errno.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <i2c.h>
+#include <malloc.h>
+#include <asm/gpio.h>
+#include <asm/io.h>
+#include <dt-bindings/gpio/gpio.h>
+
+#define PCA953X_INPUT 0
+#define PCA953X_OUTPUT 1
+#define PCA953X_INVERT 2
+#define PCA953X_DIRECTION 3
+
+#define PCA_GPIO_MASK 0x00FF
+#define PCA_INT 0x0100
+#define PCA953X_TYPE 0x1000
+#define PCA957X_TYPE 0x2000
+#define PCA_TYPE_MASK 0xF000
+#define PCA_CHIP_TYPE(x) ((x) & PCA_TYPE_MASK)
+
+enum {
+ PCA953X_DIRECTION_IN,
+ PCA953X_DIRECTION_OUT,
+};
+
+#define MAX_BANK 3
+#define BANK_SZ 8
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/*
+ * struct pca953x_info - Data for pca953x
+ *
+ * @dev: udevice structure for the device
+ * @addr: i2c slave address
+ * @invert: Polarity inversion or not
+ * @gpio_count: the number of gpio pins that the device supports
+ * @chip_type: indicate the chip type,PCA953X or PCA957X
+ * @bank_count: the number of banks that the device supports
+ * @reg_output: array to hold the value of output registers
+ * @reg_direction: array to hold the value of direction registers
+ */
+struct pca953x_info {
+ struct udevice *dev;
+ int addr;
+ int invert;
+ int gpio_count;
+ int chip_type;
+ int bank_count;
+ u8 reg_output[MAX_BANK];
+ u8 reg_direction[MAX_BANK];
+};
+
+static int pca953x_write_single(struct udevice *dev, int reg, u8 val,
+ int offset)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+ int bank_shift = fls((info->gpio_count - 1) / BANK_SZ);
+ int off = offset / BANK_SZ;
+ int ret = 0;
+
+ ret = dm_i2c_write(dev, (reg << bank_shift) + off, &val, 1);
+ if (ret) {
+ dev_err(dev, "%s error\n", __func__);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int pca953x_read_single(struct udevice *dev, int reg, u8 *val,
+ int offset)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+ int bank_shift = fls((info->gpio_count - 1) / BANK_SZ);
+ int off = offset / BANK_SZ;
+ int ret;
+ u8 byte;
+
+ ret = dm_i2c_read(dev, (reg << bank_shift) + off, &byte, 1);
+ if (ret) {
+ dev_err(dev, "%s error\n", __func__);
+ return ret;
+ }
+
+ *val = byte;
+
+ return 0;
+}
+
+static int pca953x_read_regs(struct udevice *dev, int reg, u8 *val)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+ int ret = 0;
+
+ if (info->gpio_count <= 8) {
+ ret = dm_i2c_read(dev, reg, val, 1);
+ } else if (info->gpio_count <= 16) {
+ ret = dm_i2c_read(dev, reg << 1, val, info->bank_count);
+ } else {
+ dev_err(dev, "Unsupported now\n");
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int pca953x_is_output(struct udevice *dev, int offset)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+
+ int bank = offset / BANK_SZ;
+ int off = offset % BANK_SZ;
+
+ /*0: output; 1: input */
+ return !(info->reg_direction[bank] & (1 << off));
+}
+
+static int pca953x_get_value(struct udevice *dev, unsigned offset)
+{
+ int ret;
+ u8 val = 0;
+
+ ret = pca953x_read_single(dev, PCA953X_INPUT, &val, offset);
+ if (ret)
+ return ret;
+
+ return (val >> offset) & 0x1;
+}
+
+static int pca953x_set_value(struct udevice *dev, unsigned offset,
+ int value)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+ int bank = offset / BANK_SZ;
+ int off = offset % BANK_SZ;
+ u8 val;
+ int ret;
+
+ if (value)
+ val = info->reg_output[bank] | (1 << off);
+ else
+ val = info->reg_output[bank] & ~(1 << off);
+
+ ret = pca953x_write_single(dev, PCA953X_OUTPUT, val, offset);
+ if (ret)
+ return ret;
+
+ info->reg_output[bank] = val;
+
+ return 0;
+}
+
+static int pca953x_set_direction(struct udevice *dev, unsigned offset, int dir)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+ int bank = offset / BANK_SZ;
+ int off = offset % BANK_SZ;
+ u8 val;
+ int ret;
+
+ if (dir == PCA953X_DIRECTION_IN)
+ val = info->reg_direction[bank] | (1 << off);
+ else
+ val = info->reg_direction[bank] & ~(1 << off);
+
+ ret = pca953x_write_single(dev, PCA953X_DIRECTION, val, offset);
+ if (ret)
+ return ret;
+
+ info->reg_direction[bank] = val;
+
+ return 0;
+}
+
+static int pca953x_direction_input(struct udevice *dev, unsigned offset)
+{
+ return pca953x_set_direction(dev, offset, PCA953X_DIRECTION_IN);
+}
+
+static int pca953x_direction_output(struct udevice *dev, unsigned offset,
+ int value)
+{
+ /* Configure output value. */
+ pca953x_set_value(dev, offset, value);
+
+ /* Configure direction as output. */
+ pca953x_set_direction(dev, offset, PCA953X_DIRECTION_OUT);
+
+ return 0;
+}
+
+static int pca953x_get_function(struct udevice *dev, unsigned offset)
+{
+ if (pca953x_is_output(dev, offset))
+ return GPIOF_OUTPUT;
+ else
+ return GPIOF_INPUT;
+}
+
+static int pca953x_xlate(struct udevice *dev, struct gpio_desc *desc,
+ struct fdtdec_phandle_args *args)
+{
+ desc->offset = args->args[0];
+ desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
+
+ return 0;
+}
+
+static const struct dm_gpio_ops pca953x_ops = {
+ .direction_input = pca953x_direction_input,
+ .direction_output = pca953x_direction_output,
+ .get_value = pca953x_get_value,
+ .set_value = pca953x_set_value,
+ .get_function = pca953x_get_function,
+ .xlate = pca953x_xlate,
+};
+
+static int pca953x_probe(struct udevice *dev)
+{
+ struct pca953x_info *info = dev_get_platdata(dev);
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct dm_i2c_chip *chip = dev_get_parent_platdata(dev);
+ char name[32], *str;
+ int addr;
+ ulong driver_data;
+ int ret;
+
+ if (!info) {
+ dev_err(dev, "platdata not ready\n");
+ return -ENOMEM;
+ }
+
+ if (!chip) {
+ dev_err(dev, "i2c not ready\n");
+ return -ENODEV;
+ }
+
+ addr = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", 0);
+ if (addr == 0)
+ return -ENODEV;
+
+ info->addr = addr;
+
+ driver_data = dev_get_driver_data(dev);
+
+ info->gpio_count = driver_data & PCA_GPIO_MASK;
+ if (info->gpio_count > MAX_BANK * BANK_SZ) {
+ dev_err(dev, "Max support %d pins now\n", MAX_BANK * BANK_SZ);
+ return -EINVAL;
+ }
+
+ info->chip_type = PCA_CHIP_TYPE(driver_data);
+ if (info->chip_type != PCA953X_TYPE) {
+ dev_err(dev, "Only support PCA953X chip type now.\n");
+ return -EINVAL;
+ }
+
+ info->bank_count = DIV_ROUND_UP(info->gpio_count, BANK_SZ);
+
+ ret = pca953x_read_regs(dev, PCA953X_OUTPUT, info->reg_output);
+ if (ret) {
+ dev_err(dev, "Error reading output register\n");
+ return ret;
+ }
+
+ ret = pca953x_read_regs(dev, PCA953X_DIRECTION, info->reg_direction);
+ if (ret) {
+ dev_err(dev, "Error reading direction register\n");
+ return ret;
+ }
+
+ snprintf(name, sizeof(name), "gpio@%x_", info->addr);
+ str = strdup(name);
+ if (!str)
+ return -ENOMEM;
+ uc_priv->bank_name = str;
+ uc_priv->gpio_count = info->gpio_count;
+
+ dev_dbg(dev, "%s is ready\n", str);
+
+ return 0;
+}
+
+#define OF_953X(__nrgpio, __int) (ulong)(__nrgpio | PCA953X_TYPE | __int)
+#define OF_957X(__nrgpio, __int) (ulong)(__nrgpio | PCA957X_TYPE | __int)
+
+static const struct udevice_id pca953x_ids[] = {
+ { .compatible = "nxp,pca9505", .data = OF_953X(40, PCA_INT), },
+ { .compatible = "nxp,pca9534", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca9535", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "nxp,pca9536", .data = OF_953X(4, 0), },
+ { .compatible = "nxp,pca9537", .data = OF_953X(4, PCA_INT), },
+ { .compatible = "nxp,pca9538", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca9539", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "nxp,pca9554", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "nxp,pca9555", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "nxp,pca9556", .data = OF_953X(8, 0), },
+ { .compatible = "nxp,pca9557", .data = OF_953X(8, 0), },
+ { .compatible = "nxp,pca9574", .data = OF_957X(8, PCA_INT), },
+ { .compatible = "nxp,pca9575", .data = OF_957X(16, PCA_INT), },
+ { .compatible = "nxp,pca9698", .data = OF_953X(40, 0), },
+
+ { .compatible = "maxim,max7310", .data = OF_953X(8, 0), },
+ { .compatible = "maxim,max7312", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "maxim,max7313", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "maxim,max7315", .data = OF_953X(8, PCA_INT), },
+
+ { .compatible = "ti,pca6107", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "ti,tca6408", .data = OF_953X(8, PCA_INT), },
+ { .compatible = "ti,tca6416", .data = OF_953X(16, PCA_INT), },
+ { .compatible = "ti,tca6424", .data = OF_953X(24, PCA_INT), },
+
+ { .compatible = "onsemi,pca9654", .data = OF_953X(8, PCA_INT), },
+
+ { .compatible = "exar,xra1202", .data = OF_953X(8, 0), },
+ { }
+};
+
+U_BOOT_DRIVER(pca953x) = {
+ .name = "pca953x",
+ .id = UCLASS_GPIO,
+ .ops = &pca953x_ops,
+ .probe = pca953x_probe,
+ .platdata_auto_alloc_size = sizeof(struct pca953x_info),
+ .of_match = pca953x_ids,
+};
diff --git a/drivers/gpio/pic32_gpio.c b/drivers/gpio/pic32_gpio.c
index 499b4fa5ad..7a037f3a77 100644
--- a/drivers/gpio/pic32_gpio.c
+++ b/drivers/gpio/pic32_gpio.c
@@ -12,7 +12,6 @@
#include <asm/io.h>
#include <asm/gpio.h>
#include <linux/compat.h>
-#include <dt-bindings/gpio/gpio.h>
#include <mach/pic32.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -99,14 +98,6 @@ static int pic32_gpio_direction_output(struct udevice *dev,
return 0;
}
-static int pic32_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
- struct fdtdec_phandle_args *args)
-{
- desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
-
- return 0;
-}
-
static int pic32_gpio_get_function(struct udevice *dev, unsigned offset)
{
int ret = GPIOF_UNUSED;
@@ -131,7 +122,6 @@ static const struct dm_gpio_ops gpio_pic32_ops = {
.get_value = pic32_gpio_get_value,
.set_value = pic32_gpio_set_value,
.get_function = pic32_gpio_get_function,
- .xlate = pic32_gpio_xlate,
};
static int pic32_gpio_probe(struct udevice *dev)
diff --git a/drivers/gpio/rk_gpio.c b/drivers/gpio/rk_gpio.c
index 40e87bd199..fefe3ca203 100644
--- a/drivers/gpio/rk_gpio.c
+++ b/drivers/gpio/rk_gpio.c
@@ -16,7 +16,6 @@
#include <asm/io.h>
#include <asm/arch/clock.h>
#include <dm/pinctrl.h>
-#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/clock/rk3288-cru.h>
enum {
@@ -98,15 +97,6 @@ static int rockchip_gpio_get_function(struct udevice *dev, unsigned offset)
#endif
}
-static int rockchip_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
- struct fdtdec_phandle_args *args)
-{
- desc->offset = args->args[0];
- desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
-
- return 0;
-}
-
static int rockchip_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
@@ -135,7 +125,6 @@ static const struct dm_gpio_ops gpio_rockchip_ops = {
.get_value = rockchip_gpio_get_value,
.set_value = rockchip_gpio_set_value,
.get_function = rockchip_gpio_get_function,
- .xlate = rockchip_gpio_xlate,
};
static const struct udevice_id rockchip_gpio_ids[] = {
diff --git a/drivers/gpio/s5p_gpio.c b/drivers/gpio/s5p_gpio.c
index 0f22b238ba..377fed467f 100644
--- a/drivers/gpio/s5p_gpio.c
+++ b/drivers/gpio/s5p_gpio.c
@@ -13,7 +13,6 @@
#include <asm/io.h>
#include <asm/gpio.h>
#include <dm/device-internal.h>
-#include <dt-bindings/gpio/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -276,22 +275,12 @@ static int exynos_gpio_get_function(struct udevice *dev, unsigned offset)
return GPIOF_FUNC;
}
-static int exynos_gpio_xlate(struct udevice *dev, struct gpio_desc *desc,
- struct fdtdec_phandle_args *args)
-{
- desc->offset = args->args[0];
- desc->flags = args->args[1] & GPIO_ACTIVE_LOW ? GPIOD_ACTIVE_LOW : 0;
-
- return 0;
-}
-
static const struct dm_gpio_ops gpio_exynos_ops = {
.direction_input = exynos_gpio_direction_input,
.direction_output = exynos_gpio_direction_output,
.get_value = exynos_gpio_get_value,
.set_value = exynos_gpio_set_value,
.get_function = exynos_gpio_get_function,
- .xlate = exynos_gpio_xlate,
};
static int gpio_exynos_probe(struct udevice *dev)
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 9324c6c9e5..6e22bbadff 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -58,6 +58,13 @@ config DM_I2C_GPIO
bindings are supported.
Binding info: doc/device-tree-bindings/i2c/i2c-gpio.txt
+config SYS_I2C_FSL
+ bool "Freescale I2C bus driver"
+ depends on DM_I2C
+ help
+ Add support for Freescale I2C busses as used on MPC8240, MPC8245, and
+ MPC85xx processors.
+
config SYS_I2C_CADENCE
tristate "Cadence I2C Controller"
depends on DM_I2C && (ARCH_ZYNQ || ARM64)
@@ -65,6 +72,24 @@ 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_DW
+ bool "Designware I2C Controller"
+ default n
+ help
+ Say yes here to select the Designware I2C Host Controller. This
+ controller is used in various SoCs, e.g. the ST SPEAr, Altera
+ SoCFPGA, Synopsys ARC700 and some Intel x86 SoCs.
+
+config SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
+ bool "DW I2C Enable Status Register not supported"
+ depends on SYS_I2C_DW && (TARGET_SPEAR300 || TARGET_SPEAR310 || \
+ TARGET_SPEAR320 || TARGET_SPEAR600 || TARGET_X600)
+ default y
+ help
+ Some versions of the Designware I2C controller do not support the
+ enable status register. This config option can be enabled in such
+ cases.
+
config SYS_I2C_INTEL
bool "Intel I2C/SMBUS driver"
depends on DM_I2C
diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c
index 0c7cd0ba72..e60fd0a419 100644
--- a/drivers/i2c/designware_i2c.c
+++ b/drivers/i2c/designware_i2c.c
@@ -36,6 +36,14 @@ struct dw_i2c {
struct dw_scl_sda_cfg *scl_sda_cfg;
};
+#ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
+static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
+{
+ u32 ena = enable ? IC_ENABLE_0B : 0;
+
+ writel(ena, &i2c_base->ic_enable);
+}
+#else
static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
{
u32 ena = enable ? IC_ENABLE_0B : 0;
@@ -56,6 +64,7 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
printf("timeout in %sabling I2C adapter\n", enable ? "en" : "dis");
}
+#endif
/*
* i2c_set_bus_speed - Set the i2c speed
diff --git a/drivers/i2c/fsl_i2c.c b/drivers/i2c/fsl_i2c.c
index b56a1c2541..b8cc647bd3 100644
--- a/drivers/i2c/fsl_i2c.c
+++ b/drivers/i2c/fsl_i2c.c
@@ -12,6 +12,8 @@
#include <i2c.h> /* Functional interface */
#include <asm/io.h>
#include <asm/fsl_i2c.h> /* HW definitions */
+#include <dm.h>
+#include <mapmem.h>
/* The maximum number of microseconds we will wait until another master has
* released the bus. If not defined in the board header file, then use a
@@ -34,18 +36,20 @@
DECLARE_GLOBAL_DATA_PTR;
-static const struct fsl_i2c *i2c_dev[4] = {
- (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET),
+#ifndef CONFIG_DM_I2C
+static const struct fsl_i2c_base *i2c_base[4] = {
+ (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C_OFFSET),
#ifdef CONFIG_SYS_FSL_I2C2_OFFSET
- (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET),
+ (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C2_OFFSET),
#endif
#ifdef CONFIG_SYS_FSL_I2C3_OFFSET
- (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C3_OFFSET),
+ (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C3_OFFSET),
#endif
#ifdef CONFIG_SYS_FSL_I2C4_OFFSET
- (struct fsl_i2c *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C4_OFFSET)
+ (struct fsl_i2c_base *)(CONFIG_SYS_IMMR + CONFIG_SYS_FSL_I2C4_OFFSET)
#endif
};
+#endif
/* I2C speed map for a DFSR value of 1 */
@@ -104,7 +108,7 @@ static const struct {
/**
* Set the I2C bus speed for a given I2C device
*
- * @param dev: the I2C device
+ * @param base: the I2C device registers
* @i2c_clk: I2C bus clock frequency
* @speed: the desired speed of the bus
*
@@ -112,7 +116,7 @@ static const struct {
*
* The return value is the actual bus speed that is set.
*/
-static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev,
+static unsigned int set_i2c_bus_speed(const struct fsl_i2c_base *base,
unsigned int i2c_clk, unsigned int speed)
{
unsigned short divider = min(i2c_clk / speed, (unsigned int)USHRT_MAX);
@@ -173,8 +177,8 @@ static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev,
debug("divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr);
debug("FDR:0x%.2x, speed:%d\n", fdr, speed);
#endif
- writeb(dfsr, &dev->dfsrr); /* set default filter */
- writeb(fdr, &dev->fdr); /* set bus speed */
+ writeb(dfsr, &base->dfsrr); /* set default filter */
+ writeb(fdr, &base->fdr); /* set bus speed */
#else
unsigned int i;
@@ -184,7 +188,7 @@ static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev,
fdr = fsl_i2c_speed_map[i].fdr;
speed = i2c_clk / fsl_i2c_speed_map[i].divider;
- writeb(fdr, &dev->fdr); /* set bus speed */
+ writeb(fdr, &base->fdr); /* set bus speed */
break;
}
@@ -192,6 +196,7 @@ static unsigned int set_i2c_bus_speed(const struct fsl_i2c *dev,
return speed;
}
+#ifndef CONFIG_DM_I2C
static unsigned int get_i2c_clock(int bus)
{
if (bus)
@@ -199,8 +204,9 @@ static unsigned int get_i2c_clock(int bus)
else
return gd->arch.i2c1_clk; /* I2C1 clock */
}
+#endif
-static int fsl_i2c_fixup(const struct fsl_i2c *dev)
+static int fsl_i2c_fixup(const struct fsl_i2c_base *base)
{
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
unsigned long long timeval = 0;
@@ -214,42 +220,42 @@ static int fsl_i2c_fixup(const struct fsl_i2c *dev)
flags = I2C_CR_BIT6;
#endif
- writeb(I2C_CR_MEN | I2C_CR_MSTA, &dev->cr);
+ writeb(I2C_CR_MEN | I2C_CR_MSTA, &base->cr);
timeval = get_ticks();
- while (!(readb(&dev->sr) & I2C_SR_MBB)) {
+ while (!(readb(&base->sr) & I2C_SR_MBB)) {
if ((get_ticks() - timeval) > timeout)
goto err;
}
- if (readb(&dev->sr) & I2C_SR_MAL) {
+ if (readb(&base->sr) & I2C_SR_MAL) {
/* SDA is stuck low */
- writeb(0, &dev->cr);
+ writeb(0, &base->cr);
udelay(100);
- writeb(I2C_CR_MSTA | flags, &dev->cr);
- writeb(I2C_CR_MEN | I2C_CR_MSTA | flags, &dev->cr);
+ writeb(I2C_CR_MSTA | flags, &base->cr);
+ writeb(I2C_CR_MEN | I2C_CR_MSTA | flags, &base->cr);
}
- readb(&dev->dr);
+ readb(&base->dr);
timeval = get_ticks();
- while (!(readb(&dev->sr) & I2C_SR_MIF)) {
+ while (!(readb(&base->sr) & I2C_SR_MIF)) {
if ((get_ticks() - timeval) > timeout)
goto err;
}
ret = 0;
err:
- writeb(I2C_CR_MEN | flags, &dev->cr);
- writeb(0, &dev->sr);
+ writeb(I2C_CR_MEN | flags, &base->cr);
+ writeb(0, &base->sr);
udelay(100);
return ret;
}
-static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+static void __i2c_init(const struct fsl_i2c_base *base, int speed, int
+ slaveadd, int i2c_clk, int busnum)
{
- const struct fsl_i2c *dev;
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
unsigned long long timeval;
@@ -260,23 +266,21 @@ static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
*/
i2c_init_board();
#endif
- dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
-
- writeb(0, &dev->cr); /* stop I2C controller */
+ writeb(0, &base->cr); /* stop I2C controller */
udelay(5); /* let it shutdown in peace */
- set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed);
- writeb(slaveadd << 1, &dev->adr);/* write slave address */
- writeb(0x0, &dev->sr); /* clear status register */
- writeb(I2C_CR_MEN, &dev->cr); /* start I2C controller */
+ set_i2c_bus_speed(base, i2c_clk, speed);
+ writeb(slaveadd << 1, &base->adr);/* write slave address */
+ writeb(0x0, &base->sr); /* clear status register */
+ writeb(I2C_CR_MEN, &base->cr); /* start I2C controller */
timeval = get_ticks();
- while (readb(&dev->sr) & I2C_SR_MBB) {
+ while (readb(&base->sr) & I2C_SR_MBB) {
if ((get_ticks() - timeval) < timeout)
continue;
- if (fsl_i2c_fixup(dev))
+ if (fsl_i2c_fixup(base))
debug("i2c_init: BUS#%d failed to init\n",
- adap->hwadapnr);
+ busnum);
break;
}
@@ -292,13 +296,12 @@ static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
}
static int
-i2c_wait4bus(struct i2c_adapter *adap)
+i2c_wait4bus(const struct fsl_i2c_base *base)
{
- struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
unsigned long long timeval = get_ticks();
const unsigned long long timeout = usec2ticks(CONFIG_I2C_MBB_TIMEOUT);
- while (readb(&dev->sr) & I2C_SR_MBB) {
+ while (readb(&base->sr) & I2C_SR_MBB) {
if ((get_ticks() - timeval) > timeout)
return -1;
}
@@ -306,22 +309,21 @@ i2c_wait4bus(struct i2c_adapter *adap)
return 0;
}
-static __inline__ int
-i2c_wait(struct i2c_adapter *adap, int write)
+static inline int
+i2c_wait(const struct fsl_i2c_base *base, int write)
{
u32 csr;
unsigned long long timeval = get_ticks();
const unsigned long long timeout = usec2ticks(CONFIG_I2C_TIMEOUT);
- struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
do {
- csr = readb(&dev->sr);
+ csr = readb(&base->sr);
if (!(csr & I2C_SR_MIF))
continue;
/* Read again to allow register to stabilise */
- csr = readb(&dev->sr);
+ csr = readb(&base->sr);
- writeb(0x0, &dev->sr);
+ writeb(0x0, &base->sr);
if (csr & I2C_SR_MAL) {
debug("i2c_wait: MAL\n");
@@ -345,203 +347,318 @@ i2c_wait(struct i2c_adapter *adap, int write)
return -1;
}
-static __inline__ int
-i2c_write_addr(struct i2c_adapter *adap, u8 dev, u8 dir, int rsta)
+static inline int
+i2c_write_addr(const struct fsl_i2c_base *base, u8 dev, u8 dir, int rsta)
{
- struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
-
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX
| (rsta ? I2C_CR_RSTA : 0),
- &device->cr);
+ &base->cr);
- writeb((dev << 1) | dir, &device->dr);
+ writeb((dev << 1) | dir, &base->dr);
- if (i2c_wait(adap, I2C_WRITE_BIT) < 0)
+ if (i2c_wait(base, I2C_WRITE_BIT) < 0)
return 0;
return 1;
}
-static __inline__ int
-__i2c_write(struct i2c_adapter *adap, u8 *data, int length)
+static inline int
+__i2c_write_data(const struct fsl_i2c_base *base, u8 *data, int length)
{
- struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
int i;
for (i = 0; i < length; i++) {
- writeb(data[i], &dev->dr);
+ writeb(data[i], &base->dr);
- if (i2c_wait(adap, I2C_WRITE_BIT) < 0)
+ if (i2c_wait(base, I2C_WRITE_BIT) < 0)
break;
}
return i;
}
-static __inline__ int
-__i2c_read(struct i2c_adapter *adap, u8 *data, int length)
+static inline int
+__i2c_read_data(const struct fsl_i2c_base *base, u8 *data, int length)
{
- struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
int i;
writeb(I2C_CR_MEN | I2C_CR_MSTA | ((length == 1) ? I2C_CR_TXAK : 0),
- &dev->cr);
+ &base->cr);
/* dummy read */
- readb(&dev->dr);
+ readb(&base->dr);
for (i = 0; i < length; i++) {
- if (i2c_wait(adap, I2C_READ_BIT) < 0)
+ if (i2c_wait(base, I2C_READ_BIT) < 0)
break;
/* Generate ack on last next to last byte */
if (i == length - 2)
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_TXAK,
- &dev->cr);
+ &base->cr);
/* Do not generate stop on last byte */
if (i == length - 1)
writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX,
- &dev->cr);
+ &base->cr);
- data[i] = readb(&dev->dr);
+ data[i] = readb(&base->dr);
}
return i;
}
static int
-fsl_i2c_read(struct i2c_adapter *adap, u8 dev, uint addr, int alen, u8 *data,
- int length)
+__i2c_read(const struct fsl_i2c_base *base, u8 chip_addr, u8 *offset, int olen,
+ u8 *data, int dlen)
{
- struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
- int i = -1; /* signal error */
- u8 *a = (u8*)&addr;
- int len = alen * -1;
+ int ret = -1; /* signal error */
- if (i2c_wait4bus(adap) < 0)
+ if (i2c_wait4bus(base) < 0)
return -1;
- /* To handle the need of I2C devices that require to write few bytes
- * (more than 4 bytes of address as in the case of else part)
- * of data before reading, Negative equivalent of length(bytes to write)
- * is passed, but used the +ve part of len for writing data
+ /* Some drivers use offset lengths in excess of 4 bytes. These drivers
+ * adhere to the following convention:
+ * - the offset length is passed as negative (that is, the absolute
+ * value of olen is the actual offset length)
+ * - the offset itself is passed in data, which is overwritten by the
+ * subsequent read operation
*/
- if (alen < 0) {
- /* Generate a START and send the Address and
- * the Tx Bytes to the slave.
- * "START: Address: Write bytes data[len]"
- * IF part supports writing any number of bytes in contrast
- * to the else part, which supports writing address offset
- * of upto 4 bytes only.
- * bytes that need to be written are passed in
- * "data", which will eventually keep the data READ,
- * after writing the len bytes out of it
- */
- if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0)
- i = __i2c_write(adap, data, len);
-
- if (i != len)
+ if (olen < 0) {
+ if (i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0)
+ ret = __i2c_write_data(base, data, -olen);
+
+ if (ret != -olen)
return -1;
- if (length && i2c_write_addr(adap, dev, I2C_READ_BIT, 1) != 0)
- i = __i2c_read(adap, data, length);
+ if (dlen && i2c_write_addr(base, chip_addr,
+ I2C_READ_BIT, 1) != 0)
+ ret = __i2c_read_data(base, data, dlen);
} else {
- if ((!length || alen > 0) &&
- i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 &&
- __i2c_write(adap, &a[4 - alen], alen) == alen)
- i = 0; /* No error so far */
-
- if (length &&
- i2c_write_addr(adap, dev, I2C_READ_BIT, alen ? 1 : 0) != 0)
- i = __i2c_read(adap, data, length);
+ if ((!dlen || olen > 0) &&
+ i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0 &&
+ __i2c_write_data(base, offset, olen) == olen)
+ ret = 0; /* No error so far */
+
+ if (dlen && i2c_write_addr(base, chip_addr, I2C_READ_BIT,
+ olen ? 1 : 0) != 0)
+ ret = __i2c_read_data(base, data, dlen);
}
- writeb(I2C_CR_MEN, &device->cr);
+ writeb(I2C_CR_MEN, &base->cr);
- if (i2c_wait4bus(adap)) /* Wait until STOP */
+ if (i2c_wait4bus(base)) /* Wait until STOP */
debug("i2c_read: wait4bus timed out\n");
- if (i == length)
- return 0;
+ if (ret == dlen)
+ return 0;
return -1;
}
static int
-fsl_i2c_write(struct i2c_adapter *adap, u8 dev, uint addr, int alen,
- u8 *data, int length)
+__i2c_write(const struct fsl_i2c_base *base, u8 chip_addr, u8 *offset, int olen,
+ u8 *data, int dlen)
{
- struct fsl_i2c *device = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
- int i = -1; /* signal error */
- u8 *a = (u8*)&addr;
+ int ret = -1; /* signal error */
- if (i2c_wait4bus(adap) < 0)
+ if (i2c_wait4bus(base) < 0)
return -1;
- if (i2c_write_addr(adap, dev, I2C_WRITE_BIT, 0) != 0 &&
- __i2c_write(adap, &a[4 - alen], alen) == alen) {
- i = __i2c_write(adap, data, length);
+ if (i2c_write_addr(base, chip_addr, I2C_WRITE_BIT, 0) != 0 &&
+ __i2c_write_data(base, offset, olen) == olen) {
+ ret = __i2c_write_data(base, data, dlen);
}
- writeb(I2C_CR_MEN, &device->cr);
- if (i2c_wait4bus(adap)) /* Wait until STOP */
+ writeb(I2C_CR_MEN, &base->cr);
+ if (i2c_wait4bus(base)) /* Wait until STOP */
debug("i2c_write: wait4bus timed out\n");
- if (i == length)
- return 0;
+ if (ret == dlen)
+ return 0;
return -1;
}
static int
-fsl_i2c_probe(struct i2c_adapter *adap, uchar chip)
+__i2c_probe_chip(const struct fsl_i2c_base *base, uchar chip)
{
- struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
/* For unknow reason the controller will ACK when
* probing for a slave with the same address, so skip
* it.
*/
- if (chip == (readb(&dev->adr) >> 1))
+ if (chip == (readb(&base->adr) >> 1))
return -1;
- return fsl_i2c_read(adap, chip, 0, 0, NULL, 0);
+ return __i2c_read(base, chip, 0, 0, NULL, 0);
}
-static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap,
- unsigned int speed)
+static unsigned int __i2c_set_bus_speed(const struct fsl_i2c_base *base,
+ unsigned int speed, int i2c_clk)
{
- struct fsl_i2c *dev = (struct fsl_i2c *)i2c_dev[adap->hwadapnr];
-
- writeb(0, &dev->cr); /* stop controller */
- set_i2c_bus_speed(dev, get_i2c_clock(adap->hwadapnr), speed);
- writeb(I2C_CR_MEN, &dev->cr); /* start controller */
+ writeb(0, &base->cr); /* stop controller */
+ set_i2c_bus_speed(base, i2c_clk, speed);
+ writeb(I2C_CR_MEN, &base->cr); /* start controller */
return 0;
}
+#ifndef CONFIG_DM_I2C
+static void fsl_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
+{
+ __i2c_init(i2c_base[adap->hwadapnr], speed, slaveadd,
+ get_i2c_clock(adap->hwadapnr), adap->hwadapnr);
+}
+
+static int
+fsl_i2c_probe_chip(struct i2c_adapter *adap, uchar chip)
+{
+ return __i2c_probe_chip(i2c_base[adap->hwadapnr], chip);
+}
+
+static int
+fsl_i2c_read(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
+ u8 *data, int dlen)
+{
+ u8 *o = (u8 *)&offset;
+ return __i2c_read(i2c_base[adap->hwadapnr], chip_addr, &o[4 - olen],
+ olen, data, dlen);
+}
+
+static int
+fsl_i2c_write(struct i2c_adapter *adap, u8 chip_addr, uint offset, int olen,
+ u8 *data, int dlen)
+{
+ u8 *o = (u8 *)&offset;
+ return __i2c_write(i2c_base[adap->hwadapnr], chip_addr, &o[4 - olen],
+ olen, data, dlen);
+}
+
+static unsigned int fsl_i2c_set_bus_speed(struct i2c_adapter *adap,
+ unsigned int speed)
+{
+ return __i2c_set_bus_speed(i2c_base[adap->hwadapnr], speed,
+ get_i2c_clock(adap->hwadapnr));
+}
+
/*
* Register fsl i2c adapters
*/
-U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_0, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
fsl_i2c_write, fsl_i2c_set_bus_speed,
CONFIG_SYS_FSL_I2C_SPEED, CONFIG_SYS_FSL_I2C_SLAVE,
0)
#ifdef CONFIG_SYS_FSL_I2C2_OFFSET
-U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_1, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
fsl_i2c_write, fsl_i2c_set_bus_speed,
CONFIG_SYS_FSL_I2C2_SPEED, CONFIG_SYS_FSL_I2C2_SLAVE,
1)
#endif
#ifdef CONFIG_SYS_FSL_I2C3_OFFSET
-U_BOOT_I2C_ADAP_COMPLETE(fsl_2, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_2, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
fsl_i2c_write, fsl_i2c_set_bus_speed,
CONFIG_SYS_FSL_I2C3_SPEED, CONFIG_SYS_FSL_I2C3_SLAVE,
2)
#endif
#ifdef CONFIG_SYS_FSL_I2C4_OFFSET
-U_BOOT_I2C_ADAP_COMPLETE(fsl_3, fsl_i2c_init, fsl_i2c_probe, fsl_i2c_read,
+U_BOOT_I2C_ADAP_COMPLETE(fsl_3, fsl_i2c_init, fsl_i2c_probe_chip, fsl_i2c_read,
fsl_i2c_write, fsl_i2c_set_bus_speed,
CONFIG_SYS_FSL_I2C4_SPEED, CONFIG_SYS_FSL_I2C4_SLAVE,
3)
#endif
+#else /* CONFIG_DM_I2C */
+static int fsl_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
+ u32 chip_flags)
+{
+ struct fsl_i2c_dev *dev = dev_get_priv(bus);
+ return __i2c_probe_chip(dev->base, chip_addr);
+}
+
+static int fsl_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
+{
+ struct fsl_i2c_dev *dev = dev_get_priv(bus);
+ return __i2c_set_bus_speed(dev->base, speed, dev->i2c_clk);
+}
+
+static int fsl_i2c_ofdata_to_platdata(struct udevice *bus)
+{
+ struct fsl_i2c_dev *dev = dev_get_priv(bus);
+ u64 reg;
+ u32 addr, size;
+
+ reg = fdtdec_get_addr(gd->fdt_blob, bus->of_offset, "reg");
+ addr = reg >> 32;
+ size = reg & 0xFFFFFFFF;
+
+ dev->base = map_sysmem(CONFIG_SYS_IMMR + addr, size);
+
+ if (!dev->base)
+ return -ENOMEM;
+
+ dev->index = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
+ "cell-index", -1);
+ dev->slaveadd = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
+ "u-boot,i2c-slave-addr", 0x7f);
+ dev->speed = fdtdec_get_int(gd->fdt_blob, bus->of_offset,
+ "clock-frequency", 400000);
+
+ dev->i2c_clk = dev->index ? gd->arch.i2c2_clk : gd->arch.i2c1_clk;
+
+ return 0;
+}
+
+static int fsl_i2c_probe(struct udevice *bus)
+{
+ struct fsl_i2c_dev *dev = dev_get_priv(bus);
+ __i2c_init(dev->base, dev->speed, dev->slaveadd, dev->i2c_clk,
+ dev->index);
+ return 0;
+}
+
+static int fsl_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
+{
+ struct fsl_i2c_dev *dev = dev_get_priv(bus);
+ struct i2c_msg *dmsg, *omsg, dummy;
+
+ memset(&dummy, 0, sizeof(struct i2c_msg));
+
+ /* We expect either two messages (one with an offset and one with the
+ * actucal data) or one message (just data) */
+ if (nmsgs > 2 || nmsgs == 0) {
+ debug("%s: Only one or two messages are supported.", __func__);
+ return -1;
+ }
+
+ omsg = nmsgs == 1 ? &dummy : msg;
+ dmsg = nmsgs == 1 ? msg : msg + 1;
+
+ if (dmsg->flags & I2C_M_RD)
+ return __i2c_read(dev->base, dmsg->addr, omsg->buf, omsg->len,
+ dmsg->buf, dmsg->len);
+ else
+ return __i2c_write(dev->base, dmsg->addr, omsg->buf, omsg->len,
+ dmsg->buf, dmsg->len);
+}
+
+static const struct dm_i2c_ops fsl_i2c_ops = {
+ .xfer = fsl_i2c_xfer,
+ .probe_chip = fsl_i2c_probe_chip,
+ .set_bus_speed = fsl_i2c_set_bus_speed,
+};
+
+static const struct udevice_id fsl_i2c_ids[] = {
+ { .compatible = "fsl-i2c", },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(i2c_fsl) = {
+ .name = "i2c_fsl",
+ .id = UCLASS_I2C,
+ .of_match = fsl_i2c_ids,
+ .probe = fsl_i2c_probe,
+ .ofdata_to_platdata = fsl_i2c_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct fsl_i2c_dev),
+ .ops = &fsl_i2c_ops,
+};
+
+#endif /* CONFIG_DM_I2C */
diff --git a/drivers/i2c/i2c-cdns.c b/drivers/i2c/i2c-cdns.c
index 909cea2418..5642cd91fe 100644
--- a/drivers/i2c/i2c-cdns.c
+++ b/drivers/i2c/i2c-cdns.c
@@ -112,48 +112,10 @@ static void cdns_i2c_debug_status(struct cdns_i2c_regs *cdns_i2c)
struct i2c_cdns_bus {
int id;
+ unsigned int input_freq;
struct cdns_i2c_regs __iomem *regs; /* register base */
};
-
-/** cdns_i2c_probe() - Probe method
- * @dev: udevice pointer
- *
- * DM callback called when device is probed
- */
-static int cdns_i2c_probe(struct udevice *dev)
-{
- struct i2c_cdns_bus *bus = dev_get_priv(dev);
-
- bus->regs = (struct cdns_i2c_regs *)dev_get_addr(dev);
- if (!bus->regs)
- return -ENOMEM;
-
- /* TODO: Calculate dividers based on CPU_CLK_1X */
- /* 111MHz / ( (3 * 17) * 22 ) = ~100KHz */
- writel((16 << CDNS_I2C_CONTROL_DIV_B_SHIFT) |
- (2 << CDNS_I2C_CONTROL_DIV_A_SHIFT), &bus->regs->control);
-
- /* Enable master mode, ack, and 7-bit addressing */
- setbits_le32(&bus->regs->control, CDNS_I2C_CONTROL_MS |
- CDNS_I2C_CONTROL_ACKEN | CDNS_I2C_CONTROL_NEA);
-
- debug("%s bus %d at %p\n", __func__, dev->seq, bus->regs);
-
- return 0;
-}
-
-static int cdns_i2c_remove(struct udevice *dev)
-{
- struct i2c_cdns_bus *bus = dev_get_priv(dev);
-
- debug("%s bus %d at %p\n", __func__, dev->seq, bus->regs);
-
- unmap_sysmem(bus->regs);
-
- return 0;
-}
-
/* Wait for an interrupt */
static u32 cdns_i2c_wait(struct cdns_i2c_regs *cdns_i2c, u32 mask)
{
@@ -172,14 +134,84 @@ static u32 cdns_i2c_wait(struct cdns_i2c_regs *cdns_i2c, u32 mask)
return int_status & mask;
}
+#define CDNS_I2C_DIVA_MAX 4
+#define CDNS_I2C_DIVB_MAX 64
+
+static int cdns_i2c_calc_divs(unsigned long *f, unsigned long input_clk,
+ unsigned int *a, unsigned int *b)
+{
+ unsigned long fscl = *f, best_fscl = *f, actual_fscl, temp;
+ unsigned int div_a, div_b, calc_div_a = 0, calc_div_b = 0;
+ unsigned int last_error, current_error;
+
+ /* calculate (divisor_a+1) x (divisor_b+1) */
+ temp = input_clk / (22 * fscl);
+
+ /*
+ * If the calculated value is negative or 0CDNS_I2C_DIVA_MAX,
+ * the fscl input is out of range. Return error.
+ */
+ if (!temp || (temp > (CDNS_I2C_DIVA_MAX * CDNS_I2C_DIVB_MAX)))
+ return -EINVAL;
+
+ last_error = -1;
+ for (div_a = 0; div_a < CDNS_I2C_DIVA_MAX; div_a++) {
+ div_b = DIV_ROUND_UP(input_clk, 22 * fscl * (div_a + 1));
+
+ if ((div_b < 1) || (div_b > CDNS_I2C_DIVB_MAX))
+ continue;
+ div_b--;
+
+ actual_fscl = input_clk / (22 * (div_a + 1) * (div_b + 1));
+
+ if (actual_fscl > fscl)
+ continue;
+
+ current_error = ((actual_fscl > fscl) ? (actual_fscl - fscl) :
+ (fscl - actual_fscl));
+
+ if (last_error > current_error) {
+ calc_div_a = div_a;
+ calc_div_b = div_b;
+ best_fscl = actual_fscl;
+ last_error = current_error;
+ }
+ }
+
+ *a = calc_div_a;
+ *b = calc_div_b;
+ *f = best_fscl;
+
+ return 0;
+}
+
static int cdns_i2c_set_bus_speed(struct udevice *dev, unsigned int speed)
{
- if (speed != 100000) {
- printf("%s, failed to set clock speed to %u\n", __func__,
- speed);
+ struct i2c_cdns_bus *bus = dev_get_priv(dev);
+ u32 div_a = 0, div_b = 0;
+ unsigned long speed_p = speed;
+ int ret = 0;
+
+ if (speed > 400000) {
+ debug("%s, failed to set clock speed to %u\n", __func__,
+ speed);
return -EINVAL;
}
+ ret = cdns_i2c_calc_divs(&speed_p, bus->input_freq, &div_a, &div_b);
+ if (ret)
+ return ret;
+
+ debug("%s: div_a: %d, div_b: %d, input freq: %d, speed: %d/%ld\n",
+ __func__, div_a, div_b, bus->input_freq, speed, speed_p);
+
+ writel((div_b << CDNS_I2C_CONTROL_DIV_B_SHIFT) |
+ (div_a << CDNS_I2C_CONTROL_DIV_A_SHIFT), &bus->regs->control);
+
+ /* Enable master mode, ack, and 7-bit addressing */
+ setbits_le32(&bus->regs->control, CDNS_I2C_CONTROL_MS |
+ CDNS_I2C_CONTROL_ACKEN | CDNS_I2C_CONTROL_NEA);
+
return 0;
}
@@ -313,6 +345,19 @@ static int cdns_i2c_xfer(struct udevice *dev, struct i2c_msg *msg,
return 0;
}
+static int cdns_i2c_ofdata_to_platdata(struct udevice *dev)
+{
+ struct i2c_cdns_bus *i2c_bus = dev_get_priv(dev);
+
+ i2c_bus->regs = (struct cdns_i2c_regs *)dev_get_addr(dev);
+ if (!i2c_bus->regs)
+ return -ENOMEM;
+
+ i2c_bus->input_freq = 100000000; /* TODO hardcode input freq for now */
+
+ return 0;
+}
+
static const struct dm_i2c_ops cdns_i2c_ops = {
.xfer = cdns_i2c_xfer,
.probe_chip = cdns_i2c_probe_chip,
@@ -328,8 +373,7 @@ U_BOOT_DRIVER(cdns_i2c) = {
.name = "i2c-cdns",
.id = UCLASS_I2C,
.of_match = cdns_i2c_of_match,
- .probe = cdns_i2c_probe,
- .remove = cdns_i2c_remove,
+ .ofdata_to_platdata = cdns_i2c_ofdata_to_platdata,
.priv_auto_alloc_size = sizeof(struct i2c_cdns_bus),
.ops = &cdns_i2c_ops,
};
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index f959d9de9e..48900ed2af 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -24,3 +24,13 @@ config I2C_ARB_GPIO_CHALLENGE
I2C multimaster arbitration scheme using GPIOs and a challenge &
response mechanism where masters have to claim the bus by asserting
a GPIO.
+
+config I2C_MUX_PCA954x
+ tristate "TI PCA954x I2C Mux/switches"
+ depends on I2C_MUX
+ help
+ If you say yes here you get support for the TI PCA954x
+ I2C mux/switch devices. It is x width I2C multiplexer which enables to
+ paritioning I2C bus and connect multiple devices with the same address
+ to the same I2C controller where driver handles proper routing to
+ target i2c device. PCA9544 and PCA9548 are supported.
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 47c1240d7e..0811add421 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -5,3 +5,4 @@
#
obj-$(CONFIG_I2C_ARB_GPIO_CHALLENGE) += i2c-arb-gpio-challenge.o
obj-$(CONFIG_$(SPL_)I2C_MUX) += i2c-mux-uclass.o
+obj-$(CONFIG_I2C_MUX_PCA954x) += pca954x.o
diff --git a/drivers/i2c/muxes/pca954x.c b/drivers/i2c/muxes/pca954x.c
new file mode 100644
index 0000000000..7e0d2da4d6
--- /dev/null
+++ b/drivers/i2c/muxes/pca954x.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 - 2016 Xilinx, Inc.
+ * Written by Michal Simek
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <i2c.h>
+#include <asm/gpio.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct pca954x_priv {
+ u32 addr; /* I2C mux address */
+ u32 width; /* I2C mux width - number of busses */
+};
+
+static int pca954x_deselect(struct udevice *mux, struct udevice *bus,
+ uint channel)
+{
+ struct pca954x_priv *priv = dev_get_priv(mux);
+ uchar byte = 0;
+
+ return dm_i2c_write(mux, priv->addr, &byte, 1);
+}
+
+static int pca954x_select(struct udevice *mux, struct udevice *bus,
+ uint channel)
+{
+ struct pca954x_priv *priv = dev_get_priv(mux);
+ uchar byte = 1 << channel;
+
+ return dm_i2c_write(mux, priv->addr, &byte, 1);
+}
+
+static const struct i2c_mux_ops pca954x_ops = {
+ .select = pca954x_select,
+ .deselect = pca954x_deselect,
+};
+
+static const struct udevice_id pca954x_ids[] = {
+ { .compatible = "nxp,pca9548", .data = (ulong)8 },
+ { .compatible = "nxp,pca9544", .data = (ulong)4 },
+ { }
+};
+
+static int pca954x_ofdata_to_platdata(struct udevice *dev)
+{
+ struct pca954x_priv *priv = dev_get_priv(dev);
+
+ priv->addr = fdtdec_get_int(gd->fdt_blob, dev->of_offset, "reg", 0);
+ if (!priv->addr) {
+ debug("MUX not found\n");
+ return -ENODEV;
+ }
+ priv->width = dev_get_driver_data(dev);
+
+ if (!priv->width) {
+ debug("No I2C MUX width specified\n");
+ return -EINVAL;
+ }
+
+ debug("Device %s at 0x%x with width %d\n",
+ dev->name, priv->addr, priv->width);
+
+ return 0;
+}
+
+U_BOOT_DRIVER(pca954x) = {
+ .name = "pca954x",
+ .id = UCLASS_I2C_MUX,
+ .of_match = pca954x_ids,
+ .ops = &pca954x_ops,
+ .ofdata_to_platdata = pca954x_ofdata_to_platdata,
+ .priv_auto_alloc_size = sizeof(struct pca954x_priv),
+};
diff --git a/drivers/i2c/mvtwsi.c b/drivers/i2c/mvtwsi.c
index 221ff4fe7a..bf4443287f 100644
--- a/drivers/i2c/mvtwsi.c
+++ b/drivers/i2c/mvtwsi.c
@@ -185,26 +185,17 @@ static int twsi_wait(struct i2c_adapter *adap, int expected_status)
}
/*
- * These flags are ORed to any write to the control register
- * They allow global setting of TWSIEN and ACK.
- * By default none are set.
- * twsi_start() sets TWSIEN (in case the controller was disabled)
- * twsi_recv() sets ACK or resets it depending on expected status.
- */
-static u8 twsi_control_flags = MVTWSI_CONTROL_TWSIEN;
-
-/*
* Assert the START condition, either in a single I2C transaction
* or inside back-to-back ones (repeated starts).
*/
-static int twsi_start(struct i2c_adapter *adap, int expected_status)
+static int twsi_start(struct i2c_adapter *adap, int expected_status, u8 *flags)
{
struct mvtwsi_registers *twsi = twsi_get_base(adap);
/* globally set TWSIEN in case it was not */
- twsi_control_flags |= MVTWSI_CONTROL_TWSIEN;
+ *flags |= MVTWSI_CONTROL_TWSIEN;
/* assert START */
- writel(twsi_control_flags | MVTWSI_CONTROL_START |
+ writel(*flags | MVTWSI_CONTROL_START |
MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
/* wait for controller to process START */
return twsi_wait(adap, expected_status);
@@ -213,14 +204,15 @@ static int twsi_start(struct i2c_adapter *adap, int expected_status)
/*
* Send a byte (i2c address or data).
*/
-static int twsi_send(struct i2c_adapter *adap, u8 byte, int expected_status)
+static int twsi_send(struct i2c_adapter *adap, u8 byte, int expected_status,
+ u8 *flags)
{
struct mvtwsi_registers *twsi = twsi_get_base(adap);
/* put byte in data register for sending */
writel(byte, &twsi->data);
/* clear any pending interrupt -- that'll cause sending */
- writel(twsi_control_flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
+ writel(*flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
/* wait for controller to receive byte and check ACK */
return twsi_wait(adap, expected_status);
}
@@ -229,18 +221,18 @@ static int twsi_send(struct i2c_adapter *adap, u8 byte, int expected_status)
* Receive a byte.
* Global mvtwsi_control_flags variable says if we should ack or nak.
*/
-static int twsi_recv(struct i2c_adapter *adap, u8 *byte)
+static int twsi_recv(struct i2c_adapter *adap, u8 *byte, u8 *flags)
{
struct mvtwsi_registers *twsi = twsi_get_base(adap);
int expected_status, status;
/* compute expected status based on ACK bit in global control flags */
- if (twsi_control_flags & MVTWSI_CONTROL_ACK)
+ if (*flags & MVTWSI_CONTROL_ACK)
expected_status = MVTWSI_STATUS_DATA_R_ACK;
else
expected_status = MVTWSI_STATUS_DATA_R_NAK;
/* acknowledge *previous state* and launch receive */
- writel(twsi_control_flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
+ writel(*flags | MVTWSI_CONTROL_CLEAR_IFLG, &twsi->control);
/* wait for controller to receive byte and assert ACK or NAK */
status = twsi_wait(adap, expected_status);
/* if we did receive expected byte then store it */
@@ -296,8 +288,7 @@ static unsigned int twsi_calc_freq(const int n, const int m)
static void twsi_reset(struct i2c_adapter *adap)
{
struct mvtwsi_registers *twsi = twsi_get_base(adap);
- /* ensure controller will be enabled by any twsi*() function */
- twsi_control_flags = MVTWSI_CONTROL_TWSIEN;
+
/* reset controller */
writel(0, &twsi->soft_reset);
/* wait 2 ms -- this is what the Marvell LSP does */
@@ -353,7 +344,7 @@ static void twsi_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd)
* Expected address status will derive from direction bit (bit 0) in addr.
*/
static int i2c_begin(struct i2c_adapter *adap, int expected_start_status,
- u8 addr)
+ u8 addr, u8 *flags)
{
int status, expected_addr_status;
@@ -363,10 +354,11 @@ static int i2c_begin(struct i2c_adapter *adap, int expected_start_status,
else /* writing */
expected_addr_status = MVTWSI_STATUS_ADDR_W_ACK;
/* assert START */
- status = twsi_start(adap, expected_start_status);
+ status = twsi_start(adap, expected_start_status, flags);
/* send out the address if the start went well */
if (status == 0)
- status = twsi_send(adap, addr, expected_addr_status);
+ status = twsi_send(adap, addr, expected_addr_status,
+ flags);
/* return ok or status of first failure to caller */
return status;
}
@@ -378,13 +370,14 @@ static int i2c_begin(struct i2c_adapter *adap, int expected_start_status,
static int twsi_i2c_probe(struct i2c_adapter *adap, uchar chip)
{
u8 dummy_byte;
+ u8 flags = 0;
int status;
/* begin i2c read */
- status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1) | 1);
+ status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1) | 1, &flags);
/* dummy read was accepted: receive byte but NAK it. */
if (status == 0)
- status = twsi_recv(adap, &dummy_byte);
+ status = twsi_recv(adap, &dummy_byte, &flags);
/* Stop transaction */
twsi_stop(adap, 0);
/* return 0 or status of first failure */
@@ -405,27 +398,28 @@ static int twsi_i2c_read(struct i2c_adapter *adap, uchar chip, uint addr,
int alen, uchar *data, int length)
{
int status;
+ u8 flags = 0;
/* begin i2c write to send the address bytes */
- status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1));
+ status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1), &flags);
/* send addr bytes */
while ((status == 0) && alen--)
status = twsi_send(adap, addr >> (8*alen),
- MVTWSI_STATUS_DATA_W_ACK);
+ MVTWSI_STATUS_DATA_W_ACK, &flags);
/* begin i2c read to receive eeprom data bytes */
if (status == 0)
status = i2c_begin(adap, MVTWSI_STATUS_REPEATED_START,
- (chip << 1) | 1);
+ (chip << 1) | 1, &flags);
/* prepare ACK if at least one byte must be received */
if (length > 0)
- twsi_control_flags |= MVTWSI_CONTROL_ACK;
+ flags |= MVTWSI_CONTROL_ACK;
/* now receive actual bytes */
while ((status == 0) && length--) {
/* reset NAK if we if no more to read now */
if (length == 0)
- twsi_control_flags &= ~MVTWSI_CONTROL_ACK;
+ flags &= ~MVTWSI_CONTROL_ACK;
/* read current byte */
- status = twsi_recv(adap, data++);
+ status = twsi_recv(adap, data++, &flags);
}
/* Stop transaction */
status = twsi_stop(adap, status);
@@ -441,16 +435,18 @@ static int twsi_i2c_write(struct i2c_adapter *adap, uchar chip, uint addr,
int alen, uchar *data, int length)
{
int status;
+ u8 flags = 0;
/* begin i2c write to send the eeprom adress bytes then data bytes */
- status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1));
+ status = i2c_begin(adap, MVTWSI_STATUS_START, (chip << 1), &flags);
/* send addr bytes */
while ((status == 0) && alen--)
status = twsi_send(adap, addr >> (8*alen),
- MVTWSI_STATUS_DATA_W_ACK);
+ MVTWSI_STATUS_DATA_W_ACK, &flags);
/* send data bytes */
while ((status == 0) && (length-- > 0))
- status = twsi_send(adap, *(data++), MVTWSI_STATUS_DATA_W_ACK);
+ status = twsi_send(adap, *(data++), MVTWSI_STATUS_DATA_W_ACK,
+ &flags);
/* Stop transaction */
status = twsi_stop(adap, status);
/* return 0 or status of first failure */
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 4d3df11a1b..c80efc39a7 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -2,7 +2,7 @@ menu "MMC Host controller Support"
config MMC
bool "Enable MMC support"
- depends on ARCH_SUNXI
+ depends on ARCH_SUNXI || SANDBOX
help
TODO: Move all architectures to use this option
@@ -58,4 +58,13 @@ config MMC_UNIPHIER
help
This selects support for the SD/MMC Host Controller on UniPhier SoCs.
+config SANDBOX_MMC
+ bool "Sandbox MMC support"
+ depends on MMC && SANDBOX
+ help
+ This select a dummy sandbox MMC driver. At present this does nothing
+ other than allow sandbox to be build with MMC support. This
+ improves build coverage for sandbox and makes it easier to detect
+ MMC build errors with sandbox.
+
endmenu
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 585aaf3115..3da4817a18 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -5,7 +5,13 @@
# SPDX-License-Identifier: GPL-2.0+
#
-obj-$(CONFIG_DM_MMC) += mmc-uclass.o
+ifdef CONFIG_DM_MMC
+obj-$(CONFIG_GENERIC_MMC) += mmc-uclass.o
+endif
+
+ifndef CONFIG_BLK
+obj-$(CONFIG_GENERIC_MMC) += mmc_legacy.o
+endif
obj-$(CONFIG_ARM_PL180_MMCI) += arm_pl180_mmci.o
obj-$(CONFIG_ATMEL_SDHCI) += atmel_sdhci.o
@@ -34,7 +40,11 @@ obj-$(CONFIG_ROCKCHIP_DWMMC) += rockchip_dw_mmc.o
obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
obj-$(CONFIG_S3C_SDI) += s3c_sdi.o
obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o
+ifdef CONFIG_BLK
+ifdef CONFIG_GENERIC_MMC
obj-$(CONFIG_SANDBOX) += sandbox_mmc.o
+endif
+endif
obj-$(CONFIG_SDHCI) += sdhci.o
obj-$(CONFIG_SH_MMCIF) += sh_mmcif.o
obj-$(CONFIG_SH_SDHI) += sh_sdhi.o
diff --git a/drivers/mmc/mmc-uclass.c b/drivers/mmc/mmc-uclass.c
index 777489f5d8..1b967d982b 100644
--- a/drivers/mmc/mmc-uclass.c
+++ b/drivers/mmc/mmc-uclass.c
@@ -21,6 +21,112 @@ struct mmc *mmc_get_mmc_dev(struct udevice *dev)
return upriv->mmc;
}
+#ifdef CONFIG_BLK
+struct mmc *find_mmc_device(int dev_num)
+{
+ struct udevice *dev, *mmc_dev;
+ int ret;
+
+ ret = blk_get_device(IF_TYPE_MMC, dev_num, &dev);
+
+ if (ret) {
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ printf("MMC Device %d not found\n", dev_num);
+#endif
+ return NULL;
+ }
+
+ mmc_dev = dev_get_parent(dev);
+
+ return mmc_get_mmc_dev(mmc_dev);
+}
+
+int get_mmc_num(void)
+{
+ return max(blk_find_max_devnum(IF_TYPE_MMC), 0);
+}
+
+int mmc_get_next_devnum(void)
+{
+ int ret;
+
+ ret = get_mmc_num();
+ if (ret < 0)
+ return ret;
+
+ return ret + 1;
+}
+
+struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
+{
+ struct blk_desc *desc;
+ struct udevice *dev;
+
+ device_find_first_child(mmc->dev, &dev);
+ if (!dev)
+ return NULL;
+ desc = dev_get_uclass_platdata(dev);
+
+ return desc;
+}
+
+void mmc_do_preinit(void)
+{
+ struct udevice *dev;
+ struct uclass *uc;
+ int ret;
+
+ ret = uclass_get(UCLASS_MMC, &uc);
+ if (ret)
+ return;
+ uclass_foreach_dev(dev, uc) {
+ struct mmc *m = mmc_get_mmc_dev(dev);
+
+ if (!m)
+ continue;
+#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
+ mmc_set_preinit(m, 1);
+#endif
+ if (m->preinit)
+ mmc_start_init(m);
+ }
+}
+
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+void print_mmc_devices(char separator)
+{
+ struct udevice *dev;
+ char *mmc_type;
+ bool first = true;
+
+ for (uclass_first_device(UCLASS_MMC, &dev);
+ dev;
+ uclass_next_device(&dev)) {
+ struct mmc *m = mmc_get_mmc_dev(dev);
+
+ if (!first) {
+ printf("%c", separator);
+ if (separator != '\n')
+ puts(" ");
+ }
+ if (m->has_init)
+ mmc_type = IS_SD(m) ? "SD" : "eMMC";
+ else
+ mmc_type = NULL;
+
+ printf("%s: %d", m->cfg->name, mmc_get_blk_desc(m)->devnum);
+ if (mmc_type)
+ printf(" (%s)", mmc_type);
+ }
+
+ printf("\n");
+}
+
+#else
+void print_mmc_devices(char separator) { }
+#endif
+#endif /* CONFIG_BLK */
+
U_BOOT_DRIVER(mmc) = {
.name = "mmc",
.id = UCLASS_MMC,
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index d3c22abfd5..74b3d68f87 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -21,9 +21,6 @@
#include <div64.h>
#include "mmc_private.h"
-static struct list_head mmc_devices;
-static int cur_dev_num = -1;
-
__weak int board_mmc_getwp(struct mmc *mmc)
{
return -1;
@@ -178,25 +175,6 @@ int mmc_set_blocklen(struct mmc *mmc, int len)
return mmc_send_cmd(mmc, &cmd, NULL);
}
-struct mmc *find_mmc_device(int dev_num)
-{
- struct mmc *m;
- struct list_head *entry;
-
- list_for_each(entry, &mmc_devices) {
- m = list_entry(entry, struct mmc, link);
-
- if (m->block_dev.devnum == dev_num)
- return m;
- }
-
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
- printf("MMC Device %d not found\n", dev_num);
-#endif
-
- return NULL;
-}
-
static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
lbaint_t blkcnt)
{
@@ -238,9 +216,17 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start,
return blkcnt;
}
+#ifdef CONFIG_BLK
+static ulong mmc_bread(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
+ void *dst)
+#else
static ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt, void *dst)
+#endif
{
+#ifdef CONFIG_BLK
+ struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
+#endif
int dev_num = block_dev->devnum;
int err;
lbaint_t cur, blocks_todo = blkcnt;
@@ -252,14 +238,14 @@ static ulong mmc_bread(struct blk_desc *block_dev, lbaint_t start,
if (!mmc)
return 0;
- err = mmc_select_hwpart(dev_num, block_dev->hwpart);
+ err = blk_dselect_hwpart(block_dev, block_dev->hwpart);
if (err < 0)
return 0;
- if ((start + blkcnt) > mmc->block_dev.lba) {
+ if ((start + blkcnt) > block_dev->lba) {
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
- start + blkcnt, mmc->block_dev.lba);
+ start + blkcnt, block_dev->lba);
#endif
return 0;
}
@@ -577,58 +563,73 @@ static int mmc_set_capacity(struct mmc *mmc, int part_num)
return -1;
}
- mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
+ mmc_get_blk_desc(mmc)->lba = lldiv(mmc->capacity, mmc->read_bl_len);
return 0;
}
-int mmc_select_hwpart(int dev_num, int hwpart)
+static int mmc_switch_part(struct mmc *mmc, unsigned int part_num)
{
- struct mmc *mmc = find_mmc_device(dev_num);
int ret;
- if (!mmc)
- return -ENODEV;
+ ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
+ (mmc->part_config & ~PART_ACCESS_MASK)
+ | (part_num & PART_ACCESS_MASK));
- if (mmc->block_dev.hwpart == hwpart)
+ /*
+ * Set the capacity if the switch succeeded or was intended
+ * to return to representing the raw device.
+ */
+ if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) {
+ ret = mmc_set_capacity(mmc, part_num);
+ mmc_get_blk_desc(mmc)->hwpart = part_num;
+ }
+
+ return ret;
+}
+
+#ifdef CONFIG_BLK
+static int mmc_select_hwpart(struct udevice *bdev, int hwpart)
+{
+ struct udevice *mmc_dev = dev_get_parent(bdev);
+ struct mmc *mmc = mmc_get_mmc_dev(mmc_dev);
+ struct blk_desc *desc = dev_get_uclass_platdata(bdev);
+ int ret;
+
+ if (desc->hwpart == hwpart)
return 0;
- if (mmc->part_config == MMCPART_NOAVAILABLE) {
- printf("Card doesn't support part_switch\n");
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
return -EMEDIUMTYPE;
- }
- ret = mmc_switch_part(dev_num, hwpart);
+ ret = mmc_switch_part(mmc, hwpart);
if (ret)
return ret;
return 0;
}
-
-
-int mmc_switch_part(int dev_num, unsigned int part_num)
+#else
+static int mmc_select_hwpartp(struct blk_desc *desc, int hwpart)
{
- struct mmc *mmc = find_mmc_device(dev_num);
+ struct mmc *mmc = find_mmc_device(desc->devnum);
int ret;
if (!mmc)
- return -1;
+ return -ENODEV;
- ret = mmc_switch(mmc, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_PART_CONF,
- (mmc->part_config & ~PART_ACCESS_MASK)
- | (part_num & PART_ACCESS_MASK));
+ if (mmc->block_dev.hwpart == hwpart)
+ return 0;
- /*
- * Set the capacity if the switch succeeded or was intended
- * to return to representing the raw device.
- */
- if ((ret == 0) || ((ret == -ENODEV) && (part_num == 0))) {
- ret = mmc_set_capacity(mmc, part_num);
- mmc->block_dev.hwpart = part_num;
- }
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
+ return -EMEDIUMTYPE;
- return ret;
+ ret = mmc_switch_part(mmc, hwpart);
+ if (ret)
+ return ret;
+
+ return 0;
}
+#endif
int mmc_hwpart_config(struct mmc *mmc,
const struct mmc_hwpart_conf *conf,
@@ -1039,6 +1040,7 @@ static int mmc_startup(struct mmc *mmc)
int timeout = 1000;
bool has_parts = false;
bool part_completed;
+ struct blk_desc *bdesc;
#ifdef CONFIG_MMC_SPI_CRC_ON
if (mmc_host_is_spi(mmc)) { /* enable CRC check for spi */
@@ -1335,7 +1337,7 @@ static int mmc_startup(struct mmc *mmc)
mmc->wr_rel_set = ext_csd[EXT_CSD_WR_REL_SET];
}
- err = mmc_set_capacity(mmc, mmc->block_dev.hwpart);
+ err = mmc_set_capacity(mmc, mmc_get_blk_desc(mmc)->hwpart);
if (err)
return err;
@@ -1475,31 +1477,32 @@ static int mmc_startup(struct mmc *mmc)
}
/* fill in device description */
- mmc->block_dev.lun = 0;
- mmc->block_dev.hwpart = 0;
- mmc->block_dev.type = 0;
- mmc->block_dev.blksz = mmc->read_bl_len;
- mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz);
- mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len);
+ bdesc = mmc_get_blk_desc(mmc);
+ bdesc->lun = 0;
+ bdesc->hwpart = 0;
+ bdesc->type = 0;
+ bdesc->blksz = mmc->read_bl_len;
+ bdesc->log2blksz = LOG2(bdesc->blksz);
+ bdesc->lba = lldiv(mmc->capacity, mmc->read_bl_len);
#if !defined(CONFIG_SPL_BUILD) || \
(defined(CONFIG_SPL_LIBCOMMON_SUPPORT) && \
!defined(CONFIG_USE_TINY_PRINTF))
- sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x",
+ sprintf(bdesc->vendor, "Man %06x Snr %04x%04x",
mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff),
(mmc->cid[3] >> 16) & 0xffff);
- sprintf(mmc->block_dev.product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
+ sprintf(bdesc->product, "%c%c%c%c%c%c", mmc->cid[0] & 0xff,
(mmc->cid[1] >> 24), (mmc->cid[1] >> 16) & 0xff,
(mmc->cid[1] >> 8) & 0xff, mmc->cid[1] & 0xff,
(mmc->cid[2] >> 24) & 0xff);
- sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
+ sprintf(bdesc->revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf,
(mmc->cid[2] >> 16) & 0xf);
#else
- mmc->block_dev.vendor[0] = 0;
- mmc->block_dev.product[0] = 0;
- mmc->block_dev.revision[0] = 0;
+ bdesc->vendor[0] = 0;
+ bdesc->product[0] = 0;
+ bdesc->revision[0] = 0;
#endif
#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT)
- part_init(&mmc->block_dev);
+ part_init(bdesc);
#endif
return 0;
@@ -1537,8 +1540,55 @@ int __deprecated mmc_register(struct mmc *mmc)
return -1;
}
+#ifdef CONFIG_BLK
+int mmc_bind(struct udevice *dev, struct mmc *mmc, const struct mmc_config *cfg)
+{
+ struct blk_desc *bdesc;
+ struct udevice *bdev;
+ int ret;
+
+ ret = blk_create_devicef(dev, "mmc_blk", "blk", IF_TYPE_MMC, -1, 512,
+ 0, &bdev);
+ if (ret) {
+ debug("Cannot create block device\n");
+ return ret;
+ }
+ bdesc = dev_get_uclass_platdata(bdev);
+ mmc->cfg = cfg;
+ mmc->priv = dev;
+
+ /* the following chunk was from mmc_register() */
+
+ /* Setup dsr related values */
+ mmc->dsr_imp = 0;
+ mmc->dsr = 0xffffffff;
+ /* Setup the universal parts of the block interface just once */
+ bdesc->removable = 1;
+
+ /* setup initial part type */
+ bdesc->part_type = mmc->cfg->part_type;
+ mmc->dev = dev;
+
+ return 0;
+}
+
+int mmc_unbind(struct udevice *dev)
+{
+ struct udevice *bdev;
+
+ device_find_first_child(dev, &bdev);
+ if (bdev) {
+ device_remove(bdev);
+ device_unbind(bdev);
+ }
+
+ return 0;
+}
+
+#else
struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
{
+ struct blk_desc *bdesc;
struct mmc *mmc;
/* quick validation */
@@ -1559,19 +1609,17 @@ struct mmc *mmc_create(const struct mmc_config *cfg, void *priv)
mmc->dsr_imp = 0;
mmc->dsr = 0xffffffff;
/* Setup the universal parts of the block interface just once */
- mmc->block_dev.if_type = IF_TYPE_MMC;
- mmc->block_dev.devnum = cur_dev_num++;
- mmc->block_dev.removable = 1;
- mmc->block_dev.block_read = mmc_bread;
- mmc->block_dev.block_write = mmc_bwrite;
- mmc->block_dev.block_erase = mmc_berase;
+ bdesc = mmc_get_blk_desc(mmc);
+ bdesc->if_type = IF_TYPE_MMC;
+ bdesc->removable = 1;
+ bdesc->devnum = mmc_get_next_devnum();
+ bdesc->block_read = mmc_bread;
+ bdesc->block_write = mmc_bwrite;
+ bdesc->block_erase = mmc_berase;
/* setup initial part type */
- mmc->block_dev.part_type = mmc->cfg->part_type;
-
- INIT_LIST_HEAD(&mmc->link);
-
- list_add_tail(&mmc->link, &mmc_devices);
+ bdesc->part_type = mmc->cfg->part_type;
+ mmc_list_add(mmc);
return mmc;
}
@@ -1581,15 +1629,23 @@ void mmc_destroy(struct mmc *mmc)
/* only freeing memory for now */
free(mmc);
}
+#endif
-#ifdef CONFIG_PARTITIONS
-struct blk_desc *mmc_get_dev(int dev)
+#ifndef CONFIG_BLK
+static int mmc_get_dev(int dev, struct blk_desc **descp)
{
struct mmc *mmc = find_mmc_device(dev);
- if (!mmc || mmc_init(mmc))
- return NULL;
+ int ret;
- return &mmc->block_dev;
+ if (!mmc)
+ return -ENODEV;
+ ret = mmc_init(mmc);
+ if (ret)
+ return ret;
+
+ *descp = &mmc->block_dev;
+
+ return 0;
}
#endif
@@ -1636,7 +1692,7 @@ int mmc_start_init(struct mmc *mmc)
return err;
/* The internal partition reset to user partition(0) at every CMD0*/
- mmc->block_dev.hwpart = 0;
+ mmc_get_blk_desc(mmc)->hwpart = 0;
/* Test for SD version 2 */
err = mmc_send_if_cond(mmc);
@@ -1683,7 +1739,11 @@ int mmc_init(struct mmc *mmc)
{
int err = 0;
unsigned start;
+#ifdef CONFIG_DM_MMC
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(mmc->dev);
+ upriv->mmc = mmc;
+#endif
if (mmc->has_init)
return 0;
@@ -1716,66 +1776,11 @@ __weak int board_mmc_init(bd_t *bis)
return -1;
}
-#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
-
-void print_mmc_devices(char separator)
-{
- struct mmc *m;
- struct list_head *entry;
- char *mmc_type;
-
- list_for_each(entry, &mmc_devices) {
- m = list_entry(entry, struct mmc, link);
-
- if (m->has_init)
- mmc_type = IS_SD(m) ? "SD" : "eMMC";
- else
- mmc_type = NULL;
-
- printf("%s: %d", m->cfg->name, m->block_dev.devnum);
- if (mmc_type)
- printf(" (%s)", mmc_type);
-
- if (entry->next != &mmc_devices) {
- printf("%c", separator);
- if (separator != '\n')
- puts (" ");
- }
- }
-
- printf("\n");
-}
-
-#else
-void print_mmc_devices(char separator) { }
-#endif
-
-int get_mmc_num(void)
-{
- return cur_dev_num;
-}
-
void mmc_set_preinit(struct mmc *mmc, int preinit)
{
mmc->preinit = preinit;
}
-static void do_preinit(void)
-{
- struct mmc *m;
- struct list_head *entry;
-
- list_for_each(entry, &mmc_devices) {
- m = list_entry(entry, struct mmc, link);
-
-#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
- mmc_set_preinit(m, 1);
-#endif
- if (m->preinit)
- mmc_start_init(m);
- }
-}
-
#if defined(CONFIG_DM_MMC) && defined(CONFIG_SPL_BUILD)
static int mmc_probe(bd_t *bis)
{
@@ -1828,9 +1833,9 @@ int mmc_initialize(bd_t *bis)
return 0;
initialized = 1;
- INIT_LIST_HEAD (&mmc_devices);
- cur_dev_num = 0;
-
+#ifndef CONFIG_BLK
+ mmc_list_init();
+#endif
ret = mmc_probe(bis);
if (ret)
return ret;
@@ -1839,7 +1844,7 @@ int mmc_initialize(bd_t *bis)
print_mmc_devices(',');
#endif
- do_preinit();
+ mmc_do_preinit();
return 0;
}
@@ -1965,3 +1970,25 @@ int mmc_set_rst_n_function(struct mmc *mmc, u8 enable)
enable);
}
#endif
+
+#ifdef CONFIG_BLK
+static const struct blk_ops mmc_blk_ops = {
+ .read = mmc_bread,
+ .write = mmc_bwrite,
+ .select_hwpart = mmc_select_hwpart,
+};
+
+U_BOOT_DRIVER(mmc_blk) = {
+ .name = "mmc_blk",
+ .id = UCLASS_BLK,
+ .ops = &mmc_blk_ops,
+};
+#else
+U_BOOT_LEGACY_BLK(mmc) = {
+ .if_typename = "mmc",
+ .if_type = IF_TYPE_MMC,
+ .max_devs = -1,
+ .get_dev = mmc_get_dev,
+ .select_hwpart = mmc_select_hwpartp,
+};
+#endif
diff --git a/drivers/mmc/mmc_legacy.c b/drivers/mmc/mmc_legacy.c
new file mode 100644
index 0000000000..3ec649f2b8
--- /dev/null
+++ b/drivers/mmc/mmc_legacy.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 Google, Inc
+ * Written by Simon Glass <sjg@chromium.org>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <mmc.h>
+
+static struct list_head mmc_devices;
+static int cur_dev_num = -1;
+
+struct mmc *find_mmc_device(int dev_num)
+{
+ struct mmc *m;
+ struct list_head *entry;
+
+ list_for_each(entry, &mmc_devices) {
+ m = list_entry(entry, struct mmc, link);
+
+ if (m->block_dev.devnum == dev_num)
+ return m;
+ }
+
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+ printf("MMC Device %d not found\n", dev_num);
+#endif
+
+ return NULL;
+}
+
+int mmc_get_next_devnum(void)
+{
+ return cur_dev_num++;
+}
+
+struct blk_desc *mmc_get_blk_desc(struct mmc *mmc)
+{
+ return &mmc->block_dev;
+}
+
+int get_mmc_num(void)
+{
+ return cur_dev_num;
+}
+
+void mmc_do_preinit(void)
+{
+ struct mmc *m;
+ struct list_head *entry;
+
+ list_for_each(entry, &mmc_devices) {
+ m = list_entry(entry, struct mmc, link);
+
+#ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT
+ mmc_set_preinit(m, 1);
+#endif
+ if (m->preinit)
+ mmc_start_init(m);
+ }
+}
+
+void mmc_list_init(void)
+{
+ INIT_LIST_HEAD(&mmc_devices);
+ cur_dev_num = 0;
+}
+
+void mmc_list_add(struct mmc *mmc)
+{
+ INIT_LIST_HEAD(&mmc->link);
+
+ list_add_tail(&mmc->link, &mmc_devices);
+}
+
+#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT)
+void print_mmc_devices(char separator)
+{
+ struct mmc *m;
+ struct list_head *entry;
+ char *mmc_type;
+
+ list_for_each(entry, &mmc_devices) {
+ m = list_entry(entry, struct mmc, link);
+
+ if (m->has_init)
+ mmc_type = IS_SD(m) ? "SD" : "eMMC";
+ else
+ mmc_type = NULL;
+
+ printf("%s: %d", m->cfg->name, m->block_dev.devnum);
+ if (mmc_type)
+ printf(" (%s)", mmc_type);
+
+ if (entry->next != &mmc_devices) {
+ printf("%c", separator);
+ if (separator != '\n')
+ puts(" ");
+ }
+ }
+
+ printf("\n");
+}
+
+#else
+void print_mmc_devices(char separator) { }
+#endif
diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h
index d3f6bfe123..27b9e5f56f 100644
--- a/drivers/mmc/mmc_private.h
+++ b/drivers/mmc/mmc_private.h
@@ -25,8 +25,13 @@ void mmc_adapter_card_type_ident(void);
unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start,
lbaint_t blkcnt);
-unsigned long mmc_bwrite(struct blk_desc *block_dev, lbaint_t start,
- lbaint_t blkcnt, const void *src);
+#ifdef CONFIG_BLK
+ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
+ const void *src);
+#else
+ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
+ const void *src);
+#endif
#else /* CONFIG_SPL_BUILD */
@@ -46,4 +51,28 @@ static inline ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start,
#endif /* CONFIG_SPL_BUILD */
+/**
+ * mmc_get_next_devnum() - Get the next available MMC device number
+ *
+ * @return next available device number (0 = first), or -ve on error
+ */
+int mmc_get_next_devnum(void);
+
+/**
+ * mmc_do_preinit() - Get an MMC device ready for use
+ */
+void mmc_do_preinit(void);
+
+/**
+ * mmc_list_init() - Set up the list of MMC devices
+ */
+void mmc_list_init(void);
+
+/**
+ * mmc_list_add() - Add a new MMC device to the list of devices
+ *
+ * @mmc: Device to add
+ */
+void mmc_list_add(struct mmc *mmc);
+
#endif /* _MMC_PRIVATE_H_ */
diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c
index 7b186f8500..0f8b5c79d7 100644
--- a/drivers/mmc/mmc_write.c
+++ b/drivers/mmc/mmc_write.c
@@ -9,6 +9,7 @@
#include <config.h>
#include <common.h>
+#include <dm.h>
#include <part.h>
#include <div64.h>
#include <linux/math64.h>
@@ -78,7 +79,8 @@ unsigned long mmc_berase(struct blk_desc *block_dev, lbaint_t start,
if (!mmc)
return -1;
- err = mmc_select_hwpart(dev_num, block_dev->hwpart);
+ err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num,
+ block_dev->hwpart);
if (err < 0)
return -1;
@@ -121,9 +123,9 @@ static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
struct mmc_data data;
int timeout = 1000;
- if ((start + blkcnt) > mmc->block_dev.lba) {
+ if ((start + blkcnt) > mmc_get_blk_desc(mmc)->lba) {
printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n",
- start + blkcnt, mmc->block_dev.lba);
+ start + blkcnt, mmc_get_blk_desc(mmc)->lba);
return 0;
}
@@ -171,9 +173,17 @@ static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start,
return blkcnt;
}
+#ifdef CONFIG_BLK
+ulong mmc_bwrite(struct udevice *dev, lbaint_t start, lbaint_t blkcnt,
+ const void *src)
+#else
ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
const void *src)
+#endif
{
+#ifdef CONFIG_BLK
+ struct blk_desc *block_dev = dev_get_uclass_platdata(dev);
+#endif
int dev_num = block_dev->devnum;
lbaint_t cur, blocks_todo = blkcnt;
int err;
@@ -182,7 +192,7 @@ ulong mmc_bwrite(struct blk_desc *block_dev, lbaint_t start, lbaint_t blkcnt,
if (!mmc)
return 0;
- err = mmc_select_hwpart(dev_num, block_dev->hwpart);
+ err = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_num, block_dev->hwpart);
if (err < 0)
return 0;
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index 85a832bd42..be34057ea2 100644
--- a/drivers/mmc/omap_hsmmc.c
+++ b/drivers/mmc/omap_hsmmc.c
@@ -825,6 +825,7 @@ static int omap_hsmmc_probe(struct udevice *dev)
gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN);
#endif
+ mmc->dev = dev;
upriv->mmc = mmc;
return 0;
diff --git a/drivers/mmc/pic32_sdhci.c b/drivers/mmc/pic32_sdhci.c
index e03d6dd517..abe74293ed 100644
--- a/drivers/mmc/pic32_sdhci.c
+++ b/drivers/mmc/pic32_sdhci.c
@@ -41,7 +41,12 @@ static int pic32_sdhci_probe(struct udevice *dev)
return ret;
}
- return add_sdhci(host, f_min_max[1], f_min_max[0]);
+ ret = add_sdhci(host, f_min_max[1], f_min_max[0]);
+ if (ret)
+ return ret;
+ host->mmc->dev = dev;
+
+ return 0;
}
static const struct udevice_id pic32_sdhci_ids[] = {
diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c
index cb9e1048d0..0a261c51a8 100644
--- a/drivers/mmc/rockchip_dw_mmc.c
+++ b/drivers/mmc/rockchip_dw_mmc.c
@@ -104,6 +104,7 @@ static int rockchip_dwmmc_probe(struct udevice *dev)
if (ret)
return ret;
+ host->mmc->dev = dev;
upriv->mmc = host->mmc;
return 0;
diff --git a/drivers/mmc/sandbox_mmc.c b/drivers/mmc/sandbox_mmc.c
index f4646a824f..7da059c43c 100644
--- a/drivers/mmc/sandbox_mmc.c
+++ b/drivers/mmc/sandbox_mmc.c
@@ -8,18 +8,150 @@
#include <common.h>
#include <dm.h>
#include <errno.h>
+#include <fdtdec.h>
#include <mmc.h>
#include <asm/test.h>
DECLARE_GLOBAL_DATA_PTR;
+struct sandbox_mmc_plat {
+ struct mmc_config cfg;
+ struct mmc mmc;
+};
+
+/**
+ * sandbox_mmc_send_cmd() - Emulate SD commands
+ *
+ * This emulate an SD card version 2. Single-block reads result in zero data.
+ * Multiple-block reads return a test string.
+ */
+static int sandbox_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+ struct mmc_data *data)
+{
+ switch (cmd->cmdidx) {
+ case MMC_CMD_ALL_SEND_CID:
+ break;
+ case SD_CMD_SEND_RELATIVE_ADDR:
+ cmd->response[0] = 0 << 16; /* mmc->rca */
+ case MMC_CMD_GO_IDLE_STATE:
+ break;
+ case SD_CMD_SEND_IF_COND:
+ cmd->response[0] = 0xaa;
+ break;
+ case MMC_CMD_SEND_STATUS:
+ cmd->response[0] = MMC_STATUS_RDY_FOR_DATA;
+ break;
+ case MMC_CMD_SELECT_CARD:
+ break;
+ case MMC_CMD_SEND_CSD:
+ cmd->response[0] = 0;
+ cmd->response[1] = 10 << 16; /* 1 << block_len */
+ break;
+ case SD_CMD_SWITCH_FUNC: {
+ u32 *resp = (u32 *)data->dest;
+
+ resp[7] = cpu_to_be32(SD_HIGHSPEED_BUSY);
+ break;
+ }
+ case MMC_CMD_READ_SINGLE_BLOCK:
+ memset(data->dest, '\0', data->blocksize);
+ break;
+ case MMC_CMD_READ_MULTIPLE_BLOCK:
+ strcpy(data->dest, "this is a test");
+ break;
+ case MMC_CMD_STOP_TRANSMISSION:
+ break;
+ case SD_CMD_APP_SEND_OP_COND:
+ cmd->response[0] = OCR_BUSY | OCR_HCS;
+ cmd->response[1] = 0;
+ cmd->response[2] = 0;
+ break;
+ case MMC_CMD_APP_CMD:
+ break;
+ case MMC_CMD_SET_BLOCKLEN:
+ debug("block len %d\n", cmd->cmdarg);
+ break;
+ case SD_CMD_APP_SEND_SCR: {
+ u32 *scr = (u32 *)data->dest;
+
+ scr[0] = cpu_to_be32(2 << 24 | 1 << 15); /* SD version 3 */
+ break;
+ }
+ default:
+ debug("%s: Unknown command %d\n", __func__, cmd->cmdidx);
+ break;
+ }
+
+ return 0;
+}
+
+static void sandbox_mmc_set_ios(struct mmc *mmc)
+{
+}
+
+static int sandbox_mmc_init(struct mmc *mmc)
+{
+ return 0;
+}
+
+static int sandbox_mmc_getcd(struct mmc *mmc)
+{
+ return 1;
+}
+
+static const struct mmc_ops sandbox_mmc_ops = {
+ .send_cmd = sandbox_mmc_send_cmd,
+ .set_ios = sandbox_mmc_set_ios,
+ .init = sandbox_mmc_init,
+ .getcd = sandbox_mmc_getcd,
+};
+
+int sandbox_mmc_probe(struct udevice *dev)
+{
+ struct sandbox_mmc_plat *plat = dev_get_platdata(dev);
+
+ return mmc_init(&plat->mmc);
+}
+
+int sandbox_mmc_bind(struct udevice *dev)
+{
+ struct sandbox_mmc_plat *plat = dev_get_platdata(dev);
+ struct mmc_config *cfg = &plat->cfg;
+ int ret;
+
+ cfg->name = dev->name;
+ cfg->ops = &sandbox_mmc_ops;
+ cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_8BIT;
+ cfg->voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
+ cfg->f_min = 1000000;
+ cfg->f_max = 52000000;
+ cfg->b_max = U32_MAX;
+
+ ret = mmc_bind(dev, &plat->mmc, cfg);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int sandbox_mmc_unbind(struct udevice *dev)
+{
+ mmc_unbind(dev);
+
+ return 0;
+}
+
static const struct udevice_id sandbox_mmc_ids[] = {
{ .compatible = "sandbox,mmc" },
{ }
};
-U_BOOT_DRIVER(warm_mmc_sandbox) = {
+U_BOOT_DRIVER(mmc_sandbox) = {
.name = "mmc_sandbox",
.id = UCLASS_MMC,
.of_match = sandbox_mmc_ids,
+ .bind = sandbox_mmc_bind,
+ .unbind = sandbox_mmc_unbind,
+ .probe = sandbox_mmc_probe,
+ .platdata_auto_alloc_size = sizeof(struct sandbox_mmc_plat),
};
diff --git a/drivers/mmc/socfpga_dw_mmc.c b/drivers/mmc/socfpga_dw_mmc.c
index 097db81b05..6a0e9719b8 100644
--- a/drivers/mmc/socfpga_dw_mmc.c
+++ b/drivers/mmc/socfpga_dw_mmc.c
@@ -108,6 +108,7 @@ static int socfpga_dwmmc_probe(struct udevice *dev)
return ret;
upriv->mmc = host->mmc;
+ host->mmc->dev = dev;
return 0;
}
diff --git a/drivers/mmc/uniphier-sd.c b/drivers/mmc/uniphier-sd.c
index 81a80cdbc2..4978cca76d 100644
--- a/drivers/mmc/uniphier-sd.c
+++ b/drivers/mmc/uniphier-sd.c
@@ -725,6 +725,7 @@ int uniphier_sd_probe(struct udevice *dev)
return -EIO;
upriv->mmc = priv->mmc;
+ priv->mmc->dev = dev;
return 0;
}
diff --git a/drivers/mmc/zynq_sdhci.c b/drivers/mmc/zynq_sdhci.c
index b59feca80b..d405929b64 100644
--- a/drivers/mmc/zynq_sdhci.c
+++ b/drivers/mmc/zynq_sdhci.c
@@ -35,6 +35,7 @@ static int arasan_sdhci_probe(struct udevice *dev)
CONFIG_ZYNQ_SDHCI_MIN_FREQ);
upriv->mmc = host->mmc;
+ host->mmc->dev = dev;
return 0;
}
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index c58841e7d8..390e9e4ea3 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -28,6 +28,13 @@ config ALTERA_QSPI
NOR flash to parallel flash interface. Please find details on the
"Embedded Peripherals IP User Guide" of Altera.
+config FLASH_PIC32
+ bool "Microchip PIC32 Flash driver"
+ depends on MACH_PIC32 && MTD
+ help
+ This enables access to Microchip PIC32 internal non-CFI flash
+ chips through PIC32 Non-Volatile-Memory Controller.
+
endmenu
source "drivers/mtd/nand/Kconfig"
diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile
index 703700aae0..bd680a784f 100644
--- a/drivers/mtd/Makefile
+++ b/drivers/mtd/Makefile
@@ -19,5 +19,6 @@ obj-$(CONFIG_HAS_DATAFLASH) += dataflash.o
obj-$(CONFIG_FTSMC020) += ftsmc020.o
obj-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o
obj-$(CONFIG_MW_EEPROM) += mw_eeprom.o
+obj-$(CONFIG_FLASH_PIC32) += pic32_flash.o
obj-$(CONFIG_ST_SMI) += st_smi.o
obj-$(CONFIG_STM32_FLASH) += stm32_flash.o
diff --git a/drivers/mtd/pic32_flash.c b/drivers/mtd/pic32_flash.c
new file mode 100644
index 0000000000..9166fcd980
--- /dev/null
+++ b/drivers/mtd/pic32_flash.c
@@ -0,0 +1,444 @@
+/*
+ * Copyright (C) 2015
+ * Cristian Birsan <cristian.birsan@microchip.com>
+ * Purna Chandra Mandal <purna.mandal@microchip.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdt_support.h>
+#include <flash.h>
+#include <mach/pic32.h>
+#include <wait_bit.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+/* NVM Controller registers */
+struct pic32_reg_nvm {
+ struct pic32_reg_atomic ctrl;
+ struct pic32_reg_atomic key;
+ struct pic32_reg_atomic addr;
+ struct pic32_reg_atomic data;
+};
+
+/* NVM operations */
+#define NVMOP_NOP 0
+#define NVMOP_WORD_WRITE 1
+#define NVMOP_PAGE_ERASE 4
+
+/* NVM control bits */
+#define NVM_WR BIT(15)
+#define NVM_WREN BIT(14)
+#define NVM_WRERR BIT(13)
+#define NVM_LVDERR BIT(12)
+
+/* NVM programming unlock register */
+#define LOCK_KEY 0x0
+#define UNLOCK_KEY1 0xaa996655
+#define UNLOCK_KEY2 0x556699aa
+
+/*
+ * PIC32 flash banks consist of number of pages, each page
+ * into number of rows and rows into number of words.
+ * Here we will maintain page information instead of sector.
+ */
+flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
+static struct pic32_reg_nvm *nvm_regs_p;
+
+static inline void flash_initiate_operation(u32 nvmop)
+{
+ /* set operation */
+ writel(nvmop, &nvm_regs_p->ctrl.raw);
+
+ /* enable flash write */
+ writel(NVM_WREN, &nvm_regs_p->ctrl.set);
+
+ /* unlock sequence */
+ writel(LOCK_KEY, &nvm_regs_p->key.raw);
+ writel(UNLOCK_KEY1, &nvm_regs_p->key.raw);
+ writel(UNLOCK_KEY2, &nvm_regs_p->key.raw);
+
+ /* initiate operation */
+ writel(NVM_WR, &nvm_regs_p->ctrl.set);
+}
+
+static int flash_wait_till_busy(const char *func, ulong timeout)
+{
+ int ret = wait_for_bit(__func__, &nvm_regs_p->ctrl.raw,
+ NVM_WR, false, timeout, false);
+
+ return ret ? ERR_TIMOUT : ERR_OK;
+}
+
+static inline int flash_complete_operation(void)
+{
+ u32 tmp;
+
+ tmp = readl(&nvm_regs_p->ctrl.raw);
+ if (tmp & NVM_WRERR) {
+ printf("Error in Block Erase - Lock Bit may be set!\n");
+ flash_initiate_operation(NVMOP_NOP);
+ return ERR_PROTECTED;
+ }
+
+ if (tmp & NVM_LVDERR) {
+ printf("Error in Block Erase - low-vol detected!\n");
+ flash_initiate_operation(NVMOP_NOP);
+ return ERR_NOT_ERASED;
+ }
+
+ /* disable flash write or erase operation */
+ writel(NVM_WREN, &nvm_regs_p->ctrl.clr);
+
+ return ERR_OK;
+}
+
+/*
+ * Erase flash sectors, returns:
+ * ERR_OK - OK
+ * ERR_INVAL - invalid sector arguments
+ * ERR_TIMOUT - write timeout
+ * ERR_NOT_ERASED - Flash not erased
+ * ERR_UNKNOWN_FLASH_VENDOR - incorrect flash
+ */
+int flash_erase(flash_info_t *info, int s_first, int s_last)
+{
+ ulong sect_start, sect_end, flags;
+ int prot, sect;
+ int rc;
+
+ if ((info->flash_id & FLASH_VENDMASK) != FLASH_MAN_MCHP) {
+ printf("Can't erase unknown flash type %08lx - aborted\n",
+ info->flash_id);
+ return ERR_UNKNOWN_FLASH_VENDOR;
+ }
+
+ if ((s_first < 0) || (s_first > s_last)) {
+ printf("- no sectors to erase\n");
+ return ERR_INVAL;
+ }
+
+ prot = 0;
+ for (sect = s_first; sect <= s_last; ++sect) {
+ if (info->protect[sect])
+ prot++;
+ }
+
+ if (prot)
+ printf("- Warning: %d protected sectors will not be erased!\n",
+ prot);
+ else
+ printf("\n");
+
+ /* erase on unprotected sectors */
+ for (sect = s_first; sect <= s_last; sect++) {
+ if (info->protect[sect])
+ continue;
+
+ /* disable interrupts */
+ flags = disable_interrupts();
+
+ /* write destination page address (physical) */
+ sect_start = CPHYSADDR(info->start[sect]);
+ writel(sect_start, &nvm_regs_p->addr.raw);
+
+ /* page erase */
+ flash_initiate_operation(NVMOP_PAGE_ERASE);
+
+ /* wait */
+ rc = flash_wait_till_busy(__func__,
+ CONFIG_SYS_FLASH_ERASE_TOUT);
+
+ /* re-enable interrupts if necessary */
+ if (flags)
+ enable_interrupts();
+
+ if (rc != ERR_OK)
+ return rc;
+
+ rc = flash_complete_operation();
+ if (rc != ERR_OK)
+ return rc;
+
+ /*
+ * flash content is updated but cache might contain stale
+ * data, so invalidate dcache.
+ */
+ sect_end = info->start[sect] + info->size / info->sector_count;
+ invalidate_dcache_range(info->start[sect], sect_end);
+ }
+
+ printf(" done\n");
+ return ERR_OK;
+}
+
+int page_erase(flash_info_t *info, int sect)
+{
+ return 0;
+}
+
+/* Write a word to flash */
+static int write_word(flash_info_t *info, ulong dest, ulong word)
+{
+ ulong flags;
+ int rc;
+
+ /* read flash to check if it is sufficiently erased */
+ if ((readl((void __iomem *)dest) & word) != word) {
+ printf("Error, Flash not erased!\n");
+ return ERR_NOT_ERASED;
+ }
+
+ /* disable interrupts */
+ flags = disable_interrupts();
+
+ /* update destination page address (physical) */
+ writel(CPHYSADDR(dest), &nvm_regs_p->addr.raw);
+ writel(word, &nvm_regs_p->data.raw);
+
+ /* word write */
+ flash_initiate_operation(NVMOP_WORD_WRITE);
+
+ /* wait for operation to complete */
+ rc = flash_wait_till_busy(__func__, CONFIG_SYS_FLASH_WRITE_TOUT);
+
+ /* re-enable interrupts if necessary */
+ if (flags)
+ enable_interrupts();
+
+ if (rc != ERR_OK)
+ return rc;
+
+ return flash_complete_operation();
+}
+
+/*
+ * Copy memory to flash, returns:
+ * ERR_OK - OK
+ * ERR_TIMOUT - write timeout
+ * ERR_NOT_ERASED - Flash not erased
+ */
+int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
+{
+ ulong dst, tmp_le, len = cnt;
+ int i, l, rc;
+ uchar *cp;
+
+ /* get lower word aligned address */
+ dst = (addr & ~3);
+
+ /* handle unaligned start bytes */
+ l = addr - dst;
+ if (l != 0) {
+ tmp_le = 0;
+ for (i = 0, cp = (uchar *)dst; i < l; ++i, ++cp)
+ tmp_le |= *cp << (i * 8);
+
+ for (; (i < 4) && (cnt > 0); ++i, ++src, --cnt, ++cp)
+ tmp_le |= *src << (i * 8);
+
+ for (; (cnt == 0) && (i < 4); ++i, ++cp)
+ tmp_le |= *cp << (i * 8);
+
+ rc = write_word(info, dst, tmp_le);
+ if (rc)
+ goto out;
+
+ dst += 4;
+ }
+
+ /* handle word aligned part */
+ while (cnt >= 4) {
+ tmp_le = src[0] | src[1] << 8 | src[2] << 16 | src[3] << 24;
+ rc = write_word(info, dst, tmp_le);
+ if (rc)
+ goto out;
+ src += 4;
+ dst += 4;
+ cnt -= 4;
+ }
+
+ if (cnt == 0) {
+ rc = ERR_OK;
+ goto out;
+ }
+
+ /* handle unaligned tail bytes */
+ tmp_le = 0;
+ for (i = 0, cp = (uchar *)dst; (i < 4) && (cnt > 0); ++i, ++cp) {
+ tmp_le |= *src++ << (i * 8);
+ --cnt;
+ }
+
+ for (; i < 4; ++i, ++cp)
+ tmp_le |= *cp << (i * 8);
+
+ rc = write_word(info, dst, tmp_le);
+out:
+ /*
+ * flash content updated by nvm controller but CPU cache might
+ * have stale data, so invalidate dcache.
+ */
+ invalidate_dcache_range(addr, addr + len);
+
+ printf(" done\n");
+ return rc;
+}
+
+void flash_print_info(flash_info_t *info)
+{
+ int i;
+
+ if (info->flash_id == FLASH_UNKNOWN) {
+ printf("missing or unknown FLASH type\n");
+ return;
+ }
+
+ switch (info->flash_id & FLASH_VENDMASK) {
+ case FLASH_MAN_MCHP:
+ printf("Microchip Technology ");
+ break;
+ default:
+ printf("Unknown Vendor ");
+ break;
+ }
+
+ switch (info->flash_id & FLASH_TYPEMASK) {
+ case FLASH_MCHP100T:
+ printf("Internal (8 Mbit, 64 x 16k)\n");
+ break;
+ default:
+ printf("Unknown Chip Type\n");
+ break;
+ }
+
+ printf(" Size: %ld MB in %d Sectors\n",
+ info->size >> 20, info->sector_count);
+
+ printf(" Sector Start Addresses:");
+ for (i = 0; i < info->sector_count; ++i) {
+ if ((i % 5) == 0)
+ printf("\n ");
+
+ printf(" %08lX%s", info->start[i],
+ info->protect[i] ? " (RO)" : " ");
+ }
+ printf("\n");
+}
+
+unsigned long flash_init(void)
+{
+ unsigned long size = 0;
+ struct udevice *dev;
+ int bank;
+
+ /* probe every MTD device */
+ for (uclass_first_device(UCLASS_MTD, &dev); dev;
+ uclass_next_device(&dev)) {
+ /* nop */
+ }
+
+ /* calc total flash size */
+ for (bank = 0; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank)
+ size += flash_info[bank].size;
+
+ return size;
+}
+
+static void pic32_flash_bank_init(flash_info_t *info,
+ ulong base, ulong size)
+{
+ ulong sect_size;
+ int sect;
+
+ /* device & manufacturer code */
+ info->flash_id = FLASH_MAN_MCHP | FLASH_MCHP100T;
+ info->sector_count = CONFIG_SYS_MAX_FLASH_SECT;
+ info->size = size;
+
+ /* update sector (i.e page) info */
+ sect_size = info->size / info->sector_count;
+ for (sect = 0; sect < info->sector_count; sect++) {
+ info->start[sect] = base;
+ /* protect each sector by default */
+ info->protect[sect] = 1;
+ base += sect_size;
+ }
+}
+
+static int pic32_flash_probe(struct udevice *dev)
+{
+ void *blob = (void *)gd->fdt_blob;
+ int node = dev->of_offset;
+ const char *list, *end;
+ const fdt32_t *cell;
+ unsigned long addr, size;
+ int parent, addrc, sizec;
+ flash_info_t *info;
+ int len, idx;
+
+ /*
+ * decode regs. there are multiple reg tuples, and they need to
+ * match with reg-names.
+ */
+ parent = fdt_parent_offset(blob, node);
+ of_bus_default_count_cells(blob, parent, &addrc, &sizec);
+ list = fdt_getprop(blob, node, "reg-names", &len);
+ if (!list)
+ return -ENOENT;
+
+ end = list + len;
+ cell = fdt_getprop(blob, node, "reg", &len);
+ if (!cell)
+ return -ENOENT;
+
+ for (idx = 0, info = &flash_info[0]; list < end;) {
+ addr = fdt_translate_address((void *)blob, node, cell + idx);
+ size = fdt_addr_to_cpu(cell[idx + addrc]);
+ len = strlen(list);
+ if (!strncmp(list, "nvm", len)) {
+ /* NVM controller */
+ nvm_regs_p = ioremap(addr, size);
+ } else if (!strncmp(list, "bank", 4)) {
+ /* Flash bank: use kseg0 cached address */
+ pic32_flash_bank_init(info, CKSEG0ADDR(addr), size);
+ info++;
+ }
+ idx += addrc + sizec;
+ list += len + 1;
+ }
+
+ /* disable flash write/erase operations */
+ writel(NVM_WREN, &nvm_regs_p->ctrl.clr);
+
+#if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE)
+ /* monitor protection ON by default */
+ flash_protect(FLAG_PROTECT_SET,
+ CONFIG_SYS_MONITOR_BASE,
+ CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1,
+ &flash_info[0]);
+#endif
+
+#ifdef CONFIG_ENV_IS_IN_FLASH
+ /* ENV protection ON by default */
+ flash_protect(FLAG_PROTECT_SET,
+ CONFIG_ENV_ADDR,
+ CONFIG_ENV_ADDR + CONFIG_ENV_SECT_SIZE - 1,
+ &flash_info[0]);
+#endif
+ return 0;
+}
+
+static const struct udevice_id pic32_flash_ids[] = {
+ { .compatible = "microchip,pic32mzda-flash" },
+ {}
+};
+
+U_BOOT_DRIVER(pic32_flash) = {
+ .name = "pic32_flash",
+ .id = UCLASS_MTD,
+ .of_match = pic32_flash_ids,
+ .probe = pic32_flash_probe,
+};
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 461908941d..4b73a0ff9c 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -175,11 +175,7 @@ pci_dev_t pci_find_devices(struct pci_device_id *ids, int index)
int bus;
for (hose = pci_get_hose_head(); hose; hose = hose->next) {
-#ifdef CONFIG_SYS_SCSI_SCAN_BUS_REVERSE
- for (bus = hose->last_busno; bus >= hose->first_busno; bus--) {
-#else
for (bus = hose->first_busno; bus <= hose->last_busno; bus++) {
-#endif
bdf = pci_hose_find_devices(hose, bus, ids, &index);
if (bdf != -1)
return bdf;
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 2a69babec4..567b7662d0 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -105,6 +105,24 @@ config SPL_PINCONF
if PINCTRL || SPL_PINCTRL
+config AR933X_PINCTRL
+ bool "QCA/Athores ar933x pin control driver"
+ depends on DM && SOC_AR933X
+ help
+ Support pin multiplexing control on QCA/Athores ar933x SoCs.
+ The driver is controlled by a device tree node which contains
+ both the GPIO definitions and pin control functions for each
+ available multiplex function.
+
+config QCA953X_PINCTRL
+ bool "QCA/Athores qca953x pin control driver"
+ depends on DM && SOC_QCA953X
+ help
+ Support pin multiplexing control on QCA/Athores qca953x SoCs.
+ The driver is controlled by a device tree node which contains
+ both the GPIO definitions and pin control functions for each
+ available multiplex function.
+
config ROCKCHIP_PINCTRL
bool "Rockchip pin control driver"
depends on DM
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 37dc904640..b99ed2f191 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -6,6 +6,7 @@ obj-y += pinctrl-uclass.o
obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o
obj-y += nxp/
+obj-$(CONFIG_ARCH_ATH79) += ath79/
obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
diff --git a/drivers/pinctrl/ath79/Makefile b/drivers/pinctrl/ath79/Makefile
new file mode 100644
index 0000000000..dcea10ace6
--- /dev/null
+++ b/drivers/pinctrl/ath79/Makefile
@@ -0,0 +1,6 @@
+#
+# SPDX-License-Identifier: GPL-2.0+
+#
+
+obj-$(CONFIG_AR933X_PINCTRL) += pinctrl_ar933x.o
+obj-$(CONFIG_QCA953x_PINCTRL) += pinctrl_qca953x.o
diff --git a/drivers/pinctrl/ath79/pinctrl_ar933x.c b/drivers/pinctrl/ath79/pinctrl_ar933x.c
new file mode 100644
index 0000000000..e3f64b6355
--- /dev/null
+++ b/drivers/pinctrl/ath79/pinctrl_ar933x.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+#include <mach/ar71xx_regs.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum periph_id {
+ PERIPH_ID_UART0,
+ PERIPH_ID_SPI0,
+ PERIPH_ID_NONE = -1,
+};
+
+struct ar933x_pinctrl_priv {
+ void __iomem *regs;
+};
+
+static void pinctrl_ar933x_spi_config(struct ar933x_pinctrl_priv *priv, int cs)
+{
+ switch (cs) {
+ case 0:
+ clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE,
+ AR933X_GPIO(4), AR933X_GPIO(3) |
+ AR933X_GPIO(5) | AR933X_GPIO(2));
+ setbits_be32(priv->regs + AR71XX_GPIO_REG_FUNC,
+ AR933X_GPIO_FUNC_SPI_EN |
+ AR933X_GPIO_FUNC_RES_TRUE);
+ break;
+ }
+}
+
+static void pinctrl_ar933x_uart_config(struct ar933x_pinctrl_priv *priv, int uart_id)
+{
+ switch (uart_id) {
+ case PERIPH_ID_UART0:
+ clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE,
+ AR933X_GPIO(9), AR933X_GPIO(10));
+ setbits_be32(priv->regs + AR71XX_GPIO_REG_FUNC,
+ AR933X_GPIO_FUNC_UART_EN |
+ AR933X_GPIO_FUNC_RES_TRUE);
+ break;
+ }
+}
+
+static int ar933x_pinctrl_request(struct udevice *dev, int func, int flags)
+{
+ struct ar933x_pinctrl_priv *priv = dev_get_priv(dev);
+
+ debug("%s: func=%x, flags=%x\n", __func__, func, flags);
+ switch (func) {
+ case PERIPH_ID_SPI0:
+ pinctrl_ar933x_spi_config(priv, flags);
+ break;
+ case PERIPH_ID_UART0:
+ pinctrl_ar933x_uart_config(priv, func);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ar933x_pinctrl_get_periph_id(struct udevice *dev,
+ struct udevice *periph)
+{
+ u32 cell[2];
+ int ret;
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset,
+ "interrupts", cell, ARRAY_SIZE(cell));
+ if (ret < 0)
+ return -EINVAL;
+
+ switch (cell[0]) {
+ case 128:
+ return PERIPH_ID_UART0;
+ case 129:
+ return PERIPH_ID_SPI0;
+ }
+ return -ENOENT;
+}
+
+static int ar933x_pinctrl_set_state_simple(struct udevice *dev,
+ struct udevice *periph)
+{
+ int func;
+
+ func = ar933x_pinctrl_get_periph_id(dev, periph);
+ if (func < 0)
+ return func;
+ return ar933x_pinctrl_request(dev, func, 0);
+}
+
+static struct pinctrl_ops ar933x_pinctrl_ops = {
+ .set_state_simple = ar933x_pinctrl_set_state_simple,
+ .request = ar933x_pinctrl_request,
+ .get_periph_id = ar933x_pinctrl_get_periph_id,
+};
+
+static int ar933x_pinctrl_probe(struct udevice *dev)
+{
+ struct ar933x_pinctrl_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+
+ addr = dev_get_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = map_physmem(addr,
+ AR71XX_GPIO_SIZE,
+ MAP_NOCACHE);
+ return 0;
+}
+
+static const struct udevice_id ar933x_pinctrl_ids[] = {
+ { .compatible = "qca,ar933x-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_ar933x) = {
+ .name = "pinctrl_ar933x",
+ .id = UCLASS_PINCTRL,
+ .of_match = ar933x_pinctrl_ids,
+ .priv_auto_alloc_size = sizeof(struct ar933x_pinctrl_priv),
+ .ops = &ar933x_pinctrl_ops,
+ .probe = ar933x_pinctrl_probe,
+};
diff --git a/drivers/pinctrl/ath79/pinctrl_qca953x.c b/drivers/pinctrl/ath79/pinctrl_qca953x.c
new file mode 100644
index 0000000000..d02597e968
--- /dev/null
+++ b/drivers/pinctrl/ath79/pinctrl_qca953x.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <dm/pinctrl.h>
+#include <mach/ar71xx_regs.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+enum periph_id {
+ PERIPH_ID_UART0,
+ PERIPH_ID_SPI0,
+ PERIPH_ID_NONE = -1,
+};
+
+struct qca953x_pinctrl_priv {
+ void __iomem *regs;
+};
+
+static void pinctrl_qca953x_spi_config(struct qca953x_pinctrl_priv *priv, int cs)
+{
+ switch (cs) {
+ case 0:
+ clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE,
+ QCA953X_GPIO(5) | QCA953X_GPIO(6) |
+ QCA953X_GPIO(7), QCA953X_GPIO(8));
+
+ clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_OUT_FUNC1,
+ QCA953X_GPIO_MUX_MASK(8) |
+ QCA953X_GPIO_MUX_MASK(16) |
+ QCA953X_GPIO_MUX_MASK(24),
+ (QCA953X_GPIO_OUT_MUX_SPI_CS0 << 8) |
+ (QCA953X_GPIO_OUT_MUX_SPI_CLK << 16) |
+ (QCA953X_GPIO_OUT_MUX_SPI_MOSI << 24));
+
+ clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_IN_ENABLE0,
+ QCA953X_GPIO_MUX_MASK(0),
+ QCA953X_GPIO_IN_MUX_SPI_DATA_IN);
+
+ setbits_be32(priv->regs + AR71XX_GPIO_REG_OUT,
+ QCA953X_GPIO(8));
+ break;
+ }
+}
+
+static void pinctrl_qca953x_uart_config(struct qca953x_pinctrl_priv *priv, int uart_id)
+{
+ switch (uart_id) {
+ case PERIPH_ID_UART0:
+ clrsetbits_be32(priv->regs + AR71XX_GPIO_REG_OE,
+ QCA953X_GPIO(9), QCA953X_GPIO(10));
+
+ clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_OUT_FUNC2,
+ QCA953X_GPIO_MUX_MASK(16),
+ QCA953X_GPIO_OUT_MUX_UART0_SOUT << 16);
+
+ clrsetbits_be32(priv->regs + QCA953X_GPIO_REG_IN_ENABLE0,
+ QCA953X_GPIO_MUX_MASK(8),
+ QCA953X_GPIO_IN_MUX_UART0_SIN << 8);
+
+ setbits_be32(priv->regs + AR71XX_GPIO_REG_OUT,
+ QCA953X_GPIO(10));
+ break;
+ }
+}
+
+static int qca953x_pinctrl_request(struct udevice *dev, int func, int flags)
+{
+ struct qca953x_pinctrl_priv *priv = dev_get_priv(dev);
+
+ debug("%s: func=%x, flags=%x\n", __func__, func, flags);
+ switch (func) {
+ case PERIPH_ID_SPI0:
+ pinctrl_qca953x_spi_config(priv, flags);
+ break;
+ case PERIPH_ID_UART0:
+ pinctrl_qca953x_uart_config(priv, func);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int qca953x_pinctrl_get_periph_id(struct udevice *dev,
+ struct udevice *periph)
+{
+ u32 cell[2];
+ int ret;
+
+ ret = fdtdec_get_int_array(gd->fdt_blob, periph->of_offset,
+ "interrupts", cell, ARRAY_SIZE(cell));
+ if (ret < 0)
+ return -EINVAL;
+
+ switch (cell[0]) {
+ case 128:
+ return PERIPH_ID_UART0;
+ case 129:
+ return PERIPH_ID_SPI0;
+ }
+ return -ENOENT;
+}
+
+static int qca953x_pinctrl_set_state_simple(struct udevice *dev,
+ struct udevice *periph)
+{
+ int func;
+
+ func = qca953x_pinctrl_get_periph_id(dev, periph);
+ if (func < 0)
+ return func;
+ return qca953x_pinctrl_request(dev, func, 0);
+}
+
+static struct pinctrl_ops qca953x_pinctrl_ops = {
+ .set_state_simple = qca953x_pinctrl_set_state_simple,
+ .request = qca953x_pinctrl_request,
+ .get_periph_id = qca953x_pinctrl_get_periph_id,
+};
+
+static int qca953x_pinctrl_probe(struct udevice *dev)
+{
+ struct qca953x_pinctrl_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+
+ addr = dev_get_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = map_physmem(addr,
+ AR71XX_GPIO_SIZE,
+ MAP_NOCACHE);
+ return 0;
+}
+
+static const struct udevice_id qca953x_pinctrl_ids[] = {
+ { .compatible = "qca,qca953x-pinctrl" },
+ { }
+};
+
+U_BOOT_DRIVER(pinctrl_qca953x) = {
+ .name = "pinctrl_qca953x",
+ .id = UCLASS_PINCTRL,
+ .of_match = qca953x_pinctrl_ids,
+ .priv_auto_alloc_size = sizeof(struct qca953x_pinctrl_priv),
+ .ops = &qca953x_pinctrl_ops,
+ .probe = qca953x_pinctrl_probe,
+};
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index a9a5d475dd..2497ae90a0 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -89,6 +89,15 @@ config DEBUG_UART_ALTERA_UART
You will need to provide parameters to make this work. The driver will
be available until the real driver model serial is running.
+config DEBUG_UART_AR933X
+ bool "QCA/Atheros ar933x"
+ depends on AR933X_UART
+ help
+ Select this to enable a debug UART using the ar933x uart driver.
+ You will need to provide parameters to make this work. The
+ driver will be available until the real driver model serial is
+ running.
+
config DEBUG_UART_NS16550
bool "ns16550"
help
@@ -263,6 +272,15 @@ config ALTERA_UART
Select this to enable an UART for Altera devices. Please find
details on the "Embedded Peripherals IP User Guide" of Altera.
+config AR933X_UART
+ bool "QCA/Atheros ar933x UART support"
+ depends on DM_SERIAL && SOC_AR933X
+ help
+ Select this to enable UART support for QCA/Atheros ar933x
+ devices. This driver uses driver model and requires a device
+ tree binding to operate, please refer to the document at
+ doc/device-tree-bindings/serial/qca,ar9330-uart.txt.
+
config FSL_LPUART
bool "Freescale LPUART support"
help
diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile
index b0ac9d8a56..9def128e89 100644
--- a/drivers/serial/Makefile
+++ b/drivers/serial/Makefile
@@ -17,6 +17,7 @@ endif
obj-$(CONFIG_ALTERA_UART) += altera_uart.o
obj-$(CONFIG_ALTERA_JTAG_UART) += altera_jtag_uart.o
+obj-$(CONFIG_AR933X_UART) += serial_ar933x.o
obj-$(CONFIG_ARM_DCC) += arm_dcc.o
obj-$(CONFIG_ATMEL_USART) += atmel_usart.o
obj-$(CONFIG_EFI_APP) += serial_efi.o
diff --git a/drivers/serial/mcfuart.c b/drivers/serial/mcfuart.c
index 407354fc4c..059cb0fc6e 100644
--- a/drivers/serial/mcfuart.c
+++ b/drivers/serial/mcfuart.c
@@ -2,6 +2,9 @@
* (C) Copyright 2004-2007 Freescale Semiconductor, Inc.
* TsiChung Liew, Tsi-Chung.Liew@freescale.com.
*
+ * Modified to add device model (DM) support
+ * (C) Copyright 2015 Angelo Dureghello <angelo@sysam.it>
+ *
* SPDX-License-Identifier: GPL-2.0+
*/
@@ -11,9 +14,10 @@
*/
#include <common.h>
+#include <dm.h>
+#include <dm/platform_data/serial_coldfire.h>
#include <serial.h>
#include <linux/compiler.h>
-
#include <asm/immap.h>
#include <asm/uart.h>
@@ -21,91 +25,110 @@ DECLARE_GLOBAL_DATA_PTR;
extern void uart_port_conf(int port);
-static int mcf_serial_init(void)
+static int mcf_serial_init_common(uart_t *uart, int port_idx, int baudrate)
{
- volatile uart_t *uart;
u32 counter;
- uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE);
-
- uart_port_conf(CONFIG_SYS_UART_PORT);
+ uart_port_conf(port_idx);
/* write to SICR: SIM2 = uart mode,dcd does not affect rx */
- uart->ucr = UART_UCR_RESET_RX;
- uart->ucr = UART_UCR_RESET_TX;
- uart->ucr = UART_UCR_RESET_ERROR;
- uart->ucr = UART_UCR_RESET_MR;
+ writeb(UART_UCR_RESET_RX, &uart->ucr);
+ writeb(UART_UCR_RESET_TX, &uart->ucr);
+ writeb(UART_UCR_RESET_ERROR, &uart->ucr);
+ writeb(UART_UCR_RESET_MR, &uart->ucr);
__asm__("nop");
- uart->uimr = 0;
+ writeb(0, &uart->uimr);
/* write to CSR: RX/TX baud rate from timers */
- uart->ucsr = (UART_UCSR_RCS_SYS_CLK | UART_UCSR_TCS_SYS_CLK);
+ writeb(UART_UCSR_RCS_SYS_CLK | UART_UCSR_TCS_SYS_CLK, &uart->ucsr);
- uart->umr = (UART_UMR_BC_8 | UART_UMR_PM_NONE);
- uart->umr = UART_UMR_SB_STOP_BITS_1;
+ writeb(UART_UMR_BC_8 | UART_UMR_PM_NONE, &uart->umr);
+ writeb(UART_UMR_SB_STOP_BITS_1, &uart->umr);
/* Setting up BaudRate */
- counter = (u32) ((gd->bus_clk / 32) + (gd->baudrate / 2));
- counter = counter / gd->baudrate;
+ counter = (u32) ((gd->bus_clk / 32) + (baudrate / 2));
+ counter = counter / baudrate;
/* write to CTUR: divide counter upper byte */
- uart->ubg1 = (u8) ((counter & 0xff00) >> 8);
+ writeb((u8)((counter & 0xff00) >> 8), &uart->ubg1);
/* write to CTLR: divide counter lower byte */
- uart->ubg2 = (u8) (counter & 0x00ff);
+ writeb((u8)(counter & 0x00ff), &uart->ubg2);
- uart->ucr = (UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED);
+ writeb(UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED, &uart->ucr);
return (0);
}
+static void mcf_serial_setbrg_common(uart_t *uart, int baudrate)
+{
+ u32 counter;
+
+ /* Setting up BaudRate */
+ counter = (u32) ((gd->bus_clk / 32) + (baudrate / 2));
+ counter = counter / baudrate;
+
+ /* write to CTUR: divide counter upper byte */
+ writeb(((counter & 0xff00) >> 8), &uart->ubg1);
+ /* write to CTLR: divide counter lower byte */
+ writeb((counter & 0x00ff), &uart->ubg2);
+
+ writeb(UART_UCR_RESET_RX, &uart->ucr);
+ writeb(UART_UCR_RESET_TX, &uart->ucr);
+
+ writeb(UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED, &uart->ucr);
+}
+
+#ifndef CONFIG_DM_SERIAL
+
+static int mcf_serial_init(void)
+{
+ uart_t *uart_base;
+ int port_idx;
+
+ uart_base = (uart_t *)CONFIG_SYS_UART_BASE;
+ port_idx = CONFIG_SYS_UART_PORT;
+
+ return mcf_serial_init_common(uart_base, port_idx, gd->baudrate);
+}
+
static void mcf_serial_putc(const char c)
{
- volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE);
+ uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE;
if (c == '\n')
serial_putc('\r');
/* Wait for last character to go. */
- while (!(uart->usr & UART_USR_TXRDY)) ;
+ while (!(readb(&uart->usr) & UART_USR_TXRDY))
+ ;
- uart->utb = c;
+ writeb(c, &uart->utb);
}
static int mcf_serial_getc(void)
{
- volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE);
+ uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE;
/* Wait for a character to arrive. */
- while (!(uart->usr & UART_USR_RXRDY)) ;
- return uart->urb;
-}
-
-static int mcf_serial_tstc(void)
-{
- volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE);
+ while (!(readb(&uart->usr) & UART_USR_RXRDY))
+ ;
- return (uart->usr & UART_USR_RXRDY);
+ return readb(&uart->urb);
}
static void mcf_serial_setbrg(void)
{
- volatile uart_t *uart = (volatile uart_t *)(CONFIG_SYS_UART_BASE);
- u32 counter;
-
- /* Setting up BaudRate */
- counter = (u32) ((gd->bus_clk / 32) + (gd->baudrate / 2));
- counter = counter / gd->baudrate;
+ uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE;
- /* write to CTUR: divide counter upper byte */
- uart->ubg1 = ((counter & 0xff00) >> 8);
- /* write to CTLR: divide counter lower byte */
- uart->ubg2 = (counter & 0x00ff);
+ mcf_serial_setbrg_common(uart, gd->baudrate);
+}
- uart->ucr = UART_UCR_RESET_RX;
- uart->ucr = UART_UCR_RESET_TX;
+static int mcf_serial_tstc(void)
+{
+ uart_t *uart = (uart_t *)CONFIG_SYS_UART_BASE;
- uart->ucr = UART_UCR_RX_ENABLED | UART_UCR_TX_ENABLED;
+ return readb(&uart->usr) & UART_USR_RXRDY;
}
static struct serial_device mcf_serial_drv = {
@@ -128,3 +151,80 @@ __weak struct serial_device *default_serial_console(void)
{
return &mcf_serial_drv;
}
+
+#endif
+
+#ifdef CONFIG_DM_SERIAL
+
+static int coldfire_serial_probe(struct udevice *dev)
+{
+ struct coldfire_serial_platdata *plat = dev->platdata;
+
+ return mcf_serial_init_common((uart_t *)plat->base,
+ plat->port, plat->baudrate);
+}
+
+static int coldfire_serial_putc(struct udevice *dev, const char ch)
+{
+ struct coldfire_serial_platdata *plat = dev->platdata;
+ uart_t *uart = (uart_t *)plat->base;
+
+ /* Wait for last character to go. */
+ if (!(readb(&uart->usr) & UART_USR_TXRDY))
+ return -EAGAIN;
+
+ writeb(ch, &uart->utb);
+
+ return 0;
+}
+
+static int coldfire_serial_getc(struct udevice *dev)
+{
+ struct coldfire_serial_platdata *plat = dev->platdata;
+ uart_t *uart = (uart_t *)(plat->base);
+
+ /* Wait for a character to arrive. */
+ if (!(readb(&uart->usr) & UART_USR_RXRDY))
+ return -EAGAIN;
+
+ return readb(&uart->urb);
+}
+
+int coldfire_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ struct coldfire_serial_platdata *plat = dev->platdata;
+ uart_t *uart = (uart_t *)(plat->base);
+
+ mcf_serial_setbrg_common(uart, baudrate);
+
+ return 0;
+}
+
+static int coldfire_serial_pending(struct udevice *dev, bool input)
+{
+ struct coldfire_serial_platdata *plat = dev->platdata;
+ uart_t *uart = (uart_t *)(plat->base);
+
+ if (input)
+ return readb(&uart->usr) & UART_USR_RXRDY ? 1 : 0;
+ else
+ return readb(&uart->usr) & UART_USR_TXRDY ? 0 : 1;
+
+ return 0;
+}
+
+static const struct dm_serial_ops coldfire_serial_ops = {
+ .putc = coldfire_serial_putc,
+ .pending = coldfire_serial_pending,
+ .getc = coldfire_serial_getc,
+ .setbrg = coldfire_serial_setbrg,
+};
+
+U_BOOT_DRIVER(serial_coldfire) = {
+ .name = "serial_coldfire",
+ .id = UCLASS_SERIAL,
+ .probe = coldfire_serial_probe,
+ .ops = &coldfire_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+#endif
diff --git a/drivers/serial/serial_ar933x.c b/drivers/serial/serial_ar933x.c
new file mode 100644
index 0000000000..aae66dc682
--- /dev/null
+++ b/drivers/serial/serial_ar933x.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <div64.h>
+#include <errno.h>
+#include <serial.h>
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <asm/types.h>
+#include <dm/pinctrl.h>
+#include <mach/ar71xx_regs.h>
+
+#define AR933X_UART_DATA_REG 0x00
+#define AR933X_UART_CS_REG 0x04
+#define AR933X_UART_CLK_REG 0x08
+
+#define AR933X_UART_DATA_TX_RX_MASK 0xff
+#define AR933X_UART_DATA_RX_CSR BIT(8)
+#define AR933X_UART_DATA_TX_CSR BIT(9)
+#define AR933X_UART_CS_IF_MODE_S 2
+#define AR933X_UART_CS_IF_MODE_M 0x3
+#define AR933X_UART_CS_IF_MODE_DTE 1
+#define AR933X_UART_CS_IF_MODE_DCE 2
+#define AR933X_UART_CS_TX_RDY_ORIDE BIT(7)
+#define AR933X_UART_CS_RX_RDY_ORIDE BIT(8)
+#define AR933X_UART_CLK_STEP_M 0xffff
+#define AR933X_UART_CLK_SCALE_M 0xfff
+#define AR933X_UART_CLK_SCALE_S 16
+#define AR933X_UART_CLK_STEP_S 0
+
+struct ar933x_serial_priv {
+ void __iomem *regs;
+};
+
+/*
+ * Baudrate algorithm come from Linux/drivers/tty/serial/ar933x_uart.c
+ * baudrate = (clk / (scale + 1)) * (step * (1 / 2^17))
+ */
+static u32 ar933x_serial_get_baud(u32 clk, u32 scale, u32 step)
+{
+ u64 t;
+ u32 div;
+
+ div = (2 << 16) * (scale + 1);
+ t = clk;
+ t *= step;
+ t += (div / 2);
+ do_div(t, div);
+
+ return t;
+}
+
+static void ar933x_serial_get_scale_step(u32 clk, u32 baud,
+ u32 *scale, u32 *step)
+{
+ u32 tscale, baudrate;
+ long min_diff;
+
+ *scale = 0;
+ *step = 0;
+
+ min_diff = baud;
+ for (tscale = 0; tscale < AR933X_UART_CLK_SCALE_M; tscale++) {
+ u64 tstep;
+ int diff;
+
+ tstep = baud * (tscale + 1);
+ tstep *= (2 << 16);
+ do_div(tstep, clk);
+
+ if (tstep > AR933X_UART_CLK_STEP_M)
+ break;
+
+ baudrate = ar933x_serial_get_baud(clk, tscale, tstep);
+ diff = abs(baudrate - baud);
+ if (diff < min_diff) {
+ min_diff = diff;
+ *scale = tscale;
+ *step = tstep;
+ }
+ }
+}
+
+static int ar933x_serial_setbrg(struct udevice *dev, int baudrate)
+{
+ struct ar933x_serial_priv *priv = dev_get_priv(dev);
+ u32 val, scale, step;
+
+ val = get_serial_clock();
+ ar933x_serial_get_scale_step(val, baudrate, &scale, &step);
+
+ val = (scale & AR933X_UART_CLK_SCALE_M)
+ << AR933X_UART_CLK_SCALE_S;
+ val |= (step & AR933X_UART_CLK_STEP_M)
+ << AR933X_UART_CLK_STEP_S;
+ writel(val, priv->regs + AR933X_UART_CLK_REG);
+
+ return 0;
+}
+
+static int ar933x_serial_putc(struct udevice *dev, const char c)
+{
+ struct ar933x_serial_priv *priv = dev_get_priv(dev);
+ u32 data;
+
+ data = readl(priv->regs + AR933X_UART_DATA_REG);
+ if (!(data & AR933X_UART_DATA_TX_CSR))
+ return -EAGAIN;
+
+ data = (u32)c | AR933X_UART_DATA_TX_CSR;
+ writel(data, priv->regs + AR933X_UART_DATA_REG);
+
+ return 0;
+}
+
+static int ar933x_serial_getc(struct udevice *dev)
+{
+ struct ar933x_serial_priv *priv = dev_get_priv(dev);
+ u32 data;
+
+ data = readl(priv->regs + AR933X_UART_DATA_REG);
+ if (!(data & AR933X_UART_DATA_RX_CSR))
+ return -EAGAIN;
+
+ writel(AR933X_UART_DATA_RX_CSR, priv->regs + AR933X_UART_DATA_REG);
+ return data & AR933X_UART_DATA_TX_RX_MASK;
+}
+
+static int ar933x_serial_pending(struct udevice *dev, bool input)
+{
+ struct ar933x_serial_priv *priv = dev_get_priv(dev);
+ u32 data;
+
+ data = readl(priv->regs + AR933X_UART_DATA_REG);
+ if (input)
+ return (data & AR933X_UART_DATA_RX_CSR) ? 1 : 0;
+ else
+ return (data & AR933X_UART_DATA_TX_CSR) ? 0 : 1;
+}
+
+static int ar933x_serial_probe(struct udevice *dev)
+{
+ struct ar933x_serial_priv *priv = dev_get_priv(dev);
+ fdt_addr_t addr;
+ u32 val;
+
+ addr = dev_get_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = map_physmem(addr, AR933X_UART_SIZE,
+ MAP_NOCACHE);
+
+ /*
+ * UART controller configuration:
+ * - no DMA
+ * - no interrupt
+ * - DCE mode
+ * - no flow control
+ * - set RX ready oride
+ * - set TX ready oride
+ */
+ val = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S) |
+ AR933X_UART_CS_TX_RDY_ORIDE | AR933X_UART_CS_RX_RDY_ORIDE;
+ writel(val, priv->regs + AR933X_UART_CS_REG);
+ return 0;
+}
+
+static const struct dm_serial_ops ar933x_serial_ops = {
+ .putc = ar933x_serial_putc,
+ .pending = ar933x_serial_pending,
+ .getc = ar933x_serial_getc,
+ .setbrg = ar933x_serial_setbrg,
+};
+
+static const struct udevice_id ar933x_serial_ids[] = {
+ { .compatible = "qca,ar9330-uart" },
+ { }
+};
+
+U_BOOT_DRIVER(serial_ar933x) = {
+ .name = "serial_ar933x",
+ .id = UCLASS_SERIAL,
+ .of_match = ar933x_serial_ids,
+ .priv_auto_alloc_size = sizeof(struct ar933x_serial_priv),
+ .probe = ar933x_serial_probe,
+ .ops = &ar933x_serial_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
+
+#ifdef CONFIG_DEBUG_UART_AR933X
+
+#include <debug_uart.h>
+
+static inline void _debug_uart_init(void)
+{
+ void __iomem *regs = (void *)CONFIG_DEBUG_UART_BASE;
+ u32 val, scale, step;
+
+ /*
+ * UART controller configuration:
+ * - no DMA
+ * - no interrupt
+ * - DCE mode
+ * - no flow control
+ * - set RX ready oride
+ * - set TX ready oride
+ */
+ val = (AR933X_UART_CS_IF_MODE_DCE << AR933X_UART_CS_IF_MODE_S) |
+ AR933X_UART_CS_TX_RDY_ORIDE | AR933X_UART_CS_RX_RDY_ORIDE;
+ writel(val, regs + AR933X_UART_CS_REG);
+
+ ar933x_serial_get_scale_step(CONFIG_DEBUG_UART_CLOCK,
+ CONFIG_BAUDRATE, &scale, &step);
+
+ val = (scale & AR933X_UART_CLK_SCALE_M)
+ << AR933X_UART_CLK_SCALE_S;
+ val |= (step & AR933X_UART_CLK_STEP_M)
+ << AR933X_UART_CLK_STEP_S;
+ writel(val, regs + AR933X_UART_CLK_REG);
+}
+
+static inline void _debug_uart_putc(int c)
+{
+ void __iomem *regs = (void *)CONFIG_DEBUG_UART_BASE;
+ u32 data;
+
+ do {
+ data = readl(regs + AR933X_UART_DATA_REG);
+ } while (!(data & AR933X_UART_DATA_TX_CSR));
+
+ data = (u32)c | AR933X_UART_DATA_TX_CSR;
+ writel(data, regs + AR933X_UART_DATA_REG);
+}
+
+DEBUG_UART_FUNCS
+
+#endif
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index f0258f84af..b7fd8e53a2 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -23,6 +23,15 @@ config ALTERA_SPI
IP core. Please find details on the "Embedded Peripherals IP
User Guide" of Altera.
+config ATH79_SPI
+ bool "Atheros SPI driver"
+ depends on ARCH_ATH79
+ help
+ Enable the Atheros ar7xxx/ar9xxx SoC SPI driver, it was used
+ to access SPI NOR flash and other SPI peripherals. This driver
+ uses driver model and requires a device tree binding to operate.
+ please refer to doc/device-tree-bindings/spi/spi-ath79.txt.
+
config CADENCE_QSPI
bool "Cadence QSPI driver"
help
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 3eca7456d6..7fb2926e78 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -17,6 +17,7 @@ endif
obj-$(CONFIG_ALTERA_SPI) += altera_spi.o
obj-$(CONFIG_ARMADA100_SPI) += armada100_spi.o
+obj-$(CONFIG_ATH79_SPI) += ath79_spi.o
obj-$(CONFIG_ATMEL_DATAFLASH_SPI) += atmel_dataflash_spi.o
obj-$(CONFIG_ATMEL_SPI) += atmel_spi.o
obj-$(CONFIG_BFIN_SPI) += bfin_spi.o
diff --git a/drivers/spi/ath79_spi.c b/drivers/spi/ath79_spi.c
new file mode 100644
index 0000000000..b18c733b67
--- /dev/null
+++ b/drivers/spi/ath79_spi.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2015-2016 Wills Wang <wills.wang@live.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <spi.h>
+#include <dm.h>
+#include <div64.h>
+#include <errno.h>
+#include <asm/io.h>
+#include <asm/addrspace.h>
+#include <asm/types.h>
+#include <dm/pinctrl.h>
+#include <mach/ar71xx_regs.h>
+
+/* CLOCK_DIVIDER = 3 (SPI clock = 200 / 8 ~ 25 MHz) */
+#define ATH79_SPI_CLK_DIV(x) (((x) >> 1) - 1)
+#define ATH79_SPI_RRW_DELAY_FACTOR 12000
+#define ATH79_SPI_MHZ (1000 * 1000)
+
+struct ath79_spi_priv {
+ void __iomem *regs;
+ u32 rrw_delay;
+};
+
+static void spi_cs_activate(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct ath79_spi_priv *priv = dev_get_priv(bus);
+
+ writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
+ writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC);
+}
+
+static void spi_cs_deactivate(struct udevice *dev)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct ath79_spi_priv *priv = dev_get_priv(bus);
+
+ writel(AR71XX_SPI_IOC_CS_ALL, priv->regs + AR71XX_SPI_REG_IOC);
+ writel(0, priv->regs + AR71XX_SPI_REG_FS);
+}
+
+static int ath79_spi_claim_bus(struct udevice *dev)
+{
+ return 0;
+}
+
+static int ath79_spi_release_bus(struct udevice *dev)
+{
+ return 0;
+}
+
+static int ath79_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ struct udevice *bus = dev_get_parent(dev);
+ struct ath79_spi_priv *priv = dev_get_priv(bus);
+ struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
+ u8 *rx = din;
+ const u8 *tx = dout;
+ u8 curbyte, curbitlen, restbits;
+ u32 bytes = bitlen / 8;
+ u32 out, in;
+ u64 tick;
+
+ if (flags & SPI_XFER_BEGIN)
+ spi_cs_activate(dev);
+
+ restbits = (bitlen % 8);
+ if (restbits)
+ bytes++;
+
+ out = AR71XX_SPI_IOC_CS_ALL & ~(AR71XX_SPI_IOC_CS(slave->cs));
+ while (bytes > 0) {
+ bytes--;
+ curbyte = 0;
+ if (tx)
+ curbyte = *tx++;
+
+ if (restbits && !bytes) {
+ curbitlen = restbits;
+ curbyte <<= 8 - restbits;
+ } else {
+ curbitlen = 8;
+ }
+
+ for (curbyte <<= (8 - curbitlen); curbitlen; curbitlen--) {
+ if (curbyte & 0x80)
+ out |= AR71XX_SPI_IOC_DO;
+ else
+ out &= ~(AR71XX_SPI_IOC_DO);
+
+ writel(out, priv->regs + AR71XX_SPI_REG_IOC);
+
+ /* delay for low level */
+ if (priv->rrw_delay) {
+ tick = get_ticks() + priv->rrw_delay;
+ while (get_ticks() < tick)
+ /*NOP*/;
+ }
+
+ writel(out | AR71XX_SPI_IOC_CLK,
+ priv->regs + AR71XX_SPI_REG_IOC);
+
+ /* delay for high level */
+ if (priv->rrw_delay) {
+ tick = get_ticks() + priv->rrw_delay;
+ while (get_ticks() < tick)
+ /*NOP*/;
+ }
+
+ curbyte <<= 1;
+ }
+
+ if (!bytes)
+ writel(out, priv->regs + AR71XX_SPI_REG_IOC);
+
+ in = readl(priv->regs + AR71XX_SPI_REG_RDS);
+ if (rx) {
+ if (restbits && !bytes)
+ *rx++ = (in << (8 - restbits));
+ else
+ *rx++ = in;
+ }
+ }
+
+ if (flags & SPI_XFER_END)
+ spi_cs_deactivate(dev);
+
+ return 0;
+}
+
+
+static int ath79_spi_set_speed(struct udevice *bus, uint speed)
+{
+ struct ath79_spi_priv *priv = dev_get_priv(bus);
+ u32 val, div = 0;
+ u64 time;
+
+ if (speed)
+ div = get_bus_freq(0) / speed;
+
+ if (div > 63)
+ div = 63;
+
+ if (div < 5)
+ div = 5;
+
+ /* calculate delay */
+ time = get_tbclk();
+ do_div(time, speed / 2);
+ val = get_bus_freq(0) / ATH79_SPI_MHZ;
+ val = ATH79_SPI_RRW_DELAY_FACTOR / val;
+ if (time > val)
+ priv->rrw_delay = time - val + 1;
+ else
+ priv->rrw_delay = 0;
+
+ writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
+ clrsetbits_be32(priv->regs + AR71XX_SPI_REG_CTRL,
+ AR71XX_SPI_CTRL_DIV_MASK,
+ ATH79_SPI_CLK_DIV(div));
+ writel(0, priv->regs + AR71XX_SPI_REG_FS);
+ return 0;
+}
+
+static int ath79_spi_set_mode(struct udevice *bus, uint mode)
+{
+ return 0;
+}
+
+static int ath79_spi_probe(struct udevice *bus)
+{
+ struct ath79_spi_priv *priv = dev_get_priv(bus);
+ fdt_addr_t addr;
+
+ addr = dev_get_addr(bus);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->regs = map_physmem(addr,
+ AR71XX_SPI_SIZE,
+ MAP_NOCACHE);
+
+ /* Init SPI Hardware, disable remap, set clock */
+ writel(AR71XX_SPI_FS_GPIO, priv->regs + AR71XX_SPI_REG_FS);
+ writel(AR71XX_SPI_CTRL_RD | ATH79_SPI_CLK_DIV(8),
+ priv->regs + AR71XX_SPI_REG_CTRL);
+ writel(0, priv->regs + AR71XX_SPI_REG_FS);
+
+ return 0;
+}
+
+static int ath79_cs_info(struct udevice *bus, uint cs,
+ struct spi_cs_info *info)
+{
+ /* Always allow activity on CS 0/1/2 */
+ if (cs >= 3)
+ return -ENODEV;
+
+ return 0;
+}
+
+static const struct dm_spi_ops ath79_spi_ops = {
+ .claim_bus = ath79_spi_claim_bus,
+ .release_bus = ath79_spi_release_bus,
+ .xfer = ath79_spi_xfer,
+ .set_speed = ath79_spi_set_speed,
+ .set_mode = ath79_spi_set_mode,
+ .cs_info = ath79_cs_info,
+};
+
+static const struct udevice_id ath79_spi_ids[] = {
+ { .compatible = "qca,ar7100-spi" },
+ {}
+};
+
+U_BOOT_DRIVER(ath79_spi) = {
+ .name = "ath79_spi",
+ .id = UCLASS_SPI,
+ .of_match = ath79_spi_ids,
+ .ops = &ath79_spi_ops,
+ .priv_auto_alloc_size = sizeof(struct ath79_spi_priv),
+ .probe = ath79_spi_probe,
+};
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 2fe34c9a14..60e9d6e825 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -35,6 +35,12 @@ DECLARE_GLOBAL_DATA_PTR;
#define OMAP3_MCSPI4_BASE 0x480BA000
#endif
+#define OMAP4_MCSPI_REG_OFFSET 0x100
+
+struct omap2_mcspi_platform_config {
+ unsigned int regs_offset;
+};
+
/* per-register bitmasks */
#define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE (2 << 3)
#define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP BIT(2)
@@ -623,7 +629,10 @@ static int omap3_spi_probe(struct udevice *dev)
const void *blob = gd->fdt_blob;
int node = dev->of_offset;
- priv->regs = (struct mcspi *)dev_get_addr(dev);
+ struct omap2_mcspi_platform_config* data =
+ (struct omap2_mcspi_platform_config*)dev_get_driver_data(dev);
+
+ priv->regs = (struct mcspi *)(dev_get_addr(dev) + data->regs_offset);
priv->pin_dir = fdtdec_get_uint(blob, node, "ti,pindir-d0-out-d1-in",
MCSPI_PINDIR_D0_IN_D1_OUT);
priv->wordlen = SPI_DEFAULT_WORDLEN;
@@ -662,9 +671,17 @@ static const struct dm_spi_ops omap3_spi_ops = {
*/
};
+static struct omap2_mcspi_platform_config omap2_pdata = {
+ .regs_offset = 0,
+};
+
+static struct omap2_mcspi_platform_config omap4_pdata = {
+ .regs_offset = OMAP4_MCSPI_REG_OFFSET,
+};
+
static const struct udevice_id omap3_spi_ids[] = {
- { .compatible = "ti,omap2-mcspi" },
- { .compatible = "ti,omap4-mcspi" },
+ { .compatible = "ti,omap2-mcspi", .data = (ulong)&omap2_pdata },
+ { .compatible = "ti,omap4-mcspi", .data = (ulong)&omap4_pdata },
{ }
};
diff --git a/drivers/spi/soft_spi.c b/drivers/spi/soft_spi.c
index aa4abcc3d2..d23dc81a21 100644
--- a/drivers/spi/soft_spi.c
+++ b/drivers/spi/soft_spi.c
@@ -26,15 +26,20 @@ struct soft_spi_platdata {
struct gpio_desc mosi;
struct gpio_desc miso;
int spi_delay_us;
+ int flags;
};
+#define SPI_MASTER_NO_RX BIT(0)
+#define SPI_MASTER_NO_TX BIT(1)
+
struct soft_spi_priv {
unsigned int mode;
};
static int soft_spi_scl(struct udevice *dev, int bit)
{
- struct soft_spi_platdata *plat = dev->platdata;
+ struct udevice *bus = dev_get_parent(dev);
+ struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->sclk, bit);
@@ -43,7 +48,8 @@ static int soft_spi_scl(struct udevice *dev, int bit)
static int soft_spi_sda(struct udevice *dev, int bit)
{
- struct soft_spi_platdata *plat = dev->platdata;
+ struct udevice *bus = dev_get_parent(dev);
+ struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->mosi, bit);
@@ -52,7 +58,8 @@ static int soft_spi_sda(struct udevice *dev, int bit)
static int soft_spi_cs_activate(struct udevice *dev)
{
- struct soft_spi_platdata *plat = dev->platdata;
+ struct udevice *bus = dev_get_parent(dev);
+ struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->cs, 0);
dm_gpio_set_value(&plat->sclk, 0);
@@ -63,7 +70,8 @@ static int soft_spi_cs_activate(struct udevice *dev)
static int soft_spi_cs_deactivate(struct udevice *dev)
{
- struct soft_spi_platdata *plat = dev->platdata;
+ struct udevice *bus = dev_get_parent(dev);
+ struct soft_spi_platdata *plat = dev_get_platdata(bus);
dm_gpio_set_value(&plat->cs, 0);
@@ -100,8 +108,9 @@ static int soft_spi_release_bus(struct udevice *dev)
static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
const void *dout, void *din, unsigned long flags)
{
- struct soft_spi_priv *priv = dev_get_priv(dev);
- struct soft_spi_platdata *plat = dev->platdata;
+ struct udevice *bus = dev_get_parent(dev);
+ struct soft_spi_priv *priv = dev_get_priv(bus);
+ struct soft_spi_platdata *plat = dev_get_platdata(bus);
uchar tmpdin = 0;
uchar tmpdout = 0;
const u8 *txd = dout;
@@ -134,14 +143,16 @@ static int soft_spi_xfer(struct udevice *dev, unsigned int bitlen,
if (!cpha)
soft_spi_scl(dev, 0);
- soft_spi_sda(dev, tmpdout & 0x80);
+ if ((plat->flags & SPI_MASTER_NO_TX) == 0)
+ soft_spi_sda(dev, !!(tmpdout & 0x80));
udelay(plat->spi_delay_us);
if (cpha)
soft_spi_scl(dev, 0);
else
soft_spi_scl(dev, 1);
tmpdin <<= 1;
- tmpdin |= dm_gpio_get_value(&plat->miso);
+ if ((plat->flags & SPI_MASTER_NO_RX) == 0)
+ tmpdin |= dm_gpio_get_value(&plat->miso);
tmpdout <<= 1;
udelay(plat->spi_delay_us);
if (cpha)
@@ -203,24 +214,36 @@ static int soft_spi_probe(struct udevice *dev)
struct spi_slave *slave = dev_get_parent_priv(dev);
struct soft_spi_platdata *plat = dev->platdata;
int cs_flags, clk_flags;
+ int ret;
cs_flags = (slave->mode & SPI_CS_HIGH) ? 0 : GPIOD_ACTIVE_LOW;
clk_flags = (slave->mode & SPI_CPOL) ? GPIOD_ACTIVE_LOW : 0;
- if (gpio_request_by_name(dev, "cs-gpio", 0, &plat->cs,
+
+ if (gpio_request_by_name(dev, "cs-gpios", 0, &plat->cs,
GPIOD_IS_OUT | cs_flags) ||
- gpio_request_by_name(dev, "sclk-gpio", 0, &plat->sclk,
- GPIOD_IS_OUT | clk_flags) ||
- gpio_request_by_name(dev, "mosi-gpio", 0, &plat->mosi,
- GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE) ||
- gpio_request_by_name(dev, "miso-gpio", 0, &plat->miso,
- GPIOD_IS_IN))
+ gpio_request_by_name(dev, "gpio-sck", 0, &plat->sclk,
+ GPIOD_IS_OUT | clk_flags))
+ return -EINVAL;
+
+ ret = gpio_request_by_name(dev, "gpio-mosi", 0, &plat->mosi,
+ GPIOD_IS_OUT | GPIOD_IS_OUT_ACTIVE);
+ if (ret)
+ plat->flags |= SPI_MASTER_NO_TX;
+
+ ret = gpio_request_by_name(dev, "gpio-miso", 0, &plat->miso,
+ GPIOD_IS_IN);
+ if (ret)
+ plat->flags |= SPI_MASTER_NO_RX;
+
+ if ((plat->flags & (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX)) ==
+ (SPI_MASTER_NO_RX | SPI_MASTER_NO_TX))
return -EINVAL;
return 0;
}
static const struct udevice_id soft_spi_ids[] = {
- { .compatible = "u-boot,soft-spi" },
+ { .compatible = "spi-gpio" },
{ }
};
diff --git a/drivers/spi/spi-uclass.c b/drivers/spi/spi-uclass.c
index 5561f36762..84b6786517 100644
--- a/drivers/spi/spi-uclass.c
+++ b/drivers/spi/spi-uclass.c
@@ -45,12 +45,12 @@ static int spi_set_speed_mode(struct udevice *bus, int speed, int mode)
return 0;
}
-int spi_claim_bus(struct spi_slave *slave)
+int dm_spi_claim_bus(struct udevice *dev)
{
- struct udevice *dev = slave->dev;
struct udevice *bus = dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
struct dm_spi_bus *spi = dev_get_uclass_priv(bus);
+ struct spi_slave *slave = dev_get_parent_priv(dev);
int speed;
int ret;
@@ -73,9 +73,8 @@ int spi_claim_bus(struct spi_slave *slave)
return ops->claim_bus ? ops->claim_bus(dev) : 0;
}
-void spi_release_bus(struct spi_slave *slave)
+void dm_spi_release_bus(struct udevice *dev)
{
- struct udevice *dev = slave->dev;
struct udevice *bus = dev->parent;
struct dm_spi_ops *ops = spi_get_ops(bus);
@@ -83,10 +82,9 @@ void spi_release_bus(struct spi_slave *slave)
ops->release_bus(dev);
}
-int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
- const void *dout, void *din, unsigned long flags)
+int dm_spi_xfer(struct udevice *dev, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
{
- struct udevice *dev = slave->dev;
struct udevice *bus = dev->parent;
if (bus->uclass->uc_drv->id != UCLASS_SPI)
@@ -95,6 +93,22 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
return spi_get_ops(bus)->xfer(dev, bitlen, dout, din, flags);
}
+int spi_claim_bus(struct spi_slave *slave)
+{
+ return dm_spi_claim_bus(slave->dev);
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+ dm_spi_release_bus(slave->dev);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+ const void *dout, void *din, unsigned long flags)
+{
+ return dm_spi_xfer(slave->dev, bitlen, dout, din, flags);
+}
+
static int spi_post_bind(struct udevice *dev)
{
/* Scan the bus for devices */
diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index 2f3d43d939..2f46d38d2b 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -3,5 +3,6 @@
# SPDX-License-Identifier: GPL-2.0+
#
+obj-$(CONFIG_DM_USB) += common.o
obj-$(CONFIG_USB_EHCI_FSL) += fsl-dt-fixup.o
obj-$(CONFIG_USB_XHCI_FSL) += fsl-dt-fixup.o
diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c
new file mode 100644
index 0000000000..35c2dc18d9
--- /dev/null
+++ b/drivers/usb/common/common.c
@@ -0,0 +1,40 @@
+/*
+ * Provides code common for host and device side USB.
+ *
+ * (C) Copyright 2016
+ * Texas Instruments Incorporated, <www.ti.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <common.h>
+#include <libfdt.h>
+#include <linux/usb/otg.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *const usb_dr_modes[] = {
+ [USB_DR_MODE_UNKNOWN] = "",
+ [USB_DR_MODE_HOST] = "host",
+ [USB_DR_MODE_PERIPHERAL] = "peripheral",
+ [USB_DR_MODE_OTG] = "otg",
+};
+
+enum usb_dr_mode usb_get_dr_mode(int node)
+{
+ const void *fdt = gd->fdt_blob;
+ const char *dr_mode;
+ int i;
+
+ dr_mode = fdt_getprop(fdt, node, "dr_mode", NULL);
+ if (!dr_mode) {
+ error("usb dr_mode not found\n");
+ return USB_DR_MODE_UNKNOWN;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(usb_dr_modes); i++)
+ if (!strcmp(dr_mode, usb_dr_modes[i]))
+ return i;
+
+ return USB_DR_MODE_UNKNOWN;
+}
diff --git a/drivers/video/tegra.c b/drivers/video/tegra.c
index 7fd10e6af3..c01809e89e 100644
--- a/drivers/video/tegra.c
+++ b/drivers/video/tegra.c
@@ -620,6 +620,13 @@ static int tegra_lcd_ofdata_to_platdata(struct udevice *dev)
static int tegra_lcd_bind(struct udevice *dev)
{
struct video_uc_platdata *plat = dev_get_uclass_platdata(dev);
+ const void *blob = gd->fdt_blob;
+ int node = dev->of_offset;
+ int rgb;
+
+ rgb = fdt_subnode_offset(blob, node, "rgb");
+ if ((rgb < 0) || !fdtdec_get_is_enabled(blob, rgb))
+ return -ENODEV;
plat->size = LCD_MAX_WIDTH * LCD_MAX_HEIGHT *
(1 << LCD_MAX_LOG2_BPP) / 8;
OpenPOWER on IntegriCloud