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