18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci// 38c2ecf20Sopenharmony_ci// Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 48c2ecf20Sopenharmony_ci// 58c2ecf20Sopenharmony_ci// Authors: 68c2ecf20Sopenharmony_ci// Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru> 78c2ecf20Sopenharmony_ci// Serge Semin <Sergey.Semin@baikalelectronics.ru> 88c2ecf20Sopenharmony_ci// 98c2ecf20Sopenharmony_ci// Baikal-T1 DW APB SPI and System Boot SPI driver 108c2ecf20Sopenharmony_ci// 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/clk.h> 138c2ecf20Sopenharmony_ci#include <linux/cpumask.h> 148c2ecf20Sopenharmony_ci#include <linux/err.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/module.h> 178c2ecf20Sopenharmony_ci#include <linux/mux/consumer.h> 188c2ecf20Sopenharmony_ci#include <linux/of.h> 198c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 208c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 218c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h> 228c2ecf20Sopenharmony_ci#include <linux/property.h> 238c2ecf20Sopenharmony_ci#include <linux/slab.h> 248c2ecf20Sopenharmony_ci#include <linux/spi/spi-mem.h> 258c2ecf20Sopenharmony_ci#include <linux/spi/spi.h> 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include "spi-dw.h" 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define BT1_BOOT_DIRMAP 0 308c2ecf20Sopenharmony_ci#define BT1_BOOT_REGS 1 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistruct dw_spi_bt1 { 338c2ecf20Sopenharmony_ci struct dw_spi dws; 348c2ecf20Sopenharmony_ci struct clk *clk; 358c2ecf20Sopenharmony_ci struct mux_control *mux; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci#ifdef CONFIG_SPI_DW_BT1_DIRMAP 388c2ecf20Sopenharmony_ci void __iomem *map; 398c2ecf20Sopenharmony_ci resource_size_t map_len; 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci#define to_dw_spi_bt1(_ctlr) \ 438c2ecf20Sopenharmony_ci container_of(spi_controller_get_devdata(_ctlr), struct dw_spi_bt1, dws) 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_citypedef int (*dw_spi_bt1_init_cb)(struct platform_device *pdev, 468c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#ifdef CONFIG_SPI_DW_BT1_DIRMAP 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic int dw_spi_bt1_dirmap_create(struct spi_mem_dirmap_desc *desc) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller); 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!dwsbt1->map || 558c2ecf20Sopenharmony_ci !dwsbt1->dws.mem_ops.supports_op(desc->mem, &desc->info.op_tmpl)) 568c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci /* 598c2ecf20Sopenharmony_ci * Make sure the requested region doesn't go out of the physically 608c2ecf20Sopenharmony_ci * mapped flash memory bounds and the operation is read-only. 618c2ecf20Sopenharmony_ci */ 628c2ecf20Sopenharmony_ci if (desc->info.offset + desc->info.length > dwsbt1->map_len || 638c2ecf20Sopenharmony_ci desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) 648c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci return 0; 678c2ecf20Sopenharmony_ci} 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci/* 708c2ecf20Sopenharmony_ci * Directly mapped SPI memory region is only accessible in the dword chunks. 718c2ecf20Sopenharmony_ci * That's why we have to create a dedicated read-method to copy data from there 728c2ecf20Sopenharmony_ci * to the passed buffer. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_cistatic void dw_spi_bt1_dirmap_copy_from_map(void *to, void __iomem *from, size_t len) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci size_t shift, chunk; 778c2ecf20Sopenharmony_ci u32 data; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* 808c2ecf20Sopenharmony_ci * We split the copying up into the next three stages: unaligned head, 818c2ecf20Sopenharmony_ci * aligned body, unaligned tail. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_ci shift = (size_t)from & 0x3; 848c2ecf20Sopenharmony_ci if (shift) { 858c2ecf20Sopenharmony_ci chunk = min_t(size_t, 4 - shift, len); 868c2ecf20Sopenharmony_ci data = readl_relaxed(from - shift); 878c2ecf20Sopenharmony_ci memcpy(to, (char *)&data + shift, chunk); 888c2ecf20Sopenharmony_ci from += chunk; 898c2ecf20Sopenharmony_ci to += chunk; 908c2ecf20Sopenharmony_ci len -= chunk; 918c2ecf20Sopenharmony_ci } 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci while (len >= 4) { 948c2ecf20Sopenharmony_ci data = readl_relaxed(from); 958c2ecf20Sopenharmony_ci memcpy(to, &data, 4); 968c2ecf20Sopenharmony_ci from += 4; 978c2ecf20Sopenharmony_ci to += 4; 988c2ecf20Sopenharmony_ci len -= 4; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (len) { 1028c2ecf20Sopenharmony_ci data = readl_relaxed(from); 1038c2ecf20Sopenharmony_ci memcpy(to, &data, len); 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_cistatic ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc, 1088c2ecf20Sopenharmony_ci u64 offs, size_t len, void *buf) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller); 1118c2ecf20Sopenharmony_ci struct dw_spi *dws = &dwsbt1->dws; 1128c2ecf20Sopenharmony_ci struct spi_mem *mem = desc->mem; 1138c2ecf20Sopenharmony_ci struct dw_spi_cfg cfg; 1148c2ecf20Sopenharmony_ci int ret; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * Make sure the requested operation length is valid. Truncate the 1188c2ecf20Sopenharmony_ci * length if it's greater than the length of the MMIO region. 1198c2ecf20Sopenharmony_ci */ 1208c2ecf20Sopenharmony_ci if (offs >= dwsbt1->map_len || !len) 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci len = min_t(size_t, len, dwsbt1->map_len - offs); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci /* Collect the controller configuration required by the operation */ 1268c2ecf20Sopenharmony_ci cfg.tmode = SPI_TMOD_EPROMREAD; 1278c2ecf20Sopenharmony_ci cfg.dfs = 8; 1288c2ecf20Sopenharmony_ci cfg.ndf = 4; 1298c2ecf20Sopenharmony_ci cfg.freq = mem->spi->max_speed_hz; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci /* Make sure the corresponding CS is de-asserted on transmission */ 1328c2ecf20Sopenharmony_ci dw_spi_set_cs(mem->spi, false); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci spi_enable_chip(dws, 0); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci dw_spi_update_config(dws, mem->spi, &cfg); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci spi_umask_intr(dws, SPI_INT_RXFI); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci spi_enable_chip(dws, 1); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* 1438c2ecf20Sopenharmony_ci * Enable the transparent mode of the System Boot Controller. 1448c2ecf20Sopenharmony_ci * The SPI core IO should have been locked before calling this method 1458c2ecf20Sopenharmony_ci * so noone would be touching the controller' registers during the 1468c2ecf20Sopenharmony_ci * dirmap operation. 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci ret = mux_control_select(dwsbt1->mux, BT1_BOOT_DIRMAP); 1498c2ecf20Sopenharmony_ci if (ret) 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci dw_spi_bt1_dirmap_copy_from_map(buf, dwsbt1->map + offs, len); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci mux_control_deselect(dwsbt1->mux); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci dw_spi_set_cs(mem->spi, true); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci ret = dw_spi_check_status(dws, true); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci return ret ?: len; 1618c2ecf20Sopenharmony_ci} 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci#endif /* CONFIG_SPI_DW_BT1_DIRMAP */ 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_cistatic int dw_spi_bt1_std_init(struct platform_device *pdev, 1668c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci struct dw_spi *dws = &dwsbt1->dws; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci dws->irq = platform_get_irq(pdev, 0); 1718c2ecf20Sopenharmony_ci if (dws->irq < 0) 1728c2ecf20Sopenharmony_ci return dws->irq; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci dws->num_cs = 4; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci /* 1778c2ecf20Sopenharmony_ci * Baikal-T1 Normal SPI Controllers don't always keep up with full SPI 1788c2ecf20Sopenharmony_ci * bus speed especially when it comes to the concurrent access to the 1798c2ecf20Sopenharmony_ci * APB bus resources. Thus we have no choice but to set a constraint on 1808c2ecf20Sopenharmony_ci * the SPI bus frequency for the memory operations which require to 1818c2ecf20Sopenharmony_ci * read/write data as fast as possible. 1828c2ecf20Sopenharmony_ci */ 1838c2ecf20Sopenharmony_ci dws->max_mem_freq = 20000000U; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci dw_spi_dma_setup_generic(dws); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci return 0; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic int dw_spi_bt1_sys_init(struct platform_device *pdev, 1918c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct resource *mem __maybe_unused; 1948c2ecf20Sopenharmony_ci struct dw_spi *dws = &dwsbt1->dws; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci /* 1978c2ecf20Sopenharmony_ci * Baikal-T1 System Boot Controller is equipped with a mux, which 1988c2ecf20Sopenharmony_ci * switches between the directly mapped SPI flash access mode and 1998c2ecf20Sopenharmony_ci * IO access to the DW APB SSI registers. Note the mux controller 2008c2ecf20Sopenharmony_ci * must be setup to preserve the registers being accessible by default 2018c2ecf20Sopenharmony_ci * (on idle-state). 2028c2ecf20Sopenharmony_ci */ 2038c2ecf20Sopenharmony_ci dwsbt1->mux = devm_mux_control_get(&pdev->dev, NULL); 2048c2ecf20Sopenharmony_ci if (IS_ERR(dwsbt1->mux)) 2058c2ecf20Sopenharmony_ci return PTR_ERR(dwsbt1->mux); 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci /* 2088c2ecf20Sopenharmony_ci * Directly mapped SPI flash memory is a 16MB MMIO region, which can be 2098c2ecf20Sopenharmony_ci * used to access a peripheral memory device just by reading/writing 2108c2ecf20Sopenharmony_ci * data from/to it. Note the system APB bus will stall during each IO 2118c2ecf20Sopenharmony_ci * from/to the dirmap region until the operation is finished. So don't 2128c2ecf20Sopenharmony_ci * use it concurrently with time-critical tasks (like the SPI memory 2138c2ecf20Sopenharmony_ci * operations implemented in the DW APB SSI driver). 2148c2ecf20Sopenharmony_ci */ 2158c2ecf20Sopenharmony_ci#ifdef CONFIG_SPI_DW_BT1_DIRMAP 2168c2ecf20Sopenharmony_ci mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 2178c2ecf20Sopenharmony_ci if (mem) { 2188c2ecf20Sopenharmony_ci dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem); 2198c2ecf20Sopenharmony_ci if (!IS_ERR(dwsbt1->map)) { 2208c2ecf20Sopenharmony_ci dwsbt1->map_len = (mem->end - mem->start + 1); 2218c2ecf20Sopenharmony_ci dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create; 2228c2ecf20Sopenharmony_ci dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read; 2238c2ecf20Sopenharmony_ci } else { 2248c2ecf20Sopenharmony_ci dwsbt1->map = NULL; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci#endif /* CONFIG_SPI_DW_BT1_DIRMAP */ 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* 2308c2ecf20Sopenharmony_ci * There is no IRQ, no DMA and just one CS available on the System Boot 2318c2ecf20Sopenharmony_ci * SPI controller. 2328c2ecf20Sopenharmony_ci */ 2338c2ecf20Sopenharmony_ci dws->irq = IRQ_NOTCONNECTED; 2348c2ecf20Sopenharmony_ci dws->num_cs = 1; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci /* 2378c2ecf20Sopenharmony_ci * Baikal-T1 System Boot SPI Controller doesn't keep up with the full 2388c2ecf20Sopenharmony_ci * SPI bus speed due to relatively slow APB bus and races for it' 2398c2ecf20Sopenharmony_ci * resources from different CPUs. The situation is worsen by a small 2408c2ecf20Sopenharmony_ci * FIFOs depth (just 8 words). It works better in a single CPU mode 2418c2ecf20Sopenharmony_ci * though, but still tends to be not fast enough at low CPU 2428c2ecf20Sopenharmony_ci * frequencies. 2438c2ecf20Sopenharmony_ci */ 2448c2ecf20Sopenharmony_ci if (num_possible_cpus() > 1) 2458c2ecf20Sopenharmony_ci dws->max_mem_freq = 10000000U; 2468c2ecf20Sopenharmony_ci else 2478c2ecf20Sopenharmony_ci dws->max_mem_freq = 20000000U; 2488c2ecf20Sopenharmony_ci 2498c2ecf20Sopenharmony_ci return 0; 2508c2ecf20Sopenharmony_ci} 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cistatic int dw_spi_bt1_probe(struct platform_device *pdev) 2538c2ecf20Sopenharmony_ci{ 2548c2ecf20Sopenharmony_ci dw_spi_bt1_init_cb init_func; 2558c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1; 2568c2ecf20Sopenharmony_ci struct resource *mem; 2578c2ecf20Sopenharmony_ci struct dw_spi *dws; 2588c2ecf20Sopenharmony_ci int ret; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci dwsbt1 = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_bt1), GFP_KERNEL); 2618c2ecf20Sopenharmony_ci if (!dwsbt1) 2628c2ecf20Sopenharmony_ci return -ENOMEM; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci dws = &dwsbt1->dws; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); 2678c2ecf20Sopenharmony_ci if (IS_ERR(dws->regs)) 2688c2ecf20Sopenharmony_ci return PTR_ERR(dws->regs); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci dws->paddr = mem->start; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci dwsbt1->clk = devm_clk_get(&pdev->dev, NULL); 2738c2ecf20Sopenharmony_ci if (IS_ERR(dwsbt1->clk)) 2748c2ecf20Sopenharmony_ci return PTR_ERR(dwsbt1->clk); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci ret = clk_prepare_enable(dwsbt1->clk); 2778c2ecf20Sopenharmony_ci if (ret) 2788c2ecf20Sopenharmony_ci return ret; 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci dws->bus_num = pdev->id; 2818c2ecf20Sopenharmony_ci dws->reg_io_width = 4; 2828c2ecf20Sopenharmony_ci dws->max_freq = clk_get_rate(dwsbt1->clk); 2838c2ecf20Sopenharmony_ci if (!dws->max_freq) { 2848c2ecf20Sopenharmony_ci ret = -EINVAL; 2858c2ecf20Sopenharmony_ci goto err_disable_clk; 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci init_func = device_get_match_data(&pdev->dev); 2898c2ecf20Sopenharmony_ci ret = init_func(pdev, dwsbt1); 2908c2ecf20Sopenharmony_ci if (ret) 2918c2ecf20Sopenharmony_ci goto err_disable_clk; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci pm_runtime_enable(&pdev->dev); 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci ret = dw_spi_add_host(&pdev->dev, dws); 2968c2ecf20Sopenharmony_ci if (ret) { 2978c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 2988c2ecf20Sopenharmony_ci goto err_disable_clk; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, dwsbt1); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_cierr_disable_clk: 3068c2ecf20Sopenharmony_ci clk_disable_unprepare(dwsbt1->clk); 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int dw_spi_bt1_remove(struct platform_device *pdev) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci dw_spi_remove_host(&dwsbt1->dws); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci pm_runtime_disable(&pdev->dev); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci clk_disable_unprepare(dwsbt1->clk); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci return 0; 3228c2ecf20Sopenharmony_ci} 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_cistatic const struct of_device_id dw_spi_bt1_of_match[] = { 3258c2ecf20Sopenharmony_ci { .compatible = "baikal,bt1-ssi", .data = dw_spi_bt1_std_init}, 3268c2ecf20Sopenharmony_ci { .compatible = "baikal,bt1-sys-ssi", .data = dw_spi_bt1_sys_init}, 3278c2ecf20Sopenharmony_ci { } 3288c2ecf20Sopenharmony_ci}; 3298c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, dw_spi_bt1_of_match); 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic struct platform_driver dw_spi_bt1_driver = { 3328c2ecf20Sopenharmony_ci .probe = dw_spi_bt1_probe, 3338c2ecf20Sopenharmony_ci .remove = dw_spi_bt1_remove, 3348c2ecf20Sopenharmony_ci .driver = { 3358c2ecf20Sopenharmony_ci .name = "bt1-sys-ssi", 3368c2ecf20Sopenharmony_ci .of_match_table = dw_spi_bt1_of_match, 3378c2ecf20Sopenharmony_ci }, 3388c2ecf20Sopenharmony_ci}; 3398c2ecf20Sopenharmony_cimodule_platform_driver(dw_spi_bt1_driver); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ciMODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 3428c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Baikal-T1 System Boot SPI Controller driver"); 3438c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 344