Root/
1 | /* |
2 | * SPEAr platform SPI chipselect abstraction over gpiolib |
3 | * |
4 | * Copyright (C) 2012 ST Microelectronics |
5 | * Shiraz Hashim <shiraz.hashim@st.com> |
6 | * |
7 | * This file is licensed under the terms of the GNU General Public |
8 | * License version 2. This program is licensed "as is" without any |
9 | * warranty of any kind, whether express or implied. |
10 | */ |
11 | |
12 | #include <linux/err.h> |
13 | #include <linux/gpio.h> |
14 | #include <linux/io.h> |
15 | #include <linux/module.h> |
16 | #include <linux/of.h> |
17 | #include <linux/platform_device.h> |
18 | #include <linux/types.h> |
19 | |
20 | /* maximum chipselects */ |
21 | #define NUM_OF_GPIO 4 |
22 | |
23 | /* |
24 | * Provision is available on some SPEAr SoCs to control ARM PL022 spi cs |
25 | * through system registers. This register lies outside spi (pl022) |
26 | * address space into system registers. |
27 | * |
28 | * It provides control for spi chip select lines so that any chipselect |
29 | * (out of 4 possible chipselects in pl022) can be made low to select |
30 | * the particular slave. |
31 | */ |
32 | |
33 | /** |
34 | * struct spear_spics - represents spi chip select control |
35 | * @base: base address |
36 | * @perip_cfg: configuration register |
37 | * @sw_enable_bit: bit to enable s/w control over chipselects |
38 | * @cs_value_bit: bit to program high or low chipselect |
39 | * @cs_enable_mask: mask to select bits required to select chipselect |
40 | * @cs_enable_shift: bit pos of cs_enable_mask |
41 | * @use_count: use count of a spi controller cs lines |
42 | * @last_off: stores last offset caller of set_value() |
43 | * @chip: gpio_chip abstraction |
44 | */ |
45 | struct spear_spics { |
46 | void __iomem *base; |
47 | u32 perip_cfg; |
48 | u32 sw_enable_bit; |
49 | u32 cs_value_bit; |
50 | u32 cs_enable_mask; |
51 | u32 cs_enable_shift; |
52 | unsigned long use_count; |
53 | int last_off; |
54 | struct gpio_chip chip; |
55 | }; |
56 | |
57 | /* gpio framework specific routines */ |
58 | static int spics_get_value(struct gpio_chip *chip, unsigned offset) |
59 | { |
60 | return -ENXIO; |
61 | } |
62 | |
63 | static void spics_set_value(struct gpio_chip *chip, unsigned offset, int value) |
64 | { |
65 | struct spear_spics *spics = container_of(chip, struct spear_spics, |
66 | chip); |
67 | u32 tmp; |
68 | |
69 | /* select chip select from register */ |
70 | tmp = readl_relaxed(spics->base + spics->perip_cfg); |
71 | if (spics->last_off != offset) { |
72 | spics->last_off = offset; |
73 | tmp &= ~(spics->cs_enable_mask << spics->cs_enable_shift); |
74 | tmp |= offset << spics->cs_enable_shift; |
75 | } |
76 | |
77 | /* toggle chip select line */ |
78 | tmp &= ~(0x1 << spics->cs_value_bit); |
79 | tmp |= value << spics->cs_value_bit; |
80 | writel_relaxed(tmp, spics->base + spics->perip_cfg); |
81 | } |
82 | |
83 | static int spics_direction_input(struct gpio_chip *chip, unsigned offset) |
84 | { |
85 | return -ENXIO; |
86 | } |
87 | |
88 | static int spics_direction_output(struct gpio_chip *chip, unsigned offset, |
89 | int value) |
90 | { |
91 | spics_set_value(chip, offset, value); |
92 | return 0; |
93 | } |
94 | |
95 | static int spics_request(struct gpio_chip *chip, unsigned offset) |
96 | { |
97 | struct spear_spics *spics = container_of(chip, struct spear_spics, |
98 | chip); |
99 | u32 tmp; |
100 | |
101 | if (!spics->use_count++) { |
102 | tmp = readl_relaxed(spics->base + spics->perip_cfg); |
103 | tmp |= 0x1 << spics->sw_enable_bit; |
104 | tmp |= 0x1 << spics->cs_value_bit; |
105 | writel_relaxed(tmp, spics->base + spics->perip_cfg); |
106 | } |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | static void spics_free(struct gpio_chip *chip, unsigned offset) |
112 | { |
113 | struct spear_spics *spics = container_of(chip, struct spear_spics, |
114 | chip); |
115 | u32 tmp; |
116 | |
117 | if (!--spics->use_count) { |
118 | tmp = readl_relaxed(spics->base + spics->perip_cfg); |
119 | tmp &= ~(0x1 << spics->sw_enable_bit); |
120 | writel_relaxed(tmp, spics->base + spics->perip_cfg); |
121 | } |
122 | } |
123 | |
124 | static int spics_gpio_probe(struct platform_device *pdev) |
125 | { |
126 | struct device_node *np = pdev->dev.of_node; |
127 | struct spear_spics *spics; |
128 | struct resource *res; |
129 | int ret; |
130 | |
131 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
132 | if (!res) { |
133 | dev_err(&pdev->dev, "invalid IORESOURCE_MEM\n"); |
134 | return -EBUSY; |
135 | } |
136 | |
137 | spics = devm_kzalloc(&pdev->dev, sizeof(*spics), GFP_KERNEL); |
138 | if (!spics) { |
139 | dev_err(&pdev->dev, "memory allocation fail\n"); |
140 | return -ENOMEM; |
141 | } |
142 | |
143 | spics->base = devm_ioremap_resource(&pdev->dev, res); |
144 | if (IS_ERR(spics->base)) |
145 | return PTR_ERR(spics->base); |
146 | |
147 | if (of_property_read_u32(np, "st-spics,peripcfg-reg", |
148 | &spics->perip_cfg)) |
149 | goto err_dt_data; |
150 | if (of_property_read_u32(np, "st-spics,sw-enable-bit", |
151 | &spics->sw_enable_bit)) |
152 | goto err_dt_data; |
153 | if (of_property_read_u32(np, "st-spics,cs-value-bit", |
154 | &spics->cs_value_bit)) |
155 | goto err_dt_data; |
156 | if (of_property_read_u32(np, "st-spics,cs-enable-mask", |
157 | &spics->cs_enable_mask)) |
158 | goto err_dt_data; |
159 | if (of_property_read_u32(np, "st-spics,cs-enable-shift", |
160 | &spics->cs_enable_shift)) |
161 | goto err_dt_data; |
162 | |
163 | platform_set_drvdata(pdev, spics); |
164 | |
165 | spics->chip.ngpio = NUM_OF_GPIO; |
166 | spics->chip.base = -1; |
167 | spics->chip.request = spics_request; |
168 | spics->chip.free = spics_free; |
169 | spics->chip.direction_input = spics_direction_input; |
170 | spics->chip.direction_output = spics_direction_output; |
171 | spics->chip.get = spics_get_value; |
172 | spics->chip.set = spics_set_value; |
173 | spics->chip.label = dev_name(&pdev->dev); |
174 | spics->chip.dev = &pdev->dev; |
175 | spics->chip.owner = THIS_MODULE; |
176 | spics->last_off = -1; |
177 | |
178 | ret = gpiochip_add(&spics->chip); |
179 | if (ret) { |
180 | dev_err(&pdev->dev, "unable to add gpio chip\n"); |
181 | return ret; |
182 | } |
183 | |
184 | dev_info(&pdev->dev, "spear spics registered\n"); |
185 | return 0; |
186 | |
187 | err_dt_data: |
188 | dev_err(&pdev->dev, "DT probe failed\n"); |
189 | return -EINVAL; |
190 | } |
191 | |
192 | static const struct of_device_id spics_gpio_of_match[] = { |
193 | { .compatible = "st,spear-spics-gpio" }, |
194 | {} |
195 | }; |
196 | MODULE_DEVICE_TABLE(of, spics_gpio_of_match); |
197 | |
198 | static struct platform_driver spics_gpio_driver = { |
199 | .probe = spics_gpio_probe, |
200 | .driver = { |
201 | .owner = THIS_MODULE, |
202 | .name = "spear-spics-gpio", |
203 | .of_match_table = spics_gpio_of_match, |
204 | }, |
205 | }; |
206 | |
207 | static int __init spics_gpio_init(void) |
208 | { |
209 | return platform_driver_register(&spics_gpio_driver); |
210 | } |
211 | subsys_initcall(spics_gpio_init); |
212 | |
213 | MODULE_AUTHOR("Shiraz Hashim <shiraz.hashim@st.com>"); |
214 | MODULE_DESCRIPTION("ST Microlectronics SPEAr SPI Chip Select Abstraction"); |
215 | MODULE_LICENSE("GPL"); |
216 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9