Files
SDK_SG200x_V2/linux_5.10/drivers/crypto/cvitek-spacc.c
sophgo-forum-service 50fd0630f7 linux_5.10: weekly rls 2024.07.20
-142736, Sync spacc driver code.
-2303c6, add suspend to ram featrue.
-d3bff8, support pdm mic using AEC.
-203610, support new spinand flash:HYF1GQ4UDACAE.

Change-Id: Iec21bccaee43c4a71213b42b88f41a45527fd8a5
2024-07-25 17:08:03 +08:00

749 lines
19 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2022-2023 CVITEK
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/bitops.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/of.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/version.h>
#include <linux/jiffies.h>
#include <linux/init.h>
#include <asm/cacheflush.h>
#include <linux/dma-buf.h>
#include <linux/dma-map-ops.h>
#include <linux/uaccess.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/interrupt.h>
#include <linux/cvitek_spacc.h>
#include <cvitek-spacc-regs.h>
#define DEVICE_NAME "spacc"
static char flag = 'n';
static DECLARE_WAIT_QUEUE_HEAD(wq);
struct cvi_spacc {
struct device *dev;
struct cdev cdev;
dev_t tdev;
void __iomem *spacc_base;
struct class *spacc_class;
void *buffer;
u32 buffer_size;
u32 used_size;
// for sha256/sha1
u32 state[8];
u32 result_size;
#ifdef CONFIG_PM_SLEEP
struct clk *efuse_clk;
#endif
};
#ifdef CONFIG_PM_SLEEP
static int cvitek_spacc_suspend(struct device *dev)
{
struct cvi_spacc *spacc = dev_get_drvdata(dev);
void __iomem *sec_top;
clk_prepare_enable(spacc->efuse_clk);
sec_top = ioremap(0x020b0000, 4);
iowrite32(0x3, sec_top);
iounmap(sec_top);
clk_disable_unprepare(spacc->efuse_clk);
return 0;
}
static int cvitek_spacc_resume(struct device *dev)
{
struct cvi_spacc *spacc = dev_get_drvdata(dev);
void __iomem *sec_top;
clk_prepare_enable(spacc->efuse_clk);
sec_top = ioremap(0x020b0000, 4);
iowrite32(0x0, sec_top);
iounmap(sec_top);
clk_disable_unprepare(spacc->efuse_clk);
return 0;
}
#endif /* CONFIG_PM_SLEEP */
static SIMPLE_DEV_PM_OPS(cvitek_spacc_pm_ops, cvitek_spacc_suspend, cvitek_spacc_resume);
static inline void cvi_sha256_init(struct cvi_spacc *spacc)
{
spacc->state[0] = cpu_to_be32(0x6A09E667);
spacc->state[1] = cpu_to_be32(0xBB67AE85);
spacc->state[2] = cpu_to_be32(0x3C6EF372);
spacc->state[3] = cpu_to_be32(0xA54FF53A);
spacc->state[4] = cpu_to_be32(0x510E527F);
spacc->state[5] = cpu_to_be32(0x9B05688C);
spacc->state[6] = cpu_to_be32(0x1F83D9AB);
spacc->state[7] = cpu_to_be32(0x5BE0CD19);
}
static inline void cvi_sha1_init(struct cvi_spacc *spacc)
{
spacc->state[0] = cpu_to_be32(0x67452301);
spacc->state[1] = cpu_to_be32(0xEFCDAB89);
spacc->state[2] = cpu_to_be32(0x98BADCFE);
spacc->state[3] = cpu_to_be32(0x10325476);
spacc->state[4] = cpu_to_be32(0xC3D2E1F0);
}
static inline void trigger_cryptodma_engine_and_wait_finish(struct cvi_spacc *spacc)
{
// Set cryptodma control
iowrite32(0x3, spacc->spacc_base + CRYPTODMA_INT_MASK);
// Clear interrupt
// Important!!! must do this
iowrite32(0x3, spacc->spacc_base + CRYPTODMA_WR_INT);
// Trigger cryptodma engine
iowrite32(DMA_WRITE_MAX_BURST << 24 |
DMA_READ_MAX_BURST << 16 |
DMA_DESCRIPTOR_MODE << 1 | DMA_ENABLE, spacc->spacc_base + CRYPTODMA_DMA_CTRL);
wait_event_interruptible(wq, flag == 'y');
flag = 'n';
}
static inline void get_hash_result(struct cvi_spacc *spacc, int count)
{
u32 i;
u32 *result = (u32 *)spacc->buffer;
for (i = 0; i < count; i++)
result[i] = ioread32(spacc->spacc_base + CRYPTODMA_SHA_PARA + i * 4);
}
static inline void setup_dma_descriptor(struct cvi_spacc *spacc, uint32_t *dma_descriptor)
{
phys_addr_t descriptor_phys;
descriptor_phys = virt_to_phys(dma_descriptor);
arch_sync_dma_for_device(descriptor_phys, /*sizeof(dma_descriptor)*/ 4 * 22, DMA_TO_DEVICE);
// set dma descriptor addr
iowrite32((uint32_t)((uint64_t)descriptor_phys & 0xFFFFFFFF), spacc->spacc_base + CRYPTODMA_DES_BASE_L);
iowrite32((uint32_t)((uint64_t)descriptor_phys >> 32), spacc->spacc_base + CRYPTODMA_DES_BASE_H);
}
static inline void setup_src(u32 *dma_descriptor, uintptr_t src, u32 len)
{
phys_addr_t src_phys;
src_phys = virt_to_phys((void *)src);
arch_sync_dma_for_device(src_phys, len, DMA_TO_DEVICE);
dma_descriptor[CRYPTODMA_SRC_LEN] = len;
dma_descriptor[CRYPTODMA_SRC_ADDR_L] = (uint32_t)((uint64_t)src_phys & 0xFFFFFFFF);
dma_descriptor[CRYPTODMA_SRC_ADDR_H] = (uint32_t)((uint64_t)src_phys >> 32);
}
static void setup_src_dst(u32 *dma_descriptor, phys_addr_t buffer, u32 len)
{
dma_descriptor[CRYPTODMA_SRC_LEN] = len;
dma_descriptor[CRYPTODMA_SRC_ADDR_L] = (uint32_t)((uint64_t)buffer & 0xFFFFFFFF);
dma_descriptor[CRYPTODMA_SRC_ADDR_H] = (uint32_t)((uint64_t)buffer >> 32);
dma_descriptor[CRYPTODMA_DST_ADDR_L] = (uint32_t)((uint64_t)buffer & 0xFFFFFFFF);
dma_descriptor[CRYPTODMA_DST_ADDR_H] = (uint32_t)((uint64_t)buffer >> 32);
}
#define setup_dst(dst)\
do {\
phys_addr_t dst_phys = virt_to_phys((void *)dst);\
dma_descriptor[CRYPTODMA_DST_ADDR_L] = (uint32_t)((uint64_t)dst_phys & 0xFFFFFFFF);\
dma_descriptor[CRYPTODMA_DST_ADDR_H] = (uint32_t)((uint64_t)dst_phys >> 32);\
} while (0)
static inline void setup_mode(u32 *dma_descriptor, SPACC_ALGO_MODE_E mode, unsigned char *iv)
{
switch (mode) {
case SPACC_ALGO_MODE_CBC:
dma_descriptor[CRYPTODMA_CTRL] |= DES_USE_DESCRIPTOR_IV;
dma_descriptor[CRYPTODMA_CIPHER] = CBC_ENABLE << 1;
memcpy(&dma_descriptor[CRYPTODMA_IV], iv, 16);
break;
case SPACC_ALGO_MODE_CTR:
dma_descriptor[CRYPTODMA_CTRL] |= DES_USE_DESCRIPTOR_IV;
dma_descriptor[CRYPTODMA_CIPHER] = 0x1 << 2;
memcpy(&dma_descriptor[CRYPTODMA_IV], iv, 16);
break;
case SPACC_ALGO_MODE_ECB:
default:
break;
}
}
static inline void setup_key_size(u32 *dma_descriptor, SPACC_KEY_SIZE_E size, unsigned char *key)
{
switch (size) {
case SPACC_KEY_SIZE_64BITS:
memcpy(&dma_descriptor[CRYPTODMA_KEY], key, 8);
break;
case SPACC_KEY_SIZE_128BITS:
dma_descriptor[CRYPTODMA_CIPHER] |= (0x1 << 5);
memcpy(&dma_descriptor[CRYPTODMA_KEY], key, 16);
break;
case SPACC_KEY_SIZE_192BITS:
dma_descriptor[CRYPTODMA_CIPHER] |= (0x1 << 4);
memcpy(&dma_descriptor[CRYPTODMA_KEY], key, 24);
break;
case SPACC_KEY_SIZE_256BITS:
dma_descriptor[CRYPTODMA_CIPHER] |= (0x1 << 3);
memcpy(&dma_descriptor[CRYPTODMA_KEY], key, 32);
break;
default:
break;
}
}
static inline void setup_action(u32 *dma_descriptor, SPACC_ACTION_E action)
{
if (action == SPACC_ACTION_ENCRYPTION)
dma_descriptor[CRYPTODMA_CIPHER] |= 0x1;
}
static irqreturn_t cvitek_spacc_irq(int irq, void *data)
{
struct cvi_spacc *spacc = (struct cvi_spacc *)data;
iowrite32(0x3, spacc->spacc_base + CRYPTODMA_WR_INT);
flag = 'y';
wake_up_interruptible(&wq);
return IRQ_HANDLED;
}
int spacc_sha256(struct cvi_spacc *spacc, uintptr_t src, uint32_t len)
{
__aligned(32) u32 dma_descriptor[22] = {0};
u32 i;
// must mark DES_USE_DESCRIPTOR_KEY flag
dma_descriptor[CRYPTODMA_CTRL] = DES_USE_DESCRIPTOR_KEY | DES_USE_SHA | 0xF;
dma_descriptor[CRYPTODMA_CIPHER] = (0x1 << 1) | 0x1;
for (i = 0; i < 8; i++)
dma_descriptor[CRYPTODMA_KEY + i] = spacc->state[i];
setup_src(dma_descriptor, src, len);
setup_dma_descriptor(spacc, dma_descriptor);
trigger_cryptodma_engine_and_wait_finish(spacc);
return 0;
}
int spacc_sha1(struct cvi_spacc *spacc, uintptr_t src, uint32_t len)
{
__aligned(32) u32 dma_descriptor[22] = {0};
u32 i;
// must mark DES_USE_DESCRIPTOR_KEY flag
dma_descriptor[CRYPTODMA_CTRL] = DES_USE_DESCRIPTOR_KEY | DES_USE_SHA | 0xF;
dma_descriptor[CRYPTODMA_CIPHER] = 0x1;
for (i = 0; i < 5; i++)
dma_descriptor[CRYPTODMA_KEY + i] = spacc->state[i];
setup_src(dma_descriptor, src, len);
setup_dma_descriptor(spacc, dma_descriptor);
trigger_cryptodma_engine_and_wait_finish(spacc);
return 0;
}
int spacc_base64(struct cvi_spacc *spacc, phys_addr_t src, uint32_t len, SPACC_ACTION_E ation)
{
__aligned(32) u32 dma_descriptor[22] = {0};
dma_descriptor[CRYPTODMA_CTRL] = DES_USE_BASE64 | 0xF;
if (ation == SPACC_ACTION_ENCRYPTION) {
dma_descriptor[CRYPTODMA_CIPHER] = 0x1;
spacc->result_size = (len + (3 - 1)) / 3 * 4;
dma_descriptor[CRYPTODMA_DST_LEN] = spacc->result_size;
} else {
spacc->result_size = (len / 4) * 3;
dma_descriptor[CRYPTODMA_DST_LEN] = spacc->result_size;
}
setup_src_dst(dma_descriptor, src, len);
setup_dma_descriptor(spacc, dma_descriptor);
trigger_cryptodma_engine_and_wait_finish(spacc);
return 0;
}
int spacc_aes(struct cvi_spacc *spacc, phys_addr_t src, uint32_t len, spacc_aes_config_s config)
{
__aligned(32) u32 dma_descriptor[22] = {0};
spacc->result_size = len;
dma_descriptor[CRYPTODMA_CTRL] = DES_USE_DESCRIPTOR_KEY | DES_USE_AES | 0xF;
setup_mode(dma_descriptor, config.mode, config.iv);
setup_key_size(dma_descriptor, config.size, config.key);
setup_action(dma_descriptor, config.action);
setup_src_dst(dma_descriptor, src, len);
setup_dma_descriptor(spacc, dma_descriptor);
trigger_cryptodma_engine_and_wait_finish(spacc);
return 0;
}
int spacc_sm4(struct cvi_spacc *spacc, phys_addr_t src, uint32_t len, spacc_sm4_config_s config)
{
__aligned(32) u32 dma_descriptor[22] = {0};
spacc->result_size = len;
dma_descriptor[CRYPTODMA_CTRL] = DES_USE_DESCRIPTOR_KEY | DES_USE_SM4 | 0xF;
setup_mode(dma_descriptor, config.mode, config.iv);
setup_key_size(dma_descriptor, config.size, config.key);
setup_action(dma_descriptor, config.action);
setup_src_dst(dma_descriptor, src, len);
setup_dma_descriptor(spacc, dma_descriptor);
trigger_cryptodma_engine_and_wait_finish(spacc);
return 0;
}
int spacc_des(struct cvi_spacc *spacc, phys_addr_t src, uint32_t len, spacc_des_config_s config, int is_tdes)
{
__aligned(32) u32 dma_descriptor[22] = {0};
spacc->result_size = len;
dma_descriptor[CRYPTODMA_CTRL] = DES_USE_DESCRIPTOR_KEY | DES_USE_DES | 0xF;
setup_mode(dma_descriptor, config.mode, config.iv);
if (is_tdes) {
dma_descriptor[CRYPTODMA_CIPHER] |= (0x1 << 3);
memcpy(&dma_descriptor[CRYPTODMA_KEY], config.key, 24);
} else {
memcpy(&dma_descriptor[CRYPTODMA_KEY], config.key, 8);
}
setup_action(dma_descriptor, config.action);
setup_src_dst(dma_descriptor, src, len);
setup_dma_descriptor(spacc, dma_descriptor);
trigger_cryptodma_engine_and_wait_finish(spacc);
return 0;
}
static int cvi_spacc_init_buffer(struct cvi_spacc *spacc, size_t size)
{
unsigned int order = get_order(size);
struct page *page;
if (size == spacc->buffer_size) {
return 0;
} else if (spacc->buffer_size) {
free_pages((unsigned long)spacc->buffer, get_order(spacc->buffer_size));
spacc->buffer_size = 0;
}
page = alloc_pages(GFP_KERNEL | __GFP_ZERO, order);
if (!page)
return -ENOMEM;
spacc->buffer = page_address(page);
spacc->buffer_size = size;
return 0;
}
static int spacc_open(struct inode *inode, struct file *file)
{
struct cvi_spacc *spacc;
spacc = container_of(inode->i_cdev, struct cvi_spacc, cdev);
spacc->used_size = 0;
spacc->result_size = 0;
file->private_data = spacc;
return 0;
}
static ssize_t spacc_read(struct file *filp, char *buf, size_t count, loff_t *f_pos)
{
struct cvi_spacc *spacc = filp->private_data;
int ret = 0;
if (spacc->result_size == 0) {
pr_err("spacc result is 0\n");
return -1;
}
if (count < spacc->result_size)
return -1;
ret = copy_to_user(buf, spacc->buffer, spacc->result_size);
if (ret != 0)
return -1;
return spacc->result_size;
}
static ssize_t spacc_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos)
{
struct cvi_spacc *spacc = filp->private_data;
int ret = spacc->buffer_size - spacc->used_size;
if (ret <= 0)
return spacc->used_size;
if (count > ret)
count = ret;
ret = copy_from_user(((unsigned char *)spacc->buffer + spacc->used_size), buf, count);
if (ret != 0)
return -1;
spacc->used_size += count;
return spacc->used_size;
}
static int spacc_release(struct inode *inode, struct file *file)
{
return 0;
}
static long spacc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
struct cvi_spacc *spacc = filp->private_data;
int ret;
switch (cmd) {
case IOCTL_SPACC_CREATE_MEMPOOL: {
unsigned int size = 0;
ret = copy_from_user((unsigned char *)&size, (unsigned char *)arg, sizeof(size));
if (ret != 0)
return -1;
ret = cvi_spacc_init_buffer(spacc, size);
if (ret != 0)
return -1;
break;
}
case IOCTL_SPACC_GET_MEMPOOL_SIZE: {
ret = copy_to_user((unsigned char *)arg, (unsigned char *)&spacc->buffer_size,
sizeof(spacc->buffer_size));
if (ret != 0)
return -1;
break;
}
case IOCTL_SPACC_SHA256_ACTION: {
if (spacc->used_size & 0x3F) {
pr_err("used_size : %d\n", spacc->used_size);
return -1;
}
cvi_sha256_init(spacc);
ret = spacc_sha256(spacc, (uintptr_t)spacc->buffer, spacc->used_size);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
get_hash_result(spacc, 8);
spacc->result_size = 32;
spacc->used_size = 0;
break;
}
case IOCTL_SPACC_SHA1_ACTION: {
if (spacc->used_size & 0x3F) {
pr_err("spacc_dev->used_size : %d\n", spacc->used_size);
return -1;
}
cvi_sha1_init(spacc);
ret = spacc_sha1(spacc, (uintptr_t)spacc->buffer, spacc->used_size);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
get_hash_result(spacc, 5);
spacc->result_size = 20;
spacc->used_size = 0;
break;
}
case IOCTL_SPACC_BASE64_ACTION: {
spacc_base64_action_s action = {0};
phys_addr_t src_phys;
ret = copy_from_user((unsigned char *)&action, (unsigned char *)arg, sizeof(action));
if (ret != 0)
return -1;
src_phys = virt_to_phys(spacc->buffer);
arch_sync_dma_for_device(src_phys, spacc->used_size, DMA_TO_DEVICE);
ret = spacc_base64(spacc, src_phys, spacc->used_size, action.action);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
arch_sync_dma_for_device(src_phys, spacc->result_size, DMA_FROM_DEVICE);
spacc->used_size = 0;
break;
}
case IOCTL_SPACC_AES_ACTION: {
spacc_aes_config_s config = {0};
phys_addr_t src_phys;
uint32_t len;
ret = copy_from_user((unsigned char *)&config, (unsigned char *)arg, sizeof(config));
if (ret != 0)
return -1;
if (config.src) {
if ((config.len == 0) ||
(config.len & 0xF)) {
pr_err("src len [%d] invailed\n", config.len);
return -1;
}
src_phys = (phys_addr_t)config.src;
len = config.len;
} else {
if ((spacc->used_size == 0) ||
(spacc->used_size & 0xF)) {
pr_err("used_size : %d\n", spacc->used_size);
return -1;
}
src_phys = virt_to_phys(spacc->buffer);
len = spacc->used_size;
}
arch_sync_dma_for_device(src_phys, len, DMA_TO_DEVICE);
ret = spacc_aes(spacc, src_phys, len, config);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
arch_sync_dma_for_device(src_phys, spacc->result_size, DMA_FROM_DEVICE);
spacc->used_size = 0;
break;
}
case IOCTL_SPACC_SM4_ACTION: {
spacc_sm4_config_s action = {0};
phys_addr_t src_phys;
if (spacc->used_size & 0xF) {
pr_err("used_size : %d\n", spacc->used_size);
return -1;
}
ret = copy_from_user((unsigned char *)&action, (unsigned char *)arg, sizeof(action));
if (ret != 0)
return -1;
src_phys = virt_to_phys(spacc->buffer);
arch_sync_dma_for_device(src_phys, spacc->used_size, DMA_TO_DEVICE);
ret = spacc_sm4(spacc, src_phys, spacc->used_size, action);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
arch_sync_dma_for_device(src_phys, spacc->result_size, DMA_FROM_DEVICE);
spacc->used_size = 0;
break;
}
case IOCTL_SPACC_DES_ACTION: {
spacc_des_config_s action = {0};
phys_addr_t src_phys;
if (spacc->used_size & 0x7) {
pr_err("spacc_dev->used_size : %d\n", spacc->used_size);
return -1;
}
ret = copy_from_user((unsigned char *)&action, (unsigned char *)arg, sizeof(action));
if (ret != 0)
return -1;
src_phys = virt_to_phys(spacc->buffer);
arch_sync_dma_for_device(src_phys, spacc->used_size, DMA_TO_DEVICE);
ret = spacc_des(spacc, src_phys, spacc->used_size, action, 0);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
arch_sync_dma_for_device(src_phys, spacc->result_size, DMA_FROM_DEVICE);
spacc->used_size = 0;
break;
}
case IOCTL_SPACC_TDES_ACTION: {
spacc_tdes_config_s action = {0};
phys_addr_t src_phys;
if (spacc->used_size & 0x7) {
pr_err("spacc_dev->used_size : %d\n", spacc->used_size);
return -EINVAL;
}
ret = copy_from_user((unsigned char *)&action, (unsigned char *)arg, sizeof(action));
if (ret != 0)
return -1;
src_phys = virt_to_phys(spacc->buffer);
arch_sync_dma_for_device(src_phys, spacc->used_size, DMA_TO_DEVICE);
ret = spacc_des(spacc, src_phys, spacc->used_size, action, 1);
if (ret < 0) {
pr_err("plat_cryptodma_do failed\n");
return -1;
}
arch_sync_dma_for_device(src_phys, spacc->result_size, DMA_FROM_DEVICE);
spacc->used_size = 0;
break;
}
default:
return -EINVAL;
}
return 0;
}
const struct file_operations spacc_fops = {
.owner = THIS_MODULE,
.open = spacc_open,
.read = spacc_read,
.write = spacc_write,
.release = spacc_release,
.unlocked_ioctl = spacc_ioctl,
};
static int cvitek_spacc_drv_probe(struct platform_device *pdev)
{
struct cvi_spacc *spacc;
struct device *dev = &pdev->dev;
int ret = 0;
spacc = devm_kzalloc(dev, sizeof(*spacc), GFP_KERNEL);
if (!spacc)
return -ENOMEM;
spacc->dev = dev;
spacc->spacc_base = devm_platform_ioremap_resource(pdev, 0);
if (!spacc->spacc_base)
return -ENOMEM;
ret = platform_get_irq(pdev, 0);
if (ret <= 0) {
pr_err("get irq num failed\n");
return -1;
}
#ifdef CONFIG_PM_SLEEP
spacc->efuse_clk = clk_get_sys(NULL, "clk_efuse");
if (IS_ERR(spacc->efuse_clk)) {
pr_err("%s: efuse clock not found %ld\n", __func__
, PTR_ERR(spacc->efuse_clk));
return -1;
}
#endif
ret = devm_request_irq(dev, ret, cvitek_spacc_irq
, IRQF_SHARED | IRQF_TRIGGER_RISING, pdev->name, spacc);
if (ret) {
pr_err("request irq failed\n");
return -1;
}
ret = alloc_chrdev_region(&spacc->tdev, 0, 1, DEVICE_NAME);
if (ret)
return ret;
cdev_init(&spacc->cdev, &spacc_fops);
spacc->cdev.owner = THIS_MODULE;
ret = cdev_add(&spacc->cdev, spacc->tdev, 1);
if (ret)
goto failed;
spacc->spacc_class = class_create(THIS_MODULE, DEVICE_NAME);
if (IS_ERR(spacc->spacc_class)) {
pr_err("Err: failed when create class.\n");
goto failed;
}
device_create(spacc->spacc_class, NULL, spacc->tdev, spacc, DEVICE_NAME);
return ret;
failed:
unregister_chrdev_region(spacc->tdev, 1);
return -ENOMEM;
}
static int cvitek_spacc_drv_remove(struct platform_device *pdev)
{
struct cvi_spacc *spacc = platform_get_drvdata(pdev);
device_destroy(spacc->spacc_class, spacc->tdev);
cdev_del(&spacc->cdev);
unregister_chrdev_region(spacc->tdev, 1);
class_destroy(spacc->spacc_class);
return 0;
}
#ifdef CONFIG_OF
static const struct of_device_id cvitek_spacc_of_match[] = {
{ .compatible = "cvitek,spacc", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, cvitek_spacc_of_match);
#endif
static struct platform_driver cvitek_spacc_driver = {
.probe = cvitek_spacc_drv_probe,
.remove = cvitek_spacc_drv_remove,
.driver = {
.name = "cvitek_spacc",
.of_match_table = of_match_ptr(cvitek_spacc_of_match),
.pm = &cvitek_spacc_pm_ops,
},
};
module_platform_driver(cvitek_spacc_driver);
MODULE_DESCRIPTION("Cvitek Spacc Driver");
MODULE_LICENSE("GPL");