Skip to content

Commit ba2fc16

Browse files
matthew-gerlachbroonie
authored andcommitted
spi: altera: Add DFL bus driver for Altera API Controller
This patch adds a Device Feature List (DFL) bus driver for the Altera SPI Master controller. The SPI master is connected to an Intel SPI Slave to Avalon Bridge inside an Intel MAX10 BMC Chip. Signed-off-by: Matthew Gerlach <matthew.gerlach@linux.intel.com> Link: https://lore.kernel.org/r/20210416165720.554144-3-matthew.gerlach@linux.intel.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent b0c3d93 commit ba2fc16

3 files changed

Lines changed: 214 additions & 0 deletions

File tree

drivers/spi/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ config SPI_ALTERA_CORE
7070
help
7171
"The core code for the Altera SPI Controller"
7272

73+
config SPI_ALTERA_DFL
74+
tristate "DFL bus driver for Altera SPI Controller"
75+
depends on FPGA_DFL
76+
select SPI_ALTERA_CORE
77+
help
78+
This is a Device Feature List (DFL) bus driver for the
79+
Altera SPI master controller. The SPI master is connected
80+
to a SPI slave to Avalon bridge in a Intel MAX BMC.
81+
7382
config SPI_AR934X
7483
tristate "Qualcomm Atheros AR934X/QCA95XX SPI controller driver"
7584
depends on ATH79 || COMPILE_TEST

drivers/spi/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
1616
# SPI master controller drivers (bus)
1717
obj-$(CONFIG_SPI_ALTERA) += spi-altera-platform.o
1818
obj-$(CONFIG_SPI_ALTERA_CORE) += spi-altera-core.o
19+
obj-$(CONFIG_SPI_ALTERA_DFL) += spi-altera-dfl.o
1920
obj-$(CONFIG_SPI_AR934X) += spi-ar934x.o
2021
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
2122
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o

