1/*
2 * Sonics Silicon Backplane SoC host related functions.
3 * Subsystem core
4 *
5 * Copyright 2005, Broadcom Corporation
6 * Copyright 2006, 2007, Michael Buesch <m@bues.ch>
7 *
8 * Licensed under the GNU/GPL. See COPYING for details.
9 */
10
11#include "ssb_private.h"
12
13#include <linux/bcm47xx_nvram.h>
14#include <linux/ssb/ssb.h>
15
16static u8 ssb_host_soc_read8(struct ssb_device *dev, u16 offset)
17{
18	struct ssb_bus *bus = dev->bus;
19
20	offset += dev->core_index * SSB_CORE_SIZE;
21	return readb(bus->mmio + offset);
22}
23
24static u16 ssb_host_soc_read16(struct ssb_device *dev, u16 offset)
25{
26	struct ssb_bus *bus = dev->bus;
27
28	offset += dev->core_index * SSB_CORE_SIZE;
29	return readw(bus->mmio + offset);
30}
31
32static u32 ssb_host_soc_read32(struct ssb_device *dev, u16 offset)
33{
34	struct ssb_bus *bus = dev->bus;
35
36	offset += dev->core_index * SSB_CORE_SIZE;
37	return readl(bus->mmio + offset);
38}
39
40#ifdef CONFIG_SSB_BLOCKIO
41static void ssb_host_soc_block_read(struct ssb_device *dev, void *buffer,
42				    size_t count, u16 offset, u8 reg_width)
43{
44	struct ssb_bus *bus = dev->bus;
45	void __iomem *addr;
46
47	offset += dev->core_index * SSB_CORE_SIZE;
48	addr = bus->mmio + offset;
49
50	switch (reg_width) {
51	case sizeof(u8): {
52		u8 *buf = buffer;
53
54		while (count) {
55			*buf = __raw_readb(addr);
56			buf++;
57			count--;
58		}
59		break;
60	}
61	case sizeof(u16): {
62		__le16 *buf = buffer;
63
64		WARN_ON(count & 1);
65		while (count) {
66			*buf = (__force __le16)__raw_readw(addr);
67			buf++;
68			count -= 2;
69		}
70		break;
71	}
72	case sizeof(u32): {
73		__le32 *buf = buffer;
74
75		WARN_ON(count & 3);
76		while (count) {
77			*buf = (__force __le32)__raw_readl(addr);
78			buf++;
79			count -= 4;
80		}
81		break;
82	}
83	default:
84		WARN_ON(1);
85	}
86}
87#endif /* CONFIG_SSB_BLOCKIO */
88
89static void ssb_host_soc_write8(struct ssb_device *dev, u16 offset, u8 value)
90{
91	struct ssb_bus *bus = dev->bus;
92
93	offset += dev->core_index * SSB_CORE_SIZE;
94	writeb(value, bus->mmio + offset);
95}
96
97static void ssb_host_soc_write16(struct ssb_device *dev, u16 offset, u16 value)
98{
99	struct ssb_bus *bus = dev->bus;
100
101	offset += dev->core_index * SSB_CORE_SIZE;
102	writew(value, bus->mmio + offset);
103}
104
105static void ssb_host_soc_write32(struct ssb_device *dev, u16 offset, u32 value)
106{
107	struct ssb_bus *bus = dev->bus;
108
109	offset += dev->core_index * SSB_CORE_SIZE;
110	writel(value, bus->mmio + offset);
111}
112
113#ifdef CONFIG_SSB_BLOCKIO
114static void ssb_host_soc_block_write(struct ssb_device *dev, const void *buffer,
115				     size_t count, u16 offset, u8 reg_width)
116{
117	struct ssb_bus *bus = dev->bus;
118	void __iomem *addr;
119
120	offset += dev->core_index * SSB_CORE_SIZE;
121	addr = bus->mmio + offset;
122
123	switch (reg_width) {
124	case sizeof(u8): {
125		const u8 *buf = buffer;
126
127		while (count) {
128			__raw_writeb(*buf, addr);
129			buf++;
130			count--;
131		}
132		break;
133	}
134	case sizeof(u16): {
135		const __le16 *buf = buffer;
136
137		WARN_ON(count & 1);
138		while (count) {
139			__raw_writew((__force u16)(*buf), addr);
140			buf++;
141			count -= 2;
142		}
143		break;
144	}
145	case sizeof(u32): {
146		const __le32 *buf = buffer;
147
148		WARN_ON(count & 3);
149		while (count) {
150			__raw_writel((__force u32)(*buf), addr);
151			buf++;
152			count -= 4;
153		}
154		break;
155	}
156	default:
157		WARN_ON(1);
158	}
159}
160#endif /* CONFIG_SSB_BLOCKIO */
161
162/* Ops for the plain SSB bus without a host-device (no PCI or PCMCIA). */
163const struct ssb_bus_ops ssb_host_soc_ops = {
164	.read8		= ssb_host_soc_read8,
165	.read16		= ssb_host_soc_read16,
166	.read32		= ssb_host_soc_read32,
167	.write8		= ssb_host_soc_write8,
168	.write16	= ssb_host_soc_write16,
169	.write32	= ssb_host_soc_write32,
170#ifdef CONFIG_SSB_BLOCKIO
171	.block_read	= ssb_host_soc_block_read,
172	.block_write	= ssb_host_soc_block_write,
173#endif
174};
175
176int ssb_host_soc_get_invariants(struct ssb_bus *bus,
177				struct ssb_init_invariants *iv)
178{
179	char buf[20];
180	int len, err;
181
182	/* Fill boardinfo structure */
183	memset(&iv->boardinfo, 0, sizeof(struct ssb_boardinfo));
184
185	len = bcm47xx_nvram_getenv("boardvendor", buf, sizeof(buf));
186	if (len > 0) {
187		err = kstrtou16(strim(buf), 0, &iv->boardinfo.vendor);
188		if (err)
189			pr_warn("Couldn't parse nvram board vendor entry with value \"%s\"\n",
190				buf);
191	}
192	if (!iv->boardinfo.vendor)
193		iv->boardinfo.vendor = SSB_BOARDVENDOR_BCM;
194
195	len = bcm47xx_nvram_getenv("boardtype", buf, sizeof(buf));
196	if (len > 0) {
197		err = kstrtou16(strim(buf), 0, &iv->boardinfo.type);
198		if (err)
199			pr_warn("Couldn't parse nvram board type entry with value \"%s\"\n",
200				buf);
201	}
202
203	memset(&iv->sprom, 0, sizeof(struct ssb_sprom));
204	ssb_fill_sprom_with_fallback(bus, &iv->sprom);
205
206	if (bcm47xx_nvram_getenv("cardbus", buf, sizeof(buf)) >= 0)
207		iv->has_cardbus_slot = !!simple_strtoul(buf, NULL, 10);
208
209	return 0;
210}
211