summaryrefslogtreecommitdiff
path: root/drivers/spi/npcm_pspi.c
blob: bd9ac6541135275ba5f7e25c64267634b0ab9b19 (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
219
220
221
222
223
224
225
226
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (c) 2021 Nuvoton Technology.
 */

#include <common.h>
#include <dm.h>
#include <spi.h>
#include <clk.h>
#include <asm/gpio.h>
#include <linux/iopoll.h>

#define MAX_DIV	127

/* Register offsets */
#define PSPI_DATA		0
#define PSPI_CTL1		2
#define PSPI_STAT		4

/* PSPI_CTL1 fields */
#define PSPI_CTL1_SPIEN		BIT(0)
#define PSPI_CTL1_SCM		BIT(7)
#define PSPI_CTL1_SCIDL		BIT(8)
#define PSPI_CTL1_SCDV_MASK	GENMASK(15, 9)
#define PSPI_CTL1_SCDV_SHIFT	9

/* PSPI_STAT fields */
#define PSPI_STAT_BSY		BIT(0)
#define PSPI_STAT_RBF		BIT(1)

struct npcm_pspi_priv {
	void __iomem *base;
	struct clk clk;
	struct gpio_desc cs_gpio;
	u32 max_hz;
};

static inline void spi_cs_activate(struct udevice *dev)
{
	struct udevice *bus = dev->parent;
	struct npcm_pspi_priv *priv = dev_get_priv(bus);

	dm_gpio_set_value(&priv->cs_gpio, 0);
}

static inline void spi_cs_deactivate(struct udevice *dev)
{
	struct udevice *bus = dev->parent;
	struct npcm_pspi_priv *priv = dev_get_priv(bus);

	dm_gpio_set_value(&priv->cs_gpio, 1);
}

static inline void npcm_pspi_enable(struct npcm_pspi_priv *priv)
{
	u16 val;

	val = readw(priv->base + PSPI_CTL1);
	val |= PSPI_CTL1_SPIEN;
	writew(val, priv->base + PSPI_CTL1);
}

static inline void npcm_pspi_disable(struct npcm_pspi_priv *priv)
{
	u16 val;

	val = readw(priv->base + PSPI_CTL1);
	val &= ~PSPI_CTL1_SPIEN;
	writew(val, priv->base + PSPI_CTL1);
}

static int npcm_pspi_xfer(struct udevice *dev, unsigned int bitlen,
			  const void *dout, void *din, unsigned long flags)
{
	struct udevice *bus = dev->parent;
	struct npcm_pspi_priv *priv = dev_get_priv(bus);
	void __iomem *base = priv->base;
	const u8 *tx = dout;
	u8 *rx = din;
	u32 bytes = bitlen / 8;
	u8 tmp;
	u32 val;
	int i, ret = 0;

	npcm_pspi_enable(priv);

	if (flags & SPI_XFER_BEGIN)
		spi_cs_activate(dev);

	for (i = 0; i < bytes; i++) {
		/* Making sure we can write */
		ret = readb_poll_timeout(base + PSPI_STAT, val,
					 !(val & PSPI_STAT_BSY),
					 1000000);
		if (ret < 0)
			break;

		if (tx)
			writeb(*tx++, base + PSPI_DATA);
		else
			writeb(0, base + PSPI_DATA);

		/* Wait till write completed */
		ret = readb_poll_timeout(base + PSPI_STAT, val,
					 !(val & PSPI_STAT_BSY),
					 1000000);
		if (ret < 0)
			break;

		/* Wait till read buffer full */
		ret = readb_poll_timeout(base + PSPI_STAT, val,
					 (val & PSPI_STAT_RBF),
					 1000000);
		if (ret < 0)
			break;

		tmp = readb(base + PSPI_DATA);
		if (rx)
			*rx++ = tmp;
	}

	if (flags & SPI_XFER_END)
		spi_cs_deactivate(dev);

	npcm_pspi_disable(priv);

	return ret;
}

static int npcm_pspi_set_speed(struct udevice *bus, uint speed)
{
	struct npcm_pspi_priv *priv = dev_get_priv(bus);
	ulong apb_clock;
	u32 divisor;
	u16 val;

	apb_clock = clk_get_rate(&priv->clk);
	if (!apb_clock)
		return -EINVAL;

	if (speed > priv->max_hz)
		speed = priv->max_hz;

	divisor = DIV_ROUND_CLOSEST(apb_clock, (2 * speed) - 1);
	if (divisor > MAX_DIV)
		divisor = MAX_DIV;

	val = readw(priv->base + PSPI_CTL1);
	val &= ~PSPI_CTL1_SCDV_MASK;
	val |= divisor << PSPI_CTL1_SCDV_SHIFT;
	writew(val, priv->base + PSPI_CTL1);

	debug("%s: apb_clock=%lu speed=%d divisor=%u\n",
	      __func__, apb_clock, speed, divisor);

	return 0;
}

static int npcm_pspi_set_mode(struct udevice *bus, uint mode)
{
	struct npcm_pspi_priv *priv = dev_get_priv(bus);
	u16 pspi_mode, val;

	switch (mode & (SPI_CPOL | SPI_CPHA)) {
	case SPI_MODE_0:
		pspi_mode = 0;
		break;
	case SPI_MODE_1:
		pspi_mode = PSPI_CTL1_SCM;
		break;
	case SPI_MODE_2:
		pspi_mode = PSPI_CTL1_SCIDL;
		break;
	case SPI_MODE_3:
		pspi_mode = PSPI_CTL1_SCIDL | PSPI_CTL1_SCM;
		break;
	default:
		break;
	}

	val = readw(priv->base + PSPI_CTL1);
	val &= ~(PSPI_CTL1_SCIDL | PSPI_CTL1_SCM);
	val |= pspi_mode;
	writew(val, priv->base + PSPI_CTL1);

	return 0;
}

static int npcm_pspi_probe(struct udevice *bus)
{
	struct npcm_pspi_priv *priv = dev_get_priv(bus);
	int node = dev_of_offset(bus);
	int ret;

	ret = clk_get_by_index(bus, 0, &priv->clk);
	if (ret < 0)
		return ret;

	priv->base = dev_read_addr_ptr(bus);
	priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 0);
	gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0,
				   &priv->cs_gpio, GPIOD_IS_OUT);

	return 0;
}

static const struct dm_spi_ops npcm_pspi_ops = {
	.xfer           = npcm_pspi_xfer,
	.set_speed      = npcm_pspi_set_speed,
	.set_mode       = npcm_pspi_set_mode,
};

static const struct udevice_id npcm_pspi_ids[] = {
	{ .compatible = "nuvoton,npcm845-pspi"},
	{ .compatible = "nuvoton,npcm750-pspi"},
	{ }
};

U_BOOT_DRIVER(npcm_pspi) = {
	.name   = "npcm_pspi",
	.id     = UCLASS_SPI,
	.of_match = npcm_pspi_ids,
	.ops    = &npcm_pspi_ops,
	.priv_auto = sizeof(struct npcm_pspi_priv),
	.probe  = npcm_pspi_probe,
};