diff options
Diffstat (limited to 'drivers/pcmcia/rsrc_nonstatic.c')
-rw-r--r-- | drivers/pcmcia/rsrc_nonstatic.c | 170 |
1 files changed, 114 insertions, 56 deletions
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c index 5876bab7c14c..c42455d20eb6 100644 --- a/drivers/pcmcia/rsrc_nonstatic.c +++ b/drivers/pcmcia/rsrc_nonstatic.c | |||
@@ -372,6 +372,9 @@ static int do_mem_probe(u_long base, u_long num, struct pcmcia_socket *s) | |||
372 | base, base+num-1); | 372 | base, base+num-1); |
373 | bad = fail = 0; | 373 | bad = fail = 0; |
374 | step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); | 374 | step = (num < 0x20000) ? 0x2000 : ((num>>4) & ~0x1fff); |
375 | /* don't allow too large steps */ | ||
376 | if (step > 0x800000) | ||
377 | step = 0x800000; | ||
375 | /* cis_readable wants to map 2x map_size */ | 378 | /* cis_readable wants to map 2x map_size */ |
376 | if (step < 2 * s->map_size) | 379 | if (step < 2 * s->map_size) |
377 | step = 2 * s->map_size; | 380 | step = 2 * s->map_size; |
@@ -465,8 +468,7 @@ static void validate_mem(struct pcmcia_socket *s, unsigned int probe_mask) | |||
465 | 468 | ||
466 | for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { | 469 | for (m = s_data->mem_db.next; m != &s_data->mem_db; m = mm.next) { |
467 | mm = *m; | 470 | mm = *m; |
468 | if (do_mem_probe(mm.base, mm.num, s)) | 471 | do_mem_probe(mm.base, mm.num, s); |
469 | break; | ||
470 | } | 472 | } |
471 | } | 473 | } |
472 | 474 | ||
@@ -601,7 +603,7 @@ static int nonstatic_adjust_io_region(struct resource *res, unsigned long r_star | |||
601 | 603 | ||
602 | ======================================================================*/ | 604 | ======================================================================*/ |
603 | 605 | ||
604 | struct resource *nonstatic_find_io_region(unsigned long base, int num, | 606 | static struct resource *nonstatic_find_io_region(unsigned long base, int num, |
605 | unsigned long align, struct pcmcia_socket *s) | 607 | unsigned long align, struct pcmcia_socket *s) |
606 | { | 608 | { |
607 | struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id); | 609 | struct resource *res = make_resource(0, num, IORESOURCE_IO, s->dev.class_id); |
@@ -635,8 +637,8 @@ struct resource *nonstatic_find_io_region(unsigned long base, int num, | |||
635 | return res; | 637 | return res; |
636 | } | 638 | } |
637 | 639 | ||
638 | struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long align, | 640 | static struct resource * nonstatic_find_mem_region(u_long base, u_long num, |
639 | int low, struct pcmcia_socket *s) | 641 | u_long align, int low, struct pcmcia_socket *s) |
640 | { | 642 | { |
641 | struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id); | 643 | struct resource *res = make_resource(0, num, IORESOURCE_MEM, s->dev.class_id); |
642 | struct socket_data *s_data = s->resource_data; | 644 | struct socket_data *s_data = s->resource_data; |
@@ -683,27 +685,23 @@ struct resource * nonstatic_find_mem_region(u_long base, u_long num, u_long alig | |||
683 | } | 685 | } |
684 | 686 | ||
685 | 687 | ||
686 | static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) | 688 | static int adjust_memory(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) |
687 | { | 689 | { |
688 | u_long base, num; | ||
689 | struct socket_data *data = s->resource_data; | 690 | struct socket_data *data = s->resource_data; |
690 | int ret; | 691 | unsigned long size = end - start + 1; |
691 | 692 | int ret = 0; | |
692 | base = adj->resource.memory.Base; | ||
693 | num = adj->resource.memory.Size; | ||
694 | if ((num == 0) || (base+num-1 < base)) | ||
695 | return CS_BAD_SIZE; | ||
696 | 693 | ||
697 | ret = CS_SUCCESS; | 694 | if (end <= start) |
695 | return -EINVAL; | ||
698 | 696 | ||
699 | down(&rsrc_sem); | 697 | down(&rsrc_sem); |
700 | switch (adj->Action) { | 698 | switch (action) { |
701 | case ADD_MANAGED_RESOURCE: | 699 | case ADD_MANAGED_RESOURCE: |
702 | ret = add_interval(&data->mem_db, base, num); | 700 | ret = add_interval(&data->mem_db, start, size); |
703 | break; | 701 | break; |
704 | case REMOVE_MANAGED_RESOURCE: | 702 | case REMOVE_MANAGED_RESOURCE: |
705 | ret = sub_interval(&data->mem_db, base, num); | 703 | ret = sub_interval(&data->mem_db, start, size); |
706 | if (ret == CS_SUCCESS) { | 704 | if (!ret) { |
707 | struct pcmcia_socket *socket; | 705 | struct pcmcia_socket *socket; |
708 | down_read(&pcmcia_socket_list_rwsem); | 706 | down_read(&pcmcia_socket_list_rwsem); |
709 | list_for_each_entry(socket, &pcmcia_socket_list, socket_list) | 707 | list_for_each_entry(socket, &pcmcia_socket_list, socket_list) |
@@ -712,7 +710,7 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) | |||
712 | } | 710 | } |
713 | break; | 711 | break; |
714 | default: | 712 | default: |
715 | ret = CS_UNSUPPORTED_FUNCTION; | 713 | ret = -EINVAL; |
716 | } | 714 | } |
717 | up(&rsrc_sem); | 715 | up(&rsrc_sem); |
718 | 716 | ||
@@ -720,36 +718,35 @@ static int adjust_memory(struct pcmcia_socket *s, adjust_t *adj) | |||
720 | } | 718 | } |
721 | 719 | ||
722 | 720 | ||
723 | static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) | 721 | static int adjust_io(struct pcmcia_socket *s, unsigned int action, unsigned long start, unsigned long end) |
724 | { | 722 | { |
725 | struct socket_data *data = s->resource_data; | 723 | struct socket_data *data = s->resource_data; |
726 | kio_addr_t base, num; | 724 | unsigned long size = end - start + 1; |
727 | int ret = CS_SUCCESS; | 725 | int ret = 0; |
728 | 726 | ||
729 | base = adj->resource.io.BasePort; | 727 | if (end <= start) |
730 | num = adj->resource.io.NumPorts; | 728 | return -EINVAL; |
731 | if ((base < 0) || (base > 0xffff)) | 729 | |
732 | return CS_BAD_BASE; | 730 | if (end > IO_SPACE_LIMIT) |
733 | if ((num <= 0) || (base+num > 0x10000) || (base+num <= base)) | 731 | return -EINVAL; |
734 | return CS_BAD_SIZE; | ||
735 | 732 | ||
736 | down(&rsrc_sem); | 733 | down(&rsrc_sem); |
737 | switch (adj->Action) { | 734 | switch (action) { |
738 | case ADD_MANAGED_RESOURCE: | 735 | case ADD_MANAGED_RESOURCE: |
739 | if (add_interval(&data->io_db, base, num) != 0) { | 736 | if (add_interval(&data->io_db, start, size) != 0) { |
740 | ret = CS_IN_USE; | 737 | ret = -EBUSY; |
741 | break; | 738 | break; |
742 | } | 739 | } |
743 | #ifdef CONFIG_PCMCIA_PROBE | 740 | #ifdef CONFIG_PCMCIA_PROBE |
744 | if (probe_io) | 741 | if (probe_io) |
745 | do_io_probe(s, base, num); | 742 | do_io_probe(s, start, size); |
746 | #endif | 743 | #endif |
747 | break; | 744 | break; |
748 | case REMOVE_MANAGED_RESOURCE: | 745 | case REMOVE_MANAGED_RESOURCE: |
749 | sub_interval(&data->io_db, base, num); | 746 | sub_interval(&data->io_db, start, size); |
750 | break; | 747 | break; |
751 | default: | 748 | default: |
752 | ret = CS_UNSUPPORTED_FUNCTION; | 749 | ret = -EINVAL; |
753 | break; | 750 | break; |
754 | } | 751 | } |
755 | up(&rsrc_sem); | 752 | up(&rsrc_sem); |
@@ -760,15 +757,82 @@ static int adjust_io(struct pcmcia_socket *s, adjust_t *adj) | |||
760 | 757 | ||
761 | static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj) | 758 | static int nonstatic_adjust_resource_info(struct pcmcia_socket *s, adjust_t *adj) |
762 | { | 759 | { |
760 | unsigned long end; | ||
761 | |||
763 | switch (adj->Resource) { | 762 | switch (adj->Resource) { |
764 | case RES_MEMORY_RANGE: | 763 | case RES_MEMORY_RANGE: |
765 | return adjust_memory(s, adj); | 764 | end = adj->resource.memory.Base + adj->resource.memory.Size - 1; |
765 | return adjust_memory(s, adj->Action, adj->resource.memory.Base, end); | ||
766 | case RES_IO_RANGE: | 766 | case RES_IO_RANGE: |
767 | return adjust_io(s, adj); | 767 | end = adj->resource.io.BasePort + adj->resource.io.NumPorts - 1; |
768 | return adjust_io(s, adj->Action, adj->resource.io.BasePort, end); | ||
768 | } | 769 | } |
769 | return CS_UNSUPPORTED_FUNCTION; | 770 | return CS_UNSUPPORTED_FUNCTION; |
770 | } | 771 | } |
771 | 772 | ||
773 | #ifdef CONFIG_PCI | ||
774 | static int nonstatic_autoadd_resources(struct pcmcia_socket *s) | ||
775 | { | ||
776 | struct resource *res; | ||
777 | int i, done = 0; | ||
778 | |||
779 | if (!s->cb_dev || !s->cb_dev->bus) | ||
780 | return -ENODEV; | ||
781 | |||
782 | #if defined(CONFIG_X86) || defined(CONFIG_X86_64) | ||
783 | /* If this is the root bus, the risk of hitting | ||
784 | * some strange system devices which aren't protected | ||
785 | * by either ACPI resource tables or properly requested | ||
786 | * resources is too big. Therefore, don't do auto-adding | ||
787 | * of resources at the moment. | ||
788 | */ | ||
789 | if (s->cb_dev->bus->number == 0) | ||
790 | return -EINVAL; | ||
791 | #endif | ||
792 | |||
793 | for (i=0; i < PCI_BUS_NUM_RESOURCES; i++) { | ||
794 | res = s->cb_dev->bus->resource[i]; | ||
795 | if (!res) | ||
796 | continue; | ||
797 | |||
798 | if (res->flags & IORESOURCE_IO) { | ||
799 | if (res == &ioport_resource) | ||
800 | continue; | ||
801 | printk(KERN_INFO "pcmcia: parent PCI bridge I/O window: 0x%lx - 0x%lx\n", | ||
802 | res->start, res->end); | ||
803 | if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end)) | ||
804 | done |= IORESOURCE_IO; | ||
805 | |||
806 | } | ||
807 | |||
808 | if (res->flags & IORESOURCE_MEM) { | ||
809 | if (res == &iomem_resource) | ||
810 | continue; | ||
811 | printk(KERN_INFO "pcmcia: parent PCI bridge Memory window: 0x%lx - 0x%lx\n", | ||
812 | res->start, res->end); | ||
813 | if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end)) | ||
814 | done |= IORESOURCE_MEM; | ||
815 | } | ||
816 | } | ||
817 | |||
818 | /* if we got at least one of IO, and one of MEM, we can be glad and | ||
819 | * activate the PCMCIA subsystem */ | ||
820 | if (done & (IORESOURCE_MEM | IORESOURCE_IO)) | ||
821 | s->resource_setup_done = 1; | ||
822 | |||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | #else | ||
827 | |||
828 | static inline int nonstatic_autoadd_resources(struct pcmcia_socket *s) | ||
829 | { | ||
830 | return -ENODEV; | ||
831 | } | ||
832 | |||
833 | #endif | ||
834 | |||
835 | |||
772 | static int nonstatic_init(struct pcmcia_socket *s) | 836 | static int nonstatic_init(struct pcmcia_socket *s) |
773 | { | 837 | { |
774 | struct socket_data *data; | 838 | struct socket_data *data; |
@@ -783,6 +847,8 @@ static int nonstatic_init(struct pcmcia_socket *s) | |||
783 | 847 | ||
784 | s->resource_data = (void *) data; | 848 | s->resource_data = (void *) data; |
785 | 849 | ||
850 | nonstatic_autoadd_resources(s); | ||
851 | |||
786 | return 0; | 852 | return 0; |
787 | } | 853 | } |
788 | 854 | ||
@@ -845,17 +911,16 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size | |||
845 | { | 911 | { |
846 | struct pcmcia_socket *s = class_get_devdata(class_dev); | 912 | struct pcmcia_socket *s = class_get_devdata(class_dev); |
847 | unsigned long start_addr, end_addr; | 913 | unsigned long start_addr, end_addr; |
848 | unsigned int add = 1; | 914 | unsigned int add = ADD_MANAGED_RESOURCE; |
849 | adjust_t adj; | ||
850 | ssize_t ret = 0; | 915 | ssize_t ret = 0; |
851 | 916 | ||
852 | ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); | 917 | ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); |
853 | if (ret != 2) { | 918 | if (ret != 2) { |
854 | ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); | 919 | ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); |
855 | add = 0; | 920 | add = REMOVE_MANAGED_RESOURCE; |
856 | if (ret != 2) { | 921 | if (ret != 2) { |
857 | ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); | 922 | ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); |
858 | add = 1; | 923 | add = ADD_MANAGED_RESOURCE; |
859 | if (ret != 2) | 924 | if (ret != 2) |
860 | return -EINVAL; | 925 | return -EINVAL; |
861 | } | 926 | } |
@@ -863,12 +928,9 @@ static ssize_t store_io_db(struct class_device *class_dev, const char *buf, size | |||
863 | if (end_addr <= start_addr) | 928 | if (end_addr <= start_addr) |
864 | return -EINVAL; | 929 | return -EINVAL; |
865 | 930 | ||
866 | adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; | 931 | ret = adjust_io(s, add, start_addr, end_addr); |
867 | adj.Resource = RES_IO_RANGE; | 932 | if (!ret) |
868 | adj.resource.io.BasePort = start_addr; | 933 | s->resource_setup_new = 1; |
869 | adj.resource.io.NumPorts = end_addr - start_addr + 1; | ||
870 | |||
871 | ret = adjust_io(s, &adj); | ||
872 | 934 | ||
873 | return ret ? ret : count; | 935 | return ret ? ret : count; |
874 | } | 936 | } |
@@ -901,17 +963,16 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz | |||
901 | { | 963 | { |
902 | struct pcmcia_socket *s = class_get_devdata(class_dev); | 964 | struct pcmcia_socket *s = class_get_devdata(class_dev); |
903 | unsigned long start_addr, end_addr; | 965 | unsigned long start_addr, end_addr; |
904 | unsigned int add = 1; | 966 | unsigned int add = ADD_MANAGED_RESOURCE; |
905 | adjust_t adj; | ||
906 | ssize_t ret = 0; | 967 | ssize_t ret = 0; |
907 | 968 | ||
908 | ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); | 969 | ret = sscanf (buf, "+ 0x%lx - 0x%lx", &start_addr, &end_addr); |
909 | if (ret != 2) { | 970 | if (ret != 2) { |
910 | ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); | 971 | ret = sscanf (buf, "- 0x%lx - 0x%lx", &start_addr, &end_addr); |
911 | add = 0; | 972 | add = REMOVE_MANAGED_RESOURCE; |
912 | if (ret != 2) { | 973 | if (ret != 2) { |
913 | ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); | 974 | ret = sscanf (buf, "0x%lx - 0x%lx", &start_addr, &end_addr); |
914 | add = 1; | 975 | add = ADD_MANAGED_RESOURCE; |
915 | if (ret != 2) | 976 | if (ret != 2) |
916 | return -EINVAL; | 977 | return -EINVAL; |
917 | } | 978 | } |
@@ -919,12 +980,9 @@ static ssize_t store_mem_db(struct class_device *class_dev, const char *buf, siz | |||
919 | if (end_addr <= start_addr) | 980 | if (end_addr <= start_addr) |
920 | return -EINVAL; | 981 | return -EINVAL; |
921 | 982 | ||
922 | adj.Action = add ? ADD_MANAGED_RESOURCE : REMOVE_MANAGED_RESOURCE; | 983 | ret = adjust_memory(s, add, start_addr, end_addr); |
923 | adj.Resource = RES_MEMORY_RANGE; | 984 | if (!ret) |
924 | adj.resource.memory.Base = start_addr; | 985 | s->resource_setup_new = 1; |
925 | adj.resource.memory.Size = end_addr - start_addr + 1; | ||
926 | |||
927 | ret = adjust_memory(s, &adj); | ||
928 | 986 | ||
929 | return ret ? ret : count; | 987 | return ret ? ret : count; |
930 | } | 988 | } |