1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  *  Software partition device (UCLASS_PARTITION)
4  *
5  *  Copyright (c) 2021 Linaro Limited
6  *			Author: AKASHI Takahiro
7  */
8 
9 #define LOG_CATEGORY UCLASS_PARTITION
10 
11 #include <blk.h>
12 #include <dm.h>
13 #include <log.h>
14 #include <part.h>
15 #include <vsprintf.h>
16 #include <dm/device-internal.h>
17 #include <dm/lists.h>
18 
19 /**
20  * disk_blk_part_validate() - Check whether access to partition is within limits
21  *
22  * @dev: Device (partition udevice)
23  * @start: Start block for the access(from start of partition)
24  * @blkcnt: Number of blocks to access (within the partition)
25  * @return 0 on valid block range, or -ve on error.
26  */
disk_blk_part_validate(struct udevice * dev,lbaint_t start,lbaint_t blkcnt)27 static int disk_blk_part_validate(struct udevice *dev, lbaint_t start, lbaint_t blkcnt)
28 {
29 	struct disk_part *part = dev_get_uclass_plat(dev);
30 
31 	if (device_get_uclass_id(dev) != UCLASS_PARTITION)
32 		return -ENOSYS;
33 
34 	if (start >= part->gpt_part_info.size)
35 		return -E2BIG;
36 
37 	if ((start + blkcnt) > part->gpt_part_info.size)
38 		return -ERANGE;
39 
40 	return 0;
41 }
42 
43 /**
44  * disk_blk_part_offset() - Compute offset from start of block device
45  *
46  * @dev: Device (partition udevice)
47  * @start: Start block for the access (from start of partition)
48  * @return Start block for the access (from start of block device)
49  */
disk_blk_part_offset(struct udevice * dev,lbaint_t start)50 static lbaint_t disk_blk_part_offset(struct udevice *dev, lbaint_t start)
51 {
52 	struct disk_part *part = dev_get_uclass_plat(dev);
53 
54 	return start + part->gpt_part_info.start;
55 }
56 
57 /*
58  * BLOCK IO APIs
59  */
60 /**
61  * disk_blk_read() - Read from a block device partition
62  *
63  * @dev: Device to read from (partition udevice)
64  * @start: Start block for the read (from start of partition)
65  * @blkcnt: Number of blocks to read (within the partition)
66  * @buffer: Place to put the data
67  * @return number of blocks read (which may be less than @blkcnt),
68  * or -ve on error. This never returns 0 unless @blkcnt is 0
69  */
disk_blk_read(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,void * buffer)70 unsigned long disk_blk_read(struct udevice *dev, lbaint_t start,
71 			    lbaint_t blkcnt, void *buffer)
72 {
73 	int ret = disk_blk_part_validate(dev, start, blkcnt);
74 
75 	if (ret)
76 		return ret;
77 
78 	return blk_read(dev_get_parent(dev), disk_blk_part_offset(dev, start),
79 			blkcnt, buffer);
80 }
81 
82 /**
83  * disk_blk_write() - Write to a block device
84  *
85  * @dev: Device to write to (partition udevice)
86  * @start: Start block for the write (from start of partition)
87  * @blkcnt: Number of blocks to write (within the partition)
88  * @buffer: Data to write
89  * @return number of blocks written (which may be less than @blkcnt),
90  * or -ve on error. This never returns 0 unless @blkcnt is 0
91  */
disk_blk_write(struct udevice * dev,lbaint_t start,lbaint_t blkcnt,const void * buffer)92 unsigned long disk_blk_write(struct udevice *dev, lbaint_t start,
93 			     lbaint_t blkcnt, const void *buffer)
94 {
95 	int ret = disk_blk_part_validate(dev, start, blkcnt);
96 
97 	if (ret)
98 		return ret;
99 
100 	return blk_write(dev_get_parent(dev), disk_blk_part_offset(dev, start),
101 			 blkcnt, buffer);
102 }
103 
104 /**
105  * disk_blk_erase() - Erase part of a block device
106  *
107  * @dev: Device to erase (partition udevice)
108  * @start: Start block for the erase (from start of partition)
109  * @blkcnt: Number of blocks to erase (within the partition)
110  * @return number of blocks erased (which may be less than @blkcnt),
111  * or -ve on error. This never returns 0 unless @blkcnt is 0
112  */
disk_blk_erase(struct udevice * dev,lbaint_t start,lbaint_t blkcnt)113 unsigned long disk_blk_erase(struct udevice *dev, lbaint_t start,
114 			     lbaint_t blkcnt)
115 {
116 	int ret = disk_blk_part_validate(dev, start, blkcnt);
117 
118 	if (ret)
119 		return ret;
120 
121 	return blk_erase(dev_get_parent(dev), disk_blk_part_offset(dev, start),
122 			 blkcnt);
123 }
124 
125 UCLASS_DRIVER(partition) = {
126 	.id		= UCLASS_PARTITION,
127 	.per_device_plat_auto	= sizeof(struct disk_part),
128 	.name		= "partition",
129 };
130 
131 static const struct blk_ops blk_part_ops = {
132 	.read	= disk_blk_read,
133 	.write	= disk_blk_write,
134 	.erase	= disk_blk_erase,
135 };
136 
137 U_BOOT_DRIVER(blk_partition) = {
138 	.name		= "blk_partition",
139 	.id		= UCLASS_PARTITION,
140 	.ops		= &blk_part_ops,
141 };
142