162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Rockchip RK806 Core (SPI) driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2021 Rockchip Electronics Co., Ltd.
662306a36Sopenharmony_ci * Copyright (c) 2023 Collabora Ltd.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Author: Xu Shengfei <xsf@rock-chips.com>
962306a36Sopenharmony_ci * Author: Sebastian Reichel <sebastian.reichel@collabora.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/mfd/core.h>
1462306a36Sopenharmony_ci#include <linux/mfd/rk808.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/regmap.h>
1762306a36Sopenharmony_ci#include <linux/spi/spi.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#define RK806_ADDR_SIZE 2
2062306a36Sopenharmony_ci#define RK806_CMD_WITH_SIZE(CMD, VALUE_BYTES) \
2162306a36Sopenharmony_ci	(RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (VALUE_BYTES - 1))
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistatic const struct regmap_range rk806_volatile_ranges[] = {
2462306a36Sopenharmony_ci	regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5),
2562306a36Sopenharmony_ci	regmap_reg_range(RK806_DVS_START_CTRL, RK806_INT_MSK1),
2662306a36Sopenharmony_ci};
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic const struct regmap_access_table rk806_volatile_table = {
2962306a36Sopenharmony_ci	.yes_ranges = rk806_volatile_ranges,
3062306a36Sopenharmony_ci	.n_yes_ranges = ARRAY_SIZE(rk806_volatile_ranges),
3162306a36Sopenharmony_ci};
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic const struct regmap_config rk806_regmap_config_spi = {
3462306a36Sopenharmony_ci	.reg_bits = 16,
3562306a36Sopenharmony_ci	.val_bits = 8,
3662306a36Sopenharmony_ci	.max_register = RK806_BUCK_RSERVE_REG5,
3762306a36Sopenharmony_ci	.cache_type = REGCACHE_RBTREE,
3862306a36Sopenharmony_ci	.volatile_table = &rk806_volatile_table,
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int rk806_spi_bus_write(void *context, const void *vdata, size_t count)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct device *dev = context;
4462306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
4562306a36Sopenharmony_ci	struct spi_transfer xfer[2] = { 0 };
4662306a36Sopenharmony_ci	/* data and thus count includes the register address */
4762306a36Sopenharmony_ci	size_t val_size = count - RK806_ADDR_SIZE;
4862306a36Sopenharmony_ci	char cmd;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1))
5162306a36Sopenharmony_ci		return -EINVAL;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	cmd = RK806_CMD_WITH_SIZE(WRITE, val_size);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	xfer[0].tx_buf = &cmd;
5662306a36Sopenharmony_ci	xfer[0].len = sizeof(cmd);
5762306a36Sopenharmony_ci	xfer[1].tx_buf = vdata;
5862306a36Sopenharmony_ci	xfer[1].len = count;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer));
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int rk806_spi_bus_read(void *context, const void *vreg, size_t reg_size,
6462306a36Sopenharmony_ci			      void *val, size_t val_size)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct device *dev = context;
6762306a36Sopenharmony_ci	struct spi_device *spi = to_spi_device(dev);
6862306a36Sopenharmony_ci	char txbuf[3] = { 0 };
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	if (reg_size != RK806_ADDR_SIZE ||
7162306a36Sopenharmony_ci	    val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1))
7262306a36Sopenharmony_ci		return -EINVAL;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	/* TX buffer contains command byte followed by two address bytes */
7562306a36Sopenharmony_ci	txbuf[0] = RK806_CMD_WITH_SIZE(READ, val_size);
7662306a36Sopenharmony_ci	memcpy(txbuf+1, vreg, reg_size);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return spi_write_then_read(spi, txbuf, sizeof(txbuf), val, val_size);
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic const struct regmap_bus rk806_regmap_bus_spi = {
8262306a36Sopenharmony_ci	.write = rk806_spi_bus_write,
8362306a36Sopenharmony_ci	.read = rk806_spi_bus_read,
8462306a36Sopenharmony_ci	.reg_format_endian_default = REGMAP_ENDIAN_LITTLE,
8562306a36Sopenharmony_ci};
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_cistatic int rk8xx_spi_probe(struct spi_device *spi)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct regmap *regmap;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	regmap = devm_regmap_init(&spi->dev, &rk806_regmap_bus_spi,
9262306a36Sopenharmony_ci				  &spi->dev, &rk806_regmap_config_spi);
9362306a36Sopenharmony_ci	if (IS_ERR(regmap))
9462306a36Sopenharmony_ci		return dev_err_probe(&spi->dev, PTR_ERR(regmap),
9562306a36Sopenharmony_ci				     "Failed to init regmap\n");
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	return rk8xx_probe(&spi->dev, RK806_ID, spi->irq, regmap);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic const struct of_device_id rk8xx_spi_of_match[] = {
10162306a36Sopenharmony_ci	{ .compatible = "rockchip,rk806", },
10262306a36Sopenharmony_ci	{ }
10362306a36Sopenharmony_ci};
10462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rk8xx_spi_of_match);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_cistatic const struct spi_device_id rk8xx_spi_id_table[] = {
10762306a36Sopenharmony_ci	{ "rk806", 0 },
10862306a36Sopenharmony_ci	{ }
10962306a36Sopenharmony_ci};
11062306a36Sopenharmony_ciMODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table);
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_cistatic struct spi_driver rk8xx_spi_driver = {
11362306a36Sopenharmony_ci	.driver		= {
11462306a36Sopenharmony_ci		.name	= "rk8xx-spi",
11562306a36Sopenharmony_ci		.of_match_table = rk8xx_spi_of_match,
11662306a36Sopenharmony_ci	},
11762306a36Sopenharmony_ci	.probe		= rk8xx_spi_probe,
11862306a36Sopenharmony_ci	.id_table	= rk8xx_spi_id_table,
11962306a36Sopenharmony_ci};
12062306a36Sopenharmony_cimodule_spi_driver(rk8xx_spi_driver);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ciMODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
12362306a36Sopenharmony_ciMODULE_DESCRIPTION("RK8xx SPI PMIC driver");
12462306a36Sopenharmony_ciMODULE_LICENSE("GPL");
125