1/*
2 * B53 register access through memory mapped registers
3 *
4 * Copyright (C) 2012-2013 Jonas Gorski <jogo@openwrt.org>
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <linux/kernel.h>
20#include <linux/module.h>
21#include <linux/io.h>
22#include <linux/platform_device.h>
23#include <linux/platform_data/b53.h>
24
25#include "b53_priv.h"
26
27struct b53_mmap_priv {
28	void __iomem *regs;
29};
30
31static int b53_mmap_read8(struct b53_device *dev, u8 page, u8 reg, u8 *val)
32{
33	struct b53_mmap_priv *priv = dev->priv;
34	void __iomem *regs = priv->regs;
35
36	*val = readb(regs + (page << 8) + reg);
37
38	return 0;
39}
40
41static int b53_mmap_read16(struct b53_device *dev, u8 page, u8 reg, u16 *val)
42{
43	struct b53_mmap_priv *priv = dev->priv;
44	void __iomem *regs = priv->regs;
45
46	if (WARN_ON(reg % 2))
47		return -EINVAL;
48
49	if (dev->pdata && dev->pdata->big_endian)
50		*val = ioread16be(regs + (page << 8) + reg);
51	else
52		*val = readw(regs + (page << 8) + reg);
53
54	return 0;
55}
56
57static int b53_mmap_read32(struct b53_device *dev, u8 page, u8 reg, u32 *val)
58{
59	struct b53_mmap_priv *priv = dev->priv;
60	void __iomem *regs = priv->regs;
61
62	if (WARN_ON(reg % 4))
63		return -EINVAL;
64
65	if (dev->pdata && dev->pdata->big_endian)
66		*val = ioread32be(regs + (page << 8) + reg);
67	else
68		*val = readl(regs + (page << 8) + reg);
69
70	return 0;
71}
72
73static int b53_mmap_read48(struct b53_device *dev, u8 page, u8 reg, u64 *val)
74{
75	struct b53_mmap_priv *priv = dev->priv;
76	void __iomem *regs = priv->regs;
77
78	if (WARN_ON(reg % 2))
79		return -EINVAL;
80
81	if (reg % 4) {
82		u16 lo;
83		u32 hi;
84
85		if (dev->pdata && dev->pdata->big_endian) {
86			lo = ioread16be(regs + (page << 8) + reg);
87			hi = ioread32be(regs + (page << 8) + reg + 2);
88		} else {
89			lo = readw(regs + (page << 8) + reg);
90			hi = readl(regs + (page << 8) + reg + 2);
91		}
92
93		*val = ((u64)hi << 16) | lo;
94	} else {
95		u32 lo;
96		u16 hi;
97
98		if (dev->pdata && dev->pdata->big_endian) {
99			lo = ioread32be(regs + (page << 8) + reg);
100			hi = ioread16be(regs + (page << 8) + reg + 4);
101		} else {
102			lo = readl(regs + (page << 8) + reg);
103			hi = readw(regs + (page << 8) + reg + 4);
104		}
105
106		*val = ((u64)hi << 32) | lo;
107	}
108
109	return 0;
110}
111
112static int b53_mmap_read64(struct b53_device *dev, u8 page, u8 reg, u64 *val)
113{
114	struct b53_mmap_priv *priv = dev->priv;
115	void __iomem *regs = priv->regs;
116	u32 hi, lo;
117
118	if (WARN_ON(reg % 4))
119		return -EINVAL;
120
121	if (dev->pdata && dev->pdata->big_endian) {
122		lo = ioread32be(regs + (page << 8) + reg);
123		hi = ioread32be(regs + (page << 8) + reg + 4);
124	} else {
125		lo = readl(regs + (page << 8) + reg);
126		hi = readl(regs + (page << 8) + reg + 4);
127	}
128
129	*val = ((u64)hi << 32) | lo;
130
131	return 0;
132}
133
134static int b53_mmap_write8(struct b53_device *dev, u8 page, u8 reg, u8 value)
135{
136	struct b53_mmap_priv *priv = dev->priv;
137	void __iomem *regs = priv->regs;
138
139	writeb(value, regs + (page << 8) + reg);
140
141	return 0;
142}
143
144static int b53_mmap_write16(struct b53_device *dev, u8 page, u8 reg,
145			    u16 value)
146{
147	struct b53_mmap_priv *priv = dev->priv;
148	void __iomem *regs = priv->regs;
149
150	if (WARN_ON(reg % 2))
151		return -EINVAL;
152
153	if (dev->pdata && dev->pdata->big_endian)
154		iowrite16be(value, regs + (page << 8) + reg);
155	else
156		writew(value, regs + (page << 8) + reg);
157
158	return 0;
159}
160
161static int b53_mmap_write32(struct b53_device *dev, u8 page, u8 reg,
162			    u32 value)
163{
164	struct b53_mmap_priv *priv = dev->priv;
165	void __iomem *regs = priv->regs;
166
167	if (WARN_ON(reg % 4))
168		return -EINVAL;
169
170	if (dev->pdata && dev->pdata->big_endian)
171		iowrite32be(value, regs + (page << 8) + reg);
172	else
173		writel(value, regs + (page << 8) + reg);
174
175	return 0;
176}
177
178static int b53_mmap_write48(struct b53_device *dev, u8 page, u8 reg,
179			    u64 value)
180{
181	if (WARN_ON(reg % 2))
182		return -EINVAL;
183
184	if (reg % 4) {
185		u32 hi = (u32)(value >> 16);
186		u16 lo = (u16)value;
187
188		b53_mmap_write16(dev, page, reg, lo);
189		b53_mmap_write32(dev, page, reg + 2, hi);
190	} else {
191		u16 hi = (u16)(value >> 32);
192		u32 lo = (u32)value;
193
194		b53_mmap_write32(dev, page, reg, lo);
195		b53_mmap_write16(dev, page, reg + 4, hi);
196	}
197
198	return 0;
199}
200
201static int b53_mmap_write64(struct b53_device *dev, u8 page, u8 reg,
202			    u64 value)
203{
204	u32 hi, lo;
205
206	hi = upper_32_bits(value);
207	lo = lower_32_bits(value);
208
209	if (WARN_ON(reg % 4))
210		return -EINVAL;
211
212	b53_mmap_write32(dev, page, reg, lo);
213	b53_mmap_write32(dev, page, reg + 4, hi);
214
215	return 0;
216}
217
218static int b53_mmap_phy_read16(struct b53_device *dev, int addr, int reg,
219			       u16 *value)
220{
221	return -EIO;
222}
223
224static int b53_mmap_phy_write16(struct b53_device *dev, int addr, int reg,
225				u16 value)
226{
227	return -EIO;
228}
229
230static const struct b53_io_ops b53_mmap_ops = {
231	.read8 = b53_mmap_read8,
232	.read16 = b53_mmap_read16,
233	.read32 = b53_mmap_read32,
234	.read48 = b53_mmap_read48,
235	.read64 = b53_mmap_read64,
236	.write8 = b53_mmap_write8,
237	.write16 = b53_mmap_write16,
238	.write32 = b53_mmap_write32,
239	.write48 = b53_mmap_write48,
240	.write64 = b53_mmap_write64,
241	.phy_read16 = b53_mmap_phy_read16,
242	.phy_write16 = b53_mmap_phy_write16,
243};
244
245static int b53_mmap_probe(struct platform_device *pdev)
246{
247	struct b53_platform_data *pdata = pdev->dev.platform_data;
248	struct b53_mmap_priv *priv;
249	struct b53_device *dev;
250
251	if (!pdata)
252		return -EINVAL;
253
254	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
255	if (!priv)
256		return -ENOMEM;
257
258	priv->regs = pdata->regs;
259
260	dev = b53_switch_alloc(&pdev->dev, &b53_mmap_ops, priv);
261	if (!dev)
262		return -ENOMEM;
263
264	dev->pdata = pdata;
265
266	platform_set_drvdata(pdev, dev);
267
268	return b53_switch_register(dev);
269}
270
271static int b53_mmap_remove(struct platform_device *pdev)
272{
273	struct b53_device *dev = platform_get_drvdata(pdev);
274
275	if (dev)
276		b53_switch_remove(dev);
277
278	return 0;
279}
280
281static const struct of_device_id b53_mmap_of_table[] = {
282	{ .compatible = "brcm,bcm3384-switch" },
283	{ .compatible = "brcm,bcm6328-switch" },
284	{ .compatible = "brcm,bcm6368-switch" },
285	{ .compatible = "brcm,bcm63xx-switch" },
286	{ /* sentinel */ },
287};
288MODULE_DEVICE_TABLE(of, b53_mmap_of_table);
289
290static struct platform_driver b53_mmap_driver = {
291	.probe = b53_mmap_probe,
292	.remove = b53_mmap_remove,
293	.driver = {
294		.name = "b53-switch",
295		.of_match_table = b53_mmap_of_table,
296	},
297};
298
299module_platform_driver(b53_mmap_driver);
300MODULE_AUTHOR("Jonas Gorski <jogo@openwrt.org>");
301MODULE_DESCRIPTION("B53 MMAP access driver");
302MODULE_LICENSE("Dual BSD/GPL");
303