aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/pxamci.h
blob: f6c2e2fcce370adbb533d14dc5017ee28443bc5d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#define MMC_STRPCL	0x0000
#define STOP_CLOCK		(1 << 0)
#define START_CLOCK		(2 << 0)

#define MMC_STAT	0x0004
#define STAT_END_CMD_RES		(1 << 13)
#define STAT_PRG_DONE			(1 << 12)
#define STAT_DATA_TRAN_DONE		(1 << 11)
#define STAT_CLK_EN			(1 << 8)
#define STAT_RECV_FIFO_FULL		(1 << 7)
#define STAT_XMIT_FIFO_EMPTY		(1 << 6)
#define STAT_RES_CRC_ERR		(1 << 5)
#define STAT_SPI_READ_ERROR_TOKEN	(1 << 4)
#define STAT_CRC_READ_ERROR		(1 << 3)
#define STAT_CRC_WRITE_ERROR		(1 << 2)
#define STAT_TIME_OUT_RESPONSE		(1 << 1)
#define STAT_READ_TIME_OUT		(1 << 0)

#define MMC_CLKRT	0x0008		/* 3 bit */

#define MMC_SPI		0x000c
#define SPI_CS_ADDRESS		(1 << 3)
#define SPI_CS_EN		(1 << 2)
#define CRC_ON			(1 << 1)
#define SPI_EN			(1 << 0)

#define MMC_CMDAT	0x0010
#define CMDAT_SDIO_INT_EN	(1 << 11)
#define CMDAT_SD_4DAT		(1 << 8)
#define CMDAT_DMAEN		(1 << 7)
#define CMDAT_INIT		(1 << 6)
#define CMDAT_BUSY		(1 << 5)
#define CMDAT_STREAM		(1 << 4)	/* 1 = stream */
#define CMDAT_WRITE		(1 << 3)	/* 1 = write */
#define CMDAT_DATAEN		(1 << 2)
#define CMDAT_RESP_NONE		(0 << 0)
#define CMDAT_RESP_SHORT	(1 << 0)
#define CMDAT_RESP_R2		(2 << 0)
#define CMDAT_RESP_R3		(3 << 0)

#define MMC_RESTO	0x0014	/* 7 bit */

#define MMC_RDTO	0x0018	/* 16 bit */

#define MMC_BLKLEN	0x001c	/* 10 bit */

#define MMC_NOB		0x0020	/* 16 bit */

#define MMC_PRTBUF	0x0024
#define BUF_PART_FULL		(1 << 0)

#define MMC_I_MASK	0x0028

/*PXA27x MMC interrupts*/
#define SDIO_SUSPEND_ACK  	(1 << 12)
#define SDIO_INT          	(1 << 11)
#define RD_STALLED        	(1 << 10)
#define RES_ERR           	(1 << 9)
#define DAT_ERR           	(1 << 8)
#define TINT              	(1 << 7)

/*PXA2xx MMC interrupts*/
#define TXFIFO_WR_REQ		(1 << 6)
#define RXFIFO_RD_REQ		(1 << 5)
#define CLK_IS_OFF		(1 << 4)
#define STOP_CMD		(1 << 3)
#define END_CMD_RES		(1 << 2)
#define PRG_DONE		(1 << 1)
#define DATA_TRAN_DONE		(1 << 0)

#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
#define MMC_I_MASK_ALL          0x00001fff
#else
#define MMC_I_MASK_ALL          0x0000007f
#endif

#define MMC_I_REG	0x002c
/* same as MMC_I_MASK */

#define MMC_CMD		0x0030

#define MMC_ARGH	0x0034	/* 16 bit */

#define MMC_ARGL	0x0038	/* 16 bit */

#define MMC_RES		0x003c	/* 16 bit */

#define MMC_RXFIFO	0x0040	/* 8 bit */

#define MMC_TXFIFO	0x0044	/* 8 bit */
goto@jp.fujitsu.com> 2008-04-28 05:13:31 -0400 committer Linus Torvalds <torvalds@linux-foundation.org> 2008-04-28 11:58:25 -0400 memory hotplug: register section/node id to free' href='/cgit/cgit.cgi/litmus-rt-budgetable-locks.git/.git/commit/mm/memory_hotplug.c?h=update_litmus_2019&id=04753278769f3b6c3b79a080edb52f21d83bf6e2'>04753278769f
76cdd58e5586





























