diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-07-21 00:55:14 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2008-07-21 00:55:14 -0400 |
commit | 908cf4b925e419bc74f3297b2f0e51d6f8a81da2 (patch) | |
tree | 6c2da79366d4695a9c2560ab18259eca8a2a25b4 /drivers/spi | |
parent | 92c49890922d54cba4b1eadeb0b185773c2c9570 (diff) | |
parent | 14b395e35d1afdd8019d11b92e28041fad591b71 (diff) |
Merge master.kernel.org:/pub/scm/linux/kernel/git/torvalds/linux-2.6 into next
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/mpc52xx_psc_spi.c | 2 | ||||
-rw-r--r-- | drivers/spi/spi_imx.c | 54 | ||||
-rw-r--r-- | drivers/spi/spidev.c | 179 |
3 files changed, 167 insertions, 68 deletions
diff --git a/drivers/spi/mpc52xx_psc_spi.c b/drivers/spi/mpc52xx_psc_spi.c index 681d62325d3d..604e5f0a2d95 100644 --- a/drivers/spi/mpc52xx_psc_spi.c +++ b/drivers/spi/mpc52xx_psc_spi.c | |||
@@ -17,7 +17,7 @@ | |||
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | 18 | ||
19 | #if defined(CONFIG_PPC_MERGE) | 19 | #if defined(CONFIG_PPC_MERGE) |
20 | #include <asm/of_platform.h> | 20 | #include <linux/of_platform.h> |
21 | #else | 21 | #else |
22 | #include <linux/platform_device.h> | 22 | #include <linux/platform_device.h> |
23 | #endif | 23 | #endif |
diff --git a/drivers/spi/spi_imx.c b/drivers/spi/spi_imx.c index c730d05bfeb6..54ac7bea5f8c 100644 --- a/drivers/spi/spi_imx.c +++ b/drivers/spi/spi_imx.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <linux/spi/spi.h> | 29 | #include <linux/spi/spi.h> |
30 | #include <linux/workqueue.h> | 30 | #include <linux/workqueue.h> |
31 | #include <linux/delay.h> | 31 | #include <linux/delay.h> |
32 | #include <linux/clk.h> | ||
32 | 33 | ||
33 | #include <asm/io.h> | 34 | #include <asm/io.h> |
34 | #include <asm/irq.h> | 35 | #include <asm/irq.h> |
@@ -250,6 +251,8 @@ struct driver_data { | |||
250 | int tx_dma_needs_unmap; | 251 | int tx_dma_needs_unmap; |
251 | size_t tx_map_len; | 252 | size_t tx_map_len; |
252 | u32 dummy_dma_buf ____cacheline_aligned; | 253 | u32 dummy_dma_buf ____cacheline_aligned; |
254 | |||
255 | struct clk *clk; | ||
253 | }; | 256 | }; |
254 | 257 | ||
255 | /* Runtime state */ | 258 | /* Runtime state */ |
@@ -855,15 +858,15 @@ static irqreturn_t spi_int(int irq, void *dev_id) | |||
855 | return drv_data->transfer_handler(drv_data); | 858 | return drv_data->transfer_handler(drv_data); |
856 | } | 859 | } |
857 | 860 | ||
858 | static inline u32 spi_speed_hz(u32 data_rate) | 861 | static inline u32 spi_speed_hz(struct driver_data *drv_data, u32 data_rate) |
859 | { | 862 | { |
860 | return imx_get_perclk2() / (4 << ((data_rate) >> 13)); | 863 | return clk_get_rate(drv_data->clk) / (4 << ((data_rate) >> 13)); |
861 | } | 864 | } |
862 | 865 | ||
863 | static u32 spi_data_rate(u32 speed_hz) | 866 | static u32 spi_data_rate(struct driver_data *drv_data, u32 speed_hz) |
864 | { | 867 | { |
865 | u32 div; | 868 | u32 div; |
866 | u32 quantized_hz = imx_get_perclk2() >> 2; | 869 | u32 quantized_hz = clk_get_rate(drv_data->clk) >> 2; |
867 | 870 | ||
868 | for (div = SPI_PERCLK2_DIV_MIN; | 871 | for (div = SPI_PERCLK2_DIV_MIN; |
869 | div <= SPI_PERCLK2_DIV_MAX; | 872 | div <= SPI_PERCLK2_DIV_MAX; |
@@ -947,7 +950,7 @@ static void pump_transfers(unsigned long data) | |||
947 | tmp = transfer->speed_hz; | 950 | tmp = transfer->speed_hz; |
948 | if (tmp == 0) | 951 | if (tmp == 0) |
949 | tmp = chip->max_speed_hz; | 952 | tmp = chip->max_speed_hz; |
950 | tmp = spi_data_rate(tmp); | 953 | tmp = spi_data_rate(drv_data, tmp); |
951 | u32_EDIT(control, SPI_CONTROL_DATARATE, tmp); | 954 | u32_EDIT(control, SPI_CONTROL_DATARATE, tmp); |
952 | 955 | ||
953 | writel(control, regs + SPI_CONTROL); | 956 | writel(control, regs + SPI_CONTROL); |
@@ -1109,7 +1112,7 @@ static int transfer(struct spi_device *spi, struct spi_message *msg) | |||
1109 | msg->actual_length = 0; | 1112 | msg->actual_length = 0; |
1110 | 1113 | ||
1111 | /* Per transfer setup check */ | 1114 | /* Per transfer setup check */ |
1112 | min_speed_hz = spi_speed_hz(SPI_CONTROL_DATARATE_MIN); | 1115 | min_speed_hz = spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN); |
1113 | max_speed_hz = spi->max_speed_hz; | 1116 | max_speed_hz = spi->max_speed_hz; |
1114 | list_for_each_entry(trans, &msg->transfers, transfer_list) { | 1117 | list_for_each_entry(trans, &msg->transfers, transfer_list) { |
1115 | tmp = trans->bits_per_word; | 1118 | tmp = trans->bits_per_word; |
@@ -1176,6 +1179,7 @@ msg_rejected: | |||
1176 | applied and notified to the calling driver. */ | 1179 | applied and notified to the calling driver. */ |
1177 | static int setup(struct spi_device *spi) | 1180 | static int setup(struct spi_device *spi) |
1178 | { | 1181 | { |
1182 | struct driver_data *drv_data = spi_master_get_devdata(spi->master); | ||
1179 | struct spi_imx_chip *chip_info; | 1183 | struct spi_imx_chip *chip_info; |
1180 | struct chip_data *chip; | 1184 | struct chip_data *chip; |
1181 | int first_setup = 0; | 1185 | int first_setup = 0; |
@@ -1304,14 +1308,14 @@ static int setup(struct spi_device *spi) | |||
1304 | chip->n_bytes = (tmp <= 8) ? 1 : 2; | 1308 | chip->n_bytes = (tmp <= 8) ? 1 : 2; |
1305 | 1309 | ||
1306 | /* SPI datarate */ | 1310 | /* SPI datarate */ |
1307 | tmp = spi_data_rate(spi->max_speed_hz); | 1311 | tmp = spi_data_rate(drv_data, spi->max_speed_hz); |
1308 | if (tmp == SPI_CONTROL_DATARATE_BAD) { | 1312 | if (tmp == SPI_CONTROL_DATARATE_BAD) { |
1309 | status = -EINVAL; | 1313 | status = -EINVAL; |
1310 | dev_err(&spi->dev, | 1314 | dev_err(&spi->dev, |
1311 | "setup - " | 1315 | "setup - " |
1312 | "HW min speed (%d Hz) exceeds required " | 1316 | "HW min speed (%d Hz) exceeds required " |
1313 | "max speed (%d Hz)\n", | 1317 | "max speed (%d Hz)\n", |
1314 | spi_speed_hz(SPI_CONTROL_DATARATE_MIN), | 1318 | spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN), |
1315 | spi->max_speed_hz); | 1319 | spi->max_speed_hz); |
1316 | if (first_setup) | 1320 | if (first_setup) |
1317 | goto err_first_setup; | 1321 | goto err_first_setup; |
@@ -1321,7 +1325,7 @@ static int setup(struct spi_device *spi) | |||
1321 | } else { | 1325 | } else { |
1322 | u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp); | 1326 | u32_EDIT(chip->control, SPI_CONTROL_DATARATE, tmp); |
1323 | /* Actual rounded max_speed_hz */ | 1327 | /* Actual rounded max_speed_hz */ |
1324 | tmp = spi_speed_hz(tmp); | 1328 | tmp = spi_speed_hz(drv_data, tmp); |
1325 | spi->max_speed_hz = tmp; | 1329 | spi->max_speed_hz = tmp; |
1326 | chip->max_speed_hz = tmp; | 1330 | chip->max_speed_hz = tmp; |
1327 | } | 1331 | } |
@@ -1352,7 +1356,7 @@ static int setup(struct spi_device *spi) | |||
1352 | chip->period & SPI_PERIOD_WAIT, | 1356 | chip->period & SPI_PERIOD_WAIT, |
1353 | spi->mode, | 1357 | spi->mode, |
1354 | spi->bits_per_word, | 1358 | spi->bits_per_word, |
1355 | spi_speed_hz(SPI_CONTROL_DATARATE_MIN), | 1359 | spi_speed_hz(drv_data, SPI_CONTROL_DATARATE_MIN), |
1356 | spi->max_speed_hz); | 1360 | spi->max_speed_hz); |
1357 | return status; | 1361 | return status; |
1358 | 1362 | ||
@@ -1465,6 +1469,14 @@ static int __init spi_imx_probe(struct platform_device *pdev) | |||
1465 | goto err_no_pdata; | 1469 | goto err_no_pdata; |
1466 | } | 1470 | } |
1467 | 1471 | ||
1472 | drv_data->clk = clk_get(&pdev->dev, "perclk2"); | ||
1473 | if (IS_ERR(drv_data->clk)) { | ||
1474 | dev_err(&pdev->dev, "probe - cannot get get\n"); | ||
1475 | status = PTR_ERR(drv_data->clk); | ||
1476 | goto err_no_clk; | ||
1477 | } | ||
1478 | clk_enable(drv_data->clk); | ||
1479 | |||
1468 | /* Allocate master with space for drv_data */ | 1480 | /* Allocate master with space for drv_data */ |
1469 | master = spi_alloc_master(dev, sizeof(struct driver_data)); | 1481 | master = spi_alloc_master(dev, sizeof(struct driver_data)); |
1470 | if (!master) { | 1482 | if (!master) { |
@@ -1526,24 +1538,24 @@ static int __init spi_imx_probe(struct platform_device *pdev) | |||
1526 | drv_data->rx_channel = -1; | 1538 | drv_data->rx_channel = -1; |
1527 | if (platform_info->enable_dma) { | 1539 | if (platform_info->enable_dma) { |
1528 | /* Get rx DMA channel */ | 1540 | /* Get rx DMA channel */ |
1529 | status = imx_dma_request_by_prio(&drv_data->rx_channel, | 1541 | drv_data->rx_channel = imx_dma_request_by_prio("spi_imx_rx", |
1530 | "spi_imx_rx", DMA_PRIO_HIGH); | 1542 | DMA_PRIO_HIGH); |
1531 | if (status < 0) { | 1543 | if (drv_data->rx_channel < 0) { |
1532 | dev_err(dev, | 1544 | dev_err(dev, |
1533 | "probe - problem (%d) requesting rx channel\n", | 1545 | "probe - problem (%d) requesting rx channel\n", |
1534 | status); | 1546 | drv_data->rx_channel); |
1535 | goto err_no_rxdma; | 1547 | goto err_no_rxdma; |
1536 | } else | 1548 | } else |
1537 | imx_dma_setup_handlers(drv_data->rx_channel, NULL, | 1549 | imx_dma_setup_handlers(drv_data->rx_channel, NULL, |
1538 | dma_err_handler, drv_data); | 1550 | dma_err_handler, drv_data); |
1539 | 1551 | ||
1540 | /* Get tx DMA channel */ | 1552 | /* Get tx DMA channel */ |
1541 | status = imx_dma_request_by_prio(&drv_data->tx_channel, | 1553 | drv_data->tx_channel = imx_dma_request_by_prio("spi_imx_tx", |
1542 | "spi_imx_tx", DMA_PRIO_MEDIUM); | 1554 | DMA_PRIO_MEDIUM); |
1543 | if (status < 0) { | 1555 | if (drv_data->tx_channel < 0) { |
1544 | dev_err(dev, | 1556 | dev_err(dev, |
1545 | "probe - problem (%d) requesting tx channel\n", | 1557 | "probe - problem (%d) requesting tx channel\n", |
1546 | status); | 1558 | drv_data->tx_channel); |
1547 | imx_dma_free(drv_data->rx_channel); | 1559 | imx_dma_free(drv_data->rx_channel); |
1548 | goto err_no_txdma; | 1560 | goto err_no_txdma; |
1549 | } else | 1561 | } else |
@@ -1623,6 +1635,9 @@ err_no_iores: | |||
1623 | spi_master_put(master); | 1635 | spi_master_put(master); |
1624 | 1636 | ||
1625 | err_no_pdata: | 1637 | err_no_pdata: |
1638 | clk_disable(drv_data->clk); | ||
1639 | clk_put(drv_data->clk); | ||
1640 | err_no_clk: | ||
1626 | err_no_mem: | 1641 | err_no_mem: |
1627 | return status; | 1642 | return status; |
1628 | } | 1643 | } |
@@ -1662,6 +1677,9 @@ static int __exit spi_imx_remove(struct platform_device *pdev) | |||
1662 | if (irq >= 0) | 1677 | if (irq >= 0) |
1663 | free_irq(irq, drv_data); | 1678 | free_irq(irq, drv_data); |
1664 | 1679 | ||
1680 | clk_disable(drv_data->clk); | ||
1681 | clk_put(drv_data->clk); | ||
1682 | |||
1665 | /* Release map resources */ | 1683 | /* Release map resources */ |
1666 | iounmap(drv_data->regs); | 1684 | iounmap(drv_data->regs); |
1667 | release_resource(drv_data->ioarea); | 1685 | release_resource(drv_data->ioarea); |
diff --git a/drivers/spi/spidev.c b/drivers/spi/spidev.c index b3518ca9f04e..ddbe1a5e970e 100644 --- a/drivers/spi/spidev.c +++ b/drivers/spi/spidev.c | |||
@@ -25,10 +25,12 @@ | |||
25 | #include <linux/ioctl.h> | 25 | #include <linux/ioctl.h> |
26 | #include <linux/fs.h> | 26 | #include <linux/fs.h> |
27 | #include <linux/device.h> | 27 | #include <linux/device.h> |
28 | #include <linux/err.h> | ||
28 | #include <linux/list.h> | 29 | #include <linux/list.h> |
29 | #include <linux/errno.h> | 30 | #include <linux/errno.h> |
30 | #include <linux/mutex.h> | 31 | #include <linux/mutex.h> |
31 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
33 | #include <linux/smp_lock.h> | ||
32 | 34 | ||
33 | #include <linux/spi/spi.h> | 35 | #include <linux/spi/spi.h> |
34 | #include <linux/spi/spidev.h> | 36 | #include <linux/spi/spidev.h> |
@@ -67,10 +69,12 @@ static unsigned long minors[N_SPI_MINORS / BITS_PER_LONG]; | |||
67 | | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP) | 69 | | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP) |
68 | 70 | ||
69 | struct spidev_data { | 71 | struct spidev_data { |
70 | struct device dev; | 72 | dev_t devt; |
73 | spinlock_t spi_lock; | ||
71 | struct spi_device *spi; | 74 | struct spi_device *spi; |
72 | struct list_head device_entry; | 75 | struct list_head device_entry; |
73 | 76 | ||
77 | /* buffer is NULL unless this device is open (users > 0) */ | ||
74 | struct mutex buf_lock; | 78 | struct mutex buf_lock; |
75 | unsigned users; | 79 | unsigned users; |
76 | u8 *buffer; | 80 | u8 *buffer; |
@@ -85,12 +89,75 @@ MODULE_PARM_DESC(bufsiz, "data bytes in biggest supported SPI message"); | |||
85 | 89 | ||
86 | /*-------------------------------------------------------------------------*/ | 90 | /*-------------------------------------------------------------------------*/ |
87 | 91 | ||
92 | /* | ||
93 | * We can't use the standard synchronous wrappers for file I/O; we | ||
94 | * need to protect against async removal of the underlying spi_device. | ||
95 | */ | ||
96 | static void spidev_complete(void *arg) | ||
97 | { | ||
98 | complete(arg); | ||
99 | } | ||
100 | |||
101 | static ssize_t | ||
102 | spidev_sync(struct spidev_data *spidev, struct spi_message *message) | ||
103 | { | ||
104 | DECLARE_COMPLETION_ONSTACK(done); | ||
105 | int status; | ||
106 | |||
107 | message->complete = spidev_complete; | ||
108 | message->context = &done; | ||
109 | |||
110 | spin_lock_irq(&spidev->spi_lock); | ||
111 | if (spidev->spi == NULL) | ||
112 | status = -ESHUTDOWN; | ||
113 | else | ||
114 | status = spi_async(spidev->spi, message); | ||
115 | spin_unlock_irq(&spidev->spi_lock); | ||
116 | |||
117 | if (status == 0) { | ||
118 | wait_for_completion(&done); | ||
119 | status = message->status; | ||
120 | if (status == 0) | ||
121 | status = message->actual_length; | ||
122 | } | ||
123 | return status; | ||
124 | } | ||
125 | |||
126 | static inline ssize_t | ||
127 | spidev_sync_write(struct spidev_data *spidev, size_t len) | ||
128 | { | ||
129 | struct spi_transfer t = { | ||
130 | .tx_buf = spidev->buffer, | ||
131 | .len = len, | ||
132 | }; | ||
133 | struct spi_message m; | ||
134 | |||
135 | spi_message_init(&m); | ||
136 | spi_message_add_tail(&t, &m); | ||
137 | return spidev_sync(spidev, &m); | ||
138 | } | ||
139 | |||
140 | static inline ssize_t | ||
141 | spidev_sync_read(struct spidev_data *spidev, size_t len) | ||
142 | { | ||
143 | struct spi_transfer t = { | ||
144 | .rx_buf = spidev->buffer, | ||
145 | .len = len, | ||
146 | }; | ||
147 | struct spi_message m; | ||
148 | |||
149 | spi_message_init(&m); | ||
150 | spi_message_add_tail(&t, &m); | ||
151 | return spidev_sync(spidev, &m); | ||
152 | } | ||
153 | |||
154 | /*-------------------------------------------------------------------------*/ | ||
155 | |||
88 | /* Read-only message with current device setup */ | 156 | /* Read-only message with current device setup */ |
89 | static ssize_t | 157 | static ssize_t |
90 | spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | 158 | spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) |
91 | { | 159 | { |
92 | struct spidev_data *spidev; | 160 | struct spidev_data *spidev; |
93 | struct spi_device *spi; | ||
94 | ssize_t status = 0; | 161 | ssize_t status = 0; |
95 | 162 | ||
96 | /* chipselect only toggles at start or end of operation */ | 163 | /* chipselect only toggles at start or end of operation */ |
@@ -98,18 +165,17 @@ spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos) | |||
98 | return -EMSGSIZE; | 165 | return -EMSGSIZE; |
99 | 166 | ||
100 | spidev = filp->private_data; | 167 | spidev = filp->private_data; |
101 | spi = spidev->spi; | ||
102 | 168 | ||
103 | mutex_lock(&spidev->buf_lock); | 169 | mutex_lock(&spidev->buf_lock); |
104 | status = spi_read(spi, spidev->buffer, count); | 170 | status = spidev_sync_read(spidev, count); |
105 | if (status == 0) { | 171 | if (status > 0) { |
106 | unsigned long missing; | 172 | unsigned long missing; |
107 | 173 | ||
108 | missing = copy_to_user(buf, spidev->buffer, count); | 174 | missing = copy_to_user(buf, spidev->buffer, status); |
109 | if (count && missing == count) | 175 | if (missing == status) |
110 | status = -EFAULT; | 176 | status = -EFAULT; |
111 | else | 177 | else |
112 | status = count - missing; | 178 | status = status - missing; |
113 | } | 179 | } |
114 | mutex_unlock(&spidev->buf_lock); | 180 | mutex_unlock(&spidev->buf_lock); |
115 | 181 | ||
@@ -122,7 +188,6 @@ spidev_write(struct file *filp, const char __user *buf, | |||
122 | size_t count, loff_t *f_pos) | 188 | size_t count, loff_t *f_pos) |
123 | { | 189 | { |
124 | struct spidev_data *spidev; | 190 | struct spidev_data *spidev; |
125 | struct spi_device *spi; | ||
126 | ssize_t status = 0; | 191 | ssize_t status = 0; |
127 | unsigned long missing; | 192 | unsigned long missing; |
128 | 193 | ||
@@ -131,14 +196,11 @@ spidev_write(struct file *filp, const char __user *buf, | |||
131 | return -EMSGSIZE; | 196 | return -EMSGSIZE; |
132 | 197 | ||
133 | spidev = filp->private_data; | 198 | spidev = filp->private_data; |
134 | spi = spidev->spi; | ||
135 | 199 | ||
136 | mutex_lock(&spidev->buf_lock); | 200 | mutex_lock(&spidev->buf_lock); |
137 | missing = copy_from_user(spidev->buffer, buf, count); | 201 | missing = copy_from_user(spidev->buffer, buf, count); |
138 | if (missing == 0) { | 202 | if (missing == 0) { |
139 | status = spi_write(spi, spidev->buffer, count); | 203 | status = spidev_sync_write(spidev, count); |
140 | if (status == 0) | ||
141 | status = count; | ||
142 | } else | 204 | } else |
143 | status = -EFAULT; | 205 | status = -EFAULT; |
144 | mutex_unlock(&spidev->buf_lock); | 206 | mutex_unlock(&spidev->buf_lock); |
@@ -153,7 +215,6 @@ static int spidev_message(struct spidev_data *spidev, | |||
153 | struct spi_transfer *k_xfers; | 215 | struct spi_transfer *k_xfers; |
154 | struct spi_transfer *k_tmp; | 216 | struct spi_transfer *k_tmp; |
155 | struct spi_ioc_transfer *u_tmp; | 217 | struct spi_ioc_transfer *u_tmp; |
156 | struct spi_device *spi = spidev->spi; | ||
157 | unsigned n, total; | 218 | unsigned n, total; |
158 | u8 *buf; | 219 | u8 *buf; |
159 | int status = -EFAULT; | 220 | int status = -EFAULT; |
@@ -215,7 +276,7 @@ static int spidev_message(struct spidev_data *spidev, | |||
215 | spi_message_add_tail(k_tmp, &msg); | 276 | spi_message_add_tail(k_tmp, &msg); |
216 | } | 277 | } |
217 | 278 | ||
218 | status = spi_sync(spi, &msg); | 279 | status = spidev_sync(spidev, &msg); |
219 | if (status < 0) | 280 | if (status < 0) |
220 | goto done; | 281 | goto done; |
221 | 282 | ||
@@ -269,8 +330,16 @@ spidev_ioctl(struct inode *inode, struct file *filp, | |||
269 | if (err) | 330 | if (err) |
270 | return -EFAULT; | 331 | return -EFAULT; |
271 | 332 | ||
333 | /* guard against device removal before, or while, | ||
334 | * we issue this ioctl. | ||
335 | */ | ||
272 | spidev = filp->private_data; | 336 | spidev = filp->private_data; |
273 | spi = spidev->spi; | 337 | spin_lock_irq(&spidev->spi_lock); |
338 | spi = spi_dev_get(spidev->spi); | ||
339 | spin_unlock_irq(&spidev->spi_lock); | ||
340 | |||
341 | if (spi == NULL) | ||
342 | return -ESHUTDOWN; | ||
274 | 343 | ||
275 | switch (cmd) { | 344 | switch (cmd) { |
276 | /* read requests */ | 345 | /* read requests */ |
@@ -356,8 +425,10 @@ spidev_ioctl(struct inode *inode, struct file *filp, | |||
356 | default: | 425 | default: |
357 | /* segmented and/or full-duplex I/O request */ | 426 | /* segmented and/or full-duplex I/O request */ |
358 | if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) | 427 | if (_IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0)) |
359 | || _IOC_DIR(cmd) != _IOC_WRITE) | 428 | || _IOC_DIR(cmd) != _IOC_WRITE) { |
360 | return -ENOTTY; | 429 | retval = -ENOTTY; |
430 | break; | ||
431 | } | ||
361 | 432 | ||
362 | tmp = _IOC_SIZE(cmd); | 433 | tmp = _IOC_SIZE(cmd); |
363 | if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { | 434 | if ((tmp % sizeof(struct spi_ioc_transfer)) != 0) { |
@@ -385,6 +456,7 @@ spidev_ioctl(struct inode *inode, struct file *filp, | |||
385 | kfree(ioc); | 456 | kfree(ioc); |
386 | break; | 457 | break; |
387 | } | 458 | } |
459 | spi_dev_put(spi); | ||
388 | return retval; | 460 | return retval; |
389 | } | 461 | } |
390 | 462 | ||
@@ -393,10 +465,11 @@ static int spidev_open(struct inode *inode, struct file *filp) | |||
393 | struct spidev_data *spidev; | 465 | struct spidev_data *spidev; |
394 | int status = -ENXIO; | 466 | int status = -ENXIO; |
395 | 467 | ||
468 | lock_kernel(); | ||
396 | mutex_lock(&device_list_lock); | 469 | mutex_lock(&device_list_lock); |
397 | 470 | ||
398 | list_for_each_entry(spidev, &device_list, device_entry) { | 471 | list_for_each_entry(spidev, &device_list, device_entry) { |
399 | if (spidev->dev.devt == inode->i_rdev) { | 472 | if (spidev->devt == inode->i_rdev) { |
400 | status = 0; | 473 | status = 0; |
401 | break; | 474 | break; |
402 | } | 475 | } |
@@ -418,6 +491,7 @@ static int spidev_open(struct inode *inode, struct file *filp) | |||
418 | pr_debug("spidev: nothing for minor %d\n", iminor(inode)); | 491 | pr_debug("spidev: nothing for minor %d\n", iminor(inode)); |
419 | 492 | ||
420 | mutex_unlock(&device_list_lock); | 493 | mutex_unlock(&device_list_lock); |
494 | unlock_kernel(); | ||
421 | return status; | 495 | return status; |
422 | } | 496 | } |
423 | 497 | ||
@@ -429,10 +503,22 @@ static int spidev_release(struct inode *inode, struct file *filp) | |||
429 | mutex_lock(&device_list_lock); | 503 | mutex_lock(&device_list_lock); |
430 | spidev = filp->private_data; | 504 | spidev = filp->private_data; |
431 | filp->private_data = NULL; | 505 | filp->private_data = NULL; |
506 | |||
507 | /* last close? */ | ||
432 | spidev->users--; | 508 | spidev->users--; |
433 | if (!spidev->users) { | 509 | if (!spidev->users) { |
510 | int dofree; | ||
511 | |||
434 | kfree(spidev->buffer); | 512 | kfree(spidev->buffer); |
435 | spidev->buffer = NULL; | 513 | spidev->buffer = NULL; |
514 | |||
515 | /* ... after we unbound from the underlying device? */ | ||
516 | spin_lock_irq(&spidev->spi_lock); | ||
517 | dofree = (spidev->spi == NULL); | ||
518 | spin_unlock_irq(&spidev->spi_lock); | ||
519 | |||
520 | if (dofree) | ||
521 | kfree(spidev); | ||
436 | } | 522 | } |
437 | mutex_unlock(&device_list_lock); | 523 | mutex_unlock(&device_list_lock); |
438 | 524 | ||
@@ -459,19 +545,7 @@ static struct file_operations spidev_fops = { | |||
459 | * It also simplifies memory management. | 545 | * It also simplifies memory management. |
460 | */ | 546 | */ |
461 | 547 | ||
462 | static void spidev_classdev_release(struct device *dev) | 548 | static struct class *spidev_class; |
463 | { | ||
464 | struct spidev_data *spidev; | ||
465 | |||
466 | spidev = container_of(dev, struct spidev_data, dev); | ||
467 | kfree(spidev); | ||
468 | } | ||
469 | |||
470 | static struct class spidev_class = { | ||
471 | .name = "spidev", | ||
472 | .owner = THIS_MODULE, | ||
473 | .dev_release = spidev_classdev_release, | ||
474 | }; | ||
475 | 549 | ||
476 | /*-------------------------------------------------------------------------*/ | 550 | /*-------------------------------------------------------------------------*/ |
477 | 551 | ||
@@ -488,6 +562,7 @@ static int spidev_probe(struct spi_device *spi) | |||
488 | 562 | ||
489 | /* Initialize the driver data */ | 563 | /* Initialize the driver data */ |
490 | spidev->spi = spi; | 564 | spidev->spi = spi; |
565 | spin_lock_init(&spidev->spi_lock); | ||
491 | mutex_init(&spidev->buf_lock); | 566 | mutex_init(&spidev->buf_lock); |
492 | 567 | ||
493 | INIT_LIST_HEAD(&spidev->device_entry); | 568 | INIT_LIST_HEAD(&spidev->device_entry); |
@@ -498,20 +573,20 @@ static int spidev_probe(struct spi_device *spi) | |||
498 | mutex_lock(&device_list_lock); | 573 | mutex_lock(&device_list_lock); |
499 | minor = find_first_zero_bit(minors, N_SPI_MINORS); | 574 | minor = find_first_zero_bit(minors, N_SPI_MINORS); |
500 | if (minor < N_SPI_MINORS) { | 575 | if (minor < N_SPI_MINORS) { |
501 | spidev->dev.parent = &spi->dev; | 576 | struct device *dev; |
502 | spidev->dev.class = &spidev_class; | 577 | |
503 | spidev->dev.devt = MKDEV(SPIDEV_MAJOR, minor); | 578 | spidev->devt = MKDEV(SPIDEV_MAJOR, minor); |
504 | snprintf(spidev->dev.bus_id, sizeof spidev->dev.bus_id, | 579 | dev = device_create(spidev_class, &spi->dev, spidev->devt, |
505 | "spidev%d.%d", | 580 | "spidev%d.%d", |
506 | spi->master->bus_num, spi->chip_select); | 581 | spi->master->bus_num, spi->chip_select); |
507 | status = device_register(&spidev->dev); | 582 | status = IS_ERR(dev) ? PTR_ERR(dev) : 0; |
508 | } else { | 583 | } else { |
509 | dev_dbg(&spi->dev, "no minor number available!\n"); | 584 | dev_dbg(&spi->dev, "no minor number available!\n"); |
510 | status = -ENODEV; | 585 | status = -ENODEV; |
511 | } | 586 | } |
512 | if (status == 0) { | 587 | if (status == 0) { |
513 | set_bit(minor, minors); | 588 | set_bit(minor, minors); |
514 | dev_set_drvdata(&spi->dev, spidev); | 589 | spi_set_drvdata(spi, spidev); |
515 | list_add(&spidev->device_entry, &device_list); | 590 | list_add(&spidev->device_entry, &device_list); |
516 | } | 591 | } |
517 | mutex_unlock(&device_list_lock); | 592 | mutex_unlock(&device_list_lock); |
@@ -524,15 +599,21 @@ static int spidev_probe(struct spi_device *spi) | |||
524 | 599 | ||
525 | static int spidev_remove(struct spi_device *spi) | 600 | static int spidev_remove(struct spi_device *spi) |
526 | { | 601 | { |
527 | struct spidev_data *spidev = dev_get_drvdata(&spi->dev); | 602 | struct spidev_data *spidev = spi_get_drvdata(spi); |
528 | 603 | ||
529 | mutex_lock(&device_list_lock); | 604 | /* make sure ops on existing fds can abort cleanly */ |
605 | spin_lock_irq(&spidev->spi_lock); | ||
606 | spidev->spi = NULL; | ||
607 | spi_set_drvdata(spi, NULL); | ||
608 | spin_unlock_irq(&spidev->spi_lock); | ||
530 | 609 | ||
610 | /* prevent new opens */ | ||
611 | mutex_lock(&device_list_lock); | ||
531 | list_del(&spidev->device_entry); | 612 | list_del(&spidev->device_entry); |
532 | dev_set_drvdata(&spi->dev, NULL); | 613 | device_destroy(spidev_class, spidev->devt); |
533 | clear_bit(MINOR(spidev->dev.devt), minors); | 614 | clear_bit(MINOR(spidev->devt), minors); |
534 | device_unregister(&spidev->dev); | 615 | if (spidev->users == 0) |
535 | 616 | kfree(spidev); | |
536 | mutex_unlock(&device_list_lock); | 617 | mutex_unlock(&device_list_lock); |
537 | 618 | ||
538 | return 0; | 619 | return 0; |
@@ -568,15 +649,15 @@ static int __init spidev_init(void) | |||
568 | if (status < 0) | 649 | if (status < 0) |
569 | return status; | 650 | return status; |
570 | 651 | ||
571 | status = class_register(&spidev_class); | 652 | spidev_class = class_create(THIS_MODULE, "spidev"); |
572 | if (status < 0) { | 653 | if (IS_ERR(spidev_class)) { |
573 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 654 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
574 | return status; | 655 | return PTR_ERR(spidev_class); |
575 | } | 656 | } |
576 | 657 | ||
577 | status = spi_register_driver(&spidev_spi); | 658 | status = spi_register_driver(&spidev_spi); |
578 | if (status < 0) { | 659 | if (status < 0) { |
579 | class_unregister(&spidev_class); | 660 | class_destroy(spidev_class); |
580 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 661 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
581 | } | 662 | } |
582 | return status; | 663 | return status; |
@@ -586,7 +667,7 @@ module_init(spidev_init); | |||
586 | static void __exit spidev_exit(void) | 667 | static void __exit spidev_exit(void) |
587 | { | 668 | { |
588 | spi_unregister_driver(&spidev_spi); | 669 | spi_unregister_driver(&spidev_spi); |
589 | class_unregister(&spidev_class); | 670 | class_destroy(spidev_class); |
590 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); | 671 | unregister_chrdev(SPIDEV_MAJOR, spidev_spi.driver.name); |
591 | } | 672 | } |
592 | module_exit(spidev_exit); | 673 | module_exit(spidev_exit); |