timberdale.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727
  1. /*
  2. * timberdale.c timberdale FPGA MFD driver
  3. * Copyright (c) 2009 Intel Corporation
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License version 2 as
  7. * published by the Free Software Foundation.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17. */
  18. /* Supports:
  19. * Timberdale FPGA
  20. */
  21. #include <linux/kernel.h>
  22. #include <linux/module.h>
  23. #include <linux/pci.h>
  24. #include <linux/msi.h>
  25. #include <linux/mfd/core.h>
  26. #include <linux/timb_gpio.h>
  27. #include <linux/i2c.h>
  28. #include <linux/i2c-ocores.h>
  29. #include <linux/i2c/tsc2007.h>
  30. #include <linux/spi/spi.h>
  31. #include <linux/spi/xilinx_spi.h>
  32. #include <linux/spi/max7301.h>
  33. #include <linux/spi/mc33880.h>
  34. #include <media/timb_radio.h>
  35. #include "timberdale.h"
  36. #define DRIVER_NAME "timberdale"
  37. struct timberdale_device {
  38. resource_size_t ctl_mapbase;
  39. unsigned char __iomem *ctl_membase;
  40. struct {
  41. u32 major;
  42. u32 minor;
  43. u32 config;
  44. } fw;
  45. };
  46. /*--------------------------------------------------------------------------*/
  47. static struct tsc2007_platform_data timberdale_tsc2007_platform_data = {
  48. .model = 2003,
  49. .x_plate_ohms = 100
  50. };
  51. static struct i2c_board_info timberdale_i2c_board_info[] = {
  52. {
  53. I2C_BOARD_INFO("tsc2007", 0x48),
  54. .platform_data = &timberdale_tsc2007_platform_data,
  55. .irq = IRQ_TIMBERDALE_TSC_INT
  56. },
  57. };
  58. static __devinitdata struct ocores_i2c_platform_data
  59. timberdale_ocores_platform_data = {
  60. .regstep = 4,
  61. .clock_khz = 62500,
  62. .devices = timberdale_i2c_board_info,
  63. .num_devices = ARRAY_SIZE(timberdale_i2c_board_info)
  64. };
  65. const static __devinitconst struct resource timberdale_ocores_resources[] = {
  66. {
  67. .start = OCORESOFFSET,
  68. .end = OCORESEND,
  69. .flags = IORESOURCE_MEM,
  70. },
  71. {
  72. .start = IRQ_TIMBERDALE_I2C,
  73. .end = IRQ_TIMBERDALE_I2C,
  74. .flags = IORESOURCE_IRQ,
  75. },
  76. };
  77. const struct max7301_platform_data timberdale_max7301_platform_data = {
  78. .base = 200
  79. };
  80. const struct mc33880_platform_data timberdale_mc33880_platform_data = {
  81. .base = 100
  82. };
  83. static struct spi_board_info timberdale_spi_16bit_board_info[] = {
  84. {
  85. .modalias = "max7301",
  86. .max_speed_hz = 26000,
  87. .chip_select = 2,
  88. .mode = SPI_MODE_0,
  89. .platform_data = &timberdale_max7301_platform_data
  90. },
  91. };
  92. static struct spi_board_info timberdale_spi_8bit_board_info[] = {
  93. {
  94. .modalias = "mc33880",
  95. .max_speed_hz = 4000,
  96. .chip_select = 1,
  97. .mode = SPI_MODE_1,
  98. .platform_data = &timberdale_mc33880_platform_data
  99. },
  100. };
  101. static __devinitdata struct xspi_platform_data timberdale_xspi_platform_data = {
  102. .num_chipselect = 3,
  103. .little_endian = true,
  104. /* bits per word and devices will be filled in runtime depending
  105. * on the HW config
  106. */
  107. };
  108. const static __devinitconst struct resource timberdale_spi_resources[] = {
  109. {
  110. .start = SPIOFFSET,
  111. .end = SPIEND,
  112. .flags = IORESOURCE_MEM,
  113. },
  114. {
  115. .start = IRQ_TIMBERDALE_SPI,
  116. .end = IRQ_TIMBERDALE_SPI,
  117. .flags = IORESOURCE_IRQ,
  118. },
  119. };
  120. const static __devinitconst struct resource timberdale_eth_resources[] = {
  121. {
  122. .start = ETHOFFSET,
  123. .end = ETHEND,
  124. .flags = IORESOURCE_MEM,
  125. },
  126. {
  127. .start = IRQ_TIMBERDALE_ETHSW_IF,
  128. .end = IRQ_TIMBERDALE_ETHSW_IF,
  129. .flags = IORESOURCE_IRQ,
  130. },
  131. };
  132. static __devinitdata struct timbgpio_platform_data
  133. timberdale_gpio_platform_data = {
  134. .gpio_base = 0,
  135. .nr_pins = GPIO_NR_PINS,
  136. .irq_base = 200,
  137. };
  138. const static __devinitconst struct resource timberdale_gpio_resources[] = {
  139. {
  140. .start = GPIOOFFSET,
  141. .end = GPIOEND,
  142. .flags = IORESOURCE_MEM,
  143. },
  144. {
  145. .start = IRQ_TIMBERDALE_GPIO,
  146. .end = IRQ_TIMBERDALE_GPIO,
  147. .flags = IORESOURCE_IRQ,
  148. },
  149. };
  150. const static __devinitconst struct resource timberdale_mlogicore_resources[] = {
  151. {
  152. .start = MLCOREOFFSET,
  153. .end = MLCOREEND,
  154. .flags = IORESOURCE_MEM,
  155. },
  156. {
  157. .start = IRQ_TIMBERDALE_MLCORE,
  158. .end = IRQ_TIMBERDALE_MLCORE,
  159. .flags = IORESOURCE_IRQ,
  160. },
  161. {
  162. .start = IRQ_TIMBERDALE_MLCORE_BUF,
  163. .end = IRQ_TIMBERDALE_MLCORE_BUF,
  164. .flags = IORESOURCE_IRQ,
  165. },
  166. };
  167. const static __devinitconst struct resource timberdale_uart_resources[] = {
  168. {
  169. .start = UARTOFFSET,
  170. .end = UARTEND,
  171. .flags = IORESOURCE_MEM,
  172. },
  173. {
  174. .start = IRQ_TIMBERDALE_UART,
  175. .end = IRQ_TIMBERDALE_UART,
  176. .flags = IORESOURCE_IRQ,
  177. },
  178. };
  179. const static __devinitconst struct resource timberdale_uartlite_resources[] = {
  180. {
  181. .start = UARTLITEOFFSET,
  182. .end = UARTLITEEND,
  183. .flags = IORESOURCE_MEM,
  184. },
  185. {
  186. .start = IRQ_TIMBERDALE_UARTLITE,
  187. .end = IRQ_TIMBERDALE_UARTLITE,
  188. .flags = IORESOURCE_IRQ,
  189. },
  190. };
  191. const static __devinitconst struct resource timberdale_radio_resources[] = {
  192. {
  193. .start = RDSOFFSET,
  194. .end = RDSEND,
  195. .flags = IORESOURCE_MEM,
  196. },
  197. {
  198. .start = IRQ_TIMBERDALE_RDS,
  199. .end = IRQ_TIMBERDALE_RDS,
  200. .flags = IORESOURCE_IRQ,
  201. },
  202. };
  203. static __devinitdata struct i2c_board_info timberdale_tef6868_i2c_board_info = {
  204. I2C_BOARD_INFO("tef6862", 0x60)
  205. };
  206. static __devinitdata struct i2c_board_info timberdale_saa7706_i2c_board_info = {
  207. I2C_BOARD_INFO("saa7706h", 0x1C)
  208. };
  209. static __devinitdata struct timb_radio_platform_data
  210. timberdale_radio_platform_data = {
  211. .i2c_adapter = 0,
  212. .tuner = {
  213. .module_name = "tef6862",
  214. .info = &timberdale_tef6868_i2c_board_info
  215. },
  216. .dsp = {
  217. .module_name = "saa7706h",
  218. .info = &timberdale_saa7706_i2c_board_info
  219. }
  220. };
  221. const static __devinitconst struct resource timberdale_dma_resources[] = {
  222. {
  223. .start = DMAOFFSET,
  224. .end = DMAEND,
  225. .flags = IORESOURCE_MEM,
  226. },
  227. {
  228. .start = IRQ_TIMBERDALE_DMA,
  229. .end = IRQ_TIMBERDALE_DMA,
  230. .flags = IORESOURCE_IRQ,
  231. },
  232. };
  233. static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = {
  234. {
  235. .name = "timb-uart",
  236. .num_resources = ARRAY_SIZE(timberdale_uart_resources),
  237. .resources = timberdale_uart_resources,
  238. },
  239. {
  240. .name = "timb-gpio",
  241. .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
  242. .resources = timberdale_gpio_resources,
  243. .platform_data = &timberdale_gpio_platform_data,
  244. .data_size = sizeof(timberdale_gpio_platform_data),
  245. },
  246. {
  247. .name = "timb-radio",
  248. .num_resources = ARRAY_SIZE(timberdale_radio_resources),
  249. .resources = timberdale_radio_resources,
  250. .platform_data = &timberdale_radio_platform_data,
  251. .data_size = sizeof(timberdale_radio_platform_data),
  252. },
  253. {
  254. .name = "xilinx_spi",
  255. .num_resources = ARRAY_SIZE(timberdale_spi_resources),
  256. .resources = timberdale_spi_resources,
  257. .platform_data = &timberdale_xspi_platform_data,
  258. .data_size = sizeof(timberdale_xspi_platform_data),
  259. },
  260. {
  261. .name = "ks8842",
  262. .num_resources = ARRAY_SIZE(timberdale_eth_resources),
  263. .resources = timberdale_eth_resources,
  264. },
  265. {
  266. .name = "timb-dma",
  267. .num_resources = ARRAY_SIZE(timberdale_dma_resources),
  268. .resources = timberdale_dma_resources,
  269. },
  270. };
  271. static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = {
  272. {
  273. .name = "timb-uart",
  274. .num_resources = ARRAY_SIZE(timberdale_uart_resources),
  275. .resources = timberdale_uart_resources,
  276. },
  277. {
  278. .name = "uartlite",
  279. .num_resources = ARRAY_SIZE(timberdale_uartlite_resources),
  280. .resources = timberdale_uartlite_resources,
  281. },
  282. {
  283. .name = "timb-gpio",
  284. .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
  285. .resources = timberdale_gpio_resources,
  286. .platform_data = &timberdale_gpio_platform_data,
  287. .data_size = sizeof(timberdale_gpio_platform_data),
  288. },
  289. {
  290. .name = "timb-mlogicore",
  291. .num_resources = ARRAY_SIZE(timberdale_mlogicore_resources),
  292. .resources = timberdale_mlogicore_resources,
  293. },
  294. {
  295. .name = "timb-radio",
  296. .num_resources = ARRAY_SIZE(timberdale_radio_resources),
  297. .resources = timberdale_radio_resources,
  298. .platform_data = &timberdale_radio_platform_data,
  299. .data_size = sizeof(timberdale_radio_platform_data),
  300. },
  301. {
  302. .name = "xilinx_spi",
  303. .num_resources = ARRAY_SIZE(timberdale_spi_resources),
  304. .resources = timberdale_spi_resources,
  305. .platform_data = &timberdale_xspi_platform_data,
  306. .data_size = sizeof(timberdale_xspi_platform_data),
  307. },
  308. {
  309. .name = "ks8842",
  310. .num_resources = ARRAY_SIZE(timberdale_eth_resources),
  311. .resources = timberdale_eth_resources,
  312. },
  313. {
  314. .name = "timb-dma",
  315. .num_resources = ARRAY_SIZE(timberdale_dma_resources),
  316. .resources = timberdale_dma_resources,
  317. },
  318. };
  319. static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = {
  320. {
  321. .name = "timb-uart",
  322. .num_resources = ARRAY_SIZE(timberdale_uart_resources),
  323. .resources = timberdale_uart_resources,
  324. },
  325. {
  326. .name = "timb-gpio",
  327. .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
  328. .resources = timberdale_gpio_resources,
  329. .platform_data = &timberdale_gpio_platform_data,
  330. .data_size = sizeof(timberdale_gpio_platform_data),
  331. },
  332. {
  333. .name = "timb-radio",
  334. .num_resources = ARRAY_SIZE(timberdale_radio_resources),
  335. .resources = timberdale_radio_resources,
  336. .platform_data = &timberdale_radio_platform_data,
  337. .data_size = sizeof(timberdale_radio_platform_data),
  338. },
  339. {
  340. .name = "xilinx_spi",
  341. .num_resources = ARRAY_SIZE(timberdale_spi_resources),
  342. .resources = timberdale_spi_resources,
  343. .platform_data = &timberdale_xspi_platform_data,
  344. .data_size = sizeof(timberdale_xspi_platform_data),
  345. },
  346. {
  347. .name = "timb-dma",
  348. .num_resources = ARRAY_SIZE(timberdale_dma_resources),
  349. .resources = timberdale_dma_resources,
  350. },
  351. };
  352. static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = {
  353. {
  354. .name = "timb-uart",
  355. .num_resources = ARRAY_SIZE(timberdale_uart_resources),
  356. .resources = timberdale_uart_resources,
  357. },
  358. {
  359. .name = "ocores-i2c",
  360. .num_resources = ARRAY_SIZE(timberdale_ocores_resources),
  361. .resources = timberdale_ocores_resources,
  362. .platform_data = &timberdale_ocores_platform_data,
  363. .data_size = sizeof(timberdale_ocores_platform_data),
  364. },
  365. {
  366. .name = "timb-gpio",
  367. .num_resources = ARRAY_SIZE(timberdale_gpio_resources),
  368. .resources = timberdale_gpio_resources,
  369. .platform_data = &timberdale_gpio_platform_data,
  370. .data_size = sizeof(timberdale_gpio_platform_data),
  371. },
  372. {
  373. .name = "timb-radio",
  374. .num_resources = ARRAY_SIZE(timberdale_radio_resources),
  375. .resources = timberdale_radio_resources,
  376. .platform_data = &timberdale_radio_platform_data,
  377. .data_size = sizeof(timberdale_radio_platform_data),
  378. },
  379. {
  380. .name = "xilinx_spi",
  381. .num_resources = ARRAY_SIZE(timberdale_spi_resources),
  382. .resources = timberdale_spi_resources,
  383. .platform_data = &timberdale_xspi_platform_data,
  384. .data_size = sizeof(timberdale_xspi_platform_data),
  385. },
  386. {
  387. .name = "ks8842",
  388. .num_resources = ARRAY_SIZE(timberdale_eth_resources),
  389. .resources = timberdale_eth_resources,
  390. },
  391. {
  392. .name = "timb-dma",
  393. .num_resources = ARRAY_SIZE(timberdale_dma_resources),
  394. .resources = timberdale_dma_resources,
  395. },
  396. };
  397. static const __devinitconst struct resource timberdale_sdhc_resources[] = {
  398. /* located in bar 1 and bar 2 */
  399. {
  400. .start = SDHC0OFFSET,
  401. .end = SDHC0END,
  402. .flags = IORESOURCE_MEM,
  403. },
  404. {
  405. .start = IRQ_TIMBERDALE_SDHC,
  406. .end = IRQ_TIMBERDALE_SDHC,
  407. .flags = IORESOURCE_IRQ,
  408. },
  409. };
  410. static __devinitdata struct mfd_cell timberdale_cells_bar1[] = {
  411. {
  412. .name = "sdhci",
  413. .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
  414. .resources = timberdale_sdhc_resources,
  415. },
  416. };
  417. static __devinitdata struct mfd_cell timberdale_cells_bar2[] = {
  418. {
  419. .name = "sdhci",
  420. .num_resources = ARRAY_SIZE(timberdale_sdhc_resources),
  421. .resources = timberdale_sdhc_resources,
  422. },
  423. };
  424. static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr,
  425. char *buf)
  426. {
  427. struct pci_dev *pdev = to_pci_dev(dev);
  428. struct timberdale_device *priv = pci_get_drvdata(pdev);
  429. return sprintf(buf, "%d.%d.%d\n", priv->fw.major, priv->fw.minor,
  430. priv->fw.config);
  431. }
  432. static DEVICE_ATTR(fw_ver, S_IRUGO, show_fw_ver, NULL);
  433. /*--------------------------------------------------------------------------*/
  434. static int __devinit timb_probe(struct pci_dev *dev,
  435. const struct pci_device_id *id)
  436. {
  437. struct timberdale_device *priv;
  438. int err, i;
  439. resource_size_t mapbase;
  440. struct msix_entry *msix_entries = NULL;
  441. u8 ip_setup;
  442. priv = kzalloc(sizeof(*priv), GFP_KERNEL);
  443. if (!priv)
  444. return -ENOMEM;
  445. pci_set_drvdata(dev, priv);
  446. err = pci_enable_device(dev);
  447. if (err)
  448. goto err_enable;
  449. mapbase = pci_resource_start(dev, 0);
  450. if (!mapbase) {
  451. dev_err(&dev->dev, "No resource\n");
  452. goto err_start;
  453. }
  454. /* create a resource for the PCI master register */
  455. priv->ctl_mapbase = mapbase + CHIPCTLOFFSET;
  456. if (!request_mem_region(priv->ctl_mapbase, CHIPCTLSIZE, "timb-ctl")) {
  457. dev_err(&dev->dev, "Failed to request ctl mem\n");
  458. goto err_request;
  459. }
  460. priv->ctl_membase = ioremap(priv->ctl_mapbase, CHIPCTLSIZE);
  461. if (!priv->ctl_membase) {
  462. dev_err(&dev->dev, "ioremap failed for ctl mem\n");
  463. goto err_ioremap;
  464. }
  465. /* read the HW config */
  466. priv->fw.major = ioread32(priv->ctl_membase + TIMB_REV_MAJOR);
  467. priv->fw.minor = ioread32(priv->ctl_membase + TIMB_REV_MINOR);
  468. priv->fw.config = ioread32(priv->ctl_membase + TIMB_HW_CONFIG);
  469. if (priv->fw.major > TIMB_SUPPORTED_MAJOR) {
  470. dev_err(&dev->dev, "The driver supports an older "
  471. "version of the FPGA, please update the driver to "
  472. "support %d.%d\n", priv->fw.major, priv->fw.minor);
  473. goto err_ioremap;
  474. }
  475. if (priv->fw.major < TIMB_SUPPORTED_MAJOR ||
  476. priv->fw.minor < TIMB_REQUIRED_MINOR) {
  477. dev_err(&dev->dev, "The FPGA image is too old (%d.%d), "
  478. "please upgrade the FPGA to at least: %d.%d\n",
  479. priv->fw.major, priv->fw.minor,
  480. TIMB_SUPPORTED_MAJOR, TIMB_REQUIRED_MINOR);
  481. goto err_ioremap;
  482. }
  483. msix_entries = kzalloc(TIMBERDALE_NR_IRQS * sizeof(*msix_entries),
  484. GFP_KERNEL);
  485. if (!msix_entries)
  486. goto err_ioremap;
  487. for (i = 0; i < TIMBERDALE_NR_IRQS; i++)
  488. msix_entries[i].entry = i;
  489. err = pci_enable_msix(dev, msix_entries, TIMBERDALE_NR_IRQS);
  490. if (err) {
  491. dev_err(&dev->dev,
  492. "MSI-X init failed: %d, expected entries: %d\n",
  493. err, TIMBERDALE_NR_IRQS);
  494. goto err_msix;
  495. }
  496. err = device_create_file(&dev->dev, &dev_attr_fw_ver);
  497. if (err)
  498. goto err_create_file;
  499. /* Reset all FPGA PLB peripherals */
  500. iowrite32(0x1, priv->ctl_membase + TIMB_SW_RST);
  501. /* update IRQ offsets in I2C board info */
  502. for (i = 0; i < ARRAY_SIZE(timberdale_i2c_board_info); i++)
  503. timberdale_i2c_board_info[i].irq =
  504. msix_entries[timberdale_i2c_board_info[i].irq].vector;
  505. /* Update the SPI configuration depending on the HW (8 or 16 bit) */
  506. if (priv->fw.config & TIMB_HW_CONFIG_SPI_8BIT) {
  507. timberdale_xspi_platform_data.bits_per_word = 8;
  508. timberdale_xspi_platform_data.devices =
  509. timberdale_spi_8bit_board_info;
  510. timberdale_xspi_platform_data.num_devices =
  511. ARRAY_SIZE(timberdale_spi_8bit_board_info);
  512. } else {
  513. timberdale_xspi_platform_data.bits_per_word = 16;
  514. timberdale_xspi_platform_data.devices =
  515. timberdale_spi_16bit_board_info;
  516. timberdale_xspi_platform_data.num_devices =
  517. ARRAY_SIZE(timberdale_spi_16bit_board_info);
  518. }
  519. ip_setup = priv->fw.config & TIMB_HW_VER_MASK;
  520. switch (ip_setup) {
  521. case TIMB_HW_VER0:
  522. err = mfd_add_devices(&dev->dev, -1,
  523. timberdale_cells_bar0_cfg0,
  524. ARRAY_SIZE(timberdale_cells_bar0_cfg0),
  525. &dev->resource[0], msix_entries[0].vector);
  526. break;
  527. case TIMB_HW_VER1:
  528. err = mfd_add_devices(&dev->dev, -1,
  529. timberdale_cells_bar0_cfg1,
  530. ARRAY_SIZE(timberdale_cells_bar0_cfg1),
  531. &dev->resource[0], msix_entries[0].vector);
  532. break;
  533. case TIMB_HW_VER2:
  534. err = mfd_add_devices(&dev->dev, -1,
  535. timberdale_cells_bar0_cfg2,
  536. ARRAY_SIZE(timberdale_cells_bar0_cfg2),
  537. &dev->resource[0], msix_entries[0].vector);
  538. break;
  539. case TIMB_HW_VER3:
  540. err = mfd_add_devices(&dev->dev, -1,
  541. timberdale_cells_bar0_cfg3,
  542. ARRAY_SIZE(timberdale_cells_bar0_cfg3),
  543. &dev->resource[0], msix_entries[0].vector);
  544. break;
  545. default:
  546. dev_err(&dev->dev, "Uknown IP setup: %d.%d.%d\n",
  547. priv->fw.major, priv->fw.minor, ip_setup);
  548. err = -ENODEV;
  549. goto err_mfd;
  550. break;
  551. }
  552. if (err) {
  553. dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
  554. goto err_mfd;
  555. }
  556. err = mfd_add_devices(&dev->dev, 0,
  557. timberdale_cells_bar1, ARRAY_SIZE(timberdale_cells_bar1),
  558. &dev->resource[1], msix_entries[0].vector);
  559. if (err) {
  560. dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
  561. goto err_mfd2;
  562. }
  563. /* only version 0 and 3 have the iNand routed to SDHCI */
  564. if (((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER0) ||
  565. ((priv->fw.config & TIMB_HW_VER_MASK) == TIMB_HW_VER3)) {
  566. err = mfd_add_devices(&dev->dev, 1, timberdale_cells_bar2,
  567. ARRAY_SIZE(timberdale_cells_bar2),
  568. &dev->resource[2], msix_entries[0].vector);
  569. if (err) {
  570. dev_err(&dev->dev, "mfd_add_devices failed: %d\n", err);
  571. goto err_mfd2;
  572. }
  573. }
  574. kfree(msix_entries);
  575. dev_info(&dev->dev,
  576. "Found Timberdale Card. Rev: %d.%d, HW config: 0x%02x\n",
  577. priv->fw.major, priv->fw.minor, priv->fw.config);
  578. return 0;
  579. err_mfd2:
  580. mfd_remove_devices(&dev->dev);
  581. err_mfd:
  582. device_remove_file(&dev->dev, &dev_attr_fw_ver);
  583. err_create_file:
  584. pci_disable_msix(dev);
  585. err_msix:
  586. iounmap(priv->ctl_membase);
  587. err_ioremap:
  588. release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
  589. err_request:
  590. pci_set_drvdata(dev, NULL);
  591. err_start:
  592. pci_disable_device(dev);
  593. err_enable:
  594. kfree(msix_entries);
  595. kfree(priv);
  596. pci_set_drvdata(dev, NULL);
  597. return -ENODEV;
  598. }
  599. static void __devexit timb_remove(struct pci_dev *dev)
  600. {
  601. struct timberdale_device *priv = pci_get_drvdata(dev);
  602. mfd_remove_devices(&dev->dev);
  603. device_remove_file(&dev->dev, &dev_attr_fw_ver);
  604. iounmap(priv->ctl_membase);
  605. release_mem_region(priv->ctl_mapbase, CHIPCTLSIZE);
  606. pci_disable_msix(dev);
  607. pci_disable_device(dev);
  608. pci_set_drvdata(dev, NULL);
  609. kfree(priv);
  610. }
  611. static struct pci_device_id timberdale_pci_tbl[] = {
  612. { PCI_DEVICE(PCI_VENDOR_ID_TIMB, PCI_DEVICE_ID_TIMB) },
  613. { 0 }
  614. };
  615. MODULE_DEVICE_TABLE(pci, timberdale_pci_tbl);
  616. static struct pci_driver timberdale_pci_driver = {
  617. .name = DRIVER_NAME,
  618. .id_table = timberdale_pci_tbl,
  619. .probe = timb_probe,
  620. .remove = __devexit_p(timb_remove),
  621. };
  622. static int __init timberdale_init(void)
  623. {
  624. int err;
  625. err = pci_register_driver(&timberdale_pci_driver);
  626. if (err < 0) {
  627. printk(KERN_ERR
  628. "Failed to register PCI driver for %s device.\n",
  629. timberdale_pci_driver.name);
  630. return -ENODEV;
  631. }
  632. printk(KERN_INFO "Driver for %s has been successfully registered.\n",
  633. timberdale_pci_driver.name);
  634. return 0;
  635. }
  636. static void __exit timberdale_exit(void)
  637. {
  638. pci_unregister_driver(&timberdale_pci_driver);
  639. printk(KERN_INFO "Driver for %s has been successfully unregistered.\n",
  640. timberdale_pci_driver.name);
  641. }
  642. module_init(timberdale_init);
  643. module_exit(timberdale_exit);
  644. MODULE_AUTHOR("Mocean Laboratories <info@mocean-labs.com>");
  645. MODULE_VERSION(DRV_VERSION);
  646. MODULE_LICENSE("GPL v2");