1 /*
2  * Copyright (c) 2015, Xilinx Inc. and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <string.h>
8 #include <metal/errno.h>
9 #include <metal/assert.h>
10 #include <metal/device.h>
11 #include <metal/list.h>
12 #include <metal/log.h>
13 #include <metal/sys.h>
14 #include <metal/utilities.h>
15 #include <metal/dma.h>
16 #include <metal/cache.h>
17 
metal_bus_register(struct metal_bus * bus)18 int metal_bus_register(struct metal_bus *bus)
19 {
20 	if (!bus || !bus->name || !strlen(bus->name))
21 		return -EINVAL;
22 	if (metal_bus_find(bus->name, NULL) == 0)
23 		return -EEXIST;
24 	metal_list_init(&bus->devices);
25 	metal_list_add_tail(&_metal.common.bus_list, &bus->node);
26 	metal_log(METAL_LOG_DEBUG, "registered %s bus\n", bus->name);
27 	return 0;
28 }
29 
metal_bus_unregister(struct metal_bus * bus)30 int metal_bus_unregister(struct metal_bus *bus)
31 {
32 	metal_list_del(&bus->node);
33 	if (bus->ops.bus_close)
34 		bus->ops.bus_close(bus);
35 	metal_log(METAL_LOG_DEBUG, "unregistered %s bus\n", bus->name);
36 	return 0;
37 }
38 
metal_bus_find(const char * name,struct metal_bus ** result)39 int metal_bus_find(const char *name, struct metal_bus **result)
40 {
41 	struct metal_list *node;
42 	struct metal_bus *bus;
43 
44 	metal_list_for_each(&_metal.common.bus_list, node) {
45 		bus = metal_container_of(node, struct metal_bus, node);
46 		if (strcmp(bus->name, name) != 0)
47 			continue;
48 		if (result)
49 			*result = bus;
50 		return 0;
51 	}
52 	return -ENOENT;
53 }
54 
metal_device_open(const char * bus_name,const char * dev_name,struct metal_device ** device)55 int metal_device_open(const char *bus_name, const char *dev_name,
56 		      struct metal_device **device)
57 {
58 	struct metal_bus *bus;
59 	int error;
60 
61 	if (!bus_name || !strlen(bus_name) ||
62 	    !dev_name || !strlen(dev_name) ||
63 	    !device)
64 		return -EINVAL;
65 
66 	error = metal_bus_find(bus_name, &bus);
67 	if (error)
68 		return error;
69 
70 	if (!bus->ops.dev_open)
71 		return -ENODEV;
72 
73 	error = (*bus->ops.dev_open)(bus, dev_name, device);
74 	if (error)
75 		return error;
76 
77 	return 0;
78 }
79 
metal_device_close(struct metal_device * device)80 void metal_device_close(struct metal_device *device)
81 {
82 	metal_assert(device && device->bus);
83 	if (device->bus->ops.dev_close)
84 		device->bus->ops.dev_close(device->bus, device);
85 }
86 
metal_register_generic_device(struct metal_device * device)87 int metal_register_generic_device(struct metal_device *device)
88 {
89 	if (!device->name || !strlen(device->name) ||
90 	    device->num_regions > METAL_MAX_DEVICE_REGIONS)
91 		return -EINVAL;
92 
93 	device->bus = &metal_generic_bus;
94 	metal_list_add_tail(&_metal.common.generic_device_list,
95 			    &device->node);
96 	return 0;
97 }
98 
metal_generic_dev_open(struct metal_bus * bus,const char * dev_name,struct metal_device ** device)99 int metal_generic_dev_open(struct metal_bus *bus, const char *dev_name,
100 			   struct metal_device **device)
101 {
102 	struct metal_list *node;
103 	struct metal_device *dev;
104 
105 	(void)bus;
106 
107 	metal_list_for_each(&_metal.common.generic_device_list, node) {
108 		dev = metal_container_of(node, struct metal_device, node);
109 		if (strcmp(dev->name, dev_name) != 0)
110 			continue;
111 		*device = dev;
112 		return metal_generic_dev_sys_open(dev);
113 	}
114 
115 	return -ENODEV;
116 }
117 
metal_generic_dev_dma_map(struct metal_bus * bus,struct metal_device * device,uint32_t dir,struct metal_sg * sg_in,int nents_in,struct metal_sg * sg_out)118 int metal_generic_dev_dma_map(struct metal_bus *bus,
119 			     struct metal_device *device,
120 			     uint32_t dir,
121 			     struct metal_sg *sg_in,
122 			     int nents_in,
123 			     struct metal_sg *sg_out)
124 {
125 	(void)bus;
126 	(void)device;
127 	int i;
128 
129 	if (sg_out != sg_in)
130 		memcpy(sg_out, sg_in, nents_in*(sizeof(struct metal_sg)));
131 	for (i = 0; i < nents_in; i++) {
132 		if (dir == METAL_DMA_DEV_W) {
133 			metal_cache_flush(sg_out[i].virt, sg_out[i].len);
134 		}
135 		metal_cache_invalidate(sg_out[i].virt, sg_out[i].len);
136 	}
137 
138 	return nents_in;
139 }
140 
metal_generic_dev_dma_unmap(struct metal_bus * bus,struct metal_device * device,uint32_t dir,struct metal_sg * sg,int nents)141 void metal_generic_dev_dma_unmap(struct metal_bus *bus,
142 				 struct metal_device *device,
143 				 uint32_t dir,
144 				 struct metal_sg *sg,
145 				 int nents)
146 {
147 	(void)bus;
148 	(void)device;
149 	(void)dir;
150 	int i;
151 
152 	for (i = 0; i < nents; i++) {
153 		metal_cache_invalidate(sg[i].virt, sg[i].len);
154 	}
155 }
156 
157 struct metal_bus metal_weak metal_generic_bus = {
158 	.name = "generic",
159 	.ops  = {
160 		.bus_close = NULL,
161 		.dev_open  = metal_generic_dev_open,
162 		.dev_close = NULL,
163 		.dev_irq_ack = NULL,
164 		.dev_dma_map = metal_generic_dev_dma_map,
165 		.dev_dma_unmap = metal_generic_dev_dma_unmap,
166 	},
167 };
168