Commit f74cf6ff authored by Atsushi Nemoto's avatar Atsushi Nemoto Committed by Ralf Baechle

[MIPS] rbtx4938: Convert SPI codes to use generic SPI drivers

Use rtc-rs5c348 and at25 spi protocol driver and spi_txx9 spi
controller driver instead of platform dependent codes.

This patch also removes dependencies to old RTC interfaces such as
rtc_mips_get_time, etc.
Signed-off-by: default avatarAtsushi Nemoto <anemo@mba.ocn.ne.jp>
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent 3896b054
......@@ -966,8 +966,20 @@ CONFIG_LEGACY_PTY_COUNT=256
#
# SPI support
#
# CONFIG_SPI is not set
# CONFIG_SPI_MASTER is not set
CONFIG_SPI=y
CONFIG_SPI_MASTER=y
#
# SPI Master Controller Drivers
#
# CONFIG_SPI_BITBANG is not set
CONFIG_SPI_TXX9=y
#
# SPI Protocol Masters
#
CONFIG_SPI_AT25=y
# CONFIG_SPI_SPIDEV is not set
#
# Dallas's 1-wire bus
......@@ -1207,7 +1219,43 @@ CONFIG_USB_MON=y
#
# Real Time Clock
#
# CONFIG_RTC_CLASS is not set
CONFIG_RTC_LIB=y
CONFIG_RTC_CLASS=y
CONFIG_RTC_HCTOSYS=y
CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
# CONFIG_RTC_DEBUG is not set
#
# RTC interfaces
#
CONFIG_RTC_INTF_SYSFS=y
CONFIG_RTC_INTF_PROC=y
CONFIG_RTC_INTF_DEV=y
CONFIG_RTC_INTF_DEV_UIE_EMUL=y
# CONFIG_RTC_DRV_TEST is not set
#
# I2C RTC drivers
#
#
# SPI RTC drivers
#
CONFIG_RTC_DRV_RS5C348=y
# CONFIG_RTC_DRV_MAX6902 is not set
#
# Platform RTC drivers
#
# CONFIG_RTC_DRV_CMOS is not set
# CONFIG_RTC_DRV_DS1553 is not set
# CONFIG_RTC_DRV_DS1742 is not set
# CONFIG_RTC_DRV_M48T86 is not set
# CONFIG_RTC_DRV_V3020 is not set
#
# on-CPU RTC drivers
#
#
# DMA Engine support
......
......@@ -6,6 +6,6 @@
# unless it's something special (ie not a .c file).
#
obj-y += prom.o setup.o irq.o rtc_rx5c348.o
obj-y += prom.o setup.o irq.o
obj-$(CONFIG_KGDB) += dbgio.o
/*
* RTC routines for RICOH Rx5C348 SPI chip.
* Copyright (C) 2000-2001 Toshiba Corporation
*
* 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
* terms of the GNU General Public License version 2. This program is
* licensed "as is" without any warranty of any kind, whether express
* or implied.
*
* Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/time.h>
#include <linux/bcd.h>
#include <asm/time.h>
#include <asm/tx4938/spi.h>
#define EPOCH 2000
/* registers */
#define Rx5C348_REG_SECOND 0
#define Rx5C348_REG_MINUTE 1
#define Rx5C348_REG_HOUR 2
#define Rx5C348_REG_WEEK 3
#define Rx5C348_REG_DAY 4
#define Rx5C348_REG_MONTH 5
#define Rx5C348_REG_YEAR 6
#define Rx5C348_REG_ADJUST 7
#define Rx5C348_REG_ALARM_W_MIN 8
#define Rx5C348_REG_ALARM_W_HOUR 9
#define Rx5C348_REG_ALARM_W_WEEK 10
#define Rx5C348_REG_ALARM_D_MIN 11
#define Rx5C348_REG_ALARM_D_HOUR 12
#define Rx5C348_REG_CTL1 14
#define Rx5C348_REG_CTL2 15
/* register bits */
#define Rx5C348_BIT_PM 0x20 /* REG_HOUR */
#define Rx5C348_BIT_Y2K 0x80 /* REG_MONTH */
#define Rx5C348_BIT_24H 0x20 /* REG_CTL1 */
#define Rx5C348_BIT_XSTP 0x10 /* REG_CTL2 */
/* commands */
#define Rx5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */
#define Rx5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */
#define Rx5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */
#define Rx5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */
static struct spi_dev_desc srtc_dev_desc = {
.baud = 1000000, /* 1.0Mbps @ Vdd 2.0V */
.tcss = 31,
.tcsh = 1,
.tcsr = 62,
/* 31us for Tcss (62us for Tcsr) is required for carry operation) */
.byteorder = 1, /* MSB-First */
.polarity = 0, /* High-Active */
.phase = 1, /* Shift-Then-Sample */
};
static int srtc_chipid;
static int srtc_24h;
static inline int
spi_rtc_io(unsigned char *inbuf, unsigned char *outbuf, unsigned int count)
{
unsigned char *inbufs[1], *outbufs[1];
unsigned int incounts[2], outcounts[2];
inbufs[0] = inbuf;
incounts[0] = count;
incounts[1] = 0;
outbufs[0] = outbuf;
outcounts[0] = count;
outcounts[1] = 0;
return txx9_spi_io(srtc_chipid, &srtc_dev_desc,
inbufs, incounts, outbufs, outcounts, 0);
}
/* RTC-dependent code for time.c */
static int
rtc_rx5c348_set_time(unsigned long t)
{
unsigned char inbuf[8];
struct rtc_time tm;
u8 year, month, day, hour, minute, second, century;
/* convert */
to_tm(t, &tm);
year = tm.tm_year % 100;
month = tm.tm_mon+1; /* tm_mon starts from 0 to 11 */
day = tm.tm_mday;
hour = tm.tm_hour;
minute = tm.tm_min;
second = tm.tm_sec;
century = tm.tm_year / 100;
inbuf[0] = Rx5C348_CMD_MW(Rx5C348_REG_SECOND);
BIN_TO_BCD(second);
inbuf[1] = second;
BIN_TO_BCD(minute);
inbuf[2] = minute;
if (srtc_24h) {
BIN_TO_BCD(hour);
inbuf[3] = hour;
} else {
/* hour 0 is AM12, noon is PM12 */
inbuf[3] = 0;
if (hour >= 12)
inbuf[3] = Rx5C348_BIT_PM;
hour = (hour + 11) % 12 + 1;
BIN_TO_BCD(hour);
inbuf[3] |= hour;
}
inbuf[4] = 0; /* ignore week */
BIN_TO_BCD(day);
inbuf[5] = day;
BIN_TO_BCD(month);
inbuf[6] = month;
if (century >= 20)
inbuf[6] |= Rx5C348_BIT_Y2K;
BIN_TO_BCD(year);
inbuf[7] = year;
/* write in one transfer to avoid data inconsistency */
return spi_rtc_io(inbuf, NULL, 8);
}
static unsigned long
rtc_rx5c348_get_time(void)
{
unsigned char inbuf[8], outbuf[8];
unsigned int year, month, day, hour, minute, second;
inbuf[0] = Rx5C348_CMD_MR(Rx5C348_REG_SECOND);
memset(inbuf + 1, 0, 7);
/* read in one transfer to avoid data inconsistency */
if (spi_rtc_io(inbuf, outbuf, 8))
return 0;
second = outbuf[1];
BCD_TO_BIN(second);
minute = outbuf[2];
BCD_TO_BIN(minute);
if (srtc_24h) {
hour = outbuf[3];
BCD_TO_BIN(hour);
} else {
hour = outbuf[3] & ~Rx5C348_BIT_PM;
BCD_TO_BIN(hour);
hour %= 12;
if (outbuf[3] & Rx5C348_BIT_PM)
hour += 12;
}
day = outbuf[5];
BCD_TO_BIN(day);
month = outbuf[6] & ~Rx5C348_BIT_Y2K;
BCD_TO_BIN(month);
year = outbuf[7];
BCD_TO_BIN(year);
year += EPOCH;
return mktime(year, month, day, hour, minute, second);
}
void __init
rtc_rx5c348_init(int chipid)
{
unsigned char inbuf[2], outbuf[2];
srtc_chipid = chipid;
/* turn on RTC if it is not on */
inbuf[0] = Rx5C348_CMD_R(Rx5C348_REG_CTL2);
inbuf[1] = 0;
spi_rtc_io(inbuf, outbuf, 2);
if (outbuf[1] & Rx5C348_BIT_XSTP) {
inbuf[0] = Rx5C348_CMD_W(Rx5C348_REG_CTL2);
inbuf[1] = 0;
spi_rtc_io(inbuf, NULL, 2);
}
inbuf[0] = Rx5C348_CMD_R(Rx5C348_REG_CTL1);
inbuf[1] = 0;
spi_rtc_io(inbuf, outbuf, 2);
if (outbuf[1] & Rx5C348_BIT_24H)
srtc_24h = 1;
/* set the function pointers */
rtc_mips_get_time = rtc_rx5c348_get_time;
rtc_mips_set_time = rtc_rx5c348_set_time;
}
......@@ -6,4 +6,4 @@
# unless it's something special (ie not a .c file).
#
obj-y += prom.o setup.o irq.o spi_eeprom.o spi_txx9.o
obj-y += prom.o setup.o irq.o spi_eeprom.o
......@@ -165,8 +165,6 @@ toshiba_rbtx4938_irq_ioc_disable(unsigned int irq)
TX4938_RD08(TOSHIBA_RBTX4938_IOC_INTR_ENAB);
}
extern void __init txx9_spi_irqinit(int irc_irq);
void __init arch_init_irq(void)
{
extern void tx4938_irq_init(void);
......@@ -185,9 +183,5 @@ void __init arch_init_irq(void)
/* Onboard 10M Ether: High Active */
TX4938_WR(TX4938_MKA(TX4938_IRC_IRDM0), 0x00000040);
if (tx4938_ccfgptr->pcfg & TX4938_PCFG_SPI_SEL) {
txx9_spi_irqinit(RBTX4938_IRQ_IRC_SPI);
}
wbflush();
}
......@@ -14,13 +14,13 @@
#include <linux/init.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/console.h>
#include <linux/pci.h>
#include <linux/pm.h>
#include <linux/platform_device.h>
#include <linux/clk.h>
#include <asm/wbflush.h>
#include <asm/reboot.h>
......@@ -29,13 +29,15 @@
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/gpio.h>
#include <asm/tx4938/rbtx4938.h>
#ifdef CONFIG_SERIAL_TXX9
#include <linux/tty.h>
#include <linux/serial.h>
#include <linux/serial_core.h>
#endif
#include <linux/spi/spi.h>
#include <asm/tx4938/spi.h>
#include <asm/gpio.h>
extern void rbtx4938_time_init(void) __init;
extern char * __init prom_getcmdline(void);
......@@ -575,44 +577,33 @@ arch_initcall(tx4938_pcibios_init);
#define SEEPROM3_CS 1 /* IOC */
#define SRTC_CS 2 /* IOC */
static int rbtx4938_spi_cs_func(int chipid, int on)
#ifdef CONFIG_PCI
static unsigned char rbtx4938_ethaddr[17];
static int __init rbtx4938_ethaddr_init(void)
{
unsigned char bit;
switch (chipid) {
case RBTX4938_SEEPROM1_CHIPID:
if (on)
tx4938_pioptr->dout &= ~(1 << SEEPROM1_CS);
else
tx4938_pioptr->dout |= (1 << SEEPROM1_CS);
return 0;
break;
case RBTX4938_SEEPROM2_CHIPID:
bit = (1 << SEEPROM2_CS);
break;
case RBTX4938_SEEPROM3_CHIPID:
bit = (1 << SEEPROM3_CS);
break;
case RBTX4938_SRTC_CHIPID:
bit = (1 << SRTC_CS);
break;
default:
return -ENODEV;
unsigned char sum;
int i;
/* 0-3: "MAC\0", 4-9:eth0, 10-15:eth1, 16:sum */
if (spi_eeprom_read(SEEPROM1_CS, 0,
rbtx4938_ethaddr, sizeof(rbtx4938_ethaddr)))
printk(KERN_ERR "seeprom: read error.\n");
else {
unsigned char *dat = rbtx4938_ethaddr;
if (strcmp(dat, "MAC") != 0)
printk(KERN_WARNING "seeprom: bad signature.\n");
for (i = 0, sum = 0; i < sizeof(dat); i++)
sum += dat[i];
if (sum)
printk(KERN_WARNING "seeprom: bad checksum.\n");
}
/* bit1,2,4 are low active, bit3 is high active */
*rbtx4938_spics_ptr =
(*rbtx4938_spics_ptr & ~bit) |
((on ? (bit ^ 0x0b) : ~(bit ^ 0x0b)) & bit);
return 0;
}
#ifdef CONFIG_PCI
extern int spi_eeprom_read(int chipid, int address, unsigned char *buf, int len);
device_initcall(rbtx4938_ethaddr_init);
int rbtx4938_get_tx4938_ethaddr(struct pci_dev *dev, unsigned char *addr)
{
struct pci_controller *channel = (struct pci_controller *)dev->bus->sysdata;
static unsigned char dat[17];
static int read_dat = 0;
int ch = 0;
if (channel != &tx4938_pci_controller[1])
......@@ -628,29 +619,11 @@ int rbtx4938_get_tx4938_ethaddr(struct pci_dev *dev, unsigned char *addr)
default:
return -ENODEV;
}
if (!read_dat) {
unsigned char sum;
int i;
read_dat = 1;
/* 0-3: "MAC\0", 4-9:eth0, 10-15:eth1, 16:sum */
if (spi_eeprom_read(RBTX4938_SEEPROM1_CHIPID,
0, dat, sizeof(dat))) {
printk(KERN_ERR "seeprom: read error.\n");
} else {
if (strcmp(dat, "MAC") != 0)
printk(KERN_WARNING "seeprom: bad signature.\n");
for (i = 0, sum = 0; i < sizeof(dat); i++)
sum += dat[i];
if (sum)
printk(KERN_WARNING "seeprom: bad checksum.\n");
}
}
memcpy(addr, &dat[4 + 6 * ch], 6);
memcpy(addr, &rbtx4938_ethaddr[4 + 6 * ch], 6);
return 0;
}
#endif /* CONFIG_PCI */
extern void __init txx9_spi_init(unsigned long base, int (*cs_func)(int chipid, int on));
static void __init rbtx4938_spi_setup(void)
{
/* set SPI_SEL */
......@@ -658,7 +631,6 @@ static void __init rbtx4938_spi_setup(void)
/* chip selects for SPI devices */
tx4938_pioptr->dout |= (1 << SEEPROM1_CS);
tx4938_pioptr->dir |= (1 << SEEPROM1_CS);
txx9_spi_init(TX4938_SPI_REG, rbtx4938_spi_cs_func);
}
static struct resource rbtx4938_fpga_resource;
......@@ -897,10 +869,8 @@ void tx4938_report_pcic_status(void)
/* We use onchip r4k counter or TMR timer as our system wide timer
* interrupt running at 100HZ. */
extern void __init rtc_rx5c348_init(int chipid);
void __init rbtx4938_time_init(void)
{
rtc_rx5c348_init(RBTX4938_SRTC_CHIPID);
mips_hpt_frequency = txx9_cpu_clock / 2;
}
......@@ -1017,29 +987,6 @@ void __init toshiba_rbtx4938_setup(void)
*rbtx4938_dipsw_ptr, *rbtx4938_bdipsw_ptr);
}
#ifdef CONFIG_PROC_FS
extern void spi_eeprom_proc_create(struct proc_dir_entry *dir, int chipid);
static int __init tx4938_spi_proc_setup(void)
{
struct proc_dir_entry *tx4938_spi_eeprom_dir;
tx4938_spi_eeprom_dir = proc_mkdir("spi_eeprom", 0);
if (!tx4938_spi_eeprom_dir)
return -ENOMEM;
/* don't allow user access to RBTX4938_SEEPROM1_CHIPID
* as it contains eth0 and eth1 MAC addresses
*/
spi_eeprom_proc_create(tx4938_spi_eeprom_dir, RBTX4938_SEEPROM2_CHIPID);
spi_eeprom_proc_create(tx4938_spi_eeprom_dir, RBTX4938_SEEPROM3_CHIPID);
return 0;
}
__initcall(tx4938_spi_proc_setup);
#endif
static int __init rbtx4938_ne_init(void)
{
struct resource res[] = {
......@@ -1161,3 +1108,73 @@ void gpio_set_value(unsigned gpio, int value)
else
rbtx4938_spi_gpio_set(gpio, value);
}
/* SPI support */
static void __init txx9_spi_init(unsigned long base, int irq)
{
struct resource res[] = {
{
.start = base,
.end = base + 0x20 - 1,
.flags = IORESOURCE_MEM,
.parent = &tx4938_reg_resource,
}, {
.start = irq,
.flags = IORESOURCE_IRQ,
},
};
platform_device_register_simple("txx9spi", 0,
res, ARRAY_SIZE(res));
}
static int __init rbtx4938_spi_init(void)
{
struct spi_board_info srtc_info = {
.modalias = "rs5c348",
.max_speed_hz = 1000000, /* 1.0Mbps @ Vdd 2.0V */
.bus_num = 0,
.chip_select = 16 + SRTC_CS,
/* Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS */
.mode = SPI_MODE_1 | SPI_CS_HIGH,
};
spi_register_board_info(&srtc_info, 1);
spi_eeprom_register(SEEPROM1_CS);
spi_eeprom_register(16 + SEEPROM2_CS);
spi_eeprom_register(16 + SEEPROM3_CS);
txx9_spi_init(TX4938_SPI_REG & 0xfffffffffULL, RBTX4938_IRQ_IRC_SPI);
return 0;
}
arch_initcall(rbtx4938_spi_init);
/* Minimum CLK support */
struct clk *clk_get(struct device *dev, const char *id)
{
if (!strcmp(id, "spi-baseclk"))
return (struct clk *)(txx9_gbus_clock / 2 / 4);
return ERR_PTR(-ENOENT);
}
EXPORT_SYMBOL(clk_get);
int clk_enable(struct clk *clk)
{
return 0;
}
EXPORT_SYMBOL(clk_enable);
void clk_disable(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_disable);
unsigned long clk_get_rate(struct clk *clk)
{
return (unsigned long)clk;
}
EXPORT_SYMBOL(clk_get_rate);
void clk_put(struct clk *clk)
{
}
EXPORT_SYMBOL(clk_put);
......@@ -10,209 +10,90 @@
* Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/proc_fs.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/spi/spi.h>
#include <linux/spi/eeprom.h>
#include <asm/tx4938/spi.h>
#include <asm/tx4938/tx4938.h>
/* ATMEL 250x0 instructions */
#define ATMEL_WREN 0x06
#define ATMEL_WRDI 0x04
#define ATMEL_RDSR 0x05
#define ATMEL_WRSR 0x01
#define ATMEL_READ 0x03
#define ATMEL_WRITE 0x02
#define AT250X0_PAGE_SIZE 8
#define ATMEL_SR_BSY 0x01
#define ATMEL_SR_WEN 0x02
#define ATMEL_SR_BP0 0x04
#define ATMEL_SR_BP1 0x08
DEFINE_SPINLOCK(spi_eeprom_lock);
static struct spi_dev_desc seeprom_dev_desc = {
.baud = 1500000, /* 1.5Mbps */
.tcss = 1,
.tcsh = 1,
.tcsr = 1,
.byteorder = 1, /* MSB-First */
.polarity = 0, /* High-Active */
.phase = 0, /* Sample-Then-Shift */
};
static inline int
spi_eeprom_io(int chipid,
unsigned char **inbufs, unsigned int *incounts,
unsigned char **outbufs, unsigned int *outcounts)
{
return txx9_spi_io(chipid, &seeprom_dev_desc,
inbufs, incounts, outbufs, outcounts, 0);
}
int spi_eeprom_write_enable(int chipid, int enable)
/* register board information for at25 driver */
int __init spi_eeprom_register(int chipid)
{
unsigned char inbuf[1];
unsigned char *inbufs[1];
unsigned int incounts[2];
unsigned long flags;
int stat;
inbuf[0] = enable ? ATMEL_WREN : ATMEL_WRDI;
inbufs[0] = inbuf;
incounts[0] = sizeof(inbuf);
incounts[1] = 0;
spin_lock_irqsave(&spi_eeprom_lock, flags);
stat = spi_eeprom_io(chipid, inbufs, incounts, NULL, NULL);
spin_unlock_irqrestore(&spi_eeprom_lock, flags);
return stat;
}
static int spi_eeprom_read_status_nolock(int chipid)
{
unsigned char inbuf[2], outbuf[2];
unsigned char *inbufs[1], *outbufs[1];
unsigned int incounts[2], outcounts[2];
int stat;
inbuf[0] = ATMEL_RDSR;
inbuf[1] = 0;
inbufs[0] = inbuf;
incounts[0] = sizeof(inbuf);
incounts[1] = 0;
outbufs[0] = outbuf;
outcounts[0] = sizeof(outbuf);
outcounts[1] = 0;
stat = spi_eeprom_io(chipid, inbufs, incounts, outbufs, outcounts);
if (stat < 0)
return stat;
return outbuf[1];
static struct spi_eeprom eeprom = {
.name = "at250x0",
.byte_len = 128,
.page_size = AT250X0_PAGE_SIZE,
.flags = EE_ADDR1,
};
struct spi_board_info info = {
.modalias = "at25",
.max_speed_hz = 1500000, /* 1.5Mbps */
.bus_num = 0,
.chip_select = chipid,
.platform_data = &eeprom,
/* Mode 0: High-Active, Sample-Then-Shift */
};
return spi_register_board_info(&info, 1);
}
int spi_eeprom_read_status(int chipid)
{
unsigned long flags;
int stat;
spin_lock_irqsave(&spi_eeprom_lock, flags);
stat = spi_eeprom_read_status_nolock(chipid);
spin_unlock_irqrestore(&spi_eeprom_lock, flags);
return stat;
}
/* simple temporary spi driver to provide early access to seeprom. */
int spi_eeprom_read(int chipid, int address, unsigned char *buf, int len)
{
unsigned char inbuf[2];
unsigned char *inbufs[2], *outbufs[2];
unsigned int incounts[2], outcounts[3];
unsigned long flags;
int stat;
inbuf[0] = ATMEL_READ;
inbuf[1] = address;
inbufs[0] = inbuf;
inbufs[1] = NULL;
incounts[0] = sizeof(inbuf);
incounts[1] = 0;
outbufs[0] = NULL;
outbufs[1] = buf;
outcounts[0] = 2;
outcounts[1] = len;
outcounts[2] = 0;
spin_lock_irqsave(&spi_eeprom_lock, flags);
stat = spi_eeprom_io(chipid, inbufs, incounts, outbufs, outcounts);
spin_unlock_irqrestore(&spi_eeprom_lock, flags);
return stat;
}
static struct read_param {
int chipid;
int address;
unsigned char *buf;
int len;
} *read_param;
int spi_eeprom_write(int chipid, int address, unsigned char *buf, int len)
static int __init early_seeprom_probe(struct spi_device *spi)
{
unsigned char inbuf[2];
unsigned char *inbufs[2];
unsigned int incounts[3];
unsigned long flags;
int i, stat;
if (address / 8 != (address + len - 1) / 8)
return -EINVAL;
stat = spi_eeprom_write_enable(chipid, 1);
if (stat < 0)
return stat;
stat = spi_eeprom_read_status(chipid);
if (stat < 0)
return stat;
if (!(stat & ATMEL_SR_WEN))
return -EPERM;
inbuf[0] = ATMEL_WRITE;
inbuf[1] = address;
inbufs[0] = inbuf;
inbufs[1] = buf;
incounts[0] = sizeof(inbuf);
incounts[1] = len;
incounts[2] = 0;
spin_lock_irqsave(&spi_eeprom_lock, flags);
stat = spi_eeprom_io(chipid, inbufs, incounts, NULL, NULL);
if (stat < 0)
goto unlock_return;
/* write start. max 10ms */
for (i = 10; i > 0; i--) {
int stat = spi_eeprom_read_status_nolock(chipid);
if (stat < 0)
goto unlock_return;
if (!(stat & ATMEL_SR_BSY))
break;
mdelay(1);
int stat = 0;
u8 cmd[2];
int len = read_param->len;
char *buf = read_param->buf;
int address = read_param->address;
dev_info(&spi->dev, "spiclk %u KHz.\n",
(spi->max_speed_hz + 500) / 1000);
if (read_param->chipid != spi->chip_select)
return -ENODEV;
while (len > 0) {
/* spi_write_then_read can only work with small chunk */
int c = len < AT250X0_PAGE_SIZE ? len : AT250X0_PAGE_SIZE;
cmd[0] = 0x03; /* AT25_READ */
cmd[1] = address;
stat = spi_write_then_read(spi, cmd, sizeof(cmd), buf, c);
buf += c;
len -= c;
address += c;
}
spin_unlock_irqrestore(&spi_eeprom_lock, flags);
if (i == 0)
return -EIO;
return len;
unlock_return:
spin_unlock_irqrestore(&spi_eeprom_lock, flags);
return stat;
}
#ifdef CONFIG_PROC_FS
#define MAX_SIZE 0x80 /* for ATMEL 25010 */
static int spi_eeprom_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
unsigned int size = MAX_SIZE;
if (spi_eeprom_read((int)data, 0, (unsigned char *)page, size) < 0)
size = 0;
return size;
}
static int spi_eeprom_write_proc(struct file *file, const char *buffer,
unsigned long count, void *data)
{
unsigned int size = MAX_SIZE;
int i;
if (file->f_pos >= size)
return -EIO;
if (file->f_pos + count > size)
count = size - file->f_pos;
for (i = 0; i < count; i += 8) {
int len = count - i < 8 ? count - i : 8;
if (spi_eeprom_write((int)data, file->f_pos,
(unsigned char *)buffer, len) < 0) {
count = -EIO;
break;
}
buffer += len;
file->f_pos += len;
}
return count;
}
static struct spi_driver early_seeprom_driver __initdata = {
.driver = {
.name = "at25",
.owner = THIS_MODULE,
},
.probe = early_seeprom_probe,
};
__init void spi_eeprom_proc_create(struct proc_dir_entry *dir, int chipid)
int __init spi_eeprom_read(int chipid, int address,
unsigned char *buf, int len)
{
struct proc_dir_entry *entry;
char name[128];
sprintf(name, "seeprom-%d", chipid);
entry = create_proc_entry(name, 0600, dir);
if (entry) {
entry->read_proc = spi_eeprom_read_proc;
entry->write_proc = spi_eeprom_write_proc;
entry->data = (void *)chipid;
}
int ret;
struct read_param param = {
.chipid = chipid,
.address = address,
.buf = buf,
.len = len
};
read_param = &param;
ret = spi_register_driver(&early_seeprom_driver);
if (!ret)
spi_unregister_driver(&early_seeprom_driver);
return ret;
}
#endif /* CONFIG_PROC_FS */
/*
* linux/arch/mips/tx4938/toshiba_rbtx4938/spi_txx9.c
* Copyright (C) 2000-2001 Toshiba Corporation
*
* 2003-2005 (c) MontaVista Software, Inc. This file is licensed under the
* terms of the GNU General Public License version 2. This program is
* licensed "as is" without any warranty of any kind, whether express
* or implied.
*
* Support for TX4938 in 2.6 - Manish Lachwani (mlachwani@mvista.com)
*/
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
#include <asm/tx4938/spi.h>
#include <asm/tx4938/tx4938.h>
static int (*txx9_spi_cs_func)(int chipid, int on);
static DEFINE_SPINLOCK(txx9_spi_lock);
extern unsigned int txx9_gbus_clock;
#define SPI_FIFO_SIZE 4
void __init txx9_spi_init(unsigned long base, int (*cs_func)(int chipid, int on))
{
txx9_spi_cs_func = cs_func;
/* enter config mode */
tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
}
static DECLARE_WAIT_QUEUE_HEAD(txx9_spi_wait);
static irqreturn_t txx9_spi_interrupt(int irq, void *dev_id)
{
/* disable rx intr */
tx4938_spiptr->cr0 &= ~TXx9_SPCR0_RBSIE;
wake_up(&txx9_spi_wait);
return IRQ_HANDLED;
}
static struct irqaction txx9_spi_action = {
.handler = txx9_spi_interrupt,
.name = "spi",
};
void __init txx9_spi_irqinit(int irc_irq)
{
setup_irq(irc_irq, &txx9_spi_action);
}
int txx9_spi_io(int chipid, struct spi_dev_desc *desc,
unsigned char **inbufs, unsigned int *incounts,
unsigned char **outbufs, unsigned int *outcounts,
int cansleep)
{
unsigned int incount, outcount;
unsigned char *inp, *outp;
int ret;
unsigned long flags;
spin_lock_irqsave(&txx9_spi_lock, flags);
if ((tx4938_spiptr->mcr & TXx9_SPMCR_OPMODE) == TXx9_SPMCR_ACTIVE) {
spin_unlock_irqrestore(&txx9_spi_lock, flags);
return -EBUSY;
}
/* enter config mode */
tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
tx4938_spiptr->cr0 =
(desc->byteorder ? TXx9_SPCR0_SBOS : 0) |
(desc->polarity ? TXx9_SPCR0_SPOL : 0) |
(desc->phase ? TXx9_SPCR0_SPHA : 0) |
0x08;
tx4938_spiptr->cr1 =
(((TXX9_IMCLK + desc->baud) / (2 * desc->baud) - 1) << 8) |
0x08 /* 8 bit only */;
/* enter active mode */
tx4938_spiptr->mcr = TXx9_SPMCR_ACTIVE;
spin_unlock_irqrestore(&txx9_spi_lock, flags);
/* CS ON */
if ((ret = txx9_spi_cs_func(chipid, 1)) < 0) {
spin_unlock_irqrestore(&txx9_spi_lock, flags);
return ret;
}
udelay(desc->tcss);
/* do scatter IO */
inp = inbufs ? *inbufs : NULL;
outp = outbufs ? *outbufs : NULL;
incount = 0;
outcount = 0;
while (1) {
unsigned char data;
unsigned int count;
int i;
if (!incount) {
incount = incounts ? *incounts++ : 0;
inp = (incount && inbufs) ? *inbufs++ : NULL;
}
if (!outcount) {
outcount = outcounts ? *outcounts++ : 0;
outp = (outcount && outbufs) ? *outbufs++ : NULL;
}
if (!inp && !outp)
break;
count = SPI_FIFO_SIZE;
if (incount)
count = min(count, incount);
if (outcount)
count = min(count, outcount);
/* now tx must be idle... */
while (!(tx4938_spiptr->sr & TXx9_SPSR_SIDLE))
;
tx4938_spiptr->cr0 =
(tx4938_spiptr->cr0 & ~TXx9_SPCR0_RXIFL_MASK) |
((count - 1) << 12);
if (cansleep) {
/* enable rx intr */
tx4938_spiptr->cr0 |= TXx9_SPCR0_RBSIE;
}
/* send */
for (i = 0; i < count; i++)
tx4938_spiptr->dr = inp ? *inp++ : 0;
/* wait all rx data */
if (cansleep) {
wait_event(txx9_spi_wait,
tx4938_spiptr->sr & TXx9_SPSR_SRRDY);
} else {
while (!(tx4938_spiptr->sr & TXx9_SPSR_RBSI))
;
}
/* receive */
for (i = 0; i < count; i++) {
data = tx4938_spiptr->dr;
if (outp)
*outp++ = data;
}
if (incount)
incount -= count;
if (outcount)
outcount -= count;
}
/* CS OFF */
udelay(desc->tcsh);
txx9_spi_cs_func(chipid, 0);
udelay(desc->tcsr);
spin_lock_irqsave(&txx9_spi_lock, flags);
/* enter config mode */
tx4938_spiptr->mcr = TXx9_SPMCR_CONFIG | TXx9_SPMCR_BCLR;
spin_unlock_irqrestore(&txx9_spi_lock, flags);
return 0;
}
......@@ -105,12 +105,6 @@
#define rbtx4938_pcireset_ptr \
((volatile unsigned char *)RBTX4938_PCIRESET_ADDR)
/* SPI */
#define RBTX4938_SEEPROM1_CHIPID 0
#define RBTX4938_SEEPROM2_CHIPID 1
#define RBTX4938_SEEPROM3_CHIPID 2
#define RBTX4938_SRTC_CHIPID 3
/*
* IRQ mappings
*/
......
......@@ -14,61 +14,7 @@
#ifndef __ASM_TX_BOARDS_TX4938_SPI_H
#define __ASM_TX_BOARDS_TX4938_SPI_H
/* SPI */
struct spi_dev_desc {
unsigned int baud;
unsigned short tcss, tcsh, tcsr; /* CS setup/hold/recovery time */
unsigned int byteorder:1; /* 0:LSB-First, 1:MSB-First */
unsigned int polarity:1; /* 0:High-Active */
unsigned int phase:1; /* 0:Sample-Then-Shift */
};
extern void txx9_spi_init(unsigned long base, int (*cs_func)(int chipid, int on)) __init;
extern void txx9_spi_irqinit(int irc_irq) __init;
extern int txx9_spi_io(int chipid, struct spi_dev_desc *desc,
unsigned char **inbufs, unsigned int *incounts,
unsigned char **outbufs, unsigned int *outcounts,
int cansleep);
extern int spi_eeprom_write_enable(int chipid, int enable);
extern int spi_eeprom_read_status(int chipid);
extern int spi_eeprom_register(int chipid);
extern int spi_eeprom_read(int chipid, int address, unsigned char *buf, int len);
extern int spi_eeprom_write(int chipid, int address, unsigned char *buf, int len);
extern void spi_eeprom_proc_create(struct proc_dir_entry *dir, int chipid) __init;
#define TXX9_IMCLK (txx9_gbus_clock / 2)
/*
* SPI
*/
/* SPMCR : SPI Master Control */
#define TXx9_SPMCR_OPMODE 0xc0
#define TXx9_SPMCR_CONFIG 0x40
#define TXx9_SPMCR_ACTIVE 0x80
#define TXx9_SPMCR_SPSTP 0x02
#define TXx9_SPMCR_BCLR 0x01
/* SPCR0 : SPI Status */
#define TXx9_SPCR0_TXIFL_MASK 0xc000
#define TXx9_SPCR0_RXIFL_MASK 0x3000
#define TXx9_SPCR0_SIDIE 0x0800
#define TXx9_SPCR0_SOEIE 0x0400
#define TXx9_SPCR0_RBSIE 0x0200
#define TXx9_SPCR0_TBSIE 0x0100
#define TXx9_SPCR0_IFSPSE 0x0010
#define TXx9_SPCR0_SBOS 0x0004
#define TXx9_SPCR0_SPHA 0x0002
#define TXx9_SPCR0_SPOL 0x0001
/* SPSR : SPI Status */
#define TXx9_SPSR_TBSI 0x8000
#define TXx9_SPSR_RBSI 0x4000
#define TXx9_SPSR_TBS_MASK 0x3800
#define TXx9_SPSR_RBS_MASK 0x0700
#define TXx9_SPSR_SPOE 0x0080
#define TXx9_SPSR_IFSD 0x0008
#define TXx9_SPSR_SIDLE 0x0004
#define TXx9_SPSR_STRDY 0x0002
#define TXx9_SPSR_SRRDY 0x0001
#endif /* __ASM_TX_BOARDS_TX4938_SPI_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment