pcmcia.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. /*
  2. * Sonics Silicon Backplane
  3. * PCMCIA-Hostbus related functions
  4. *
  5. * Copyright 2006 Johannes Berg <johannes@sipsolutions.net>
  6. * Copyright 2007 Michael Buesch <mb@bu3sch.de>
  7. *
  8. * Licensed under the GNU/GPL. See COPYING for details.
  9. */
  10. #include <linux/ssb/ssb.h>
  11. #include <linux/delay.h>
  12. #include <linux/io.h>
  13. #include <pcmcia/cs_types.h>
  14. #include <pcmcia/cs.h>
  15. #include <pcmcia/cistpl.h>
  16. #include <pcmcia/ciscode.h>
  17. #include <pcmcia/ds.h>
  18. #include <pcmcia/cisreg.h>
  19. #include "ssb_private.h"
  20. /* Define the following to 1 to enable a printk on each coreswitch. */
  21. #define SSB_VERBOSE_PCMCIACORESWITCH_DEBUG 0
  22. int ssb_pcmcia_switch_coreidx(struct ssb_bus *bus,
  23. u8 coreidx)
  24. {
  25. struct pcmcia_device *pdev = bus->host_pcmcia;
  26. int err;
  27. int attempts = 0;
  28. u32 cur_core;
  29. conf_reg_t reg;
  30. u32 addr;
  31. u32 read_addr;
  32. addr = (coreidx * SSB_CORE_SIZE) + SSB_ENUM_BASE;
  33. while (1) {
  34. reg.Action = CS_WRITE;
  35. reg.Offset = 0x2E;
  36. reg.Value = (addr & 0x0000F000) >> 12;
  37. err = pcmcia_access_configuration_register(pdev, &reg);
  38. if (err != CS_SUCCESS)
  39. goto error;
  40. reg.Offset = 0x30;
  41. reg.Value = (addr & 0x00FF0000) >> 16;
  42. err = pcmcia_access_configuration_register(pdev, &reg);
  43. if (err != CS_SUCCESS)
  44. goto error;
  45. reg.Offset = 0x32;
  46. reg.Value = (addr & 0xFF000000) >> 24;
  47. err = pcmcia_access_configuration_register(pdev, &reg);
  48. if (err != CS_SUCCESS)
  49. goto error;
  50. read_addr = 0;
  51. reg.Action = CS_READ;
  52. reg.Offset = 0x2E;
  53. err = pcmcia_access_configuration_register(pdev, &reg);
  54. if (err != CS_SUCCESS)
  55. goto error;
  56. read_addr |= ((u32)(reg.Value & 0x0F)) << 12;
  57. reg.Offset = 0x30;
  58. err = pcmcia_access_configuration_register(pdev, &reg);
  59. if (err != CS_SUCCESS)
  60. goto error;
  61. read_addr |= ((u32)reg.Value) << 16;
  62. reg.Offset = 0x32;
  63. err = pcmcia_access_configuration_register(pdev, &reg);
  64. if (err != CS_SUCCESS)
  65. goto error;
  66. read_addr |= ((u32)reg.Value) << 24;
  67. cur_core = (read_addr - SSB_ENUM_BASE) / SSB_CORE_SIZE;
  68. if (cur_core == coreidx)
  69. break;
  70. if (attempts++ > SSB_BAR0_MAX_RETRIES)
  71. goto error;
  72. udelay(10);
  73. }
  74. return 0;
  75. error:
  76. ssb_printk(KERN_ERR PFX "Failed to switch to core %u\n", coreidx);
  77. return -ENODEV;
  78. }
  79. int ssb_pcmcia_switch_core(struct ssb_bus *bus,
  80. struct ssb_device *dev)
  81. {
  82. int err;
  83. #if SSB_VERBOSE_PCMCIACORESWITCH_DEBUG
  84. ssb_printk(KERN_INFO PFX
  85. "Switching to %s core, index %d\n",
  86. ssb_core_name(dev->id.coreid),
  87. dev->core_index);
  88. #endif
  89. err = ssb_pcmcia_switch_coreidx(bus, dev->core_index);
  90. if (!err)
  91. bus->mapped_device = dev;
  92. return err;
  93. }
  94. int ssb_pcmcia_switch_segment(struct ssb_bus *bus, u8 seg)
  95. {
  96. int attempts = 0;
  97. conf_reg_t reg;
  98. int res;
  99. SSB_WARN_ON((seg != 0) && (seg != 1));
  100. reg.Offset = 0x34;
  101. reg.Function = 0;
  102. while (1) {
  103. reg.Action = CS_WRITE;
  104. reg.Value = seg;
  105. res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
  106. if (unlikely(res != CS_SUCCESS))
  107. goto error;
  108. reg.Value = 0xFF;
  109. reg.Action = CS_READ;
  110. res = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
  111. if (unlikely(res != CS_SUCCESS))
  112. goto error;
  113. if (reg.Value == seg)
  114. break;
  115. if (unlikely(attempts++ > SSB_BAR0_MAX_RETRIES))
  116. goto error;
  117. udelay(10);
  118. }
  119. bus->mapped_pcmcia_seg = seg;
  120. return 0;
  121. error:
  122. ssb_printk(KERN_ERR PFX "Failed to switch pcmcia segment\n");
  123. return -ENODEV;
  124. }
  125. static int select_core_and_segment(struct ssb_device *dev,
  126. u16 *offset)
  127. {
  128. struct ssb_bus *bus = dev->bus;
  129. int err;
  130. u8 need_segment;
  131. if (*offset >= 0x800) {
  132. *offset -= 0x800;
  133. need_segment = 1;
  134. } else
  135. need_segment = 0;
  136. if (unlikely(dev != bus->mapped_device)) {
  137. err = ssb_pcmcia_switch_core(bus, dev);
  138. if (unlikely(err))
  139. return err;
  140. }
  141. if (unlikely(need_segment != bus->mapped_pcmcia_seg)) {
  142. err = ssb_pcmcia_switch_segment(bus, need_segment);
  143. if (unlikely(err))
  144. return err;
  145. }
  146. return 0;
  147. }
  148. static u16 ssb_pcmcia_read16(struct ssb_device *dev, u16 offset)
  149. {
  150. struct ssb_bus *bus = dev->bus;
  151. unsigned long flags;
  152. int err;
  153. u16 value = 0xFFFF;
  154. spin_lock_irqsave(&bus->bar_lock, flags);
  155. err = select_core_and_segment(dev, &offset);
  156. if (likely(!err))
  157. value = readw(bus->mmio + offset);
  158. spin_unlock_irqrestore(&bus->bar_lock, flags);
  159. return value;
  160. }
  161. static u32 ssb_pcmcia_read32(struct ssb_device *dev, u16 offset)
  162. {
  163. struct ssb_bus *bus = dev->bus;
  164. unsigned long flags;
  165. int err;
  166. u32 lo = 0xFFFFFFFF, hi = 0xFFFFFFFF;
  167. spin_lock_irqsave(&bus->bar_lock, flags);
  168. err = select_core_and_segment(dev, &offset);
  169. if (likely(!err)) {
  170. lo = readw(bus->mmio + offset);
  171. hi = readw(bus->mmio + offset + 2);
  172. }
  173. spin_unlock_irqrestore(&bus->bar_lock, flags);
  174. return (lo | (hi << 16));
  175. }
  176. static void ssb_pcmcia_write16(struct ssb_device *dev, u16 offset, u16 value)
  177. {
  178. struct ssb_bus *bus = dev->bus;
  179. unsigned long flags;
  180. int err;
  181. spin_lock_irqsave(&bus->bar_lock, flags);
  182. err = select_core_and_segment(dev, &offset);
  183. if (likely(!err))
  184. writew(value, bus->mmio + offset);
  185. mmiowb();
  186. spin_unlock_irqrestore(&bus->bar_lock, flags);
  187. }
  188. static void ssb_pcmcia_write32(struct ssb_device *dev, u16 offset, u32 value)
  189. {
  190. struct ssb_bus *bus = dev->bus;
  191. unsigned long flags;
  192. int err;
  193. spin_lock_irqsave(&bus->bar_lock, flags);
  194. err = select_core_and_segment(dev, &offset);
  195. if (likely(!err)) {
  196. writew((value & 0x0000FFFF), bus->mmio + offset);
  197. writew(((value & 0xFFFF0000) >> 16), bus->mmio + offset + 2);
  198. }
  199. mmiowb();
  200. spin_unlock_irqrestore(&bus->bar_lock, flags);
  201. }
  202. /* Not "static", as it's used in main.c */
  203. const struct ssb_bus_ops ssb_pcmcia_ops = {
  204. .read16 = ssb_pcmcia_read16,
  205. .read32 = ssb_pcmcia_read32,
  206. .write16 = ssb_pcmcia_write16,
  207. .write32 = ssb_pcmcia_write32,
  208. };
  209. #include <linux/etherdevice.h>
  210. int ssb_pcmcia_get_invariants(struct ssb_bus *bus,
  211. struct ssb_init_invariants *iv)
  212. {
  213. //TODO
  214. random_ether_addr(iv->sprom.il0mac);
  215. return 0;
  216. }
  217. int ssb_pcmcia_init(struct ssb_bus *bus)
  218. {
  219. conf_reg_t reg;
  220. int err;
  221. if (bus->bustype != SSB_BUSTYPE_PCMCIA)
  222. return 0;
  223. /* Switch segment to a known state and sync
  224. * bus->mapped_pcmcia_seg with hardware state. */
  225. ssb_pcmcia_switch_segment(bus, 0);
  226. /* Init IRQ routing */
  227. reg.Action = CS_READ;
  228. reg.Function = 0;
  229. if (bus->chip_id == 0x4306)
  230. reg.Offset = 0x00;
  231. else
  232. reg.Offset = 0x80;
  233. err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
  234. if (err != CS_SUCCESS)
  235. goto error;
  236. reg.Action = CS_WRITE;
  237. reg.Value |= 0x04 | 0x01;
  238. err = pcmcia_access_configuration_register(bus->host_pcmcia, &reg);
  239. if (err != CS_SUCCESS)
  240. goto error;
  241. return 0;
  242. error:
  243. return -ENODEV;
  244. }