1 /*
2  * Copyright (c) 2014 Qualcomm Atheros, Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include <linux/uaccess.h>
18 
19 #include "wil6210.h"
20 #include <uapi/linux/wil6210_uapi.h>
21 
22 #define wil_hex_dump_ioctl(prefix_str, buf, len) \
23 	print_hex_dump_debug("DBG[IOC ]" prefix_str, \
24 			     DUMP_PREFIX_OFFSET, 16, 1, buf, len, true)
25 #define wil_dbg_ioctl(wil, fmt, arg...) wil_dbg(wil, "DBG[IOC ]" fmt, ##arg)
26 
wil_ioc_addr(struct wil6210_priv * wil,uint32_t addr,uint32_t size,enum wil_memio_op op)27 static void __iomem *wil_ioc_addr(struct wil6210_priv *wil, uint32_t addr,
28 				  uint32_t size, enum wil_memio_op op)
29 {
30 	void __iomem *a;
31 	u32 off;
32 
33 	switch (op & wil_mmio_addr_mask) {
34 	case wil_mmio_addr_linker:
35 		a = wmi_buffer(wil, cpu_to_le32(addr));
36 		break;
37 	case wil_mmio_addr_ahb:
38 		a = wmi_addr(wil, addr);
39 		break;
40 	case wil_mmio_addr_bar:
41 		a = wmi_addr(wil, addr + WIL6210_FW_HOST_OFF);
42 		break;
43 	default:
44 		wil_err(wil, "Unsupported address mode, op = 0x%08x\n", op);
45 		return NULL;
46 	}
47 
48 	off = a - wil->csr;
49 	if (size >= WIL6210_MEM_SIZE - off) {
50 		wil_err(wil, "Requested block does not fit into memory: "
51 			"off = 0x%08x size = 0x%08x\n", off, size);
52 		return NULL;
53 	}
54 
55 	return a;
56 }
57 
wil_ioc_memio_dword(struct wil6210_priv * wil,void __user * data)58 static int wil_ioc_memio_dword(struct wil6210_priv *wil, void __user *data)
59 {
60 	struct wil_memio io;
61 	void __iomem *a;
62 	bool need_copy = false;
63 
64 	if (copy_from_user(&io, data, sizeof(io)))
65 		return -EFAULT;
66 
67 	wil_dbg_ioctl(wil, "IO: addr = 0x%08x val = 0x%08x op = 0x%08x\n",
68 		      io.addr, io.val, io.op);
69 
70 	a = wil_ioc_addr(wil, io.addr, sizeof(u32), io.op);
71 	if (!a) {
72 		wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr,
73 			io.op);
74 		return -EINVAL;
75 	}
76 	/* operation */
77 	switch (io.op & wil_mmio_op_mask) {
78 	case wil_mmio_read:
79 		io.val = readl(a);
80 		need_copy = true;
81 		break;
82 	case wil_mmio_write:
83 		writel(io.val, a);
84 		wmb(); /* make sure write propagated to HW */
85 		break;
86 	default:
87 		wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
88 		return -EINVAL;
89 	}
90 
91 	if (need_copy) {
92 		wil_dbg_ioctl(wil, "IO done: addr = 0x%08x"
93 			      " val = 0x%08x op = 0x%08x\n",
94 			      io.addr, io.val, io.op);
95 		if (copy_to_user(data, &io, sizeof(io)))
96 			return -EFAULT;
97 	}
98 
99 	return 0;
100 }
101 
wil_ioc_memio_block(struct wil6210_priv * wil,void __user * data)102 static int wil_ioc_memio_block(struct wil6210_priv *wil, void __user *data)
103 {
104 	struct wil_memio_block io;
105 	void *block;
106 	void __iomem *a;
107 	int rc = 0;
108 
109 	if (copy_from_user(&io, data, sizeof(io)))
110 		return -EFAULT;
111 
112 	wil_dbg_ioctl(wil, "IO: addr = 0x%08x size = 0x%08x op = 0x%08x\n",
113 		      io.addr, io.size, io.op);
114 
115 	/* size */
116 	if (io.size % 4) {
117 		wil_err(wil, "size is not multiple of 4:  0x%08x\n", io.size);
118 		return -EINVAL;
119 	}
120 
121 	a = wil_ioc_addr(wil, io.addr, io.size, io.op);
122 	if (!a) {
123 		wil_err(wil, "invalid address 0x%08x, op = 0x%08x\n", io.addr,
124 			io.op);
125 		return -EINVAL;
126 	}
127 
128 	block = kmalloc(io.size, GFP_USER);
129 	if (!block)
130 		return -ENOMEM;
131 
132 	/* operation */
133 	switch (io.op & wil_mmio_op_mask) {
134 	case wil_mmio_read:
135 		wil_memcpy_fromio_32(block, a, io.size);
136 		wil_hex_dump_ioctl("Read  ", block, io.size);
137 		if (copy_to_user(io.block, block, io.size)) {
138 			rc = -EFAULT;
139 			goto out_free;
140 		}
141 		break;
142 	case wil_mmio_write:
143 		if (copy_from_user(block, io.block, io.size)) {
144 			rc = -EFAULT;
145 			goto out_free;
146 		}
147 		wil_memcpy_toio_32(a, block, io.size);
148 		wmb(); /* make sure write propagated to HW */
149 		wil_hex_dump_ioctl("Write ", block, io.size);
150 		break;
151 	default:
152 		wil_err(wil, "Unsupported operation, op = 0x%08x\n", io.op);
153 		rc = -EINVAL;
154 		break;
155 	}
156 
157 out_free:
158 	kfree(block);
159 	return rc;
160 }
161 
wil_ioctl(struct wil6210_priv * wil,void __user * data,int cmd)162 int wil_ioctl(struct wil6210_priv *wil, void __user *data, int cmd)
163 {
164 	switch (cmd) {
165 	case WIL_IOCTL_MEMIO:
166 		return wil_ioc_memio_dword(wil, data);
167 	case WIL_IOCTL_MEMIO_BLOCK:
168 		return wil_ioc_memio_block(wil, data);
169 	default:
170 		wil_dbg_ioctl(wil, "Unsupported IOCTL 0x%04x\n", cmd);
171 		return -ENOIOCTLCMD;
172 	}
173 }
174