diff options
| author | Thomas Abraham <thomas.abraham@linaro.org> | 2012-07-12 18:15:15 -0400 |
|---|---|---|
| committer | Kukjin Kim <kgene.kim@samsung.com> | 2012-07-13 02:23:49 -0400 |
| commit | 2b90807549e5d1f700e523ddd098651ecfc18e65 (patch) | |
| tree | c341f65fb5d32d81025836343f73114d2fdfc516 | |
| parent | 1c20c200ef96c50b3075f71220c8c8bc018a93c8 (diff) | |
spi: s3c64xx: add device tree support
Add support for device based discovery.
Signed-off-by: Thomas Abraham <thomas.abraham@linaro.org>
Acked-by: Jaswinder Singh <jaswinder.singh@linaro.org>
Acked-by: Grant Likely <grant.likely@secretlab.ca>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
| -rw-r--r-- | Documentation/devicetree/bindings/spi/spi-samsung.txt | 113 | ||||
| -rw-r--r-- | drivers/spi/spi-s3c64xx.c | 303 |
2 files changed, 377 insertions, 39 deletions
diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt new file mode 100644 index 000000000000..59bfc4f9feb3 --- /dev/null +++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt | |||
| @@ -0,0 +1,113 @@ | |||
| 1 | * Samsung SPI Controller | ||
| 2 | |||
| 3 | The Samsung SPI controller is used to interface with various devices such as flash | ||
| 4 | and display controllers using the SPI communication interface. | ||
| 5 | |||
| 6 | Required SoC Specific Properties: | ||
| 7 | |||
| 8 | - compatible: should be one of the following. | ||
| 9 | - samsung,s3c2443-spi: for s3c2443, s3c2416 and s3c2450 platforms | ||
| 10 | - samsung,s3c6410-spi: for s3c6410 platforms | ||
| 11 | - samsung,s5p6440-spi: for s5p6440 and s5p6450 platforms | ||
| 12 | - samsung,s5pv210-spi: for s5pv210 and s5pc110 platforms | ||
| 13 | - samsung,exynos4210-spi: for exynos4 and exynos5 platforms | ||
| 14 | |||
| 15 | - reg: physical base address of the controller and length of memory mapped | ||
| 16 | region. | ||
| 17 | |||
| 18 | - interrupts: The interrupt number to the cpu. The interrupt specifier format | ||
| 19 | depends on the interrupt controller. | ||
| 20 | |||
| 21 | - tx-dma-channel: The dma channel specifier for tx operations. The format of | ||
| 22 | the dma specifier depends on the dma controller. | ||
| 23 | |||
| 24 | - rx-dma-channel: The dma channel specifier for rx operations. The format of | ||
| 25 | the dma specifier depends on the dma controller. | ||
| 26 | |||
| 27 | Required Board Specific Properties: | ||
| 28 | |||
| 29 | - #address-cells: should be 1. | ||
| 30 | - #size-cells: should be 0. | ||
| 31 | - gpios: The gpio specifier for clock, mosi and miso interface lines (in the | ||
| 32 | order specified). The format of the gpio specifier depends on the gpio | ||
| 33 | controller. | ||
| 34 | |||
| 35 | Optional Board Specific Properties: | ||
| 36 | |||
| 37 | - samsung,spi-src-clk: If the spi controller includes a internal clock mux to | ||
| 38 | select the clock source for the spi bus clock, this property can be used to | ||
| 39 | indicate the clock to be used for driving the spi bus clock. If not specified, | ||
| 40 | the clock number 0 is used as default. | ||
| 41 | |||
| 42 | - num-cs: Specifies the number of chip select lines supported. If | ||
| 43 | not specified, the default number of chip select lines is set to 1. | ||
| 44 | |||
| 45 | SPI Controller specific data in SPI slave nodes: | ||
| 46 | |||
| 47 | - The spi slave nodes should provide the following information which is required | ||
| 48 | by the spi controller. | ||
| 49 | |||
| 50 | - cs-gpio: A gpio specifier that specifies the gpio line used as | ||
| 51 | the slave select line by the spi controller. The format of the gpio | ||
| 52 | specifier depends on the gpio controller. | ||
| 53 | |||
| 54 | - samsung,spi-feedback-delay: The sampling phase shift to be applied on the | ||
| 55 | miso line (to account for any lag in the miso line). The following are the | ||
| 56 | valid values. | ||
| 57 | |||
| 58 | - 0: No phase shift. | ||
| 59 | - 1: 90 degree phase shift sampling. | ||
| 60 | - 2: 180 degree phase shift sampling. | ||
| 61 | - 3: 270 degree phase shift sampling. | ||
| 62 | |||
| 63 | Aliases: | ||
| 64 | |||
| 65 | - All the SPI controller nodes should be represented in the aliases node using | ||
| 66 | the following format 'spi{n}' where n is a unique number for the alias. | ||
| 67 | |||
| 68 | |||
| 69 | Example: | ||
| 70 | |||
| 71 | - SoC Specific Portion: | ||
| 72 | |||
| 73 | spi_0: spi@12d20000 { | ||
| 74 | compatible = "samsung,exynos4210-spi"; | ||
| 75 | reg = <0x12d20000 0x100>; | ||
| 76 | interrupts = <0 66 0>; | ||
| 77 | tx-dma-channel = <&pdma0 5>; | ||
| 78 | rx-dma-channel = <&pdma0 4>; | ||
| 79 | }; | ||
| 80 | |||
| 81 | - Board Specific Portion: | ||
| 82 | |||
| 83 | spi_0: spi@12d20000 { | ||
| 84 | #address-cells = <1>; | ||
| 85 | #size-cells = <0>; | ||
| 86 | gpios = <&gpa2 4 2 3 0>, | ||
| 87 | <&gpa2 6 2 3 0>, | ||
| 88 | <&gpa2 7 2 3 0>; | ||
| 89 | |||
| 90 | w25q80bw@0 { | ||
| 91 | #address-cells = <1>; | ||
| 92 | #size-cells = <1>; | ||
| 93 | compatible = "w25x80"; | ||
| 94 | reg = <0>; | ||
| 95 | spi-max-frequency = <10000>; | ||
| 96 | |||
| 97 | controller-data { | ||
| 98 | cs-gpio = <&gpa2 5 1 0 3>; | ||
| 99 | samsung,spi-feedback-delay = <0>; | ||
| 100 | }; | ||
| 101 | |||
| 102 | partition@0 { | ||
| 103 | label = "U-Boot"; | ||
| 104 | reg = <0x0 0x40000>; | ||
| 105 | read-only; | ||
| 106 | }; | ||
| 107 | |||
| 108 | partition@40000 { | ||
| 109 | label = "Kernel"; | ||
| 110 | reg = <0x40000 0xc0000>; | ||
| 111 | }; | ||
| 112 | }; | ||
| 113 | }; | ||
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index e4182ea2f306..0dedbbdb153a 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c | |||
| @@ -28,6 +28,8 @@ | |||
| 28 | #include <linux/pm_runtime.h> | 28 | #include <linux/pm_runtime.h> |
| 29 | #include <linux/spi/spi.h> | 29 | #include <linux/spi/spi.h> |
| 30 | #include <linux/gpio.h> | 30 | #include <linux/gpio.h> |
| 31 | #include <linux/of.h> | ||
| 32 | #include <linux/of_gpio.h> | ||
| 31 | 33 | ||
| 32 | #include <mach/dma.h> | 34 | #include <mach/dma.h> |
| 33 | #include <plat/s3c64xx-spi.h> | 35 | #include <plat/s3c64xx-spi.h> |
| @@ -137,6 +139,7 @@ struct s3c64xx_spi_dma_data { | |||
| 137 | unsigned ch; | 139 | unsigned ch; |
| 138 | enum dma_data_direction direction; | 140 | enum dma_data_direction direction; |
| 139 | enum dma_ch dmach; | 141 | enum dma_ch dmach; |
| 142 | struct property *dma_prop; | ||
| 140 | }; | 143 | }; |
| 141 | 144 | ||
| 142 | /** | 145 | /** |
| @@ -201,6 +204,7 @@ struct s3c64xx_spi_driver_data { | |||
| 201 | struct samsung_dma_ops *ops; | 204 | struct samsung_dma_ops *ops; |
| 202 | struct s3c64xx_spi_port_config *port_conf; | 205 | struct s3c64xx_spi_port_config *port_conf; |
| 203 | unsigned int port_id; | 206 | unsigned int port_id; |
| 207 | unsigned long gpios[4]; | ||
| 204 | }; | 208 | }; |
| 205 | 209 | ||
| 206 | static struct s3c2410_dma_client s3c64xx_spi_dma_client = { | 210 | static struct s3c2410_dma_client s3c64xx_spi_dma_client = { |
| @@ -326,7 +330,9 @@ static int acquire_dma(struct s3c64xx_spi_driver_data *sdd) | |||
| 326 | req.cap = DMA_SLAVE; | 330 | req.cap = DMA_SLAVE; |
| 327 | req.client = &s3c64xx_spi_dma_client; | 331 | req.client = &s3c64xx_spi_dma_client; |
| 328 | 332 | ||
| 333 | req.dt_dmach_prop = sdd->rx_dma.dma_prop; | ||
| 329 | sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req); | 334 | sdd->rx_dma.ch = sdd->ops->request(sdd->rx_dma.dmach, &req); |
| 335 | req.dt_dmach_prop = sdd->tx_dma.dma_prop; | ||
| 330 | sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req); | 336 | sdd->tx_dma.ch = sdd->ops->request(sdd->tx_dma.dmach, &req); |
| 331 | 337 | ||
| 332 | return 1; | 338 | return 1; |
| @@ -819,6 +825,48 @@ static int s3c64xx_spi_unprepare_transfer(struct spi_master *spi) | |||
| 819 | return 0; | 825 | return 0; |
| 820 | } | 826 | } |
| 821 | 827 | ||
| 828 | static struct s3c64xx_spi_csinfo *s3c64xx_get_slave_ctrldata( | ||
| 829 | struct s3c64xx_spi_driver_data *sdd, | ||
| 830 | struct spi_device *spi) | ||
| 831 | { | ||
| 832 | struct s3c64xx_spi_csinfo *cs; | ||
| 833 | struct device_node *slave_np, *data_np; | ||
| 834 | u32 fb_delay = 0; | ||
| 835 | |||
| 836 | slave_np = spi->dev.of_node; | ||
| 837 | if (!slave_np) { | ||
| 838 | dev_err(&spi->dev, "device node not found\n"); | ||
| 839 | return ERR_PTR(-EINVAL); | ||
| 840 | } | ||
| 841 | |||
| 842 | for_each_child_of_node(slave_np, data_np) | ||
| 843 | if (!strcmp(data_np->name, "controller-data")) | ||
| 844 | break; | ||
| 845 | if (!data_np) { | ||
| 846 | dev_err(&spi->dev, "child node 'controller-data' not found\n"); | ||
| 847 | return ERR_PTR(-EINVAL); | ||
| 848 | } | ||
| 849 | |||
| 850 | cs = kzalloc(sizeof(*cs), GFP_KERNEL); | ||
| 851 | if (!cs) { | ||
| 852 | dev_err(&spi->dev, "could not allocate memory for controller" | ||
| 853 | " data\n"); | ||
| 854 | return ERR_PTR(-ENOMEM); | ||
| 855 | } | ||
| 856 | |||
| 857 | cs->line = of_get_named_gpio(data_np, "cs-gpio", 0); | ||
| 858 | if (!gpio_is_valid(cs->line)) { | ||
| 859 | dev_err(&spi->dev, "chip select gpio is not specified or " | ||
| 860 | "invalid\n"); | ||
| 861 | kfree(cs); | ||
| 862 | return ERR_PTR(-EINVAL); | ||
| 863 | } | ||
| 864 | |||
| 865 | of_property_read_u32(data_np, "samsung,spi-feedback-delay", &fb_delay); | ||
| 866 | cs->fb_delay = fb_delay; | ||
| 867 | return cs; | ||
| 868 | } | ||
| 869 | |||
| 822 | /* | 870 | /* |
| 823 | * Here we only check the validity of requested configuration | 871 | * Here we only check the validity of requested configuration |
| 824 | * and save the configuration in a local data-structure. | 872 | * and save the configuration in a local data-structure. |
| @@ -832,9 +880,15 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
| 832 | struct s3c64xx_spi_info *sci; | 880 | struct s3c64xx_spi_info *sci; |
| 833 | struct spi_message *msg; | 881 | struct spi_message *msg; |
| 834 | unsigned long flags; | 882 | unsigned long flags; |
| 835 | int err = 0; | 883 | int err; |
| 836 | 884 | ||
| 837 | if (cs == NULL) { | 885 | sdd = spi_master_get_devdata(spi->master); |
| 886 | if (!cs && spi->dev.of_node) { | ||
| 887 | cs = s3c64xx_get_slave_ctrldata(sdd, spi); | ||
| 888 | spi->controller_data = cs; | ||
| 889 | } | ||
| 890 | |||
| 891 | if (IS_ERR_OR_NULL(cs)) { | ||
| 838 | dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select); | 892 | dev_err(&spi->dev, "No CS for SPI(%d)\n", spi->chip_select); |
| 839 | return -ENODEV; | 893 | return -ENODEV; |
| 840 | } | 894 | } |
| @@ -844,12 +898,12 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
| 844 | if (err) { | 898 | if (err) { |
| 845 | dev_err(&spi->dev, "request for slave select gpio " | 899 | dev_err(&spi->dev, "request for slave select gpio " |
| 846 | "line [%d] failed\n", cs->line); | 900 | "line [%d] failed\n", cs->line); |
| 847 | return -EBUSY; | 901 | err = -EBUSY; |
| 902 | goto err_gpio_req; | ||
| 848 | } | 903 | } |
| 849 | spi_set_ctldata(spi, cs); | 904 | spi_set_ctldata(spi, cs); |
| 850 | } | 905 | } |
| 851 | 906 | ||
| 852 | sdd = spi_master_get_devdata(spi->master); | ||
| 853 | sci = sdd->cntrlr_info; | 907 | sci = sdd->cntrlr_info; |
| 854 | 908 | ||
| 855 | spin_lock_irqsave(&sdd->lock, flags); | 909 | spin_lock_irqsave(&sdd->lock, flags); |
| @@ -860,7 +914,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
| 860 | dev_err(&spi->dev, | 914 | dev_err(&spi->dev, |
| 861 | "setup: attempt while mssg in queue!\n"); | 915 | "setup: attempt while mssg in queue!\n"); |
| 862 | spin_unlock_irqrestore(&sdd->lock, flags); | 916 | spin_unlock_irqrestore(&sdd->lock, flags); |
| 863 | return -EBUSY; | 917 | err = -EBUSY; |
| 918 | goto err_msgq; | ||
| 864 | } | 919 | } |
| 865 | } | 920 | } |
| 866 | 921 | ||
| @@ -903,19 +958,29 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
| 903 | } | 958 | } |
| 904 | 959 | ||
| 905 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); | 960 | speed = clk_get_rate(sdd->src_clk) / 2 / (psr + 1); |
| 906 | if (spi->max_speed_hz >= speed) | 961 | if (spi->max_speed_hz >= speed) { |
| 907 | spi->max_speed_hz = speed; | 962 | spi->max_speed_hz = speed; |
| 908 | else | 963 | } else { |
| 909 | err = -EINVAL; | 964 | err = -EINVAL; |
| 965 | goto setup_exit; | ||
| 966 | } | ||
| 910 | } | 967 | } |
| 911 | 968 | ||
| 912 | pm_runtime_put(&sdd->pdev->dev); | 969 | pm_runtime_put(&sdd->pdev->dev); |
| 970 | disable_cs(sdd, spi); | ||
| 971 | return 0; | ||
| 913 | 972 | ||
| 914 | setup_exit: | 973 | setup_exit: |
| 915 | |||
| 916 | /* setup() returns with device de-selected */ | 974 | /* setup() returns with device de-selected */ |
| 917 | disable_cs(sdd, spi); | 975 | disable_cs(sdd, spi); |
| 918 | 976 | ||
| 977 | err_msgq: | ||
| 978 | gpio_free(cs->line); | ||
| 979 | spi_set_ctldata(spi, NULL); | ||
| 980 | |||
| 981 | err_gpio_req: | ||
| 982 | kfree(cs); | ||
| 983 | |||
| 919 | return err; | 984 | return err; |
| 920 | } | 985 | } |
| 921 | 986 | ||
| @@ -923,8 +988,11 @@ static void s3c64xx_spi_cleanup(struct spi_device *spi) | |||
| 923 | { | 988 | { |
| 924 | struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); | 989 | struct s3c64xx_spi_csinfo *cs = spi_get_ctldata(spi); |
| 925 | 990 | ||
| 926 | if (cs) | 991 | if (cs) { |
| 927 | gpio_free(cs->line); | 992 | gpio_free(cs->line); |
| 993 | if (spi->dev.of_node) | ||
| 994 | kfree(cs); | ||
| 995 | } | ||
| 928 | spi_set_ctldata(spi, NULL); | 996 | spi_set_ctldata(spi, NULL); |
| 929 | } | 997 | } |
| 930 | 998 | ||
| @@ -989,49 +1057,166 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) | |||
| 989 | flush_fifo(sdd); | 1057 | flush_fifo(sdd); |
| 990 | } | 1058 | } |
| 991 | 1059 | ||
| 1060 | static int __devinit s3c64xx_spi_get_dmares( | ||
| 1061 | struct s3c64xx_spi_driver_data *sdd, bool tx) | ||
| 1062 | { | ||
| 1063 | struct platform_device *pdev = sdd->pdev; | ||
| 1064 | struct s3c64xx_spi_dma_data *dma_data; | ||
| 1065 | struct property *prop; | ||
| 1066 | struct resource *res; | ||
| 1067 | char prop_name[15], *chan_str; | ||
| 1068 | |||
| 1069 | if (tx) { | ||
| 1070 | dma_data = &sdd->tx_dma; | ||
| 1071 | dma_data->direction = DMA_TO_DEVICE; | ||
| 1072 | chan_str = "tx"; | ||
| 1073 | } else { | ||
| 1074 | dma_data = &sdd->rx_dma; | ||
| 1075 | dma_data->direction = DMA_FROM_DEVICE; | ||
| 1076 | chan_str = "rx"; | ||
| 1077 | } | ||
| 1078 | |||
| 1079 | if (!sdd->pdev->dev.of_node) { | ||
| 1080 | res = platform_get_resource(pdev, IORESOURCE_DMA, tx ? 0 : 1); | ||
| 1081 | if (!res) { | ||
| 1082 | dev_err(&pdev->dev, "Unable to get SPI-%s dma " | ||
| 1083 | "resource\n", chan_str); | ||
| 1084 | return -ENXIO; | ||
| 1085 | } | ||
| 1086 | dma_data->dmach = res->start; | ||
| 1087 | return 0; | ||
| 1088 | } | ||
| 1089 | |||
| 1090 | sprintf(prop_name, "%s-dma-channel", chan_str); | ||
| 1091 | prop = of_find_property(pdev->dev.of_node, prop_name, NULL); | ||
| 1092 | if (!prop) { | ||
| 1093 | dev_err(&pdev->dev, "%s dma channel property not specified\n", | ||
| 1094 | chan_str); | ||
| 1095 | return -ENXIO; | ||
| 1096 | } | ||
| 1097 | |||
| 1098 | dma_data->dmach = DMACH_DT_PROP; | ||
| 1099 | dma_data->dma_prop = prop; | ||
| 1100 | return 0; | ||
| 1101 | } | ||
| 1102 | |||
| 1103 | #ifdef CONFIG_OF | ||
| 1104 | static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) | ||
| 1105 | { | ||
| 1106 | struct device *dev = &sdd->pdev->dev; | ||
| 1107 | int idx, gpio, ret; | ||
| 1108 | |||
| 1109 | /* find gpios for mosi, miso and clock lines */ | ||
| 1110 | for (idx = 0; idx < 3; idx++) { | ||
| 1111 | gpio = of_get_gpio(dev->of_node, idx); | ||
| 1112 | if (!gpio_is_valid(gpio)) { | ||
| 1113 | dev_err(dev, "invalid gpio[%d]: %d\n", idx, gpio); | ||
| 1114 | goto free_gpio; | ||
| 1115 | } | ||
| 1116 | |||
| 1117 | ret = gpio_request(gpio, "spi-bus"); | ||
| 1118 | if (ret) { | ||
| 1119 | dev_err(dev, "gpio [%d] request failed\n", gpio); | ||
| 1120 | goto free_gpio; | ||
| 1121 | } | ||
| 1122 | } | ||
| 1123 | return 0; | ||
| 1124 | |||
| 1125 | free_gpio: | ||
| 1126 | while (--idx >= 0) | ||
| 1127 | gpio_free(sdd->gpios[idx]); | ||
| 1128 | return -EINVAL; | ||
| 1129 | } | ||
| 1130 | |||
| 1131 | static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd) | ||
| 1132 | { | ||
| 1133 | unsigned int idx; | ||
| 1134 | for (idx = 0; idx < 3; idx++) | ||
| 1135 | gpio_free(sdd->gpios[idx]); | ||
| 1136 | } | ||
| 1137 | |||
| 1138 | static struct __devinit s3c64xx_spi_info * s3c64xx_spi_parse_dt( | ||
| 1139 | struct device *dev) | ||
| 1140 | { | ||
| 1141 | struct s3c64xx_spi_info *sci; | ||
| 1142 | u32 temp; | ||
| 1143 | |||
| 1144 | sci = devm_kzalloc(dev, sizeof(*sci), GFP_KERNEL); | ||
| 1145 | if (!sci) { | ||
| 1146 | dev_err(dev, "memory allocation for spi_info failed\n"); | ||
| 1147 | return ERR_PTR(-ENOMEM); | ||
| 1148 | } | ||
| 1149 | |||
| 1150 | if (of_property_read_u32(dev->of_node, "samsung,spi-src-clk", &temp)) { | ||
| 1151 | dev_warn(dev, "spi bus clock parent not specified, using " | ||
| 1152 | "clock at index 0 as parent\n"); | ||
| 1153 | sci->src_clk_nr = 0; | ||
| 1154 | } else { | ||
| 1155 | sci->src_clk_nr = temp; | ||
| 1156 | } | ||
| 1157 | |||
| 1158 | if (of_property_read_u32(dev->of_node, "num-cs", &temp)) { | ||
| 1159 | dev_warn(dev, "number of chip select lines not specified, " | ||
| 1160 | "assuming 1 chip select line\n"); | ||
| 1161 | sci->num_cs = 1; | ||
| 1162 | } else { | ||
| 1163 | sci->num_cs = temp; | ||
| 1164 | } | ||
| 1165 | |||
| 1166 | return sci; | ||
| 1167 | } | ||
| 1168 | #else | ||
| 1169 | static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) | ||
| 1170 | { | ||
| 1171 | return dev->platform_data; | ||
| 1172 | } | ||
| 1173 | |||
| 1174 | static int s3c64xx_spi_parse_dt_gpio(struct s3c64xx_spi_driver_data *sdd) | ||
| 1175 | { | ||
| 1176 | return -EINVAL; | ||
| 1177 | } | ||
| 1178 | |||
| 1179 | static void s3c64xx_spi_dt_gpio_free(struct s3c64xx_spi_driver_data *sdd) | ||
| 1180 | { | ||
| 1181 | } | ||
| 1182 | #endif | ||
| 1183 | |||
| 1184 | static const struct of_device_id s3c64xx_spi_dt_match[]; | ||
| 1185 | |||
| 992 | static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( | 1186 | static inline struct s3c64xx_spi_port_config *s3c64xx_spi_get_port_config( |
| 993 | struct platform_device *pdev) | 1187 | struct platform_device *pdev) |
| 994 | { | 1188 | { |
| 1189 | #ifdef CONFIG_OF | ||
| 1190 | if (pdev->dev.of_node) { | ||
| 1191 | const struct of_device_id *match; | ||
| 1192 | match = of_match_node(s3c64xx_spi_dt_match, pdev->dev.of_node); | ||
| 1193 | return (struct s3c64xx_spi_port_config *)match->data; | ||
| 1194 | } | ||
| 1195 | #endif | ||
| 995 | return (struct s3c64xx_spi_port_config *) | 1196 | return (struct s3c64xx_spi_port_config *) |
| 996 | platform_get_device_id(pdev)->driver_data; | 1197 | platform_get_device_id(pdev)->driver_data; |
| 997 | } | 1198 | } |
| 998 | 1199 | ||
| 999 | static int __init s3c64xx_spi_probe(struct platform_device *pdev) | 1200 | static int __init s3c64xx_spi_probe(struct platform_device *pdev) |
| 1000 | { | 1201 | { |
| 1001 | struct resource *mem_res, *dmatx_res, *dmarx_res; | 1202 | struct resource *mem_res; |
| 1002 | struct s3c64xx_spi_driver_data *sdd; | 1203 | struct s3c64xx_spi_driver_data *sdd; |
| 1003 | struct s3c64xx_spi_info *sci; | 1204 | struct s3c64xx_spi_info *sci = pdev->dev.platform_data; |
| 1004 | struct spi_master *master; | 1205 | struct spi_master *master; |
| 1005 | int ret, irq; | 1206 | int ret, irq; |
| 1006 | char clk_name[16]; | 1207 | char clk_name[16]; |
| 1007 | 1208 | ||
| 1008 | if (pdev->id < 0) { | 1209 | if (!sci && pdev->dev.of_node) { |
| 1009 | dev_err(&pdev->dev, | 1210 | sci = s3c64xx_spi_parse_dt(&pdev->dev); |
| 1010 | "Invalid platform device id-%d\n", pdev->id); | 1211 | if (IS_ERR(sci)) |
| 1011 | return -ENODEV; | 1212 | return PTR_ERR(sci); |
| 1012 | } | 1213 | } |
| 1013 | 1214 | ||
| 1014 | if (pdev->dev.platform_data == NULL) { | 1215 | if (!sci) { |
| 1015 | dev_err(&pdev->dev, "platform_data missing!\n"); | 1216 | dev_err(&pdev->dev, "platform_data missing!\n"); |
| 1016 | return -ENODEV; | 1217 | return -ENODEV; |
| 1017 | } | 1218 | } |
| 1018 | 1219 | ||
| 1019 | sci = pdev->dev.platform_data; | ||
| 1020 | |||
| 1021 | /* Check for availability of necessary resource */ | ||
| 1022 | |||
| 1023 | dmatx_res = platform_get_resource(pdev, IORESOURCE_DMA, 0); | ||
| 1024 | if (dmatx_res == NULL) { | ||
| 1025 | dev_err(&pdev->dev, "Unable to get SPI-Tx dma resource\n"); | ||
| 1026 | return -ENXIO; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | dmarx_res = platform_get_resource(pdev, IORESOURCE_DMA, 1); | ||
| 1030 | if (dmarx_res == NULL) { | ||
| 1031 | dev_err(&pdev->dev, "Unable to get SPI-Rx dma resource\n"); | ||
| 1032 | return -ENXIO; | ||
| 1033 | } | ||
| 1034 | |||
| 1035 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1220 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| 1036 | if (mem_res == NULL) { | 1221 | if (mem_res == NULL) { |
| 1037 | dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); | 1222 | dev_err(&pdev->dev, "Unable to get SPI MEM resource\n"); |
| @@ -1059,14 +1244,29 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
| 1059 | sdd->cntrlr_info = sci; | 1244 | sdd->cntrlr_info = sci; |
| 1060 | sdd->pdev = pdev; | 1245 | sdd->pdev = pdev; |
| 1061 | sdd->sfr_start = mem_res->start; | 1246 | sdd->sfr_start = mem_res->start; |
| 1062 | sdd->tx_dma.dmach = dmatx_res->start; | 1247 | if (pdev->dev.of_node) { |
| 1063 | sdd->tx_dma.direction = DMA_MEM_TO_DEV; | 1248 | ret = of_alias_get_id(pdev->dev.of_node, "spi"); |
| 1064 | sdd->rx_dma.dmach = dmarx_res->start; | 1249 | if (ret < 0) { |
| 1065 | sdd->rx_dma.direction = DMA_DEV_TO_MEM; | 1250 | dev_err(&pdev->dev, "failed to get alias id, " |
| 1066 | sdd->port_id = pdev->id; | 1251 | "errno %d\n", ret); |
| 1252 | goto err0; | ||
| 1253 | } | ||
| 1254 | sdd->port_id = ret; | ||
| 1255 | } else { | ||
| 1256 | sdd->port_id = pdev->id; | ||
| 1257 | } | ||
| 1067 | 1258 | ||
| 1068 | sdd->cur_bpw = 8; | 1259 | sdd->cur_bpw = 8; |
| 1069 | 1260 | ||
| 1261 | ret = s3c64xx_spi_get_dmares(sdd, true); | ||
| 1262 | if (ret) | ||
| 1263 | goto err0; | ||
| 1264 | |||
| 1265 | ret = s3c64xx_spi_get_dmares(sdd, false); | ||
| 1266 | if (ret) | ||
| 1267 | goto err0; | ||
| 1268 | |||
| 1269 | master->dev.of_node = pdev->dev.of_node; | ||
| 1070 | master->bus_num = sdd->port_id; | 1270 | master->bus_num = sdd->port_id; |
| 1071 | master->setup = s3c64xx_spi_setup; | 1271 | master->setup = s3c64xx_spi_setup; |
| 1072 | master->cleanup = s3c64xx_spi_cleanup; | 1272 | master->cleanup = s3c64xx_spi_cleanup; |
| @@ -1092,7 +1292,10 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
| 1092 | goto err1; | 1292 | goto err1; |
| 1093 | } | 1293 | } |
| 1094 | 1294 | ||
| 1095 | if (sci->cfg_gpio == NULL || sci->cfg_gpio()) { | 1295 | if (!sci->cfg_gpio && pdev->dev.of_node) { |
| 1296 | if (s3c64xx_spi_parse_dt_gpio(sdd)) | ||
| 1297 | return -EBUSY; | ||
| 1298 | } else if (sci->cfg_gpio == NULL || sci->cfg_gpio()) { | ||
| 1096 | dev_err(&pdev->dev, "Unable to config gpio\n"); | 1299 | dev_err(&pdev->dev, "Unable to config gpio\n"); |
| 1097 | ret = -EBUSY; | 1300 | ret = -EBUSY; |
| 1098 | goto err2; | 1301 | goto err2; |
| @@ -1173,6 +1376,8 @@ err5: | |||
| 1173 | err4: | 1376 | err4: |
| 1174 | clk_put(sdd->clk); | 1377 | clk_put(sdd->clk); |
| 1175 | err3: | 1378 | err3: |
| 1379 | if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) | ||
| 1380 | s3c64xx_spi_dt_gpio_free(sdd); | ||
| 1176 | err2: | 1381 | err2: |
| 1177 | iounmap((void *) sdd->regs); | 1382 | iounmap((void *) sdd->regs); |
| 1178 | err1: | 1383 | err1: |
| @@ -1204,6 +1409,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) | |||
| 1204 | clk_disable(sdd->clk); | 1409 | clk_disable(sdd->clk); |
| 1205 | clk_put(sdd->clk); | 1410 | clk_put(sdd->clk); |
| 1206 | 1411 | ||
| 1412 | if (!sdd->cntrlr_info->cfg_gpio && pdev->dev.of_node) | ||
| 1413 | s3c64xx_spi_dt_gpio_free(sdd); | ||
| 1414 | |||
| 1207 | iounmap((void *) sdd->regs); | 1415 | iounmap((void *) sdd->regs); |
| 1208 | 1416 | ||
| 1209 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1417 | mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
| @@ -1228,6 +1436,9 @@ static int s3c64xx_spi_suspend(struct device *dev) | |||
| 1228 | clk_disable(sdd->src_clk); | 1436 | clk_disable(sdd->src_clk); |
| 1229 | clk_disable(sdd->clk); | 1437 | clk_disable(sdd->clk); |
| 1230 | 1438 | ||
| 1439 | if (!sdd->cntrlr_info->cfg_gpio && dev->of_node) | ||
| 1440 | s3c64xx_spi_dt_gpio_free(sdd); | ||
| 1441 | |||
| 1231 | sdd->cur_speed = 0; /* Output Clock is stopped */ | 1442 | sdd->cur_speed = 0; /* Output Clock is stopped */ |
| 1232 | 1443 | ||
| 1233 | return 0; | 1444 | return 0; |
| @@ -1239,7 +1450,10 @@ static int s3c64xx_spi_resume(struct device *dev) | |||
| 1239 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | 1450 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
| 1240 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | 1451 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; |
| 1241 | 1452 | ||
| 1242 | sci->cfg_gpio(); | 1453 | if (!sci->cfg_gpio && dev->of_node) |
| 1454 | s3c64xx_spi_parse_dt_gpio(sdd); | ||
| 1455 | else | ||
| 1456 | sci->cfg_gpio(); | ||
| 1243 | 1457 | ||
| 1244 | /* Enable the clock */ | 1458 | /* Enable the clock */ |
| 1245 | clk_enable(sdd->src_clk); | 1459 | clk_enable(sdd->src_clk); |
| @@ -1347,11 +1561,22 @@ static struct platform_device_id s3c64xx_spi_driver_ids[] = { | |||
| 1347 | { }, | 1561 | { }, |
| 1348 | }; | 1562 | }; |
| 1349 | 1563 | ||
| 1564 | #ifdef CONFIG_OF | ||
| 1565 | static const struct of_device_id s3c64xx_spi_dt_match[] = { | ||
| 1566 | { .compatible = "samsung,exynos4210-spi", | ||
| 1567 | .data = (void *)&exynos4_spi_port_config, | ||
| 1568 | }, | ||
| 1569 | { }, | ||
| 1570 | }; | ||
| 1571 | MODULE_DEVICE_TABLE(of, s3c64xx_spi_dt_match); | ||
| 1572 | #endif /* CONFIG_OF */ | ||
| 1573 | |||
| 1350 | static struct platform_driver s3c64xx_spi_driver = { | 1574 | static struct platform_driver s3c64xx_spi_driver = { |
| 1351 | .driver = { | 1575 | .driver = { |
| 1352 | .name = "s3c64xx-spi", | 1576 | .name = "s3c64xx-spi", |
| 1353 | .owner = THIS_MODULE, | 1577 | .owner = THIS_MODULE, |
| 1354 | .pm = &s3c64xx_spi_pm, | 1578 | .pm = &s3c64xx_spi_pm, |
| 1579 | .of_match_table = of_match_ptr(s3c64xx_spi_dt_match), | ||
| 1355 | }, | 1580 | }, |
| 1356 | .remove = s3c64xx_spi_remove, | 1581 | .remove = s3c64xx_spi_remove, |
| 1357 | .id_table = s3c64xx_spi_driver_ids, | 1582 | .id_table = s3c64xx_spi_driver_ids, |
