blob: 37bab709672dce3ed2bbf937b72af8801e8ba98e [file] [log] [blame]
Jim Liu68d29122022-05-31 18:14:02 +08001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (c) 2021 Nuvoton Technology.
4 */
5
6#include <common.h>
7#include <dm.h>
8#include <spi.h>
9#include <clk.h>
10#include <asm/gpio.h>
11#include <linux/iopoll.h>
12
13#define MAX_DIV 127
14
15/* Register offsets */
16#define PSPI_DATA 0
17#define PSPI_CTL1 2
18#define PSPI_STAT 4
19
20/* PSPI_CTL1 fields */
21#define PSPI_CTL1_SPIEN BIT(0)
22#define PSPI_CTL1_SCM BIT(7)
23#define PSPI_CTL1_SCIDL BIT(8)
24#define PSPI_CTL1_SCDV_MASK GENMASK(15, 9)
25#define PSPI_CTL1_SCDV_SHIFT 9
26
27/* PSPI_STAT fields */
28#define PSPI_STAT_BSY BIT(0)
29#define PSPI_STAT_RBF BIT(1)
30
31struct npcm_pspi_priv {
32 void __iomem *base;
33 struct clk clk;
34 struct gpio_desc cs_gpio;
35 u32 max_hz;
36};
37
38static inline void spi_cs_activate(struct udevice *dev)
39{
40 struct udevice *bus = dev->parent;
41 struct npcm_pspi_priv *priv = dev_get_priv(bus);
42
Jim Liud7cb4782023-07-04 16:01:56 +080043 dm_gpio_set_value(&priv->cs_gpio, 1);
Jim Liu68d29122022-05-31 18:14:02 +080044}
45
46static inline void spi_cs_deactivate(struct udevice *dev)
47{
48 struct udevice *bus = dev->parent;
49 struct npcm_pspi_priv *priv = dev_get_priv(bus);
50
Jim Liud7cb4782023-07-04 16:01:56 +080051 dm_gpio_set_value(&priv->cs_gpio, 0);
Jim Liu68d29122022-05-31 18:14:02 +080052}
53
54static inline void npcm_pspi_enable(struct npcm_pspi_priv *priv)
55{
56 u16 val;
57
58 val = readw(priv->base + PSPI_CTL1);
59 val |= PSPI_CTL1_SPIEN;
60 writew(val, priv->base + PSPI_CTL1);
61}
62
63static inline void npcm_pspi_disable(struct npcm_pspi_priv *priv)
64{
65 u16 val;
66
67 val = readw(priv->base + PSPI_CTL1);
68 val &= ~PSPI_CTL1_SPIEN;
69 writew(val, priv->base + PSPI_CTL1);
70}
71
72static int npcm_pspi_xfer(struct udevice *dev, unsigned int bitlen,
73 const void *dout, void *din, unsigned long flags)
74{
75 struct udevice *bus = dev->parent;
76 struct npcm_pspi_priv *priv = dev_get_priv(bus);
77 void __iomem *base = priv->base;
78 const u8 *tx = dout;
79 u8 *rx = din;
80 u32 bytes = bitlen / 8;
81 u8 tmp;
82 u32 val;
83 int i, ret = 0;
84
85 npcm_pspi_enable(priv);
86
87 if (flags & SPI_XFER_BEGIN)
88 spi_cs_activate(dev);
89
90 for (i = 0; i < bytes; i++) {
91 /* Making sure we can write */
92 ret = readb_poll_timeout(base + PSPI_STAT, val,
93 !(val & PSPI_STAT_BSY),
94 1000000);
95 if (ret < 0)
96 break;
97
98 if (tx)
99 writeb(*tx++, base + PSPI_DATA);
100 else
101 writeb(0, base + PSPI_DATA);
102
103 /* Wait till write completed */
104 ret = readb_poll_timeout(base + PSPI_STAT, val,
105 !(val & PSPI_STAT_BSY),
106 1000000);
107 if (ret < 0)
108 break;
109
110 /* Wait till read buffer full */
111 ret = readb_poll_timeout(base + PSPI_STAT, val,
112 (val & PSPI_STAT_RBF),
113 1000000);
114 if (ret < 0)
115 break;
116
117 tmp = readb(base + PSPI_DATA);
118 if (rx)
119 *rx++ = tmp;
120 }
121
122 if (flags & SPI_XFER_END)
123 spi_cs_deactivate(dev);
124
Jim Liud7cb4782023-07-04 16:01:56 +0800125 debug("npcm_pspi_xfer: slave %s:%s dout %08X din %08X bitlen %u\n",
126 dev->parent->name, dev->name, *(uint *)tx, *(uint *)rx, bitlen);
127
Jim Liu68d29122022-05-31 18:14:02 +0800128 npcm_pspi_disable(priv);
129
130 return ret;
131}
132
133static int npcm_pspi_set_speed(struct udevice *bus, uint speed)
134{
135 struct npcm_pspi_priv *priv = dev_get_priv(bus);
136 ulong apb_clock;
137 u32 divisor;
138 u16 val;
139
140 apb_clock = clk_get_rate(&priv->clk);
141 if (!apb_clock)
142 return -EINVAL;
143
144 if (speed > priv->max_hz)
145 speed = priv->max_hz;
146
147 divisor = DIV_ROUND_CLOSEST(apb_clock, (2 * speed) - 1);
148 if (divisor > MAX_DIV)
149 divisor = MAX_DIV;
150
151 val = readw(priv->base + PSPI_CTL1);
152 val &= ~PSPI_CTL1_SCDV_MASK;
153 val |= divisor << PSPI_CTL1_SCDV_SHIFT;
154 writew(val, priv->base + PSPI_CTL1);
155
156 debug("%s: apb_clock=%lu speed=%d divisor=%u\n",
157 __func__, apb_clock, speed, divisor);
158
159 return 0;
160}
161
162static int npcm_pspi_set_mode(struct udevice *bus, uint mode)
163{
164 struct npcm_pspi_priv *priv = dev_get_priv(bus);
165 u16 pspi_mode, val;
166
167 switch (mode & (SPI_CPOL | SPI_CPHA)) {
168 case SPI_MODE_0:
169 pspi_mode = 0;
170 break;
171 case SPI_MODE_1:
172 pspi_mode = PSPI_CTL1_SCM;
173 break;
174 case SPI_MODE_2:
175 pspi_mode = PSPI_CTL1_SCIDL;
176 break;
177 case SPI_MODE_3:
178 pspi_mode = PSPI_CTL1_SCIDL | PSPI_CTL1_SCM;
179 break;
180 default:
181 break;
182 }
183
184 val = readw(priv->base + PSPI_CTL1);
185 val &= ~(PSPI_CTL1_SCIDL | PSPI_CTL1_SCM);
186 val |= pspi_mode;
187 writew(val, priv->base + PSPI_CTL1);
188
Jim Liud7cb4782023-07-04 16:01:56 +0800189 debug("%s: mode=%u\n", __func__, mode);
Jim Liu68d29122022-05-31 18:14:02 +0800190 return 0;
191}
192
193static int npcm_pspi_probe(struct udevice *bus)
194{
195 struct npcm_pspi_priv *priv = dev_get_priv(bus);
196 int node = dev_of_offset(bus);
197 int ret;
198
199 ret = clk_get_by_index(bus, 0, &priv->clk);
200 if (ret < 0)
201 return ret;
202
203 priv->base = dev_read_addr_ptr(bus);
Jim Liud7cb4782023-07-04 16:01:56 +0800204 priv->max_hz = dev_read_u32_default(bus, "spi-max-frequency", 1000000);
Jim Liu68d29122022-05-31 18:14:02 +0800205 gpio_request_by_name_nodev(offset_to_ofnode(node), "cs-gpios", 0,
Jim Liud7cb4782023-07-04 16:01:56 +0800206 &priv->cs_gpio, GPIOD_IS_OUT| GPIOD_ACTIVE_LOW);
Jim Liu68d29122022-05-31 18:14:02 +0800207
208 return 0;
209}
210
211static const struct dm_spi_ops npcm_pspi_ops = {
212 .xfer = npcm_pspi_xfer,
213 .set_speed = npcm_pspi_set_speed,
214 .set_mode = npcm_pspi_set_mode,
215};
216
217static const struct udevice_id npcm_pspi_ids[] = {
218 { .compatible = "nuvoton,npcm845-pspi"},
219 { .compatible = "nuvoton,npcm750-pspi"},
220 { }
221};
222
223U_BOOT_DRIVER(npcm_pspi) = {
224 .name = "npcm_pspi",
225 .id = UCLASS_SPI,
226 .of_match = npcm_pspi_ids,
227 .ops = &npcm_pspi_ops,
228 .priv_auto = sizeof(struct npcm_pspi_priv),
229 .probe = npcm_pspi_probe,
230};