dilnetpc.c
上传用户:lgb322
上传日期:2013-02-24
资源大小:30529k
文件大小:15k
源码类别:

嵌入式Linux

开发平台:

Unix_Linux

  1. /* dilnetpc.c -- MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP"
  2.  *
  3.  * This program is free software; you can redistribute it and/or modify
  4.  * it under the terms of the GNU General Public License as published by
  5.  * the Free Software Foundation; either version 2 of the License, or
  6.  * (at your option) any later version.
  7.  *
  8.  * This program is distributed in the hope that it will be useful,
  9.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11.  * GNU General Public License for more details.
  12.  *
  13.  * You should have received a copy of the GNU General Public License
  14.  * along with this program; if not, write to the Free Software
  15.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  16.  *
  17.  * $Id: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $
  18.  *
  19.  * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
  20.  * featuring the AMD Elan SC410 processor. There are two variants of this
  21.  * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash
  22.  * ROM (Intel 28F016S3) and 8 megs of DRAM, the ADNP version has 4 megs
  23.  * flash and 16 megs of RAM.
  24.  * For details, see http://www.ssv-embedded.de/ssv/pc104/p169.htm
  25.  * and http://www.ssv-embedded.de/ssv/pc104/p170.htm
  26.  */
  27. #include <linux/config.h>
  28. #include <linux/module.h>
  29. #include <linux/types.h>
  30. #include <linux/kernel.h>
  31. #include <asm/io.h>
  32. #include <linux/mtd/mtd.h>
  33. #include <linux/mtd/map.h>
  34. #include <linux/mtd/partitions.h>
  35. #include <linux/mtd/concat.h>
  36. /*
  37. ** The DIL/NetPC keeps it's BIOS in two distinct flash blocks.
  38. ** Destroying any of these blocks transforms the DNPC into
  39. ** a paperweight (albeit not a very useful one, considering
  40. ** it only weighs a few grams).
  41. **
  42. ** Therefore, the BIOS blocks must never be erased or written to
  43. ** except by people who know exactly what they are doing (e.g.
  44. ** to install a BIOS update). These partitions are marked read-only
  45. ** by default, but can be made read/write by undefining
  46. ** DNPC_BIOS_BLOCKS_WRITEPROTECTED:
  47. */
  48. #define DNPC_BIOS_BLOCKS_WRITEPROTECTED
  49. /*
  50. ** The ID string (in ROM) is checked to determine whether we
  51. ** are running on a DNP/1486 or ADNP/1486
  52. */
  53. #define BIOSID_BASE 0x000fe100
  54. #define ID_DNPC "DNP1486"
  55. #define ID_ADNP "ADNP1486"
  56. /*
  57. ** Address where the flash should appear in CPU space
  58. */
  59. #define FLASH_BASE 0x2000000
  60. /*
  61. ** Chip Setup and Control (CSC) indexed register space
  62. */
  63. #define CSC_INDEX 0x22
  64. #define CSC_DATA 0x23
  65. #define CSC_MMSWAR 0x30 /* MMS window C-F attributes register */
  66. #define CSC_MMSWDSR 0x31 /* MMS window C-F device select register */
  67. #define CSC_RBWR 0xa7 /* GPIO Read-Back/Write Register B */
  68. #define CSC_CR 0xd0 /* internal I/O device disable/Echo */
  69. /* Z-bus/configuration register */
  70. #define CSC_PCCMDCR 0xf1 /* PC card mode and DMA control register */
  71. /*
  72. ** PC Card indexed register space:
  73. */
  74. #define PCC_INDEX 0x3e0
  75. #define PCC_DATA 0x3e1
  76. #define PCC_AWER_B 0x46 /* Socket B Address Window enable register */
  77. #define PCC_MWSAR_1_Lo 0x58 /* memory window 1 start address low register */
  78. #define PCC_MWSAR_1_Hi 0x59 /* memory window 1 start address high register */
  79. #define PCC_MWEAR_1_Lo 0x5A /* memory window 1 stop address low register */
  80. #define PCC_MWEAR_1_Hi 0x5B /* memory window 1 stop address high register */
  81. #define PCC_MWAOR_1_Lo 0x5C /* memory window 1 address offset low register */
  82. #define PCC_MWAOR_1_Hi 0x5D /* memory window 1 address offset high register */
  83. /*
  84. ** Access to SC4x0's Chip Setup and Control (CSC)
  85. ** and PC Card (PCC) indexed registers:
  86. */
  87. static inline void setcsc(int reg, unsigned char data)
  88. {
  89. outb(reg, CSC_INDEX);
  90. outb(data, CSC_DATA);
  91. }
  92. static inline unsigned char getcsc(int reg)
  93. {
  94. outb(reg, CSC_INDEX);
  95. return(inb(CSC_DATA));
  96. }
  97. static inline void setpcc(int reg, unsigned char data)
  98. {
  99. outb(reg, PCC_INDEX);
  100. outb(data, PCC_DATA);
  101. }
  102. static inline unsigned char getpcc(int reg)
  103. {
  104. outb(reg, PCC_INDEX);
  105. return(inb(PCC_DATA));
  106. }
  107. /*
  108. ************************************************************
  109. ** Enable access to DIL/NetPC's flash by mapping it into
  110. ** the SC4x0's MMS Window C.
  111. ************************************************************
  112. */
  113. static void dnpc_map_flash(unsigned long flash_base, unsigned long flash_size)
  114. {
  115. unsigned long flash_end = flash_base + flash_size - 1;
  116. /*
  117. ** enable setup of MMS windows C-F:
  118. */
  119. /* - enable PC Card indexed register space */
  120. setcsc(CSC_CR, getcsc(CSC_CR) | 0x2);
  121. /* - set PC Card controller to operate in standard mode */
  122. setcsc(CSC_PCCMDCR, getcsc(CSC_PCCMDCR) & ~1);
  123. /*
  124. ** Program base address and end address of window
  125. ** where the flash ROM should appear in CPU address space
  126. */
  127. setpcc(PCC_MWSAR_1_Lo, (flash_base >> 12) & 0xff);
  128. setpcc(PCC_MWSAR_1_Hi, (flash_base >> 20) & 0x3f);
  129. setpcc(PCC_MWEAR_1_Lo, (flash_end >> 12) & 0xff);
  130. setpcc(PCC_MWEAR_1_Hi, (flash_end >> 20) & 0x3f);
  131. /* program offset of first flash location to appear in this window (0) */
  132. setpcc(PCC_MWAOR_1_Lo, ((0 - flash_base) >> 12) & 0xff);
  133. setpcc(PCC_MWAOR_1_Hi, ((0 - flash_base)>> 20) & 0x3f);
  134. /* set attributes for MMS window C: non-cacheable, write-enabled */
  135. setcsc(CSC_MMSWAR, getcsc(CSC_MMSWAR) & ~0x11);
  136. /* select physical device ROMCS0 (i.e. flash) for MMS Window C */
  137. setcsc(CSC_MMSWDSR, getcsc(CSC_MMSWDSR) & ~0x03);
  138. /* enable memory window 1 */
  139. setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) | 0x02);
  140. /* now disable PC Card indexed register space again */
  141. setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2);
  142. }
  143. /*
  144. ************************************************************
  145. ** Disable access to DIL/NetPC's flash by mapping it into
  146. ** the SC4x0's MMS Window C.
  147. ************************************************************
  148. */
  149. static void dnpc_unmap_flash(void)
  150. {
  151. /* - enable PC Card indexed register space */
  152. setcsc(CSC_CR, getcsc(CSC_CR) | 0x2);
  153. /* disable memory window 1 */
  154. setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) & ~0x02);
  155. /* now disable PC Card indexed register space again */
  156. setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2);
  157. }
  158. static __u8 dnpc_read8(struct map_info *map, unsigned long ofs)
  159. {
  160. return readb(map->map_priv_1 + ofs);
  161. }
  162. static __u16 dnpc_read16(struct map_info *map, unsigned long ofs)
  163. {
  164. return readw(map->map_priv_1 + ofs);
  165. }
  166. static __u32 dnpc_read32(struct map_info *map, unsigned long ofs)
  167. {
  168. return readl(map->map_priv_1 + ofs);
  169. }
  170. static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
  171. {
  172. memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
  173. }
  174. static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr)
  175. {
  176. writeb(d, map->map_priv_1 + adr);
  177. }
  178. static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr)
  179. {
  180. writew(d, map->map_priv_1 + adr);
  181. }
  182. static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr)
  183. {
  184. writel(d, map->map_priv_1 + adr);
  185. }
  186. static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
  187. {
  188. memcpy_toio((void *)(map->map_priv_1 + to), from, len);
  189. }
  190. /*
  191. ************************************************************
  192. ** Enable/Disable VPP to write to flash
  193. ************************************************************
  194. */
  195. static spinlock_t dnpc_spin   = SPIN_LOCK_UNLOCKED;
  196. static int        vpp_counter = 0;
  197. /*
  198. ** This is what has to be done for the DNP board ..
  199. */
  200. static void dnp_set_vpp(struct map_info *not_used, int on)
  201. {
  202. spin_lock_irq(&dnpc_spin);
  203. if (on)
  204. {
  205. if(++vpp_counter == 1)
  206. setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x4);
  207. }
  208. else
  209. {
  210. if(--vpp_counter == 0)
  211. setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x4);
  212. else if(vpp_counter < 0)
  213. BUG();
  214. }
  215. spin_unlock_irq(&dnpc_spin);
  216. }
  217. /*
  218. ** .. and this the ADNP version:
  219. */
  220. static void adnp_set_vpp(struct map_info *not_used, int on)
  221. {
  222. spin_lock_irq(&dnpc_spin);
  223. if (on)
  224. {
  225. if(++vpp_counter == 1)
  226. setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x8);
  227. }
  228. else
  229. {
  230. if(--vpp_counter == 0)
  231. setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x8);
  232. else if(vpp_counter < 0)
  233. BUG();
  234. }
  235. spin_unlock_irq(&dnpc_spin);
  236. }
  237. #define DNP_WINDOW_SIZE 0x00200000 /*  DNP flash size is 2MiB  */
  238. #define ADNP_WINDOW_SIZE 0x00400000 /* ADNP flash size is 4MiB */
  239. #define WINDOW_ADDR FLASH_BASE
  240. static struct map_info dnpc_map = {
  241. name: "ADNP Flash Bank",
  242. size: ADNP_WINDOW_SIZE,
  243. buswidth: 1,
  244. read8: dnpc_read8,
  245. read16: dnpc_read16,
  246. read32: dnpc_read32,
  247. copy_from: dnpc_copy_from,
  248. write8: dnpc_write8,
  249. write16: dnpc_write16,
  250. write32: dnpc_write32,
  251. copy_to: dnpc_copy_to,
  252. set_vpp: adnp_set_vpp,
  253. map_priv_2: WINDOW_ADDR
  254. };
  255. /*
  256. ** The layout of the flash is somewhat "strange":
  257. **
  258. ** 1.  960 KiB (15 blocks) : Space for ROM Bootloader and user data
  259. ** 2.   64 KiB (1 block)   : System BIOS
  260. ** 3.  960 KiB (15 blocks) : User Data (DNP model) or
  261. ** 3. 3008 KiB (47 blocks) : User Data (ADNP model)
  262. ** 4.   64 KiB (1 block)   : System BIOS Entry
  263. */
  264. static struct mtd_partition partition_info[]=
  265. {
  266. name: "ADNP boot", 
  267. offset: 0, 
  268. size: 0xf0000,
  269. },
  270. name: "ADNP system BIOS", 
  271. offset: MTDPART_OFS_NXTBLK,
  272. size: 0x10000,
  273. #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
  274. mask_flags: MTD_WRITEABLE,
  275. #endif
  276. },
  277. {
  278. name: "ADNP file system",
  279. offset: MTDPART_OFS_NXTBLK,
  280. size: 0x2f0000,
  281. },
  282. {
  283. name: "ADNP system BIOS entry", 
  284. offset: MTDPART_OFS_NXTBLK,
  285. size: MTDPART_SIZ_FULL,
  286. #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
  287. mask_flags: MTD_WRITEABLE,
  288. #endif
  289. },
  290. };
  291. #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
  292. static struct mtd_info *mymtd;
  293. static struct mtd_info *lowlvl_parts[NUM_PARTITIONS];
  294. static struct mtd_info *merged_mtd;
  295. /*
  296. ** "Highlevel" partition info:
  297. **
  298. ** Using the MTD concat layer, we can re-arrange partitions to our
  299. ** liking: we construct a virtual MTD device by concatenating the
  300. ** partitions, specifying the sequence such that the boot block
  301. ** is immediately followed by the filesystem block (i.e. the stupid
  302. ** system BIOS block is mapped to a different place). When re-partitioning
  303. ** this concatenated MTD device, we can set the boot block size to
  304. ** an arbitrary (though erase block aligned) value i.e. not one that
  305. ** is dictated by the flash's physical layout. We can thus set the
  306. ** boot block to be e.g. 64 KB (which is fully sufficient if we want
  307. ** to boot an etherboot image) or to -say- 1.5 MB if we want to boot
  308. ** a large kernel image. In all cases, the remainder of the flash
  309. ** is available as file system space.
  310. */
  311. static struct mtd_partition higlvl_partition_info[]=
  312. {
  313. name: "ADNP boot block", 
  314. offset: 0, 
  315. size: CONFIG_MTD_DILNETPC_BOOTSIZE,
  316. },
  317. {
  318. name: "ADNP file system space",
  319. offset: MTDPART_OFS_NXTBLK,
  320. size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
  321. },
  322. name: "ADNP system BIOS + BIOS Entry", 
  323. offset: MTDPART_OFS_NXTBLK,
  324. size: MTDPART_SIZ_FULL,
  325. #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
  326. mask_flags: MTD_WRITEABLE,
  327. #endif
  328. },
  329. };
  330. #define NUM_HIGHLVL_PARTITIONS (sizeof(higlvl_partition_info)/sizeof(partition_info[0]))
  331. static int dnp_adnp_probe(void)
  332. {
  333. char *biosid, rc = -1;
  334. biosid = (char*)ioremap(BIOSID_BASE, 16);
  335. if(biosid)
  336. {
  337. if(!strcmp(biosid, ID_DNPC))
  338. rc = 1; /* this is a DNPC  */
  339. else if(!strcmp(biosid, ID_ADNP))
  340. rc = 0; /* this is a ADNPC */
  341. }
  342. iounmap((void *)biosid);
  343. return(rc);
  344. }
  345. static int __init init_dnpc(void)
  346. {
  347. int is_dnp;
  348. /*
  349. ** determine hardware (DNP/ADNP/invalid)
  350. */
  351. if((is_dnp = dnp_adnp_probe()) < 0)
  352. return -ENXIO;
  353. /*
  354. ** Things are set up for ADNP by default
  355. ** -> modify all that needs to be different for DNP
  356. */
  357. if(is_dnp)
  358. { /*
  359. ** Adjust window size, select correct set_vpp function.
  360. ** The partitioning scheme is identical on both DNP
  361. ** and ADNP except for the size of the third partition.
  362. */
  363. int i;
  364. dnpc_map.size          = DNP_WINDOW_SIZE;
  365. dnpc_map.set_vpp       = dnp_set_vpp;
  366. partition_info[2].size = 0xf0000;
  367. /*
  368. ** increment all string pointers so the leading 'A' gets skipped,
  369. ** thus turning all occurrences of "ADNP ..." into "DNP ..."
  370. */
  371. ++dnpc_map.name;
  372. for(i = 0; i < NUM_PARTITIONS; i++)
  373. ++partition_info[i].name;
  374. higlvl_partition_info[1].size = DNP_WINDOW_SIZE - 
  375. CONFIG_MTD_DILNETPC_BOOTSIZE - 0x20000;
  376. for(i = 0; i < NUM_HIGHLVL_PARTITIONS; i++)
  377. ++higlvl_partition_info[i].name;
  378. }
  379. printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lxn", 
  380. is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2);
  381. dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size);
  382. dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size);
  383. if (!dnpc_map.map_priv_1) {
  384. printk("Failed to ioremap_nocachen");
  385. return -EIO;
  386. }
  387. printk("FLASH virtual address: 0x%lxn", dnpc_map.map_priv_1);
  388. mymtd = do_map_probe("jedec_probe", &dnpc_map);
  389. if (!mymtd)
  390. mymtd = do_map_probe("cfi_probe", &dnpc_map);
  391. /*
  392. ** If flash probes fail, try to make flashes accessible
  393. ** at least as ROM. Ajust erasesize in this case since
  394. ** the default one (128M) will break our partitioning
  395. */
  396. if (!mymtd)
  397. if((mymtd = do_map_probe("map_rom", &dnpc_map)))
  398. mymtd->erasesize = 0x10000;
  399. if (!mymtd) {
  400. iounmap((void *)dnpc_map.map_priv_1);
  401. return -ENXIO;
  402. }
  403. mymtd->module = THIS_MODULE;
  404. /*
  405. ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions()
  406. ** -> add_mtd_partitions() will _not_ register MTD devices for
  407. ** the partitions, but will instead store pointers to the MTD
  408. ** objects it creates into our lowlvl_parts[] array.
  409. ** NOTE: we arrange the pointers such that the sequence of the
  410. **       partitions gets re-arranged: partition #2 follows
  411. **       partition #0.
  412. */
  413. partition_info[0].mtdp = &lowlvl_parts[0];
  414. partition_info[1].mtdp = &lowlvl_parts[2];
  415. partition_info[2].mtdp = &lowlvl_parts[1];
  416. partition_info[3].mtdp = &lowlvl_parts[3];
  417. add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
  418. /*
  419. ** now create a virtual MTD device by concatenating the for partitions
  420. ** (in the sequence given by the lowlvl_parts[] array.
  421. */
  422. merged_mtd = mtd_concat_create(lowlvl_parts, NUM_PARTITIONS, "(A)DNP Flash Concatenated");
  423. if(merged_mtd)
  424. { /*
  425. ** now partition the new device the way we want it. This time,
  426. ** we do not supply mtd pointers in higlvl_partition_info, so
  427. ** add_mtd_partitions() will register the devices.
  428. */
  429. add_mtd_partitions(merged_mtd, higlvl_partition_info, NUM_HIGHLVL_PARTITIONS);
  430. }
  431. return 0;
  432. }
  433. static void __exit cleanup_dnpc(void)
  434. {
  435. if(merged_mtd) {
  436. del_mtd_partitions(merged_mtd);
  437. mtd_concat_destroy(merged_mtd);
  438. }
  439. if (mymtd) {
  440. del_mtd_partitions(mymtd);
  441. map_destroy(mymtd);
  442. }
  443. if (dnpc_map.map_priv_1) {
  444. iounmap((void *)dnpc_map.map_priv_1);
  445. dnpc_unmap_flash();
  446. dnpc_map.map_priv_1 = 0;
  447. }
  448. }
  449. module_init(init_dnpc);
  450. module_exit(cleanup_dnpc);
  451. MODULE_LICENSE("GPL");
  452. MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
  453. MODULE_DESCRIPTION("MTD map driver for SSV DIL/NetPC DNP & ADNP");