/* $NetBSD: sitara_cm.c,v 1.3 2016/10/04 16:03:39 kiyohara Exp $ */
/*
* Copyright (c) 2010
* Ben Gray <ben.r.gray@gmail.com>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Ben Gray.
* 4. The name of the company nor the name of the author may be used to
* endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* SCM - System Control Module
*
* Hopefully in the end this module will contain a bunch of utility functions
* for configuring and querying the general system control registers, but for
* now it only does pin(pad) multiplexing.
*
* This is different from the GPIO module in that it is used to configure the
* pins between modules not just GPIO input/output.
*
* This file contains the generic top level driver, however it relies on chip
* specific settings and therefore expects an array of sitara_cm_padconf structs
* call ti_padconf_devmap to be located somewhere in the kernel.
*
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: sitara_cm.c,v 1.3 2016/10/04 16:03:39 kiyohara Exp $");
#include "opt_omap.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/proc.h>
#include <sys/kernel.h>
#include <arm/omap/omap2_obiovar.h>
#include <arm/omap/omap2_reg.h>
#include <arm/omap/sitara_cm.h>
#include <arm/omap/sitara_cmreg.h>
struct sitara_cm_softc {
device_t sc_dev;
bus_space_tag_t sc_iot;
bus_space_handle_t sc_ioh;
uint32_t sc_cid; /* Chip Identification */
uint32_t sc_did; /* Device IDCODE */
};
static struct sitara_cm_softc *sitara_cm_sc = NULL;
static int sitara_cm_match(device_t, cfdata_t, void *);
static void sitara_cm_attach(device_t, device_t, void *);
#define sitara_cm_read_2(sc, reg) \
bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg))
#define sitara_cm_write_2(sc, reg, val) \
bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
#define sitara_cm_read_4(sc, reg) \
bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))
#define sitara_cm_write_4(sc, reg, val) \
bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
/**
* ti_padconf_devmap - Array of pins, should be defined one per SoC
*
* This array is typically defined in one of the targeted *_scm_pinumx.c
* files and is specific to the given SoC platform. Each entry in the array
* corresponds to an individual pin.
*/
extern const struct sitara_cm_device sitara_cm_dev;
/**
* sitara_cm_padconf_from_name - searches the list of pads and returns entry
* with matching ball name.
* @ballname: the name of the ball
*
* RETURNS:
* A pointer to the matching padconf or NULL if the ball wasn't found.
*/
static const struct sitara_cm_padconf*
sitara_cm_padconf_from_name(const char *ballname)
{
const struct sitara_cm_padconf *padconf;
padconf = sitara_cm_dev.padconf;
while (padconf->ballname != NULL) {
if (strcmp(ballname, padconf->ballname) == 0)
return(padconf);
padconf++;
}
return (NULL);
}
/**
* sitara_cm_padconf_set_internal - sets the muxmode and state for a pad/pin
* @padconf: pointer to the pad structure
* @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
* @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
*
*
* LOCKING:
* Internally locks its own context.
*
* RETURNS:
* 0 on success.
* EINVAL if pin requested is outside valid range or already in use.
*/
static int
sitara_cm_padconf_set_internal(struct sitara_cm_softc *sc,
const struct sitara_cm_padconf *padconf,
const char *muxmode, unsigned int state)
{
unsigned int mode;
uint16_t reg_val;
/* populate the new value for the PADCONF register */
reg_val = (uint16_t)(state & sitara_cm_dev.padconf_sate_mask);
/* find the new mode requested */
for (mode = 0; mode < 8; mode++) {
if ((padconf->muxmodes[mode] != NULL) &&
(strcmp(padconf->muxmodes[mode], muxmode) == 0)) {
break;
}
}
/* couldn't find the mux mode */
if (mode >= 8) {
aprint_error_dev(sc->sc_dev, "Invalid mode \"%s\"\n", muxmode);
return (EINVAL);
}
/* set the mux mode */
reg_val |= (uint16_t)(mode & sitara_cm_dev.padconf_muxmode_mask);
aprint_debug_dev(sc->sc_dev,
"setting internal %x for %s\n", reg_val, muxmode);
/* write the register value (16-bit writes) */
sitara_cm_write_2(sc, padconf->reg_off, reg_val);
return (0);
}
/**
* sitara_cm_padconf_set - sets the muxmode and state for a pad/pin
* @padname: the name of the pad, i.e. "c12"
* @muxmode: the name of the mode to use for the pin, i.e. "uart1_rx"
* @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
*
*
* LOCKING:
* Internally locks its own context.
*
* RETURNS:
* 0 on success.
* EINVAL if pin requested is outside valid range or already in use.
*/
int
sitara_cm_padconf_set(const char *padname, const char *muxmode, unsigned int state)
{
const struct sitara_cm_padconf *padconf;
if (!sitara_cm_sc)
return (ENXIO);
/* find the pin in the devmap */
padconf = sitara_cm_padconf_from_name(padname);
if (padconf == NULL)
return (EINVAL);
return (
sitara_cm_padconf_set_internal(sitara_cm_sc, padconf, muxmode, state)
);
}
/**
* sitara_cm_padconf_get - gets the muxmode and state for a pad/pin
* @padname: the name of the pad, i.e. "c12"
* @muxmode: upon return will contain the name of the muxmode of the pin
* @state: upon return will contain the state of the pad/pin
*
*
* LOCKING:
* Internally locks its own context.
*
* RETURNS:
* 0 on success.
* EINVAL if pin requested is outside valid range or already in use.
*/
int
sitara_cm_padconf_get(const char *padname, const char **muxmode,
unsigned int *state)
{
const struct sitara_cm_padconf *padconf;
uint16_t reg_val;
if (!sitara_cm_sc)
return (ENXIO);
/* find the pin in the devmap */
padconf = sitara_cm_padconf_from_name(padname);
if (padconf == NULL)
return (EINVAL);
/* read the register value (16-bit reads) */
reg_val = sitara_cm_read_2(sitara_cm_sc, padconf->reg_off);
/* save the state */
if (state)
*state = (reg_val & sitara_cm_dev.padconf_sate_mask);
/* save the mode */
if (muxmode) {
*muxmode = padconf->muxmodes[
(reg_val & sitara_cm_dev.padconf_muxmode_mask)
];
}
return (0);
}
/**
* sitara_cm_padconf_set_gpiomode - converts a pad to GPIO mode.
* @gpio: the GPIO pin number (0-195)
* @state: the state to put the pad/pin in, i.e. PADCONF_PIN_???
*
*
*
* LOCKING:
* Internally locks its own context.
*
* RETURNS:
* 0 on success.
* EINVAL if pin requested is outside valid range or already in use.
*/
int
sitara_cm_padconf_set_gpiomode(uint32_t gpio, unsigned int state)
{
const struct sitara_cm_padconf *padconf;
uint16_t reg_val;
if (!sitara_cm_sc)
return (ENXIO);
/* find the gpio pin in the padconf array */
padconf = sitara_cm_dev.padconf;
while (padconf->ballname != NULL) {
if (padconf->gpio_pin == gpio)
break;
padconf++;
}
if (padconf->ballname == NULL)
return (EINVAL);
/* populate the new value for the PADCONF register */
reg_val = (uint16_t)(state & sitara_cm_dev.padconf_sate_mask);
/* set the mux mode */
reg_val |=
(uint16_t)(padconf->gpio_mode & sitara_cm_dev.padconf_muxmode_mask);
/* write the register value (16-bit writes) */
sitara_cm_write_2(sitara_cm_sc, padconf->reg_off, reg_val);
return (0);
}
/**
* sitara_cm_padconf_get_gpiomode - gets the current GPIO mode of the pin
* @gpio: the GPIO pin number (0-195)
* @state: upon return will contain the state
*
*
*
* LOCKING:
* Internally locks its own context.
*
* RETURNS:
* 0 on success.
* EINVAL if pin requested is outside valid range or not configured as GPIO.
*/
int
sitara_cm_padconf_get_gpiomode(uint32_t gpio, unsigned int *state)
{
const struct sitara_cm_padconf *padconf;
uint16_t reg_val;
if (!sitara_cm_sc)
return (ENXIO);
/* find the gpio pin in the padconf array */
padconf = sitara_cm_dev.padconf;
while (padconf->ballname != NULL) {
if (padconf->gpio_pin == gpio)
break;
padconf++;
}
if (padconf->ballname == NULL)
return (EINVAL);
/* read the current register settings */
reg_val = sitara_cm_read_2(sitara_cm_sc, padconf->reg_off);
/*
* check to make sure the pins is configured as GPIO in the
* first state
*/
if ((reg_val & sitara_cm_dev.padconf_muxmode_mask) !=
padconf->gpio_mode)
return (EINVAL);
/*
* read and store the reset of the state,
* i.e. pull-up, pull-down, etc
*/
if (state)
*state = (reg_val & sitara_cm_dev.padconf_sate_mask);
return (0);
}
int
sitara_cm_reg_read_4(uint32_t reg, uint32_t *val)
{
if (!sitara_cm_sc)
return (ENXIO);
*val = sitara_cm_read_4(sitara_cm_sc, reg);
return (0);
}
int
sitara_cm_reg_write_4(uint32_t reg, uint32_t val)
{
if (!sitara_cm_sc)
return (ENXIO);
sitara_cm_write_4(sitara_cm_sc, reg, val);
return (0);
}
/*
* Device part of OMAP SCM driver
*/
CFATTACH_DECL_NEW(sitaracm, sizeof(struct sitara_cm_softc),
sitara_cm_match, sitara_cm_attach, NULL, NULL);
static int
sitara_cm_match(device_t parent, cfdata_t match, void *opaque)
{
struct obio_attach_args *obio = opaque;
#ifdef TI_AM335X
if (obio->obio_addr == 0x44e10000)
return 1;
#endif
return 0;
}
static void
sitara_cm_attach(device_t parent, device_t self, void *opaque)
{
struct sitara_cm_softc *sc = device_private(self);
struct obio_attach_args *obio = opaque;
uint32_t rev;
char cid, buf[256];
const char *did;
const char *fmt = "\177\020"
"b\0ICSS\0"
"b\1CPSW\0"
"b\7DCAN\0"
"f\16\1ICSS_FEA EtherCAT functionality\0=\0disabled\0=\1enabled\0"
"f\17\1ICSS_FEA TX_AUTO_SEQUENCE\0=\0disabled\0=\1enabled\0"
"b\29SGX\0";
aprint_naive("\n");
if (sitara_cm_sc)
panic("sitara_cm_attach: already attached");
sc->sc_dev = self;
sc->sc_iot = obio->obio_iot;
if (bus_space_map(obio->obio_iot, obio->obio_addr, obio->obio_size,
0, &sc->sc_ioh) != 0) {
aprint_error(": couldn't map address space\n");
return ;
}
sitara_cm_sc = sc;
if (sitara_cm_reg_read_4(OMAP2SCM_REVISION, &rev) != 0)
panic("sitara_cm_attach: read revision");
aprint_normal(": control module, rev %d.%d\n",
SCM_REVISION_MAJOR(rev), SCM_REVISION_MINOR(rev));
sitara_cm_reg_read_4(OMAP2SCM_DEVID, &sc->sc_did);
sitara_cm_reg_read_4(OMAP2SCM_DEV_FEATURE, &sc->sc_cid);
switch (sc->sc_cid) {
case CHIPID_AM3351: cid = '1'; break;
case CHIPID_AM3352: cid = '2'; break;
case CHIPID_AM3354: cid = '4'; break;
case CHIPID_AM3356: cid = '6'; break;
case CHIPID_AM3357: cid = '7'; break;
case CHIPID_AM3358: cid = '8'; break;
case CHIPID_AM3359: cid = '9'; break;
default:
aprint_normal_dev(self, "unknwon ChipID found 0x%08x\n",
sc->sc_cid);
cid = 'x';
break;
}
aprint_normal_dev(self, "AM335%c", cid);
switch (sc->sc_did) {
case DEVID_AM335X_SR_10: did = "1.0"; break;
case DEVID_AM335X_SR_20: did = "2.0"; break;
case DEVID_AM335X_SR_21: did = "2.1"; break;
default:
aprint_normal_dev(self, "unknwon DeviceID found 0x%08x\n",
sc->sc_did);
did = NULL;
break;
}
if (did != NULL)
aprint_normal(" Silicon Revision %s", did);
snprintb(buf, sizeof(buf), fmt, sc->sc_cid);
aprint_normal(": %s\n", buf);
}
uint32_t
omap_chipid(void)
{
struct sitara_cm_softc *sc;
device_t dev;
dev = device_find_by_xname("sitaracm0");
KASSERT(dev != NULL);
sc = device_private(dev);
return sc->sc_cid;
}
uint32_t
omap_devid(void)
{
struct sitara_cm_softc *sc;
device_t dev;
dev = device_find_by_xname("sitaracm0");
KASSERT(dev != NULL);
sc = device_private(dev);
return sc->sc_did;
}