1abf00907SSerge Semin // SPDX-License-Identifier: GPL-2.0-only 2abf00907SSerge Semin // 3abf00907SSerge Semin // Copyright (C) 2020 BAIKAL ELECTRONICS, JSC 4abf00907SSerge Semin // 5abf00907SSerge Semin // Authors: 6abf00907SSerge Semin // Ramil Zaripov <Ramil.Zaripov@baikalelectronics.ru> 7abf00907SSerge Semin // Serge Semin <Sergey.Semin@baikalelectronics.ru> 8abf00907SSerge Semin // 9abf00907SSerge Semin // Baikal-T1 DW APB SPI and System Boot SPI driver 10abf00907SSerge Semin // 11abf00907SSerge Semin 12abf00907SSerge Semin #include <linux/clk.h> 13abf00907SSerge Semin #include <linux/cpumask.h> 14abf00907SSerge Semin #include <linux/err.h> 15abf00907SSerge Semin #include <linux/interrupt.h> 16abf00907SSerge Semin #include <linux/module.h> 17abf00907SSerge Semin #include <linux/mux/consumer.h> 18abf00907SSerge Semin #include <linux/of.h> 19abf00907SSerge Semin #include <linux/of_platform.h> 20abf00907SSerge Semin #include <linux/platform_device.h> 21abf00907SSerge Semin #include <linux/pm_runtime.h> 22abf00907SSerge Semin #include <linux/property.h> 23abf00907SSerge Semin #include <linux/slab.h> 24abf00907SSerge Semin #include <linux/spi/spi-mem.h> 25abf00907SSerge Semin #include <linux/spi/spi.h> 26abf00907SSerge Semin 27abf00907SSerge Semin #include "spi-dw.h" 28abf00907SSerge Semin 29abf00907SSerge Semin #define BT1_BOOT_DIRMAP 0 30abf00907SSerge Semin #define BT1_BOOT_REGS 1 31abf00907SSerge Semin 32abf00907SSerge Semin struct dw_spi_bt1 { 33abf00907SSerge Semin struct dw_spi dws; 34abf00907SSerge Semin struct clk *clk; 35abf00907SSerge Semin struct mux_control *mux; 36abf00907SSerge Semin 37abf00907SSerge Semin #ifdef CONFIG_SPI_DW_BT1_DIRMAP 38abf00907SSerge Semin void __iomem *map; 39abf00907SSerge Semin resource_size_t map_len; 40abf00907SSerge Semin #endif 41abf00907SSerge Semin }; 42abf00907SSerge Semin #define to_dw_spi_bt1(_ctlr) \ 43abf00907SSerge Semin container_of(spi_controller_get_devdata(_ctlr), struct dw_spi_bt1, dws) 44abf00907SSerge Semin 45abf00907SSerge Semin typedef int (*dw_spi_bt1_init_cb)(struct platform_device *pdev, 46abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1); 47abf00907SSerge Semin 48abf00907SSerge Semin #ifdef CONFIG_SPI_DW_BT1_DIRMAP 49abf00907SSerge Semin 50abf00907SSerge Semin static int dw_spi_bt1_dirmap_create(struct spi_mem_dirmap_desc *desc) 51abf00907SSerge Semin { 52abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller); 53abf00907SSerge Semin 54abf00907SSerge Semin if (!dwsbt1->map || 55abf00907SSerge Semin !dwsbt1->dws.mem_ops.supports_op(desc->mem, &desc->info.op_tmpl)) 56abf00907SSerge Semin return -EOPNOTSUPP; 57abf00907SSerge Semin 58abf00907SSerge Semin /* 59abf00907SSerge Semin * Make sure the requested region doesn't go out of the physically 60abf00907SSerge Semin * mapped flash memory bounds and the operation is read-only. 61abf00907SSerge Semin */ 62abf00907SSerge Semin if (desc->info.offset + desc->info.length > dwsbt1->map_len || 63abf00907SSerge Semin desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN) 64abf00907SSerge Semin return -EOPNOTSUPP; 65abf00907SSerge Semin 66abf00907SSerge Semin return 0; 67abf00907SSerge Semin } 68abf00907SSerge Semin 69abf00907SSerge Semin /* 70abf00907SSerge Semin * Directly mapped SPI memory region is only accessible in the dword chunks. 71abf00907SSerge Semin * That's why we have to create a dedicated read-method to copy data from there 72abf00907SSerge Semin * to the passed buffer. 73abf00907SSerge Semin */ 74abf00907SSerge Semin static void dw_spi_bt1_dirmap_copy_from_map(void *to, void __iomem *from, size_t len) 75abf00907SSerge Semin { 76abf00907SSerge Semin size_t shift, chunk; 77abf00907SSerge Semin u32 data; 78abf00907SSerge Semin 79abf00907SSerge Semin /* 80abf00907SSerge Semin * We split the copying up into the next three stages: unaligned head, 81abf00907SSerge Semin * aligned body, unaligned tail. 82abf00907SSerge Semin */ 83abf00907SSerge Semin shift = (size_t)from & 0x3; 84abf00907SSerge Semin if (shift) { 85abf00907SSerge Semin chunk = min_t(size_t, 4 - shift, len); 86abf00907SSerge Semin data = readl_relaxed(from - shift); 87abf00907SSerge Semin memcpy(to, &data + shift, chunk); 88abf00907SSerge Semin from += chunk; 89abf00907SSerge Semin to += chunk; 90abf00907SSerge Semin len -= chunk; 91abf00907SSerge Semin } 92abf00907SSerge Semin 93abf00907SSerge Semin while (len >= 4) { 94abf00907SSerge Semin data = readl_relaxed(from); 95abf00907SSerge Semin memcpy(to, &data, 4); 96abf00907SSerge Semin from += 4; 97abf00907SSerge Semin to += 4; 98abf00907SSerge Semin len -= 4; 99abf00907SSerge Semin } 100abf00907SSerge Semin 101abf00907SSerge Semin if (len) { 102abf00907SSerge Semin data = readl_relaxed(from); 103abf00907SSerge Semin memcpy(to, &data, len); 104abf00907SSerge Semin } 105abf00907SSerge Semin } 106abf00907SSerge Semin 107abf00907SSerge Semin static ssize_t dw_spi_bt1_dirmap_read(struct spi_mem_dirmap_desc *desc, 108abf00907SSerge Semin u64 offs, size_t len, void *buf) 109abf00907SSerge Semin { 110abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1 = to_dw_spi_bt1(desc->mem->spi->controller); 111abf00907SSerge Semin struct dw_spi *dws = &dwsbt1->dws; 112abf00907SSerge Semin struct spi_mem *mem = desc->mem; 113abf00907SSerge Semin struct dw_spi_cfg cfg; 114abf00907SSerge Semin int ret; 115abf00907SSerge Semin 116abf00907SSerge Semin /* 117abf00907SSerge Semin * Make sure the requested operation length is valid. Truncate the 118abf00907SSerge Semin * length if it's greater than the length of the MMIO region. 119abf00907SSerge Semin */ 120abf00907SSerge Semin if (offs >= dwsbt1->map_len || !len) 121abf00907SSerge Semin return 0; 122abf00907SSerge Semin 123abf00907SSerge Semin len = min_t(size_t, len, dwsbt1->map_len - offs); 124abf00907SSerge Semin 125abf00907SSerge Semin /* Collect the controller configuration required by the operation */ 126abf00907SSerge Semin cfg.tmode = SPI_TMOD_EPROMREAD; 127abf00907SSerge Semin cfg.dfs = 8; 128abf00907SSerge Semin cfg.ndf = 4; 129abf00907SSerge Semin cfg.freq = mem->spi->max_speed_hz; 130abf00907SSerge Semin 131abf00907SSerge Semin /* Make sure the corresponding CS is de-asserted on transmission */ 132abf00907SSerge Semin dw_spi_set_cs(mem->spi, false); 133abf00907SSerge Semin 134abf00907SSerge Semin spi_enable_chip(dws, 0); 135abf00907SSerge Semin 136abf00907SSerge Semin dw_spi_update_config(dws, mem->spi, &cfg); 137abf00907SSerge Semin 138abf00907SSerge Semin spi_umask_intr(dws, SPI_INT_RXFI); 139abf00907SSerge Semin 140abf00907SSerge Semin spi_enable_chip(dws, 1); 141abf00907SSerge Semin 142abf00907SSerge Semin /* 143abf00907SSerge Semin * Enable the transparent mode of the System Boot Controller. 144abf00907SSerge Semin * The SPI core IO should have been locked before calling this method 145abf00907SSerge Semin * so noone would be touching the controller' registers during the 146abf00907SSerge Semin * dirmap operation. 147abf00907SSerge Semin */ 148abf00907SSerge Semin ret = mux_control_select(dwsbt1->mux, BT1_BOOT_DIRMAP); 149abf00907SSerge Semin if (ret) 150abf00907SSerge Semin return ret; 151abf00907SSerge Semin 152abf00907SSerge Semin dw_spi_bt1_dirmap_copy_from_map(buf, dwsbt1->map + offs, len); 153abf00907SSerge Semin 154abf00907SSerge Semin mux_control_deselect(dwsbt1->mux); 155abf00907SSerge Semin 156abf00907SSerge Semin dw_spi_set_cs(mem->spi, true); 157abf00907SSerge Semin 158abf00907SSerge Semin ret = dw_spi_check_status(dws, true); 159abf00907SSerge Semin 160abf00907SSerge Semin return ret ?: len; 161abf00907SSerge Semin } 162abf00907SSerge Semin 163abf00907SSerge Semin #endif /* CONFIG_SPI_DW_BT1_DIRMAP */ 164abf00907SSerge Semin 165abf00907SSerge Semin static int dw_spi_bt1_std_init(struct platform_device *pdev, 166abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1) 167abf00907SSerge Semin { 168abf00907SSerge Semin struct dw_spi *dws = &dwsbt1->dws; 169abf00907SSerge Semin 170abf00907SSerge Semin dws->irq = platform_get_irq(pdev, 0); 171abf00907SSerge Semin if (dws->irq < 0) 172abf00907SSerge Semin return dws->irq; 173abf00907SSerge Semin 174abf00907SSerge Semin dws->num_cs = 4; 175abf00907SSerge Semin 176abf00907SSerge Semin /* 177abf00907SSerge Semin * Baikal-T1 Normal SPI Controllers don't always keep up with full SPI 178abf00907SSerge Semin * bus speed especially when it comes to the concurrent access to the 179abf00907SSerge Semin * APB bus resources. Thus we have no choice but to set a constraint on 180abf00907SSerge Semin * the SPI bus frequency for the memory operations which require to 181abf00907SSerge Semin * read/write data as fast as possible. 182abf00907SSerge Semin */ 183abf00907SSerge Semin dws->max_mem_freq = 20000000U; 184abf00907SSerge Semin 185abf00907SSerge Semin dw_spi_dma_setup_generic(dws); 186abf00907SSerge Semin 187abf00907SSerge Semin return 0; 188abf00907SSerge Semin } 189abf00907SSerge Semin 190abf00907SSerge Semin static int dw_spi_bt1_sys_init(struct platform_device *pdev, 191abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1) 192abf00907SSerge Semin { 193abf00907SSerge Semin struct resource *mem __maybe_unused; 194abf00907SSerge Semin struct dw_spi *dws = &dwsbt1->dws; 195abf00907SSerge Semin 196abf00907SSerge Semin /* 197abf00907SSerge Semin * Baikal-T1 System Boot Controller is equipped with a mux, which 198abf00907SSerge Semin * switches between the directly mapped SPI flash access mode and 199abf00907SSerge Semin * IO access to the DW APB SSI registers. Note the mux controller 200abf00907SSerge Semin * must be setup to preserve the registers being accessible by default 201abf00907SSerge Semin * (on idle-state). 202abf00907SSerge Semin */ 203abf00907SSerge Semin dwsbt1->mux = devm_mux_control_get(&pdev->dev, NULL); 204abf00907SSerge Semin if (IS_ERR(dwsbt1->mux)) 205abf00907SSerge Semin return PTR_ERR(dwsbt1->mux); 206abf00907SSerge Semin 207abf00907SSerge Semin /* 208abf00907SSerge Semin * Directly mapped SPI flash memory is a 16MB MMIO region, which can be 209abf00907SSerge Semin * used to access a peripheral memory device just by reading/writing 210abf00907SSerge Semin * data from/to it. Note the system APB bus will stall during each IO 211abf00907SSerge Semin * from/to the dirmap region until the operation is finished. So don't 212abf00907SSerge Semin * use it concurrently with time-critical tasks (like the SPI memory 213abf00907SSerge Semin * operations implemented in the DW APB SSI driver). 214abf00907SSerge Semin */ 215abf00907SSerge Semin #ifdef CONFIG_SPI_DW_BT1_DIRMAP 216abf00907SSerge Semin mem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 217abf00907SSerge Semin if (mem) { 218abf00907SSerge Semin dwsbt1->map = devm_ioremap_resource(&pdev->dev, mem); 219abf00907SSerge Semin if (!IS_ERR(dwsbt1->map)) { 220abf00907SSerge Semin dwsbt1->map_len = (mem->end - mem->start + 1); 221abf00907SSerge Semin dws->mem_ops.dirmap_create = dw_spi_bt1_dirmap_create; 222abf00907SSerge Semin dws->mem_ops.dirmap_read = dw_spi_bt1_dirmap_read; 223abf00907SSerge Semin } else { 224abf00907SSerge Semin dwsbt1->map = NULL; 225abf00907SSerge Semin } 226abf00907SSerge Semin } 227abf00907SSerge Semin #endif /* CONFIG_SPI_DW_BT1_DIRMAP */ 228abf00907SSerge Semin 229abf00907SSerge Semin /* 230abf00907SSerge Semin * There is no IRQ, no DMA and just one CS available on the System Boot 231abf00907SSerge Semin * SPI controller. 232abf00907SSerge Semin */ 233abf00907SSerge Semin dws->irq = IRQ_NOTCONNECTED; 234abf00907SSerge Semin dws->num_cs = 1; 235abf00907SSerge Semin 236abf00907SSerge Semin /* 237abf00907SSerge Semin * Baikal-T1 System Boot SPI Controller doesn't keep up with the full 238abf00907SSerge Semin * SPI bus speed due to relatively slow APB bus and races for it' 239abf00907SSerge Semin * resources from different CPUs. The situation is worsen by a small 240abf00907SSerge Semin * FIFOs depth (just 8 words). It works better in a single CPU mode 241abf00907SSerge Semin * though, but still tends to be not fast enough at low CPU 242abf00907SSerge Semin * frequencies. 243abf00907SSerge Semin */ 244abf00907SSerge Semin if (num_possible_cpus() > 1) 245abf00907SSerge Semin dws->max_mem_freq = 10000000U; 246abf00907SSerge Semin else 247abf00907SSerge Semin dws->max_mem_freq = 20000000U; 248abf00907SSerge Semin 249abf00907SSerge Semin return 0; 250abf00907SSerge Semin } 251abf00907SSerge Semin 252abf00907SSerge Semin static int dw_spi_bt1_probe(struct platform_device *pdev) 253abf00907SSerge Semin { 254abf00907SSerge Semin dw_spi_bt1_init_cb init_func; 255abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1; 256abf00907SSerge Semin struct resource *mem; 257abf00907SSerge Semin struct dw_spi *dws; 258abf00907SSerge Semin int ret; 259abf00907SSerge Semin 260abf00907SSerge Semin dwsbt1 = devm_kzalloc(&pdev->dev, sizeof(struct dw_spi_bt1), GFP_KERNEL); 261abf00907SSerge Semin if (!dwsbt1) 262abf00907SSerge Semin return -ENOMEM; 263abf00907SSerge Semin 264abf00907SSerge Semin dws = &dwsbt1->dws; 265abf00907SSerge Semin 266abf00907SSerge Semin dws->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem); 267abf00907SSerge Semin if (IS_ERR(dws->regs)) 268abf00907SSerge Semin return PTR_ERR(dws->regs); 269abf00907SSerge Semin 270abf00907SSerge Semin dws->paddr = mem->start; 271abf00907SSerge Semin 272abf00907SSerge Semin dwsbt1->clk = devm_clk_get(&pdev->dev, NULL); 273abf00907SSerge Semin if (IS_ERR(dwsbt1->clk)) 274abf00907SSerge Semin return PTR_ERR(dwsbt1->clk); 275abf00907SSerge Semin 276abf00907SSerge Semin ret = clk_prepare_enable(dwsbt1->clk); 277abf00907SSerge Semin if (ret) 278abf00907SSerge Semin return ret; 279abf00907SSerge Semin 280abf00907SSerge Semin dws->bus_num = pdev->id; 281abf00907SSerge Semin dws->reg_io_width = 4; 282abf00907SSerge Semin dws->max_freq = clk_get_rate(dwsbt1->clk); 283abf00907SSerge Semin if (!dws->max_freq) 284abf00907SSerge Semin goto err_disable_clk; 285abf00907SSerge Semin 286abf00907SSerge Semin init_func = device_get_match_data(&pdev->dev); 287abf00907SSerge Semin ret = init_func(pdev, dwsbt1); 288abf00907SSerge Semin if (ret) 289abf00907SSerge Semin goto err_disable_clk; 290abf00907SSerge Semin 291abf00907SSerge Semin pm_runtime_enable(&pdev->dev); 292abf00907SSerge Semin 293abf00907SSerge Semin ret = dw_spi_add_host(&pdev->dev, dws); 294abf00907SSerge Semin if (ret) 295abf00907SSerge Semin goto err_disable_clk; 296abf00907SSerge Semin 297abf00907SSerge Semin platform_set_drvdata(pdev, dwsbt1); 298abf00907SSerge Semin 299abf00907SSerge Semin return 0; 300abf00907SSerge Semin 301abf00907SSerge Semin err_disable_clk: 302abf00907SSerge Semin clk_disable_unprepare(dwsbt1->clk); 303abf00907SSerge Semin 304abf00907SSerge Semin return ret; 305abf00907SSerge Semin } 306abf00907SSerge Semin 307abf00907SSerge Semin static int dw_spi_bt1_remove(struct platform_device *pdev) 308abf00907SSerge Semin { 309abf00907SSerge Semin struct dw_spi_bt1 *dwsbt1 = platform_get_drvdata(pdev); 310abf00907SSerge Semin 311abf00907SSerge Semin dw_spi_remove_host(&dwsbt1->dws); 312abf00907SSerge Semin 313abf00907SSerge Semin pm_runtime_disable(&pdev->dev); 314abf00907SSerge Semin 315abf00907SSerge Semin clk_disable_unprepare(dwsbt1->clk); 316abf00907SSerge Semin 317abf00907SSerge Semin return 0; 318abf00907SSerge Semin } 319abf00907SSerge Semin 320abf00907SSerge Semin static const struct of_device_id dw_spi_bt1_of_match[] = { 321abf00907SSerge Semin { .compatible = "baikal,bt1-ssi", .data = dw_spi_bt1_std_init}, 322abf00907SSerge Semin { .compatible = "baikal,bt1-sys-ssi", .data = dw_spi_bt1_sys_init}, 323abf00907SSerge Semin { } 324abf00907SSerge Semin }; 325abf00907SSerge Semin MODULE_DEVICE_TABLE(of, dw_spi_bt1_of_match); 326abf00907SSerge Semin 327abf00907SSerge Semin static struct platform_driver dw_spi_bt1_driver = { 328abf00907SSerge Semin .probe = dw_spi_bt1_probe, 329abf00907SSerge Semin .remove = dw_spi_bt1_remove, 330abf00907SSerge Semin .driver = { 331abf00907SSerge Semin .name = "bt1-sys-ssi", 332abf00907SSerge Semin .of_match_table = dw_spi_bt1_of_match, 333abf00907SSerge Semin }, 334abf00907SSerge Semin }; 335abf00907SSerge Semin module_platform_driver(dw_spi_bt1_driver); 336abf00907SSerge Semin 337abf00907SSerge Semin MODULE_AUTHOR("Serge Semin <Sergey.Semin@baikalelectronics.ru>"); 338abf00907SSerge Semin MODULE_DESCRIPTION("Baikal-T1 System Boot SPI Controller driver"); 339abf00907SSerge Semin MODULE_LICENSE("GPL v2"); 340