handle new revisions of vlynq wrt reset sequence, patch from sn9
[oweals/openwrt.git] / target / linux / ar7 / patches-2.6.32 / 160-vlynq_try_remote_first.patch
1 Index: linux-2.6.32.26/drivers/vlynq/vlynq.c
2 ===================================================================
3 --- linux-2.6.32.26.orig/drivers/vlynq/vlynq.c  2010-11-24 13:01:20.459985351 -0800
4 +++ linux-2.6.32.26/drivers/vlynq/vlynq.c       2010-11-24 13:01:43.537494084 -0800
5 @@ -103,6 +103,12 @@
6  }
7  #endif
8  
9 +u32 __vlynq_rev_reg(struct vlynq_regs *regs)
10 +{
11 +       return readl(&regs->revision);
12 +}
13 +EXPORT_SYMBOL(__vlynq_rev_reg);
14 +
15  /* Check the VLYNQ link status with a given device */
16  static int vlynq_linked(struct vlynq_device *dev)
17  {
18 @@ -117,20 +123,43 @@
19         return 0;
20  }
21  
22 +static volatile int vlynq_delay_value_new = 0;
23 +
24 +static void vlynq_delay_wait(u32 count)
25 +{
26 +       /* Code adopted from original vlynq driver */
27 +       int i = 0;
28 +       volatile int *ptr = &vlynq_delay_value_new;
29 +       *ptr = 0;
30 +
31 +       /* We are assuming that the each cycle takes about
32 +        * 23 assembly instructions. */
33 +       for(i = 0; i < (count + 23)/23; i++)
34 +               *ptr = *ptr + 1;
35 +}
36 +
37  static void vlynq_reset(struct vlynq_device *dev)
38  {
39 +       u32 rtm = readl(&dev->local->revision);
40 +
41 +       if (rtm < 0x00010200)
42 +               return;
43 +
44 +       rtm = rtm < 0x00010205 || readl(&dev->local->status) & 0x800 == 0 ?
45 +                       0 : 0x600000;
46 +
47         writel(readl(&dev->local->control) | VLYNQ_CTRL_RESET,
48                         &dev->local->control);
49  
50         /* Wait for the devices to finish resetting */
51 -       msleep(5);
52 +       vlynq_delay_wait(0xffffff);
53  
54         /* Remove reset bit */
55 -       writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET,
56 +       writel(readl(&dev->local->control) & ~VLYNQ_CTRL_RESET | rtm,
57                         &dev->local->control);
58  
59         /* Give some time for the devices to settle */
60 -       msleep(5);
61 +       vlynq_delay_wait(0xffffff);
62  }
63  
64  static void vlynq_irq_unmask(unsigned int irq)
65 @@ -379,6 +408,62 @@
66  }
67  EXPORT_SYMBOL(vlynq_unregister_driver);
68  
69 +enum vlynq_clk_src {
70 +       vlynq_clk_external,
71 +       vlynq_clk_local,
72 +       vlynq_clk_remote,
73 +       vlynq_clk_invalid,
74 +};
75 +
76 +static int __vlynq_set_clocks(struct vlynq_device *dev,
77 +                               enum vlynq_clk_src clk_dir,
78 +                               int lclk_div, int rclk_div)
79 +{
80 +       u32 reg;
81 +
82 +       if (clk_dir == vlynq_clk_invalid) {
83 +               printk(KERN_ERR "%s: attempt to set invalid clocking\n",
84 +                               dev_name(&dev->dev));
85 +               return -EINVAL;
86 +       }
87 +
88 +       printk(KERN_INFO "%s: local VLYNQ protocol rev. is 0x%08x\n",
89 +                       dev_name(&dev->dev), readl(&dev->local->revision));
90 +
91 +       reg = readl(&dev->local->control);
92 +       if (readl(&dev->local->revision) < 0x00010205) {
93 +               if (clk_dir & vlynq_clk_local)
94 +                       reg |= VLYNQ_CTRL_CLOCK_INT;
95 +               else
96 +                       reg &= ~VLYNQ_CTRL_CLOCK_INT;
97 +       }
98 +       reg &= ~VLYNQ_CTRL_CLOCK_MASK;
99 +       reg |= VLYNQ_CTRL_CLOCK_DIV(lclk_div);
100 +       writel(reg, &dev->local->control);
101 +
102 +       if (!vlynq_linked(dev))
103 +               return -ENODEV;
104 +
105 +       printk(KERN_INFO "%s: remote VLYNQ protocol rev. is 0x%08x\n",
106 +                       dev_name(&dev->dev), readl(&dev->remote->revision));
107 +
108 +       reg = readl(&dev->remote->control);
109 +       if (readl(&dev->remote->revision) < 0x00010205) {
110 +               if (clk_dir & vlynq_clk_remote)
111 +                       reg |= VLYNQ_CTRL_CLOCK_INT;
112 +               else
113 +                       reg &= ~VLYNQ_CTRL_CLOCK_INT;
114 +       }
115 +       reg &= ~VLYNQ_CTRL_CLOCK_MASK;
116 +       reg |= VLYNQ_CTRL_CLOCK_DIV(rclk_div);
117 +       writel(reg, &dev->remote->control);
118 +
119 +       if (!vlynq_linked(dev))
120 +               return -ENODEV;
121 +
122 +       return 0;
123 +}
124 +
125  /*
126   * A VLYNQ remote device can clock the VLYNQ bus master
127   * using a dedicated clock line. In that case, both the
128 @@ -392,29 +477,15 @@
129         int i;
130  
131         vlynq_reset(dev);
132 -       for (i = dev->dev_id ? vlynq_rdiv2 : vlynq_rdiv8; dev->dev_id ?
133 -                       i <= vlynq_rdiv8 : i >= vlynq_rdiv2;
134 -               dev->dev_id ? i++ : i--) {
135 -
136 +       for (i = 0; i <= 7; i++) {
137                 if (!vlynq_linked(dev))
138                         break;
139  
140 -               writel((readl(&dev->remote->control) &
141 -                               ~VLYNQ_CTRL_CLOCK_MASK) |
142 -                               VLYNQ_CTRL_CLOCK_INT |
143 -                               VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
144 -                               &dev->remote->control);
145 -               writel((readl(&dev->local->control)
146 -                               & ~(VLYNQ_CTRL_CLOCK_INT |
147 -                               VLYNQ_CTRL_CLOCK_MASK)) |
148 -                               VLYNQ_CTRL_CLOCK_DIV(i - vlynq_rdiv1),
149 -                               &dev->local->control);
150 -
151 -               if (vlynq_linked(dev)) {
152 +               if (!__vlynq_set_clocks(dev, vlynq_clk_remote, i, i)) {
153                         printk(KERN_DEBUG
154 -                               "%s: using remote clock divisor %d\n",
155 -                               dev_name(&dev->dev), i - vlynq_rdiv1 + 1);
156 -                       dev->divisor = i;
157 +                                       "%s: using remote clock divisor %d\n",
158 +                                       dev_name(&dev->dev), i + 1);
159 +                       dev->divisor = i + vlynq_rdiv1;
160                         return 0;
161                 } else {
162                         vlynq_reset(dev);
163 @@ -437,21 +508,12 @@
164  
165         vlynq_reset(dev);
166  
167 -       for (i = dev->dev_id ? vlynq_ldiv2 : vlynq_ldiv8; dev->dev_id ?
168 -                       i <= vlynq_ldiv8 : i >= vlynq_ldiv2;
169 -               dev->dev_id ? i++ : i--) {
170 -
171 -               writel((readl(&dev->local->control) &
172 -                               ~VLYNQ_CTRL_CLOCK_MASK) |
173 -                               VLYNQ_CTRL_CLOCK_INT |
174 -                               VLYNQ_CTRL_CLOCK_DIV(i - vlynq_ldiv1),
175 -                               &dev->local->control);
176 -
177 -               if (vlynq_linked(dev)) {
178 +       for (i = 0; i <= 7; i++) {
179 +               if (!__vlynq_set_clocks(dev, vlynq_clk_local, i, 0)) {
180                         printk(KERN_DEBUG
181 -                               "%s: using local clock divisor %d\n",
182 -                               dev_name(&dev->dev), i - vlynq_ldiv1 + 1);
183 -                       dev->divisor = i;
184 +                                       "%s: using local clock divisor %d\n",
185 +                                       dev_name(&dev->dev), i + 1);
186 +                       dev->divisor = i + vlynq_ldiv1;
187                         return 0;
188                 } else {
189                         vlynq_reset(dev);
190 @@ -473,18 +535,10 @@
191         if (!vlynq_linked(dev))
192                 return -ENODEV;
193  
194 -       writel((readl(&dev->remote->control) &
195 -                       ~VLYNQ_CTRL_CLOCK_INT),
196 -                       &dev->remote->control);
197 -
198 -       writel((readl(&dev->local->control) &
199 -                       ~VLYNQ_CTRL_CLOCK_INT),
200 -                       &dev->local->control);
201 -
202 -       if (vlynq_linked(dev)) {
203 +       if (!__vlynq_set_clocks(dev, vlynq_clk_external, 0, 0)) {
204                 printk(KERN_DEBUG "%s: using external clock\n",
205 -                       dev_name(&dev->dev));
206 -                       dev->divisor = vlynq_div_external;
207 +                               dev_name(&dev->dev));
208 +                               dev->divisor = vlynq_div_external;
209                 return 0;
210         }
211  
212 @@ -507,18 +561,9 @@
213                  * generation negotiated by hardware.
214                  * Check which device is generating clocks and perform setup
215                  * accordingly */
216 -               if (vlynq_linked(dev) && readl(&dev->remote->control) &
217 -                  VLYNQ_CTRL_CLOCK_INT) {
218 -                       if (!__vlynq_try_remote(dev) ||
219 -                               !__vlynq_try_local(dev)  ||
220 -                               !__vlynq_try_external(dev))
221 -                               return 0;
222 -               } else {
223 -                       if (!__vlynq_try_external(dev) ||
224 -                               !__vlynq_try_local(dev)    ||
225 -                               !__vlynq_try_remote(dev))
226 -                               return 0;
227 -               }
228 +               if (!__vlynq_try_remote(dev) || !__vlynq_try_local(dev) ||
229 +                                               !__vlynq_try_external(dev))
230 +                       return 0;
231                 break;
232         case vlynq_ldiv1:
233         case vlynq_ldiv2:
234 @@ -528,15 +573,12 @@
235         case vlynq_ldiv6:
236         case vlynq_ldiv7:
237         case vlynq_ldiv8:
238 -               writel(VLYNQ_CTRL_CLOCK_INT |
239 -                       VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
240 -                       vlynq_ldiv1), &dev->local->control);
241 -               writel(0, &dev->remote->control);
242 -               if (vlynq_linked(dev)) {
243 +               if (!__vlynq_set_clocks(dev, vlynq_clk_local, dev->divisor -
244 +                               vlynq_ldiv1, 0)) {
245                         printk(KERN_DEBUG
246 -                               "%s: using local clock divisor %d\n",
247 -                               dev_name(&dev->dev),
248 -                               dev->divisor - vlynq_ldiv1 + 1);
249 +                                       "%s: using local clock divisor %d\n",
250 +                                       dev_name(&dev->dev),
251 +                                       dev->divisor - vlynq_ldiv1 + 1);
252                         return 0;
253                 }
254                 break;
255 @@ -548,15 +590,12 @@
256         case vlynq_rdiv6:
257         case vlynq_rdiv7:
258         case vlynq_rdiv8:
259 -               writel(0, &dev->local->control);
260 -               writel(VLYNQ_CTRL_CLOCK_INT |
261 -                       VLYNQ_CTRL_CLOCK_DIV(dev->divisor -
262 -                       vlynq_rdiv1), &dev->remote->control);
263 -               if (vlynq_linked(dev)) {
264 +               if (!__vlynq_set_clocks(dev, vlynq_clk_remote, 0,
265 +                               dev->divisor - vlynq_rdiv1)) {
266                         printk(KERN_DEBUG
267 -                               "%s: using remote clock divisor %d\n",
268 -                               dev_name(&dev->dev),
269 -                               dev->divisor - vlynq_rdiv1 + 1);
270 +                                       "%s: using remote clock divisor %d\n",
271 +                                       dev_name(&dev->dev),
272 +                                       dev->divisor - vlynq_rdiv1 + 1);
273                         return 0;
274                 }
275                 break;
276 @@ -732,13 +771,13 @@
277         platform_set_drvdata(pdev, dev);
278  
279         printk(KERN_INFO "%s: regs 0x%p, irq %d, mem 0x%p\n",
280 -              dev_name(&dev->dev), (void *)dev->regs_start, dev->irq,
281 -              (void *)dev->mem_start);
282 +                       dev_name(&dev->dev), (void *)dev->regs_start,
283 +                       dev->irq, (void *)dev->mem_start);
284  
285 -       dev->dev_id = 0;
286         dev->divisor = vlynq_div_auto;
287 -       result = __vlynq_enable_device(dev);
288 -       if (result == 0) {
289 +       if (__vlynq_enable_device(dev))
290 +               dev->dev_id = 0;
291 +       else {
292                 dev->dev_id = readl(&dev->remote->chip);
293                 ((struct plat_vlynq_ops *)(dev->dev.platform_data))->off(dev);
294         }
295 Index: linux-2.6.32.26/include/linux/vlynq.h
296 ===================================================================
297 --- linux-2.6.32.26.orig/include/linux/vlynq.h  2010-11-24 13:07:33.297487888 -0800
298 +++ linux-2.6.32.26/include/linux/vlynq.h       2010-11-24 13:08:44.107488596 -0800
299 @@ -98,6 +98,7 @@
300  
301  extern struct bus_type vlynq_bus_type;
302  
303 +extern u32 __vlynq_rev_reg(struct vlynq_regs *regs);
304  extern int __vlynq_register_driver(struct vlynq_driver *driver,
305                                    struct module *owner);
306