1 /*
2  * Driver for Altera Partial Reconfiguration IP Core
3  *
4  * Copyright (C) 2016-2017 Intel Corporation
5  *
6  * Based on socfpga-a10.c Copyright (C) 2015-2016 Altera Corporation
7  *  by Alan Tull <atull@opensource.altera.com>
8  *
9  * This program is free software; you can redistribute it and/or modify it
10  * under the terms and conditions of the GNU General Public License,
11  * version 2, as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 #include <linux/delay.h>
22 #include <linux/fpga/altera-pr-ip-core.h>
23 #include <linux/fpga/fpga-mgr.h>
24 #include <linux/module.h>
25 
26 #define ALT_PR_DATA_OFST		0x00
27 #define ALT_PR_CSR_OFST			0x04
28 
29 #define ALT_PR_CSR_PR_START		BIT(0)
30 #define ALT_PR_CSR_STATUS_SFT		2
31 #define ALT_PR_CSR_STATUS_MSK		(7 << ALT_PR_CSR_STATUS_SFT)
32 #define ALT_PR_CSR_STATUS_NRESET	(0 << ALT_PR_CSR_STATUS_SFT)
33 #define ALT_PR_CSR_STATUS_PR_ERR	(1 << ALT_PR_CSR_STATUS_SFT)
34 #define ALT_PR_CSR_STATUS_CRC_ERR	(2 << ALT_PR_CSR_STATUS_SFT)
35 #define ALT_PR_CSR_STATUS_BAD_BITS	(3 << ALT_PR_CSR_STATUS_SFT)
36 #define ALT_PR_CSR_STATUS_PR_IN_PROG	(4 << ALT_PR_CSR_STATUS_SFT)
37 #define ALT_PR_CSR_STATUS_PR_SUCCESS	(5 << ALT_PR_CSR_STATUS_SFT)
38 
39 struct alt_pr_priv {
40 	void __iomem *reg_base;
41 };
42 
43 static enum fpga_mgr_states alt_pr_fpga_state(struct fpga_manager *mgr)
44 {
45 	struct alt_pr_priv *priv = mgr->priv;
46 	const char *err = "unknown";
47 	enum fpga_mgr_states ret = FPGA_MGR_STATE_UNKNOWN;
48 	u32 val;
49 
50 	val = readl(priv->reg_base + ALT_PR_CSR_OFST);
51 
52 	val &= ALT_PR_CSR_STATUS_MSK;
53 
54 	switch (val) {
55 	case ALT_PR_CSR_STATUS_NRESET:
56 		return FPGA_MGR_STATE_RESET;
57 
58 	case ALT_PR_CSR_STATUS_PR_ERR:
59 		err = "pr error";
60 		ret = FPGA_MGR_STATE_WRITE_ERR;
61 		break;
62 
63 	case ALT_PR_CSR_STATUS_CRC_ERR:
64 		err = "crc error";
65 		ret = FPGA_MGR_STATE_WRITE_ERR;
66 		break;
67 
68 	case ALT_PR_CSR_STATUS_BAD_BITS:
69 		err = "bad bits";
70 		ret = FPGA_MGR_STATE_WRITE_ERR;
71 		break;
72 
73 	case ALT_PR_CSR_STATUS_PR_IN_PROG:
74 		return FPGA_MGR_STATE_WRITE;
75 
76 	case ALT_PR_CSR_STATUS_PR_SUCCESS:
77 		return FPGA_MGR_STATE_OPERATING;
78 
79 	default:
80 		break;
81 	}
82 
83 	dev_err(&mgr->dev, "encountered error code %d (%s) in %s()\n",
84 		val, err, __func__);
85 	return ret;
86 }
87 
88 static int alt_pr_fpga_write_init(struct fpga_manager *mgr,
89 				  struct fpga_image_info *info,
90 				  const char *buf, size_t count)
91 {
92 	struct alt_pr_priv *priv = mgr->priv;
93 	u32 val;
94 
95 	if (!(info->flags & FPGA_MGR_PARTIAL_RECONFIG)) {
96 		dev_err(&mgr->dev, "%s Partial Reconfiguration flag not set\n",
97 			__func__);
98 		return -EINVAL;
99 	}
100 
101 	val = readl(priv->reg_base + ALT_PR_CSR_OFST);
102 
103 	if (val & ALT_PR_CSR_PR_START) {
104 		dev_err(&mgr->dev,
105 			"%s Partial Reconfiguration already started\n",
106 		       __func__);
107 		return -EINVAL;
108 	}
109 
110 	writel(val | ALT_PR_CSR_PR_START, priv->reg_base + ALT_PR_CSR_OFST);
111 
112 	return 0;
113 }
114 
115 static int alt_pr_fpga_write(struct fpga_manager *mgr, const char *buf,
116 			     size_t count)
117 {
118 	struct alt_pr_priv *priv = mgr->priv;
119 	u32 *buffer_32 = (u32 *)buf;
120 	size_t i = 0;
121 
122 	if (count <= 0)
123 		return -EINVAL;
124 
125 	/* Write out the complete 32-bit chunks */
126 	while (count >= sizeof(u32)) {
127 		writel(buffer_32[i++], priv->reg_base);
128 		count -= sizeof(u32);
129 	}
130 
131 	/* Write out remaining non 32-bit chunks */
132 	switch (count) {
133 	case 3:
134 		writel(buffer_32[i++] & 0x00ffffff, priv->reg_base);
135 		break;
136 	case 2:
137 		writel(buffer_32[i++] & 0x0000ffff, priv->reg_base);
138 		break;
139 	case 1:
140 		writel(buffer_32[i++] & 0x000000ff, priv->reg_base);
141 		break;
142 	case 0:
143 		break;
144 	default:
145 		/* This will never happen */
146 		return -EFAULT;
147 	}
148 
149 	if (alt_pr_fpga_state(mgr) == FPGA_MGR_STATE_WRITE_ERR)
150 		return -EIO;
151 
152 	return 0;
153 }
154 
155 static int alt_pr_fpga_write_complete(struct fpga_manager *mgr,
156 				      struct fpga_image_info *info)
157 {
158 	u32 i = 0;
159 
160 	do {
161 		switch (alt_pr_fpga_state(mgr)) {
162 		case FPGA_MGR_STATE_WRITE_ERR:
163 			return -EIO;
164 
165 		case FPGA_MGR_STATE_OPERATING:
166 			dev_info(&mgr->dev,
167 				 "successful partial reconfiguration\n");
168 			return 0;
169 
170 		default:
171 			break;
172 		}
173 		udelay(1);
174 	} while (info->config_complete_timeout_us > i++);
175 
176 	dev_err(&mgr->dev, "timed out waiting for write to complete\n");
177 	return -ETIMEDOUT;
178 }
179 
180 static const struct fpga_manager_ops alt_pr_ops = {
181 	.state = alt_pr_fpga_state,
182 	.write_init = alt_pr_fpga_write_init,
183 	.write = alt_pr_fpga_write,
184 	.write_complete = alt_pr_fpga_write_complete,
185 };
186 
187 int alt_pr_register(struct device *dev, void __iomem *reg_base)
188 {
189 	struct alt_pr_priv *priv;
190 	u32 val;
191 
192 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
193 	if (!priv)
194 		return -ENOMEM;
195 
196 	priv->reg_base = reg_base;
197 
198 	val = readl(priv->reg_base + ALT_PR_CSR_OFST);
199 
200 	dev_dbg(dev, "%s status=%d start=%d\n", __func__,
201 		(val & ALT_PR_CSR_STATUS_MSK) >> ALT_PR_CSR_STATUS_SFT,
202 		(int)(val & ALT_PR_CSR_PR_START));
203 
204 	return fpga_mgr_register(dev, dev_name(dev), &alt_pr_ops, priv);
205 }
206 EXPORT_SYMBOL_GPL(alt_pr_register);
207 
208 int alt_pr_unregister(struct device *dev)
209 {
210 	dev_dbg(dev, "%s\n", __func__);
211 
212 	fpga_mgr_unregister(dev);
213 
214 	return 0;
215 }
216 EXPORT_SYMBOL_GPL(alt_pr_unregister);
217 
218 MODULE_AUTHOR("Matthew Gerlach <matthew.gerlach@linux.intel.com>");
219 MODULE_DESCRIPTION("Altera Partial Reconfiguration IP Core");
220 MODULE_LICENSE("GPL v2");
221