blob: 101c486e11c4cd15c19d10d3691f90eb02f73f61 [file] [log] [blame]
developere0cea0f2021-12-16 16:08:26 +08001// SPDX-License-Identifier: GPL-2.0
2
3#include <linux/module.h>
4#include <linux/spi/spi.h>
5#include <linux/spinlock.h>
6#include <linux/of_gpio.h>
7#include <linux/gpio.h>
8#include <linux/delay.h>
9
10#include "proslic_sys.h"
11
12/*****************************************************************************************************/
13
14#define PROSLIC_REG_ID 0
15#define PROSLIC_REG_RAM_WAIT 4
16#define PROSLIC_REG_RAM_HI 5
17#define PROSLIC_REG_RAM_D0 6
18#define PROSLIC_REG_RAM_D1 7
19#define PROSLIC_REG_RAM_D2 8
20#define PROSLIC_REG_RAM_D3 9
21#define PROSLIC_REG_RAM_LO 10
22#define PROSLIC_REG_WRVSLIC 12
23#define PROSLIC_REG_WRVDAA 34
24#define PROSLIC_REG_ISDAA 11
25
26#define PROSLIC_CW_BYTE 0
27#define PROSLIC_REG_BYTE 1
28#define RAM_ADR_HIGH_MASK(ADDR) (((ADDR)>>3)&0xE0)
29
30/* Basic register definitions, regardless of chipset ('4x, '2x, '17x, '26x compatible */
31
32#define PROSLIC_CW_RD 0x60
33#define PROSLIC_CW_WR 0x20
34#define PROSLIC_CW_BCAST 0x80
35#define PROSLIC_BCAST 0xFF
36
37#ifndef PROSLIC_XFER_BUF
38#define PROSLIC_XFER_BUF (SILABS_BITS_PER_WORD/8)
39#endif
40
41/* Now do some sanity checks */
42#if (SILABS_BITS_PER_WORD != 32) && (SILABS_BITS_PER_WORD != 16) && (SILABS_BITS_PER_WORD != 8)
43#error "SILABS_BITS_PER_WORD needs to be either 8, 16 or 32"
44#endif
45
46#if (((SILABS_BITS_PER_WORD == 16) && (PROSLIC_XFER_BUF != 2) ) \
47 || ((SILABS_BITS_PER_WORD == 32) && (PROSLIC_XFER_BUF != 4) ) )
48#error "SILABS_BITS_PER_WORD & PROSLIC_XFER_BUF size mismatch"
49#endif
50
51#if (SILABS_BITS_PER_WORD != 8)
52#define SILABS_CW_BYTE 1
53#define SILABS_REG_BYTE 0
54#else
55#define SILABS_CW_BYTE 0
56#define SILABS_REG_BYTE 1
57#endif
58
59typedef struct
60{
61 proslic_dev_t deviceType[SILABS_MAX_CHANNELS];
62 spinlock_t bus_lock;
63 uInt8 channel_count; /* In most cases this is the channel count as well */
64} proslic_spi_data_t;
65
66typedef struct
67{
68 /* Placeholder for now */
69} proslic_spi_pform_t;
70
71/* Chan address is sent in reverse bit order */
72static uInt8 chanAddr[SILABS_MAX_CHAN] =
73{
74 0x00, 0x10,
75#if (SILABS_MAX_CHAN > 2)
76 0x08, 0x18,
77#endif
78#if (SILABS_MAX_CHAN > 4)
79 0x04, 0x14,
80#endif
81#if (SILABS_MAX_CHAN > 6)
82 0x0C, 0x1C,
83#endif
84#if (SILABS_MAX_CHAN > 8)
85 0x02, 0x12,
86#endif
87#if (SILABS_MAX_CHAN > 10)
88 0x0A, 0x1A,
89#endif
90#if (SILABS_MAX_CHAN > 12)
91 0x06, 0x16,
92#endif
93#if (SILABS_MAX_CHAN > 14)
94 0x0E, 0x1E,
95#endif
96#if (SILABS_MAX_CHAN > 16)
97 0x01, 0x11,
98#endif
99#if (SILABS_MAX_CHAN > 18)
100 0x09, 0x19,
101#endif
102#if (SILABS_MAX_CHAN > 20)
103 0x05, 0x15,
104#endif
105#if (SILABS_MAX_CHAN > 22)
106 0x0D, 0x1D,
107#endif
108#if (SILABS_MAX_CHAN > 24)
109 0x03, 0x13,
110#endif
111#if (SILABS_MAX_CHAN > 26)
112 0x0B, 0x1B,
113#endif
114#if (SILABS_MAX_CHAN > 28)
115 0x07,0x17,
116#endif
117#if (SILABS_MAX_CHAN > 30)
118 0x0F, 0x1F
119#endif
120};
121
122static proslic_spi_data_t *proslic_data;
123static unsigned int proslic_reset_gpio = SILABS_RESET_GPIO;
124struct spi_driver *proslic_spi;
125struct spi_device *proslic_spidev;
126
127/*****************************************************************************************************/
128uInt8 proslic_read_register_private(void *hCtrl, uInt8 channel, uInt8 regAddr)
129{
130 u_int8_t data_stream[4];
131// proslic_spi_data_t *spi_data = (proslic_spi_data_t *)hCtrl;
132 if (proslic_spidev == NULL)
133 printk ("%s : small spi NULL\n",__FUNCTION__);
134
135 if( unlikely(channel >= SILABS_MAX_CHANNELS) )
136 {
137 return PROSLIC_SPI_NOK;
138 }
139 else
140 {
141 data_stream[PROSLIC_CW_BYTE] = PROSLIC_CW_RD | chanAddr[channel];
142 }
143
144 data_stream[PROSLIC_REG_BYTE] = regAddr;
145
146 data_stream[2] = 0xFF; /* Set the register to a non-zero value, just in case */
147#if (PROSLIC_XFER_BUF == 4) || (PROSLIC_XFER_BUF == 2)
148 spi_write_then_read(proslic_spidev, data_stream, 2, &data_stream[2], 2); /* In essence, 2 16 bit ops: 1 to send the request, 1 to get the data, needs to be 32 bit aligned. */
149 proslic_debug("%s(%d): chan = %u reg = %u data = 0x%02X", __FUNCTION__, __LINE__, channel, regAddr, data_stream[2]);
150#else
151 {
152 int i;
153 for(i = 0; i < 2; i++)
154 {
155 spi_write(proslic_spidev, &data_stream[i], 1);
156 }
157 }
158 spi_read(proslic_spidev, &data_stream[2], 1);
159#endif
160
161 return data_stream[2];
162}
163
164/*****************************************************************************************************/
165
166uInt8 proslic_read_register(void *hCtrl, uInt8 channel, uInt8 regAddr)
167{
168 uInt8 data;
169 //unsigned long irqSettings;
170 //proslic_spi_data_t *spi_data = (proslic_spi_data_t *)hCtrl;
171 //spin_lock_irqsave(&spi_data->bus_lock, irqSettings);
172 data = proslic_read_register_private(hCtrl, channel, regAddr);
173 //spin_unlock_irqrestore(&spi_data->bus_lock, irqSettings);
174 proslic_debug("%s(%d): chan = %u reg = %u data = 0x%02X", __FUNCTION__, __LINE__, channel, regAddr, data);
175 return data;
176}
177EXPORT_SYMBOL(proslic_read_register);
178
179
180/*****************************************************************************************************/
181/* ProSLIC API register write function */
182#if PROSLIC_XFER_BUF == 4
183#define proslic_write_register_private proslic_write_register
184#endif
185
186int proslic_write_register_private(void *hCtrl, uInt8 channel, uInt8 regAddr, uInt8 data)
187{
188 u_int8_t data_stream[4];
189 //struct spi_device *spi_data = (struct spi_device *)hCtrl;
190
191#if PROSLIC_XFER_BUF != 4
192 int i;
193#endif
194
195 if( unlikely(channel == PROSLIC_BCAST) )
196 {
197 data_stream[PROSLIC_CW_BYTE] = PROSLIC_CW_BCAST | PROSLIC_CW_WR;
198 }
199 else if( unlikely(channel >= SILABS_MAX_CHANNELS) )
200 {
201 return PROSLIC_SPI_NOK;
202 }
203 else
204 {
205 data_stream[PROSLIC_CW_BYTE] = PROSLIC_CW_WR | chanAddr[channel];
206 }
207
208 proslic_trace("%s(%d): chan = %u reg = %u data = 0x%02X", __FUNCTION__, __LINE__, channel, regAddr, data);
209
210 data_stream[PROSLIC_REG_BYTE] = regAddr;
211
212 data_stream[2] = data_stream[3] = data;
213
214#if PROSLIC_XFER_BUF == 4
215 spi_write(proslic_spidev, data_stream, PROSLIC_XFER_BUF);
216#else
217#if PROSLIC_XFER_BUF == 2 /* 16 bit mode */
218 for(i = 0; i < 4; i+=2)
219#else /* Byte by byte */
220 for(i = 0; i < 3; i++)
221#endif
222 {
223 spi_write(proslic_spidev, &data_stream[i], PROSLIC_XFER_BUF);
224 }
225#endif
226
227 return PROSLIC_SPI_OK;
228}
229/*****************************************************************************************************/
230#if PROSLIC_XFER_BUF != 4
231int proslic_write_register(void *hCtrl, uInt8 channel, uInt8 regAddr, uInt8 data)
232{
233 unsigned long irqSettings;
234 int rc;
235 //proslic_spi_data_t *spi_data = (proslic_spi_data_t *)hCtrl;
236 //spin_lock_irqsave(&spi_data->bus_lock, irqSettings);
237 rc = proslic_write_register_private(hCtrl, channel, regAddr, data);
238 //spin_unlock_irqrestore(&spi_data->bus_lock, irqSettings);
239 return rc;
240}
241EXPORT_SYMBOL(proslic_write_register);
242#endif
243
244/*****************************************************************************************************/
245
246/*
247 * wait for RAM access
248 *
249 * return code 0 = OK
250 *
251 */
252
253static int wait_ram(void *hCtrl, uInt8 channel)
254{
255 unsigned int count = SILABS_MAX_RAM_WAIT;
256 uInt8 data;
257
258 do
259 {
260 data = proslic_read_register_private(hCtrl, channel, PROSLIC_REG_RAM_WAIT) & 0x1;
261 if(unlikely(data))
262 {
263 mdelay(5);
264 }
265 }while((data) && (count--));
266
267 if(likely(count))
268 {
269 return 0;
270 }
271 return -1; /* Timed out */
272}
273
274/*****************************************************************************************************/
275/* RAM Write wrapper */
276int proslic_write_ram(void *hCtrl, uInt8 channel, uInt16 ramAddr, ramData data )
277{
278
279 ramData myData = data;
280 int rc = 0;
281 //unsigned long irqSettings;
282 //struct spi_device *spi_data = (struct spi_device *)hCtrl;
283 hCtrl = proslic_spidev;
284
285 proslic_trace("%s(%d): chan = %u ram = %u data = 0x%08X", __FUNCTION__, __LINE__, channel, ramAddr, data);
286 //spin_lock_irqsave(&spi_data->bus_lock, irqSettings);
287
288 if(wait_ram(hCtrl,channel) != 0)
289 {
290 return PROSLIC_SPI_NOK;
291 }
292
293
294#ifdef SILABS_RAM_BLOCK_ACCESS
295 {
296 uInt8 ramWriteData[24]; /* This encapsulates the 6 reg writes into 1 block */
297 const uInt8 regAddr[6] = {PROSLIC_REG_RAM_HI, PROSLIC_REG_RAM_D0, PROSLIC_REG_RAM_D1,
298 PROSLIC_REG_RAM_D2, PROSLIC_REG_RAM_D3, PROSLIC_REG_RAM_LO};
299 int i;
300 uInt8 scratch;
301
302 /* Setup control word & registers for ALL the reg access */
303 scratch = chanAddr[channel] | PROSLIC_CW_WR;
304
305 for(i = 0; i < 6; i++)
306 {
307 ramWriteData[(i<<2)] = scratch ;
308 ramWriteData[(i<<2)+1] = regAddr[i];
309 }
310
311 /* For the data, we send the same byte twice to keep things 32 bit aligned */
312 ramWriteData[2] = ramWriteData[3] = RAM_ADR_HIGH_MASK(ramAddr);
313
314 ramWriteData[6] = ramWriteData[7] = (uInt8)(myData<<3);
315 myData = myData >> 5;
316
317 ramWriteData[10] = ramWriteData[11] = (uInt8)(myData & 0xFF);
318 myData = myData >> 8;
319
320 ramWriteData[14] = ramWriteData[15] = (uInt8)(myData & 0xFF);
321 myData = myData >> 8;
322
323 ramWriteData[18] = ramWriteData[19] = (uInt8)(myData & 0xFF);
324
325 ramWriteData[22] = ramWriteData[23] = (uInt8)(ramAddr& 0xFF);
326
327 spi_write(hCtrl, ramWriteData, 24);
328
329 }
330#else
331 proslic_write_register_private(hCtrl, channel, RAM_ADDR_HI, RAM_ADR_HIGH_MASK(ramAddr));
332
333 proslic_write_register_private(hCtrl, channel, RAM_DATA_B0, ((unsigned char)(myData<<3)));
334
335 myData = myData >> 5;
336
337 proslic_write_register_private(hCtrl, channel, RAM_DATA_B1, ((unsigned char)(myData & 0xFF)));
338
339 myData = myData >> 8;
340
341 proslic_write_register_private(hCtrl, channel, RAM_DATA_B2, ((unsigned char)(myData & 0xFF)));
342
343 myData = myData >> 8;
344
345 proslic_write_register_private(hCtrl, channel, RAM_DATA_B3, ((unsigned char)(myData & 0xFF)));
346
347 proslic_write_register_private(hCtrl, channel, RAM_ADDR_LO, (unsigned char)(ramAddr & 0xFF));
348#endif
349 if(wait_ram(hCtrl,channel) != 0)
350 {
351 rc = PROSLIC_SPI_NOK;
352 }
353 else
354 {
355 rc = PROSLIC_SPI_OK;
356 }
357 //spin_unlock_irqrestore(&spi_data->bus_lock, irqSettings);
358
359 return rc;
360
361}
362EXPORT_SYMBOL(proslic_write_ram);
363
364/*****************************************************************************************************/
365ramData proslic_read_ram(void *hCtrl, uInt8 channel, uInt16 ramAddr)
366{
367 ramData data;
368 //unsigned long irqSettings;
369 //proslic_spi_data_t *spi_data = (proslic_spi_data_t *)hCtrl;
370 hCtrl = proslic_spidev;
371
372 if(wait_ram(hCtrl,channel) != 0)
373 {
374 return PROSLIC_SPI_NOK;
375 }
376
377 //spin_lock_irqsave(&spi_data->bus_lock, irqSettings);
378 /* TODO: could combine these 2 writes into 1 spi_write call... */
379 proslic_write_register_private(hCtrl, channel, PROSLIC_REG_RAM_HI, RAM_ADR_HIGH_MASK(ramAddr));
380
381 proslic_write_register_private(hCtrl, channel, PROSLIC_REG_RAM_LO, (unsigned char)(ramAddr&0xFF));
382
383 if(wait_ram(hCtrl,channel) != 0)
384 {
385 return PROSLIC_SPI_NOK;
386 }
387
388 data = proslic_read_register_private(hCtrl, channel, PROSLIC_REG_RAM_D3);
389 data = data << 8;
390 data |= proslic_read_register_private(hCtrl, channel, PROSLIC_REG_RAM_D2);
391 data = data << 8;
392 data |= proslic_read_register_private(hCtrl, channel, PROSLIC_REG_RAM_D1);
393 data = data << 8;
394 data |= proslic_read_register_private(hCtrl, channel, PROSLIC_REG_RAM_D0);
395 //spin_unlock_irqrestore(&spi_data->bus_lock, irqSettings);
396
397 data = data >>3;
398
399 proslic_trace("%s(%d): chan = %u ram = %u data = 0x%08X", __FUNCTION__, __LINE__, channel, ramAddr, data);
400
401 return data;
402
403}
404EXPORT_SYMBOL(proslic_read_ram);
405
406/*****************************************************************************************************/
407int proslic_reset(void *hCtrl, int in_reset)
408{
409 proslic_trace("%s(%d): in_reset = %d", __FUNCTION__, __LINE__, in_reset);
410 gpio_direction_output(proslic_reset_gpio, (in_reset == 0) );
411
412 return PROSLIC_SPI_OK;
413}
414EXPORT_SYMBOL(proslic_reset);
415
416/*****************************************************************************************************/
417
418/*
419 * Do a simple write/verify test on a given register. 0 = OK.
420 */
421static int simple_wrv(void *hCtrl, uInt8 channel, uInt8 regAddr)
422{
423 uInt8 data;
424
425 proslic_write_register(hCtrl, channel, regAddr, 0);
426 data = proslic_read_register(hCtrl, channel, regAddr);
427 if(unlikely(data != 0) )
428 {
429 return -1;
430 }
431
432 proslic_write_register(hCtrl, channel, regAddr, 0x5A);
433 data = proslic_read_register(hCtrl, channel, regAddr);
434 if(unlikely(data != 0x5A) )
435 {
436 return -2;
437 }
438
439 return 0;
440}
441
442/*****************************************************************************************************/
443
444/*
445 * Determine if this a ProSLIC or DAA or unknown type - NOT fully tested against ALL chipsets, may not
446 * properly work with Si3218/9 parts.
447 */
448static proslic_dev_t proslic_detect_type(void *hCtrl, uInt8 channel)
449{
450 /* Guess it's a ProSLIC first */
451 uInt8 data;
452
453 data = proslic_read_register(hCtrl, channel, PROSLIC_REG_ISDAA);
454 proslic_debug("%s(%d): channel = %d data = 0x%0X", __FUNCTION__, __LINE__, channel, data);
455
456 /* For ProSLIC ISDAA = 5, for DAA it is not (it is non-zero and not FF */
457
458 if( unlikely((data != 0xFF) && data ))
459 {
460 /* Likely a ProSLIC, let's confirm it by doing a few register write/verify operations */
461 if( data == 5)
462 {
463 if(unlikely(simple_wrv(hCtrl, channel, PROSLIC_REG_WRVSLIC) == 0))
464 {
465 proslic_debug("%s(%d): channel = %d is_proslic", __FUNCTION__, __LINE__, channel);
466 return PROSLIC_IS_PROSLIC;
467 }
468 }
469 else /* Likely a DAA/Si3050 device */
470 {
471 if(unlikely(simple_wrv(hCtrl, channel, PROSLIC_REG_WRVDAA) == 0))
472 {
473 proslic_debug("%s(%d): channel = %d is_daa", __FUNCTION__, __LINE__, channel);
474 return PROSLIC_IS_DAA;
475 }
476 }
477 }
478
479 proslic_debug("%s(%d): channel = %d is_unknown", __FUNCTION__, __LINE__, channel);
480 return PROSLIC_IS_UNKNOWN;
481}
482
483/*****************************************************************************************************/
484/* Pull in any device tree parameters */
485#ifdef CONFIG_OF
486static void proslic_of_probe(struct spi_device *spi)
487{
488 int len;
489 const __be32 *property;
490 u8 scratch;
491
492 printk(KERN_INFO "proslic_of_probe()\n");
493
494 /* see if the user specified number of channels */
495 property = of_get_property( spi->dev.of_node, "channel_count", &len);
496
497 if(property && (len >= sizeof(__be32)) )
498 {
499 scratch = be32_to_cpup(property);
500
501 if(( scratch <= SILABS_MAX_CHANNELS )
502 && (scratch > 0) )
503 {
504 proslic_channel_count = scratch;
505 }
506 }
507
508 /* See if the user specified a debug setting */
509 property = of_get_property( spi->dev.of_node, "debug_level", &len);
510
511 if(property && (len >= sizeof(__be32)) )
512 {
513 scratch = be32_to_cpup(property);
514
515 if(( scratch <= SILABS_DEFAULT_DBG )
516 && (scratch > 0) )
517 {
518 proslic_debug_setting = scratch;
519 printk(KERN_INFO "debug_level = %d\n", proslic_debug_setting);
520 }
521 }
522
523 printk(KERN_INFO "[previous]reset_gpio = %d\n", proslic_reset_gpio);
524 proslic_reset_gpio =
525 of_get_named_gpio(spi->dev.of_node, "reset_gpio", 0);
526
527 printk(KERN_INFO "reset_gpio = %d\n", proslic_reset_gpio);
528}
529#endif
530
531/*****************************************************************************************************/
532int proslic_spi_probe(struct spi_device *spi, struct spi_driver *spi_drv)
533{
534 proslic_spi_pform_t *pform_data;
535 unsigned int channel;
536 int rc;
537
538 printk(KERN_INFO "PROSLIC module being probed\n");
539
540 proslic_spi = spi_drv;
541 pform_data = (proslic_spi_pform_t *) &(spi->dev.platform_data);
542
543#ifdef CONFIG_OF
544 proslic_of_probe(spi);
545#endif
546
547 rc = gpio_request(proslic_reset_gpio,"proslic_reset");
548 if(rc == 0)
549 {
550 printk(KERN_INFO "PROSLIC GPIO registered OK");
551 gpio_export( proslic_reset_gpio, 0);
552 proslic_reset(NULL, 0);
553 }
554 else
555 {
556 printk(KERN_INFO "PROSLIC GPIO registered FAil!! rc = %d", rc);
557 return -ENODEV;
558 }
559
560 if(unlikely(!pform_data))
561 {
562 return -ENODEV;
563 }
564
565 proslic_data = kzalloc(sizeof(*proslic_data), GFP_KERNEL);
566
567 if(unlikely(!proslic_data))
568 {
569 return -ENOMEM;
570 }
571
572 spin_lock_init(&(proslic_data->bus_lock));
573 proslic_spidev = spi;
574#ifdef CONFIG_OF
575 spi->bits_per_word = SILABS_BITS_PER_WORD;
576 spi->max_speed_hz = SILABS_SPI_RATE;
577 spi->mode = SPI_MODE_3;
578 if( spi_setup(spi) != 0)
579 {
580 printk(KERN_ERR PROSLIC_API_HDR "failed to configure spi mode");
581 kfree(proslic_data);
582 return -EIO;
583 }
584#endif
585 spi_set_drvdata(spi, proslic_data);
586
587 /* Probe to determine the number of DAA's or ProSLIC's present */
588 for(channel = 0; channel < proslic_channel_count; channel++)
589 {
590 proslic_data->deviceType[channel] = proslic_detect_type(proslic_spidev, channel);
591 if( proslic_data->deviceType[channel] != PROSLIC_IS_UNKNOWN)
592 {
593 proslic_data->channel_count++;
594 }
595 }
596
597 if(proslic_data->channel_count)
598 {
599 return 0;
600 }
601 else
602 {
603 return -ENXIO;
604 }
605}
606EXPORT_SYMBOL(proslic_spi_probe);
607
608/*****************************************************************************************************/
609int proslic_spi_remove(struct spi_device *spi)
610{
611 void *ptr;
612
613 printk(KERN_INFO "ProSLIC module being removed");
614 /* Just put the device(s) into reset - assumes 1 reset per SPI device */
615 proslic_reset(NULL, 1);
616
617 ptr = spi_get_drvdata(spi);
618 if(ptr != NULL)
619 {
620 kfree(ptr);
621 }
622
623 return 0;
624}
625EXPORT_SYMBOL(proslic_spi_remove);
626
627/*****************************************************************************************************/
628int proslic_spi_setup()
629{
630 int rc;
631 printk(KERN_INFO "proslic_spi_setup()\n");
632
633 rc = spi_register_driver(proslic_spi);
634
635 if(rc != 0)
636 {
637 proslic_error("%s(%d): spi_register driver returned = %d", __FUNCTION__, __LINE__, rc);
638 }
639 else
640 {
641 proslic_debug("%s(%d): spi driver registered", __FUNCTION__, __LINE__);
642 }
643
644 proslic_trace("%s(%d): completed", __FUNCTION__, __LINE__);
645 return rc;
646}
647
648
649void proslic_spi_shutdown()
650{
651 spi_unregister_driver(proslic_spi);
652 gpio_unexport( proslic_reset_gpio );
653 gpio_free( proslic_reset_gpio );
654}
655/*****************************************************************************************************/
656int proslic_get_channel_count()
657{
658 return proslic_data->channel_count;
659}
660
661proslic_dev_t proslic_get_device_type(uint8_t channel_number)
662{
663 if(channel_number < proslic_data->channel_count)
664 {
665 return proslic_data->deviceType[channel_number];
666 }
667 else
668 {
669 return PROSLIC_IS_UNKNOWN;
670 }
671}
672
673void *proslic_get_hCtrl(uint8_t channel)
674{
675 if(channel < proslic_data->channel_count)
676 {
677 return proslic_data;
678 }
679 else
680 {
681 return NULL;
682 }
683}
684
685/*****************************************************************************************************/
686proslic_spi_fptrs_t proslic_spi_if =
687{
688 proslic_reset,
689
690 proslic_write_register,
691 proslic_write_ram,
692
693 proslic_read_register,
694 proslic_read_ram,
695
696 NULL, /* semaphore */
697
698};