1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Freescale SPI/eSPI controller driver library. |
4 | * |
5 | * Maintainer: Kumar Gala |
6 | * |
7 | * Copyright (C) 2006 Polycom, Inc. |
8 | * |
9 | * CPM SPI and QE buffer descriptors mode support: |
10 | * Copyright (c) 2009 MontaVista Software, Inc. |
11 | * Author: Anton Vorontsov <avorontsov@ru.mvista.com> |
12 | * |
13 | * Copyright 2010 Freescale Semiconductor, Inc. |
14 | */ |
15 | #include <linux/dma-mapping.h> |
16 | #include <linux/fsl_devices.h> |
17 | #include <linux/interrupt.h> |
18 | #include <linux/kernel.h> |
19 | #include <linux/mm.h> |
20 | #include <linux/module.h> |
21 | #include <linux/of.h> |
22 | #include <linux/platform_device.h> |
23 | #include <linux/spi/spi.h> |
24 | #ifdef CONFIG_FSL_SOC |
25 | #include <sysdev/fsl_soc.h> |
26 | #endif |
27 | |
28 | #include "spi-fsl-lib.h" |
29 | |
30 | #define MPC8XXX_SPI_RX_BUF(type) \ |
31 | void mpc8xxx_spi_rx_buf_##type(u32 data, struct mpc8xxx_spi *mpc8xxx_spi) \ |
32 | { \ |
33 | type *rx = mpc8xxx_spi->rx; \ |
34 | *rx++ = (type)(data >> mpc8xxx_spi->rx_shift); \ |
35 | mpc8xxx_spi->rx = rx; \ |
36 | } \ |
37 | EXPORT_SYMBOL_GPL(mpc8xxx_spi_rx_buf_##type); |
38 | |
39 | #define MPC8XXX_SPI_TX_BUF(type) \ |
40 | u32 mpc8xxx_spi_tx_buf_##type(struct mpc8xxx_spi *mpc8xxx_spi) \ |
41 | { \ |
42 | u32 data; \ |
43 | const type *tx = mpc8xxx_spi->tx; \ |
44 | if (!tx) \ |
45 | return 0; \ |
46 | data = *tx++ << mpc8xxx_spi->tx_shift; \ |
47 | mpc8xxx_spi->tx = tx; \ |
48 | return data; \ |
49 | } \ |
50 | EXPORT_SYMBOL_GPL(mpc8xxx_spi_tx_buf_##type); |
51 | |
52 | MPC8XXX_SPI_RX_BUF(u8) |
53 | MPC8XXX_SPI_RX_BUF(u16) |
54 | MPC8XXX_SPI_RX_BUF(u32) |
55 | MPC8XXX_SPI_TX_BUF(u8) |
56 | MPC8XXX_SPI_TX_BUF(u16) |
57 | MPC8XXX_SPI_TX_BUF(u32) |
58 | |
59 | struct mpc8xxx_spi_probe_info *to_of_pinfo(struct fsl_spi_platform_data *pdata) |
60 | { |
61 | return container_of(pdata, struct mpc8xxx_spi_probe_info, pdata); |
62 | } |
63 | EXPORT_SYMBOL_GPL(to_of_pinfo); |
64 | |
65 | const char *mpc8xxx_spi_strmode(unsigned int flags) |
66 | { |
67 | if (flags & SPI_QE_CPU_MODE) { |
68 | return "QE CPU" ; |
69 | } else if (flags & SPI_CPM_MODE) { |
70 | if (flags & SPI_QE) |
71 | return "QE" ; |
72 | else if (flags & SPI_CPM2) |
73 | return "CPM2" ; |
74 | else |
75 | return "CPM1" ; |
76 | } |
77 | return "CPU" ; |
78 | } |
79 | EXPORT_SYMBOL_GPL(mpc8xxx_spi_strmode); |
80 | |
81 | void mpc8xxx_spi_probe(struct device *dev, struct resource *mem, |
82 | unsigned int irq) |
83 | { |
84 | struct fsl_spi_platform_data *pdata = dev_get_platdata(dev); |
85 | struct spi_master *master; |
86 | struct mpc8xxx_spi *mpc8xxx_spi; |
87 | |
88 | master = dev_get_drvdata(dev); |
89 | |
90 | /* the spi->mode bits understood by this driver: */ |
91 | master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH |
92 | | SPI_LSB_FIRST | SPI_LOOP; |
93 | |
94 | master->dev.of_node = dev->of_node; |
95 | |
96 | mpc8xxx_spi = spi_master_get_devdata(master); |
97 | mpc8xxx_spi->dev = dev; |
98 | mpc8xxx_spi->get_rx = mpc8xxx_spi_rx_buf_u8; |
99 | mpc8xxx_spi->get_tx = mpc8xxx_spi_tx_buf_u8; |
100 | mpc8xxx_spi->flags = pdata->flags; |
101 | mpc8xxx_spi->spibrg = pdata->sysclk; |
102 | mpc8xxx_spi->irq = irq; |
103 | |
104 | mpc8xxx_spi->rx_shift = 0; |
105 | mpc8xxx_spi->tx_shift = 0; |
106 | |
107 | master->bus_num = pdata->bus_num; |
108 | master->num_chipselect = pdata->max_chipselect; |
109 | |
110 | init_completion(x: &mpc8xxx_spi->done); |
111 | } |
112 | EXPORT_SYMBOL_GPL(mpc8xxx_spi_probe); |
113 | |
114 | int of_mpc8xxx_spi_probe(struct platform_device *ofdev) |
115 | { |
116 | struct device *dev = &ofdev->dev; |
117 | struct device_node *np = ofdev->dev.of_node; |
118 | struct mpc8xxx_spi_probe_info *pinfo; |
119 | struct fsl_spi_platform_data *pdata; |
120 | const void *prop; |
121 | int ret = -ENOMEM; |
122 | |
123 | pinfo = devm_kzalloc(dev: &ofdev->dev, size: sizeof(*pinfo), GFP_KERNEL); |
124 | if (!pinfo) |
125 | return ret; |
126 | |
127 | pdata = &pinfo->pdata; |
128 | dev->platform_data = pdata; |
129 | |
130 | /* Allocate bus num dynamically. */ |
131 | pdata->bus_num = -1; |
132 | |
133 | #ifdef CONFIG_FSL_SOC |
134 | /* SPI controller is either clocked from QE or SoC clock. */ |
135 | pdata->sysclk = get_brgfreq(); |
136 | if (pdata->sysclk == -1) { |
137 | pdata->sysclk = fsl_get_sys_freq(); |
138 | if (pdata->sysclk == -1) |
139 | return -ENODEV; |
140 | } |
141 | #else |
142 | ret = of_property_read_u32(np, propname: "clock-frequency" , out_value: &pdata->sysclk); |
143 | if (ret) |
144 | return ret; |
145 | #endif |
146 | |
147 | prop = of_get_property(node: np, name: "mode" , NULL); |
148 | if (prop && !strcmp(prop, "cpu-qe" )) |
149 | pdata->flags = SPI_QE_CPU_MODE; |
150 | else if (prop && !strcmp(prop, "qe" )) |
151 | pdata->flags = SPI_CPM_MODE | SPI_QE; |
152 | else if (of_device_is_compatible(device: np, "fsl,cpm2-spi" )) |
153 | pdata->flags = SPI_CPM_MODE | SPI_CPM2; |
154 | else if (of_device_is_compatible(device: np, "fsl,cpm1-spi" )) |
155 | pdata->flags = SPI_CPM_MODE | SPI_CPM1; |
156 | |
157 | return 0; |
158 | } |
159 | EXPORT_SYMBOL_GPL(of_mpc8xxx_spi_probe); |
160 | |
161 | MODULE_LICENSE("GPL" ); |
162 | |