aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi/spi-altera-dfl.c
blob: ca40923258af30741f085368ab16d1586e638c41 (plain) (blame)
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
// SPDX-License-Identifier: GPL-2.0
//
// DFL bus driver for Altera SPI Master
//
// Copyright (C) 2020 Intel Corporation, Inc.
//
// Authors:
//   Matthew Gerlach <matthew.gerlach@linux.intel.com>
//

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/stddef.h>
#include <linux/errno.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/bitfield.h>
#include <linux/io-64-nonatomic-lo-hi.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/spi/altera.h>
#include <linux/dfl.h>

#define FME_FEATURE_ID_MAX10_SPI	0xe
#define FME_FEATURE_REV_MAX10_SPI_N5010	0x1

#define SPI_CORE_PARAMETER      0x8
#define SHIFT_MODE              BIT_ULL(1)
#define SHIFT_MODE_MSB          0
#define SHIFT_MODE_LSB          1
#define DATA_WIDTH              GENMASK_ULL(7, 2)
#define NUM_CHIPSELECT          GENMASK_ULL(13, 8)
#define CLK_POLARITY            BIT_ULL(14)
#define CLK_PHASE               BIT_ULL(15)
#define PERIPHERAL_ID           GENMASK_ULL(47, 32)
#define SPI_CLK                 GENMASK_ULL(31, 22)
#define SPI_INDIRECT_ACC_OFST   0x10

#define INDIRECT_ADDR           (SPI_INDIRECT_ACC_OFST+0x0)
#define INDIRECT_WR             BIT_ULL(8)
#define INDIRECT_RD             BIT_ULL(9)
#define INDIRECT_RD_DATA        (SPI_INDIRECT_ACC_OFST+0x8)
#define INDIRECT_DATA_MASK      GENMASK_ULL(31, 0)
#define INDIRECT_DEBUG          BIT_ULL(32)
#define INDIRECT_WR_DATA        (SPI_INDIRECT_ACC_OFST+0x10)
#define INDIRECT_TIMEOUT        10000

static int indirect_bus_reg_read(void *context, unsigned int reg,
				 unsigned int *val)
{
	void __iomem *base = context;
	int loops;
	u64 v;

	writeq((reg >> 2) | INDIRECT_RD, base + INDIRECT_ADDR);

	loops = 0;
	while ((readq(base + INDIRECT_ADDR) & INDIRECT_RD) &&
	       (loops++ < INDIRECT_TIMEOUT))
		cpu_relax();

	if (loops >= INDIRECT_TIMEOUT) {
		pr_err("%s timed out %d\n", __func__, loops);
		return -ETIME;
	}

	v = readq(base + INDIRECT_RD_DATA);

	*val = v & INDIRECT_DATA_MASK;

	return 0;
}

static int indirect_bus_reg_write(void *context, unsigned int reg,
				  unsigned int val)
{
	void __iomem *base = context;
	int loops;

	writeq(val, base + INDIRECT_WR_DATA);
	writeq((reg >> 2) | INDIRECT_WR, base + INDIRECT_ADDR);

	loops = 0;
	while ((readq(base + INDIRECT_ADDR) & INDIRECT_WR) &&
	       (loops++ < INDIRECT_TIMEOUT))
		cpu_relax();

	if (loops >= INDIRECT_TIMEOUT) {
		pr_err("%s timed out %d\n", __func__, loops);
		return -ETIME;
	}
	return 0;
}

static const struct regmap_config indirect_regbus_cfg = {
	.reg_bits = 32,
	.reg_stride = 4,
	.val_bits = 32,
	.fast_io = true,
	.max_register = 24,

	.reg_write = indirect_bus_reg_write,
	.reg_read = indirect_bus_reg_read,
};

static void config_spi_master(void __iomem *base, struct spi_master *master)
{
	u64 v;

	v = readq(base + SPI_CORE_PARAMETER);

	master->mode_bits = SPI_CS_HIGH;
	if (FIELD_GET(CLK_POLARITY, v))
		master->mode_bits |= SPI_CPOL;
	if (FIELD_GET(CLK_PHASE, v))
		master->mode_bits |= SPI_CPHA;

	master->num_chipselect = FIELD_GET(NUM_CHIPSELECT, v);
	master->bits_per_word_mask =
		SPI_BPW_RANGE_MASK(1, FIELD_GET(DATA_WIDTH, v));
}

static int dfl_spi_altera_probe(struct dfl_device *dfl_dev)
{
	struct spi_board_info board_info = { 0 };
	struct device *dev = &dfl_dev->dev;
	struct spi_master *master;
	struct altera_spi *hw;
	void __iomem *base;
	int err = -ENODEV;

	master = spi_alloc_master(dev, sizeof(struct altera_spi));
	if (!master)
		return -ENOMEM;

	master->bus_num = -1;

	hw = spi_master_get_devdata(master);

	hw->dev = dev;

	base = devm_ioremap_resource(dev, &dfl_dev->mmio_res);

	if (IS_ERR(base))
		return PTR_ERR(base);

	config_spi_master(base, master);
	dev_dbg(dev, "%s cs %u bpm 0x%x mode 0x%x\n", __func__,
		master->num_chipselect, master->bits_per_word_mask,
		master->mode_bits);

	hw->regmap = devm_regmap_init(dev, NULL, base, &indirect_regbus_cfg);
	if (IS_ERR(hw->regmap))
		return PTR_ERR(hw->regmap);

	hw->irq = -EINVAL;

	altera_spi_init_master(master);

	err = devm_spi_register_master(dev, master);
	if (err) {
		dev_err(dev, "%s failed to register spi master %d\n", __func__, err);
		goto exit;
	}

	if (dfl_dev->revision == FME_FEATURE_REV_MAX10_SPI_N5010)
		strscpy(board_info.modalias, "m10-n5010", SPI_NAME_SIZE);
	else
		strscpy(board_info.modalias, "m10-d5005", SPI_NAME_SIZE);

	board_info.max_speed_hz = 12500000;
	board_info.bus_num = 0;
	board_info.chip_select = 0;

	if (!spi_new_device(master, &board_info)) {
		dev_err(dev, "%s failed to create SPI device: %s\n",
			__func__, board_info.modalias);
	}

	return 0;
exit:
	spi_master_put(master);
	return err;
}

static const struct dfl_device_id dfl_spi_altera_ids[] = {
	{ FME_ID, FME_FEATURE_ID_MAX10_SPI },
	{ }
};

static struct dfl_driver dfl_spi_altera_driver = {
	.drv	= {
		.name       = "dfl-spi-altera",
	},
	.id_table = dfl_spi_altera_ids,
	.probe   = dfl_spi_altera_probe,
};

module_dfl_driver(dfl_spi_altera_driver);

MODULE_DEVICE_TABLE(dfl, dfl_spi_altera_ids);
MODULE_DESCRIPTION("DFL spi altera driver");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");