31168481c32c
3947be1969a9




76cdd58e5586
3947be1969a9

76cdd58e5586












a2f3aa025766

718127cc3170
3947be1969a9

c04fc586c1a4

3947be1969a9
3947be1969a9
3947be1969a9

ebd15302dc0b


0b0acbec1bed
3947be1969a9



718127cc3170




c04fc586c1a4
3947be1969a9

0c0a4a517a31









ea01ea937dca

















0c0a4a517a31
ea01ea937dca
3947be1969a9





c04fc586c1a4

3947be1969a9


6f712711dbd1



3947be1969a9
6f712711dbd1
c04fc586c1a4
3947be1969a9
6f712711dbd1
183ff22bb6bd
6f712711dbd1

bed120c64eb0

3947be1969a9
6f712711dbd1
3947be1969a9



bed120c64eb0
3947be1969a9
ea01ea937dca






















ea01ea937dca


de7f0cba9678

ea01ea937dca







180c06efce69

4738e1b9cf8f

180c06efce69
4738e1b9cf8f

180c06efce69














75884fb1c638

3947be1969a9

75884fb1c638














3947be1969a9

6811378e7d8b
7b78d335ac15










3947be1969a9
7b78d335ac15





3947be1969a9


da19cbcf71cd
3947be1969a9

6811378e7d8b







908eedc6168b
75884fb1c638
fd8a4221ad76






3947be1969a9
f2937be5895d
3947be1969a9
112067f0905b
bc75d33f0fc1
bce7394a3ef8
7ea1530ab3fd



61b13993a818
6811378e7d8b

2f7f24eca31c


2d1d43f6a43b
7b78d335ac15



3947be1969a9

53947027ad90
bc02af93dd2b
e13193319d3a

9af3c2dea3a3














9109fb7b3520
9af3c2dea3a3










0a54703904a4
31168481c32c

bc02af93dd2b
9af3c2dea3a3

ebd15302dc0b
bc02af93dd2b

6ad696d2cf53

ebd15302dc0b
6ad696d2cf53
ebd15302dc0b
6ad696d2cf53
ebd15302dc0b
9af3c2dea3a3

6ad696d2cf53
9af3c2dea3a3
6ad696d2cf53
9af3c2dea3a3
9af3c2dea3a3

bc02af93dd2b


9af3c2dea3a3


0fc44159bfcb
9af3c2dea3a3

0fc44159bfcb









d96ae5309165


6ad696d2cf53

9af3c2dea3a3



ebd15302dc0b

9af3c2dea3a3
6ad696d2cf53

bc02af93dd2b


0c0e61958965


5c755e9fd813



























































0c0e61958965











































3c1d43787b48
0c0e61958965
3c1d43787b48

0c0e61958965

0c0e61958965




















62695a84eb8f
0c0e61958965
62695a84eb8f
0c0e61958965
6d9c285a632b


0c0e61958965





















62b61f611eb5
0c0e61958965


















908eedc6168b
0c0e61958965
























908eedc6168b
0c0e61958965





b4e655a4aaa3
0c0e61958965



7b78d335ac15
0c0e61958965
7b78d335ac15
0c0e61958965










7b78d335ac15
6ad696d2cf53

7b78d335ac15



0c0e61958965


6ad696d2cf53
7b78d335ac15











0c0e61958965
















9f8f2172537d
0c0e61958965





















9f8f2172537d
0c0e61958965









dbc0e4cefd00

0c0e61958965
0c0e61958965


7b78d335ac15
bce7394a3ef8

8fe23e057172



bce7394a3ef8
0c0e61958965

7b78d335ac15

6ad696d2cf53
0c0e61958965




7b78d335ac15
0c0e61958965

7b78d335ac15
6ad696d2cf53

0c0e61958965

71088785c6bc








48e94196a533




