162306a36Sopenharmony_ci// SPDX-License-Identifier: (GPL-2.0 OR MIT)
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SPI core driver for the Ocelot chip family.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This driver will handle everything necessary to allow for communication over
662306a36Sopenharmony_ci * SPI to the VSC7511, VSC7512, VSC7513 and VSC7514 chips. The main functions
762306a36Sopenharmony_ci * are to prepare the chip's SPI interface for a specific bus speed, and a host
862306a36Sopenharmony_ci * processor's endianness. This will create and distribute regmaps for any
962306a36Sopenharmony_ci * children.
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci * Copyright 2021-2022 Innovative Advantage Inc.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * Author: Colin Foster <colin.foster@in-advantage.com>
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#include <linux/device.h>
1762306a36Sopenharmony_ci#include <linux/err.h>
1862306a36Sopenharmony_ci#include <linux/errno.h>
1962306a36Sopenharmony_ci#include <linux/export.h>
2062306a36Sopenharmony_ci#include <linux/ioport.h>
2162306a36Sopenharmony_ci#include <linux/mod_devicetable.h>
2262306a36Sopenharmony_ci#include <linux/module.h>
2362306a36Sopenharmony_ci#include <linux/regmap.h>
2462306a36Sopenharmony_ci#include <linux/spi/spi.h>
2562306a36Sopenharmony_ci#include <linux/types.h>
2662306a36Sopenharmony_ci#include <linux/units.h>
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci#include "ocelot.h"
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define REG_DEV_CPUORG_IF_CTRL		0x0000
3162306a36Sopenharmony_ci#define REG_DEV_CPUORG_IF_CFGSTAT	0x0004
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci#define CFGSTAT_IF_NUM_VCORE		(0 << 24)
3462306a36Sopenharmony_ci#define CFGSTAT_IF_NUM_VRAP		(1 << 24)
3562306a36Sopenharmony_ci#define CFGSTAT_IF_NUM_SI		(2 << 24)
3662306a36Sopenharmony_ci#define CFGSTAT_IF_NUM_MIIM		(3 << 24)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define VSC7512_DEVCPU_ORG_RES_START	0x71000000
3962306a36Sopenharmony_ci#define VSC7512_DEVCPU_ORG_RES_SIZE	0x38
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci#define VSC7512_CHIP_REGS_RES_START	0x71070000
4262306a36Sopenharmony_ci#define VSC7512_CHIP_REGS_RES_SIZE	0x14
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic const struct resource vsc7512_dev_cpuorg_resource =
4562306a36Sopenharmony_ci	DEFINE_RES_REG_NAMED(VSC7512_DEVCPU_ORG_RES_START,
4662306a36Sopenharmony_ci			     VSC7512_DEVCPU_ORG_RES_SIZE,
4762306a36Sopenharmony_ci			     "devcpu_org");
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_cistatic const struct resource vsc7512_gcb_resource =
5062306a36Sopenharmony_ci	DEFINE_RES_REG_NAMED(VSC7512_CHIP_REGS_RES_START,
5162306a36Sopenharmony_ci			     VSC7512_CHIP_REGS_RES_SIZE,
5262306a36Sopenharmony_ci			     "devcpu_gcb_chip_regs");
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int ocelot_spi_initialize(struct device *dev)
5562306a36Sopenharmony_ci{
5662306a36Sopenharmony_ci	struct ocelot_ddata *ddata = dev_get_drvdata(dev);
5762306a36Sopenharmony_ci	u32 val, check;
5862306a36Sopenharmony_ci	int err;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	val = OCELOT_SPI_BYTE_ORDER;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/*
6362306a36Sopenharmony_ci	 * The SPI address must be big-endian, but we want the payload to match
6462306a36Sopenharmony_ci	 * our CPU. These are two bits (0 and 1) but they're repeated such that
6562306a36Sopenharmony_ci	 * the write from any configuration will be valid. The four
6662306a36Sopenharmony_ci	 * configurations are:
6762306a36Sopenharmony_ci	 *
6862306a36Sopenharmony_ci	 * 0b00: little-endian, MSB first
6962306a36Sopenharmony_ci	 * |            111111   | 22221111 | 33222222 |
7062306a36Sopenharmony_ci	 * | 76543210 | 54321098 | 32109876 | 10987654 |
7162306a36Sopenharmony_ci	 *
7262306a36Sopenharmony_ci	 * 0b01: big-endian, MSB first
7362306a36Sopenharmony_ci	 * | 33222222 | 22221111 | 111111   |          |
7462306a36Sopenharmony_ci	 * | 10987654 | 32109876 | 54321098 | 76543210 |
7562306a36Sopenharmony_ci	 *
7662306a36Sopenharmony_ci	 * 0b10: little-endian, LSB first
7762306a36Sopenharmony_ci	 * |              111111 | 11112222 | 22222233 |
7862306a36Sopenharmony_ci	 * | 01234567 | 89012345 | 67890123 | 45678901 |
7962306a36Sopenharmony_ci	 *
8062306a36Sopenharmony_ci	 * 0b11: big-endian, LSB first
8162306a36Sopenharmony_ci	 * | 22222233 | 11112222 |   111111 |          |
8262306a36Sopenharmony_ci	 * | 45678901 | 67890123 | 89012345 | 01234567 |
8362306a36Sopenharmony_ci	 */
8462306a36Sopenharmony_ci	err = regmap_write(ddata->cpuorg_regmap, REG_DEV_CPUORG_IF_CTRL, val);
8562306a36Sopenharmony_ci	if (err)
8662306a36Sopenharmony_ci		return err;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	/*
8962306a36Sopenharmony_ci	 * Apply the number of padding bytes between a read request and the data
9062306a36Sopenharmony_ci	 * payload. Some registers have access times of up to 1us, so if the
9162306a36Sopenharmony_ci	 * first payload bit is shifted out too quickly, the read will fail.
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci	val = ddata->spi_padding_bytes;
9462306a36Sopenharmony_ci	err = regmap_write(ddata->cpuorg_regmap, REG_DEV_CPUORG_IF_CFGSTAT, val);
9562306a36Sopenharmony_ci	if (err)
9662306a36Sopenharmony_ci		return err;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	/*
9962306a36Sopenharmony_ci	 * After we write the interface configuration, read it back here. This
10062306a36Sopenharmony_ci	 * will verify several different things. The first is that the number of
10162306a36Sopenharmony_ci	 * padding bytes actually got written correctly. These are found in bits
10262306a36Sopenharmony_ci	 * 0:3.
10362306a36Sopenharmony_ci	 *
10462306a36Sopenharmony_ci	 * The second is that bit 16 is cleared. Bit 16 is IF_CFGSTAT:IF_STAT,
10562306a36Sopenharmony_ci	 * and will be set if the register access is too fast. This would be in
10662306a36Sopenharmony_ci	 * the condition that the number of padding bytes is insufficient for
10762306a36Sopenharmony_ci	 * the SPI bus frequency.
10862306a36Sopenharmony_ci	 *
10962306a36Sopenharmony_ci	 * The last check is for bits 31:24, which define the interface by which
11062306a36Sopenharmony_ci	 * the registers are being accessed. Since we're accessing them via the
11162306a36Sopenharmony_ci	 * serial interface, it must return IF_NUM_SI.
11262306a36Sopenharmony_ci	 */
11362306a36Sopenharmony_ci	check = val | CFGSTAT_IF_NUM_SI;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	err = regmap_read(ddata->cpuorg_regmap, REG_DEV_CPUORG_IF_CFGSTAT, &val);
11662306a36Sopenharmony_ci	if (err)
11762306a36Sopenharmony_ci		return err;
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	if (check != val)
12062306a36Sopenharmony_ci		return -ENODEV;
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	return 0;
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_cistatic const struct regmap_config ocelot_spi_regmap_config = {
12662306a36Sopenharmony_ci	.reg_bits = 24,
12762306a36Sopenharmony_ci	.reg_stride = 4,
12862306a36Sopenharmony_ci	.reg_shift = REGMAP_DOWNSHIFT(2),
12962306a36Sopenharmony_ci	.val_bits = 32,
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	.write_flag_mask = 0x80,
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	.use_single_read = true,
13462306a36Sopenharmony_ci	.use_single_write = true,
13562306a36Sopenharmony_ci	.can_multi_write = false,
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ci	.reg_format_endian = REGMAP_ENDIAN_BIG,
13862306a36Sopenharmony_ci	.val_format_endian = REGMAP_ENDIAN_NATIVE,
13962306a36Sopenharmony_ci};
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic int ocelot_spi_regmap_bus_read(void *context, const void *reg, size_t reg_size,
14262306a36Sopenharmony_ci				      void *val, size_t val_size)
14362306a36Sopenharmony_ci{
14462306a36Sopenharmony_ci	struct spi_transfer xfers[3] = {0};
14562306a36Sopenharmony_ci	struct device *dev = context;
14662306a36Sopenharmony_ci	struct ocelot_ddata *ddata;
14762306a36Sopenharmony_ci	struct spi_device *spi;
14862306a36Sopenharmony_ci	struct spi_message msg;
14962306a36Sopenharmony_ci	unsigned int index = 0;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	ddata = dev_get_drvdata(dev);
15262306a36Sopenharmony_ci	spi = to_spi_device(dev);
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	xfers[index].tx_buf = reg;
15562306a36Sopenharmony_ci	xfers[index].len = reg_size;
15662306a36Sopenharmony_ci	index++;
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	if (ddata->spi_padding_bytes) {
15962306a36Sopenharmony_ci		xfers[index].len = ddata->spi_padding_bytes;
16062306a36Sopenharmony_ci		xfers[index].tx_buf = ddata->dummy_buf;
16162306a36Sopenharmony_ci		xfers[index].dummy_data = 1;
16262306a36Sopenharmony_ci		index++;
16362306a36Sopenharmony_ci	}
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	xfers[index].rx_buf = val;
16662306a36Sopenharmony_ci	xfers[index].len = val_size;
16762306a36Sopenharmony_ci	index++;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	spi_message_init_with_transfers(&msg, xfers, index);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	return spi_sync(spi, &msg);
17262306a36Sopenharmony_ci}
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_cistatic int ocelot_spi_regmap_bus_write(void *context, const void *data, size_t count)
17562306a36Sopenharmony_ci{
17662306a36Sopenharmony_ci	struct device *dev = context;
17762306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	return spi_write(spi, data, count);
18062306a36Sopenharmony_ci}
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_cistatic const struct regmap_bus ocelot_spi_regmap_bus = {
18362306a36Sopenharmony_ci	.write = ocelot_spi_regmap_bus_write,
18462306a36Sopenharmony_ci	.read = ocelot_spi_regmap_bus_read,
18562306a36Sopenharmony_ci};
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistruct regmap *ocelot_spi_init_regmap(struct device *dev, const struct resource *res)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct regmap_config regmap_config;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	memcpy(&regmap_config, &ocelot_spi_regmap_config, sizeof(regmap_config));
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	regmap_config.name = res->name;
19462306a36Sopenharmony_ci	regmap_config.max_register = resource_size(res) - 1;
19562306a36Sopenharmony_ci	regmap_config.reg_base = res->start;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	return devm_regmap_init(dev, &ocelot_spi_regmap_bus, dev, &regmap_config);
19862306a36Sopenharmony_ci}
19962306a36Sopenharmony_ciEXPORT_SYMBOL_NS(ocelot_spi_init_regmap, MFD_OCELOT_SPI);
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int ocelot_spi_probe(struct spi_device *spi)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	struct device *dev = &spi->dev;
20462306a36Sopenharmony_ci	struct ocelot_ddata *ddata;
20562306a36Sopenharmony_ci	struct regmap *r;
20662306a36Sopenharmony_ci	int err;
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
20962306a36Sopenharmony_ci	if (!ddata)
21062306a36Sopenharmony_ci		return -ENOMEM;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	spi_set_drvdata(spi, ddata);
21362306a36Sopenharmony_ci
21462306a36Sopenharmony_ci	if (spi->max_speed_hz <= 500000) {
21562306a36Sopenharmony_ci		ddata->spi_padding_bytes = 0;
21662306a36Sopenharmony_ci	} else {
21762306a36Sopenharmony_ci		/*
21862306a36Sopenharmony_ci		 * Calculation taken from the manual for IF_CFGSTAT:IF_CFG.
21962306a36Sopenharmony_ci		 * Register access time is 1us, so we need to configure and send
22062306a36Sopenharmony_ci		 * out enough padding bytes between the read request and data
22162306a36Sopenharmony_ci		 * transmission that lasts at least 1 microsecond.
22262306a36Sopenharmony_ci		 */
22362306a36Sopenharmony_ci		ddata->spi_padding_bytes = 1 + (spi->max_speed_hz / HZ_PER_MHZ + 2) / 8;
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci		ddata->dummy_buf = devm_kzalloc(dev, ddata->spi_padding_bytes, GFP_KERNEL);
22662306a36Sopenharmony_ci		if (!ddata->dummy_buf)
22762306a36Sopenharmony_ci			return -ENOMEM;
22862306a36Sopenharmony_ci	}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	spi->bits_per_word = 8;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	err = spi_setup(spi);
23362306a36Sopenharmony_ci	if (err)
23462306a36Sopenharmony_ci		return dev_err_probe(&spi->dev, err, "Error performing SPI setup\n");
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	r = ocelot_spi_init_regmap(dev, &vsc7512_dev_cpuorg_resource);
23762306a36Sopenharmony_ci	if (IS_ERR(r))
23862306a36Sopenharmony_ci		return PTR_ERR(r);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	ddata->cpuorg_regmap = r;
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	r = ocelot_spi_init_regmap(dev, &vsc7512_gcb_resource);
24362306a36Sopenharmony_ci	if (IS_ERR(r))
24462306a36Sopenharmony_ci		return PTR_ERR(r);
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	ddata->gcb_regmap = r;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/*
24962306a36Sopenharmony_ci	 * The chip must be set up for SPI before it gets initialized and reset.
25062306a36Sopenharmony_ci	 * This must be done before calling init, and after a chip reset is
25162306a36Sopenharmony_ci	 * performed.
25262306a36Sopenharmony_ci	 */
25362306a36Sopenharmony_ci	err = ocelot_spi_initialize(dev);
25462306a36Sopenharmony_ci	if (err)
25562306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Error initializing SPI bus\n");
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	err = ocelot_chip_reset(dev);
25862306a36Sopenharmony_ci	if (err)
25962306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Error resetting device\n");
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	/*
26262306a36Sopenharmony_ci	 * A chip reset will clear the SPI configuration, so it needs to be done
26362306a36Sopenharmony_ci	 * again before we can access any registers.
26462306a36Sopenharmony_ci	 */
26562306a36Sopenharmony_ci	err = ocelot_spi_initialize(dev);
26662306a36Sopenharmony_ci	if (err)
26762306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Error initializing SPI bus after reset\n");
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ci	err = ocelot_core_init(dev);
27062306a36Sopenharmony_ci	if (err)
27162306a36Sopenharmony_ci		return dev_err_probe(dev, err, "Error initializing Ocelot core\n");
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci	return 0;
27462306a36Sopenharmony_ci}
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic const struct spi_device_id ocelot_spi_ids[] = {
27762306a36Sopenharmony_ci	{ "vsc7512", 0 },
27862306a36Sopenharmony_ci	{ }
27962306a36Sopenharmony_ci};
28062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, ocelot_spi_ids);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_cistatic const struct of_device_id ocelot_spi_of_match[] = {
28362306a36Sopenharmony_ci	{ .compatible = "mscc,vsc7512" },
28462306a36Sopenharmony_ci	{ }
28562306a36Sopenharmony_ci};
28662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, ocelot_spi_of_match);
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_cistatic struct spi_driver ocelot_spi_driver = {
28962306a36Sopenharmony_ci	.driver = {
29062306a36Sopenharmony_ci		.name = "ocelot-soc",
29162306a36Sopenharmony_ci		.of_match_table = ocelot_spi_of_match,
29262306a36Sopenharmony_ci	},
29362306a36Sopenharmony_ci	.id_table = ocelot_spi_ids,
29462306a36Sopenharmony_ci	.probe = ocelot_spi_probe,
29562306a36Sopenharmony_ci};
29662306a36Sopenharmony_cimodule_spi_driver(ocelot_spi_driver);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ciMODULE_DESCRIPTION("SPI Controlled Ocelot Chip Driver");
29962306a36Sopenharmony_ciMODULE_AUTHOR("Colin Foster <colin.foster@in-advantage.com>");
30062306a36Sopenharmony_ciMODULE_LICENSE("Dual MIT/GPL");
30162306a36Sopenharmony_ciMODULE_IMPORT_NS(MFD_OCELOT);
302