aboutsummaryrefslogtreecommitdiff
path: root/arch/sh/drivers/superhyway/ops-sh4-202.c
blob: 490142274e3b0a8f5cc5f345b16e1f728486160c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// SPDX-License-Identifier: GPL-2.0
/*
 * arch/sh/drivers/superhyway/ops-sh4-202.c
 *
 * SuperHyway bus support for SH4-202
 *
 * Copyright (C) 2005  Paul Mundt
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/superhyway.h>
#include <linux/string.h>
#include <asm/addrspace.h>
#include <asm/io.h>

#define PHYS_EMI_CBLOCK		P4SEGADDR(0x1ec00000)
#define PHYS_EMI_DBLOCK		P4SEGADDR(0x08000000)
#define PHYS_FEMI_CBLOCK	P4SEGADDR(0x1f800000)
#define PHYS_FEMI_DBLOCK	P4SEGADDR(0x00000000)

#define PHYS_EPBR_BLOCK		P4SEGADDR(0x1de00000)
#define PHYS_DMAC_BLOCK		P4SEGADDR(0x1fa00000)
#define PHYS_PBR_BLOCK		P4SEGADDR(0x1fc00000)

static struct resource emi_resources[] = {
	[0] = {
		.start	= PHYS_EMI_CBLOCK,
		.end	= PHYS_EMI_CBLOCK + 0x00300000 - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= PHYS_EMI_DBLOCK,
		.end	= PHYS_EMI_DBLOCK + 0x08000000 - 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct superhyway_device emi_device = {
	.name		= "emi",
	.num_resources	= ARRAY_SIZE(emi_resources),
	.resource	= emi_resources,
};

static struct resource femi_resources[] = {
	[0] = {
		.start	= PHYS_FEMI_CBLOCK,
		.end	= PHYS_FEMI_CBLOCK + 0x00100000 - 1,
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= PHYS_FEMI_DBLOCK,
		.end	= PHYS_FEMI_DBLOCK + 0x08000000 - 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct superhyway_device femi_device = {
	.name		= "femi",
	.num_resources	= ARRAY_SIZE(femi_resources),
	.resource	= femi_resources,
};

static struct resource epbr_resources[] = {
	[0] = {
		.start	= P4SEGADDR(0x1e7ffff8),
		.end	= P4SEGADDR(0x1e7ffff8 + (sizeof(u32) * 2) - 1),
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= PHYS_EPBR_BLOCK,
		.end	= PHYS_EPBR_BLOCK + 0x00a00000 - 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct superhyway_device epbr_device = {
	.name		= "epbr",
	.num_resources	= ARRAY_SIZE(epbr_resources),
	.resource	= epbr_resources,
};

static struct resource dmac_resource = {
	.start	= PHYS_DMAC_BLOCK,
	.end	= PHYS_DMAC_BLOCK + 0x00100000 - 1,
	.flags	= IORESOURCE_MEM,
};

static struct superhyway_device dmac_device = {
	.name		= "dmac",
	.num_resources	= 1,
	.resource	= &dmac_resource,
};

static struct resource pbr_resources[] = {
	[0] = {
		.start	= P4SEGADDR(0x1ffffff8),
		.end	= P4SEGADDR(0x1ffffff8 + (sizeof(u32) * 2) - 1),
		.flags	= IORESOURCE_MEM,
	},
	[1] = {
		.start	= PHYS_PBR_BLOCK,
		.end	= PHYS_PBR_BLOCK + 0x00400000 - (sizeof(u32) * 2) - 1,
		.flags	= IORESOURCE_MEM,
	},
};

static struct superhyway_device pbr_device = {
	.name		= "pbr",
	.num_resources	= ARRAY_SIZE(pbr_resources),
	.resource	= pbr_resources,
};

static struct superhyway_device *sh4202_devices[] __initdata = {
	&emi_device, &femi_device, &epbr_device, &dmac_device, &pbr_device,
};

static int sh4202_read_vcr(unsigned long base, struct superhyway_vcr_info *vcr)
{
	u32 vcrh, vcrl;
	u64 tmp;

	/*
	 * XXX: Even though the SH4-202 Evaluation Device documentation
	 * indicates that VCRL is mapped first with VCRH at a + 0x04
	 * offset, the opposite seems to be true.
	 *
	 * Some modules (PBR and ePBR for instance) also appear to have
	 * VCRL/VCRH flipped in the documentation, but on the SH4-202
	 * itself it appears that these are all consistently mapped with
	 * VCRH preceding VCRL.
	 *
	 * Do not trust the documentation, for it is evil.
	 */
	vcrh = __raw_readl(base);
	vcrl = __raw_readl(base + sizeof(u32));

	tmp = ((u64)vcrh << 32) | vcrl;
	memcpy(vcr, &tmp, sizeof(u64));

	return 0;
}

static int sh4202_write_vcr(unsigned long base, struct superhyway_vcr_info vcr)
{
	u64 tmp = *(u64 *)&vcr;

	__raw_writel((tmp >> 32) & 0xffffffff, base);
	__raw_writel(tmp & 0xffffffff, base + sizeof(u32));

	return 0;
}

static struct superhyway_ops sh4202_superhyway_ops = {
	.read_vcr	= sh4202_read_vcr,
	.write_vcr	= sh4202_write_vcr,
};

struct superhyway_bus superhyway_channels[] = {
	{ &sh4202_superhyway_ops, },
	{ 0, },
};

int __init superhyway_scan_bus(struct superhyway_bus *bus)
{
	return superhyway_add_devices(bus, sh4202_devices,
				      ARRAY_SIZE(sh4202_devices));
}