13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0 23d0407baSopenharmony_ci/* 33d0407baSopenharmony_ci * SPI access driver for rockchip rk806 43d0407baSopenharmony_ci * 53d0407baSopenharmony_ci * Copyright (c) 2021 Rockchip Electronics Co., Ltd. 63d0407baSopenharmony_ci * 73d0407baSopenharmony_ci * Author: Xu Shengfei <xsf@rock-chips.com> 83d0407baSopenharmony_ci */ 93d0407baSopenharmony_ci 103d0407baSopenharmony_ci#include <linux/mfd/rk806.h> 113d0407baSopenharmony_ci#include <linux/module.h> 123d0407baSopenharmony_ci#include <linux/regmap.h> 133d0407baSopenharmony_ci#include <linux/spi/spi.h> 143d0407baSopenharmony_ci 153d0407baSopenharmony_cistatic const struct of_device_id rk806_spi_of_match_table[] = { 163d0407baSopenharmony_ci { .compatible = "rockchip,rk806", }, 173d0407baSopenharmony_ci { } 183d0407baSopenharmony_ci}; 193d0407baSopenharmony_ciMODULE_DEVICE_TABLE(of, rk806_spi_of_match_table); 203d0407baSopenharmony_ci 213d0407baSopenharmony_cistatic int rk806_spi_write(struct spi_device *spi, 223d0407baSopenharmony_ci char addr, 233d0407baSopenharmony_ci const char *data, 243d0407baSopenharmony_ci size_t data_len) 253d0407baSopenharmony_ci{ 263d0407baSopenharmony_ci char write_cmd = RK806_CMD_WRITE | (data_len - 1); 273d0407baSopenharmony_ci char addrh = RK806_REG_H; 283d0407baSopenharmony_ci struct spi_message m; 293d0407baSopenharmony_ci int buffer, ret = 0; 303d0407baSopenharmony_ci struct spi_transfer write_cmd_packet = { 313d0407baSopenharmony_ci .tx_buf = &buffer, 323d0407baSopenharmony_ci .len = sizeof(buffer), 333d0407baSopenharmony_ci }; 343d0407baSopenharmony_ci 353d0407baSopenharmony_ci buffer = write_cmd | (addr << 8) | (addrh << 16) | (*data << 24); 363d0407baSopenharmony_ci 373d0407baSopenharmony_ci spi_message_init(&m); 383d0407baSopenharmony_ci spi_message_add_tail(&write_cmd_packet, &m); 393d0407baSopenharmony_ci ret = spi_sync(spi, &m); 403d0407baSopenharmony_ci return ret; 413d0407baSopenharmony_ci} 423d0407baSopenharmony_ci 433d0407baSopenharmony_cistatic int rk806_spi_bus_write(void *context, const void *data, size_t count) 443d0407baSopenharmony_ci{ 453d0407baSopenharmony_ci struct device *dev = context; 463d0407baSopenharmony_ci struct spi_device *spi = to_spi_device(dev); 473d0407baSopenharmony_ci char buf[2]; 483d0407baSopenharmony_ci 493d0407baSopenharmony_ci if (count < 2) { 503d0407baSopenharmony_ci dev_err(&spi->dev, "regmap write err!\n"); 513d0407baSopenharmony_ci return -EINVAL; 523d0407baSopenharmony_ci } 533d0407baSopenharmony_ci memcpy(buf, data, 2); 543d0407baSopenharmony_ci 553d0407baSopenharmony_ci return rk806_spi_write(spi, buf[0], &buf[1], (count - 1)); 563d0407baSopenharmony_ci} 573d0407baSopenharmony_ci 583d0407baSopenharmony_cistatic int rk806_spi_bus_read(void *context, 593d0407baSopenharmony_ci const void *reg, 603d0407baSopenharmony_ci size_t reg_size, 613d0407baSopenharmony_ci void *val, 623d0407baSopenharmony_ci size_t val_size) 633d0407baSopenharmony_ci{ 643d0407baSopenharmony_ci struct device *dev = context; 653d0407baSopenharmony_ci struct spi_device *spi = to_spi_device(dev); 663d0407baSopenharmony_ci char addr; 673d0407baSopenharmony_ci char txbuf[3] = { 0 }; 683d0407baSopenharmony_ci 693d0407baSopenharmony_ci if (reg_size != sizeof(char) || val_size < 1) 703d0407baSopenharmony_ci return -EINVAL; 713d0407baSopenharmony_ci 723d0407baSopenharmony_ci /* Copy address to read from into first element of SPI buffer. */ 733d0407baSopenharmony_ci memcpy(&addr, reg, sizeof(char)); 743d0407baSopenharmony_ci 753d0407baSopenharmony_ci txbuf[0] = RK806_CMD_READ | (val_size - 1); 763d0407baSopenharmony_ci txbuf[1] = addr; 773d0407baSopenharmony_ci txbuf[2] = RK806_REG_H; 783d0407baSopenharmony_ci 793d0407baSopenharmony_ci return spi_write_then_read(spi, txbuf, 3, val, val_size); 803d0407baSopenharmony_ci} 813d0407baSopenharmony_ci 823d0407baSopenharmony_cistatic const struct regmap_bus rk806_regmap_bus_spi = { 833d0407baSopenharmony_ci .write = rk806_spi_bus_write, 843d0407baSopenharmony_ci .read = rk806_spi_bus_read, 853d0407baSopenharmony_ci .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, 863d0407baSopenharmony_ci .val_format_endian_default = REGMAP_ENDIAN_NATIVE, 873d0407baSopenharmony_ci}; 883d0407baSopenharmony_ci 893d0407baSopenharmony_cistatic int rk806_spi_probe(struct spi_device *spi) 903d0407baSopenharmony_ci{ 913d0407baSopenharmony_ci struct rk806 *rk806; 923d0407baSopenharmony_ci 933d0407baSopenharmony_ci rk806 = devm_kzalloc(&spi->dev, sizeof(*rk806), GFP_KERNEL); 943d0407baSopenharmony_ci if (!rk806) 953d0407baSopenharmony_ci return -ENOMEM; 963d0407baSopenharmony_ci 973d0407baSopenharmony_ci spi_set_drvdata(spi, rk806); 983d0407baSopenharmony_ci rk806->dev = &spi->dev; 993d0407baSopenharmony_ci rk806->irq = spi->irq; 1003d0407baSopenharmony_ci 1013d0407baSopenharmony_ci rk806->regmap = devm_regmap_init(&spi->dev, 1023d0407baSopenharmony_ci &rk806_regmap_bus_spi, 1033d0407baSopenharmony_ci &spi->dev, 1043d0407baSopenharmony_ci &rk806_regmap_config_spi); 1053d0407baSopenharmony_ci if (IS_ERR(rk806->regmap)) { 1063d0407baSopenharmony_ci dev_err(rk806->dev, "Failed to initialize register map\n"); 1073d0407baSopenharmony_ci return PTR_ERR(rk806->regmap); 1083d0407baSopenharmony_ci } 1093d0407baSopenharmony_ci 1103d0407baSopenharmony_ci return rk806_device_init(rk806); 1113d0407baSopenharmony_ci} 1123d0407baSopenharmony_ci 1133d0407baSopenharmony_cistatic int rk806_spi_remove(struct spi_device *spi) 1143d0407baSopenharmony_ci{ 1153d0407baSopenharmony_ci struct rk806 *rk806 = spi_get_drvdata(spi); 1163d0407baSopenharmony_ci 1173d0407baSopenharmony_ci return rk806_device_exit(rk806); 1183d0407baSopenharmony_ci} 1193d0407baSopenharmony_ci 1203d0407baSopenharmony_cistatic const struct spi_device_id rk806_spi_id_table[] = { 1213d0407baSopenharmony_ci { "rk806", 0 }, 1223d0407baSopenharmony_ci { /* sentinel */ } 1233d0407baSopenharmony_ci}; 1243d0407baSopenharmony_ciMODULE_DEVICE_TABLE(spi, rk806_spi_id_table); 1253d0407baSopenharmony_ci 1263d0407baSopenharmony_cistatic struct spi_driver rk806_spi_driver = { 1273d0407baSopenharmony_ci .driver = { 1283d0407baSopenharmony_ci .name = "rk806", 1293d0407baSopenharmony_ci .owner = THIS_MODULE, 1303d0407baSopenharmony_ci .of_match_table = rk806_spi_of_match_table, 1313d0407baSopenharmony_ci }, 1323d0407baSopenharmony_ci .probe = rk806_spi_probe, 1333d0407baSopenharmony_ci .remove = rk806_spi_remove, 1343d0407baSopenharmony_ci .id_table = rk806_spi_id_table, 1353d0407baSopenharmony_ci}; 1363d0407baSopenharmony_cimodule_spi_driver(rk806_spi_driver); 1373d0407baSopenharmony_ci 1383d0407baSopenharmony_ciMODULE_AUTHOR("Xu Shengfei <xsf@rock-chips.com>"); 1393d0407baSopenharmony_ciMODULE_DESCRIPTION("RK806 SPI Interface Driver"); 1403d0407baSopenharmony_ciMODULE_LICENSE("GPL v2"); 141