0c0e61958965
71088785c6bc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900





                             








                            
                            






                                 
                         


                                 
                      
                          
                            
                               


                         

                     









                                                                     
                                                      

















                                                                              
                                   
                                
                                                                              
 
                                           




                                     


                                                            
 
                 
 

                                             









                                                    
                                                                       































                                                                        
                                                                     






































                                                                        





























                                                                               
                                                                                




                                                     
                            

                                             












                                                                          

                                                         
                 

 

                                                                     
 
                                         

                


                                      
                                                                     



                           




                                               
                                                                          

 









                                                                      

















                                                                      
      
 





                                                         

                                                                               


                        



                                                                         
 
                                                
                                                                       
 
                  
                                                                       

                                                                        

                                            
                              
                        



                   
                               
 






















                                                                              


                                                                         

                                                                    







                                                                    

                                   

                                              
                         

                                        














                                                      

                                                                              

                        














                                                            

                                        
                                       










                                            
 





                                                       


                                                           
                                     

                                           







                                                                     
                                                                  
                                    






                                                                     
                                             
                                                              
 
                              
                                
                                            



                                                                 
 

                                      


                                                           
                                  



                                                

                 
                                         
 

                                                                            














                                                                               
                                                                     










                                                           
 

                                                                            
 

                                
                             

                

                            
                                                    
                      
                 
                         
 

                                                     
                              
                           
                                 
                              

         


                                                


                           
                                                                

                             









                                                                          


                                                                    

                 



                                                  

                                             
 

                              


                              


                              



























































                                                                               











































                                                                               
                                                                          
 

                                                

 




















                                                                             
                                             
                                         
                                                           
                                     


                                                                      





















                                                                          
                                                                    


















                                                                      
                                                                   
























                                                                        
                                                                              





                                                 
                                                 



                                                               
                                        
                          
                                 










                                                                
 

                            



                                                 


                                                           
                         











                                                     
















                                        
                                  





















                                                                  
                          









                                                                  

                                                                       
                             


                                                               
 

                                            



                                                      
 

                                                   

                                         
                              




                                                                
                                                

                                                    
 

                              

                   








                                                           




                                      
                                    
                                 
/*
 *  linux/mm/memory_hotplug.c
 *
 *  Copyright (C)
 */

#include <linux/stddef.h>
#include <linux/mm.h>
#include <linux/swap.h>
#include <linux/interrupt.h>
#include <linux/pagemap.h>
#include <linux/bootmem.h>
#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/pagevec.h>
#include <linux/writeback.h>
#include <linux/slab.h>
#include <linux/sysctl.h>
#include <linux/cpu.h>
#include <linux/memory.h>
#include <linux/memory_hotplug.h>
#include <linux/highmem.h>
#include <linux/vmalloc.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/migrate.h>
#include <linux/page-isolation.h>
#include <linux/pfn.h>
#include <linux/suspend.h>
#include <linux/mm_inline.h>
#include <linux/firmware-map.h>

#include <asm/tlbflush.h>

#include "internal.h"

/* add this memory to iomem resource */
static struct resource *register_memory_resource(u64 start, u64 size)
{
	struct resource *res;
	res = kzalloc(sizeof(struct resource), GFP_KERNEL);
	BUG_ON(!res);

	res->name = "System RAM";
	res->start = start;
	res->end = start + size - 1;
	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
	if (request_resource(&iomem_resource, res) < 0) {
		printk("System RAM resource %llx - %llx cannot be added\n",
		(unsigned long long)res->start, (unsigned long long)res->end);
		kfree(res);
		res = NULL;
	}
	return res;
}

static void release_memory_resource(struct resource *res)
{
	if (!res)
		return;
	release_resource(res);
	kfree(res);
	return;
}

#ifdef CONFIG_MEMORY_HOTPLUG_SPARSE
#ifndef CONFIG_SPARSEMEM_VMEMMAP
static void get_page_bootmem(unsigned long info,  struct page *page, int type)
{
	atomic_set(&page->_mapcount, type);
	SetPagePrivate(page);
	set_page_private(page, info);
	atomic_inc(&page->_count);
}

/* reference to __meminit __free_pages_bootmem is valid
 * so use __ref to tell modpost not to generate a warning */
void __ref put_page_bootmem(struct page *page)
{
	int type;

	type = atomic_read(&page->_mapcount);
	BUG_ON(type >= -1);

	if (atomic_dec_return(&page->_count) == 1) {
		ClearPagePrivate(page);
		set_page_private(page, 0);
		reset_page_mapcount(page);
		__free_pages_bootmem(page, 0);
	}

}

