diff options
author | Grant Likely <grant.likely@secretlab.ca> | 2012-01-30 10:32:37 -0500 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2012-01-30 10:32:37 -0500 |
commit | bc266185888e4033d8439b4fc6bdd14a41f4041e (patch) | |
tree | 1455fafca38f366b092766594b9d71975bc12d50 /drivers/spi | |
parent | dcd6c92267155e70a94b3927bce681ce74b80d1f (diff) | |
parent | b97b662174162b44944abd0fa9faea50006ba711 (diff) |
Merge branch 'spi/s3c64xx' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/misc
Conflicts:
drivers/spi/spi-s3c64xx.c
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-s3c64xx.c | 114 |
1 files changed, 103 insertions, 11 deletions
diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index dcf7e1006426..b899af6640a2 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c | |||
@@ -20,10 +20,12 @@ | |||
20 | #include <linux/init.h> | 20 | #include <linux/init.h> |
21 | #include <linux/module.h> | 21 | #include <linux/module.h> |
22 | #include <linux/workqueue.h> | 22 | #include <linux/workqueue.h> |
23 | #include <linux/interrupt.h> | ||
23 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
24 | #include <linux/clk.h> | 25 | #include <linux/clk.h> |
25 | #include <linux/dma-mapping.h> | 26 | #include <linux/dma-mapping.h> |
26 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | #include <linux/pm_runtime.h> | ||
27 | #include <linux/spi/spi.h> | 29 | #include <linux/spi/spi.h> |
28 | 30 | ||
29 | #include <mach/dma.h> | 31 | #include <mach/dma.h> |
@@ -153,6 +155,7 @@ struct s3c64xx_spi_dma_data { | |||
153 | * @tx_dmach: Controller's DMA channel for Tx. | 155 | * @tx_dmach: Controller's DMA channel for Tx. |
154 | * @sfr_start: BUS address of SPI controller regs. | 156 | * @sfr_start: BUS address of SPI controller regs. |
155 | * @regs: Pointer to ioremap'ed controller registers. | 157 | * @regs: Pointer to ioremap'ed controller registers. |
158 | * @irq: interrupt | ||
156 | * @xfer_completion: To indicate completion of xfer task. | 159 | * @xfer_completion: To indicate completion of xfer task. |
157 | * @cur_mode: Stores the active configuration of the controller. | 160 | * @cur_mode: Stores the active configuration of the controller. |
158 | * @cur_bpw: Stores the active bits per word settings. | 161 | * @cur_bpw: Stores the active bits per word settings. |
@@ -780,6 +783,8 @@ static void s3c64xx_spi_work(struct work_struct *work) | |||
780 | while (!acquire_dma(sdd)) | 783 | while (!acquire_dma(sdd)) |
781 | msleep(10); | 784 | msleep(10); |
782 | 785 | ||
786 | pm_runtime_get_sync(&sdd->pdev->dev); | ||
787 | |||
783 | spin_lock_irqsave(&sdd->lock, flags); | 788 | spin_lock_irqsave(&sdd->lock, flags); |
784 | 789 | ||
785 | while (!list_empty(&sdd->queue) | 790 | while (!list_empty(&sdd->queue) |
@@ -808,6 +813,8 @@ static void s3c64xx_spi_work(struct work_struct *work) | |||
808 | /* Free DMA channels */ | 813 | /* Free DMA channels */ |
809 | sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); | 814 | sdd->ops->release(sdd->rx_dma.ch, &s3c64xx_spi_dma_client); |
810 | sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); | 815 | sdd->ops->release(sdd->tx_dma.ch, &s3c64xx_spi_dma_client); |
816 | |||
817 | pm_runtime_put(&sdd->pdev->dev); | ||
811 | } | 818 | } |
812 | 819 | ||
813 | static int s3c64xx_spi_transfer(struct spi_device *spi, | 820 | static int s3c64xx_spi_transfer(struct spi_device *spi, |
@@ -890,6 +897,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
890 | goto setup_exit; | 897 | goto setup_exit; |
891 | } | 898 | } |
892 | 899 | ||
900 | pm_runtime_get_sync(&sdd->pdev->dev); | ||
901 | |||
893 | /* Check if we can provide the requested rate */ | 902 | /* Check if we can provide the requested rate */ |
894 | if (!sci->clk_from_cmu) { | 903 | if (!sci->clk_from_cmu) { |
895 | u32 psr, speed; | 904 | u32 psr, speed; |
@@ -922,6 +931,8 @@ static int s3c64xx_spi_setup(struct spi_device *spi) | |||
922 | err = -EINVAL; | 931 | err = -EINVAL; |
923 | } | 932 | } |
924 | 933 | ||
934 | pm_runtime_put(&sdd->pdev->dev); | ||
935 | |||
925 | setup_exit: | 936 | setup_exit: |
926 | 937 | ||
927 | /* setup() returns with device de-selected */ | 938 | /* setup() returns with device de-selected */ |
@@ -930,6 +941,33 @@ setup_exit: | |||
930 | return err; | 941 | return err; |
931 | } | 942 | } |
932 | 943 | ||
944 | static irqreturn_t s3c64xx_spi_irq(int irq, void *data) | ||
945 | { | ||
946 | struct s3c64xx_spi_driver_data *sdd = data; | ||
947 | struct spi_master *spi = sdd->master; | ||
948 | unsigned int val; | ||
949 | |||
950 | val = readl(sdd->regs + S3C64XX_SPI_PENDING_CLR); | ||
951 | |||
952 | val &= S3C64XX_SPI_PND_RX_OVERRUN_CLR | | ||
953 | S3C64XX_SPI_PND_RX_UNDERRUN_CLR | | ||
954 | S3C64XX_SPI_PND_TX_OVERRUN_CLR | | ||
955 | S3C64XX_SPI_PND_TX_UNDERRUN_CLR; | ||
956 | |||
957 | writel(val, sdd->regs + S3C64XX_SPI_PENDING_CLR); | ||
958 | |||
959 | if (val & S3C64XX_SPI_PND_RX_OVERRUN_CLR) | ||
960 | dev_err(&spi->dev, "RX overrun\n"); | ||
961 | if (val & S3C64XX_SPI_PND_RX_UNDERRUN_CLR) | ||
962 | dev_err(&spi->dev, "RX underrun\n"); | ||
963 | if (val & S3C64XX_SPI_PND_TX_OVERRUN_CLR) | ||
964 | dev_err(&spi->dev, "TX overrun\n"); | ||
965 | if (val & S3C64XX_SPI_PND_TX_UNDERRUN_CLR) | ||
966 | dev_err(&spi->dev, "TX underrun\n"); | ||
967 | |||
968 | return IRQ_HANDLED; | ||
969 | } | ||
970 | |||
933 | static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) | 971 | static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) |
934 | { | 972 | { |
935 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | 973 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; |
@@ -970,7 +1008,7 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
970 | struct s3c64xx_spi_driver_data *sdd; | 1008 | struct s3c64xx_spi_driver_data *sdd; |
971 | struct s3c64xx_spi_info *sci; | 1009 | struct s3c64xx_spi_info *sci; |
972 | struct spi_master *master; | 1010 | struct spi_master *master; |
973 | int ret; | 1011 | int ret, irq; |
974 | char clk_name[16]; | 1012 | char clk_name[16]; |
975 | 1013 | ||
976 | if (pdev->id < 0) { | 1014 | if (pdev->id < 0) { |
@@ -1006,6 +1044,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
1006 | return -ENXIO; | 1044 | return -ENXIO; |
1007 | } | 1045 | } |
1008 | 1046 | ||
1047 | irq = platform_get_irq(pdev, 0); | ||
1048 | if (irq < 0) { | ||
1049 | dev_warn(&pdev->dev, "Failed to get IRQ: %d\n", irq); | ||
1050 | return irq; | ||
1051 | } | ||
1052 | |||
1009 | master = spi_alloc_master(&pdev->dev, | 1053 | master = spi_alloc_master(&pdev->dev, |
1010 | sizeof(struct s3c64xx_spi_driver_data)); | 1054 | sizeof(struct s3c64xx_spi_driver_data)); |
1011 | if (master == NULL) { | 1055 | if (master == NULL) { |
@@ -1100,10 +1144,21 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
1100 | INIT_WORK(&sdd->work, s3c64xx_spi_work); | 1144 | INIT_WORK(&sdd->work, s3c64xx_spi_work); |
1101 | INIT_LIST_HEAD(&sdd->queue); | 1145 | INIT_LIST_HEAD(&sdd->queue); |
1102 | 1146 | ||
1147 | ret = request_irq(irq, s3c64xx_spi_irq, 0, "spi-s3c64xx", sdd); | ||
1148 | if (ret != 0) { | ||
1149 | dev_err(&pdev->dev, "Failed to request IRQ %d: %d\n", | ||
1150 | irq, ret); | ||
1151 | goto err8; | ||
1152 | } | ||
1153 | |||
1154 | writel(S3C64XX_SPI_INT_RX_OVERRUN_EN | S3C64XX_SPI_INT_RX_UNDERRUN_EN | | ||
1155 | S3C64XX_SPI_INT_TX_OVERRUN_EN | S3C64XX_SPI_INT_TX_UNDERRUN_EN, | ||
1156 | sdd->regs + S3C64XX_SPI_INT_EN); | ||
1157 | |||
1103 | if (spi_register_master(master)) { | 1158 | if (spi_register_master(master)) { |
1104 | dev_err(&pdev->dev, "cannot register SPI master\n"); | 1159 | dev_err(&pdev->dev, "cannot register SPI master\n"); |
1105 | ret = -EBUSY; | 1160 | ret = -EBUSY; |
1106 | goto err8; | 1161 | goto err9; |
1107 | } | 1162 | } |
1108 | 1163 | ||
1109 | dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " | 1164 | dev_dbg(&pdev->dev, "Samsung SoC SPI Driver loaded for Bus SPI-%d " |
@@ -1113,8 +1168,12 @@ static int __init s3c64xx_spi_probe(struct platform_device *pdev) | |||
1113 | mem_res->end, mem_res->start, | 1168 | mem_res->end, mem_res->start, |
1114 | sdd->rx_dma.dmach, sdd->tx_dma.dmach); | 1169 | sdd->rx_dma.dmach, sdd->tx_dma.dmach); |
1115 | 1170 | ||
1171 | pm_runtime_enable(&pdev->dev); | ||
1172 | |||
1116 | return 0; | 1173 | return 0; |
1117 | 1174 | ||
1175 | err9: | ||
1176 | free_irq(irq, sdd); | ||
1118 | err8: | 1177 | err8: |
1119 | destroy_workqueue(sdd->workqueue); | 1178 | destroy_workqueue(sdd->workqueue); |
1120 | err7: | 1179 | err7: |
@@ -1144,6 +1203,8 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) | |||
1144 | struct resource *mem_res; | 1203 | struct resource *mem_res; |
1145 | unsigned long flags; | 1204 | unsigned long flags; |
1146 | 1205 | ||
1206 | pm_runtime_disable(&pdev->dev); | ||
1207 | |||
1147 | spin_lock_irqsave(&sdd->lock, flags); | 1208 | spin_lock_irqsave(&sdd->lock, flags); |
1148 | sdd->state |= SUSPND; | 1209 | sdd->state |= SUSPND; |
1149 | spin_unlock_irqrestore(&sdd->lock, flags); | 1210 | spin_unlock_irqrestore(&sdd->lock, flags); |
@@ -1153,6 +1214,10 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) | |||
1153 | 1214 | ||
1154 | spi_unregister_master(master); | 1215 | spi_unregister_master(master); |
1155 | 1216 | ||
1217 | writel(0, sdd->regs + S3C64XX_SPI_INT_EN); | ||
1218 | |||
1219 | free_irq(platform_get_irq(pdev, 0), sdd); | ||
1220 | |||
1156 | destroy_workqueue(sdd->workqueue); | 1221 | destroy_workqueue(sdd->workqueue); |
1157 | 1222 | ||
1158 | clk_disable(sdd->src_clk); | 1223 | clk_disable(sdd->src_clk); |
@@ -1174,9 +1239,9 @@ static int s3c64xx_spi_remove(struct platform_device *pdev) | |||
1174 | } | 1239 | } |
1175 | 1240 | ||
1176 | #ifdef CONFIG_PM | 1241 | #ifdef CONFIG_PM |
1177 | static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) | 1242 | static int s3c64xx_spi_suspend(struct device *dev) |
1178 | { | 1243 | { |
1179 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); | 1244 | struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); |
1180 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | 1245 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
1181 | unsigned long flags; | 1246 | unsigned long flags; |
1182 | 1247 | ||
@@ -1196,9 +1261,10 @@ static int s3c64xx_spi_suspend(struct platform_device *pdev, pm_message_t state) | |||
1196 | return 0; | 1261 | return 0; |
1197 | } | 1262 | } |
1198 | 1263 | ||
1199 | static int s3c64xx_spi_resume(struct platform_device *pdev) | 1264 | static int s3c64xx_spi_resume(struct device *dev) |
1200 | { | 1265 | { |
1201 | struct spi_master *master = spi_master_get(platform_get_drvdata(pdev)); | 1266 | struct platform_device *pdev = to_platform_device(dev); |
1267 | struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); | ||
1202 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | 1268 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); |
1203 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; | 1269 | struct s3c64xx_spi_info *sci = sdd->cntrlr_info; |
1204 | unsigned long flags; | 1270 | unsigned long flags; |
@@ -1217,19 +1283,45 @@ static int s3c64xx_spi_resume(struct platform_device *pdev) | |||
1217 | 1283 | ||
1218 | return 0; | 1284 | return 0; |
1219 | } | 1285 | } |
1220 | #else | ||
1221 | #define s3c64xx_spi_suspend NULL | ||
1222 | #define s3c64xx_spi_resume NULL | ||
1223 | #endif /* CONFIG_PM */ | 1286 | #endif /* CONFIG_PM */ |
1224 | 1287 | ||
1288 | #ifdef CONFIG_PM_RUNTIME | ||
1289 | static int s3c64xx_spi_runtime_suspend(struct device *dev) | ||
1290 | { | ||
1291 | struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); | ||
1292 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | ||
1293 | |||
1294 | clk_disable(sdd->clk); | ||
1295 | clk_disable(sdd->src_clk); | ||
1296 | |||
1297 | return 0; | ||
1298 | } | ||
1299 | |||
1300 | static int s3c64xx_spi_runtime_resume(struct device *dev) | ||
1301 | { | ||
1302 | struct spi_master *master = spi_master_get(dev_get_drvdata(dev)); | ||
1303 | struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(master); | ||
1304 | |||
1305 | clk_enable(sdd->src_clk); | ||
1306 | clk_enable(sdd->clk); | ||
1307 | |||
1308 | return 0; | ||
1309 | } | ||
1310 | #endif /* CONFIG_PM_RUNTIME */ | ||
1311 | |||
1312 | static const struct dev_pm_ops s3c64xx_spi_pm = { | ||
1313 | SET_SYSTEM_SLEEP_PM_OPS(s3c64xx_spi_suspend, s3c64xx_spi_resume) | ||
1314 | SET_RUNTIME_PM_OPS(s3c64xx_spi_runtime_suspend, | ||
1315 | s3c64xx_spi_runtime_resume, NULL) | ||
1316 | }; | ||
1317 | |||
1225 | static struct platform_driver s3c64xx_spi_driver = { | 1318 | static struct platform_driver s3c64xx_spi_driver = { |
1226 | .driver = { | 1319 | .driver = { |
1227 | .name = "s3c64xx-spi", | 1320 | .name = "s3c64xx-spi", |
1228 | .owner = THIS_MODULE, | 1321 | .owner = THIS_MODULE, |
1322 | .pm = &s3c64xx_spi_pm, | ||
1229 | }, | 1323 | }, |
1230 | .remove = s3c64xx_spi_remove, | 1324 | .remove = s3c64xx_spi_remove, |
1231 | .suspend = s3c64xx_spi_suspend, | ||
1232 | .resume = s3c64xx_spi_resume, | ||
1233 | }; | 1325 | }; |
1234 | MODULE_ALIAS("platform:s3c64xx-spi"); | 1326 | MODULE_ALIAS("platform:s3c64xx-spi"); |
1235 | 1327 | ||