Linux Audio

Check our new training course

Loading...
Note: File does not exist in v5.4.
  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
 23static 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
 28static 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
 33static 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_RBTREE,
 38	.volatile_table = &rk806_volatile_table,
 39};
 40
 41static 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
 63static 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
 81static 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
 87static 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
100static const struct of_device_id rk8xx_spi_of_match[] = {
101	{ .compatible = "rockchip,rk806", },
102	{ }
103};
104MODULE_DEVICE_TABLE(of, rk8xx_spi_of_match);
105
106static const struct spi_device_id rk8xx_spi_id_table[] = {
107	{ "rk806", 0 },
108	{ }
109};
110MODULE_DEVICE_TABLE(spi, rk8xx_spi_id_table);
111
112static 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};
120module_spi_driver(rk8xx_spi_driver);
121
122MODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>");
123MODULE_DESCRIPTION("RK8xx SPI PMIC driver");
124MODULE_LICENSE("GPL");