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