Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.10 375 lines 8.7 kB view raw
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * Altera SPI driver 4 * 5 * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw> 6 * 7 * Based on spi_s3c24xx.c, which is: 8 * Copyright (c) 2006 Ben Dooks 9 * Copyright (c) 2006 Simtec Electronics 10 * Ben Dooks <ben@simtec.co.uk> 11 */ 12 13#include <linux/interrupt.h> 14#include <linux/errno.h> 15#include <linux/module.h> 16#include <linux/platform_device.h> 17#include <linux/spi/altera.h> 18#include <linux/spi/spi.h> 19#include <linux/io.h> 20#include <linux/of.h> 21 22#define DRV_NAME "spi_altera" 23 24#define ALTERA_SPI_RXDATA 0 25#define ALTERA_SPI_TXDATA 4 26#define ALTERA_SPI_STATUS 8 27#define ALTERA_SPI_CONTROL 12 28#define ALTERA_SPI_SLAVE_SEL 20 29 30#define ALTERA_SPI_STATUS_ROE_MSK 0x8 31#define ALTERA_SPI_STATUS_TOE_MSK 0x10 32#define ALTERA_SPI_STATUS_TMT_MSK 0x20 33#define ALTERA_SPI_STATUS_TRDY_MSK 0x40 34#define ALTERA_SPI_STATUS_RRDY_MSK 0x80 35#define ALTERA_SPI_STATUS_E_MSK 0x100 36 37#define ALTERA_SPI_CONTROL_IROE_MSK 0x8 38#define ALTERA_SPI_CONTROL_ITOE_MSK 0x10 39#define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40 40#define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80 41#define ALTERA_SPI_CONTROL_IE_MSK 0x100 42#define ALTERA_SPI_CONTROL_SSO_MSK 0x400 43 44#define ALTERA_SPI_MAX_CS 32 45 46enum altera_spi_type { 47 ALTERA_SPI_TYPE_UNKNOWN, 48 ALTERA_SPI_TYPE_SUBDEV, 49}; 50 51struct altera_spi { 52 int irq; 53 int len; 54 int count; 55 int bytes_per_word; 56 u32 imr; 57 58 /* data buffers */ 59 const unsigned char *tx; 60 unsigned char *rx; 61 62 struct regmap *regmap; 63 u32 regoff; 64 struct device *dev; 65}; 66 67static const struct regmap_config spi_altera_config = { 68 .reg_bits = 32, 69 .reg_stride = 4, 70 .val_bits = 32, 71 .fast_io = true, 72}; 73 74static int altr_spi_writel(struct altera_spi *hw, unsigned int reg, 75 unsigned int val) 76{ 77 int ret; 78 79 ret = regmap_write(hw->regmap, hw->regoff + reg, val); 80 if (ret) 81 dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n", 82 reg, val, ret); 83 84 return ret; 85} 86 87static int altr_spi_readl(struct altera_spi *hw, unsigned int reg, 88 unsigned int *val) 89{ 90 int ret; 91 92 ret = regmap_read(hw->regmap, hw->regoff + reg, val); 93 if (ret) 94 dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret); 95 96 return ret; 97} 98 99static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev) 100{ 101 return spi_master_get_devdata(sdev->master); 102} 103 104static void altera_spi_set_cs(struct spi_device *spi, bool is_high) 105{ 106 struct altera_spi *hw = altera_spi_to_hw(spi); 107 108 if (is_high) { 109 hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK; 110 altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); 111 altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 0); 112 } else { 113 altr_spi_writel(hw, ALTERA_SPI_SLAVE_SEL, 114 BIT(spi->chip_select)); 115 hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK; 116 altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); 117 } 118} 119 120static void altera_spi_tx_word(struct altera_spi *hw) 121{ 122 unsigned int txd = 0; 123 124 if (hw->tx) { 125 switch (hw->bytes_per_word) { 126 case 1: 127 txd = hw->tx[hw->count]; 128 break; 129 case 2: 130 txd = (hw->tx[hw->count * 2] 131 | (hw->tx[hw->count * 2 + 1] << 8)); 132 break; 133 case 4: 134 txd = (hw->tx[hw->count * 4] 135 | (hw->tx[hw->count * 4 + 1] << 8) 136 | (hw->tx[hw->count * 4 + 2] << 16) 137 | (hw->tx[hw->count * 4 + 3] << 24)); 138 break; 139 140 } 141 } 142 143 altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd); 144} 145 146static void altera_spi_rx_word(struct altera_spi *hw) 147{ 148 unsigned int rxd; 149 150 altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd); 151 if (hw->rx) { 152 switch (hw->bytes_per_word) { 153 case 1: 154 hw->rx[hw->count] = rxd; 155 break; 156 case 2: 157 hw->rx[hw->count * 2] = rxd; 158 hw->rx[hw->count * 2 + 1] = rxd >> 8; 159 break; 160 case 4: 161 hw->rx[hw->count * 4] = rxd; 162 hw->rx[hw->count * 4 + 1] = rxd >> 8; 163 hw->rx[hw->count * 4 + 2] = rxd >> 16; 164 hw->rx[hw->count * 4 + 3] = rxd >> 24; 165 break; 166 167 } 168 } 169 170 hw->count++; 171} 172 173static int altera_spi_txrx(struct spi_master *master, 174 struct spi_device *spi, struct spi_transfer *t) 175{ 176 struct altera_spi *hw = spi_master_get_devdata(master); 177 u32 val; 178 179 hw->tx = t->tx_buf; 180 hw->rx = t->rx_buf; 181 hw->count = 0; 182 hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8); 183 hw->len = t->len / hw->bytes_per_word; 184 185 if (hw->irq >= 0) { 186 /* enable receive interrupt */ 187 hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK; 188 altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); 189 190 /* send the first byte */ 191 altera_spi_tx_word(hw); 192 } else { 193 while (hw->count < hw->len) { 194 altera_spi_tx_word(hw); 195 196 for (;;) { 197 altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); 198 if (val & ALTERA_SPI_STATUS_RRDY_MSK) 199 break; 200 201 cpu_relax(); 202 } 203 204 altera_spi_rx_word(hw); 205 } 206 spi_finalize_current_transfer(master); 207 } 208 209 return t->len; 210} 211 212static irqreturn_t altera_spi_irq(int irq, void *dev) 213{ 214 struct spi_master *master = dev; 215 struct altera_spi *hw = spi_master_get_devdata(master); 216 217 altera_spi_rx_word(hw); 218 219 if (hw->count < hw->len) { 220 altera_spi_tx_word(hw); 221 } else { 222 /* disable receive interrupt */ 223 hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK; 224 altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); 225 226 spi_finalize_current_transfer(master); 227 } 228 229 return IRQ_HANDLED; 230} 231 232static int altera_spi_probe(struct platform_device *pdev) 233{ 234 const struct platform_device_id *platid = platform_get_device_id(pdev); 235 struct altera_spi_platform_data *pdata = dev_get_platdata(&pdev->dev); 236 enum altera_spi_type type = ALTERA_SPI_TYPE_UNKNOWN; 237 struct altera_spi *hw; 238 struct spi_master *master; 239 int err = -ENODEV; 240 u32 val; 241 u16 i; 242 243 master = spi_alloc_master(&pdev->dev, sizeof(struct altera_spi)); 244 if (!master) 245 return err; 246 247 /* setup the master state. */ 248 master->bus_num = pdev->id; 249 250 if (pdata) { 251 if (pdata->num_chipselect > ALTERA_SPI_MAX_CS) { 252 dev_err(&pdev->dev, 253 "Invalid number of chipselect: %hu\n", 254 pdata->num_chipselect); 255 return -EINVAL; 256 } 257 258 master->num_chipselect = pdata->num_chipselect; 259 master->mode_bits = pdata->mode_bits; 260 master->bits_per_word_mask = pdata->bits_per_word_mask; 261 } else { 262 master->num_chipselect = 16; 263 master->mode_bits = SPI_CS_HIGH; 264 master->bits_per_word_mask = SPI_BPW_RANGE_MASK(1, 16); 265 } 266 267 master->dev.of_node = pdev->dev.of_node; 268 master->transfer_one = altera_spi_txrx; 269 master->set_cs = altera_spi_set_cs; 270 271 hw = spi_master_get_devdata(master); 272 hw->dev = &pdev->dev; 273 274 if (platid) 275 type = platid->driver_data; 276 277 /* find and map our resources */ 278 if (type == ALTERA_SPI_TYPE_SUBDEV) { 279 struct resource *regoff; 280 281 hw->regmap = dev_get_regmap(pdev->dev.parent, NULL); 282 if (!hw->regmap) { 283 dev_err(&pdev->dev, "get regmap failed\n"); 284 goto exit; 285 } 286 287 regoff = platform_get_resource(pdev, IORESOURCE_REG, 0); 288 if (regoff) 289 hw->regoff = regoff->start; 290 } else { 291 void __iomem *res; 292 293 res = devm_platform_ioremap_resource(pdev, 0); 294 if (IS_ERR(res)) { 295 err = PTR_ERR(res); 296 goto exit; 297 } 298 299 hw->regmap = devm_regmap_init_mmio(&pdev->dev, res, 300 &spi_altera_config); 301 if (IS_ERR(hw->regmap)) { 302 dev_err(&pdev->dev, "regmap mmio init failed\n"); 303 err = PTR_ERR(hw->regmap); 304 goto exit; 305 } 306 } 307 308 /* program defaults into the registers */ 309 hw->imr = 0; /* disable spi interrupts */ 310 altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr); 311 altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */ 312 altr_spi_readl(hw, ALTERA_SPI_STATUS, &val); 313 if (val & ALTERA_SPI_STATUS_RRDY_MSK) 314 altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */ 315 /* irq is optional */ 316 hw->irq = platform_get_irq(pdev, 0); 317 if (hw->irq >= 0) { 318 err = devm_request_irq(&pdev->dev, hw->irq, altera_spi_irq, 0, 319 pdev->name, master); 320 if (err) 321 goto exit; 322 } 323 324 err = devm_spi_register_master(&pdev->dev, master); 325 if (err) 326 goto exit; 327 328 if (pdata) { 329 for (i = 0; i < pdata->num_devices; i++) { 330 if (!spi_new_device(master, pdata->devices + i)) 331 dev_warn(&pdev->dev, 332 "unable to create SPI device: %s\n", 333 pdata->devices[i].modalias); 334 } 335 } 336 337 dev_info(&pdev->dev, "regoff %u, irq %d\n", hw->regoff, hw->irq); 338 339 return 0; 340exit: 341 spi_master_put(master); 342 return err; 343} 344 345#ifdef CONFIG_OF 346static const struct of_device_id altera_spi_match[] = { 347 { .compatible = "ALTR,spi-1.0", }, 348 { .compatible = "altr,spi-1.0", }, 349 {}, 350}; 351MODULE_DEVICE_TABLE(of, altera_spi_match); 352#endif /* CONFIG_OF */ 353 354static const struct platform_device_id altera_spi_ids[] = { 355 { DRV_NAME, ALTERA_SPI_TYPE_UNKNOWN }, 356 { "subdev_spi_altera", ALTERA_SPI_TYPE_SUBDEV }, 357 { } 358}; 359MODULE_DEVICE_TABLE(platform, altera_spi_ids); 360 361static struct platform_driver altera_spi_driver = { 362 .probe = altera_spi_probe, 363 .driver = { 364 .name = DRV_NAME, 365 .pm = NULL, 366 .of_match_table = of_match_ptr(altera_spi_match), 367 }, 368 .id_table = altera_spi_ids, 369}; 370module_platform_driver(altera_spi_driver); 371 372MODULE_DESCRIPTION("Altera SPI driver"); 373MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>"); 374MODULE_LICENSE("GPL"); 375MODULE_ALIAS("platform:" DRV_NAME);