This core provides unified access to different block controllers (SATA, SCSI).

Signed-off-by: Pavel Herrmann <morpheus.i...@gmail.com>
---
 Makefile                   |   1 +
 drivers/blockctrl/Makefile |  42 ++++++
 drivers/blockctrl/core.c   | 349 +++++++++++++++++++++++++++++++++++++++++++++
 include/dm/blockctrl.h     |  75 ++++++++++
 4 files changed, 467 insertions(+)
 create mode 100644 drivers/blockctrl/Makefile
 create mode 100644 drivers/blockctrl/core.c
 create mode 100644 include/dm/blockctrl.h

diff --git a/Makefile b/Makefile
index e43fd9d..4420484 100644
--- a/Makefile
+++ b/Makefile
@@ -304,6 +304,7 @@ LIBS-y += test/libtest.o
 LIBS-$(CONFIG_DM) += common/dm/libdm.o
 LIBS-$(CONFIG_DM) += drivers/demo/libdemo.o
 LIBS-${CONFIG_DM_BLOCK} += drivers/blockdev/libblockdev.o
+LIBS-${CONFIG_DM_BLOCK} += drivers/blockctrl/libblockctrl.o
 
 ifneq ($(CONFIG_AM33XX)$(CONFIG_OMAP34XX)$(CONFIG_OMAP44XX)$(CONFIG_OMAP54XX),)
 LIBS-y += $(CPUDIR)/omap-common/libomap-common.o
