1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2020 Wind River Systems, Inc.
4  *
5  * Author:
6  *   Bin Meng <bin.meng@windriver.com>
7  *
8  * A command interface to access misc devices with MISC uclass driver APIs.
9  */
10 
11 #include <common.h>
12 #include <command.h>
13 #include <dm.h>
14 #include <errno.h>
15 #include <misc.h>
16 
17 enum misc_op {
18 	MISC_OP_READ,
19 	MISC_OP_WRITE
20 };
21 
22 static char *misc_op_str[] = {
23 	"read",
24 	"write"
25 };
26 
do_misc_list(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])27 static int do_misc_list(struct cmd_tbl *cmdtp, int flag,
28 			int argc, char *const argv[])
29 {
30 	struct udevice *dev;
31 
32 	printf("Device               Index     Driver\n");
33 	printf("-------------------------------------\n");
34 	for (uclass_first_device(UCLASS_MISC, &dev);
35 	     dev;
36 	     uclass_next_device(&dev)) {
37 		printf("%-20s %5d %10s\n", dev->name, dev_seq(dev),
38 		       dev->driver->name);
39 	}
40 
41 	return 0;
42 }
43 
do_misc_op(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[],enum misc_op op)44 static int do_misc_op(struct cmd_tbl *cmdtp, int flag,
45 		      int argc, char *const argv[], enum misc_op op)
46 {
47 	int (*misc_op)(struct udevice *, int, void *, int);
48 	struct udevice *dev;
49 	int offset;
50 	void *buf;
51 	int size;
52 	int ret;
53 
54 	ret = uclass_get_device_by_name(UCLASS_MISC, argv[0], &dev);
55 	if (ret) {
56 		printf("Unable to find device %s\n", argv[0]);
57 		return ret;
58 	}
59 
60 	offset = simple_strtoul(argv[1], NULL, 16);
61 	buf = (void *)simple_strtoul(argv[2], NULL, 16);
62 	size = simple_strtoul(argv[3], NULL, 16);
63 
64 	if (op == MISC_OP_READ)
65 		misc_op = misc_read;
66 	else
67 		misc_op = misc_write;
68 
69 	ret = misc_op(dev, offset, buf, size);
70 	if (ret < 0) {
71 		if (ret == -ENOSYS) {
72 			printf("The device does not support %s\n",
73 			       misc_op_str[op]);
74 			ret = 0;
75 		}
76 	} else {
77 		if (ret == size)
78 			ret = 0;
79 		else
80 			printf("Partially %s %d bytes\n", misc_op_str[op], ret);
81 	}
82 
83 	return ret;
84 }
85 
do_misc_read(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])86 static int do_misc_read(struct cmd_tbl *cmdtp, int flag,
87 			int argc, char *const argv[])
88 {
89 	return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_READ);
90 }
91 
do_misc_write(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])92 static int do_misc_write(struct cmd_tbl *cmdtp, int flag,
93 			 int argc, char *const argv[])
94 {
95 	return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_WRITE);
96 }
97 
98 static struct cmd_tbl misc_commands[] = {
99 	U_BOOT_CMD_MKENT(list, 0, 1, do_misc_list, "", ""),
100 	U_BOOT_CMD_MKENT(read, 4, 1, do_misc_read, "", ""),
101 	U_BOOT_CMD_MKENT(write, 4, 1, do_misc_write, "", ""),
102 };
103 
do_misc(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])104 static int do_misc(struct cmd_tbl *cmdtp, int flag,
105 		   int argc, char *const argv[])
106 {
107 	struct cmd_tbl *misc_cmd;
108 	int ret;
109 
110 	if (argc < 2)
111 		return CMD_RET_USAGE;
112 	misc_cmd = find_cmd_tbl(argv[1], misc_commands,
113 				ARRAY_SIZE(misc_commands));
114 	argc -= 2;
115 	argv += 2;
116 	if (!misc_cmd || argc != misc_cmd->maxargs)
117 		return CMD_RET_USAGE;
118 
119 	ret = misc_cmd->cmd(misc_cmd, flag, argc, argv);
120 
121 	return cmd_process_error(misc_cmd, ret);
122 }
123 
124 U_BOOT_CMD(
125 	misc,	6,	1,	do_misc,
126 	"Access miscellaneous devices with MISC uclass driver APIs",
127 	"list                       - list all miscellaneous devices\n"
128 	"misc read  name offset addr len - read `len' bytes starting at\n"
129 	"				  `offset' of device `name'\n"
130 	"				  to memory at `addr'\n"
131 	"misc write name offset addr len - write `len' bytes starting at\n"
132 	"				  `offset' of device `name'\n"
133 	"				  from memory at `addr'"
134 );
135