static void register_page_bootmem_info_section(unsigned long start_pfn)
{
	unsigned long *usemap, mapsize, section_nr, i;
	struct mem_section *ms;
	struct page *page, *memmap;

	if (!pfn_valid(start_pfn))
		return;

	section_nr = pfn_to_section_nr(start_pfn);
	ms = __nr_to_section(section_nr);

	/* Get section's memmap address */
	memmap = sparse_decode_mem_map(ms->section_mem_map, section_nr);

	/*
	 * Get page for the memmap's phys address
	 * XXX: need more consideration for sparse_vmemmap...
	 */
	page = virt_to_page(memmap);
	mapsize = sizeof(struct page) * PAGES_PER_SECTION;
	mapsize = PAGE_ALIGN(mapsize) >> PAGE_SHIFT;

	/* remember memmap's page */
	for (i = 0; i < mapsize; i++, page++)
		get_page_bootmem(section_nr, page, SECTION_INFO);

	usemap = __nr_to_section(section_nr)->pageblock_flags;
	page = virt_to_page(usemap);

	mapsize = PAGE_ALIGN(usemap_size()) >> PAGE_SHIFT;

	for (i = 0; i < mapsize; i++, page++)
		get_page_bootmem(section_nr, page, MIX_SECTION_INFO);

}

void register_page_bootmem_info_node(struct pglist_data *pgdat)
{
	unsigned long i, pfn, end_pfn, nr_pages;
	int node = pgdat->node_id;
	struct page *page;
	struct zone *zone;

	nr_pages = PAGE_ALIGN(sizeof(struct pglist_data)) >> PAGE_SHIFT;
	page = virt_to_page(pgdat);

	for (i = 0; i < nr_pages; i++, page++)
		get_page_bootmem(node, page, NODE_INFO);

	zone = &pgdat->node_zones[0];
	for (; zone < pgdat->node_zones + MAX_NR_ZONES - 1; zone++) {
		if (zone->wait_table) {
			nr_pages = zone->wait_table_hash_nr_entries
				* sizeof(wait_queue_head_t);
			nr_pages = PAGE_ALIGN(nr_pages) >> PAGE_SHIFT;
			page = virt_to_page(zone->wait_table);

			for (i = 0; i < nr_pages; i++, page++)
				get_page_bootmem(node, page, NODE_INFO);
		}
	}

	pfn = pgdat->node_start_pfn;
	end_pfn = pfn + pgdat->node_spanned_pages;

	/* register_section info */
	for (; pfn < end_pfn; pfn += PAGES_PER_SECTION)
		register_page_bootmem_info_section(pfn);

}
#endif /* !CONFIG_SPARSEMEM_VMEMMAP */

static void grow_zone_span(struct zone *zone, unsigned long start_pfn,
			   unsigned long end_pfn)
{
	unsigned long old_zone_end_pfn;

	zone_span_writelock(zone);

	old_zone_end_pfn = zone->zone_start_pfn + zone->spanned_pages;
	if (start_pfn < zone->zone_start_pfn)
		zone->zone_start_pfn = start_pfn;

	zone->spanned_pages = max(old_zone_end_pfn, end_pfn) -
				zone->zone_start_pfn;

	zone_span_writeunlock(zone);
}

static void grow_pgdat_span(struct pglist_data *pgdat, unsigned long start_pfn,
			    unsigned long end_pfn)
{
	unsigned long old_pgdat_end_pfn =
		pgdat->node_start_pfn + pgdat->node_spanned_pages;

	if (start_pfn < pgdat->node_start_pfn)
		pgdat->node_start_pfn = start_pfn;

	pgdat->node_spanned_pages = max(old_pgdat_end_pfn, end_pfn) -
					pgdat->node_start_pfn;
}

static int __meminit __add_zone(struct zone *zone, unsigned long phys_start_pfn)
{
	struct pglist_data *pgdat = zone->zone_pgdat;
	int nr_pages = PAGES_PER_SECTION;
	int nid = pgdat->node_id;
	int zone_type;
	unsigned long flags;

	zone_type = zone - pgdat->node_zones;
	if (!zone->wait_table) {
		int ret;

		ret = init_currently_empty_zone(zone, phys_start_pfn,
						nr_pages, MEMMAP_HOTPLUG);
		if (ret)
			return ret;
	}
	pgdat_resize_lock(zone->zone_pgdat, &flags);
	grow_zone_span(zone, phys_start_pfn, phys_start_pfn + nr_pages);
	grow_pgdat_span(zone->zone_pgdat, phys_start_pfn,
			phys_start_pfn + nr_pages);
	pgdat_resize_unlock(zone->zone_pgdat, &flags);
	memmap_init_zone(nr_pages, nid, zone_type,
			 phys_start_pfn, MEMMAP_HOTPLUG);
	return 0;
}