drivers/spi/spi-altera-dfl.c

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
//
3+
// DFL bus driver for Altera SPI Master
4+
//
5+
// Copyright (C) 2020 Intel Corporation, Inc.
6+
//
7+
// Authors:
8+
// Matthew Gerlach <matthew.gerlach@linux.intel.com>
9+
//
10+
11+
#include <linux/types.h>
12+
#include <linux/kernel.h>
13+
#include <linux/module.h>
14+
#include <linux/stddef.h>
15+
#include <linux/errno.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/io.h>
18+
#include <linux/bitfield.h>
19+
#include <linux/io-64-nonatomic-lo-hi.h>
20+
#include <linux/regmap.h>
21+
#include <linux/spi/spi.h>
22+
#include <linux/spi/altera.h>
23+
#include <linux/dfl.h>
24+
25+
#define FME_FEATURE_ID_MAX10_SPI 0xe
26+
#define FME_FEATURE_REV_MAX10_SPI_N5010 0x1
27+
28+
#define SPI_CORE_PARAMETER 0x8
29+
#define SHIFT_MODE BIT_ULL(1)
30+
#define SHIFT_MODE_MSB 0
31+
#define SHIFT_MODE_LSB 1
32+
#define DATA_WIDTH GENMASK_ULL(7, 2)
33+
#define NUM_CHIPSELECT GENMASK_ULL(13, 8)
34+
#define CLK_POLARITY BIT_ULL(14)
35+
#define CLK_PHASE BIT_ULL(15)
36+
#define PERIPHERAL_ID GENMASK_ULL(47, 32)
37+
#define SPI_CLK GENMASK_ULL(31, 22)
38+
#define SPI_INDIRECT_ACC_OFST 0x10
39+
40+
#define INDIRECT_ADDR (SPI_INDIRECT_ACC_OFST+0x0)
41+
#define INDIRECT_WR BIT_ULL(8)
42+
#define INDIRECT_RD BIT_ULL(9)
43+
#define INDIRECT_RD_DATA (SPI_INDIRECT_ACC_OFST+0x8)
44+
#define INDIRECT_DATA_MASK GENMASK_ULL(31, 0)
45+
#define INDIRECT_DEBUG BIT_ULL(32)
46+
#define INDIRECT_WR_DATA (SPI_INDIRECT_ACC_OFST+0x10)
47+
#define INDIRECT_TIMEOUT 10000
48+
49+
static int indirect_bus_reg_read(void *context, unsigned int reg,
50+
unsigned int *val)
51+
{
52+
void __iomem *base = context;
53+
int loops;
54+
u64 v;
55+
56+
writeq((reg >> 2) | INDIRECT_RD, base + INDIRECT_ADDR);
57+
58+
loops = 0;
59+
while ((readq(base + INDIRECT_ADDR) & INDIRECT_RD) &&
60+
(loops++ < INDIRECT_TIMEOUT))
61+
cpu_relax();
62+
63+
if (loops >= INDIRECT_TIMEOUT) {
64+
pr_err("%s timed out %d\n", __func__, loops);
65+
return -ETIME;
66+
}
67+
68+
v = readq(base + INDIRECT_RD_DATA);
69+
70+
*val = v & INDIRECT_DATA_MASK;
71+
72+
return 0;
73+
}
74+
75+
static int indirect_bus_reg_write(void *context, unsigned int reg,
76+
unsigned int val)
77+
{
78+
void __iomem *base = context;
79+
int loops;
80+
81+
writeq(val, base + INDIRECT_WR_DATA);
82+
writeq((reg >> 2) | INDIRECT_WR, base + INDIRECT_ADDR);
83+
84+
loops = 0;
85+
while ((readq(base + INDIRECT_ADDR) & INDIRECT_WR) &&
86+
(loops++ < INDIRECT_TIMEOUT))
87+
cpu_relax();
88+
89+
if (loops >= INDIRECT_TIMEOUT) {
90+
pr_err("%s timed out %d\n", __func__, loops);
91+
return -ETIME;
92+
}
93+
return 0;
94+
}
95+
96+
static const struct regmap_config indirect_regbus_cfg = {
97+
.reg_bits = 32,
98+
.reg_stride = 4,
99+
.val_bits = 32,
100+
.fast_io = true,
101+
.max_register = 24,
102+
103+
.reg_write = indirect_bus_reg_write,
104+
.reg_read = indirect_bus_reg_read,
105+
};
106+
107+
static struct spi_board_info m10_bmc_info = {
108+
.modalias = "m10-d5005",
109+
.max_speed_hz = 12500000,
110+
.bus_num = 0,
111+
.chip_select = 0,
112+
};
113+
114+
static void config_spi_master(void __iomem *base, struct spi_master *master)
115+
{
116+
u64 v;
117+
118+
v = readq(base + SPI_CORE_PARAMETER);
119+
120+
master->mode_bits = SPI_CS_HIGH;
121+
if (FIELD_GET(CLK_POLARITY, v))
122+
master->mode_bits |= SPI_CPOL;
123+
if (FIELD_GET(CLK_PHASE, v))
124+
master->mode_bits |= SPI_CPHA;
125+
126+
master->num_chipselect = FIELD_GET(NUM_CHIPSELECT, v);
127+
master->bits_per_word_mask =
128+
SPI_BPW_RANGE_MASK(1, FIELD_GET(DATA_WIDTH, v));
129+
}
130+
131+
static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
132+
{
133+
struct device *dev = &dfl_dev->dev;
134+
struct spi_master *master;
135+
struct altera_spi *hw;
136+
void __iomem *base;
137+
int err = -ENODEV;
138+
139+
master = spi_alloc_master(dev, sizeof(struct altera_spi));
140+
if (!master)
141+
return -ENOMEM;
142+
143+
master->bus_num = dfl_dev->id;
144+
145+
hw = spi_master_get_devdata(master);
146+
147+
hw->dev = dev;
148+
149+
base = devm_ioremap_resource(dev, &dfl_dev->mmio_res);
150+
151+
if (IS_ERR(base)) {
152+
dev_err(dev, "%s get mem resource fail!\n", __func__);
153+
return PTR_ERR(base);
154+
}
155+
156+
config_spi_master(base, master);
157+
dev_dbg(dev, "%s cs %u bpm 0x%x mode 0x%x\n", __func__,
158+
master->num_chipselect, master->bits_per_word_mask,
159+
master->mode_bits);
160+
161+
hw->regmap = devm_regmap_init(dev, NULL, base, &indirect_regbus_cfg);
162+
if (IS_ERR(hw->regmap))
163+
return PTR_ERR(hw->regmap);
164+
165+
hw->irq = -EINVAL;
166+
167+
altera_spi_init_master(master);
168+
169+
err = devm_spi_register_master(dev, master);
170+
if (err) {
171+
dev_err(dev, "%s failed to register spi master %d\n", __func__, err);
172+
goto exit;
173+
}
174+
175+
if (!spi_new_device(master, &m10_bmc_info)) {
176+
dev_err(dev, "%s failed to create SPI device: %s\n",
177+
__func__, m10_bmc_info.modalias);
178+
}
179+
180+
return 0;
181+
exit:
182+
spi_master_put(master);
183+
return err;
184+
}
185+
186+
static const struct dfl_device_id dfl_spi_altera_ids[] = {
187+
{ FME_ID, FME_FEATURE_ID_MAX10_SPI },
188+
{ }
189+
};
190+
191+
static struct dfl_driver dfl_spi_altera_driver = {
192+
.drv = {
193+
.name = "dfl-spi-altera",
194+
},
195+
.id_table = dfl_spi_altera_ids,
196+
.probe = dfl_spi_altera_probe,
197+
};
198+
199+
module_dfl_driver(dfl_spi_altera_driver);
200+
201+
MODULE_DEVICE_TABLE(dfl, dfl_spi_altera_ids);
202+
MODULE_DESCRIPTION("DFL spi altera driver");
203+
MODULE_AUTHOR("Intel Corporation");
204+
MODULE_LICENSE("GPL v2");

0 commit comments

Comments
 (0)