0007-spi-add-rb4xx-cpld-driver.patch 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548
  1. From bb8d2a4ebf63bc2f04f15a28c92652700416ff83 Mon Sep 17 00:00:00 2001
  2. From: Phil Sutter <phil@nwl.cc>
  3. Date: Tue, 13 May 2014 00:20:04 +0200
  4. Subject: [PATCH] spi: add rb4xx cpld driver
  5. ---
  6. arch/mips/include/asm/mach-ath79/rb4xx_cpld.h | 48 +++
  7. drivers/spi/Kconfig | 7 +
  8. drivers/spi/Makefile | 1 +
  9. drivers/spi/spi-rb4xx-cpld.c | 441 ++++++++++++++++++++++++++
  10. 4 files changed, 497 insertions(+)
  11. create mode 100644 arch/mips/include/asm/mach-ath79/rb4xx_cpld.h
  12. create mode 100644 drivers/spi/spi-rb4xx-cpld.c
  13. diff --git a/arch/mips/include/asm/mach-ath79/rb4xx_cpld.h b/arch/mips/include/asm/mach-ath79/rb4xx_cpld.h
  14. new file mode 100644
  15. index 0000000..5b17e94
  16. --- /dev/null
  17. +++ b/arch/mips/include/asm/mach-ath79/rb4xx_cpld.h
  18. @@ -0,0 +1,48 @@
  19. +/*
  20. + * SPI driver definitions for the CPLD chip on the Mikrotik RB4xx boards
  21. + *
  22. + * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
  23. + *
  24. + * This file was based on the patches for Linux 2.6.27.39 published by
  25. + * MikroTik for their RouterBoard 4xx series devices.
  26. + *
  27. + * This program is free software; you can redistribute it and/or modify it
  28. + * under the terms of the GNU General Public License version 2 as published
  29. + * by the Free Software Foundation.
  30. + */
  31. +
  32. +#define CPLD_GPIO_nLED1 0
  33. +#define CPLD_GPIO_nLED2 1
  34. +#define CPLD_GPIO_nLED3 2
  35. +#define CPLD_GPIO_nLED4 3
  36. +#define CPLD_GPIO_FAN 4
  37. +#define CPLD_GPIO_ALE 5
  38. +#define CPLD_GPIO_CLE 6
  39. +#define CPLD_GPIO_nCE 7
  40. +#define CPLD_GPIO_nLED5 8
  41. +
  42. +#define CPLD_NUM_GPIOS 9
  43. +
  44. +#define CPLD_CFG_nLED1 BIT(CPLD_GPIO_nLED1)
  45. +#define CPLD_CFG_nLED2 BIT(CPLD_GPIO_nLED2)
  46. +#define CPLD_CFG_nLED3 BIT(CPLD_GPIO_nLED3)
  47. +#define CPLD_CFG_nLED4 BIT(CPLD_GPIO_nLED4)
  48. +#define CPLD_CFG_FAN BIT(CPLD_GPIO_FAN)
  49. +#define CPLD_CFG_ALE BIT(CPLD_GPIO_ALE)
  50. +#define CPLD_CFG_CLE BIT(CPLD_GPIO_CLE)
  51. +#define CPLD_CFG_nCE BIT(CPLD_GPIO_nCE)
  52. +#define CPLD_CFG_nLED5 BIT(CPLD_GPIO_nLED5)
  53. +
  54. +struct rb4xx_cpld_platform_data {
  55. + unsigned gpio_base;
  56. +};
  57. +
  58. +extern int rb4xx_cpld_change_cfg(unsigned mask, unsigned value);
  59. +extern int rb4xx_cpld_read(unsigned char *rx_buf,
  60. + const unsigned char *verify_buf,
  61. + unsigned cnt);
  62. +extern int rb4xx_cpld_read_from(unsigned addr,
  63. + unsigned char *rx_buf,
  64. + const unsigned char *verify_buf,
  65. + unsigned cnt);
  66. +extern int rb4xx_cpld_write(const unsigned char *buf, unsigned count);
  67. diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
  68. index 721f3a7..dbd7e98 100644
  69. --- a/drivers/spi/Kconfig
  70. +++ b/drivers/spi/Kconfig
  71. @@ -577,6 +577,13 @@ config SPI_TLE62X0
  72. sysfs interface, with each line presented as a kind of GPIO
  73. exposing both switch control and diagnostic feedback.
  74. +config SPI_RB4XX_CPLD
  75. + tristate "MikroTik RB4XX CPLD driver"
  76. + depends on ATH79_MACH_RB4XX
  77. + help
  78. + SPI driver for the Xilinx CPLD chip present on the
  79. + MikroTik RB4xx boards.
  80. +
  81. #
  82. # Add new SPI protocol masters in alphabetical order above this line
  83. #
  84. diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
  85. index e738c7a..50913ae 100644
  86. --- a/drivers/spi/Makefile
  87. +++ b/drivers/spi/Makefile
  88. @@ -60,6 +60,7 @@ spi-pxa2xx-platform-$(CONFIG_SPI_PXA2XX_DMA) += spi-pxa2xx-dma.o
  89. obj-$(CONFIG_SPI_PXA2XX) += spi-pxa2xx-platform.o
  90. obj-$(CONFIG_SPI_PXA2XX_PCI) += spi-pxa2xx-pci.o
  91. obj-$(CONFIG_SPI_RB4XX) += spi-rb4xx.o
  92. +obj-$(CONFIG_SPI_RB4XX_CPLD) += spi-rb4xx-cpld.o
  93. obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
  94. obj-$(CONFIG_SPI_S3C24XX) += spi-s3c24xx-hw.o
  95. spi-s3c24xx-hw-y := spi-s3c24xx.o
  96. diff --git a/drivers/spi/spi-rb4xx-cpld.c b/drivers/spi/spi-rb4xx-cpld.c
  97. new file mode 100644
  98. index 0000000..a8d5282
  99. --- /dev/null
  100. +++ b/drivers/spi/spi-rb4xx-cpld.c
  101. @@ -0,0 +1,441 @@
  102. +/*
  103. + * SPI driver for the CPLD chip on the Mikrotik RB4xx boards
  104. + *
  105. + * Copyright (C) 2010 Gabor Juhos <juhosg@openwrt.org>
  106. + *
  107. + * This file was based on the patches for Linux 2.6.27.39 published by
  108. + * MikroTik for their RouterBoard 4xx series devices.
  109. + *
  110. + * This program is free software; you can redistribute it and/or modify it
  111. + * under the terms of the GNU General Public License version 2 as published
  112. + * by the Free Software Foundation.
  113. + */
  114. +
  115. +#include <linux/types.h>
  116. +#include <linux/kernel.h>
  117. +#include <linux/module.h>
  118. +#include <linux/init.h>
  119. +#include <linux/module.h>
  120. +#include <linux/device.h>
  121. +#include <linux/bitops.h>
  122. +#include <linux/spi/spi.h>
  123. +#include <linux/gpio.h>
  124. +#include <linux/slab.h>
  125. +
  126. +#include <asm/mach-ath79/rb4xx_cpld.h>
  127. +
  128. +#define DRV_NAME "spi-rb4xx-cpld"
  129. +#define DRV_DESC "RB4xx CPLD driver"
  130. +#define DRV_VERSION "0.1.0"
  131. +
  132. +#define CPLD_CMD_WRITE_NAND 0x08 /* send cmd, n x send data, send indle */
  133. +#define CPLD_CMD_WRITE_CFG 0x09 /* send cmd, n x send cfg */
  134. +#define CPLD_CMD_READ_NAND 0x0a /* send cmd, send idle, n x read data */
  135. +#define CPLD_CMD_READ_FAST 0x0b /* send cmd, 4 x idle, n x read data */
  136. +#define CPLD_CMD_LED5_ON 0x0c /* send cmd */
  137. +#define CPLD_CMD_LED5_OFF 0x0d /* send cmd */
  138. +
  139. +struct rb4xx_cpld {
  140. + struct spi_device *spi;
  141. + struct mutex lock;
  142. + struct gpio_chip chip;
  143. + unsigned int config;
  144. +};
  145. +
  146. +static struct rb4xx_cpld *rb4xx_cpld;
  147. +
  148. +static inline struct rb4xx_cpld *gpio_to_cpld(struct gpio_chip *chip)
  149. +{
  150. + return container_of(chip, struct rb4xx_cpld, chip);
  151. +}
  152. +
  153. +static int rb4xx_cpld_write_cmd(struct rb4xx_cpld *cpld, unsigned char cmd)
  154. +{
  155. + struct spi_transfer t[1];
  156. + struct spi_message m;
  157. + unsigned char tx_buf[1];
  158. + int err;
  159. +
  160. + spi_message_init(&m);
  161. + memset(&t, 0, sizeof(t));
  162. +
  163. + t[0].tx_buf = tx_buf;
  164. + t[0].len = sizeof(tx_buf);
  165. + spi_message_add_tail(&t[0], &m);
  166. +
  167. + tx_buf[0] = cmd;
  168. +
  169. + err = spi_sync(cpld->spi, &m);
  170. + return err;
  171. +}
  172. +
  173. +static int rb4xx_cpld_write_cfg(struct rb4xx_cpld *cpld, unsigned char config)
  174. +{
  175. + struct spi_transfer t[1];
  176. + struct spi_message m;
  177. + unsigned char cmd[2];
  178. + int err;
  179. +
  180. + spi_message_init(&m);
  181. + memset(&t, 0, sizeof(t));
  182. +
  183. + t[0].tx_buf = cmd;
  184. + t[0].len = sizeof(cmd);
  185. + spi_message_add_tail(&t[0], &m);
  186. +
  187. + cmd[0] = CPLD_CMD_WRITE_CFG;
  188. + cmd[1] = config;
  189. +
  190. + err = spi_sync(cpld->spi, &m);
  191. + return err;
  192. +}
  193. +
  194. +static int __rb4xx_cpld_change_cfg(struct rb4xx_cpld *cpld, unsigned mask,
  195. + unsigned value)
  196. +{
  197. + unsigned int config;
  198. + int err;
  199. +
  200. + config = cpld->config & ~mask;
  201. + config |= value;
  202. +
  203. + if ((cpld->config ^ config) & 0xff) {
  204. + err = rb4xx_cpld_write_cfg(cpld, config);
  205. + if (err)
  206. + return err;
  207. + }
  208. +
  209. + if ((cpld->config ^ config) & CPLD_CFG_nLED5) {
  210. + err = rb4xx_cpld_write_cmd(cpld, (value) ? CPLD_CMD_LED5_ON :
  211. + CPLD_CMD_LED5_OFF);
  212. + if (err)
  213. + return err;
  214. + }
  215. +
  216. + cpld->config = config;
  217. + return 0;
  218. +}
  219. +
  220. +int rb4xx_cpld_change_cfg(unsigned mask, unsigned value)
  221. +{
  222. + int ret;
  223. +
  224. + if (rb4xx_cpld == NULL)
  225. + return -ENODEV;
  226. +
  227. + mutex_lock(&rb4xx_cpld->lock);
  228. + ret = __rb4xx_cpld_change_cfg(rb4xx_cpld, mask, value);
  229. + mutex_unlock(&rb4xx_cpld->lock);
  230. +
  231. + return ret;
  232. +}
  233. +EXPORT_SYMBOL_GPL(rb4xx_cpld_change_cfg);
  234. +
  235. +int rb4xx_cpld_read_from(unsigned addr, unsigned char *rx_buf,
  236. + const unsigned char *verify_buf, unsigned count)
  237. +{
  238. + const unsigned char cmd[5] = {
  239. + CPLD_CMD_READ_FAST,
  240. + (addr >> 16) & 0xff,
  241. + (addr >> 8) & 0xff,
  242. + addr & 0xff,
  243. + 0
  244. + };
  245. + struct spi_transfer t[2] = {
  246. + {
  247. + .tx_buf = &cmd,
  248. + .len = 5,
  249. + },
  250. + {
  251. + .tx_buf = verify_buf,
  252. + .rx_buf = rx_buf,
  253. + .len = count,
  254. + .verify = (verify_buf != NULL),
  255. + },
  256. + };
  257. + struct spi_message m;
  258. +
  259. + if (rb4xx_cpld == NULL)
  260. + return -ENODEV;
  261. +
  262. + spi_message_init(&m);
  263. + m.fast_read = 1;
  264. + spi_message_add_tail(&t[0], &m);
  265. + spi_message_add_tail(&t[1], &m);
  266. + return spi_sync(rb4xx_cpld->spi, &m);
  267. +}
  268. +EXPORT_SYMBOL_GPL(rb4xx_cpld_read_from);
  269. +
  270. +#if 0
  271. +int rb4xx_cpld_read(unsigned char *buf, unsigned char *verify_buf,
  272. + unsigned count)
  273. +{
  274. + struct spi_transfer t[2];
  275. + struct spi_message m;
  276. + unsigned char cmd[2];
  277. +
  278. + if (rb4xx_cpld == NULL)
  279. + return -ENODEV;
  280. +
  281. + spi_message_init(&m);
  282. + memset(&t, 0, sizeof(t));
  283. +
  284. + /* send command */
  285. + t[0].tx_buf = cmd;
  286. + t[0].len = sizeof(cmd);
  287. + spi_message_add_tail(&t[0], &m);
  288. +
  289. + cmd[0] = CPLD_CMD_READ_NAND;
  290. + cmd[1] = 0;
  291. +
  292. + /* read data */
  293. + t[1].rx_buf = buf;
  294. + t[1].len = count;
  295. + spi_message_add_tail(&t[1], &m);
  296. +
  297. + return spi_sync(rb4xx_cpld->spi, &m);
  298. +}
  299. +#else
  300. +int rb4xx_cpld_read(unsigned char *rx_buf, const unsigned char *verify_buf,
  301. + unsigned count)
  302. +{
  303. + static const unsigned char cmd[2] = { CPLD_CMD_READ_NAND, 0 };
  304. + struct spi_transfer t[2] = {
  305. + {
  306. + .tx_buf = &cmd,
  307. + .len = 2,
  308. + }, {
  309. + .tx_buf = verify_buf,
  310. + .rx_buf = rx_buf,
  311. + .len = count,
  312. + .verify = (verify_buf != NULL),
  313. + },
  314. + };
  315. + struct spi_message m;
  316. +
  317. + if (rb4xx_cpld == NULL)
  318. + return -ENODEV;
  319. +
  320. + spi_message_init(&m);
  321. + spi_message_add_tail(&t[0], &m);
  322. + spi_message_add_tail(&t[1], &m);
  323. + return spi_sync(rb4xx_cpld->spi, &m);
  324. +}
  325. +#endif
  326. +EXPORT_SYMBOL_GPL(rb4xx_cpld_read);
  327. +
  328. +int rb4xx_cpld_write(const unsigned char *buf, unsigned count)
  329. +{
  330. +#if 0
  331. + struct spi_transfer t[3];
  332. + struct spi_message m;
  333. + unsigned char cmd[1];
  334. +
  335. + if (rb4xx_cpld == NULL)
  336. + return -ENODEV;
  337. +
  338. + memset(&t, 0, sizeof(t));
  339. + spi_message_init(&m);
  340. +
  341. + /* send command */
  342. + t[0].tx_buf = cmd;
  343. + t[0].len = sizeof(cmd);
  344. + spi_message_add_tail(&t[0], &m);
  345. +
  346. + cmd[0] = CPLD_CMD_WRITE_NAND;
  347. +
  348. + /* write data */
  349. + t[1].tx_buf = buf;
  350. + t[1].len = count;
  351. + spi_message_add_tail(&t[1], &m);
  352. +
  353. + /* send idle */
  354. + t[2].len = 1;
  355. + spi_message_add_tail(&t[2], &m);
  356. +
  357. + return spi_sync(rb4xx_cpld->spi, &m);
  358. +#else
  359. + static const unsigned char cmd = CPLD_CMD_WRITE_NAND;
  360. + struct spi_transfer t[3] = {
  361. + {
  362. + .tx_buf = &cmd,
  363. + .len = 1,
  364. + }, {
  365. + .tx_buf = buf,
  366. + .len = count,
  367. + .fast_write = 1,
  368. + }, {
  369. + .len = 1,
  370. + .fast_write = 1,
  371. + },
  372. + };
  373. + struct spi_message m;
  374. +
  375. + if (rb4xx_cpld == NULL)
  376. + return -ENODEV;
  377. +
  378. + spi_message_init(&m);
  379. + spi_message_add_tail(&t[0], &m);
  380. + spi_message_add_tail(&t[1], &m);
  381. + spi_message_add_tail(&t[2], &m);
  382. + return spi_sync(rb4xx_cpld->spi, &m);
  383. +#endif
  384. +}
  385. +EXPORT_SYMBOL_GPL(rb4xx_cpld_write);
  386. +
  387. +static int rb4xx_cpld_gpio_get(struct gpio_chip *chip, unsigned offset)
  388. +{
  389. + struct rb4xx_cpld *cpld = gpio_to_cpld(chip);
  390. + int ret;
  391. +
  392. + mutex_lock(&cpld->lock);
  393. + ret = (cpld->config >> offset) & 1;
  394. + mutex_unlock(&cpld->lock);
  395. +
  396. + return ret;
  397. +}
  398. +
  399. +static void rb4xx_cpld_gpio_set(struct gpio_chip *chip, unsigned offset,
  400. + int value)
  401. +{
  402. + struct rb4xx_cpld *cpld = gpio_to_cpld(chip);
  403. +
  404. + mutex_lock(&cpld->lock);
  405. + __rb4xx_cpld_change_cfg(cpld, (1 << offset), !!value << offset);
  406. + mutex_unlock(&cpld->lock);
  407. +}
  408. +
  409. +static int rb4xx_cpld_gpio_direction_input(struct gpio_chip *chip,
  410. + unsigned offset)
  411. +{
  412. + return -EOPNOTSUPP;
  413. +}
  414. +
  415. +static int rb4xx_cpld_gpio_direction_output(struct gpio_chip *chip,
  416. + unsigned offset,
  417. + int value)
  418. +{
  419. + struct rb4xx_cpld *cpld = gpio_to_cpld(chip);
  420. + int ret;
  421. +
  422. + mutex_lock(&cpld->lock);
  423. + ret = __rb4xx_cpld_change_cfg(cpld, (1 << offset), !!value << offset);
  424. + mutex_unlock(&cpld->lock);
  425. +
  426. + return ret;
  427. +}
  428. +
  429. +static int rb4xx_cpld_gpio_init(struct rb4xx_cpld *cpld, unsigned int base)
  430. +{
  431. + int err;
  432. +
  433. + /* init config */
  434. + cpld->config = CPLD_CFG_nLED1 | CPLD_CFG_nLED2 | CPLD_CFG_nLED3 |
  435. + CPLD_CFG_nLED4 | CPLD_CFG_nCE;
  436. + rb4xx_cpld_write_cfg(cpld, cpld->config);
  437. +
  438. + /* setup GPIO chip */
  439. + cpld->chip.label = DRV_NAME;
  440. +
  441. + cpld->chip.get = rb4xx_cpld_gpio_get;
  442. + cpld->chip.set = rb4xx_cpld_gpio_set;
  443. + cpld->chip.direction_input = rb4xx_cpld_gpio_direction_input;
  444. + cpld->chip.direction_output = rb4xx_cpld_gpio_direction_output;
  445. +
  446. + cpld->chip.base = base;
  447. + cpld->chip.ngpio = CPLD_NUM_GPIOS;
  448. + cpld->chip.can_sleep = 1;
  449. + cpld->chip.dev = &cpld->spi->dev;
  450. + cpld->chip.owner = THIS_MODULE;
  451. +
  452. + err = gpiochip_add(&cpld->chip);
  453. + if (err)
  454. + dev_err(&cpld->spi->dev, "adding GPIO chip failed, err=%d\n",
  455. + err);
  456. +
  457. + return err;
  458. +}
  459. +
  460. +static int rb4xx_cpld_probe(struct spi_device *spi)
  461. +{
  462. + struct rb4xx_cpld *cpld;
  463. + struct rb4xx_cpld_platform_data *pdata;
  464. + int err;
  465. +
  466. + pdata = spi->dev.platform_data;
  467. + if (!pdata) {
  468. + dev_dbg(&spi->dev, "no platform data\n");
  469. + return -EINVAL;
  470. + }
  471. +
  472. + cpld = kzalloc(sizeof(*cpld), GFP_KERNEL);
  473. + if (!cpld) {
  474. + dev_err(&spi->dev, "no memory for private data\n");
  475. + return -ENOMEM;
  476. + }
  477. +
  478. + mutex_init(&cpld->lock);
  479. + cpld->spi = spi_dev_get(spi);
  480. + dev_set_drvdata(&spi->dev, cpld);
  481. +
  482. + spi->mode = SPI_MODE_0;
  483. + spi->bits_per_word = 8;
  484. + err = spi_setup(spi);
  485. + if (err) {
  486. + dev_err(&spi->dev, "spi_setup failed, err=%d\n", err);
  487. + goto err_drvdata;
  488. + }
  489. +
  490. + err = rb4xx_cpld_gpio_init(cpld, pdata->gpio_base);
  491. + if (err)
  492. + goto err_drvdata;
  493. +
  494. + rb4xx_cpld = cpld;
  495. +
  496. + return 0;
  497. +
  498. +err_drvdata:
  499. + dev_set_drvdata(&spi->dev, NULL);
  500. + kfree(cpld);
  501. +
  502. + return err;
  503. +}
  504. +
  505. +static int rb4xx_cpld_remove(struct spi_device *spi)
  506. +{
  507. + struct rb4xx_cpld *cpld;
  508. +
  509. + rb4xx_cpld = NULL;
  510. + cpld = dev_get_drvdata(&spi->dev);
  511. + dev_set_drvdata(&spi->dev, NULL);
  512. + kfree(cpld);
  513. +
  514. + return 0;
  515. +}
  516. +
  517. +static struct spi_driver rb4xx_cpld_driver = {
  518. + .driver = {
  519. + .name = DRV_NAME,
  520. + .bus = &spi_bus_type,
  521. + .owner = THIS_MODULE,
  522. + },
  523. + .probe = rb4xx_cpld_probe,
  524. + .remove = rb4xx_cpld_remove,
  525. +};
  526. +
  527. +static int __init rb4xx_cpld_init(void)
  528. +{
  529. + return spi_register_driver(&rb4xx_cpld_driver);
  530. +}
  531. +module_init(rb4xx_cpld_init);
  532. +
  533. +static void __exit rb4xx_cpld_exit(void)
  534. +{
  535. + spi_unregister_driver(&rb4xx_cpld_driver);
  536. +}
  537. +module_exit(rb4xx_cpld_exit);
  538. +
  539. +MODULE_DESCRIPTION(DRV_DESC);
  540. +MODULE_VERSION(DRV_VERSION);
  541. +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org>");
  542. +MODULE_LICENSE("GPL v2");
  543. --
  544. 1.8.5.3