static int __meminit __add_section(int nid, struct zone *zone,
					unsigned long phys_start_pfn)
{
	int nr_pages = PAGES_PER_SECTION;
	int ret;

	if (pfn_valid(phys_start_pfn))
		return -EEXIST;

	ret = sparse_add_one_section(zone, phys_start_pfn, nr_pages);

	if (ret < 0)
		return ret;

	ret = __add_zone(zone, phys_start_pfn);

	if (ret < 0)
		return ret;

	return register_new_memory(nid, __pfn_to_section(phys_start_pfn));
}

#ifdef CONFIG_SPARSEMEM_VMEMMAP
static int __remove_section(struct zone *zone, struct mem_section *ms)
{
	/*
	 * XXX: Freeing memmap with vmemmap is not implement yet.
	 *      This should be removed later.
	 */
	return -EBUSY;
}
#else
static int __remove_section(struct zone *zone, struct mem_section *ms)
{
	unsigned long flags;
	struct pglist_data *pgdat = zone->zone_pgdat;
	int ret = -EINVAL;

	if (!valid_section(ms))
		return ret;

	ret = unregister_memory_section(ms);
	if (ret)
		return ret;

	pgdat_resize_lock(pgdat, &flags);
	sparse_remove_one_section(zone, ms);
	pgdat_resize_unlock(pgdat, &flags);
	return 0;
}
#endif

/*
 * Reasonably generic function for adding memory.  It is
 * expected that archs that support memory hotplug will
 * call this function after deciding the zone to which to
 * add the new pages.
 */
int __ref __add_pages(int nid, struct zone *zone, unsigned long phys_start_pfn,
			unsigned long nr_pages)
{
	unsigned long i;
	int err = 0;
	int start_sec, end_sec;
	/* during initialize mem_map, align hot-added range to section */
	start_sec = pfn_to_section_nr(phys_start_pfn);
	end_sec = pfn_to_section_nr(phys_start_pfn + nr_pages - 1);

	for (i = start_sec; i <= end_sec; i++) {
		err = __add_section(nid, zone, i << PFN_SECTION_SHIFT);

		/*
		 * EEXIST is finally dealt with by ioresource collision
		 * check. see add_memory() => register_memory_resource()
		 * Warning will be printed if there is collision.
		 */
		if (err && (err != -EEXIST))
			break;
		err = 0;
	}

	return err;
}
EXPORT_SYMBOL_GPL(__add_pages);

/**
 * __remove_pages() - remove sections of pages from a zone
 * @zone: zone from which pages need to be removed
 * @phys_start_pfn: starting pageframe (must be aligned to start of a section)
 * @nr_pages: number of pages to remove (must be multiple of section size)
 *
 * Generic helper function to remove section mappings and sysfs entries
 * for the section of the memory we are removing. Caller needs to make
 * sure that pages are marked reserved and zones are adjust properly by
 * calling offline_pages().
 */
int __remove_pages(struct zone *zone, unsigned long phys_start_pfn,
		 unsigned long nr_pages)
{
	unsigned long i, ret = 0;
	int sections_to_remove;

	/*
	 * We can only remove entire sections
	 */
	BUG_ON(phys_start_pfn & ~PAGE_SECTION_MASK);
	BUG_ON(nr_pages % PAGES_PER_SECTION);

	sections_to_remove = nr_pages / PAGES_PER_SECTION;
	for (i = 0; i < sections_to_remove; i++) {
		unsigned long pfn = phys_start_pfn + i*PAGES_PER_SECTION;
		release_mem_region(pfn << PAGE_SHIFT,
				   PAGES_PER_SECTION << PAGE_SHIFT);
		ret = __remove_section(zone, __pfn_to_section(pfn));
		if (ret)
			break;
	}
	return ret;
}
EXPORT_SYMBOL_GPL(__remove_pages);

