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
17enum misc_op {
18	MISC_OP_READ,
19	MISC_OP_WRITE
20};
21
22static char *misc_op_str[] = {
23	"read",
24	"write"
25};
26
27static 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
44static int do_misc_op(struct cmd_tbl *cmdtp, int flag,
45		      int argc, char *const argv[], enum misc_op op)
46{
47	struct udevice *dev;
48	int offset;
49	void *buf;
50	int size;
51	int ret;
52
53	ret = uclass_get_device_by_name(UCLASS_MISC, argv[0], &dev);
54	if (ret) {
55		printf("Unable to find device %s\n", argv[0]);
56		return ret;
57	}
58
59	offset = hextoul(argv[1], NULL);
60	buf = (void *)hextoul(argv[2], NULL);
61	size = hextoul(argv[3], NULL);
62
63	if (op == MISC_OP_READ)
64		ret = misc_read(dev, offset, buf, size);
65	else
66		ret = misc_write(dev, offset, buf, size);
67
68	if (ret < 0) {
69		if (ret == -ENOSYS) {
70			printf("The device does not support %s\n",
71			       misc_op_str[op]);
72			ret = 0;
73		}
74	} else {
75		if (ret == size)
76			ret = 0;
77		else
78			printf("Partially %s %d bytes\n", misc_op_str[op], ret);
79	}
80
81	return ret;
82}
83
84static int do_misc_read(struct cmd_tbl *cmdtp, int flag,
85			int argc, char *const argv[])
86{
87	return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_READ);
88}
89
90static int do_misc_write(struct cmd_tbl *cmdtp, int flag,
91			 int argc, char *const argv[])
92{
93	return do_misc_op(cmdtp, flag, argc, argv, MISC_OP_WRITE);
94}
95
96static struct cmd_tbl misc_commands[] = {
97	U_BOOT_CMD_MKENT(list, 0, 1, do_misc_list, "", ""),
98	U_BOOT_CMD_MKENT(read, 4, 1, do_misc_read, "", ""),
99	U_BOOT_CMD_MKENT(write, 4, 1, do_misc_write, "", ""),
100};
101
102static int do_misc(struct cmd_tbl *cmdtp, int flag,
103		   int argc, char *const argv[])
104{
105	struct cmd_tbl *misc_cmd;
106	int ret;
107
108	if (argc < 2)
109		return CMD_RET_USAGE;
110	misc_cmd = find_cmd_tbl(argv[1], misc_commands,
111				ARRAY_SIZE(misc_commands));
112	argc -= 2;
113	argv += 2;
114	if (!misc_cmd || argc != misc_cmd->maxargs)
115		return CMD_RET_USAGE;
116
117	ret = misc_cmd->cmd(misc_cmd, flag, argc, argv);
118
119	return cmd_process_error(misc_cmd, ret);
120}
121
122U_BOOT_CMD(
123	misc,	6,	1,	do_misc,
124	"Access miscellaneous devices with MISC uclass driver APIs",
125	"list                       - list all miscellaneous devices\n"
126	"misc read  name offset addr len - read `len' bytes starting at\n"
127	"				  `offset' of device `name'\n"
128	"				  to memory at `addr'\n"
129	"misc write name offset addr len - write `len' bytes starting at\n"
130	"				  `offset' of device `name'\n"
131	"				  from memory at `addr'"
132);
133