summaryrefslogtreecommitdiff
path: root/src/drivers/spi/cbfs_spi.c
blob: e311752dee99f46b1865c10fd545d603a3e6f323 (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
/*
 * This file is part of the coreboot project.
 *
 * Copyright 2014 Google Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/*
 * This file provides a common CBFS wrapper for SPI storage. SPI driver
 * context is expanded with the buffer descriptor used to store data read from
 * SPI.
 */

#include <boot_device.h>
#include <console/console.h>
#include <spi_flash.h>
#include <symbols.h>
#include <cbmem.h>
#include <stdint.h>
#include <timer.h>

static struct spi_flash spi_flash_info;
static bool spi_flash_init_done;

/*
 * Set this to 1 to debug SPI speed, 0 to disable it
 * The format is:
 *
 * read SPI 62854 7db7: 10416 us, 3089 KB/s, 24.712 Mbps
 *
 * The important number is the last one. It should roughly match your SPI
 * clock. If it doesn't, your driver might need a little tuning.
 */
#define SPI_SPEED_DEBUG		0

static ssize_t spi_readat(const struct region_device *rd, void *b,
				size_t offset, size_t size)
{
	struct stopwatch sw;
	bool show = SPI_SPEED_DEBUG && size >= 4 * KiB;

	if (show)
		stopwatch_init(&sw);
	if (spi_flash_read(&spi_flash_info, offset, size, b))
		return -1;
	if (show) {
		long usecs;

		usecs = stopwatch_duration_usecs(&sw);
		u64 speed;	/* KiB/s */
		int bps;	/* Bits per second */

		speed = (u64)size * 1000 / usecs;
		bps = speed * 8;

		printk(BIOS_DEBUG, "read SPI %#zx %#zx: %ld us, %lld KB/s, %d.%03d Mbps\n",
		       offset, size, usecs, speed, bps / 1000, bps % 1000);
	}
	return size;
}

static ssize_t spi_writeat(const struct region_device *rd, const void *b,
				size_t offset, size_t size)
{
	if (spi_flash_write(&spi_flash_info, offset, size, b))
		return -1;
	return size;
}

static ssize_t spi_eraseat(const struct region_device *rd,
				size_t offset, size_t size)
{
	if (spi_flash_erase(&spi_flash_info, offset, size))
		return -1;
	return size;
}

/* Provide all operations on the same device. */
static const struct region_device_ops spi_ops = {
	.mmap = mmap_helper_rdev_mmap,
	.munmap = mmap_helper_rdev_munmap,
	.readat = spi_readat,
	.writeat = spi_writeat,
	.eraseat = spi_eraseat,
};

static struct mmap_helper_region_device mdev =
	MMAP_HELPER_REGION_INIT(&spi_ops, 0, CONFIG_ROM_SIZE);

static void switch_to_postram_cache(int unused)
{
	/*
	 * Call boot_device_init() to ensure spi_flash is initialized before
	 * backing mdev with postram cache. This prevents the mdev backing from
	 * being overwritten if spi_flash was not accessed before dram was up.
	 */
	boot_device_init();
	if (_preram_cbfs_cache != _postram_cbfs_cache)
		mmap_helper_device_init(&mdev, _postram_cbfs_cache,
					REGION_SIZE(postram_cbfs_cache));
}
ROMSTAGE_CBMEM_INIT_HOOK(switch_to_postram_cache);

void boot_device_init(void)
{
	int bus = CONFIG_BOOT_DEVICE_SPI_FLASH_BUS;
	int cs = 0;

	if (spi_flash_init_done == true)
		return;

	if (spi_flash_probe(bus, cs, &spi_flash_info))
		return;

	spi_flash_init_done = true;

	mmap_helper_device_init(&mdev, _cbfs_cache, REGION_SIZE(cbfs_cache));
}

/* Return the CBFS boot device. */
const struct region_device *boot_device_ro(void)
{
	if (spi_flash_init_done != true)
		return NULL;

	return &mdev.rdev;
}

/* The read-only and read-write implementations are symmetric. */
const struct region_device *boot_device_rw(void)
{
	return boot_device_ro();
}

const struct spi_flash *boot_device_spi_flash(void)
{
	boot_device_init();

	if (spi_flash_init_done != true)
		return NULL;

	return &spi_flash_info;
}