void online_page(struct page *page)
{
	unsigned long pfn = page_to_pfn(page);

	totalram_pages++;
	if (pfn >= num_physpages)
		num_physpages = pfn + 1;

#ifdef CONFIG_HIGHMEM
	if (PageHighMem(page))
		totalhigh_pages++;
#endif

#ifdef CONFIG_FLATMEM
	max_mapnr = max(page_to_pfn(page), max_mapnr);
#endif

	ClearPageReserved(page);
	init_page_count(page);
	__free_page(page);
}

static int online_pages_range(unsigned long start_pfn, unsigned long nr_pages,
			void *arg)
{
	unsigned long i;
	unsigned long onlined_pages = *(unsigned long *)arg;
	struct page *page;
	if (PageReserved(pfn_to_page(start_pfn)))
		for (i = 0; i < nr_pages; i++) {
			page = pfn_to_page(start_pfn + i);
			online_page(page);
			onlined_pages++;
		}
	*(unsigned long *)arg = onlined_pages;
	return 0;
}


int online_pages(unsigned long pfn, unsigned long nr_pages)
{
	unsigned long onlined_pages = 0;
	struct zone *zone;
	int need_zonelists_rebuild = 0;
	int nid;
	int ret;
	struct memory_notify arg;

	arg.start_pfn = pfn;
	arg.nr_pages = nr_pages;
	arg.status_change_nid = -1;

	nid = page_to_nid(pfn_to_page(pfn));
	if (node_present_pages(nid) == 0)
		arg.status_change_nid = nid;

	ret = memory_notify(MEM_GOING_ONLINE, &arg);
	ret = notifier_to_errno(ret);
	if (ret) {
		memory_notify(MEM_CANCEL_ONLINE, &arg);
		return ret;
	}
	/*
	 * This doesn't need a lock to do pfn_to_page().
	 * The section can't be removed here because of the
	 * memory_block->state_mutex.
	 */
	zone = page_zone(pfn_to_page(pfn));
	/*
	 * If this zone is not populated, then it is not in zonelist.
	 * This means the page allocator ignores this zone.
	 * So, zonelist must be updated after online.
	 */
	if (!populated_zone(zone))
		need_zonelists_rebuild = 1;

	ret = walk_system_ram_range(pfn, nr_pages, &onlined_pages,
		online_pages_range);
	if (ret) {
		printk(KERN_DEBUG "online_pages %lx at %lx failed\n",
			nr_pages, pfn);
		memory_notify(MEM_CANCEL_ONLINE, &arg);
		return ret;
	}

	zone->present_pages += onlined_pages;
	zone->zone_pgdat->node_present_pages += onlined_pages;

	zone_pcp_update(zone);
	setup_per_zone_wmarks();
	calculate_zone_inactive_ratio(zone);
	if (onlined_pages) {
		kswapd_run(zone_to_nid(zone));
		node_set_state(zone_to_nid(zone), N_HIGH_MEMORY);
	}

	if (need_zonelists_rebuild)
		build_all_zonelists();
	else
		vm_total_pages = nr_free_pagecache_pages();

	writeback_set_ratelimit();

	if (onlined_pages)
		memory_notify(MEM_ONLINE, &arg);

	return 0;
}
#endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */

/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
static pg_data_t __ref *hotadd_new_pgdat(int nid, u64 start)
{
	struct pglist_data *pgdat;
	unsigned long zones_size[MAX_NR_ZONES] = {0};
	unsigned long zholes_size[MAX_NR_ZONES] = {0};
	unsigned long start_pfn = start >> PAGE_SHIFT;

	pgdat = arch_alloc_nodedata(nid);
	if (!pgdat)
		return NULL;

	arch_refresh_nodedata(nid, pgdat);

	/* we can use NODE_DATA(nid) from here */

	/* init node's zones as empty zones, we don't have any present pages.*/
	free_area_init_node(nid, zones_size, start_pfn, zholes_size);

	return pgdat;
}

static void rollback_node_hotadd(int nid, pg_data_t *pgdat)
{
	arch_refresh_nodedata(nid, NULL);
	arch_free_nodedata(pgdat);
	return;
}


