kernel_samsung_a53x/drivers/soc/samsung/cpif/boot_device_spi.c
2024-06-15 16:02:09 -03:00

175 lines
3.7 KiB
C
Executable file

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014-2019, Samsung Electronics.
*
*/
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/spi/spi.h>
#include "modem_prj.h"
#include "modem_utils.h"
#include "link_device_memory.h"
#include "boot_device_spi.h"
#define MAX_IMAGE_SIZE SZ_128K
#define MAX_SPI_DEVICE 2
static int _count;
static struct cpboot_spi _cpboot[MAX_SPI_DEVICE];
/*
* Export functions
*/
int cpboot_spi_load_cp_image(struct link_device *ld, struct io_device *iod, unsigned long arg)
{
int ret = 0;
struct mem_link_device *mld = ld_to_mem_link_device(ld);
struct cpboot_spi *cpboot = cpboot_spi_get_device(mld->spi_bus_num);
struct cp_image img;
char *buff = NULL;
struct spi_message msg;
struct spi_transfer xfer;
if (!cpboot || !cpboot->spi) {
mif_err("spi is null\n");
return -EPERM;
}
mutex_lock(&cpboot->lock);
ret = copy_from_user(&img, (const void __user *)arg, sizeof(struct cp_image));
if (ret) {
mif_err("copy_from_user() arg error:%d\n", ret);
goto exit;
}
mif_info("size:%d bus_num:%d\n", img.size, cpboot->spi->controller->bus_num);
if ((img.size == 0) || (img.size > MAX_IMAGE_SIZE)) {
mif_err("size error:%d\n", img.size);
ret = -EINVAL;
goto exit;
}
/* MUST not enable dma-mode on SPI
* dma-mode does not support non-contiguous buffer
*/
buff = vzalloc(img.size);
if (!buff) {
mif_err("vzalloc(%u) error\n", img.size);
ret = -ENOMEM;
goto exit;
}
ret = copy_from_user(buff, (const void __user *)img.binary, img.size);
if (ret) {
mif_err("copy_from_user() buff error:%d\n", ret);
goto exit;
}
memset(&xfer, 0, sizeof(struct spi_transfer));
xfer.len = img.size;
xfer.tx_buf = buff;
spi_message_init(&msg);
spi_message_add_tail(&xfer, &msg);
ret = spi_sync(cpboot->spi, &msg);
if (ret < 0) {
mif_err("spi_sync() error:%d\n", ret);
goto exit;
}
exit:
if (buff)
vfree(buff);
mutex_unlock(&cpboot->lock);
return ret;
}
EXPORT_SYMBOL(cpboot_spi_load_cp_image);
struct cpboot_spi *cpboot_spi_get_device(int bus_num)
{
int i;
struct cpboot_spi *cpboot = NULL;
for (i = 0; i < MAX_SPI_DEVICE; i++) {
if (_cpboot[i].spi && (bus_num == _cpboot[i].spi->controller->bus_num)) {
mif_info("Get bus_num:%d\n", bus_num);
cpboot = &_cpboot[i];
return cpboot;
}
}
mif_err("Can not get bus_num:%d\n", bus_num);
return NULL;
}
EXPORT_SYMBOL(cpboot_spi_get_device);
/*
* Probe
*/
static int cpboot_spi_probe(struct spi_device *spi)
{
int ret = 0;
mif_info("bus_num:%d count:%d\n", spi->controller->bus_num, _count);
if (_count >= MAX_SPI_DEVICE) {
mif_err("_count is over %d\n", MAX_SPI_DEVICE);
ret = -EINVAL;
goto err;
}
mutex_init(&_cpboot[_count].lock);
spi->bits_per_word = 8;
if (spi_setup(spi)) {
mif_err("ERR! spi_setup fail\n");
ret = -EINVAL;
goto err_setup;
}
spi_set_drvdata(spi, &_cpboot[_count]);
_cpboot[_count].spi = spi;
_count++;
return 0;
err_setup:
mutex_destroy(&_cpboot[_count].lock);
err:
panic("CP SPI driver probe failed\n");
return ret;
}
static int cpboot_spi_remove(struct spi_device *spi)
{
struct cpboot_spi *cpboot = spi_get_drvdata(spi);
mutex_destroy(&cpboot->lock);
return 0;
}
static const struct of_device_id cpboot_spi_dt_match[] = {
{ .compatible = "samsung,exynos-cp-spi" },
{},
};
MODULE_DEVICE_TABLE(of, cpboot_spi_dt_match);
static struct spi_driver cpboot_spi_driver = {
.probe = cpboot_spi_probe,
.remove = cpboot_spi_remove,
.driver = {
.name = "cpboot_spi",
.owner = THIS_MODULE,
.of_match_table = of_match_ptr(cpboot_spi_dt_match),
.suppress_bind_attrs = true,
},
};
module_spi_driver(cpboot_spi_driver);
MODULE_DESCRIPTION("Exynos SPI driver to load CP bootloader");
MODULE_LICENSE("GPL");