1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Rockchip RK806 Core (SPI) driver 4 * 5 * Copyright (c) 2021 Rockchip Electronics Co., Ltd. 6 * Copyright (c) 2023 Collabora Ltd. 7 * 8 * Author: Xu Shengfei <xsf@rock-chips.com> 9 * Author: Sebastian Reichel <sebastian.reichel@collabora.com> 10 */ 11 12 #include <linux/interrupt.h> 13 #include <linux/mfd/core.h> 14 #include <linux/mfd/rk808.h> 15 #include <linux/module.h> 16 #include <linux/regmap.h> 17 #include <linux/spi/spi.h> 18 19 #define RK806_ADDR_SIZE 2 20 #define RK806_CMD_WITH_SIZE(CMD, VALUE_BYTES) \ 21 (RK806_CMD_##CMD | RK806_CMD_CRC_DIS | (VALUE_BYTES - 1)) 22 23 static const struct regmap_range rk806_volatile_ranges[] = { 24 regmap_reg_range(RK806_POWER_EN0, RK806_POWER_EN5), 25 regmap_reg_range(RK806_DVS_START_CTRL, RK806_INT_MSK1), 26 }; 27 28 static const struct regmap_access_table rk806_volatile_table = { 29 .yes_ranges = rk806_volatile_ranges, 30 .n_yes_ranges = ARRAY_SIZE(rk806_volatile_ranges), 31 }; 32 33 static const struct regmap_config rk806_regmap_config_spi = { 34 .reg_bits = 16, 35 .val_bits = 8, 36 .max_register = RK806_BUCK_RSERVE_REG5, 37 .cache_type = REGCACHE_MAPLE, 38 .volatile_table = &rk806_volatile_table, 39 }; 40 41 static int rk806_spi_bus_write(void *context, const void *vdata, size_t count) 42 { 43 struct device *dev = context; 44 struct spi_device *spi = to_spi_device(dev); 45 struct spi_transfer xfer[2] = { 0 }; 46 /* data and thus count includes the register address */ 47 size_t val_size = count - RK806_ADDR_SIZE; 48 char cmd; 49 50 if (val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) 51 return -EINVAL; 52 53 cmd = RK806_CMD_WITH_SIZE(WRITE, val_size); 54 55 xfer[0].tx_buf = &cmd; 56 xfer[0].len = sizeof(cmd); 57 xfer[1].tx_buf = vdata; 58 xfer[1].len = count; 59 60 return spi_sync_transfer(spi, xfer, ARRAY_SIZE(xfer)); 61 } 62 63 static int rk806_spi_bus_read(void *context, const void *vreg, size_t reg_size, 64 void *val, size_t val_size) 65 { 66 struct device *dev = context; 67 struct spi_device *spi = to_spi_device(dev); 68 char txbuf[3] = { 0 }; 69 70 if (reg_size != RK806_ADDR_SIZE || 71 val_size < 1 || val_size > (RK806_CMD_LEN_MSK + 1)) 72 return -EINVAL; 73 74 /* TX buffer contains command byte followed by two address bytes */ 75 txbuf[0] = RK806_CMD_WITH_SIZE(READ, val_size); 76 memcpy(txbuf+1, vreg, reg_size); 77 78 return spi_write_then_read(spi, txbuf, sizeof(txbuf), val, val_size); 79 } 80 81 static const struct regmap_bus rk806_regmap_bus_spi = { 82 .write = rk806_spi_bus_write, 83 .read = rk806_spi_bus_read, 84 .reg_format_endian_default = REGMAP_ENDIAN_LITTLE, 85 }; 86 87 static int rk8xx_spi_probe(struct spi_device *spi) 88 { 89 struct regmap *regmap; 90 91 regmap = devm_regmap_init(&spi->dev, &rk806_regmap_bus_spi, 92 &spi->dev, &rk806_regmap_config_spi); 93 if (IS_ERR(regmap)) 94 return dev_err_probe(&spi->dev, PTR_ERR(regmap), 95 "Failed to init regmap\n"); 96 97 return rk8xx_probe(&spi->dev, RK806_ID, spi->irq, regmap); 98 } 99 100 static const struct of_device_id rk8xx_spi_of_match[] = { 101 { .compatible = "rockchip,rk806", }, 102 { } 103 }; 104 MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match); 105 106 static const struct spi_device_id rk8xx_spi_id_table[] = { 107 { "rk806", 0 }, 108 { } 109 }; 110 MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table); 111 112 static struct spi_driver rk8xx_spi_driver = { 113 .driver = { 114 .name = "rk8xx-spi", 115 .of_match_table = rk8xx_spi_of_match, 116 }, 117 .probe = rk8xx_spi_probe, 118 .id_table = rk8xx_spi_id_table, 119 }; 120 module_spi_driver(rk8xx_spi_driver); 121 122 MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>"); 123 MODULE_DESCRIPTION("RK8xx SPI PMIC driver"); 124 MODULE_LICENSE("GPL"); 125