/* we are OK calling __meminit stuff here - we have CONFIG_MEMORY_HOTPLUG */
int __ref add_memory(int nid, u64 start, u64 size)
{
	pg_data_t *pgdat = NULL;
	int new_pgdat = 0;
	struct resource *res;
	int ret;

	lock_system_sleep();

	res = register_memory_resource(start, size);
	ret = -EEXIST;
	if (!res)
		goto out;

	if (!node_online(nid)) {
		pgdat = hotadd_new_pgdat(nid, start);
		ret = -ENOMEM;
		if (!pgdat)
			goto out;
		new_pgdat = 1;
	}

	/* call arch's memory hotadd */
	ret = arch_add_memory(nid, start, size);

	if (ret < 0)
		goto error;

	/* we online node here. we can't roll back from here. */
	node_set_online(nid);

	if (new_pgdat) {
		ret = register_one_node(nid);
		/*
		 * If sysfs file of new node can't create, cpu on the node
		 * can't be hot-added. There is no rollback way now.
		 * So, check by BUG_ON() to catch it reluctantly..
		 */
		BUG_ON(ret);
	}

	/* create new memmap entry */
	firmware_map_add_hotplug(start, start + size, "System RAM");

	goto out;

error:
	/* rollback pgdat allocation and others */
	if (new_pgdat)
		rollback_node_hotadd(nid, pgdat);
	if (res)
		release_memory_resource(res);

out:
	unlock_system_sleep();
	return ret;
}
EXPORT_SYMBOL_GPL(add_memory);

#ifdef CONFIG_MEMORY_HOTREMOVE
/*
 * A free page on the buddy free lists (not the per-cpu lists) has PageBuddy
 * set and the size of the free page is given by page_order(). Using this,
 * the function determines if the pageblock contains only free pages.
 * Due to buddy contraints, a free page at least the size of a pageblock will
 * be located at the start of the pageblock
 */
static inline int pageblock_free(struct page *page)
{
	return PageBuddy(page) && page_order(page) >= pageblock_order;
}

/* Return the start of the next active pageblock after a given page */
static struct page *next_active_pageblock(struct page *page)
{
	int pageblocks_stride;

	/* Ensure the starting page is pageblock-aligned */
	BUG_ON(page_to_pfn(page) & (pageblock_nr_pages - 1));

	/* Move forward by at least 1 * pageblock_nr_pages */
	pageblocks_stride = 1;

	/* If the entire pageblock is free, move to the end of free page */
	if (pageblock_free(page))
		pageblocks_stride += page_order(page) - pageblock_order;

	return page + (pageblocks_stride * pageblock_nr_pages);
}

/* Checks if this range of memory is likely to be hot-removable. */
int is_mem_section_removable(unsigned long start_pfn, unsigned long nr_pages)
{
	int type;
	struct page *page = pfn_to_page(start_pfn);
	struct page *end_page = page + nr_pages;

	/* Check the starting page of each pageblock within the range */
	for (; page < end_page; page = next_active_pageblock(page)) {
		type = get_pageblock_migratetype(page);

		/*
		 * A pageblock containing MOVABLE or free pages is considered
		 * removable
		 */
		if (type != MIGRATE_MOVABLE && !pageblock_free(page))
			return 0;

		/*
		 * A pageblock starting with a PageReserved page is not
		 * considered removable.
		 */
		if (PageReserved(page))
			return 0;
	}

	/* All pageblocks in the memory block are likely to be hot-removable */
	return 1;
}

/*
 * Confirm all pages in a range [start, end) is belongs to the same zone.
 */
static int test_pages_in_a_zone(unsigned long start_pfn, unsigned long end_pfn)
{
	unsigned long pfn;
	struct zone *zone = NULL;
	struct page *page;
	int i;
	for (pfn = start_pfn;
	     pfn < end_pfn;
	     pfn += MAX_ORDER_NR_PAGES) {
		i = 0;
		/* This is just a CONFIG_HOLES_IN_ZONE check.*/
		while ((i < MAX_ORDER_NR_PAGES) && !pfn_valid_within(pfn + i))
			i++;
		if (i == MAX_ORDER_NR_PAGES)
			continue;
		page = pfn_to_page(pfn + i);
		if (zone && page_zone(page) != zone)
			return 0;
		zone = page_zone(page);
	}
	return 1;
}

/*
 * Scanning pfn is much easier than scanning lru list.
 * Scan pfn from start to end and Find LRU page.
 */
int scan_lru_pages(unsigned long start, unsigned long end)
{
	unsigned long pfn;
	struct page *page;
	for (pfn = start; pfn < end; pfn++) {
		if (pfn_valid(pfn)) {
			page = pfn_to_page(pfn);