| /* | 
|  * drivers/net/wan/slic_ds26522.c | 
|  * | 
|  * Copyright (C) 2016 Freescale Semiconductor, Inc. | 
|  * | 
|  * Author:Zhao Qiang<qiang.zhao@nxp.com> | 
|  * | 
|  * This program is free software; you can redistribute  it and/or modify it | 
|  * under  the terms of  the GNU General  Public License as published by the | 
|  * Free Software Foundation;  either version 2 of the  License, or (at your | 
|  * option) any later version. | 
|  */ | 
|   | 
| #include <linux/bitrev.h> | 
| #include <linux/module.h> | 
| #include <linux/device.h> | 
| #include <linux/kernel.h> | 
| #include <linux/sched.h> | 
| #include <linux/kthread.h> | 
| #include <linux/spi/spi.h> | 
| #include <linux/wait.h> | 
| #include <linux/param.h> | 
| #include <linux/delay.h> | 
| #include <linux/of.h> | 
| #include <linux/of_address.h> | 
| #include <linux/io.h> | 
| #include "slic_ds26522.h" | 
|   | 
| #define DRV_NAME "ds26522" | 
|   | 
| #define SLIC_TRANS_LEN 1 | 
| #define SLIC_TWO_LEN 2 | 
| #define SLIC_THREE_LEN 3 | 
|   | 
| static struct spi_device *g_spi; | 
|   | 
| MODULE_LICENSE("GPL"); | 
| MODULE_AUTHOR("Zhao Qiang<B45475@freescale.com>"); | 
|   | 
| /* the read/write format of address is | 
|  * w/r|A13|A12|A11|A10|A9|A8|A7|A6|A5|A4|A3|A2|A1|A0|x | 
|  */ | 
| static void slic_write(struct spi_device *spi, u16 addr, | 
|                u8 data) | 
| { | 
|     u8 temp[3]; | 
|   | 
|     addr = bitrev16(addr) >> 1; | 
|     data = bitrev8(data); | 
|     temp[0] = (u8)((addr >> 8) & 0x7f); | 
|     temp[1] = (u8)(addr & 0xfe); | 
|     temp[2] = data; | 
|   | 
|     /* write spi addr and value */ | 
|     spi_write(spi, &temp[0], SLIC_THREE_LEN); | 
| } | 
|   | 
| static u8 slic_read(struct spi_device *spi, u16 addr) | 
| { | 
|     u8 temp[2]; | 
|     u8 data; | 
|   | 
|     addr = bitrev16(addr) >> 1; | 
|     temp[0] = (u8)(((addr >> 8) & 0x7f) | 0x80); | 
|     temp[1] = (u8)(addr & 0xfe); | 
|   | 
|     spi_write_then_read(spi, &temp[0], SLIC_TWO_LEN, &data, | 
|                 SLIC_TRANS_LEN); | 
|   | 
|     data = bitrev8(data); | 
|     return data; | 
| } | 
|   | 
| static bool get_slic_product_code(struct spi_device *spi) | 
| { | 
|     u8 device_id; | 
|   | 
|     device_id = slic_read(spi, DS26522_IDR_ADDR); | 
|     if ((device_id & 0xf8) == 0x68) | 
|         return true; | 
|     else | 
|         return false; | 
| } | 
|   | 
| static void ds26522_e1_spec_config(struct spi_device *spi) | 
| { | 
|     /* Receive E1 Mode, Framer Disabled */ | 
|     slic_write(spi, DS26522_RMMR_ADDR, DS26522_RMMR_E1); | 
|   | 
|     /* Transmit E1 Mode, Framer Disable */ | 
|     slic_write(spi, DS26522_TMMR_ADDR, DS26522_TMMR_E1); | 
|   | 
|     /* Receive E1 Mode Framer Enable */ | 
|     slic_write(spi, DS26522_RMMR_ADDR, | 
|            slic_read(spi, DS26522_RMMR_ADDR) | DS26522_RMMR_FRM_EN); | 
|   | 
|     /* Transmit E1 Mode Framer Enable */ | 
|     slic_write(spi, DS26522_TMMR_ADDR, | 
|            slic_read(spi, DS26522_TMMR_ADDR) | DS26522_TMMR_FRM_EN); | 
|   | 
|     /* RCR1, receive E1 B8zs & ESF */ | 
|     slic_write(spi, DS26522_RCR1_ADDR, | 
|            DS26522_RCR1_E1_HDB3 | DS26522_RCR1_E1_CCS); | 
|   | 
|     /* RSYSCLK=2.048MHz, RSYNC-Output */ | 
|     slic_write(spi, DS26522_RIOCR_ADDR, | 
|            DS26522_RIOCR_2048KHZ | DS26522_RIOCR_RSIO_OUT); | 
|   | 
|     /* TCR1 Transmit E1 b8zs */ | 
|     slic_write(spi, DS26522_TCR1_ADDR, DS26522_TCR1_TB8ZS); | 
|   | 
|     /* TSYSCLK=2.048MHz, TSYNC-Output */ | 
|     slic_write(spi, DS26522_TIOCR_ADDR, | 
|            DS26522_TIOCR_2048KHZ | DS26522_TIOCR_TSIO_OUT); | 
|   | 
|     /* Set E1TAF */ | 
|     slic_write(spi, DS26522_E1TAF_ADDR, DS26522_E1TAF_DEFAULT); | 
|   | 
|     /* Set E1TNAF register */ | 
|     slic_write(spi, DS26522_E1TNAF_ADDR, DS26522_E1TNAF_DEFAULT); | 
|   | 
|     /* Receive E1 Mode Framer Enable & init Done */ | 
|     slic_write(spi, DS26522_RMMR_ADDR, slic_read(spi, DS26522_RMMR_ADDR) | | 
|            DS26522_RMMR_INIT_DONE); | 
|   | 
|     /* Transmit E1 Mode Framer Enable & init Done */ | 
|     slic_write(spi, DS26522_TMMR_ADDR, slic_read(spi, DS26522_TMMR_ADDR) | | 
|            DS26522_TMMR_INIT_DONE); | 
|   | 
|     /* Configure LIU E1 mode */ | 
|     slic_write(spi, DS26522_LTRCR_ADDR, DS26522_LTRCR_E1); | 
|   | 
|     /* E1 Mode default 75 ohm w/Transmit Impedance Matlinking */ | 
|     slic_write(spi, DS26522_LTITSR_ADDR, | 
|            DS26522_LTITSR_TLIS_75OHM | DS26522_LTITSR_LBOS_75OHM); | 
|   | 
|     /* E1 Mode default 75 ohm Long Haul w/Receive Impedance Matlinking */ | 
|     slic_write(spi, DS26522_LRISMR_ADDR, | 
|            DS26522_LRISMR_75OHM | DS26522_LRISMR_MAX); | 
|   | 
|     /* Enable Transmit output */ | 
|     slic_write(spi, DS26522_LMCR_ADDR, DS26522_LMCR_TE); | 
| } | 
|   | 
| static int slic_ds26522_init_configure(struct spi_device *spi) | 
| { | 
|     u16 addr; | 
|   | 
|     /* set clock */ | 
|     slic_write(spi, DS26522_GTCCR_ADDR, DS26522_GTCCR_BPREFSEL_REFCLKIN | | 
|             DS26522_GTCCR_BFREQSEL_2048KHZ | | 
|             DS26522_GTCCR_FREQSEL_2048KHZ); | 
|     slic_write(spi, DS26522_GTCR2_ADDR, DS26522_GTCR2_TSSYNCOUT); | 
|     slic_write(spi, DS26522_GFCR_ADDR, DS26522_GFCR_BPCLK_2048KHZ); | 
|   | 
|     /* set gtcr */ | 
|     slic_write(spi, DS26522_GTCR1_ADDR, DS26522_GTCR1); | 
|   | 
|     /* Global LIU Software Reset Register */ | 
|     slic_write(spi, DS26522_GLSRR_ADDR, DS26522_GLSRR_RESET); | 
|   | 
|     /* Global Framer and BERT Software Reset Register */ | 
|     slic_write(spi, DS26522_GFSRR_ADDR, DS26522_GFSRR_RESET); | 
|   | 
|     usleep_range(100, 120); | 
|   | 
|     slic_write(spi, DS26522_GLSRR_ADDR, DS26522_GLSRR_NORMAL); | 
|     slic_write(spi, DS26522_GFSRR_ADDR, DS26522_GFSRR_NORMAL); | 
|   | 
|     /* Perform RX/TX SRESET,Reset receiver */ | 
|     slic_write(spi, DS26522_RMMR_ADDR, DS26522_RMMR_SFTRST); | 
|   | 
|     /* Reset tranceiver */ | 
|     slic_write(spi, DS26522_TMMR_ADDR, DS26522_TMMR_SFTRST); | 
|   | 
|     usleep_range(100, 120); | 
|   | 
|     /* Zero all Framer Registers */ | 
|     for (addr = DS26522_RF_ADDR_START; addr <= DS26522_RF_ADDR_END; | 
|          addr++) | 
|         slic_write(spi, addr, 0); | 
|   | 
|     for (addr = DS26522_TF_ADDR_START; addr <= DS26522_TF_ADDR_END; | 
|          addr++) | 
|         slic_write(spi, addr, 0); | 
|   | 
|     for (addr = DS26522_LIU_ADDR_START; addr <= DS26522_LIU_ADDR_END; | 
|          addr++) | 
|         slic_write(spi, addr, 0); | 
|   | 
|     for (addr = DS26522_BERT_ADDR_START; addr <= DS26522_BERT_ADDR_END; | 
|          addr++) | 
|         slic_write(spi, addr, 0); | 
|   | 
|     /* setup ds26522 for E1 specification */ | 
|     ds26522_e1_spec_config(spi); | 
|   | 
|     slic_write(spi, DS26522_GTCR1_ADDR, 0x00); | 
|   | 
|     return 0; | 
| } | 
|   | 
| static int slic_ds26522_remove(struct spi_device *spi) | 
| { | 
|     pr_info("DS26522 module uninstalled\n"); | 
|     return 0; | 
| } | 
|   | 
| static int slic_ds26522_probe(struct spi_device *spi) | 
| { | 
|     int ret = 0; | 
|   | 
|     g_spi = spi; | 
|     spi->bits_per_word = 8; | 
|   | 
|     if (!get_slic_product_code(spi)) | 
|         return ret; | 
|   | 
|     ret = slic_ds26522_init_configure(spi); | 
|     if (ret == 0) | 
|         pr_info("DS26522 cs%d configured\n", spi->chip_select); | 
|   | 
|     return ret; | 
| } | 
|   | 
| static const struct spi_device_id slic_ds26522_id[] = { | 
|     { .name = "ds26522" }, | 
|     { /* sentinel */ }, | 
| }; | 
| MODULE_DEVICE_TABLE(spi, slic_ds26522_id); | 
|   | 
| static const struct of_device_id slic_ds26522_match[] = { | 
|     { | 
|      .compatible = "maxim,ds26522", | 
|      }, | 
|     {}, | 
| }; | 
| MODULE_DEVICE_TABLE(of, slic_ds26522_match); | 
|   | 
| static struct spi_driver slic_ds26522_driver = { | 
|     .driver = { | 
|            .name = "ds26522", | 
|            .bus = &spi_bus_type, | 
|            .of_match_table = slic_ds26522_match, | 
|            }, | 
|     .probe = slic_ds26522_probe, | 
|     .remove = slic_ds26522_remove, | 
|     .id_table = slic_ds26522_id, | 
| }; | 
|   | 
| module_spi_driver(slic_ds26522_driver); |