summaryrefslogtreecommitdiff
path: root/src/southbridge/ricoh/rl5c476/rl5c476.c
blob: 645e5f11b477f73b9bfdd0aabf87a202634fbf5d (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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
/* SPDX-License-Identifier: GPL-2.0-or-later */

#include <device/device.h>
#include <device/pci.h>
#include <device/pci_ops.h>
#include <device/pci_ids.h>
#include <console/console.h>
#include <device/cardbus.h>
#include <delay.h>
#include "rl5c476.h"
#include "chip.h"

static int enable_cf_boot = 0;
static unsigned int cf_base;

static void rl5c476_init(struct device *dev)
{
	pc16reg_t *pc16;
	unsigned char *base;

	/* cardbus controller function 1 for CF Socket */
	printk(BIOS_DEBUG, "Ricoh RL5c476: Initializing.\n");

	printk(BIOS_DEBUG, "CF Base = %0x\n",cf_base);

	/* misc control register */
	pci_write_config16(dev,0x82,0x00a0);

	/* set up second slot as compact flash port if asked to do so */

	if (!enable_cf_boot) {
		printk(BIOS_DEBUG, "CF boot not enabled.\n");
		return;
	}

	if (PCI_FUNC(dev->path.pci.devfn) != 1) {
		// Only configure if second CF slot.
		return;
	}

	/* make sure isa interrupts are enabled */
	pci_write_config16(dev,0x3e,0x0780);

	/* pick up where 16 bit card control structure is
	 * (0x800 bytes into config structure)
	 */
	base = (unsigned char *)pci_read_config32(dev,0x10);
	pc16 = (pc16reg_t *)(base + 0x800);

	/* disable memory and io windows and turn off socket power */
	pc16->pwctrl = 0;

	/* disable irq lines */
	pc16->igctrl = 0;

	/* disable memory and I/O windows */
	pc16->awinen = 0;

	/* reset card, configure for I/O and set IRQ line */
	pc16->igctrl = 0x69;

	/* set io window 0 for 1e0 - 1ef */
	/* NOTE: This now sets CF up on a contiguous I/O window of
	 * 16 bytes, 0x1e0 to 0x1ef.
	 * Be warned that this is not a standard IDE address as
	 * automatically detected by the likes of FILO, and would need
	 * patching to recognize these addresses as an IDE drive.
	 *
	 * An earlier version of this driver set up 2 I/O windows to
	 * emulate the expected addresses for IDE2, however the PCMCIA
	 * package within Linux then could not re-initialize the
	 * device as it tried to take control of it. So I believe it is
	 * easier to patch Filo or the like to pick up this drive
	 * rather than playing silly games as the kernel tries to
	 * boot.
	 *
	 * Nonetheless, FILO needs a special option enabled to boot
	 * from this configuration, and it needs to clean up
	 * afterwards. Please refer to FILO documentation and source
	 * code for more details.
	 */
	pc16->iostl0 = 0xe0;
	pc16->iosth0 = 1;

	pc16->iospl0 = 0xef;
	pc16->iosph0 = 1;

	pc16->ioffl0 = 0;
	pc16->ioffh0 = 0;

	/* clear window 1 */
	pc16->iostl1 = 0;
	pc16->iosth1 = 0;

	pc16->iospl1 = 0;
	pc16->iosph1 = 0;

	pc16->ioffl1 = 0x0;
	pc16->ioffh1 = 0;

	/* set up CF config window */
	pc16->smpga0 = cf_base>>24;
	pc16->smsth0 = (cf_base>>20)&0x0f;
	pc16->smstl0 = (cf_base>>12)&0xff;
	pc16->smsph0 = ((cf_base>>20)&0x0f) | 0x80;
	pc16->smspl0 = (cf_base>>12)&0xff;
	pc16->moffl0 = 0;
	pc16->moffh0 = 0x40;

	/* set I/O width for Auto Data width */
	pc16->ioctrl = 0x22;

	/* enable I/O window 0 and 1 */
	pc16->awinen = 0xc1;

	pc16->miscc1 = 1;

	/* apply power and enable outputs */
	pc16->pwctrl = 0xb0;

	// delay could be optimised, but this works
	udelay(100000);

	pc16->igctrl = 0x69;

	/* 16 bit CF always have first config byte at 0x200 into
	 * Config structure, but CF+ may not according to spec -
	 * should locate through reading tuple data, but this should
	 * do for now.
	 */
	unsigned char *cptr;
	cptr = (unsigned char *)(cf_base + 0x200);
	printk(BIOS_DEBUG, "CF Config = %x\n",*cptr);

	/* Set CF to decode 16 IO bytes on any 16 byte boundary -
	 * rely on the io windows of the bridge set up above to
	 * map those bytes into the addresses for IDE controller 3
	 * (0x1e8 - 0x1ef and 0x3ed - 0x3ee)
	 */
	*cptr = 0x41;
}

static void rl5c476_read_resources(struct device *dev)
{
	struct resource *resource;

	/* For CF socket we need an extra memory window for
	 * the control structure of the CF itself
	 */
	if (enable_cf_boot && (PCI_FUNC(dev->path.pci.devfn) == 1)) {
		/* fake index as it isn't in PCI config space */
		resource = new_resource(dev, 1);
		resource->flags |= IORESOURCE_MEM;
		resource->size = 0x1000;
		resource->align = resource->gran = 12;
		resource->limit= 0xffff0000;
	}
	cardbus_read_resources(dev);
}

static void rl5c476_set_resources(struct device *dev)
{
	struct resource *resource;
	printk(BIOS_DEBUG, "%s In set resources\n",dev_path(dev));
	if (enable_cf_boot && (PCI_FUNC(dev->path.pci.devfn) == 1)) {
		resource = find_resource(dev,1);
		if (!(resource->flags & IORESOURCE_STORED)) {
			resource->flags |= IORESOURCE_STORED;
			printk(BIOS_DEBUG, "%s 1 ==> %llx\n", dev_path(dev), resource->base);
			cf_base = resource->base;
		}
	}

	pci_dev_set_resources(dev);
}

static void rl5c476_set_subsystem(struct device *dev, unsigned int vendor,
				  unsigned int device)
{
	u16 miscreg = pci_read_config16(dev, 0x82);
	/* Enable subsystem id register writes */
	pci_write_config16(dev, 0x82, miscreg | 0x40);

	pci_dev_set_subsystem(dev, vendor, device);

	/* restore original contents */
	pci_write_config16(dev, 0x82, miscreg);
}

static struct pci_operations rl5c476_pci_ops = {
	.set_subsystem    = rl5c476_set_subsystem,
};

static struct device_operations ricoh_rl5c476_ops = {
	.read_resources   = rl5c476_read_resources,
	.set_resources    = rl5c476_set_resources,
	.enable_resources = cardbus_enable_resources,
	.init             = rl5c476_init,
	.scan_bus         = pci_scan_bridge,
	.ops_pci          = &rl5c476_pci_ops,
};

static const struct pci_driver ricoh_rl5c476_driver __pci_driver = {
	.ops    = &ricoh_rl5c476_ops,
	.vendor = PCI_VID_RICOH,
	.device = PCI_DID_RICOH_RL5C476,
};

static void southbridge_init(struct device *dev)
{
	struct southbridge_ricoh_rl5c476_config *conf = dev->chip_info;
	enable_cf_boot = conf->enable_cf;
}

struct chip_operations southbridge_ricoh_rl5c476_ops = {
	.name = "Ricoh RL5C476 CardBus Controller",
	.enable_dev    = southbridge_init,
};