diff --git a/drivers/blockctrl/Makefile b/drivers/blockctrl/Makefile
new file mode 100644
index 0000000..21a9094
--- /dev/null
+++ b/drivers/blockctrl/Makefile
@@ -0,0 +1,42 @@
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB    := $(obj)libblockctrl.o
+
+COBJS-${CONFIG_DM_BLOCK} := core.o
+
+COBJS  := $(COBJS-y)
+SRCS   := $(COBJS:.o=.c)
+OBJS   := $(addprefix $(obj),$(COBJS))
+
+all:   $(LIB)
+
+$(LIB):        $(obj).depend $(OBJS)
+       $(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/drivers/blockctrl/core.c b/drivers/blockctrl/core.c
new file mode 100644
index 0000000..1f1f834
--- /dev/null
+++ b/drivers/blockctrl/core.c
@@ -0,0 +1,349 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.i...@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <dm/blockctrl.h>
+#include <dm/manager.h>
+#include <malloc.h>
+
+struct blockctrl_core_entry {
+       struct list_head        list;
+       struct instance         *instance;
+       struct blockctrl_ops    *ops;
+};
+
+static struct blockctrl_core_entry *get_entry_by_instance(struct instance *dev)
+{
+       struct blockctrl_core_entry *entry;
+       struct core_instance *core = get_core_instance(CORE_BLOCKCTRL);
+
+       if (!core)
+               return NULL;
+
+       list_for_each_entry(entry, &core->succ, list) {
+               if (entry->instance == dev)
+                       return entry;
+       }
+
+       return NULL;
+}
+
+/* Core API functions */
+static int get_count(struct core_instance *core)
+{
+       int count = 0;
+       struct blockctrl_core_entry *entry;
+
+       list_for_each_entry(entry, &core->succ, list) {
+               count++;
+       }
+
+       return count;
+}
+
+static struct instance *get_child(struct core_instance *core, int index)
+{
+       struct blockctrl_core_entry *entry = NULL;
+
+       list_for_each_entry(entry, &core->succ, list) {
+               if (!index)
+                       return entry->instance;
+               index--;
+       }
+
+       return NULL;
+}
+
+static int bind(struct core_instance *core, struct instance *dev, void *ops,
+       void *data)
+{
+       struct blockctrl_core_entry *entry;
+
+       if (ops == NULL)
+               return -EINVAL;
+
+       entry = malloc(sizeof(*entry));
+       if (entry == NULL)
+               return -ENOMEM;
+
+       INIT_LIST_HEAD(&entry->list);
+       entry->instance = dev;
+       entry->ops = ops;
+       list_add_tail(&entry->list, &core->succ);
+
+       return 0;
+}
+
+static int unbind(struct core_instance *core, struct instance *dev)
+{
+       struct blockctrl_core_entry *entry, *n;
+
+       list_for_each_entry_safe(entry, n, &core->succ, list) {
+               if (entry->instance == dev) {
+                       list_del(&entry->list);
+                       free(entry);
+               }
+       }
+
+       return 0;
+}
+
+static int replace(struct core_instance *core, struct instance *new,
+       struct instance *old)
+{
+       struct blockctrl_core_entry *entry = get_entry_by_instance(old);
+
+       if (!entry)
+               return -ENOENT;
+
+       entry->instance = new;
+
+       return 0;
+}
+
+static int init(struct core_instance *core)
+{
+       INIT_LIST_HEAD(&core->succ);
+       core->private_data = NULL;
+
+       return 0;
+}
+
+static int reloc(struct core_instance *core, struct core_instance *old)
+{
+       struct blockctrl_core_entry *entry, *new;
+
+       /* no private_data to copy, yet */
+
+       /* fixup links in old list and prepare new list head */
+       /* FIXME */
+       /* list_fix_reloc(&old->succ); */
+       INIT_LIST_HEAD(&core->succ);
+       core->private_data = NULL;
+
+       /* copy list entries to new memory */
+       list_for_each_entry(entry, &old->succ, list) {
+               new = malloc(sizeof(*new));
+               if (!new)
+                       return -ENOMEM;
+
+               INIT_LIST_HEAD(&new->list);
+               new->instance = entry->instance;
+               new->ops = entry->ops;
+               list_add_tail(&new->list, &core->succ);
+               /*no free at this point, old memory should not be freed*/
+       }
+
+       return 0;
+}
+
+static int destroy(struct core_instance *core)
+{
+       struct blockctrl_core_entry *entry, *n;
+
+       /* destroy private data */
+       free(core->private_data);
+       core->private_data = NULL;
+
+       /* destroy successor list */
+       list_for_each_entry_safe(entry, n, &core->succ, list) {
+               list_del(&entry->list);
+               free(entry);
+       }
+
+       return 0;
+}
+
+U_BOOT_CORE(CORE_BLOCKCTRL,
+       init,
+       reloc,
+       destroy,
+       get_count,
+       get_child,
+       bind,
+       unbind,
+       replace);
+
+lbaint_t blockctrl_read(struct instance *i, int port, lbaint_t start,
+       lbaint_t length, void *buffer)
+{
+       struct blockctrl_core_entry *entry;
+       struct blockctrl_ops *driver_ops;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       driver_ops = entry->ops;
+       if (!driver_ops || !driver_ops->read)
+               return -EINVAL;
+
+       return driver_ops->read(i, port, start, length, buffer);
+}
+
+lbaint_t blockctrl_write(struct instance *i, int port, lbaint_t start,
+       lbaint_t length, void *buffer)
+{
+       struct blockctrl_core_entry *entry;
+       struct blockctrl_ops *driver_ops;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       driver_ops = entry->ops;
+       if (!driver_ops || !driver_ops->write)
+               return -EINVAL;
+
+       return driver_ops->write(i, port, start, length, buffer);
+}
+
+int blockctrl_scan(struct instance *i, int port)
+{
+       struct blockctrl_core_entry *entry;
+       struct blockctrl_ops *driver_ops;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       driver_ops = entry->ops;
+       if (!driver_ops || !driver_ops->scan)
+               return -EINVAL;
+
+       return driver_ops->scan(i, port);
+}
+
+int blockctrl_get_port_count(struct instance *i)
+{
+       struct blockctrl_core_entry *entry;
+       struct blockctrl_ops *driver_ops;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       driver_ops = entry->ops;
+       if (!driver_ops || !driver_ops->get_port_count)
+               return -EINVAL;
+
+       return driver_ops->get_port_count(i);
+}
+
+int blockctrl_get_port_option(struct instance *i, int port,
+       enum blockctrl_port_option_code op, struct option *result)
+{
+       struct blockctrl_core_entry *entry;
+       struct blockctrl_ops *driver_ops;
+       int error;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return -ENOENT;
+
+       error = driver_activate(i);
+       if (error)
+               return error;
+
+       driver_ops = entry->ops;
+       if (!driver_ops || !driver_ops->get_port_option)
+               return -EINVAL;
+
+       return driver_ops->get_port_option(i, port, op, result);
+}
+
+struct instance *blockctrl_rescan_port(struct instance *i, int port)
+{
+       /* we assume that all children of blockctrl are blockdev_ata */
+       struct instance *child = NULL;
+       struct blockctrl_core_entry *entry;
+       struct driver_instance *di;
+       struct blockdev_ata_platform_data *pdata;
+       struct driver_info *info;
+       int nfound;
+
+       entry = get_entry_by_instance(i);
+       if (!entry)
+               return NULL;
+
+       list_for_each_entry(di, &i->succ, list) {
+               if (di->i.info)
+                       pdata = di->i.info->platform_data;
+               else
+                       pdata = NULL;
+
+               if (pdata && (pdata->port_number == port))
+                       child = &di->i;
+       }
+
+       /*
+        * If we have an active link, we check whether this is a new device,
+        * in which case we simply bind a new instance, or an old device,
+        * in which case we reactivate the device to force partition rescan.
+        * If we dont have an active link, we remove any device attached.
+        */
+       nfound = blockctrl_scan(i, port);
+       if (!nfound) {
+               if (child) {
+                       /* rescan the disk size and partitions, just in case */
+                       driver_remove(child);
+                       scan_partitions(child);
+               } else {
+                       pdata = malloc(sizeof(*pdata));
+                       pdata->port_number = port;
+                       info = malloc(sizeof(*info));
+                       info->platform_data = pdata;
+                       info->name = "blockdev_ata";
+                       child = driver_bind(i, info);
+                       scan_partitions(child);
+                       return child;
+               }
+       } else {
+               /* link is not active, remove and unbind the child device */
+               if (child) {
+                       driver_remove(child);
+                       driver_unbind(child);
+               }
+       }
+
+       return NULL;
+}
diff --git a/include/dm/blockctrl.h b/include/dm/blockctrl.h
new file mode 100644
index 0000000..4b6d582
--- /dev/null
+++ b/include/dm/blockctrl.h
@@ -0,0 +1,75 @@
+/*
+ * (C) Copyright 2012
+ * Pavel Herrmann <morpheus.i...@gmail.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#ifndef _DM_BLOCKCTRL_H_
+#define _DM_BLOCKCTRL_H 1
+
+#include <dm/structures.h>
+#include <dm/blockdev.h>
+
+enum blockctrl_port_option_code {
+       BLKP_OPT_IFTYPE = 0,
+       BLKP_OPT_IFSPEED,
+       BLKP_OPT_BLOCKSIZE,
+       BLKP_OPT_BLOCKCOUNT,
+       BLKP_OPT_REMOVABLE,
+       BLKP_OPT_LBA48,
+       BLKP_OPT_VENDOR,
+       BLKP_OPT_PRODUCT,
+       BLKP_OPT_REVISION,
+};
+
+enum blockctrl_iftype {
+       BLKP_IFTYPE_SATA,
+       BLKP_IFTYPE_PATA,
+       BLKP_IFTYPE_SCSI,
+       BLKP_IFTYPE_VIRTUAL,
+};
+
+
+struct blockctrl_ops {
+       lbaint_t        (*read)(struct instance *i, int port, lbaint_t start,
+                               lbaint_t length, void *buffer);
+       lbaint_t        (*write)(struct instance *i, int port, lbaint_t start,
+                               lbaint_t length, void *buffer);
+       int             (*scan)(struct instance *i, int port);
+       int             (*get_port_count)(struct instance *i);
+       int             (*get_port_option)(struct instance *i, int port,
+                               enum blockctrl_port_option_code op,
+                               struct option *result);
+};
+
+/* driver wrappers */
+lbaint_t blockctrl_read(struct instance *i, int port, lbaint_t start,
+       lbaint_t length, void *buffer);
+lbaint_t blockctrl_write(struct instance *i, int port, lbaint_t start,
+       lbaint_t length, void *buffer);
+int blockctrl_scan(struct instance *i, int port);
+int blockctrl_get_port_count(struct instance *i);
+int blockctrl_get_port_option(struct instance *i, int port,
+       enum blockctrl_port_option_code op, struct option *result);
+
+/* command helpers */
+struct instance *blockctrl_rescan_port(struct instance *i, int port);
+
+#endif
-- 
1.7.12

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to