| #include <linux/kernel.h> |
| #include <linux/module.h> |
| #include <linux/slab.h> |
| #include <linux/err.h> |
| #include <linux/clk-provider.h> |
| #include <linux/clk.h> |
| #include <linux/io.h> |
| #include <linux/hw_random.h> |
| #include <linux/platform_device.h> |
| #include <linux/device.h> |
| #include <linux/init.h> |
| #include <linux/errno.h> |
| #include <linux/interrupt.h> |
| #include <linux/irq.h> |
| #include <linux/scatterlist.h> |
| #include <linux/dma-mapping.h> |
| #include <linux/of_device.h> |
| #include <linux/delay.h> |
| #include <linux/crypto.h> |
| #include <crypto/scatterwalk.h> |
| #include <crypto/algapi.h> |
| |
| #include "asr-bcm.h" |
| #include "../asr_aes_clk.h" |
| |
| static inline u32 asr_bcm_read(struct asr_bcm_dev *dd, u32 offset) |
| { |
| u32 value = readl_relaxed(dd->io_base + offset); |
| |
| return value; |
| } |
| |
| static inline void asr_bcm_write(struct asr_bcm_dev *dd, |
| u32 offset, u32 value) |
| { |
| writel_relaxed(value, dd->io_base + offset); |
| } |
| |
| int dma_input_config(struct asr_bcm_dev *dd, int rid_ext, int rid) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, DMA_IN_CTRL); |
| val &= 0x0f0f0000; |
| val |= (0x7 << 28) | /* dis error check */ |
| ((rid_ext & 0xF) << 20) | /* rid ext */ |
| (0x1 << 18) | /* dis out-of-order */ |
| (0x1 << 17) | /* data 64 byte aligned */ |
| (0x1 << 15) | /* FIFO bus size 64bit */ |
| (0x1 << 13) | /* brust type: Inc */ |
| (0x8 << 8) | /* brust len */ |
| ((rid & 0xF) << 4); |
| |
| asr_bcm_write(dd, DMA_IN_CTRL, val); |
| |
| return 0; |
| } |
| |
| int dma_output_config(struct asr_bcm_dev *dd, int wid_ext, int wid) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, DMA_OUT_CTRL); |
| val &= 0x0f0f0000; |
| val |= (0x7 << 28) | /* dis error check */ |
| ((wid_ext & 0xF) << 20) | /* rid ext */ |
| (0x1 << 18) | /* dis out-of-order */ |
| (0x1 << 17) | /* data 64 byte aligned */ |
| (0x1 << 15) | /* FIFO bus size 64bit */ |
| (0x1 << 13) | /* brust type: Inc */ |
| (0x8 << 8) | /* brust len */ |
| ((wid & 0xF) << 4); |
| |
| asr_bcm_write(dd, DMA_OUT_CTRL, val); |
| |
| return 0; |
| } |
| |
| |
| int dma_input_address(struct asr_bcm_dev *dd, uint32_t src_addr, \ |
| uint32_t src_size, int chained) |
| { |
| if (chained) { |
| asr_bcm_write(dd, DMA_IN_NX_LL_ADR, src_addr); |
| asr_bcm_write(dd, DMA_IN_SRC_ADR, 0x0); |
| asr_bcm_write(dd, DMA_IN_XFER_CNTR, 0x0); |
| } else { |
| asr_bcm_write(dd, DMA_IN_NX_LL_ADR, 0x0); |
| asr_bcm_write(dd, DMA_IN_SRC_ADR, src_addr); |
| asr_bcm_write(dd, DMA_IN_XFER_CNTR, src_size); |
| } |
| |
| return 0; |
| } |
| |
| int dma_output_address(struct asr_bcm_dev *dd, uint32_t dst_addr, uint32_t dst_size, int chained) |
| { |
| if (chained) { |
| asr_bcm_write(dd, DMA_OUT_NX_LL_ADR, dst_addr); |
| asr_bcm_write(dd, DMA_OUT_DEST_ADR, 0x0); |
| asr_bcm_write(dd, DMA_OUT_XFER_CNTR, 0x0); |
| } else { |
| asr_bcm_write(dd, DMA_OUT_NX_LL_ADR, 0x0); |
| asr_bcm_write(dd, DMA_OUT_DEST_ADR, dst_addr); |
| asr_bcm_write(dd, DMA_OUT_XFER_CNTR, dst_size); |
| } |
| |
| return 0; |
| } |
| |
| void dma_input_start(struct asr_bcm_dev *dd) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, DMA_IN_INT); |
| asr_bcm_write(dd, DMA_IN_INT, val); |
| |
| val = asr_bcm_read(dd, DMA_IN_CTRL); |
| val |= 0x1; |
| asr_bcm_write(dd, DMA_IN_CTRL, val); |
| } |
| |
| void dma_output_start(struct asr_bcm_dev *dd) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, DMA_OUT_INT); |
| asr_bcm_write(dd, DMA_OUT_INT, val); |
| |
| val = asr_bcm_read(dd, DMA_OUT_CTRL); |
| val |= 0x1; |
| asr_bcm_write(dd, DMA_OUT_CTRL, val); |
| |
| return; |
| } |
| |
| void dma_input_stop(struct asr_bcm_dev *dd) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, DMA_IN_CTRL); |
| val &= ~0x1; |
| asr_bcm_write(dd, DMA_IN_CTRL, val); |
| } |
| |
| void dma_output_stop(struct asr_bcm_dev *dd) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, DMA_OUT_CTRL); |
| val &= ~0x1; |
| asr_bcm_write(dd, DMA_OUT_CTRL, val); |
| |
| return; |
| } |
| |
| int dma_wait_input_finish(struct asr_bcm_dev *dd) |
| { |
| uint32_t val, val_ori; |
| int loop; |
| int ret = 0; |
| |
| loop = 10000; |
| while (loop > 0) { |
| val_ori = asr_bcm_read(dd, DMA_IN_INT); |
| val = (val_ori & 0x1); |
| if (val !=0) |
| break; |
| loop--; |
| udelay(1); |
| } |
| |
| if (loop == 0) { |
| ret = -1; |
| } else { |
| ret = 0; |
| } |
| |
| dma_input_stop(dd); |
| |
| val = asr_bcm_read(dd, DMA_IN_INT); |
| asr_bcm_write(dd, DMA_IN_INT, val); |
| |
| return ret; |
| } |
| |
| int dma_wait_output_finish(struct asr_bcm_dev *dd) |
| { |
| uint32_t val, val_ori; |
| int loop; |
| int ret = 0; |
| |
| loop = 10000; |
| while (loop > 0) { |
| val_ori = asr_bcm_read(dd, DMA_OUT_INT); |
| val = (val_ori & 0x1); |
| if (val !=0) |
| break; |
| loop--; |
| udelay(1); |
| } |
| |
| if (loop == 0) { |
| ret = -1; |
| } else { |
| ret = 0; |
| } |
| |
| dma_output_stop(dd); |
| |
| val = asr_bcm_read(dd, DMA_OUT_INT); |
| asr_bcm_write(dd, DMA_OUT_INT, val); |
| |
| return ret; |
| } |
| |
| int adec_engine_hw_reset(struct asr_bcm_dev *dd, \ |
| ADEC_ACC_ENG_T engine) |
| { |
| uint32_t val; |
| int tmp; |
| |
| if (engine == ACC_ENG_ALL) |
| tmp = 0xffff; |
| else |
| tmp = 1 << engine; |
| |
| val = asr_bcm_read(dd, ADEC_CTRL); |
| val |= tmp; |
| asr_bcm_write(dd, ADEC_CTRL, val); |
| val &= ~tmp; |
| asr_bcm_write(dd, ADEC_CTRL, val); |
| |
| return 0; |
| } |
| |
| int abus_set_mode(struct asr_bcm_dev *dd, |
| ABUS_GRP_A_T grp_a_mode, |
| ABUS_GRP_B_T grp_b_mode, |
| ABUS_CROSS_BAR_T input_bar, |
| ABUS_CROSS_BAR_T output_bar) |
| { |
| uint32_t val; |
| |
| val = asr_bcm_read(dd, ABUS_BUS_CTRL); |
| |
| val &= ~(0x77 << 0x4); |
| val |= (grp_a_mode << 0x4) | (grp_b_mode << 0x8); |
| |
| if (input_bar == ABUS_STRAIGHT) { |
| val &= ~(0x1 << 0x0); |
| } else if (input_bar == ABUS_CROSS) { |
| val |= (0x1 << 0x0); |
| } else { |
| return -1; |
| } |
| |
| if (output_bar == ABUS_STRAIGHT) { |
| val &= ~(0x1 << 0x2); |
| } else if (input_bar == ABUS_CROSS) { |
| val |= (0x1 << 0x2); |
| } else { |
| return -1; |
| } |
| |
| asr_bcm_write(dd, ABUS_BUS_CTRL, val); |
| |
| return 0; |
| } |
| |
| static int asr_bcm_clk_sync(struct asr_bcm_dev *dd) |
| { |
| struct clk *bcm_clk; |
| |
| if (dd->clk_synced) |
| return 0; |
| |
| bcm_clk = dd->bcm_clk; |
| /* BCM clk will be disable by CP core, but the enable count is still 1. |
| * Need to sync the clk enable state here and re-enable the clk. |
| */ |
| if (__clk_is_enabled(bcm_clk) == false && |
| __clk_get_enable_count(bcm_clk)) |
| { |
| asr_aes_clk_put(bcm_clk); |
| asr_aes_clk_get(bcm_clk); |
| dd->clk_synced = 1; |
| dev_dbg(dd->dev, "sync bcm clk done\n"); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| static int asr_bcm_dev_get(struct asr_bcm_dev *dd) |
| { |
| mutex_lock(&dd->bcm_lock); |
| |
| asr_bcm_clk_sync(dd); |
| asr_aes_clk_get(dd->bcm_clk); |
| |
| return 0; |
| } |
| |
| static int asr_bcm_dev_put(struct asr_bcm_dev *dd) |
| { |
| asr_aes_clk_put(dd->bcm_clk); |
| |
| mutex_unlock(&dd->bcm_lock); |
| return 0; |
| } |
| |
| static int bcm_hw_init(struct asr_bcm_dev *dd) |
| { |
| /* init */ |
| asr_bcm_write(dd, BIU_SP_CONTROL, 0x6); |
| asr_bcm_write(dd, BIU_SP_INTERRUPT_MASK, 0xFFFFFFFF); |
| asr_bcm_write(dd, BIU_HST_INTERRUPT_MASK, 0xFFFFFFFF); |
| asr_bcm_write(dd, ADEC_INT_MSK, 0xFFFFFFFF); |
| return 0; |
| } |
| |
| static void asr_bcm_hw_init(struct asr_bcm_dev *dd) |
| { |
| bcm_hw_init(dd); |
| adec_engine_hw_reset(dd, ACC_ENG_ALL); |
| } |
| |
| static irqreturn_t asr_bcm_irq(int irq, void *dev_id) |
| { |
| irqreturn_t ret = IRQ_NONE; |
| |
| // TODO irq |
| |
| return ret; |
| } |
| |
| #if defined(CONFIG_OF) |
| static const struct of_device_id asr_bcm_dt_ids[] = { |
| { .compatible = "asr,asr-bcm" }, |
| { /* sentinel */ } |
| }; |
| MODULE_DEVICE_TABLE(of, asr_bcm_dt_ids); |
| #endif |
| |
| static struct asr_bcm_ops bcm_ops = { |
| .dev_get = asr_bcm_dev_get, |
| .dev_put = asr_bcm_dev_put, |
| }; |
| |
| static int asr_bcm_probe(struct platform_device *pdev) |
| { |
| struct asr_bcm_dev *bcm_dd; |
| struct device *dev = &pdev->dev; |
| struct resource *bcm_res; |
| struct device_node *np = NULL; |
| int err = 0, devnum = 0; |
| |
| bcm_dd = devm_kzalloc(&pdev->dev, sizeof(*bcm_dd), GFP_KERNEL); |
| if (bcm_dd == NULL) { |
| err = -ENOMEM; |
| goto res_err; |
| } |
| |
| np = dev->of_node; |
| bcm_dd->dev = dev; |
| bcm_dd->bcm_ops = &bcm_ops; |
| |
| platform_set_drvdata(pdev, bcm_dd); |
| |
| mutex_init(&bcm_dd->bcm_lock); |
| |
| /* Get the base address */ |
| bcm_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| if (!bcm_res) { |
| dev_err(dev, "no MEM resource info\n"); |
| err = -ENODEV; |
| goto res_err; |
| } |
| bcm_dd->phys_base = bcm_res->start; |
| |
| /* Get the IRQ */ |
| bcm_dd->irq = platform_get_irq(pdev, 0); |
| if (bcm_dd->irq < 0) { |
| err = bcm_dd->irq; |
| goto res_err; |
| } |
| err = devm_request_irq(&pdev->dev, bcm_dd->irq, asr_bcm_irq, |
| IRQF_SHARED, "asr-bcm", bcm_dd); |
| if (err) { |
| dev_err(dev, "unable to request bcm irq.\n"); |
| goto no_mem_err; |
| } |
| |
| /* Initializing the clock */ |
| bcm_dd->bcm_clk = devm_clk_get(&pdev->dev, NULL); |
| if (IS_ERR(bcm_dd->bcm_clk)) { |
| dev_err(dev, "clock initialization failed.\n"); |
| err = PTR_ERR(bcm_dd->bcm_clk); |
| goto res_err; |
| } |
| bcm_dd->clk_synced = 0; |
| |
| bcm_dd->io_base = devm_ioremap_resource(&pdev->dev, bcm_res); |
| if (IS_ERR(bcm_dd->io_base)) { |
| dev_err(dev, "can't ioremap\n"); |
| err = PTR_ERR(bcm_dd->io_base); |
| goto res_err; |
| } |
| |
| err = clk_prepare(bcm_dd->bcm_clk); |
| if (err) |
| goto res_err; |
| |
| err = asr_aes_clk_get(bcm_dd->bcm_clk); |
| if (err) |
| goto bcm_clk_unprepare; |
| |
| asr_bcm_hw_init(bcm_dd); |
| |
| #ifdef CONFIG_ASR_BCM_CIPHER |
| if (of_get_property(np, "asr,asr-cipher", NULL)) { |
| err = asr_bcm_cipher_register(bcm_dd); |
| if (err) |
| goto bcm_asr_aes_clk_put; |
| dev_info(dev, "CIPHER engine is initialized\n"); |
| devnum ++; |
| } |
| #endif |
| |
| #ifdef CONFIG_ASR_BCM_SHA |
| if (of_get_property(np, "asr,asr-sha", NULL)) { |
| err = asr_bcm_sha_register(bcm_dd); |
| if (err) |
| goto sha_err; |
| dev_info(dev, "SHA engine is initialized\n"); |
| devnum ++; |
| } |
| #endif |
| |
| if (!devnum) { |
| dev_err(dev, "No BCM device enabled\n"); |
| err = -ENODEV; |
| goto bcm_asr_aes_clk_put; |
| } |
| |
| return 0; |
| |
| #ifdef CONFIG_ASR_BCM_SHA |
| sha_err: |
| #ifdef CONFIG_ASR_BCM_CIPHER |
| asr_bcm_cipher_unregister(bcm_dd); |
| #endif |
| #endif |
| bcm_asr_aes_clk_put: |
| asr_aes_clk_put(bcm_dd->bcm_clk); |
| bcm_clk_unprepare: |
| clk_unprepare(bcm_dd->bcm_clk); |
| res_err: |
| devm_kfree(dev, bcm_dd); |
| no_mem_err: |
| dev_err(dev, "initialization failed.\n"); |
| |
| return err; |
| } |
| |
| static int asr_bcm_remove(struct platform_device *pdev) |
| { |
| struct asr_bcm_dev *bcm_dd; |
| |
| bcm_dd = platform_get_drvdata(pdev); |
| if (!bcm_dd) |
| return -ENODEV; |
| |
| clk_unprepare(bcm_dd->bcm_clk); |
| asr_aes_clk_put(bcm_dd->bcm_clk); |
| |
| #ifdef CONFIG_ASR_BCM_CIPHER |
| asr_bcm_cipher_unregister(bcm_dd); |
| #endif |
| |
| #ifdef CONFIG_ASR_BCM_SHA |
| asr_bcm_sha_unregister(bcm_dd); |
| #endif |
| |
| devm_kfree(bcm_dd->dev, bcm_dd); |
| |
| return 0; |
| } |
| |
| #ifdef CONFIG_PM |
| static int asr_bcm_suspend(struct device *dev) |
| { |
| struct asr_bcm_dev *bcm_dd = dev_get_drvdata(dev); |
| |
| asr_aes_clk_put(bcm_dd->bcm_clk); |
| |
| return 0; |
| } |
| |
| static int asr_bcm_resume(struct device *dev) |
| { |
| struct asr_bcm_dev *bcm_dd = dev_get_drvdata(dev); |
| |
| return asr_aes_clk_get(bcm_dd->bcm_clk); |
| } |
| |
| static const struct dev_pm_ops asr_bcm_pm_ops = { |
| .suspend = asr_bcm_suspend, |
| .resume = asr_bcm_resume, |
| }; |
| #endif /* CONFIG_PM */ |
| |
| static struct platform_driver asr_bcm_driver = { |
| .probe = asr_bcm_probe, |
| .remove = asr_bcm_remove, |
| .driver = { |
| .name = "asr_bcm", |
| #ifdef CONFIG_PM |
| .pm = &asr_bcm_pm_ops, |
| #endif |
| .of_match_table = of_match_ptr(asr_bcm_dt_ids), |
| }, |
| }; |
| |
| static int __init asr_bcm_init(void) |
| { |
| int ret; |
| |
| ret = platform_driver_register(&asr_bcm_driver); |
| |
| return ret; |
| } |
| |
| device_initcall_sync(asr_bcm_init); |
| |
| MODULE_DESCRIPTION("BCM: ASR Trust Engine support."); |
| MODULE_LICENSE("GPL v2"); |
| MODULE_AUTHOR("Yonggan Wang"); |