cregit-Linux how code gets into the kernel

Release 4.7 drivers/spi/spi-lp8841-rtc.c

Directory: drivers/spi
/*
 * SPI master driver for ICP DAS LP-8841 RTC
 *
 * Copyright (C) 2016 Sergei Ianovich
 *
 * based on
 *
 * Dallas DS1302 RTC Support
 * Copyright (C) 2002 David McCullough
 * Copyright (C) 2003 - 2007 Paul Mundt
 *
 * 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.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>


#define DRIVER_NAME	"spi_lp8841_rtc"


#define SPI_LP8841_RTC_CE	0x01

#define SPI_LP8841_RTC_CLK	0x02

#define SPI_LP8841_RTC_nWE	0x04

#define SPI_LP8841_RTC_MOSI	0x08

#define SPI_LP8841_RTC_MISO	0x01

/*
 * REVISIT If there is support for SPI_3WIRE and SPI_LSB_FIRST in SPI
 * GPIO driver, this SPI driver can be replaced by a simple GPIO driver
 * providing 3 GPIO pins.
 */


struct spi_lp8841_rtc {
	
void		*iomem;
	
unsigned long	state;
};


static inline void setsck(struct spi_lp8841_rtc *data, int is_on) { if (is_on) data->state |= SPI_LP8841_RTC_CLK; else data->state &= ~SPI_LP8841_RTC_CLK; writeb(data->state, data->iomem); }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich44100.00%1100.00%
Total44100.00%1100.00%


static inline void setmosi(struct spi_lp8841_rtc *data, int is_on) { if (is_on) data->state |= SPI_LP8841_RTC_MOSI; else data->state &= ~SPI_LP8841_RTC_MOSI; writeb(data->state, data->iomem); }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich44100.00%1100.00%
Total44100.00%1100.00%


static inline int getmiso(struct spi_lp8841_rtc *data) { return ioread8(data->iomem) & SPI_LP8841_RTC_MISO; }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich22100.00%1100.00%
Total22100.00%1100.00%


static inline u32 bitbang_txrx_be_cpha0_lsb(struct spi_lp8841_rtc *data, unsigned usecs, unsigned cpol, unsigned flags, u32 word, u8 bits) { /* if (cpol == 0) this is SPI_MODE_0; else this is SPI_MODE_2 */ u32 shift = 32 - bits; /* clock starts at inactive polarity */ for (; likely(bits); bits--) { /* setup LSB (to slave) on leading edge */ if ((flags & SPI_MASTER_NO_TX) == 0) setmosi(data, (word & 1)); usleep_range(usecs, usecs + 1); /* T(setup) */ /* sample LSB (from slave) on trailing edge */ word >>= 1; if ((flags & SPI_MASTER_NO_RX) == 0) word |= (getmiso(data) << 31); setsck(data, !cpol); usleep_range(usecs, usecs + 1); setsck(data, cpol); } word >>= shift; return word; }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich138100.00%1100.00%
Total138100.00%1100.00%


static int spi_lp8841_rtc_transfer_one(struct spi_master *master, struct spi_device *spi, struct spi_transfer *t) { struct spi_lp8841_rtc *data = spi_master_get_devdata(master); unsigned count = t->len; const u8 *tx = t->tx_buf; u8 *rx = t->rx_buf; u8 word = 0; int ret = 0; if (tx) { data->state &= ~SPI_LP8841_RTC_nWE; writeb(data->state, data->iomem); while (likely(count > 0)) { word = *tx++; bitbang_txrx_be_cpha0_lsb(data, 1, 0, SPI_MASTER_NO_RX, word, 8); count--; } } else if (rx) { data->state |= SPI_LP8841_RTC_nWE; writeb(data->state, data->iomem); while (likely(count > 0)) { word = bitbang_txrx_be_cpha0_lsb(data, 1, 0, SPI_MASTER_NO_TX, word, 8); *rx++ = word; count--; } } else { ret = -EINVAL; } spi_finalize_current_transfer(master); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich201100.00%1100.00%
Total201100.00%1100.00%


static void spi_lp8841_rtc_set_cs(struct spi_device *spi, bool enable) { struct spi_lp8841_rtc *data = spi_master_get_devdata(spi->master); data->state = 0; writeb(data->state, data->iomem); if (enable) { usleep_range(4, 5); data->state |= SPI_LP8841_RTC_CE; writeb(data->state, data->iomem); usleep_range(4, 5); } }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich80100.00%1100.00%
Total80100.00%1100.00%


static int spi_lp8841_rtc_setup(struct spi_device *spi) { if ((spi->mode & SPI_CS_HIGH) == 0) { dev_err(&spi->dev, "unsupported active low chip select\n"); return -EINVAL; } if ((spi->mode & SPI_LSB_FIRST) == 0) { dev_err(&spi->dev, "unsupported MSB first mode\n"); return -EINVAL; } if ((spi->mode & SPI_3WIRE) == 0) { dev_err(&spi->dev, "unsupported wiring. 3 wires required\n"); return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich98100.00%1100.00%
Total98100.00%1100.00%

#ifdef CONFIG_OF static const struct of_device_id spi_lp8841_rtc_dt_ids[] = { { .compatible = "icpdas,lp8841-spi-rtc" }, { } }; MODULE_DEVICE_TABLE(of, spi_lp8841_rtc_dt_ids); #endif
static int spi_lp8841_rtc_probe(struct platform_device *pdev) { int ret; struct spi_master *master; struct spi_lp8841_rtc *data; void *iomem; master = spi_alloc_master(&pdev->dev, sizeof(*data)); if (!master) return -ENOMEM; platform_set_drvdata(pdev, master); master->flags = SPI_MASTER_HALF_DUPLEX; master->mode_bits = SPI_CS_HIGH | SPI_3WIRE | SPI_LSB_FIRST; master->bus_num = pdev->id; master->num_chipselect = 1; master->setup = spi_lp8841_rtc_setup; master->set_cs = spi_lp8841_rtc_set_cs; master->transfer_one = spi_lp8841_rtc_transfer_one; master->bits_per_word_mask = SPI_BPW_MASK(8); #ifdef CONFIG_OF master->dev.of_node = pdev->dev.of_node; #endif data = spi_master_get_devdata(master); iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); data->iomem = devm_ioremap_resource(&pdev->dev, iomem); ret = PTR_ERR_OR_ZERO(data->iomem); if (ret) { dev_err(&pdev->dev, "failed to get IO address\n"); goto err_put_master; } /* register with the SPI framework */ ret = devm_spi_register_master(&pdev->dev, master); if (ret) { dev_err(&pdev->dev, "cannot register spi master\n"); goto err_put_master; } return ret; err_put_master: spi_master_put(master); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich23297.07%150.00%
arnd bergmannarnd bergmann72.93%150.00%
Total239100.00%2100.00%

MODULE_ALIAS("platform:" DRIVER_NAME); static struct platform_driver spi_lp8841_rtc_driver = { .driver = { .name = DRIVER_NAME, .of_match_table = of_match_ptr(spi_lp8841_rtc_dt_ids), }, .probe = spi_lp8841_rtc_probe, }; module_platform_driver(spi_lp8841_rtc_driver); MODULE_DESCRIPTION("SPI master driver for ICP DAS LP-8841 RTC"); MODULE_AUTHOR("Sergei Ianovich"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
sergey yanovichsergey yanovich100599.31%150.00%
arnd bergmannarnd bergmann70.69%150.00%
Total1012100.00%2100.00%
Directory: drivers/spi
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}